Why Gemfury? Push, build, and install  RubyGems npm packages Python packages Maven artifacts PHP packages Go Modules Debian packages RPM packages NuGet packages

Repository URL to install this package:

Details    
crossover / opt / cxoffice / bin / cxnsplugin
Size: Mime:
#!/usr/bin/perl
# (c) Copyright 2005-2012. CodeWeavers, Inc.
use warnings;
use strict;


# 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;

my $filters;
sub get_filter_list($$)
{
    my ($cxnsplugin, $filter)=@_;

    $filter||="";
    my $filter_list=$filters->{$filter};
    if (!defined $filter_list)
    {
        my @list;
        if ($filter eq "")
        {
            @list=$cxnsplugin->get_section_names();
        }
        else
        {
            @list=split /:+/, $filter;
        }
        $filters->{$filter}=$filter_list=\@list;
    }
    return $filter_list;
}

sub get_install_mode($$$)
{
    my ($dll, $hash_mode, $default_mode)=@_;
    foreach my $mode ("install", "ignore")
    {
        if (defined $hash_mode->{$mode} and
            $dll =~ /(?:$hash_mode->{$mode})/i)
        {
            return $mode;
        }
    }
    return $default_mode;
}

sub get_dllname($)
{
    my ($dllkey)=@_;
    my $dllname=cxbasename(demangle_string($dllkey));
    my $checksum = unpack("%32C*", $dllkey) % 65535;
    $dllname =~ s/\.dll$/.$checksum/i;

    return $dllname;
}

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;
}

my $field_list;
sub get_field_list($$)
{
    if (!defined $field_list)
    {
        my ($crossover, $scope)=@_;
        my @list;
        if (defined $crossover)
        {
            my $prefix=($scope eq "managed" ? "Managed" : "Private");
            @list=grep /^$prefix.*NSPluginDirs$/, @{$crossover->get_field_list()};
        }
        $field_list=\@list;
    }
    # Return the list, not a reference, so a foreach
    # cannot modify the contents of $field_list
    return @$field_list;
}

sub field_to_architecture($)
{
    my ($arch)=@_;
    $arch =~ s/^(?:Managed|Private)(.*)NSPluginDirs$/$1/i;
    $arch =~ tr/A-Z/a-z/;
    return $arch;
}

sub arch_to_lib_dir($$)
{
    my ($desktopdata, $arch)=@_;
    return "$desktopdata/cxnsplugin/$arch";
}


# Process command-line options
my $opt_create;
my $opt_create_filter;
my $opt_delete;
my $opt_delete_filter;
my $opt_mode;
my $opt_mode_filter;
my $opt_query;
my $opt_query_filter;
my $opt_list_files;
my $opt_list_files_filter;
my $opt_install;
my $opt_install_filter;
my $opt_uninstall;
my $opt_uninstall_filter;
my $opt_removeall;
my $opt_sync;
my $opt_sync_install_none;
my $opt_sync_uninstall_none;
my $opt_utf8;
my $opt_bottle;
my $opt_conf;
my $opt_tag;
my $opt_pattern;
my $opt_ro_desktopdata;
my $opt_filter;
my $opt_scope;
my $opt_plugin_dirs;
my $opt_verbose;
my $opt_help;
require CXOpts;
my $cxopts=CXOpts->new();
$cxopts->add_options(["create"        => \$opt_create,
                      "create-filter=s" => \$opt_create_filter,
                      "delete"        => \$opt_delete,
                      "delete-filter=s" => \$opt_delete_filter,
                      "mode=s"        => \$opt_mode,
                      "mode-filter=s" => \$opt_mode_filter,
                      "query"         => \$opt_query,
                      "query-filter=s" => \$opt_query_filter,
                      "list-files"    => \$opt_list_files,
                      "list-files-filter=s" => \$opt_list_files_filter,
                      "install"       => \$opt_install,
                      "install-filter=s" => \$opt_install_filter,
                      "uninstall"     => \$opt_uninstall,
                      "uninstall-filter=s" => \$opt_uninstall_filter,
                      "removeall"     => \$opt_removeall,
                      "sync"          => \$opt_sync,
                      "sync-install-none" => \$opt_sync_install_none,
                      "sync-uninstall-none" => \$opt_sync_uninstall_none,
                      "utf8"          => \$opt_utf8,
                      "bottle=s"      => \$opt_bottle,
                      "conf=s"        => \$opt_conf,
                      "tag=s"         => \$opt_tag,
                      "pattern=s"     => \$opt_pattern,
                      "ro-desktopdata"=> \$opt_ro_desktopdata,
                      "filter=s"      => \$opt_filter,
                      "scope=s"       => \$opt_scope,
                      "plugin-dirs=s" => \$opt_plugin_dirs,
                      "verbose!"      => \$opt_verbose,
                      "?|h|help"      => \$opt_help
                     ]);
