#!/usr/bin/perl
# (c) Copyright 2005-2014. CodeWeavers, Inc.
use warnings;
use strict;
my @desktop_plugins=("CXMenuWindows", "CXMenuCheck",
"CXMenuDebian", "CXMenuCDE",
"CXMenuXDG", "CXMenuDtop", "CXMenuPerm",
"CXMenuMacOSX",
"CXMenuShortcut");
my %auto_localize=("Programs" => 1,
"Windows Applications" => 1,
"Windows Games" => 1);
# Portable which(1) implementation
sub cxwhich($$;$)
{
my ($dirs, $app, $noexec)=@_;
if ($app =~ /^\//)
{
return $app if ((-x $app or $noexec) and -f $app);
}
elsif ($app =~ /\//)
{
require Cwd;
my $path=Cwd::cwd() . "/$app";
return $path if ((-x $path or $noexec) and -f $path);
}
else
{
foreach my $dir (split /:/, $dirs)
{
return "$dir/$app" if ($dir ne "" and (-x "$dir/$app" or $noexec) and -f "$dir/$app");
}
}
return undef;
}
# Fast dirname() implementation
sub _cxdirname($)
{
my ($path)=@_;
return undef if (!defined $path);
return "." if ($path !~ s!/+[^/]+/*$!!s);
return "/" if ($path eq "");
return $path;
}
# Locate where CrossOver is installed by looking for the directory
# where the cxmenu script is located, unwinding symlinks on the way
sub locate_cx_root(;$)
{
my ($fallback)=@_;
my $argv0=cxwhich($ENV{PATH},$0);
$argv0=$0 if (!defined $argv0);
if ($argv0 !~ m+^/+)
{
require Cwd;
$argv0=Cwd::cwd() . "/$argv0";
}
my $dir=_cxdirname($argv0);
my $bindir=$dir;
$bindir =~ s%/lib$%/bin%;
while (!-x "$bindir/cxmenu" or !-f "$bindir/cxmenu")
{
last if (!-l $argv0);
$argv0=readlink($argv0);
$argv0="$dir/$argv0" if ($argv0 !~ m+^/+);
$dir=_cxdirname($argv0);
$bindir=$dir;
$bindir =~ s%/lib$%/bin%;
}
$bindir =~ s%/(?:\./)+%/%g;
$bindir =~ s%/\.$%%;
$ENV{CX_ROOT}=_cxdirname($bindir);
if ((!-x "$ENV{CX_ROOT}/bin/cxmenu" or !-f "$ENV{CX_ROOT}/bin/cxmenu") and
$fallback)
{
$ENV{CX_ROOT}=$fallback;
}
if (!-x "$ENV{CX_ROOT}/bin/cxmenu" or !-f "$ENV{CX_ROOT}/bin/cxmenu")
{
my $name0=$0;
$name0 =~ s+^.*/++;
print STDERR "$name0:error: could not find CrossOver in '$ENV{CX_ROOT}'\n";
exit 1;
}
return $ENV{CX_ROOT};
}
BEGIN {
unshift @INC, locate_cx_root() . "/lib/perl";
}
use CXLog;
use CXUtils;
use CXMenu;
my $filters={};
sub get_filter_list($$)
{
my ($cxmenu, $filter)=@_;
$filter||="";
my $filter_list=$filters->{$filter};
if (!defined $filter_list)
{
my @list;
if ($filter eq "")
{
@list=grep !/^Settings$/i, $cxmenu->get_section_names();
}
else
{
@list=split /:+/, $filter;
}
$filters->{$filter}=$filter_list=\@list;
}
return $filter_list;
}
sub check_menu_path($)
{
my ($path)=@_;
if ($path =~ /:/)
{
cxerr("colons are not allowed in menu path '$path'\n");
return 0;
}
if ($path =~ m%/\.*/%)
{
# Path contains stuff like '//', '/./', '/../', etc.
cxerr("invalid menu path '$path'\n");
return 0;
}
if ($path !~ m%^(?:Desktop|StartMenu)(?:[._][^/]+)?/%)
{
cxerr("unknown menu root '$path'\n");
return 0;
}
return 1;
}
my %menu_components;
sub path_to_components($$$$$)
{
my ($rawpath, $cxconfig, $menu_root, $menu_strip, $tag)=@_;
$rawpath =~ s!/+!/!g;
my $components=$menu_components{$rawpath};
if (!exists $menu_components{$rawpath})
{
# Don't give up even if the menu path or root is not valid
# so we build a negative cache
my $menu;
if (check_menu_path($rawpath))
{
$menu->{rawpath}=$rawpath;
my $path=$rawpath;
$path =~ s%^(Desktop|StartMenu)(?:[._][^/]+)?/%/%;
$menu->{root}=$1;
$menu->{is_dir}=1 if ($path =~ s%/$%%);
$menu->{is_desktop}=1 if ($menu->{root} eq "Desktop");
if (!$menu->{is_desktop})
{
if ($menu_strip)
{
my @path_items=split "/", $path;
if ($menu_strip+1 < @path_items)
{
splice @path_items, 0, $menu_strip+1;
$path=join("/", "", @path_items);
}
elsif ($menu->{is_dir})
{
# Menu folders that are not deep enough disappear
# entirely
$menu=undef;
}
else
{
# But menu entries just get moved to the root folder
$path="/$path_items[-1]";
}
}
$path="$menu_root$path" if (defined $menu_root);
}
if ($menu)
{
# These are the only _file_ extensions that are always hidden
# by Windows, including in the menus
$path =~ s%\.(?:lnk|pif|url)$%%i if (!$menu->{is_dir});
$menu->{path}=$path;
$menu->{dir}=$path;
$menu->{dir}=~ s%/[^/]*$%/%;
$menu->{name}=$path;
$menu->{name}=~s%^.*/%%;
$menu->{name}=~s%/$%%;
}
# Re-name the Windows Steam icon to distinguish
# it from the native client.
if ($menu && CXUtils::get_desktop_environment ne "macosx")
{
$menu->{name}=~s/^Steam$/Steam\ \(CrossOver\)/;
}
}
if ($menu)
{
$menu->{tag}=$tag;
my $path="";
foreach my $item (split "/", $menu->{dir})
{
next if ($item eq "");
$path .="/$item";
my $component={
name => $item,
path => $path,
intermediate => 1,
is_dir => 1,
tag => $tag
};
push @$components, $component;
}
push @$components, $menu;
}
$menu_components{$rawpath}=$components;
}
return $components;
}
sub update_field($$$)
{
my ($section, $name, $value)=@_;
$value||="";
my $oldval=$section->get($name, "");
return 0 if ($oldval eq $value);
if ($value eq "")
{
$section->remove($name);
}
else
{
$section->set($name, $value);
}
return 1;
}
# Process command-line options
my $opt_create;
my $opt_delete;
my $opt_purge;
my $opt_delete_filter;
my $opt_mode;
my $opt_mode_filter;
my $opt_mmenu_never;
my $opt_query;
my $opt_query_filter;
my $opt_list_files;
my $opt_list_files_filter;
my $opt_start;
my $opt_wait_children;
my $opt_enable_alt_loader;
my $opt_install;
my $opt_install_filter;
my $opt_uninstall;
my $opt_uninstall_filter;
my $opt_removeall;
my $opt_destdir;
my $opt_sync;
my $opt_sync_install_none;
my $opt_sync_uninstall_none;
my $opt_utf8;
my $opt_bottle;
my $opt_crossover;
my $opt_conf;
my $opt_menu_root;
my $opt_menu_strip;
my $opt_tag;
my $opt_pattern;
my $opt_filter;
my $opt_ignorelist;
my $opt_ro_desktopdata;
my $opt_description;
my $opt_type;
my $opt_command;
my $opt_icon;
my $opt_shortcut;
my $opt_scope;
my $opt_menu_scope;
my $opt_arch;
my $opt_arch_filter;
my $opt_verbose;
my $opt_help;
require CXOpts;
my $cxopts=CXOpts->new(["stop_on_unknown", "stop_on_non_option"]);
$cxopts->add_options(["create=s" => \$opt_create,
"delete" => \$opt_delete,
"purge" => \$opt_purge,
"delete-filter=s" => \$opt_delete_filter,
"mode=s" => \$opt_mode,
"mode-filter=s" => \$opt_mode_filter,
"mmenu-never=s" => \$opt_mmenu_never,
"query" => \$opt_query,
"query-filter=s" => \$opt_query_filter,
"list-files" => \$opt_list_files,
"list-files-filter=s" => \$opt_list_files_filter,
"start=s" => \$opt_start,
"wait-children" => \$opt_wait_children,
"enable-alt-loader=s" => \$opt_enable_alt_loader,
"install" => \$opt_install,
"install-filter=s" => \$opt_install_filter,
"uninstall" => \$opt_uninstall,
"uninstall-filter=s" => \$opt_uninstall_filter,
"removeall" => \$opt_removeall,
"sync" => \$opt_sync,
"destdir=s" => \$opt_destdir,
"sync-install-none" => \$opt_sync_install_none,
"sync-uninstall-none" => \$opt_sync_uninstall_none,
"utf8" => \$opt_utf8,
"bottle=s" => \$opt_bottle,
"crossover" => \$opt_crossover,
"conf=s" => \$opt_conf,
"menu-root=s" => \$opt_menu_root,
"menu-strip=s" => \$opt_menu_strip,
"tag=s" => \$opt_tag,
"pattern=s" => \$opt_pattern,
"filter=s" => \$opt_filter,
"ignorelist=s" => \$opt_ignorelist,
"ro-desktopdata"=> \$opt_ro_desktopdata,
"description=s" => \$opt_description,
"type=s" => \$opt_type,
"command=s" => \$opt_command,
"icon=s" => \$opt_icon,
"shortcut=s" => \$opt_shortcut,
"scope=s" => \$opt_scope,
"menu-scope=s" => \$opt_menu_scope,
"arch=s" => \$opt_arch,
"arch-filter=s" => \$opt_arch_filter,
Loading ...