Adding side stream changes to Maemian. Working to integrate full upstream libraries...
[maemian] / nokia-lintian / checks / menus
diff --git a/nokia-lintian/checks/menus b/nokia-lintian/checks/menus
new file mode 100644 (file)
index 0000000..6674d33
--- /dev/null
@@ -0,0 +1,688 @@
+# menus -- lintian check script -*- perl -*-
+
+# somewhat of a misnomer -- it doesn't only check menus
+
+# Copyright (C) 1998 Christian Schwarz
+#
+# 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.
+
+package Lintian::menus;
+use strict;
+use lib "$ENV{'LINTIAN_ROOT'}/checks/";
+use common_data;
+
+use Lintian::Data;
+use Spelling;
+use Tags;
+use Util;
+
+my $pkg;
+my %all_files = ();
+my %all_links = ();
+
+# Known fields for doc-base files.  The value is 1 for required fields and 0
+# for optional fields.
+my %known_docbase_main_fields = (
+       'document' => 1,
+       'title'    => 1,
+       'section'  => 1,
+       'abstract' => 0,
+       'author'   => 0
+);
+my %known_docbase_format_fields = (
+       'format'  => 1,
+       'files'   => 1,
+       'index'   => 0
+);
+
+sub run {
+
+$pkg = shift;
+my $type = shift;
+
+my %preinst;
+my %postinst;
+my %prerm;
+my %postrm;
+
+my $docbase_file;
+my $menu_file;
+my $menumethod_file;
+my $anymenu_file;
+
+# check preinst script
+if ( -f "control/preinst" ) {
+    # parse script...
+    check_script("preinst",\%preinst);
+}
+
+# check postinst script
+if ( -f "control/postinst" ) {
+    # parse script...
+    check_script("postinst",\%postinst);
+}
+
+# check prerm script
+if ( -f "control/prerm" ) {
+    # parse script...
+    check_script("prerm",\%prerm);
+}
+
+# check postrm script
+if ( -f "control/postrm" ) {
+    # parse script...
+    check_script("postrm",\%postrm);
+}
+
+# read package contents
+open(IN, '<', "index") or fail("cannot open index file index: $!");
+while (<IN>) {
+    chomp;
+    my ($perm,$owner,$size,$date,$time,$file) = split(' ', $_, 6);
+    $file =~ s,^(\./),,;
+    add_file_link_info ($file);
+    $file =~ s/ link to .*//;
+    $file =~ s/ -> .*//;
+
+    my $operm = perm2oct($perm);
+
+    if ($perm =~ m,^-,o) { # file checks
+       # menu file?
+       if ($file =~ m,^usr/(lib|share)/menu/\S,o) { # correct permissions?
+           if ($perm =~ m,x,o) {
+               tag "executable-menu-file", sprintf("$file %04o",$operm);
+           }
+
+           next if $file =~ m,^usr/(lib|share)/menu/README$,;
+
+           if ($file =~ m,^usr/lib/,o) {
+               tag "menu-file-in-usr-lib", $file;
+           }
+
+           $menu_file = $file;
+
+           if ($file =~ m,usr/(lib|share)/menu/menu$,o and $pkg ne 'menu') {
+               tag "bad-menu-file-name", $file;
+           }
+       }
+       # doc-base file?
+       elsif ($file =~ m,^usr/share/doc-base/\S,o) { # correct permissions?
+           if ($perm =~ m,x,o) {
+               tag "executable-in-usr-share-docbase", $file, sprintf("%04o",$operm);
+           }
+           $docbase_file = $file;
+       }
+       #menu-methods file?
+       elsif ( $file =~ m,^etc/menu-methods/\S,o ) {
+           #TODO: we should test if the menu-methods file
+           # is made executable in the postinst as recommended by
+           # the menu manual
+
+           my $menumethod_includes_menu_h = 0;
+           $menumethod_file = $file;
+
+           open(MM, '<', "unpacked/$file") or fail("cannot open menu-method file $file: $!");
+           while (<MM>) {
+               chomp;
+               if (m,^!include menu.h,o) {
+                   $menumethod_includes_menu_h = 1;
+                   last;
+               }
+           }
+           close MM;
+           tag "menu-method-should-include-menu-h", "$file"
+               unless $menumethod_includes_menu_h or $pkg eq 'menu';
+       }
+    }
+}
+close IN;
+
+# prerm scripts should not call update-menus
+if ($prerm{'calls-updatemenus'}) {
+    tag "prerm-calls-updatemenus", "";
+}
+
+# postrm scripts should not call install-docs
+if ($postrm{'calls-installdocs'} or $postrm{'calls-installdocs-r'}) {
+    tag "postrm-calls-installdocs", "";
+}
+
+# preinst scripts should not call either update-menus nor installdocs
+if ($preinst{'calls-updatemenus'}) {
+    tag "preinst-calls-updatemenus", "";
+}
+
+if ($preinst{'calls-installdocs'}) {
+    tag "preinst-calls-installdocs", "";
+}
+
+# don't set the /usr/doc link, the FHS transition is over (2002-10-08)
+if (defined $postinst{'sets-link'} && $postinst{'sets-link'} == 1) {
+    tag "postinst-should-not-set-usr-doc-link", "";
+}
+
+$anymenu_file = $menu_file || $menumethod_file;
+
+# check consistency
+# docbase file?
+if ($docbase_file) {           # postinst has to call install-docs
+    if (not $postinst{'calls-installdocs'}) {
+       tag "postinst-does-not-call-installdocs", "$docbase_file";
+    }
+    # prerm has to call install-docs -r
+    if (not $prerm{'calls-installdocs-r'}) {
+       tag "prerm-does-not-call-installdocs", "$docbase_file";
+    }
+
+    # check the contents of the doc-base file(s)
+    opendir DOCBASEDIR, "doc-base" or fail("cannot read doc-base directory.");
+    my $dbfile;
+    while (defined ($dbfile = readdir DOCBASEDIR)) {
+       # don't try to parse executables, plus we already warned about it
+       next if -x "doc-base/$dbfile";
+       check_doc_base_file($dbfile, $pkg, $type);
+    }
+    closedir DOCBASEDIR;
+} else {
+    # postinst and postrm should not need to call install-docs
+    if ($postinst{'calls-installdocs'} or $postinst{'calls-installdocs-r'}) {
+       tag "postinst-has-useless-call-to-install-docs", "";
+    }
+    if ($prerm{'calls-installdocs'} or $prerm{'calls-installdocs-r'}) {
+       tag "prerm-has-useless-call-to-install-docs", "";
+    }
+}
+
+if ($anymenu_file) {
+    # postinst has to call update-menus
+    if (not $postinst{'calls-updatemenus'}) {
+       tag "postinst-does-not-call-updatemenus", "$anymenu_file";
+    }
+    # postrm has to call update-menus
+    if (not $postrm{'calls-updatemenus'}) {
+       tag "postrm-does-not-call-updatemenus", "$anymenu_file" unless $pkg eq 'menu';
+    }
+} else {
+    # postinst and postrm should not need to call update-menus
+    if ($postinst{'calls-updatemenus'}) {
+       tag "postinst-has-useless-call-to-update-menus", "";
+    }
+    if ($postrm{'calls-updatemenus'}) {
+       tag "postrm-has-useless-call-to-update-menus", "";
+    }
+}
+
+}
+
+# -----------------------------------
+
+sub check_doc_base_file {
+    my ($dbfile, $pkg, $type) = @_;
+
+    my $line = file_is_encoded_in_non_utf8("doc-base/$dbfile", $type, $pkg);
+    if ($line) {
+       tag 'doc-base-file-uses-obsolete-national-encoding', "$dbfile:$line";
+    }
+
+    open (IN, '<', "doc-base/$dbfile")
+        or fail("cannot open doc-base file $dbfile for reading.");
+
+    my (@files, $field, @vals);
+    my $knownfields = \%known_docbase_main_fields;
+    $line           = 0;  # global
+    my %sawfields   = (); # local for each section of control file
+    my %sawformats  = (); # global for control file
+
+    while (<IN>) {
+        chomp;
+
+        # New field.  check previous field, if we have any.
+        if (/^(\S+)\s*:\s*(.*)$/) {
+            my (@new) = ($1, $2);
+            if ($field) {
+                check_doc_base_field($dbfile, $line, $field, \@vals,
+                                     \%sawfields, \%sawformats, $knownfields);
+            }
+            $field = lc $new[0];
+            @vals  = ($new[1]);
+            $line  = $.;
+
+        # Continuation of previously defined field.
+        } elsif ($field && /^\s+\S/) {
+            push (@vals, $_);
+
+            # All tags will be reported on the last continuation line of the
+            # doc-base field.
+            $line  = $.;
+
+        # Sections' separator.
+        } elsif (/^(\s*)$/) {
+            tag "doc-base-file-separator-extra-whitespaces", "$dbfile:$."
+                if $1;
+            next unless $field; # skip successive empty lines
+
+            # Check previously defined field and section.
+            check_doc_base_field($dbfile, $line, $field, \@vals, \%sawfields,
+                                 \%sawformats, $knownfields);
+            check_doc_base_file_section($dbfile, $line + 1, \%sawfields,
+                                        \%sawformats, $knownfields);
+
+            # Intialize variables for new section.
+            undef $field;
+            undef $line;
+            @vals      = ();
+            %sawfields = ();
+
+            # Each section except the first one is format section.
+            $knownfields = \%known_docbase_format_fields;
+
+        # Everything else is a syntax error.
+        } else {
+            tag "doc-base-file-syntax-error", "$dbfile:$.";
+        }
+    }
+
+    # Check the last field/section of the control file.
+    if ($field) {
+        check_doc_base_field($dbfile, $line, $field, \@vals, \%sawfields,
+                             \%sawformats, $knownfields);
+        check_doc_base_file_section($dbfile, $line, \%sawfields, \%sawformats,
+                                    $knownfields);
+    }
+
+    # Make sure we saw at least one format.
+    tag "doc-base-file-no-format-section", "$dbfile:$." unless %sawformats;
+
+    close IN;
+}
+
+# Checks one field of a doc-base control file.  $vals is array ref containing
+# all lines of the field.  Modifies $sawfields and $sawformats.
+sub check_doc_base_field {
+    my ($dbfile, $line, $field, $vals, $sawfields, $sawformats,
+        $knownfields) = @_;
+
+    tag "doc-base-file-unknown-field", "$dbfile:$line", "$field"
+        unless defined $knownfields->{$field};
+    tag "doc-base-file-duplicated-field", "$dbfile:$line", "$field"
+        if $sawfields->{$field};
+    $sawfields->{$field} = 1;
+
+    # Index/Files field.
+    #
+    # Check if files referenced by doc-base are included in the package.  The
+    # Index field should refer to only one file without wildcards.  The Files
+    # field is a whitespace-separated list of files and may contain wildcards.
+    # We skip without validating wildcard patterns containing character
+    # classes since otherwise we'd need to deal with wildcards inside
+    # character classes and aren't there yet.
+    if ($field eq 'index' or $field eq 'files') {
+        my @files = map { split ('\s+', $_) } @$vals;
+
+        if ($field eq 'index' && @files > 1) {
+            tag "doc-base-index-references-multiple-files", "$dbfile:$line";
+        }
+        for my $file (@files) {
+            if ($file =~ m%^/usr/doc%) {
+                tag "doc-base-file-references-usr-doc", "$dbfile:$line";
+            }
+            my $realfile = delink ($file);
+
+            # openoffice.org-dev-doc has thousands of files listed so try to
+            # use the hash if possible.
+            my $found;
+            if ($realfile =~ /[*?]/) {
+                my $regex = quotemeta ($realfile);
+                unless ($field eq 'index') {
+                    next if $regex =~ /\[/;
+                    $regex =~ s%\\\*%[^/]*%g;
+                    $regex =~ s%\\\?%[^/]%g;
+                    $regex .= '/?';
+                }
+                $found = grep { /^$regex\z/ } keys %all_files;
+            } else {
+                $found = $all_files{$realfile} || $all_files{"$realfile/"};
+            }
+            unless ($found) {
+                tag "doc-base-file-references-missing-file", "$dbfile:$line",
+                    $file;
+            }
+        }
+        undef @files;
+
+    # Format field.
+    } elsif ($field eq 'format') {
+        my $format = join (' ', @$vals);
+        $format =~ s/^\s+//o;
+        $format =~ s/\s+$//o;
+        $format = lc $format;
+        tag "doc-base-file-unknown-format", "$dbfile:$line", $format
+            unless $known_doc_base_formats{$format};
+        tag "doc-base-file-duplicated-format", "$dbfile:$line", $format
+            if $sawformats->{$format};
+        $sawformats->{$format} = 1;
+
+        # Save the current format for the later section check.
+        $sawformats->{' *current* '} = $format;
+
+    # Document field.
+    } elsif ($field eq 'document') {
+        $_ = join (' ', @$vals);
+
+        tag "doc-base-invalid-document-field", "$dbfile:$line", "$_"
+            unless /^[a-z0-9+.-]+$/;
+        tag "doc-base-document-field-ends-in-whitespace", "$dbfile:$line"
+            if /[ \t]$/;
+        tag "doc-base-document-field-not-in-first-line", "$dbfile:$line"
+            unless $line == 1;
+
+    # Title field.
+    } elsif ($field eq 'title') {
+        if (@$vals) {
+            spelling_check("spelling-error-in-doc-base-title-field",
+                           join (' ', @$vals), "$dbfile:$line");
+            spelling_check_picky("spelling-error-in-doc-base-title-field",
+                                 join (' ', @$vals), "$dbfile:$line");
+        }
+
+    # Section field.
+    } elsif ($field eq 'section') {
+       my $sections = Lintian::Data->new('doc-base/sections');
+       $_ = join (' ', @$vals);
+       tag "doc-base-unknown-section", "$dbfile:$line", $_
+           unless $sections->known($_);
+
+    # Abstract field.
+    } elsif ($field eq 'abstract') {
+        # The three following variables are used for checking if the field is
+        # correctly phrased.  We detect if each line (except for the first
+        # line and lines containing single dot) of the field starts with the
+        # same number of spaces, not followed by the same non-space character,
+        # and the number of spaces is > 1.
+        #
+        # We try to match fields like this:
+        #  ||Abstract: The Boost web site provides free peer-reviewed portable
+        #  ||  C++ source libraries.  The emphasis is on libraries which work
+        #  ||  well with the C++ Standard Library.  One goal is to establish
+        #
+        # but not like this:
+        #  ||Abstract:  This is "Ding"
+        #  ||  * a dictionary lookup program for Unix,
+        #  ||  * DIctionary Nice Grep,
+        my $leadsp    = undef; # string with leading spaces from second line
+        my $charafter = undef; # first non-whitespace char of second line
+        my $leadsp_ok = 1;     # are spaces OK?
+
+        # Intentionally skipping the first line.
+        for my $idx (1 .. $#{$vals}) {
+            $_ = $vals->[$idx];
+            if (/manage\s+online\s+manuals\s.*Debian/o) {
+                tag "doc-base-abstract-field-is-template", "$dbfile:$line"
+                    unless $pkg eq "doc-base";
+            } elsif (/^(\s+)\.(\s*)$/o and ($1 ne " " or $2)) {
+                tag "doc-base-abstract-field-separator-extra-whitespaces",
+                    "$dbfile:" . ($line - $#{$vals} + $idx);
+            } elsif (!$leadsp && /^(\s+)(\S)/o) {
+                # The regexp should always match.
+                ($leadsp, $charafter) = ($1, $2);
+                $leadsp_ok = $leadsp eq " ";
+            } elsif (!$leadsp_ok && /^(\s+)(\S)/o) {
+                # The regexp should always match.
+                undef $charafter if $charafter && $charafter ne $2;
+                $leadsp_ok = 1
+                    if ($1 ne $leadsp) || ($1 eq $leadsp && $charafter);
+            }
+        }
+        unless ($leadsp_ok) {
+            tag "doc-base-abstract-might-contain-extra-leading-whitespaces",
+                "$dbfile:$line";
+        }
+
+        # Check spelling.
+        if (@$vals) {
+            spelling_check("spelling-error-in-doc-base-abstract-field",
+                           join (' ', @$vals), "$dbfile:$line");
+            spelling_check_picky("spelling-error-in-doc-base-abstract-field",
+                                 join (' ', @$vals), "$dbfile:$line");
+        }
+    }
+}
+
+# Checks the section of the doc-base control file.  Tries to find required
+# fields missing in the section.
+sub check_doc_base_file_section {
+    my ($dbfile, $line, $sawfields, $sawformats, $knownfields) = @_;
+
+    tag "doc-base-file-no-format", "$dbfile:$line"
+        if ((defined $sawfields->{'files'} || defined $sawfields->{'index'})
+            && !(defined $sawfields->{'format'}));
+
+    # The current format is set by check_doc_base_field.
+    if ($sawfields->{'format'}) {
+        my $format =  $sawformats->{' *current* '};
+        tag "doc-base-file-no-index", "$dbfile:$line"
+            if ($format && ($format eq 'html' || $format eq 'info')
+                && !$sawfields->{'index'});
+    }
+    for my $field (sort keys %$knownfields) {
+        tag "doc-base-file-lacks-required-field", "$dbfile:$line", "$field"
+            if ($knownfields->{$field} == 1 && !$sawfields->{$field});
+    }
+}
+
+# Add file and link to %all_files and %all_links.  Note that both files and
+# links have to include a leading /.
+sub add_file_link_info {
+    my $file = shift;
+    my $link = undef;
+
+    $file = "/" . $file if (not $file =~ m%^/%); # make file absolute
+    $file =~ s%/+%/%g;                          # remove duplicated `/'
+    my $ishard = ($file =~ / link to /);
+    ($file, $link) = split(/ (?:->|link to) /, $file);
+
+    $all_files{$file} = 1;
+
+    if (defined $link) {
+       if ($ishard) {
+           $link =~ s,^\./,/,;
+       } elsif (not $link =~ m,^/,) {            # not absolute link
+           $link = "/" . $link;                  # make sure link starts with '/'
+           $link =~ s,/+\./+,/,g;                # remove all /./ parts
+           my $dcount = 1;
+           while ($link =~ s,^/+\.\./+,/,) {     #\ count & remove
+              $dcount++;                         #/ any leading /../ parts
+           }
+           my $f = $file;
+           while ($dcount--) {                   #\ remove last $dcount
+               $f =~ s,/[^/]*$,,;                #/ path components from $file
+           }
+           $link = $f . $link;                   # now we should have absolute link
+       }
+       $all_links{$file} = $link unless ($link eq $file);
+    }
+}
+
+
+# Dereference all symlinks in file.  Uses %all_links.
+sub delink {
+    my $file = shift;
+
+    $file =~ s%/+%/%g;                           # remove duplicated '/'
+    return $file unless %all_links;              # package doesn't symlinks
+
+    my $p1 = "";
+    my $p2 = $file;
+    my %used_links = ();
+
+    # In the loop below we split $file into two parts on each '/' until
+    # there's no remaining slashes.  We try substituting the first part with
+    # corresponding symlink and if it succeedes, we start the procedure from
+    # beginning.
+    #
+    # Example:
+    #   Let $all_links{"/a/b"} == "/d", and $file == "/a/b/c"
+    #   Then 0) $p1 == "",     $p2 == "/a/b/c"
+    #        1) $p1 == "/a",   $p2 == "/b/c"
+    #        2) $p1 == "/a/b", $p2 == "/c"      ; substitute "/d" for "/a/b"
+    #        3) $p1 == "",     $p2 == "/d/c"
+    #        4) $p1 == "/d",   $p2 == "/c"
+    #        5) $p1 == "/d/c", $p2 == ""
+    #
+    # Note that the algorithm supposes, that
+    #   i) $all_links{$X} != $X for each $X
+    #  ii) both keys and values of %all_links start with '/'
+
+    while (($p2 =~ s%^(/[^/]*)%%g) > 0) {
+       $p1 .= $1;
+       if (defined $all_links{$p1}) {
+           return '!!! SYMLINK LOOP !!!' if defined $used_links{$p1};
+           $p2 = $all_links{$p1} . $p2;
+           $p1 = "";
+           $used_links{$p1} = 1;
+       }
+    }
+
+    # After the loop $p2 should be empty and $p1 should contain the target
+    # file.  In some rare cases when $file contains no slashes, $p1 will be
+    # empty and $p2 will contain the result (which will be equal to $file).
+    return $p1 ne "" ? $p1 : $p2;
+}
+
+sub check_script {
+    my ($script,$pres) = @_;
+    my ($no_check_menu,$no_check_installdocs,$no_check_wmmenu,$calls_wmmenu);
+    my $interp;
+
+    open(IN, '<', "control/$script") or
+       fail("cannot open maintainer script control/$script for reading: $!");
+    $interp = <IN>;
+    $interp = '' unless defined $interp;
+    if ($interp =~ m,^\#\!\s*/bin/(a|ba|k|pdk)?sh,) {
+        $interp = 'sh';
+    } elsif ($interp =~ m,^\#\!\s*/usr/bin/perl,) {
+        $interp = 'perl';
+    } else {
+       if ($interp =~ m,^\#\!\s*(.+),) {
+            $interp = $1;
+       }
+       else { # hmm, doesn't seem to start with #!
+           # is it a binary? look for ELF header
+           if ($interp =~ m/^\177ELF/) {
+               return; # nothing to do here
+           }
+           $interp = 'unknown';
+       }
+    }
+
+    while (<IN>) {
+       # skip comments
+       s/\#.*$//o;
+
+       ##
+       # either update-menus or wm-menu-config will satisfy
+       # the checks that the menu file installed is properly used
+       ##
+
+       # does the script check whether update-menus exists?
+       if (/-x\s+\S*update-menus/o or /(which|command)(\s+\S+)?\s+update-menus/o) {
+           # yes, it does.
+           $pres->{'checks-for-updatemenus'} = 1;
+       }
+
+       # does the script call update-menus?
+       # TODO this regex-magic should be moved to some lib for checking
+       # whether a certain word is likely called as command... --Jeroen
+       if (/(?:^\s*|[;&|]\s*|(?:then|do)\s+)(?:\/usr\/bin\/)?update-menus(?:\s|[;&|<>]|$)/) {
+           # yes, it does.
+           $pres->{'calls-updatemenus'} = 1;
+
+           # checked first?
+           if (not $pres->{'checks-for-updatemenus'} and $pkg ne 'menu') {
+               tag "maintainer-script-does-not-check-for-existence-of-updatemenus", "$script:$." unless $no_check_menu++;
+           }
+       }
+
+       # does the script check whether wm-menu-config exists?
+       if (s/-x\s+\S*wm-menu-config//o or /which\s+wm-menu-config/o
+           or s/command\s+.*?wm-menu-config//o) {
+           # yes, it does.
+           $pres->{'checks-for-wmmenuconfig'} = 1;
+       }
+
+       # does the script call wm-menu-config?
+       if (m/(?:^\s*|[;&|]\s*|(?:then|do)\s+)(?:\/usr\/sbin\/)?wm-menu-config(?:\s|[;&|<>]|$)/) {
+           # yes, it does.
+           $pres->{'calls-wmmenuconfig'} = 1;
+           tag "maintainer-script-calls-deprecated-wm-menu-config", "$script:$." unless $calls_wmmenu++;
+
+           # checked first?
+           if (not $pres->{'checks-for-wmmenuconfig'} and $pkg ne 'menu') {
+               tag "maintainer-script-does-not-check-for-existence-of-wm-menu-config", "$script:$." unless $no_check_wmmenu++;
+           }
+       }
+
+       # does the script set a link in /usr/doc?
+       # does the script remove a link in /usr/doc?
+       if ($interp eq 'sh') {
+           if (m,ln\s+(-\w+)?\s+\"?\.\./share/doc/\S+, ) {
+               $pres->{'sets-link'} = 1;
+           }
+           if (m,rm\s+(-\w+\s+)?\"?/usr/doc/\S+, ) {
+               $pres->{'removes-link'} = 1;
+           }
+       } elsif ($interp eq 'perl') {
+           if (m|symlink\s*\(?\s*[\"\']\.\./share/doc/\.+?[\"\']\s*,|) {
+               $pres->{'sets-link'} = 1;
+           } elsif (m,ln\s+(-\w+)?\s+\"?\.\./share/doc/\S+, ) {
+               $pres->{'sets-link'} = 1;
+           }
+       } else {
+           # just fall through for now
+       }
+
+       # does the script check whether install-docs exists?
+       if (s/-x\s+\S*install-docs//o or /which\s+install-docs/o
+           or s/command\s+.*?install-docs//o) {
+           # yes, it does.
+           $pres->{'checks-for-installdocs'} = 1;
+       }
+
+       # does the script call install-docs?
+       if (m/(?:^\s*|[;&|]\s*|(?:then|do)\s+)(?:\/usr\/sbin\/)?install-docs(?:\s|[;&|<>]|$)/) {
+           # yes, it does.  Does it remove or add a doc?
+           if (m/install-docs\s+(-r|--remove)\s/) {
+               $pres->{'calls-installdocs-r'} = 1;
+           } else {
+               $pres->{'calls-installdocs'} = 1;
+           }
+           # checked first?
+           if (not $pres->{'checks-for-installdocs'}) {
+               tag "maintainer-script-does-not-check-for-existence-of-installdocs", "$script" unless $no_check_installdocs++;
+           }
+       }
+    }
+    close IN;
+}
+
+1;
+
+# Local Variables:
+# indent-tabs-mode: t
+# cperl-indent-level: 4
+# End:
+# vim: syntax=perl