my $err=$cxopts->parse();
CXLog::fdopen(2) if ($opt_verbose);


# Validate the command line options
my $usage;
my ($read_only, $only_removeall);
my ($default_mode, $hash_mode);
if ($err)
{
    cxerr("$err\n");
    $usage=2;
}
elsif ($opt_help)
{
    $usage=0;
}
else
{
    if (!$opt_utf8)
    {
        # Re-encode the parameters to UTF-8
        require CXRecode;
        $opt_filter=CXRecode::from_sys("UTF-8", $opt_filter);
        $opt_create_filter=CXRecode::from_sys("UTF-8", $opt_create_filter);
        $opt_delete_filter=CXRecode::from_sys("UTF-8", $opt_delete_filter);
        $opt_mode_filter=CXRecode::from_sys("UTF-8", $opt_mode_filter);
        $opt_query_filter=CXRecode::from_sys("UTF-8", $opt_query_filter);
        $opt_list_files_filter=CXRecode::from_sys("UTF-8", $opt_list_files_filter);
        $opt_install_filter=CXRecode::from_sys("UTF-8", $opt_install_filter);
        $opt_uninstall_filter=CXRecode::from_sys("UTF-8", $opt_uninstall_filter);
    }

    my $cmd_count=0;
    $cmd_count++ if (defined $opt_query);
    $cmd_count++ if (defined $opt_list_files);
    my $read_only_count=$cmd_count;
    $cmd_count++ if (defined $opt_create);
    $cmd_count++ if (defined $opt_mode and !$opt_sync);
    $cmd_count++ if (defined $opt_delete);
    $cmd_count++ if (defined $opt_install);
    $cmd_count++ if (defined $opt_uninstall);
    $cmd_count++ if (defined $opt_removeall);
    $cmd_count++ if (defined $opt_sync);
    $read_only=1 if ($read_only_count == $cmd_count);
    $only_removeall=1 if ($cmd_count == 1 and defined $opt_removeall);
    if ($cmd_count == 0)
    {
        cxerr("you must specify the operation to perform\n");
        $usage=2;
    }
    elsif ($opt_sync)
    {
        if ($opt_delete)
        {
            cxerr("--sync is incompatible with --delete\n");
            $usage=2;
        }
        if ($opt_sync_uninstall_none and ($opt_uninstall or $opt_removeall))
        {
            cxerr("--sync-uninstall-none is incompatible with --uninstall and --removeall\n");
            $usage=2;
        }
        if ($opt_sync_install_none and $opt_install)
        {
            cxerr("--sync-install-none is incompatible with --install\n");
            $usage=2;
        }
        if (defined $opt_filter or defined $opt_create_filter or
            defined $opt_delete_filter or defined $opt_mode_filter or
            defined $opt_query_filter or defined $opt_list_files_filter or
            defined $opt_install_filter or defined $opt_uninstall_filter)
        {
            cxerr("--sync is incompatible with the --filter options\n");
            $usage=2;
        }
    }
    elsif ($opt_sync_install_none or $opt_sync_uninstall_none)
    {
        cxerr("--sync-install-none and --sync-uninstall-none can only be used with --sync\n");
        $usage=2;
    }
    if ($opt_create and defined $opt_mode_filter)
    {
        cxerr("--create is incompatible with the --mode-filter option\n");
        $usage=2;
    }

    if (defined $opt_mode)
    {
        # Prepare and check the $opt_mode specification
        foreach my $mode_spec (split /;+/, $opt_mode)
        {
            if ($mode_spec !~ /^(install|ignore)(?:=(.*))?$/i)
            {
                cxerr("unknown install mode specification '$mode_spec'\n");
                $usage=2;
                last;
            }
            my ($mode, $regexp_list)=($1, $2);
            if (defined $regexp_list)
            {
                foreach my $regexp (split /:+/, $regexp_list)
                {
                    next if ($regexp eq "");
                    if (!defined $hash_mode->{$mode})
                    {
                        $hash_mode->{$mode}=$regexp;
                    }
                    else
                    {
                        $hash_mode->{$mode}.="|$regexp";
                    }
                }
            }
            elsif (defined $default_mode)
            {
                cxerr("'$mode_spec': the default mode can only be set once\n");
                $usage=2;
                last;
            }
            else
            {
                $default_mode=$mode;
            }
        }
        if (CXLog::is_on())
        {
            while (my ($mode, $regexp)=each %$hash_mode)
            {
                cxlog("Install mode for ($regexp) -> $mode\n");
            }
            if (defined $default_mode)
            {
                cxlog("Default install mode -> $default_mode\n");
            }
        }
    }
    if (defined $opt_create and !defined $opt_create_filter and
        !defined $opt_filter)
    {
        cxerr("you must use --filter or --create-filter with --create\n");
        $usage=2;
    }
    if (defined $opt_delete_filter and !defined $opt_delete)
    {
        cxerr("--delete-filter can only be used with --delete\n");
        $usage=2;
    }
    if (defined $opt_mode_filter and !defined $opt_mode)
    {
        cxerr("--mode-filter can only be used with --mode\n");
        $usage=2;
    }
    if (defined $opt_query_filter and !defined $opt_query)
    {
        cxerr("--query-filter can only be used with --query\n");
        $usage=2;
    }
    if (defined $opt_list_files_filter and !defined $opt_list_files)
    {
        cxerr("--list-files-filter can only be used with --list-files\n");
        $usage=2;
    }
    if (defined $opt_install_filter and !defined $opt_install)
    {
        cxerr("--install-filter can only be used with --install\n");
        $usage=2;
    }
    if (defined $opt_uninstall_filter and !defined $opt_uninstall)
    {
        cxerr("--uninstall-filter can only be used with --uninstall\n");
        $usage=2;
    }
    if (defined $opt_filter)
    {
        if (!defined $opt_create and !defined $opt_delete and
            !defined $opt_mode and !defined $opt_query and
            !defined $opt_list_files and
            !defined $opt_install and !defined $opt_uninstall)
        {
            cxerr("--filter can only be used with --create, --delete, --install, --mode, --query, --list-files and --uninstall\n");
            $usage=2;
        }
        if (defined $opt_create_filter or defined $opt_delete_filter or
            defined $opt_mode_filter or defined $opt_query_filter or
            defined $opt_list_files_filter or
            defined $opt_install_filter or defined $opt_uninstall_filter)
        {
            cxerr("--create-filter, --delete-filter, --mode-filter, --query-filter, --list-files-filter, --install-filter and --uninstall-filter are incompatible with --filter\n");
            $usage=2;
        }
        else
        {
            $opt_create_filter=$opt_delete_filter=$opt_mode_filter=$opt_query_filter=$opt_list_files_filter=$opt_install_filter=$opt_uninstall_filter=$opt_filter;
        }
    }
    if (defined $opt_plugin_dirs and !$opt_removeall)
    {
        cxerr("--plugin-dirs can only be used with the --removeall command\n");
        $usage=2;
    }
    if (defined $opt_ro_desktopdata and !defined $opt_install and
        !defined $opt_uninstall and !defined $opt_removeall)
    {
        cxerr("--ro-desktopdata must be used with one of --install, --uninstall or --removeall\n");
        $usage=2;
    }
    if (defined $opt_tag and $opt_tag eq "")
    {
        cxerr("specifying an empty tag is not allowed\n");
        $usage=2;
    }
    if (defined $opt_pattern)
    {
        if (!defined $opt_removeall)
        {
            cxerr("--pattern can only be used with --removeall\n");
            $usage=2;
        }
        if (length($opt_pattern) < 4)
        {
            cxerr("the pattern must be at least 4 characters long\n");
            $usage=2;
        }
    }
    if (defined $opt_scope)
    {
        $opt_scope=~ tr/A-Z/a-z/;
        if ($opt_scope !~ /^(?:managed|private)$/)
        {
            cxerr("unknown scope value '$opt_scope'\n");
            $usage=2;
        }
    }
    elsif (defined $opt_conf or ($only_removeall and defined $opt_pattern))
    {
        $opt_scope="private";
    }
}


