[Date Prev][Date Next] [Thread Prev][Thread Next] [Date Index] [Thread Index]

Re: Status of install-info



Hello,

I'm keeping in CC the people who participated in the previous discussions:
 * GNU's install-info upstream
   bug-texinfo at gnu.org
   Karl Berry
   Eli Zaretskii
 * Debian's texinfo maintainer
   Norbert Preining
 * debhelper (dh_installinfo) maintainer
   Joey Hess
 * dpkg maintainers
   debian-dpkg at lists.debian.org
   Ian Zimmerman (I think you're also on the list, sorry)



Two issues were raised in the previous threads:
 * dpkg's i-i do not support the new info format (with multiple sections)
 * corruption of the dir file, and the need of re-building the info
   database from scratch.

I'm focusing on the first issue, and I think the right way to fix this
issue is to use the GNU's i-i on Debian. I also think this issue will be
easier to fix before Etch (no need for a policy change or change in the
way info uses its database)

Other ideas were presented in the recent threads regarding install-info.
One of then was to have an option to re-generate the dir file. I think
this will require a transition for most packages using info files. If you
think this should be done (and if this can be done for Etch), then the
content of this mail is mostly useless. (Ian did you start working on
--update-dir-from-info-files in the GNU's i-i)



I made an experiment on my box with an install-info perl script based on a
wrapper that calls the GNU install-info.

As expected by the previous "Status of install-info" mail, some of the
dpkg's i-i options are not supported. The ones that were noticed during my
test are: --remove-exactly, --menuitem, --description
(i.e. --test --debug and --keep-old were not encountered)

It was also expected that --section receives two arguments in dpkg, and
only one in the GNU's i-i. One issue is that
    install-info --section section infofile infodir
and
    install-info --section regex   section  infofile
are difficult to differentiate automatically.

In the wrapper, I used some heuristics to detect if the command line
expects a dpkg's or GNU's i-i. (see the beginning of the wrapper code)

I had to implement a --section-regex option in the GNU's i-i to mimic the
dpkg's i-i behaviour. Another solution could be to silently ignore the
regular expression provided to the dpkg's --section option.
(in fact, I'm not sure it's needed, and comments from Ian let me think
that this feature is quite disturbing)

In addition, I had to implement the --remove-exactly option. Otherwise,
--remove emacs-0.21/info also removes info.

Also, some info files (at least in Debian) do not contain any info items.
With dpkg's i-i, the --menuitem and --description options were used to
provide these missing items to i-i. In the GNU's i-i, the --item option
must be used (and the item is a concatenation of the menuitem, filename
and descritpion).
I've choosen to add a --item option when both --menuitem and --description
are used. But if --description is used alone, I'm discarding this option.
(This is the case for a few packages)



With the wrapper and the GNU's i-i changes, I could update all my packages
which distribute info files.



The only drawback I noticed at this time is that the descriptions are
not aligned, as mentionned by Ian Zimmerman.
(Note: I do prefer unaligned descriptions than missing items)
This issue could be solved in the GNU's i-i if needed.

Also there are no backups (with dpkg's i-i, the dir file was copied in
/usr/share/info/dir.old and in /var/backup/infodir.bak).

I think the wrapper could ease a transition from dpkg's i-i to the GNU's
i-i.



Some questions:
 * How does the current GNU's i-i (or other distribution) solve the issue
   of removing emacs-21/info?
   Should we keep the --remove-exactly option? (Send it upstream?)
 * Is there a plan to implement a description alignment algorithm in the
   GNU's i-i? Do you think it will be mandatory to replace the dpkg's i-i?
 * Should we keep the --section-regex option? (Send it upstream?)
 * Do you think this wrapper could be a start for replacing in Debian the
   dpkg's i-i by the GNU's one?
   How does it fit with the Etch freeze?


Kind Regards,
-- 
Nekral
diff -rauN ../orig/texinfo-4.8.dfsg.1/util/install-info.c ./texinfo-4.8.dfsg.1/util/install-info.c
--- ../orig/texinfo-4.8.dfsg.1/util/install-info.c	2006-05-29 05:54:40.000000000 +0200
+++ ./texinfo-4.8.dfsg.1/util/install-info.c	2006-06-17 16:50:02.000000000 +0200
@@ -20,6 +20,7 @@
 
 #include "system.h"
 #include <getopt.h>
+#include <regex.h>
 
 static char *progname = "install-info";
 
@@ -129,11 +130,19 @@
   { "item",      required_argument, NULL, 'e' },
   { "quiet",     no_argument, NULL, 'q' },
   { "remove",    no_argument, NULL, 'r' },
+  { "remove-exactly",    no_argument, NULL, 'x' },
+  { "section-regex", required_argument, NULL, 'R' },
   { "section",   required_argument, NULL, 's' },
   { "version",   no_argument, NULL, 'V' },
   { 0 }
 };
 
+regex_t *psecreg = NULL;
+  /* Nonzero means that the name specified for the info file will be used
+   * (without removing .gz, .info extension or leading path) to match the
+   * entries that must be removed.  */
+  int remove_exactly = 0;
+
 /* Error message functions.  */
 
 /* Print error message.  S1 is printf control string, S2 and S3 args for it. */
@@ -383,6 +392,7 @@
      filename from the new dir entries, not the filename on the command
      line.  Not worrying about those things right now, though.  --karl,
      26mar04.  */
+  if (!remove_exactly) {
   while (*item_basename && !IS_SLASH (*item_basename)
 	 && *item_basename != term_char)
     item_basename++;
@@ -390,6 +400,7 @@
     item_basename = item;  /* no /, use original */
   else
     item_basename++;       /* have /, move past it */
+  }
     
   /* First, ITEM must actually match NAME (usually it won't).  */
   ret = strncasecmp (item_basename, name, name_len) == 0;
@@ -455,6 +466,10 @@
                      An Info directory entry is actually a menu item.\n\
  --quiet           suppress warnings.\n\
  --remove          same as --delete.\n\
+ --section-regex=REGEX\n\
+                     if an entry is added to a section that does not alredy\n\
+                     exist, try to find a section that matches this\n\
+                     regular expression before starting a new section.\n\
  --section=SEC     put this file's entries in section SEC of the directory.\n\
                      If you specify more than one section, all the entries\n\
                      are added in each of the sections.\n\
@@ -1248,6 +1263,27 @@
           delete_flag = 1;
           break;
 
+        case 'R':
+          {
+            int error;
+            if (psecreg)
+              warning(_("Only one regular expression can be specified `%s'"),
+                      optarg, 0);
+            else
+              psecreg = (regex_t *) xmalloc(sizeof (regex_t));
+
+            if ((error = regcomp(psecreg, optarg, REG_ICASE|REG_NOSUB)) != 0)
+              {
+                int errbuf_size = regerror(error, psecreg, NULL, 0);
+                char *errbuf = (char *) xmalloc(errbuf_size);
+                regerror(error, psecreg, errbuf, errbuf_size);
+                fatal (_("Error in regular expression `%s': %s"),
+                       optarg,
+                       errbuf);
+              };
+          }
+          break;
+
         case 's':
           {
             struct spec_section *next
@@ -1268,6 +1304,11 @@
 For more information about these matters, see the files named COPYING.\n"));
           xexit (0);
 
+        case 'x':
+          delete_flag = 1;
+          remove_exactly = 1;
+          break;
+
         default:
           suggest_asking_for_help ();
         }
@@ -1349,7 +1390,7 @@
   /* We will be comparing the entries in the dir file against the
      current filename, so need to strip off any directory prefix and/or
      [.info][.gz] suffix.  */
-  {
+  if (!remove_exactly) {
     char *infile_basename = infile + strlen (infile);
 
     if (HAVE_DRIVE (infile))
@@ -1359,7 +1400,8 @@
       infile_basename--;
 
     infile_sans_info = strip_info_suffix (infile_basename);
-  }
+  } else
+      infile_sans_info = xstrdup(infile);
 
   something_deleted
     = parse_dir_file (dir_lines, dir_nlines, &dir_nodes, infile_sans_info);
@@ -1374,6 +1416,32 @@
       struct menu_section *section;
       struct spec_section *spec;
 
+      for (spec = input_sections; spec; spec = spec->next)
+        {
+          int found = 0;
+          /* Check if the section specified (e.g. in the info file) exists */
+          for (node = dir_nodes; node && found == 0; node = node->next)
+            for (section = node->sections;
+                 section && found == 0;
+                 section = section->next)
+              if (!strcmp (spec->name, section->name))
+                found = 1;
+
+          /* If it does not exist, but the user specified a regular expression,
+           * try to find a section that matches this regex.
+           */
+          if (!found && psecreg)
+            for (node = dir_nodes; node && found == 0; node = node->next)
+              for (section = node->sections;
+                   section && found == 0;
+                   section = section->next)
+                if (regexec(psecreg, section->name, 0, NULL, 0) == 0)
+                  {
+                    spec->name = section->name;
+                    spec->missing = 0;
+                    found = 1;
+                  }
+        }
       for (node = dir_nodes; node; node = node->next)
         for (section = node->sections; section; section = section->next)
           {
#!/usr/bin/perl --

use strict;
use warnings;

# Diffs:
# * dir is not indented
#   Minor issue
# * No backup.
#   Not a problem if dir can be regenerated
# * gcj-4.0: installs in gcj-4.0 and gcj => --remove does not remove everything
#                                        => reinstall does not reinstall
#                                           because some parts are already
#                                           installed
# * some info files do not have menu entry (e.g. menu, linuxdoc-sgml)
#   => they use --menuentry and --descritpion
#   => this could be detected by lintian/linda
# * unsupported options
#   --remove-exactly replaced by --remove
#     Not that nice. Removing emacs-21/info also removes info
#     => need to reimplement --remove-exactly
#   --section with two arguments
#     could be detected by lintian/linda
#     IMO, --section should be avoided. everything should be in the info file
#     (maybe with a debian specific patch)
#     ditto for --descritpion and --menuentry
#     The current implementation seems to deal nicely with it
#   --description is sometime used without --menuentry
#     I discard this description

my $personality=""; # '', 'dpkg', 'gnu'

open (LOG, ">>/var/log/install-info.log");
print LOG "original command: ".join("'", @ARGV)."\n";

# Try to detect the personality
###############################

#  1. no short options in dpkg's i-i
foreach my $arg (@ARGV) {
    if ($arg =~ m/^-[ideshHr](=|$)/) {
        $personality="gnu";
        goto PERSONALITY_FOUND;
    }
}

#  2. dpkg's i-i deprecated options
foreach my $arg (@ARGV) {
    if ($arg =~ m/^--(debug$|maxwidth=|align=|calign=)/) {
        $personality="dpkg";
        goto PERSONALITY_FOUND;
    }
}

#  3. dpkg's specific options
foreach my $arg (@ARGV) {
    if ($arg =~ m/^--(menuentry=|description=|test$|keep-old$|remove-exactly$)/) {
        $personality="dpkg";
        goto PERSONALITY_FOUND;
    }
}

#  4. gnu's specific options
foreach my $arg (@ARGV) {
    if (   $arg =~ m/^--(entry|item|delete)(=|$)/
        or $arg =~ m/^--section=/) {
        $personality="gnu";
        goto PERSONALITY_FOUND;
    }
}

#  5. dpkg's parameter are always specified with an '='
foreach my $arg (@ARGV) {
    if ($arg =~ m/^--(info-?dir|info-file|dir-file)$/) {
        $personality="gnu";
        goto PERSONALITY_FOUND;
    }
}

#  6. two args to --section
#  7. no dir file => dpkg's defaut dir file
#  8. ends with two files in argument => gnu

PERSONALITY_FOUND:

my @args = ();

my $dir_specified=0;
foreach my $arg (@ARGV) {
    if (   $arg =~ m/^--(dir-file|info-?dir)(=|$)/
        or $arg =~ m/^-d(=|$)/) {
        $dir_specified=1;
        last;
    }
}

my $infofile="";
my $menuentry="";
my $description="";

my @tmp_args = @ARGV;
while (@tmp_args) {
    my $arg = $tmp_args[0];
    # unsupported dpkg's i-i options
    if (   $arg =~ m/^--(c?align|maxwidth)=/
        or $arg =~ m/^--(debug|test|keep-old)$/) {
        print STDERR "install-info: option '$arg' not supported and ignored.\n";
        print LOG "option '$arg' not supported and ignored.\n";
        shift @tmp_args;
    } elsif ($arg eq "--remove-exactly") {
        shift @tmp_args;
        push @args, $arg;
    } elsif ($arg =~ m/^--menuentry=(.*)$/) {
        $menuentry=$1;
        shift @tmp_args;
    } elsif ($arg =~ m/^--description=(.*)$/) {
        $description=$1;
        shift @tmp_args;
    } elsif ($arg =~ m/^--info-file(=|$)/) {
        shift @tmp_args;
        push @args, $arg;
        if ($arg =~ m/^--info-file=(.*)$/) {
            $infofile = $1;
        } else {
            $infofile = shift @tmp_args;
            push @args, $infofile;
        }
    } elsif (
        # dpkg's i-i options (except --section)
           $arg =~ m/^--(version|quiet|remove|help)$/
        or $arg =~ m/^--(info-?dir|dir-file)=/
        # gnu's i-i options (except --section)
        or $arg =~ m/^-([hHr]$|[ides]=)/
        or $arg =~ m/^--(delete|help|quiet|remove|version)$/
        or $arg =~ m/^--(dir-file|entry|info-?dir|item|section)=/) {
        shift @tmp_args;
        push @args, $arg;
    } elsif (    (   $arg =~ m/^-[ides]$/
                  or $arg =~ m/^--(dir-file|entry|info-?dir|item)$/)
             and scalar(@tmp_args)>=2) {
        # an option with its argument
        shift @tmp_args;
        push @args, $arg;
        push @args, shift @tmp_args;
    } elsif ($arg =~ m/^--section$/) {
        if ($personality eq "") {
            # dpkg's i-i expect two parameters for the --section option
            if (    scalar(@tmp_args) > 2
                and not (-f $tmp_args[1] or -f $tmp_args[1].".gz")
                and not (-f $tmp_args[2])
                and ($dir_specified or scalar(@tmp_args) > 3)) {
                $personality = "dpkg";
            }
        }

        if ($personality eq "dpkg") {
            shift @tmp_args;
            push @args, $arg;
            my $regex = shift @tmp_args;
            push @args, shift @tmp_args;
            push @args, "--section-regex";
            push @args, $regex;
        } else {
            shift @tmp_args;
            push @args, $arg;
            push @args, shift @tmp_args;
        }
    } elsif ($arg =~ m/^-/) {
        print LOG "Unknow option: '$arg'\n";
        shift @tmp_args;
        push @args, $arg;
    } else {
        if (scalar @tmp_args == 2 and $tmp_args[1] !~ m/^-/) {
            $dir_specified = 1;
        }
        $infofile=$arg unless (length $infofile);
        shift @tmp_args;
        push @args, $arg;
    }
}

# With the dpkg personality, we must specify the dir file
if ($personality ne "gnu") {
    if (not $dir_specified) {
        unshift @args, "/usr/share/info/dir";
        unshift @args, "--dir-file";
    }

    if (length $description and length $menuentry) {
        my $basename = $infofile;
        $basename =~ s|/usr/share/info/||;
        $basename =~ s/(\.info)?(\.gz)?$//;
        my $item="* $menuentry: ($basename).\t\t$description";
        unshift @args, $item;
        unshift @args, "--item";
    } elsif (length $description) {
        print STDERR "install-info: option '--description' used without '--menuentry'. Ignored.\n";
        print LOG "install-info: option '--description' used without '--menuentry'. Ignored.\n";
    } elsif (length $menuentry) {
        print STDERR "install-info: option '--menuentry' used without '--description'. Ignored.\n";
        print LOG "install-info: option '--menuentry' used without '--description'. Ignored.\n";
    }
} else {
    if (length $description) {
        print STDERR "install-info: The '--description' option is not supported and is ignored.\n";
    }
    if (length $menuentry) {
        print STDERR "install-info: The '--menuentry' option is not supported and is ignored.\n";
    }

}

unshift @args, "/usr/bin/ginstall-info";

print LOG "personality: '$personality'\n";
print LOG "dir_specified: '$dir_specified'\n";
print LOG "command: ".join("'", @args)."\n";
close LOG;
system(@args);



Reply to: