+++ /dev/null
-# menu format -- lintian check script -*- perl -*-
-
-# Copyright (C) 1998 by Joey Hess
-#
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation; either version 2 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program. If not, you can find it on the World Wide
-# Web at http://www.gnu.org/copyleft/gpl.html, or write to the Free
-# Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
-# MA 02110-1301, USA.
-
-# This script also checks desktop entries, since they share quite a bit of
-# code. At some point, it would make sense to try to refactor this so that
-# shared code is in libraries.
-#
-# Further things that the desktop file validation should be checking:
-#
-# - Encoding of the file should be UTF-8.
-# - Additional Categories should be associated with Main Categories.
-# - List entries (MimeType, Categories) should end with a semicolon.
-# - Check for GNOME/GTK/X11/etc. dependencies and require the relevant
-# Additional Category to be present.
-# - Check all the escape characters supported by Exec.
-# - Review desktop-file-validate to see what else we're missing.
-
-package Lintian::menu_format;
-use strict;
-use Tags;
-use Util;
-use File::Basename;
-
-# This is a list of all tags that should be in every menu item.
-my @req_tags=qw(needs section title command);
-
-# This is a list of all known tags.
-my @known_tags=qw(
- needs
- section
- title
- sort
- command
- longtitle
- icon
- icon16x16
- icon32x32
- description
- hotkey
- hints
- );
-
-# These 'needs' tags are always valid, no matter the context, and no other
-# values are valid outside the Window Managers context (don't include wm here,
-# in other words). It's case insensitive, use lower case here.
-my @needs_tag_vals=qw(x11 text vc);
-
-# Authorative source of menu sections:
-# http://www.debian.org/doc/packaging-manuals/menu-policy/ch2#s2.1
-
-# This is a list of all valid section on the root menu.
-my @root_sections = ('Applications', 'Games', 'Help', 'Screen',
- 'Window Managers', 'FVWM Modules', 'Window Maker');
-
-# This is a list of all valid sections a menu item or submenu can go in.
-my @sections = ('Applications/Accessibility',
- 'Applications/Amateur Radio',
- 'Applications/Data Management',
- 'Applications/Editors',
- 'Applications/Education',
- 'Applications/Emulators',
- 'Applications/File Management',
- 'Applications/Graphics',
- 'Applications/Mobile Devices',
- 'Applications/Network/Communication',
- 'Applications/Network/File Transfer',
- 'Applications/Network/Monitoring',
- 'Applications/Network/Web Browsing',
- 'Applications/Network/Web News',
- 'Applications/Office',
- 'Applications/Programming',
- 'Applications/Project Management',
- 'Applications/Science/Astronomy',
- 'Applications/Science/Biology',
- 'Applications/Science/Chemistry',
- 'Applications/Science/Data Analysis',
- 'Applications/Science/Electronics',
- 'Applications/Science/Engineering',
- 'Applications/Science/Geoscience',
- 'Applications/Science/Mathematics',
- 'Applications/Science/Medicine',
- 'Applications/Science/Physics',
- 'Applications/Science/Social',
- 'Applications/Shells',
- 'Applications/Sound',
- 'Applications/System/Administration',
- 'Applications/System/Hardware',
- 'Applications/System/Language Environment',
- 'Applications/System/Monitoring',
- 'Applications/System/Package Management',
- 'Applications/System/Security',
- 'Applications/Terminal Emulators',
- 'Applications/Text',
- 'Applications/TV and Radio',
- 'Applications/Video',
- 'Applications/Viewers',
- 'Applications/Web Development',
- 'Games/Action',
- 'Games/Adventure',
- 'Games/Blocks',
- 'Games/Board',
- 'Games/Card',
- 'Games/Puzzles',
- 'Games/Simulation',
- 'Games/Strategy',
- 'Games/Tools',
- 'Games/Toys',
- 'Help',
- 'Screen/Saving',
- 'Screen/Locking',
- 'Window Managers',
- 'FVWM Modules',
- 'Window Maker'
- );
-
-# Authorative source of desktop keys:
-# http://standards.freedesktop.org/desktop-entry-spec/1.0/
-#
-# This is a list of all keys that should be in every desktop entry.
-my @req_desktop_keys = qw(Type Name);
-
-# This is a list of all known keys.
-my %known_desktop_keys = map { $_ => 1 }
- qw(
- Type
- Version
- Name
- GenericName
- NoDisplay
- Comment
- Icon
- Hidden
- OnlyShowIn
- NotShowIn
- TryExec
- Exec
- Path
- Terminal
- MimeType
- Categories
- MimeType
- Categories
- StartupNotify
- StartupWMClass
- URL
- );
-
-my %deprecated_desktop_keys = map { $_ => 1 }
- qw(
- Encoding
- MiniIcon
- TerminalOptions
- Protocols
- Extensions
- BinaryPattern
- MapNotify
- SwallowTitle
- SwallowExec
- SortOrder
- FilePattern
- );
-
-# KDE uses some additional keys that should start with X-KDE but don't for
-# historical reasons. Actions will in theory be in a later version of the
-# standard (it's not mentioned in the current standard, but is implemented by
-# KDE and widely used).
-my %kde_desktop_keys = map { $_ => 1 }
- qw(
- ServiceTypes
- DocPath
- Keywords
- InitialPreference
- Dev
- FSType
- MountPoint
- ReadOnly
- UnmountIcon
- Actions
- );
-
-# Known types of desktop entries.
-# http://standards.freedesktop.org/desktop-entry-spec/1.0/ar01s05.html
-my %known_desktop_types = map { $_ => 1 }
- qw(
- Application
- Link
- Directory
- );
-
-# Authorative source of desktop categories:
-# http://standards.freedesktop.org/menu-spec/1.0/apa.html
-
-# This is a list of all Main Categories for .desktop files. Application is
-# added as an exception; it's not listed in the standard, but it's widely used
-# and used as an example in the GNOME documentation. GNUstep is added as an
-# exception since it's used by GNUstep packages.
-my %main_categories = map { $_ => 1 }
- qw(
- AudioVideo
- Audio
- Video
- Development
- Education
- Game
- Graphics
- Network
- Office
- Settings
- System
- Utility
- Application
- GNUstep
- );
-
-# This is a list of all Additional Categories for .desktop files. Ideally we
-# should be checking to be sure the associated Main Categories are present,
-# but we don't have support for that yet.
-my %categories = map { $_ => 1 }
- qw(
- Building
- Debugger
- IDE
- GUIDesigner
- Profiling
- RevisionControl
- Translation
- Calendar
- ContactManagement
- Database
- Dictionary
- Chart
- Email
- Finance
- FlowChart
- PDA
- ProjectManagement
- Presentation
- Spreadsheet
- WordProcessor
- 2DGraphics
- VectorGraphics
- RasterGraphics
- 3DGraphics
- Scanning
- OCR
- Photography
- Publishing
- Viewer
- TextTools
- DesktopSettings
- HardwareSettings
- Printing
- PackageManager
- Dialup
- InstantMessaging
- Chat
- IRCClient
- FileTransfer
- HamRadio
- News
- P2P
- RemoteAccess
- Telephony
- TelephonyTools
- VideoConference
- WebBrowser
- WebDevelopment
- Midi
- Mixer
- Sequencer
- Tuner
- TV
- AudioVideoEditing
- Player
- Recorder
- DiscBurning
- ActionGame
- AdventureGame
- ArcadeGame
- BoardGame
- BlocksGame
- CardGame
- KidsGame
- LogicGame
- RolePlaying
- Simulation
- SportsGame
- StrategyGame
- Art
- Construction
- Music
- Languages
- Science
- ArtificialIntelligence
- Astronomy
- Biology
- Chemistry
- ComputerScience
- DataVisualization
- Economy
- Electricity
- Geography
- Geology
- Geoscience
- History
- ImageProcessing
- Literature
- Math
- NumericalAnalysis
- MedicalSoftware
- Physics
- Robotics
- Sports
- ParallelComputing
- Amusement
- Archiving
- Compression
- Electronics
- Emulator
- Engineering
- FileTools
- FileManager
- TerminalEmulator
- Filesystem
- Monitor
- Security
- Accessibility
- Calculator
- Clock
- TextEditor
- Documentation
- Core
- KDE
- GNOME
- GTK
- Qt
- Motif
- Java
- ConsoleOnly
- );
-
-# This is a list of Reserved Categories for .desktop files. To use one of
-# these, the desktop entry must also have an OnlyShowIn key limiting the
-# environment to one that supports this category.
-my %reserved_categories = map { $_ => 1 }
- qw(
- Screensaver
- TrayIcon
- Applet
- Shell
- );
-
-# Path in which to search for binaries referenced in menu entries.
-my @path = qw(/usr/local/bin/ /usr/bin/ /bin/ /usr/X11R6/bin/ /usr/games/);
-
-my %known_tags_hash = map { $_ => 1 } @known_tags;
-my %needs_tag_vals_hash = map { $_ => 1 } @needs_tag_vals;
-my %root_sections_hash = map { $_ => 1 } @root_sections;
-my %sections_hash = map { $_ => 1 } @sections;
-
-# Holds a hash of all files in the package, used for checking for executables.
-my %file_index;
-
-# -----------------------------------
-
-sub run {
-
-my $pkg = shift;
-my $type = shift;
-
-my @menufiles;
-opendir (MENUDIR, "menu/lib") or fail("cannot read menu/lib file directory.");
-push @menufiles, map { "menu/lib/$_" } readdir(MENUDIR);
-closedir MENUDIR;
-opendir (MENUDIR, "menu/share") or fail("cannot read menu/share file directory.");
-push @menufiles, map { "menu/share/$_" } readdir(MENUDIR);
-closedir MENUDIR;
-
-# Find the desktop files in the package for verification and also build a hash
-# of every file in the package to use to verify that the command referenced by
-# a menu item or desktop entry is there.
-my @desktop_files;
-open(IN, '<', "index") or fail("cannot open index file index: $!");
-while (<IN>) {
- chomp;
- my ($perm, $owner, $size, $date, $time, $file) = split(' ', $_, 6);
- $file =~ s,^\./,/,;
- $file =~ s/ link to .*//;
- $file =~ s/ -> .*//;
- my $operm = perm2oct($perm);
- $file_index{$file} = 1;
-
- if ($perm =~ m,^-, && $file =~ m,/usr/share/applications/.*\.desktop$,) {
- if ($perm =~ m,x,o) {
- tag "executable-desktop-file", sprintf("$file %04o",$operm);
- }
- unless (m,template,) {
- push (@desktop_files, $file);
- }
- }
-}
-close IN;
-
-# Verify all the desktop files.
-for my $desktop_file (@desktop_files) {
- VerifyDesktopFile ($desktop_file, $desktop_file, $pkg);
-}
-
-# Now all the menu files.
-foreach my $menufile (@menufiles) {
- next if -x $menufile; # don't try to parse executables
-
- my $basename = basename $menufile;
- my $fullname = "/usr/share/menu/$basename";
- $fullname = "/usr/lib/menu/$basename" if $menufile =~ m,^menu/lib/,o;
-
- next if $basename eq "README"; # README is a special case
-
- my $menufile_line ="";
- open (IN, '<', $menufile) or
- fail("cannot open menu file $menufile for reading.");
- # line below is commented out in favour of the while loop
- # do { $_=<IN>; } while defined && (m/^\s* \#/ || m/^\s*$/);
- while (<IN>) {
- if (m/^\s*\#/ || m/^\s*$/) {
- next;
- } else {
- $menufile_line = $_;
- last;
- }
- }
-
- # Check first line of file to see if it matches the old menu file format.
- if ($menufile_line =~ m/^(?!\?package\(.*\)).* .* .* .* "?.*"? .*$/o) {
- tag "old-format-menu-file", $fullname;
- close IN;
- next;
- } elsif ($menufile_line =~ m/^!C\s*menu-2/o) {
- # we can't parse that yet
- close IN;
- next;
- }
-
- # Parse entire file as a new format menu file.
- my $line="";
- my $lc=0;
- do {
- $lc++;
-
- # Ignore lines that are comments.
- if ($menufile_line =~ m/^\s*\#/o) {
- next;
- }
- $line .= $menufile_line;
- # Note that I allow whitespace after the continuation character.
- # This is caught by VerifyLine().
- if (! ($menufile_line =~ m/\\\s*?$/)) {
- VerifyLine($pkg,$type,$menufile,$fullname,$line,$lc);
- $line="";
- }
- } while ($menufile_line = <IN>);
- VerifyLine($pkg,$type,$menufile,$fullname,$line,$lc);
-
- close IN;
-}
-
-}
-
-# -----------------------------------
-
-# Pass this a line of a menu file, it sanitizes it and
-# verifies that it is correct.
-sub VerifyLine {
- my ( $pkg, $type, $menufile, $fullname, $line, $linecount ) = @_;
-
- my %vals;
-
- chomp $line;
-
- # Replace all line continuation characters with whitespace.
- # (do not remove them completely, because update-menus doesn't)
- $line =~ s/\\\n/ /mgo;
-
- # This is in here to fix a common mistake: whitespace after a '\'
- # character.
- if ($line =~ s/\\\s+\n/ /mgo) {
- tag "whitespace-after-continuation-character", "$fullname:$linecount";
- }
-
- # Ignore lines that are all whitespace or empty.
- return if $line =~ m/^\s+$/o or ! $line;
-
- # Ignore lines that are comments.
- return if $line =~ m/^\s*\#/o;
-
- # Start by testing the package check.
- if (not $line =~ m/^\?package\((.*?)\):/o) {
- tag "bad-test-in-menu-item", "$fullname:$linecount";
- return;
- }
- my $pkg_test = $1;
- my %tested_packages = map { $_ => 1 } split( /\s*,\s*/, $pkg_test);
- my $tested_packages = scalar keys %tested_packages;
- unless (exists $tested_packages{$pkg}) {
- tag "pkg-not-in-package-test", "$pkg_test $fullname";
- }
- $line =~ s/^\?package\(.*?\)://;
-
- # Now collect all the tag=value pairs. I've heavily commented
- # the killer regexp that's responsible.
- #
- # The basic idea here is we start at the beginning of the line.
- # Each loop pulls off one tag=value pair and advances to the next
- # when we have no more matches, there should be no text left on
- # the line - if there is, it's a parse error.
- while ($line =~ m/
- \s*? # allow whitespace between pairs
- ( # capture what follows in $1, it's our tag
- [^\"\s=] # a non-quote, non-whitespace, character
- * # match as many as we can
- )
- =
- ( # capture what follows in $2, it's our value
- (?:
- \" # this is a quoted string
- (?:
- \\. # any quoted character
- | # or
- [^\"] # a non-quote character
- )
- * # repeat as many times as possible
- \" # end of the quoted value string
- )
- | # the other possibility is a non-quoted string
- (?:
- [^\"\s] # a non-quote, non-whitespace character
- * # match as many times as we can
- )
- )
- /ogcx) {
- my $tag = $1;
- my $value = $2;
-
- if (exists $vals{$tag}) {
- tag "duplicated-tag-in-menu-item", "$fullname $1:$linecount";
- }
-
- # If the value was quoted, remove those quotes.
- if ($value =~ m/^\"(.*)\"$/) {
- $value = $1;
- } else {
- tag "unquoted-string-in-menu-item", "$fullname $1:$linecount";
- }
-
- # If the value has escaped characters, remove the
- # escapes.
- $value =~ s/\\(.)/$1/g;
-
- $vals{$tag} = $value;
- }
-
- # This is not really a no-op. Note the use of the /c
- # switch - this makes perl keep track of the current
- # search position. Notice, we did it above in the loop,
- # too. (I have a /g here just so the /c takes affect.)
- # We use this below when we look at how far along in the
- # string we matched. So the point of this line is to allow
- # trailing whitespace on the end of a line.
- $line =~ m/\s*/ogc;
-
- # If that loop didn't match up to end of line, we have a
- # problem..
- if (pos($line) < length($line)) {
- tag "unparsable-menu-item", "$fullname:$linecount";
- # Give up now, before things just blow up in our face.
- return;
- }
-
- # Now validate the data in the menu file.
-
- # Test for important tags.
- foreach my $tag (@req_tags) {
- unless ( exists($vals{$tag}) && defined($vals{$tag}) ) {
- tag "menu-item-missing-required-tag", "$tag $fullname:$linecount";
- # Just give up right away, if such an essential tag is missing,
- # chance is high the rest doesn't make sense either. And now all
- # following checks can assume those tags to be there
- return;
- }
- }
-
- # Make sure all tags are known.
- foreach my $tag (keys %vals) {
- if (! $known_tags_hash{$tag}) {
- tag "menu-item-contains-unknown-tag", "$tag $fullname:$linecount";
- }
- }
-
- # Sanitize the section tag
- my $section = $vals{'section'};
- $section =~ tr:/:/:s; # eliminate duplicate slashes.
- $section =~ s:/$::; # remove trailing slash.
-
- # Be sure the command is provided by the package.
- my ($okay, $command) = VerifyCmd ($fullname, $linecount, $vals{'command'}, $pkg);
- tag "menu-command-not-in-package", "$fullname:$linecount $command"
- unless ($okay
- or not $command
- or ($tested_packages >= 2)
- or ($section =~ m:^(WindowManagers/Modules|FVWM Modules|Window Maker):));
-
- if (exists($vals{'icon'})) {
- VerifyIcon($menufile, $fullname, $linecount, $vals{'icon'}, 32);
- }
- if (exists($vals{'icon32x32'})) {
- VerifyIcon($menufile, $fullname, $linecount, $vals{'icon32x32'}, 32);
- }
- if (exists($vals{'icon16x16'})) {
- VerifyIcon($menufile, $fullname, $linecount, $vals{'icon16x16'}, 16);
- }
-
- # Check the needs tag.
- my $needs = lc($vals{'needs'}); # needs is case insensitive.
-
- if ($section =~ m:^(WindowManagers/Modules|FVWM Modules|Window Maker):) {
- # WM/Modules: needs must not be the regular ones nor wm
- if ($needs_tag_vals_hash{$needs} or $needs eq "wm") {
- tag "non-wm-module-in-wm-modules-menu-section", "$needs $fullname:$linecount";
- }
- } elsif ($section =~ m:^Window ?Managers:) {
- # Other WM sections: needs must be wm
- if ($needs ne 'wm') {
- tag "non-wm-in-windowmanager-menu-section", "$needs $fullname:$linecount";
- }
- } else {
- # Any other section: just only the general ones
- if ($needs eq "dwww") {
- tag "menu-item-needs-dwww", "$fullname:$linecount";
- } elsif (not $needs_tag_vals_hash{$needs}) {
- tag "menu-item-needs-tag-has-unknown-value", "$needs $fullname:$linecount";
- }
- }
-
- # Check the section tag
- # Check for historical changes in the section tree.
- if ($section =~ m:^Apps/Games:) {
- tag "menu-item-uses-apps-games-section", "$fullname:$linecount";
- $section =~ s:^Apps/::;
- }
- if ($section =~ m:^Apps/:) {
- tag "menu-item-uses-apps-section", "$fullname:$linecount";
- $section =~ s:^Apps/:Applications/:;
- }
- if ($section =~ m:^WindowManagers:) {
- tag "menu-item-uses-windowmanagers-section", "$fullname:$linecount";
- $section =~ s:^WindowManagers:Window Managers:;
- }
-
- # Check for Evil new root sections.
- my ($rootsection) = $section =~ m:([^/]*):;
- if (not $root_sections_hash{$rootsection}) {
- if (not $rootsection =~ m/$pkg/i) {
- tag "menu-item-creates-new-root-section", "$rootsection $fullname:$linecount";
- }
- } else {
- if (not $sections_hash{$section}) {
- tag "menu-item-creates-new-section", "$vals{section} $fullname:$linecount";
- }
- }
-}
-
-
-sub VerifyIcon {
- my ($menufile, $fullname, $linecount, $icon, $size) = @_;
- local *IN;
-
- if ($icon eq 'none') {
- tag "menu-item-uses-icon-none", "$fullname:$linecount";
- return;
- }
-
- if (not ($icon =~ m/\.xpm$/i)) {
- tag "menu-icon-not-in-xpm-format", "$icon";
- return;
- }
-
- # Try the explicit location, and if that fails, try the standard path.
- my $iconfile = "unpacked/$icon";
- if (! -f $iconfile) {
- $iconfile = "unpacked/usr/share/pixmaps/$icon";
- }
-
- if (! open (IN, '<', $iconfile)) {
- tag "menu-icon-missing", "$icon";
- return;
- }
-
- my $parse = "XPM header";
- my $line;
- do { defined ($line = <IN>) or goto parse_error; }
- until ($line =~ /\/\*\s*XPM\s*\*\//);
-
- $parse = "size line";
- do { defined ($line = <IN>) or goto parse_error; }
- until ($line =~ /"\s*([0-9]+)\s*([0-9]+)\s*([0-9]+)\s*([0-9]+)\s*"/);
- my $width = $1 + 0;
- my $height = $2 + 0;
- my $numcolours = $3 + 0;
- my $cpp = $4 + 0;
-
- if ($width > $size || $height > $size) {
- tag "menu-icon-too-big", "$icon: ${width}x${height} > ${size}x${size}";
- }
-
- close IN or die;
- return;
-
-parse_error:
- close IN or die;
- tag "menu-icon-cannot-be-parsed", "$icon: looking for $parse";
- return;
-}
-
-
-# Syntax-checks a .desktop file.
-sub VerifyDesktopFile {
- my ($desktopfile, $file, $pkg) = @_;
- my %vals;
- open (DESKTOP, '<', "unpacked/$file")
- or fail("cannot open desktop file $file: $!");
- my ($line, $saw_first, $warned_cr);
- my @pending;
- while (defined ($line = <DESKTOP>)) {
- chomp $line;
- next if ($line =~ m/^\s*\#/ or $line =~ m/^\s*$/);
- if ($line =~ s/\r//) {
- tag 'desktop-entry-file-has-crs', "$file:$." unless $warned_cr;
- $warned_cr = 1;
- }
-
- # Err on the side of caution for now. If the first non-comment line
- # is not the required [Desktop Entry] group, ignore this file. Also
- # ignore any keys in other groups.
- last if ($saw_first and $line =~ /^\[(.*)\]\s*$/);
- unless ($saw_first) {
- return unless $line =~ /^\[Desktop Entry\]\s*$/;
- $saw_first = 1;
- }
-
- # Tag = Value. For most errors, just add the error to pending rather
- # than warning on it immediately since we want to not warn on tag
- # errors if we didn't know the file type.
- #
- # TODO: We do not check for properly formatted localised values for
- # keys but might be worth checking if they are properly formatted (not
- # their value)
- if ($line =~ /^(.*?)\s*=\s*(.*)$/) {
- my ($tag, $value) = ($1, $2);
- my $basetag = $tag;
- my ($encoding) = ($basetag =~ s/\[([^\]]+)\]$//);
- if (exists $vals{$tag}) {
- tag "duplicated-key-in-desktop-entry", "$file:$. $tag";
- } elsif ($deprecated_desktop_keys{$basetag}) {
- if ($basetag eq 'Encoding') {
- push (@pending, [ "desktop-entry-contains-encoding-key", "$file:$. $tag" ]);
- } else {
- push (@pending, [ "desktop-entry-contains-deprecated-key", "$file:$. $tag" ]);
- }
- } elsif ( not $known_desktop_keys{$basetag}
- and not $kde_desktop_keys{$basetag}
- and not $basetag =~ /^X-/) {
- push (@pending, [ "desktop-entry-contains-unknown-key", "$file:$. $tag" ]);
- }
- $vals{$tag} = $value;
- }
- }
- close DESKTOP;
-
- # Now validate the data in the desktop file, but only if it's a known type.
- return unless ($vals{'Type'} and $known_desktop_types{$vals{'Type'}});
-
- # Now we can issue any pending tags.
- for my $pending (@pending) {
- tag @$pending;
- }
-
- # Test for important keys.
- for my $tag (@req_desktop_keys) {
- unless (defined $vals{$tag}) {
- tag "desktop-entry-missing-required-key", "$file $tag";
- }
- }
-
- # Only test whether the binary is in the package if the desktop file is
- # directly under /usr/share/applications. Too many applications use
- # desktop files for other purposes with custom paths.
- #
- # TODO: Should check quoting and the check special field
- # codes in Exec for desktop files.
- if ($file =~ m,^/usr/share/applications/, and $vals{'Exec'} and $vals{'Exec'} =~ /\S/) {
- my ($okay, $command) = VerifyCmd ($file, undef, $vals{'Exec'}, $pkg);
- tag "desktop-command-not-in-package", "$file $command"
- unless $okay or $command eq 'kcmshell';
- }
-
- # Check the Category tag.
- if (defined $vals{'Categories'}) {
- my @cats = split (';', $vals{'Categories'});
- my $saw_main;
- for my $cat (@cats) {
- next if $cat =~ /^X-/;
- if ($reserved_categories{$cat}) {
- tag "desktop-entry-uses-reserved-category", "$cat $file"
- unless $vals{'OnlyShowIn'};
- $saw_main = 1;
- } elsif (not $categories{$cat} and not $main_categories{$cat}) {
- tag "desktop-entry-invalid-category", "$cat $file";
- } elsif ($main_categories{$cat}) {
- $saw_main = 1;
- }
- }
- unless ($saw_main) {
- tag "desktop-entry-lacks-main-category", "$file";
- }
- }
-}
-
-# Verify whether a command is shipped as part of the package. Takes the full
-# path to the file being checked (for error reporting) and the binary.
-# Returns a list whose first member is true if the command is present and
-# false otherwise, and whose second member is the command (minus any leading
-# su-to-root wrapper). Shared between the desktop and menu code.
-sub VerifyCmd {
- my ($file, $line, $exec, $pkg) = @_;
- my $location = ($line ? "$file:$line" : $file);
-
- # This routine handles su wrappers. The option parsing here is ugly and
- # dead-simple, but it's hopefully good enough for what will show up in
- # desktop files. su-to-root and sux require -c options, kdesu optionally
- # allows one, and gksu has the command at the end of its arguments.
- my @com = split (' ', $exec);
- my $cmd;
- if ($com[0] and $com[0] eq "/usr/sbin/su-to-root") {
- tag 'su-to-root-with-usr-sbin', $location;
- }
- if ($com[0] and $com[0] =~ m,^(?:/usr/s?bin/)?(su-to-root|gksu|kdesu|sux)$,) {
- my $wrapper = $1;
- shift @com;
- while (@com) {
- unless ($com[0]) {
- shift @com;
- next;
- }
- if ($com[0] eq '-c') {
- $cmd = $com[1];
- last;
- } elsif ($com[0] =~ /^-[Dfmupi]|^--(user|description|message)/) {
- shift @com;
- shift @com;
- } elsif ($com[0] =~ /^-/) {
- shift @com;
- } else {
- last;
- }
- }
- if (!$cmd && $wrapper =~ /^(gk|kde)su$/) {
- if (@com) {
- $cmd = $com[0];
- } else {
- $cmd = $wrapper;
- undef $wrapper;
- }
- }
- tag 'su-wrapper-without--c', "$location $wrapper" unless $cmd;
- if ($wrapper && $wrapper !~ /su-to-root/ && $wrapper ne $pkg) {
- tag 'su-wrapper-not-su-to-root', "$location $wrapper";
- }
- } else {
- $cmd = $com[0];
- }
- my $okay = $cmd && ($cmd =~ /^[\'\"]/ || $file_index{$cmd} || grep { $file_index{$_ . $cmd} } @path);
- return ($okay, $cmd);
-}
-
-1;
-
-# Local Variables:
-# indent-tabs-mode: t
-# cperl-indent-level: 4
-# End:
-# vim: syntax=perl ts=8 sw=4