# Determine and check wineprefix
my $cxconfig;
if (!defined $usage)
{
    require CXBottle;
    $cxconfig=CXBottle::get_crossover_config();

    $ENV{CX_BOTTLE}=$opt_bottle if (defined $opt_bottle);
    $ENV{CX_BOTTLE}="default" if (!defined $ENV{CX_BOTTLE} and !defined $opt_conf and !($only_removeall and defined $opt_pattern));
    if (defined $ENV{CX_BOTTLE})
    {
        my ($scope)=CXBottle::setup_bottle_wineprefix($opt_scope);
        if (!defined $scope)
        {
            cxerr($@);
            $usage=1;
        }
        else
        {
            CXBottle::setup_bottle_environment($cxconfig, $ENV{WINEPREFIX});
            if (!$read_only and
                CXBottle::get_bottle_mode($cxconfig, $opt_scope) eq "stub")
            {
                cxerr("cannot operate on the '$ENV{CX_BOTTLE}' stub bottle (missing --scope option?)\n");
                $usage=1;
            }
            $opt_scope=$scope;
        }
    }
    else
    {
        # Delete WINEPREFIX in case it is set in the user's environment
        delete $ENV{WINEPREFIX};
    }
}


# Print usage
if (defined $usage)
{
    my $name0=cxname0();
    if ($usage)
    {
        cxerr("try '$name0 --help' for more information\n");
        exit $usage;
    }
    print "Usage: $name0 --sync [--bottle BOTTLE] [--mode MODE] [--verbose]\n";
    print "or     $name0 [--bottle BOTTLE] [--install] [--uninstall] [--mode MODE]\n";
    print "                  [--removeall [--pattern PATTERN]] [--help]\n";
    print "                  [--create] [--delete] [--query] [--filter FILTER]\n";
    print "                  [advanced-options]\n";

    print "\n";
    print "Makes the Windows Netscape plugins available to native browsers and provides\n";
    print "a command-line interface for managing these plugins.\n";

    print "\n";
    print "Options:\n";
    print "  --sync      Scans the bottle's Windows Netscape plugins and updates the\n";
    print "              plugin list accordingly\n";
    print "              New plugins will receive the mode specified by the --mode\n";
    print "              option and will be installed if appropriate. Obsolete plugins\n";
    print "              will be uninstalled and deleted\n";
    print "    --sync-uninstall-none Instructs --sync not to uninstall and delete obsolete\n";
    print "                plugins\n";
    print "    --sync-install-none Instructs --sync not to (re)install modified or new\n";
    print "                plugins\n";
    print "  --bottle BOTTLE Use the specified bottle. If this option is not used,\n";
    print "              fallback to \$CX_BOTTLE and then to 'default'\n";
    print "  --mode MODE Sets the plugin install mode. If the install mode is 'install'\n";
    print "              the plugin will be installed by subsequent --install\n";
    print "              operations. If it is set to 'ignore' it will be ignored by such\n";
    print "              operations\n";
    print "              MODE can also be a semi-colon separated list of strings of the\n";
    print "              form 'MODE=REGEXP1:REGEXP2:...', where MODE is one of the above\n";
    print "              and applies to all plugins matching the specified regular\n";
    print "              expressions\n";
    print "    --mode-filter Specifies the plugins to set the mode on. See --filter\n";
    print "  --install   Exports the plugins so they can be used from the supported\n";
    print "              native browsers. This operation ignores plugins that have their\n";
    print "              install mode set to 'ignore'\n";
    print "    --install-filter Specifies the plugins to install. See --filter\n";
    print "  --uninstall Removes the plugins from the native browsers, even if their\n";
    print "              install mode is 'ignore'\n";
    print "    --uninstall-filter Specifies the plugins to uninstall. See --filter\n";
    print "  --removeall Each bottle has a unique id which is used to tag its plugins\n";
    print "              This option uninstalls any plugin bearing the bottle's id\n";
    print "    --pattern PATTERN Specifies that --removeall should uninstall any plugin\n";
    print "                that bears an id matching the specified regular expression\n";
    print "  --help, -h  Shows this help message\n";

    print "\n";
    print "Advanced options:\n";
    print "  --create    Creates the plugins specified by the --filter option\n";
    print "    --create-filter Specifies the plugins to create. See --filter\n";
    print "  --filter FILTER Specifies the plugins to operate on. FILTER is a\n";
    print "              colon-separated list of plugins, where each plugin is\n";
    print "              identified by its dll path. By default the operations apply\n";
    print "              to all known plugins\n";
    print "  --delete    Removes the plugins from the plugin list. Note that this does\n";
    print "              not uninstall them and does not delete the corresponding\n";
    print "              Windows dll\n";
    print "    --delete-filter Specifies the plugins to delete. See --filter\n";
    print "  --query     Outputs the list of known native plugin directories and then, for\n";
    print "              each plugin, the list of directories it is installed in\n";
    print "    --query-filter Specifies the plugins to query. See --filter\n";
    print "  --list-files Reports the files used by the browsers.\n";
    print "    --list-files-filter Restricts the browser files reporting to those";
    print "              of the specified plugins. See --filter\n";
    print "  --plugin-dirs DIRS Specifies a colon-separated list of native plugin\n";
    print "              directories. This overrides the '*NSPluginDirs' settings\n";
    print "  --utf8      Specifies that the script parameters are in UTF-8, regardless of\n";
    print "              the default system encoding\n";
    print "  --conf FILE Specifies an alternative file containing the plugin list\n";
    print "  --tag TAG   Specifies an alternative tag to be used for identifying the\n";
    print "              plugins\n";
    print "  --ro-desktopdata Treat the desktopdata / bottle directory as read-only\n";
    print "  --scope SCOPE If set to managed and given administrative rights, the\n";
    print "              plugins will be installed in a system-wide location. Otherwise\n";
    print "              they will be installed for use in the current account only. When\n";
    print "              operating on a bottle, this also specifies whether to look for a\n";
    print "              managed or a private bottle\n";
    print "  --verbose   Output more information about what is going on\n";
    exit 0;
}


