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

beebox / crossover   deb

Repository URL to install this package:

Version: 18.5.0-1 

/ opt / cxoffice / lib / perl / CXMenuDebian.pm

# (c) Copyright 2005-2008, 2010, 2014. CodeWeavers, Inc.
package CXMenuDebian;
use warnings;
use strict;
use CXLog;
use CXUtils;
use CXMenu;
use base "CXMenu";

sub detect($$$$)
{
    my ($class, $cxoptions, $cxconfig, $gui_info)=@_;
    return () if (!$gui_info->{debian_menu_on});

    my @selves;
    if ($gui_info->{debian_preferred_menu})
    {
        my $self={
            tag             => $cxoptions->{tag},
            destdir         => $cxoptions->{destdir},
            menu            => "$cxoptions->{destdir}$gui_info->{debian_preferred_menu}",
            old_menu        => "$cxoptions->{destdir}$gui_info->{debian_old_preferred_menu}",
        };
        bless $self, $class;
        push @selves, $self;
    }

    return @selves;
}

sub id($)
{
    my ($self)=@_;
    my $id="CXMenuDebian/$self->{menu}";
    $id =~ s%/+%/%g;
    return $id;
}

sub read_menu_file($)
{
    my ($self)=@_;

    return 1 if (defined $self->{filename});
    $self->{filename}="$self->{menu}/cxmenu-$self->{tag}";
    if ($self->{old_menu})
    {
        my $old_filename="$self->{old_menu}/cxmenu-$self->{tag}";
        if (-f $old_filename and !-f $self->{filename})
        {
            if (!cxmv($old_filename, $self->{filename}))
            {
                cxwarn("unable to rename '$old_filename' to '$self->{filename}': $!\n");
            }
            # Delete the parent directory if it's empty
            rmdir $self->{old_menu};
        }
    }
    $self->{lines}=[];
    $self->{menus}={};
    $self->{modified}=0;
    return 1 if (!-e $self->{filename});

    cxlog("Reading '$self->{filename}'\n");
    my $fh;
    if (!open($fh, "<", $self->{filename}))
    {
        cxerr("unable to open '$self->{filename}' for reading: $!\n");
        return 0;
    }

    my $count=0;
    foreach my $line (<$fh>)
    {
        chomp $line;
        push @{$self->{lines}}, $line;
        #cxlog(" line=[$line]\n");
        if ($line =~ /(?:\s|:)section=\"(\/(?:[^\\\"]*|\\.)*)\"\s+title=\"((?:[^\\\"]*|\\.)*)\"(?:\s|$)/)
        {
            my $dir=$1;
            my $name=$2;
            #cxlog("  1=[$dir] 2=[$name]\n");
            my $path=unescape_string($dir);
            $path.="/" if ($path !~ m%/$%);
            $path.=unescape_string($name);
            if ($line !~ /(?:\s|:)command=\"((?:[^\\\"]*|\\.)*)\"(?:\s|$)/)
            {
                # This would be a menu folder
                $path.="/";
            }
            $self->{menus}->{$path}=$count;
        }
        $count++;
    }
    close($fh);
    # For debug...
    # map { cxlog("  $_\n"); } keys %{$self->{menus}};

    return 1;
}

sub create_component($$$)
{
    my ($self, $dir, $component)=@_;

    if (!exists $self->{hacks})
    {
        $self->{hacks}={};
        if (CXUtils::file_grep("/etc/mandriva-release", "release\\s+2006\\.0[^0-9]"))
        {
            # Notes:
            # - Mandriva 2006.0 passes the '<', '>' and '&' characters
            #   straight to XDG's XML file which causes a parse error, and
            #   the user to lose all his menus.
            # - It also puts ';' straight into the folder's keyword which
            #   causes the loss of that folder as ';' is also the keyword
            #   separator in .desktop files.
            cxlog("Detected Mandriva 2006.0\n");
            $self->{hacks}->{badxdg}=1;
        }
    }

    my $path="$dir$component->{name}";
    $path.="/" if ($component->{is_dir});

    my $line=$self->{menus}->{$path};
    if (!defined $line or !$component->{intermediate})
    {
        cxlog("  creating path='$path'\n");
        my $name=$component->{name};
        if ($ENV{CX_TAGALL} and !$component->{is_dir})
        {
            my $creator=$self->id();
            $creator =~ s%/%-%g;
            $name.=" ($creator)";
        }

        # Notes:
        # - The specification says to use ASCII 7bit but in practice using
        #   UTF-8 work fine and lets accents go through. Furthermore, recent
        #   versions (at least on Mandrake >= 10.1) have a charset property.
        # - Double quotes and backslashes are allowed by the specification
        #   but some update-menus plugins (e.g. Enlightenment on Mandrake 8.2)
        #   are buggy and can be confused by double-quotes in the section
        #   field etc. Since this is plugin and distribution specific we
        #   ignore such issues.
        # - Backslashes must be quadrupled on all platforms. The current
        #   theory is that this is because escape sequences like '\t' are
        #   interpreted which we do not want.
        # - On a related note, complex escape sequences like \\\" are causing
        #   a global meltdown in  Mandrake 8.2 to 9.2. Quadrupling backslashes
        #   avoids this problem.
        # - No need to escape '$'s though some of the update-menus
        #   plugins (e.g. KDE) handle them incorrectly.
        $dir=~s%/$%% if ($dir ne "/");
        if ($self->{hacks}->{badxdg})
        {
            $dir =~ s/[<>&;]//g;
            $name =~ s/[<>&;]//g if ($component->{is_dir});
        }
        my $str="?package(local." . CXUtils::get_product_id() .
                "): charset=\"utf8\" needs=\"x11\" section=\"" .
                escape_string($dir) . "\" title=\"" . escape_string($name) .
                "\"";
        if (($component->{description} || "") ne "")
        {
            $str.=" longtitle=\"" .
                  escape_string($component->{description}) . "\"";
        }
        if (($component->{command} || "") ne "")
        {
            my $cmd=$component->{command};
            # We must quadruple backslashes!
            $cmd =~ s%\\%\\\\\\\\%g;
            # But double quotes are escaped normally
            $cmd =~ s%\"%\\\"%g;
            $str.=" command=\"$cmd\"";
        }
        my $icon=CXMenu::get_unix_icon_path($component) ||
                 CXMenu::get_default_icon_path($component->{is_dir});
        $str.=" icon=\"" . escape_string($icon) . "\"";
        # Note: Debian menus cannot be localized

        if (defined $line)
        {
            $self->{lines}->[$line]=$str;
        }
        else
        {
            $self->{menus}->{$path}=@{$self->{lines}};
            push @{$self->{lines}}, $str;
        }
    }

    return $path;
}

sub install($$)
{
    my ($self, $components)=@_;

    # We don't support desktop icons
    my $menu=$components->[-1];
    return 1 if ($menu->{is_desktop});
    return 0 if (!$self->read_menu_file());

    # Note: Some update-menus plugins (e.g. the KDE one) don't take
    # advantage of the menu folder entries we create but some do
    # (e.g. the fvwm95 one or the KDE one on Mandrake). So we should
    # still create and specify icons for each intermediate menu folder.
    my $path="/";
    foreach my $component (@$components)
    {
        $path=$self->create_component($path, $component);
    }
    if ($menu->{is_dir})
    {
        # Make sure our brand new (maybe empty) folder will not be
        # 'garbage collected' by finalize()
        $self->{status}->{$menu->{path}}="in-use";
    }
    else
    {
        $self->{status}->{$menu->{dir}}="in-use";
    }
    $self->{modified}=1;
    return 1;
}

sub query($$)
{
    my ($self, $components)=@_;
    return ($self->id(), "") if (!defined $components);

    # We don't support desktop icons
    my $menu=$components->[-1];
    return "" if ($menu->{is_desktop});
    return 0 if (!$self->read_menu_file());

    my $path=$menu->{path};
    return $self->id() if (defined $self->{menus}->{$menu->{path}});
    return "";
}

sub get_files($$)
{
    my ($self, $components)=@_;
    my $filename="$self->{menu}/cxmenu-$self->{tag}";
    return -f $filename ? [$filename] : [];
}

sub uninstall($$)
{
    my ($self, $components)=@_;

    # We don't support desktop icons
    my $menu=$components->[-1];
    return 1 if ($menu->{is_desktop});
    return 0 if (!$self->read_menu_file());

    # Always mark the parent folder for garbage collection
    $self->{garbage_collect}->{$menu->{dir}}=1;

    # Note: We don't remove lines from the array so we don't upset the
    # line numbers
    if ($menu->{is_dir})
    {
        cxlog("folder '$menu->{path}'\n");
        while (my ($item, $line)=each %{$self->{menus}})
        {
            next if ($item !~ /^\Q$menu->{path}\E/);
            cxlog(" removing line $line '$item'\n");
            $self->{lines}->[$line]=undef;
            delete $self->{menus}->{$item};
            $self->{modified}=1;
        }
    }
    else
    {
        cxlog("menu '$menu->{path}'\n");
        my $line=$self->{menus}->{$menu->{path}};
        if (defined $line)
        {
            cxlog(" removing line $line\n");
            $self->{lines}->[$line]=undef;
            delete $self->{menus}->{$menu->{path}};
            $self->{modified}=1;
        }
    }

    return 1;
}

# This function assumes that it is being called before any of the others
# which means it does not have to clean up 'filename', 'menus', etc.
sub removeall($$)
{
    my ($self, $pattern)=@_;

    if ($pattern eq "legacy")
    {
        $pattern="^" . CXUtils::get_product_id() . "(?:-[0-9a-f]+-[0-9a-f]+)?\$";
    }
    else
    {
        $pattern="^cxmenu-$pattern";
    }
    if (CXUtils::delete_files($self->{menu}, $pattern) > 0)
    {
        # Delete the parent directory if it's empty
        rmdir $self->{menu};
        $self->{update}=1;
    }
    if ($self->{old_menu} and CXUtils::delete_files($self->{old_menu}, $pattern) > 0)
    {
        # Delete the parent directory if it's empty
        rmdir $self->{old_menu};
        $self->{update}=1;
    }

    return 1;
}

sub gc_get_folder_status($$)
{
    my ($self, $path)=@_;

    cxlog("menus=[",join(":", keys %{$self->{menus}}),"]\n");
    return "in-use" if (grep m%^\Q$path/\E.$%, keys %{$self->{menus}});
    return "empty";
}

sub gc_delete_folder($$$)
{
    my ($self, $path)=@_;

    cxlog("deleting the '$path' directory\n");
    my $line=delete $self->{menus}->{"$path/"};
    if (defined $line)
    {
        cxlog(" 274 -> removing line $line\n");
        $self->{lines}->[$line]=undef;
        $self->{modified}=1;
    }
}

sub finalize($)
{
    my ($self)=@_;

    $self->SUPER::finalize();

    if ($self->{modified})
    {
        my $empty=1;
        foreach my $line (@{$self->{lines}})
        {
            if (defined $line)
            {
                $empty=0;
                last;
            }
        }
        if ($empty)
        {
            cxlog("CXMenuDebian deleting empty '$self->{filename}' file\n");
            # Delete the menu file
            if (-e $self->{filename} and !unlink $self->{filename})
            {
                cxerr("unable to delete '$self->{filename}': $!\n");
                return 0;
            }
            # Delete the parent directory if it's empty
            rmdir $self->{menu};
        }
        else
        {
            # Save the menu file
            if (!cxmkpath($self->{menu}))
            {
                cxerr("unable to create the '$self->{menu}' directory: $@\n");
                return 0;
            }
            my $fh;
            if (!open($fh, ">", $self->{filename}))
            {
                cxerr("unable to open '$self->{filename}' for writing: $!\n");
                return 0;
            }
            cxlog("CXMenuDebian writing to '$self->{filename}'\n");
            foreach my $line (@{$self->{lines}})
            {
                next if (!defined $line);
                print $fh "$line\n";
            }
            close($fh);
        }
        $self->{update}=1;
    }
    if ($self->{update} and !$self->{destdir})
    {
        # Make sure we only run one instance at a time (just to be safe).
        # See also CXAssocMandrake.pm.
        my $lock=CXUtils::cxlock("update-menus");
        cxsystem("update-menus");
        CXUtils::cxunlock($lock);
    }
    return 1;
}

return 1;