# Because of user installs and builtin bottles the effective scope of the
# plugins may be different from the bottle (or other assumed) scope.
my $desktop_scope="private";
if ($opt_scope eq "managed")
{
    my $ref_dir=$ENV{WINEPREFIX} || $ENV{CX_ROOT};
    $desktop_scope="managed" if ((stat($ref_dir))[4] == 0);

    # Make sure the plugins are going to be world-readable
    umask(umask() & ~0055) if ($> == 0);
}

my $bottle_mode=$cxconfig->get("Bottle", "NSPluginMode", "");
$bottle_mode =~ tr/A-Z/a-z/;
$bottle_mode="ignore" if ($bottle_mode !~ /^(?:install|ignore|frozen)$/);
if ($bottle_mode eq "frozen" and !$read_only)
{
    cxerr("installing, uninstalling or modifying the bottle's Netscape plugins is forbidden because they are frozen\n");
    exit 1;
}
if (defined $ENV{WINEPREFIX})
{
    require CXBottle;
    if (!$only_removeall and
        !CXBottle::update_bottle($cxconfig, $ENV{WINEPREFIX}, $opt_scope))
    {
        cxerr("unable to update the '$ENV{CX_BOTTLE}' bottle\n");
        exit 1;
    }
    $opt_conf="$ENV{WINEPREFIX}/cxnsplugin.conf" if (!defined $opt_conf);
}
require CXRWConfig;
my $cxnsplugin=CXRWConfig->new($opt_conf) if (defined $opt_conf);
my $crossover=$cxconfig->get_section("CrossOver");


# Finally do the work
my $rc=0;
if ($opt_create)
{
    $default_mode="install" if (!$default_mode);
    foreach my $dllkey (@{get_filter_list($cxnsplugin, $opt_create_filter)})
    {
        my $section=$cxnsplugin->get_section($dllkey);
        if (!$section)
        {
            $section=$cxnsplugin->append_section($dllkey);
            cxlog("Added plugin '$dllkey'\n");
        }
        my $mode=get_install_mode($dllkey, $hash_mode, $default_mode);
        $section->set("Mode", $mode);
        cxlog("Set install mode of '$dllkey' to '$mode'\n");
    }
}
elsif ($opt_sync)
{
    if (!defined $ENV{WINEPREFIX})
    {
        cxerr("no bottle was specified for the --sync option\n");
        exit 1;
    }

    $default_mode="ignore" if (!$default_mode);
    my %to_delete;
    map { $to_delete{$_}=1; } $cxnsplugin->get_section_names();

    # Let pluginserver scan for the Windows plugin dlls
    my $start=CXLog::cxtime();
    my $cmd=shquote_string("$ENV{CX_ROOT}/bin/wine") .
            " --scope " . $opt_scope .
            " --wl-app pluginserver.exe  --scandlls |";
    require CXConfig;
    my $cxscan=CXConfig->new($cmd);
    cxlog("-> rc=$rc  (took ", CXLog::cxtime()-$start, " seconds)\n");
    if ($? != 0)
    {
        cxerr("an error occurred while scanning the Windows plugins\n");
        exit 1;
    }

    if ($bottle_mode ne "install" or $opt_install)
    {
        $opt_sync_install_none=1;
    }
    else
    {
        $opt_install_filter="";
    }
    my %scanned_dlls;
    foreach my $name (sort $cxscan->get_section_names())
    {
        my $scanned=$cxscan->get_section($name);
        # Pluginserver may return duplicate entries if a dll is present
        # in more than one place. For now we will only take the first one
        # into account.
        my $dll=cxbasename($name);
        next if ($scanned_dlls{$dll});
        $scanned_dlls{$dll}=1;

        my $dllkey=$name;
        $dllkey =~ s!$ENV{WINEPREFIX}/!!;
        $dllkey=mangle_string($dllkey);

        my ($modified, $mode);
        my $section=$cxnsplugin->get_section($dllkey);
        if ($section)
        {
            delete $to_delete{$dllkey};
        }
        else
        {
            cxlog("Adding '$dllkey'\n");
            $section=$cxnsplugin->append_section($dllkey);
            $mode=get_install_mode($dllkey, $hash_mode, $default_mode);
            $section->set("Mode", $mode);
            $modified=1;
        }
        $modified|=update_field($section, "Name", $scanned->get("Name"));
        $modified|=update_field($section, "Description", $scanned->get("Description"));
        $modified|=update_field($section, "MimeTypes", $scanned->get("MimeTypes"));
        $modified|=update_field($section, "Extensions", $scanned->get("Extensions"));
        $modified|=update_field($section, "OpenMasks", $scanned->get("OpenMasks"));
        if ($modified and !$opt_sync_install_none)
        {
            $mode=$section->get("Mode", "ignore") if (!defined $mode);
            $opt_install_filter.="$dllkey:" if ($mode ne "ignore");
        }
    }

    # Schedule obsolete plugins for deletion
    if (%to_delete)
    {
        $opt_delete_filter=join(":", keys %to_delete);
        $opt_delete=1;
        if ($bottle_mode eq "install" and !$opt_sync_uninstall_none and
            !$opt_uninstall and !$opt_removeall)
        {
            $opt_uninstall_filter=$opt_delete_filter;
            $opt_uninstall=1;
        }
    }
    if ($opt_install_filter)
    {
        $opt_install=1;
    }
}
elsif ($opt_mode)
{
    $default_mode="install" if (!$default_mode);
    foreach my $dllkey (@{get_filter_list($cxnsplugin, $opt_mode_filter)})
    {
        my $section=$cxnsplugin->get_section($dllkey);
        if ($section)
        {
            my $mode=get_install_mode($dllkey, $hash_mode, $default_mode);
            $section->set("Mode", $mode);
            cxlog("Set install mode of '$dllkey' to '$mode'\n");
        }
        else
        {
            cxerr("plugin dll '$dllkey' not found\n");
            $rc=1;
        }
    }
}

if (!defined $opt_tag and
    ($opt_query or $opt_list_files or $opt_install or $opt_uninstall or
     !($only_removeall and defined $opt_pattern))
   )
{
    if (defined $ENV{WINEPREFIX})
    {
        require CXBottle;
        $opt_tag=CXBottle::get_bottle_tag($cxconfig);
        if (!defined $opt_tag)
        {
            cxerr("unable to determine the tag of the $ENV{CX_BOTTLE} bottle\n");
            exit 1;
        }
    }
    else
    {
        my $name0=cxname0();
        cxerr("you must specify the tag identifying the plugins\n");
        cxerr("try '$name0 --help' for more information\n");
        exit 1;
    }
}
require CXBottle;
my $desktopdata_dir=CXBottle::get_desktopdata_dir($desktop_scope, $opt_tag);

if (defined $ENV{WINEPREFIX} and
    ($opt_install or $opt_uninstall or $opt_removeall) and
    !$opt_create and !defined $opt_install_filter and
    !defined $opt_uninstall_filter)
{
    my $mode=($opt_install ? "install" : "ignore");
    if ($mode ne $bottle_mode)
    {
        # Serialize cxbottle.conf modifications
        my $lock=CXBottle::lock_bottle($ENV{WINEPREFIX});

        my $cxbottle=CXRWConfig->new("$ENV{WINEPREFIX}/cxbottle.conf");
        $cxbottle->set("Bottle", "NSPluginMode", $mode);
        if (!$cxbottle->save())
        {
            cxerr("unable to save '$ENV{WINEPREFIX}/cxbottle.conf': $!\n");
            $rc=1;
        }

        CXUtils::cxunlock($lock);
    }
}

if ($opt_removeall)
{
    if ($opt_ro_desktopdata)
    {
        # Don't delete the desktopdata directory.
    }
    elsif ($desktopdata_dir)
    {
        foreach my $field (get_field_list($crossover, $desktop_scope))
        {
            my $arch=field_to_architecture($field);
            my $lib_dir=arch_to_lib_dir($desktopdata_dir, $arch);
            if (-d $lib_dir)
            {
                cxlog("Deleting the '$lib_dir' directory\n");
                require File::Path;
                if (!File::Path::rmtree($lib_dir))
                {
                    cxerr("unable to delete the '$lib_dir' directory: $!\n");
                    $rc=1;
                }
            }
        }
        CXUtils::garbage_collect_subdirs($desktopdata_dir, "/cxnsplugin", 1);
    }
    else
    {
        CXBottle::removeall_desktopdata_dirs($opt_pattern, "/cxnsplugin");
    }
    if (!defined $opt_plugin_dirs)
    {
        $opt_plugin_dirs="";
        if ($crossover)
        {
            foreach my $field (get_field_list($crossover, $desktop_scope))
            {
                my $dirs=expand_string($crossover->get($field));
                next if (!$dirs);
                $opt_plugin_dirs .= ":$dirs";
            }
        }
    }
    if (!defined $opt_pattern)
    {
        $opt_pattern=$opt_tag;
    }
    elsif ($opt_pattern eq "legacy")
    {
        # Don't use \w+ as it would risk matching non-CrossOver files
        $opt_pattern="(?:wine|cxoffice";
        if (CXUtils::get_product_id() ne "cxoffice")
        {
            $opt_pattern.="|" . CXUtils::get_product_id();
        }
        $opt_pattern.=")";
    }
    elsif ($opt_pattern !~ s/\$$//)
    {
        $opt_pattern .= ".*";
    }
    $opt_pattern="^np$opt_pattern\\..*\\.so\$";

    foreach my $dir (split /:+/, $opt_plugin_dirs)
    {
        $dir=expand_string($dir);
        next if ($dir eq "" or !-d $dir);
        CXUtils::delete_files($dir, $opt_pattern);
    }
}

if ($opt_uninstall and $crossover)
{
    foreach my $field (get_field_list($crossover, $desktop_scope))
    {
        my $dirs=expand_string($crossover->get($field));
        next if (!$dirs);

        my $arch=field_to_architecture($field);
        my $lib_dir=arch_to_lib_dir($desktopdata_dir, $arch);

        foreach my $dllkey (@{get_filter_list($cxnsplugin, $opt_uninstall_filter)})
        {
            # Delete the Unix plugin library
            my $filename="$lib_dir/$dllkey.so";
            if (!$opt_ro_desktopdata and -f $filename)
            {
                cxlog("Deleting '$filename'\n");
                if (!unlink $filename)
                {
                    cxerr("unable to delete '$filename': $!\n");
                    $rc=1;
                }
            }

            # Then the symbolic links to it
            my $dllname=get_dllname($dllkey);
            foreach my $dir (split /:+/, $dirs)
            {
                $dir=expand_string($dir);
                next if ($dir eq "" or !-d $dir);
                my $link="$dir/np$opt_tag.$arch.$dllname.so";
                if (-l $link)
                {
                    cxlog("Deleting '$link'\n");
                    if (!unlink $link)
                    {
                        cxerr("unable to delete '$link': $!\n");
                        $rc=1;
                    }
                }
            }
        }
    }
}

if ($opt_install and $crossover)
{
    my $productid=CXUtils::get_product_id();

    my %create;
    foreach my $dir (split /:+/, $crossover->get("CreateNSPluginDirs", ""))
    {
        $dir=expand_string($dir);
        next if ($dir eq "");
        $create{$dir}=1;
    }

    # For install we must first check that we have a template plugin library
    # for that architecture. If not, complain here _once_ and then ignore
    # that architecture.
    foreach my $field (get_field_list($crossover, $desktop_scope))
    {
        my $dirs=expand_string($crossover->get($field));
        next if (!$dirs);

        my $arch=field_to_architecture($field);
        my $template="$ENV{CX_ROOT}/lib/nsplugin-$arch.so";
        if (!-f $template)
        {
            cxwarn("'$template' does not exist, ignoring '$field'\n");
            next;
        }
        cxlog("Template plugin library for architecture '$field': '$template'\n");

        my $lib_dir=arch_to_lib_dir($desktopdata_dir, $arch);
        if (!cxmkpath($lib_dir))
        {
            cxerr("unable to create '$lib_dir': $@\n");
            $rc=1;
            next;
        }

        foreach my $dllkey (@{get_filter_list($cxnsplugin, $opt_install_filter)})
        {
            my $mode=$cxnsplugin->get($dllkey, "Mode", "");
            next if ($mode !~ /^install$/i);

            # Create the Unix plugin library
            my $filename="$lib_dir/$dllkey.so";
            if ($opt_ro_desktopdata and -f $filename)
            {
                # Assume this is our plugin
            }
            else
            {
                require File::Copy;
                if (!File::Copy::copy($template, $filename))
                {
                    cxerr("unable to create '$filename': $!\n");
                    $rc=1;
                    next;
                }
                my $dllpath=demangle_string($dllkey);
                if (cxsystem("$ENV{CX_ROOT}/bin/cxburner",
                             "set", $filename,
                             "CXRoot", $ENV{CX_ROOT},
                             "ProductID", $productid,
                             "PluginKey", $dllkey,
                             "PluginPath", $dllpath,
                             "CXBottle", $ENV{CX_BOTTLE},
                             "WinePrefix", $ENV{WINEPREFIX}))
                {
                    cxwarn("unable to initialize '$filename'\n");
                    unlink $filename;
                    $rc=1;
                    next;
                }
            }

            # Then create symbolic links to it
            my $dllname=get_dllname($dllkey);
            foreach my $dir (split /:+/, $dirs)
            {
                $dir=expand_string($dir);
                next if ($dir eq "");
                if (!-d $dir)
                {
                    next if (!$create{$dir});
                    if (!cxmkpath($dir))
                    {
                        cxwarn("unable to create the '$dir' directory: $!\n");
                        next;
                    }
                }

                my $link="$dir/np$opt_tag.$arch.$dllname.so";
                # Delete the old symlink in case it's broken
                unlink $link;
                if (!symlink($filename, $link))
                {
                    cxwarn("unable to symlink '$link' to '$filename': $!\n");
                    $rc=1;
                }
                else
                {
                    cxlog("Symlinked as '$link'\n");
                }
            }
        }
    }
}

if ($opt_query and $crossover)
{
    print "[.all]\n";
    foreach my $field (get_field_list($crossover, $desktop_scope))
    {
        my $dirs=expand_string($crossover->get($field));
        next if (!$dirs);
        $field =~ s/^(?:Managed|Private)//;
        my @query_dirs;
        foreach my $dir (split /:+/, $dirs)
        {
            $dir=expand_string($dir);
            next if ($dir eq "" or !-d $dir);
            push @query_dirs, $dir;
        }
        print "$field=", join(":", @query_dirs), "\n";
    }

    foreach my $dllkey (@{get_filter_list($cxnsplugin, $opt_query_filter)})
    {
        print "[$dllkey]\n";
        my $dllname=get_dllname($dllkey);
        foreach my $field (get_field_list($crossover, $desktop_scope))
        {
            my $dirs=expand_string($crossover->get($field));
            next if (!$dirs);

            my $arch=field_to_architecture($field);

            my @active_dirs;
            foreach my $dir (split /:+/, $dirs)
            {
                $dir=expand_string($dir);
                next if ($dir eq "" or !-d $dir);
                my $link="$dir/np$opt_tag.$arch.$dllname.so";
                push @active_dirs, $dir if (-l $link and -f $link);
            }
            $field =~ s/^(?:Managed|Private)//;
            print "$field=", join(":", @active_dirs), "\n";
        }
    }
}

if ($opt_list_files and $crossover)
{
    my $first=1;
    foreach my $field (get_field_list($crossover, $desktop_scope))
    {
        my $dirs=expand_string($crossover->get($field));
        next if (!$dirs);

        my %plugin_files;
        my $arch=field_to_architecture($field);
        my $lib_dir=arch_to_lib_dir($desktopdata_dir, $arch);
        foreach my $dllkey (@{get_filter_list($cxnsplugin, $opt_query_filter)})
        {
            my $filename="$lib_dir/$dllkey.so";
            $plugin_files{$filename}=1 if (-f $filename);

            foreach my $dir (split /:+/, $dirs)
            {
                $dir=expand_string($dir);
                next if ($dir eq "" or !-d $dir);

                my $dllname=get_dllname($dllkey);
                my $link="$dir/np$opt_tag.$arch.$dllname.so";
                $plugin_files{$link}=1 if (-l $link and -f $link);
            }
        }
        if (%plugin_files)
        {
            print "\n" if (!$first);
            $field =~ s/^(?:Managed|Private)//;
            $field =~ s/Dirs$/s/;
            print "[$field]\n";
            for my $file (sort keys %plugin_files)
            {
                $file =~ s%//+%/%g;
                print "\"", escape_string($file), "\"=\"1\"\n";
            }
            $first=undef;
        }
    }
}

if ($opt_delete)
{
    foreach my $dllkey (@{get_filter_list($cxnsplugin, $opt_delete_filter)})
    {
        cxlog("Removed plugin '$dllkey'\n");
        $cxnsplugin->remove_section($dllkey);
    }
}

if (defined $cxnsplugin and !$cxnsplugin->save())
{
    cxerr("unable to save '$opt_conf': $!\n");
}

exit $rc;