Back to index

tetex-bin  3.0
install-info.c
Go to the documentation of this file.
00001 /* install-info -- create Info directory entry(ies) for an Info file.
00002    $Id: install-info.c,v 1.12 2004/04/11 17:56:47 karl Exp $
00003 
00004    Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004 Free Software
00005    Foundation, Inc.
00006 
00007    This program is free software; you can redistribute it and/or modify
00008    it under the terms of the GNU General Public License as published by
00009    the Free Software Foundation; either version 2 of the License, or
00010    (at your option) any later version.
00011 
00012    This program is distributed in the hope that it will be useful,
00013    but WITHOUT ANY WARRANTY; without even the implied warranty of
00014    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00015    GNU General Public License for more details.
00016 
00017    You should have received a copy of the GNU General Public License
00018    along with this program; if not, write to the Free Software
00019    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.*/
00020 
00021 #include "system.h"
00022 #include <getopt.h>
00023 
00024 static char *progname = "install-info";
00025 
00026 struct spec_entry;
00027 struct spec_section;
00028 
00029 struct line_data *findlines (char *data, int size, int *nlinesp);
00030 void insert_entry_here (struct spec_entry *entry, int line_number,
00031                         struct line_data *dir_lines, int n_entries); 
00032 int compare_section_names (const void *s1, const void *s2);
00033 int compare_entries_text (const void *e1, const void *e2); 
00034 
00035 /* Data structures.  */
00036 
00037 
00038 /* Record info about a single line from a file as read into core.  */
00039 struct line_data
00040 {
00041   /* The start of the line.  */
00042   char *start;
00043   /* The number of characters in the line,
00044      excluding the terminating newline.  */
00045   int size;
00046   /* Vector containing pointers to the entries to add before this line.
00047      The vector is null-terminated.  */
00048   struct spec_entry **add_entries_before;
00049   /* 1 means output any needed new sections before this line.  */
00050   int add_sections_before;
00051   /* 1 means don't output this line.  */
00052   int delete;
00053 };
00054 
00055 
00056 /* This is used for a list of the specified menu section names
00057    in which entries should be added.  */
00058 struct spec_section
00059 {
00060   struct spec_section *next;
00061   char *name;
00062   /* 1 means we have not yet found an existing section with this name
00063      in the dir file--so we will need to add a new section.  */
00064   int missing;
00065 };
00066 
00067 
00068 /* This is used for a list of the entries specified to be added.  */
00069 struct spec_entry
00070 {
00071   struct spec_entry *next;
00072   char *text;
00073   int text_len;
00074   /* A pointer to the list of sections to which this entry should be
00075      added.  */
00076   struct spec_section *entry_sections;
00077   /* A pointer to a section that is beyond the end of the chain whose
00078      head is pointed to by entry_sections.  */
00079   struct spec_section *entry_sections_tail;
00080 };
00081 
00082 
00083 /* This is used for a list of nodes found by parsing the dir file.  */
00084 struct node
00085 {
00086   struct node *next;
00087   /* The node name.  */
00088   char *name;
00089   /* The line number of the line where the node starts.
00090      This is the line that contains control-underscore.  */
00091   int start_line;
00092   /* The line number of the line where the node ends,
00093      which is the end of the file or where the next line starts.  */
00094   int end_line;
00095   /* Start of first line in this node's menu
00096      (the line after the * Menu: line).  */
00097   char *menu_start;
00098   /* The start of the chain of sections in this node's menu.  */
00099   struct menu_section *sections;
00100   /* The last menu section in the chain.  */
00101   struct menu_section *last_section;
00102 };
00103 
00104 
00105 /* This is used for a list of sections found in a node's menu.
00106    Each  struct node  has such a list in the  sections  field.  */
00107 struct menu_section
00108 {
00109   struct menu_section *next;
00110   char *name;
00111   /* Line number of start of section.  */
00112   int start_line;
00113   /* Line number of end of section.  */
00114   int end_line;
00115 };
00116 
00117 /* This table defines all the long-named options, says whether they
00118    use an argument, and maps them into equivalent single-letter options.  */
00119 
00120 struct option longopts[] =
00121 {
00122   { "delete",    no_argument, NULL, 'r' },
00123   { "dir-file",  required_argument, NULL, 'd' },
00124   { "entry",     required_argument, NULL, 'e' },
00125   { "help",      no_argument, NULL, 'h' },
00126   { "infodir",   required_argument, NULL, 'D' },
00127   { "info-dir",  required_argument, NULL, 'D' },
00128   { "info-file", required_argument, NULL, 'i' },
00129   { "item",      required_argument, NULL, 'e' },
00130   { "quiet",     no_argument, NULL, 'q' },
00131   { "remove",    no_argument, NULL, 'r' },
00132   { "section",   required_argument, NULL, 's' },
00133   { "version",   no_argument, NULL, 'V' },
00134   { 0 }
00135 };
00136 
00137 /* Error message functions.  */
00138 
00139 /* Print error message.  S1 is printf control string, S2 and S3 args for it. */
00140 
00141 /* VARARGS1 */
00142 void
00143 error (const char *s1, const char *s2, const char *s3)
00144 {
00145   fprintf (stderr, "%s: ", progname);
00146   fprintf (stderr, s1, s2, s3);
00147   putc ('\n', stderr);
00148 }
00149 
00150 /* VARARGS1 */
00151 void
00152 warning (const char *s1, const char *s2, const char *s3)
00153 {
00154   fprintf (stderr, _("%s: warning: "), progname);
00155   fprintf (stderr, s1, s2, s3);
00156   putc ('\n', stderr);
00157 }
00158 
00159 /* Print error message and exit.  */
00160 
00161 void
00162 fatal (const char *s1, const char *s2, const char *s3)
00163 {
00164   error (s1, s2, s3);
00165   xexit (1);
00166 }
00167 
00168 /* Return a newly-allocated string
00169    whose contents concatenate those of S1, S2, S3.  */
00170 char *
00171 concat (const char *s1, const char *s2, const char *s3)
00172 {
00173   int len1 = strlen (s1), len2 = strlen (s2), len3 = strlen (s3);
00174   char *result = (char *) xmalloc (len1 + len2 + len3 + 1);
00175 
00176   strcpy (result, s1);
00177   strcpy (result + len1, s2);
00178   strcpy (result + len1 + len2, s3);
00179   *(result + len1 + len2 + len3) = 0;
00180 
00181   return result;
00182 }
00183 
00184 /* Return a string containing SIZE characters
00185    copied from starting at STRING.  */
00186 
00187 char *
00188 copy_string (const char *string, int size)
00189 {
00190   int i;
00191   char *copy = (char *) xmalloc (size + 1);
00192   for (i = 0; i < size; i++)
00193     copy[i] = string[i];
00194   copy[size] = 0;
00195   return copy;
00196 }
00197 
00198 /* Print fatal error message based on errno, with file name NAME.  */
00199 
00200 void
00201 pfatal_with_name (const char *name)
00202 {
00203   char *s = concat ("", strerror (errno), _(" for %s"));
00204   fatal (s, name, 0);
00205 }
00206 
00207 /* Compare the menu item names in LINE1 (line length LEN1)
00208    and LINE2 (line length LEN2).  Return 1 if the item name
00209    in LINE1 is less, 0 otherwise.  */
00210 
00211 static int
00212 menu_line_lessp (char *line1, int len1, char *line2, int len2)
00213 {
00214   int minlen = (len1 < len2 ? len1 : len2);
00215   int i;
00216 
00217   for (i = 0; i < minlen; i++)
00218     {
00219       /* If one item name is a prefix of the other,
00220          the former one is less.  */
00221       if (line1[i] == ':' && line2[i] != ':')
00222         return 1;
00223       if (line2[i] == ':' && line1[i] != ':')
00224         return 0;
00225       /* If they both continue and differ, one is less.  */
00226       if (line1[i] < line2[i])
00227         return 1;
00228       if (line1[i] > line2[i])
00229         return 0;
00230     }
00231   /* With a properly formatted dir file,
00232      we can only get here if the item names are equal.  */
00233   return 0;
00234 }
00235 
00236 /* Compare the menu item names in LINE1 (line length LEN1)
00237    and LINE2 (line length LEN2).  Return 1 if the item names are equal,
00238    0 otherwise.  */
00239 
00240 static int
00241 menu_line_equal (char *line1, int len1, char *line2, int len2)
00242 {
00243   int minlen = (len1 < len2 ? len1 : len2);
00244   int i;
00245 
00246   for (i = 0; i < minlen; i++)
00247     {
00248       /* If both item names end here, they are equal.  */
00249       if (line1[i] == ':' && line2[i] == ':')
00250         return 1;
00251       /* If they both continue and differ, one is less.  */
00252       if (line1[i] != line2[i])
00253         return 0;
00254     }
00255   /* With a properly formatted dir file,
00256      we can only get here if the item names are equal.  */
00257   return 1;
00258 }
00259 
00260 
00261 /* Given the full text of a menu entry, null terminated,
00262    return just the menu item name (copied).  */
00263 
00264 char *
00265 extract_menu_item_name (char *item_text)
00266 {
00267   char *p;
00268 
00269   if (*item_text == '*')
00270     item_text++;
00271   while (*item_text == ' ')
00272     item_text++;
00273 
00274   p = item_text;
00275   while (*p && *p != ':') p++;
00276   return copy_string (item_text, p - item_text);
00277 }
00278 
00279 /* Given the full text of a menu entry, terminated by null or newline,
00280    return just the menu item file (copied).  */
00281 
00282 char *
00283 extract_menu_file_name (char *item_text)
00284 {
00285   char *p = item_text;
00286 
00287   /* If we have text that looks like * ITEM: (FILE)NODE...,
00288      extract just FILE.  Otherwise return "(none)".  */
00289 
00290   if (*p == '*')
00291     p++;
00292   while (*p == ' ')
00293     p++;
00294 
00295   /* Skip to and past the colon.  */
00296   while (*p && *p != '\n' && *p != ':') p++;
00297   if (*p == ':') p++;
00298 
00299   /* Skip past the open-paren.  */
00300   while (1)
00301     {
00302       if (*p == '(')
00303         break;
00304       else if (*p == ' ' || *p == '\t')
00305         p++;
00306       else
00307         return "(none)";
00308     }
00309   p++;
00310 
00311   item_text = p;
00312 
00313   /* File name ends just before the close-paren.  */
00314   while (*p && *p != '\n' && *p != ')') p++;
00315   if (*p != ')')
00316     return "(none)";
00317 
00318   return copy_string (item_text, p - item_text);
00319 }
00320 
00321 
00322 
00323 /* Return FNAME with any [.info][.gz] suffix removed.  */
00324 
00325 static char *
00326 strip_info_suffix (char *fname)
00327 {
00328   char *ret = xstrdup (fname);
00329   unsigned len = strlen (ret);
00330 
00331   if (len > 3 && FILENAME_CMP (ret + len - 3, ".gz") == 0)
00332     {
00333       len -= 3;
00334       ret[len] = 0;
00335     }
00336   else if (len > 4 && FILENAME_CMP (ret + len - 4, ".bz2") == 0)
00337     {
00338       len -= 4;
00339       ret[len] = 0;
00340     }
00341 
00342   if (len > 5 && FILENAME_CMP (ret + len - 5, ".info") == 0)
00343     {
00344       len -= 5;
00345       ret[len] = 0;
00346     }
00347   else if (len > 4 && FILENAME_CMP (ret + len - 4, ".inf") == 0)
00348     {
00349       len -= 4;
00350       ret[len] = 0;
00351     }
00352 #ifdef __MSDOS__
00353   else if (len > 4 && (FILENAME_CMP (ret + len - 4, ".inz") == 0
00354                        || FILENAME_CMP (ret + len - 4, ".igz") == 0))
00355     {
00356       len -= 4;
00357       ret[len] = 0;
00358     }
00359 #endif /* __MSDOS__ */
00360 
00361   return ret;
00362 }
00363 
00364 
00365 /* Return true if ITEM matches NAME and is followed by TERM_CHAR.  ITEM
00366    can also be followed by `.gz', `.info.gz', or `.info' (and then
00367    TERM_CHAR) and still match.  */
00368 
00369 static int
00370 menu_item_equal (const char *item, char term_char, const char *name)
00371 {
00372   int ret;
00373   const char *item_basename = item;
00374   unsigned name_len = strlen (name);
00375 
00376   /* We must compare the basename in ITEM, since we are passed the
00377      basename of the original info file.  Otherwise, a new entry like
00378      "lilypond/lilypond" won't match "lilypond".
00379      
00380      Actually, it seems to me that we should really compare the whole
00381      name, and not just the basename.  Couldn't there be dir1/foo.info
00382      and dir2/foo.info?  Also, it seems like we should be using the
00383      filename from the new dir entries, not the filename on the command
00384      line.  Not worrying about those things right now, though.  --karl,
00385      26mar04.  */
00386   while (*item_basename && !IS_SLASH (*item_basename)
00387         && *item_basename != term_char)
00388     item_basename++;
00389   if (! *item_basename || *item_basename == term_char)
00390     item_basename = item;  /* no /, use original */
00391   else
00392     item_basename++;       /* have /, move past it */
00393     
00394   /* First, ITEM must actually match NAME (usually it won't).  */
00395   ret = strncasecmp (item_basename, name, name_len) == 0;
00396   if (ret)
00397     {
00398       /* Then, `foobar' doesn't match `foo', so be sure we've got all of
00399          ITEM.  The various suffixes should never actually appear in the
00400          dir file, but sometimes people put them in.  */
00401       static char *suffixes[]
00402         = { "", ".info.gz", ".info", ".inf", ".gz",
00403 #ifdef __MSDOS__
00404             ".inz", ".igz",
00405 #endif
00406             NULL };
00407       unsigned i;
00408       ret = 0;
00409       for (i = 0; !ret && suffixes[i]; i++)
00410         {
00411           char *suffix = suffixes[i];
00412           unsigned suffix_len = strlen (suffix);
00413           ret = strncasecmp (item_basename + name_len, suffix, suffix_len) == 0
00414                 && item_basename[name_len + suffix_len] == term_char;
00415         }
00416     }
00417 
00418   return ret;
00419 }
00420 
00421 
00422 
00423 void
00424 suggest_asking_for_help (void)
00425 {
00426   fprintf (stderr, _("\tTry `%s --help' for a complete list of options.\n"),
00427            progname);
00428   xexit (1);
00429 }
00430 
00431 void
00432 print_help (void)
00433 {
00434   printf (_("Usage: %s [OPTION]... [INFO-FILE [DIR-FILE]]\n\
00435 \n\
00436 Install or delete dir entries from INFO-FILE in the Info directory file\n\
00437 DIR-FILE.\n\
00438 \n\
00439 Options:\n\
00440  --delete          delete existing entries for INFO-FILE from DIR-FILE;\n\
00441                      don't insert any new entries.\n\
00442  --dir-file=NAME   specify file name of Info directory file.\n\
00443                      This is equivalent to using the DIR-FILE argument.\n\
00444  --entry=TEXT      insert TEXT as an Info directory entry.\n\
00445                      TEXT should have the form of an Info menu item line\n\
00446                      plus zero or more extra lines starting with whitespace.\n\
00447                      If you specify more than one entry, they are all added.\n\
00448                      If you don't specify any entries, they are determined\n\
00449                      from information in the Info file itself.\n\
00450  --help            display this help and exit.\n\
00451  --info-file=FILE  specify Info file to install in the directory.\n\
00452                      This is equivalent to using the INFO-FILE argument.\n\
00453  --info-dir=DIR    same as --dir-file=DIR/dir.\n\
00454  --item=TEXT       same as --entry TEXT.\n\
00455                      An Info directory entry is actually a menu item.\n\
00456  --quiet           suppress warnings.\n\
00457  --remove          same as --delete.\n\
00458  --section=SEC     put this file's entries in section SEC of the directory.\n\
00459                      If you specify more than one section, all the entries\n\
00460                      are added in each of the sections.\n\
00461                      If you don't specify any sections, they are determined\n\
00462                      from information in the Info file itself.\n\
00463  --version         display version information and exit.\n\
00464 "), progname);
00465 
00466   puts (_("\n\
00467 Email bug reports to bug-texinfo@gnu.org,\n\
00468 general questions and discussion to help-texinfo@gnu.org.\n\
00469 Texinfo home page: http://www.gnu.org/software/texinfo/"));
00470 }
00471 
00472 
00473 /* If DIRFILE does not exist, create a minimal one (or abort).  If it
00474    already exists, do nothing.  */
00475 
00476 void
00477 ensure_dirfile_exists (char *dirfile)
00478 {
00479   int desc = open (dirfile, O_RDONLY);
00480   if (desc < 0 && errno == ENOENT)
00481     {
00482       FILE *f;
00483       char *readerr = strerror (errno);
00484       close (desc);
00485       f = fopen (dirfile, "w");
00486       if (f)
00487         {
00488           fprintf (f, _("This is the file .../info/dir, which contains the\n\
00489 topmost node of the Info hierarchy, called (dir)Top.\n\
00490 The first time you invoke Info you start off looking at this node.\n\
00491 \x1f\n\
00492 %s\tThis is the top of the INFO tree\n\
00493 \n\
00494   This (the Directory node) gives a menu of major topics.\n\
00495   Typing \"q\" exits, \"?\" lists all Info commands, \"d\" returns here,\n\
00496   \"h\" gives a primer for first-timers,\n\
00497   \"mEmacs<Return>\" visits the Emacs manual, etc.\n\
00498 \n\
00499   In Emacs, you can click mouse button 2 on a menu item or cross reference\n\
00500   to select it.\n\
00501 \n\
00502 %s\n\
00503 "), "File: dir,\tNode: Top",  /* These keywords must not be translated.  */
00504     "* Menu:"
00505 );
00506           if (fclose (f) < 0)
00507             pfatal_with_name (dirfile);
00508         }
00509       else
00510         {
00511           /* Didn't exist, but couldn't open for writing.  */
00512           fprintf (stderr,
00513                    _("%s: could not read (%s) and could not create (%s)\n"),
00514                    dirfile, readerr, strerror (errno));
00515           xexit (1);
00516         }
00517     }
00518   else
00519     close (desc); /* It already existed, so fine.  */
00520 }
00521 
00522 /* Open FILENAME and return the resulting stream pointer.  If it doesn't
00523    exist, try FILENAME.gz.  If that doesn't exist either, call
00524    CREATE_CALLBACK (with FILENAME as arg) to create it, if that is
00525    non-NULL.  If still no luck, fatal error.
00526 
00527    If we do open it, return the actual name of the file opened in
00528    OPENED_FILENAME and the compress program to use to (de)compress it in
00529    COMPRESSION_PROGRAM.  The compression program is determined by the
00530    magic number, not the filename.  */
00531 
00532 FILE *
00533 open_possibly_compressed_file (char *filename,
00534     void (*create_callback) (char *),
00535     char **opened_filename, char **compression_program, int *is_pipe) 
00536 {
00537   char *local_opened_filename, *local_compression_program;
00538   int nread;
00539   char data[4];
00540   FILE *f;
00541 
00542   /* We let them pass NULL if they don't want this info, but it's easier
00543      to always determine it.  */
00544   if (!opened_filename)
00545     opened_filename = &local_opened_filename;
00546 
00547   *opened_filename = filename;
00548   f = fopen (*opened_filename, FOPEN_RBIN);
00549   if (!f)
00550     {
00551       *opened_filename = concat (filename, ".gz", "");
00552       f = fopen (*opened_filename, FOPEN_RBIN);
00553   if (!f)
00554     {
00555       free (*opened_filename);
00556       *opened_filename = concat (filename, ".bz2", "");
00557       f = fopen (*opened_filename, FOPEN_RBIN);
00558     }
00559 
00560 #ifdef __MSDOS__
00561       if (!f)
00562         {
00563           free (*opened_filename);
00564           *opened_filename = concat (filename, ".igz", "");
00565           f = fopen (*opened_filename, FOPEN_RBIN);
00566         }
00567       if (!f)
00568         {
00569           free (*opened_filename);
00570           *opened_filename = concat (filename, ".inz", "");
00571           f = fopen (*opened_filename, FOPEN_RBIN);
00572         }
00573 #endif
00574       if (!f)
00575         {
00576           if (create_callback)
00577             { /* That didn't work either.  Create the file if we can.  */
00578               (*create_callback) (filename);
00579 
00580               /* And try opening it again.  */
00581               free (*opened_filename);
00582               *opened_filename = filename;
00583               f = fopen (*opened_filename, FOPEN_RBIN);
00584               if (!f)
00585                 pfatal_with_name (filename);
00586             }
00587           else
00588             pfatal_with_name (filename);
00589         }
00590     }
00591 
00592   /* Read first few bytes of file rather than relying on the filename.
00593      If the file is shorter than this it can't be usable anyway.  */
00594   nread = fread (data, sizeof (data), 1, f);
00595   if (nread != 1)
00596     {
00597       /* Empty files don't set errno, so we get something like
00598          "install-info: No error for foo", which is confusing.  */
00599       if (nread == 0)
00600         fatal (_("%s: empty file"), *opened_filename, 0);
00601       pfatal_with_name (*opened_filename);
00602     }
00603 
00604   if (!compression_program)
00605     compression_program = &local_compression_program;
00606 
00607   if (data[0] == '\x1f' && data[1] == '\x8b')
00608 #if STRIP_DOT_EXE
00609     /* An explicit .exe yields a better diagnostics from popen below
00610        if they don't have gzip installed.  */
00611     *compression_program = "gzip.exe";
00612 #else
00613     *compression_program = "gzip";
00614 #endif
00615   else if(data[0] == 'B' && data[1] == 'Z' && data[2] == 'h')
00616 #ifndef STRIP_DOT_EXE
00617     *compression_program = "bzip2.exe";
00618 #else
00619     *compression_program = "bzip2";
00620 #endif
00621   else if(data[0] == 'B' && data[1] == 'Z' && data[2] == '0')
00622 #ifndef STRIP_DOT_EXE
00623     *compression_program = "bzip.exe";
00624 #else
00625     *compression_program = "bzip";
00626 #endif
00627   else
00628     *compression_program = NULL;
00629 
00630   if (*compression_program)
00631     { /* It's compressed, so fclose the file and then open a pipe.  */
00632       char *command = concat (*compression_program," -cd <", *opened_filename);
00633       if (fclose (f) < 0)
00634         pfatal_with_name (*opened_filename);
00635       f = popen (command, "r");
00636       if (f)
00637         *is_pipe = 1;
00638       else
00639         pfatal_with_name (command);
00640     }
00641   else
00642     { /* It's a plain file, seek back over the magic bytes.  */
00643       if (fseek (f, 0, 0) < 0)
00644         pfatal_with_name (*opened_filename);
00645 #if O_BINARY
00646       /* Since this is a text file, and we opened it in binary mode,
00647          switch back to text mode.  */
00648       f = freopen (*opened_filename, "r", f);
00649 #endif
00650       *is_pipe = 0;
00651     }
00652 
00653   return f;
00654 }
00655 
00656 /* Read all of file FILENAME into memory and return the address of the
00657    data.  Store the size of the data into SIZEP.  If need be, uncompress
00658    (i.e., try FILENAME.gz et al. if FILENAME does not exist) and store
00659    the actual file name that was opened into OPENED_FILENAME (if it is
00660    non-NULL), and the companion compression program (if any, else NULL)
00661    into COMPRESSION_PROGRAM (if that is non-NULL).  If trouble, do
00662    a fatal error.  */
00663 
00664 char *
00665 readfile (char *filename, int *sizep,
00666     void (*create_callback) (char *), char **opened_filename,
00667     char **compression_program)
00668 {
00669   char *real_name;
00670   FILE *f;
00671   int pipe_p;
00672   int filled = 0;
00673   int data_size = 8192;
00674   char *data = xmalloc (data_size);
00675 
00676   /* If they passed the space for the file name to return, use it.  */
00677   f = open_possibly_compressed_file (filename, create_callback,
00678                                      opened_filename ? opened_filename
00679                                                      : &real_name,
00680                                      compression_program, &pipe_p);
00681 
00682   for (;;)
00683     {
00684       int nread = fread (data + filled, 1, data_size - filled, f);
00685       if (nread < 0)
00686         pfatal_with_name (real_name);
00687       if (nread == 0)
00688         break;
00689 
00690       filled += nread;
00691       if (filled == data_size)
00692         {
00693           data_size += 65536;
00694           data = xrealloc (data, data_size);
00695         }
00696     }
00697 
00698   /* We'll end up wasting space if we're not passing the filename back
00699      and it is not just FILENAME, but so what.  */
00700   /* We need to close the stream, since on some systems the pipe created
00701      by popen is simulated by a temporary file which only gets removed
00702      inside pclose.  */
00703   if (pipe_p)
00704     pclose (f);
00705   else
00706     fclose (f);
00707 
00708   *sizep = filled;
00709   return data;
00710 }
00711 
00712 /* Output the old dir file, interpolating the new sections
00713    and/or new entries where appropriate.  If COMPRESSION_PROGRAM is not
00714    null, pipe to it to create DIRFILE.  Thus if we read dir.gz on input,
00715    we'll write dir.gz on output.  */
00716 
00717 static void
00718 output_dirfile (char *dirfile, int dir_nlines, struct line_data *dir_lines,
00719                 int n_entries_to_add, struct spec_entry *entries_to_add,
00720                 struct spec_section *input_sections, char *compression_program)
00721 {
00722   int i;
00723   FILE *output;
00724 
00725   if (compression_program)
00726     {
00727       char *command = concat (compression_program, ">", dirfile);
00728       output = popen (command, "w");
00729     }
00730   else
00731     output = fopen (dirfile, "w");
00732 
00733   if (!output)
00734     {
00735       perror (dirfile);
00736       xexit (1);
00737     }
00738 
00739   for (i = 0; i <= dir_nlines; i++)
00740     {
00741       int j;
00742 
00743       /* If we decided to output some new entries before this line,
00744          output them now.  */
00745       if (dir_lines[i].add_entries_before)
00746         for (j = 0; j < n_entries_to_add; j++)
00747           {
00748             struct spec_entry *this = dir_lines[i].add_entries_before[j];
00749             if (this == 0)
00750               break;
00751             fputs (this->text, output);
00752           }
00753       /* If we decided to add some sections here
00754          because there are no such sections in the file,
00755          output them now.  */
00756       if (dir_lines[i].add_sections_before)
00757         {
00758           struct spec_section *spec;
00759           struct spec_section **sections;
00760           int n_sections = 0;
00761           struct spec_entry *entry;
00762           struct spec_entry **entries;
00763           int n_entries = 0;
00764 
00765           /* Count the sections and allocate a vector for all of them.  */
00766           for (spec = input_sections; spec; spec = spec->next)
00767             n_sections++;
00768           sections = ((struct spec_section **)
00769                       xmalloc (n_sections * sizeof (struct spec_section *)));
00770 
00771           /* Fill the vector SECTIONS with pointers to all the sections,
00772              and sort them.  */
00773           j = 0;
00774           for (spec = input_sections; spec; spec = spec->next)
00775             sections[j++] = spec;
00776           qsort (sections, n_sections, sizeof (struct spec_section *),
00777                  compare_section_names);
00778 
00779           /* Count the entries and allocate a vector for all of them.  */
00780           for (entry = entries_to_add; entry; entry = entry->next)
00781             n_entries++;
00782           entries = ((struct spec_entry **)
00783                      xmalloc (n_entries * sizeof (struct spec_entry *)));
00784 
00785           /* Fill the vector ENTRIES with pointers to all the sections,
00786              and sort them.  */
00787           j = 0;
00788           for (entry = entries_to_add; entry; entry = entry->next)
00789             entries[j++] = entry;
00790           qsort (entries, n_entries, sizeof (struct spec_entry *),
00791                  compare_entries_text);
00792 
00793           /* Generate the new sections in alphabetical order.  In each
00794              new section, output all of the entries that belong to that
00795              section, in alphabetical order.  */
00796           for (j = 0; j < n_sections; j++)
00797             {
00798               spec = sections[j];
00799               if (spec->missing)
00800                 {
00801                   int k;
00802 
00803                   putc ('\n', output);
00804                   fputs (spec->name, output);
00805                   putc ('\n', output);
00806                   for (k = 0; k < n_entries; k++)
00807                     {
00808                       struct spec_section *spec1;
00809                       /* Did they at all want this entry to be put into
00810                          this section?  */
00811                       entry = entries[k];
00812                       for (spec1 = entry->entry_sections;
00813                            spec1 && spec1 != entry->entry_sections_tail;
00814                            spec1 = spec1->next)
00815                         {
00816                           if (!strcmp (spec1->name, spec->name))
00817                             break;
00818                         }
00819                       if (spec1 && spec1 != entry->entry_sections_tail)
00820                         fputs (entry->text, output);
00821                     }
00822                 }
00823             }
00824 
00825           free (entries);
00826           free (sections);
00827         }
00828 
00829       /* Output the original dir lines unless marked for deletion.  */
00830       if (i < dir_nlines && !dir_lines[i].delete)
00831         {
00832           fwrite (dir_lines[i].start, 1, dir_lines[i].size, output);
00833           putc ('\n', output);
00834         }
00835     }
00836 
00837   /* Some systems, such as MS-DOS, simulate pipes with temporary files.
00838      On those systems, the compressor actually gets run inside pclose,
00839      so we must call pclose.  */
00840   if (compression_program)
00841     pclose (output);
00842   else
00843     fclose (output);
00844 }
00845 
00846 /* Parse the input to find the section names and the entry names it
00847    specifies.  Return the number of entries to add from this file.  */
00848 int
00849 parse_input (const struct line_data *lines, int nlines,
00850              struct spec_section **sections, struct spec_entry **entries) 
00851 {
00852   int n_entries = 0;
00853   int prefix_length = strlen ("INFO-DIR-SECTION ");
00854   struct spec_section *head = *sections, *tail = NULL;
00855   int reset_tail = 0;
00856   char *start_of_this_entry = 0;
00857   int ignore_sections = *sections != 0;
00858   int ignore_entries  = *entries  != 0;
00859 
00860   int i;
00861 
00862   if (ignore_sections && ignore_entries)
00863     return 0;
00864 
00865   /* Loop here processing lines from the input file.  Each
00866      INFO-DIR-SECTION entry is added to the SECTIONS linked list.
00867      Each START-INFO-DIR-ENTRY block is added to the ENTRIES linked
00868      list, and all its entries inherit the chain of SECTION entries
00869      defined by the last group of INFO-DIR-SECTION entries we have
00870      seen until that point.  */
00871   for (i = 0; i < nlines; i++)
00872     {
00873       if (!ignore_sections
00874           && !strncmp ("INFO-DIR-SECTION ", lines[i].start, prefix_length))
00875         {
00876           struct spec_section *next
00877             = (struct spec_section *) xmalloc (sizeof (struct spec_section));
00878           next->name = copy_string (lines[i].start + prefix_length,
00879                                     lines[i].size - prefix_length);
00880           next->next = *sections;
00881           next->missing = 1;
00882           if (reset_tail)
00883             {
00884               tail = *sections;
00885               reset_tail = 0;
00886             }
00887           *sections = next;
00888           head = *sections;
00889         }
00890       /* If entries were specified explicitly with command options,
00891          ignore the entries in the input file.  */
00892       else if (!ignore_entries)
00893         {
00894           if (!strncmp ("START-INFO-DIR-ENTRY", lines[i].start, lines[i].size)
00895               && sizeof ("START-INFO-DIR-ENTRY") - 1 == lines[i].size)
00896             {
00897               if (!*sections)
00898                 {
00899                   /* We found an entry, but didn't yet see any sections
00900                      specified.  Default to section "Miscellaneous".  */
00901                   *sections = (struct spec_section *)
00902                     xmalloc (sizeof (struct spec_section));
00903                   (*sections)->name = "Miscellaneous";
00904                   (*sections)->next = 0;
00905                   (*sections)->missing = 1;
00906                   head = *sections;
00907                 }
00908               /* Next time we see INFO-DIR-SECTION, we will reset the
00909                  tail pointer.  */
00910               reset_tail = 1;
00911 
00912               if (start_of_this_entry != 0)
00913                 fatal (_("START-INFO-DIR-ENTRY without matching END-INFO-DIR-ENTRY"), 0, 0);
00914               start_of_this_entry = lines[i + 1].start;
00915             }
00916           else if (start_of_this_entry)
00917             {
00918               if ((!strncmp ("* ", lines[i].start, 2)
00919                    && lines[i].start > start_of_this_entry)
00920                   || (!strncmp ("END-INFO-DIR-ENTRY",
00921                                 lines[i].start, lines[i].size)
00922                       && sizeof ("END-INFO-DIR-ENTRY") - 1 == lines[i].size))
00923                 {
00924                   /* We found an end of this entry.  Allocate another
00925                      entry, fill its data, and add it to the linked
00926                      list.  */
00927                   struct spec_entry *next
00928                     = (struct spec_entry *) xmalloc (sizeof (struct spec_entry));
00929                   next->text
00930                     = copy_string (start_of_this_entry,
00931                                    lines[i].start - start_of_this_entry);
00932                   next->text_len = lines[i].start - start_of_this_entry;
00933                   next->entry_sections = head;
00934                   next->entry_sections_tail = tail;
00935                   next->next = *entries;
00936                   *entries = next;
00937                   n_entries++;
00938                   if (!strncmp ("END-INFO-DIR-ENTRY",
00939                                 lines[i].start, lines[i].size)
00940                       && sizeof ("END-INFO-DIR-ENTRY") - 1 == lines[i].size)
00941                     start_of_this_entry = 0;
00942                   else
00943                     start_of_this_entry = lines[i].start;
00944                 }
00945               else if (!strncmp ("END-INFO-DIR-ENTRY",
00946                                  lines[i].start, lines[i].size)
00947                        && sizeof ("END-INFO-DIR-ENTRY") - 1 == lines[i].size)
00948                 fatal (_("END-INFO-DIR-ENTRY without matching START-INFO-DIR-ENTRY"), 0, 0);
00949             }
00950         }
00951     }
00952   if (start_of_this_entry != 0)
00953     fatal (_("START-INFO-DIR-ENTRY without matching END-INFO-DIR-ENTRY"),
00954            0, 0);
00955 
00956   /* If we ignored the INFO-DIR-ENTRY directives, we need now go back
00957      and plug the names of all the sections we found into every
00958      element of the ENTRIES list.  */
00959   if (ignore_entries && *entries)
00960     {
00961       struct spec_entry *entry;
00962 
00963       for (entry = *entries; entry; entry = entry->next)
00964         {
00965           entry->entry_sections = head;
00966           entry->entry_sections_tail = tail;
00967         }
00968     }
00969 
00970   return n_entries;
00971 }
00972 
00973 /* Parse the dir file whose basename is BASE_NAME.  Find all the
00974    nodes, and their menus, and the sections of their menus.  */
00975 int
00976 parse_dir_file (struct line_data *lines, int nlines, struct node **nodes,
00977                 const char *base_name)
00978 {
00979   int node_header_flag = 0;
00980   int something_deleted = 0;
00981   int i;
00982 
00983   *nodes = 0;
00984   for (i = 0; i < nlines; i++)
00985     {
00986       /* Parse node header lines.  */
00987       if (node_header_flag)
00988         {
00989           int j, end;
00990           for (j = 0; j < lines[i].size; j++)
00991             /* Find the node name and store it in the `struct node'.  */
00992             if (!strncmp ("Node:", lines[i].start + j, 5))
00993               {
00994                 char *line = lines[i].start;
00995                 /* Find the start of the node name.  */
00996                 j += 5;
00997                 while (line[j] == ' ' || line[j] == '\t')
00998                   j++;
00999                 /* Find the end of the node name.  */
01000                 end = j;
01001                 while (line[end] != 0 && line[end] != ',' && line[end] != '\n'
01002                        && line[end] != '\t')
01003                   end++;
01004                 (*nodes)->name = copy_string (line + j, end - j);
01005               }
01006           node_header_flag = 0;
01007         }
01008 
01009       /* Notice the start of a node.  */
01010       if (*lines[i].start == 037)
01011         {
01012           struct node *next = (struct node *) xmalloc (sizeof (struct node));
01013 
01014           next->next = *nodes;
01015           next->name = NULL;
01016           next->start_line = i;
01017           next->end_line = 0;
01018           next->menu_start = NULL;
01019           next->sections = NULL;
01020           next->last_section = NULL;
01021 
01022           if (*nodes != 0)
01023             (*nodes)->end_line = i;
01024           /* Fill in the end of the last menu section
01025              of the previous node.  */
01026           if (*nodes != 0 && (*nodes)->last_section != 0)
01027             (*nodes)->last_section->end_line = i;
01028 
01029           *nodes = next;
01030 
01031           /* The following line is the header of this node;
01032              parse it.  */
01033           node_header_flag = 1;
01034         }
01035 
01036       /* Notice the lines that start menus.  */
01037       if (*nodes != 0 && !strncmp ("* Menu:", lines[i].start, 7))
01038         (*nodes)->menu_start = lines[i + 1].start;
01039 
01040       /* Notice sections in menus.  */
01041       if (*nodes != 0
01042           && (*nodes)->menu_start != 0
01043           && *lines[i].start != '\n'
01044           && *lines[i].start != '*'
01045           && *lines[i].start != ' '
01046           && *lines[i].start != '\t')
01047         {
01048           /* Add this menu section to the node's list.
01049              This list grows in forward order.  */
01050           struct menu_section *next
01051             = (struct menu_section *) xmalloc (sizeof (struct menu_section));
01052 
01053           next->start_line = i + 1;
01054           next->next = 0;
01055           next->end_line = 0;
01056           next->name = copy_string (lines[i].start, lines[i].size);
01057           if ((*nodes)->sections)
01058             {
01059               (*nodes)->last_section->next = next;
01060               (*nodes)->last_section->end_line = i;
01061             }
01062           else
01063             (*nodes)->sections = next;
01064           (*nodes)->last_section = next;
01065         }
01066 
01067       /* Check for an existing entry that should be deleted.
01068          Delete all entries which specify this file name.  */
01069       if (*lines[i].start == '*')
01070         {
01071           char *q;
01072           char *p = lines[i].start;
01073 
01074           p++; /* skip * */
01075           while (*p == ' ') p++; /* ignore following spaces */
01076           q = p; /* remember this, it's the beginning of the menu item.  */
01077 
01078           /* Read menu item.  */
01079           while (*p != 0 && *p != ':')
01080             p++;
01081           p++; /* skip : */
01082 
01083           if (*p == ':')
01084             { /* XEmacs-style entry, as in * Mew::Messaging.  */
01085               if (menu_item_equal (q, ':', base_name))
01086                 {
01087                   lines[i].delete = 1;
01088                   something_deleted = 1;
01089                 }
01090             }
01091           else
01092             { /* Emacs-style entry, as in * Emacs: (emacs).  */
01093               while (*p == ' ') p++; /* skip spaces after : */
01094               if (*p == '(')         /* if at parenthesized (FILENAME) */
01095                 {
01096                   p++;
01097                   if (menu_item_equal (p, ')', base_name))
01098                     {
01099                       lines[i].delete = 1;
01100                       something_deleted = 1;
01101                     }
01102                 }
01103             }
01104         }
01105 
01106       /* Treat lines that start with whitespace
01107          as continuations; if we are deleting an entry,
01108          delete all its continuations as well.  */
01109       else if (i > 0 && (*lines[i].start == ' ' || *lines[i].start == '\t'))
01110         {
01111           lines[i].delete = lines[i - 1].delete;
01112         }
01113     }
01114 
01115   /* Finish the info about the end of the last node.  */
01116   if (*nodes != 0)
01117     {
01118       (*nodes)->end_line = nlines;
01119       if ((*nodes)->last_section != 0)
01120         (*nodes)->last_section->end_line = nlines;
01121     }
01122 
01123   return something_deleted;
01124 }
01125 
01126 int
01127 main (int argc, char **argv)
01128 {
01129   char *opened_dirfilename;
01130   char *compression_program;
01131   char *infile_sans_info;
01132   char *infile = 0, *dirfile = 0;
01133 
01134   /* Record the text of the Info file, as a sequence of characters
01135      and as a sequence of lines.  */
01136   char *input_data = NULL;
01137   int input_size = 0;
01138   struct line_data *input_lines = NULL;
01139   int input_nlines = 0;
01140 
01141   /* Record here the specified section names and directory entries.  */
01142   struct spec_section *input_sections = NULL;
01143   struct spec_entry *entries_to_add = NULL;
01144   int n_entries_to_add = 0;
01145 
01146   /* Record the old text of the dir file, as plain characters,
01147      as lines, and as nodes.  */
01148   char *dir_data;
01149   int dir_size;
01150   int dir_nlines;
01151   struct line_data *dir_lines;
01152   struct node *dir_nodes;
01153 
01154   /* Nonzero means --delete was specified (just delete existing entries).  */
01155   int delete_flag = 0;
01156   int something_deleted = 0;
01157   /* Nonzero means -q was specified.  */
01158   int quiet_flag = 0;
01159 
01160   int i;
01161 
01162 #ifdef HAVE_SETLOCALE
01163   /* Set locale via LC_ALL.  */
01164   setlocale (LC_ALL, "");
01165 #endif
01166 
01167   /* Set the text message domain.  */
01168   bindtextdomain (PACKAGE, LOCALEDIR);
01169   textdomain (PACKAGE);
01170 
01171   while (1)
01172     {
01173       int opt = getopt_long (argc, argv, "i:d:e:s:hHr", longopts, 0);
01174 
01175       if (opt == EOF)
01176         break;
01177 
01178       switch (opt)
01179         {
01180         case 0:
01181           /* If getopt returns 0, then it has already processed a
01182              long-named option.  We should do nothing.  */
01183           break;
01184 
01185         case 1:
01186           abort ();
01187 
01188         case 'd':
01189           if (dirfile)
01190             {
01191               fprintf (stderr, _("%s: already have dir file: %s\n"),
01192                        progname, dirfile);
01193               suggest_asking_for_help ();
01194             }
01195           dirfile = optarg;
01196           break;
01197 
01198         case 'D':
01199           if (dirfile)
01200             {
01201               fprintf (stderr, _("%s: already have dir file: %s\n"),
01202                        progname, dirfile);
01203               suggest_asking_for_help ();
01204             }
01205           dirfile = concat (optarg, "", "/dir");
01206           break;
01207 
01208         case 'e':
01209           {
01210             struct spec_entry *next
01211               = (struct spec_entry *) xmalloc (sizeof (struct spec_entry));
01212             int olen = strlen (optarg);
01213             if (! (*optarg != 0 && optarg[olen - 1] == '\n'))
01214               {
01215                 optarg = concat (optarg, "\n", "");
01216                 olen++;
01217               }
01218             next->text = optarg;
01219             next->text_len = olen;
01220             next->entry_sections = NULL;
01221             next->entry_sections_tail = NULL;
01222             next->next = entries_to_add;
01223             entries_to_add = next;
01224             n_entries_to_add++;
01225           }
01226           break;
01227 
01228         case 'h':
01229         case 'H':
01230           print_help ();
01231           xexit (0);
01232 
01233         case 'i':
01234           if (infile)
01235             {
01236               fprintf (stderr, _("%s: Specify the Info file only once.\n"),
01237                        progname);
01238               suggest_asking_for_help ();
01239             }
01240           infile = optarg;
01241           break;
01242 
01243         case 'q':
01244           quiet_flag = 1;
01245           break;
01246 
01247         case 'r':
01248           delete_flag = 1;
01249           break;
01250 
01251         case 's':
01252           {
01253             struct spec_section *next
01254               = (struct spec_section *) xmalloc (sizeof (struct spec_section));
01255             next->name = optarg;
01256             next->next = input_sections;
01257             next->missing = 1;
01258             input_sections = next;
01259           }
01260           break;
01261 
01262         case 'V':
01263           printf ("install-info (GNU %s) %s\n", PACKAGE, VERSION);
01264           puts ("");
01265           puts ("Copyright (C) 2004 Free Software Foundation, Inc.");
01266           printf (_("There is NO warranty.  You may redistribute this software\n\
01267 under the terms of the GNU General Public License.\n\
01268 For more information about these matters, see the files named COPYING.\n"));
01269           xexit (0);
01270 
01271         default:
01272           suggest_asking_for_help ();
01273         }
01274     }
01275 
01276   /* Interpret the non-option arguments as file names.  */
01277   for (; optind < argc; ++optind)
01278     {
01279       if (infile == 0)
01280         infile = argv[optind];
01281       else if (dirfile == 0)
01282         dirfile = argv[optind];
01283       else
01284         error (_("excess command line argument `%s'"), argv[optind], 0);
01285     }
01286 
01287   if (!infile)
01288     fatal (_("No input file specified; try --help for more information."),
01289            0, 0);
01290   if (!dirfile)
01291     fatal (_("No dir file specified; try --help for more information."), 0, 0);
01292 
01293   /* Read the Info file and parse it into lines, unless we're deleting.  */
01294   if (!delete_flag)
01295     {
01296       input_data = readfile (infile, &input_size, NULL, NULL, NULL);
01297       input_lines = findlines (input_data, input_size, &input_nlines);
01298     }
01299 
01300   i = parse_input (input_lines, input_nlines,
01301                    &input_sections, &entries_to_add);
01302   if (i > n_entries_to_add)
01303     n_entries_to_add = i;
01304 
01305   if (!delete_flag)
01306     {
01307       if (entries_to_add == 0)
01308         { /* No need to abort here, the original info file may not
01309              have the requisite Texinfo commands.  This is not
01310              something an installer should have to correct (it's a
01311              problem for the maintainer), and there's no need to cause
01312              subsequent parts of `make install' to fail.  */
01313           warning (_("no info dir entry in `%s'"), infile, 0);
01314           xexit (0);
01315         }
01316 
01317       /* If the entries came from the command-line arguments, their
01318          entry_sections pointers are not yet set.  Walk the chain of
01319          the entries and for each entry update entry_sections to point
01320          to the head of the list of sections where this entry should
01321          be put.  Note that all the entries specified on the command
01322          line get put into ALL the sections we've got, either from the
01323          Info file, or (under --section) from the command line,
01324          because in the loop below every entry inherits the entire
01325          chain of sections.  */
01326       if (n_entries_to_add > 0 && entries_to_add->entry_sections == NULL)
01327         {
01328           struct spec_entry *ep;
01329 
01330           /* If we got no sections, default to "Miscellaneous".  */
01331           if (input_sections == NULL)
01332             {
01333               input_sections = (struct spec_section *)
01334                 xmalloc (sizeof (struct spec_section));
01335               input_sections->name = "Miscellaneous";
01336               input_sections->next = NULL;
01337               input_sections->missing = 1;
01338             }
01339           for (ep = entries_to_add; ep; ep = ep->next)
01340             ep->entry_sections = input_sections;
01341         }
01342     }
01343 
01344   /* Now read in the Info dir file.  */
01345   dir_data = readfile (dirfile, &dir_size, ensure_dirfile_exists,
01346                        &opened_dirfilename, &compression_program);
01347   dir_lines = findlines (dir_data, dir_size, &dir_nlines);
01348 
01349   /* We will be comparing the entries in the dir file against the
01350      current filename, so need to strip off any directory prefix and/or
01351      [.info][.gz] suffix.  */
01352   {
01353     char *infile_basename = infile + strlen (infile);
01354 
01355     if (HAVE_DRIVE (infile))
01356       infile += 2;      /* get past the drive spec X: */
01357 
01358     while (infile_basename > infile && !IS_SLASH (infile_basename[-1]))
01359       infile_basename--;
01360 
01361     infile_sans_info = strip_info_suffix (infile_basename);
01362   }
01363 
01364   something_deleted
01365     = parse_dir_file (dir_lines, dir_nlines, &dir_nodes, infile_sans_info);
01366 
01367   /* Decide where to add the new entries (unless --delete was used).
01368      Find the menu sections to add them in.
01369      In each section, find the proper alphabetical place to add
01370      each of the entries.  */
01371   if (!delete_flag)
01372     {
01373       struct node *node;
01374       struct menu_section *section;
01375       struct spec_section *spec;
01376 
01377       for (node = dir_nodes; node; node = node->next)
01378         for (section = node->sections; section; section = section->next)
01379           {
01380             for (i = section->end_line; i > section->start_line; i--)
01381               if (dir_lines[i - 1].size != 0)
01382                 break;
01383             section->end_line = i;
01384 
01385             for (spec = input_sections; spec; spec = spec->next)
01386               if (!strcmp (spec->name, section->name))
01387                 break;
01388             if (spec)
01389               {
01390                 int add_at_line = section->end_line;
01391                 struct spec_entry *entry;
01392                 /* Say we have found at least one section with this name,
01393                    so we need not add such a section.  */
01394                 spec->missing = 0;
01395                 /* For each entry, find the right place in this section
01396                    to add it.  */
01397                 for (entry = entries_to_add; entry; entry = entry->next)
01398                   {
01399                     /* Did they at all want this entry to be put into
01400                        this section?  */
01401                     for (spec = entry->entry_sections;
01402                          spec && spec != entry->entry_sections_tail;
01403                          spec = spec->next)
01404                       {
01405                         if (!strcmp (spec->name, section->name))
01406                           break;
01407                       }
01408                     if (!spec || spec == entry->entry_sections_tail)
01409                       continue;
01410 
01411                     /* Subtract one because dir_lines is zero-based,
01412                        but the `end_line' and `start_line' members are
01413                        one-based.  */
01414                     for (i = section->end_line - 1;
01415                          i >= section->start_line - 1; i--)
01416                       {
01417                         /* If an entry exists with the same name,
01418                            and was not marked for deletion
01419                            (which means it is for some other file),
01420                            we are in trouble.  */
01421                         if (dir_lines[i].start[0] == '*'
01422                             && menu_line_equal (entry->text, entry->text_len,
01423                                                 dir_lines[i].start,
01424                                                 dir_lines[i].size)
01425                             && !dir_lines[i].delete)
01426                           fatal (_("menu item `%s' already exists, for file `%s'"),
01427                                  extract_menu_item_name (entry->text),
01428                                  extract_menu_file_name (dir_lines[i].start));
01429                         if (dir_lines[i].start[0] == '*'
01430                             && menu_line_lessp (entry->text, entry->text_len,
01431                                                 dir_lines[i].start,
01432                                                 dir_lines[i].size))
01433                           add_at_line = i;
01434                       }
01435                     insert_entry_here (entry, add_at_line,
01436                                        dir_lines, n_entries_to_add);
01437                   }
01438               }
01439           }
01440 
01441       /* Mark the end of the Top node as the place to add any
01442          new sections that are needed.  */
01443       for (node = dir_nodes; node; node = node->next)
01444         if (node->name && strcmp (node->name, "Top") == 0)
01445           dir_lines[node->end_line].add_sections_before = 1;
01446     }
01447 
01448   if (delete_flag && !something_deleted && !quiet_flag)
01449     warning (_("no entries found for `%s'; nothing deleted"), infile, 0);
01450 
01451   output_dirfile (opened_dirfilename, dir_nlines, dir_lines, n_entries_to_add,
01452                   entries_to_add, input_sections, compression_program);
01453 
01454   xexit (0);
01455   return 0; /* Avoid bogus warnings.  */
01456 }
01457 
01458 /* Divide the text at DATA (of SIZE bytes) into lines.
01459    Return a vector of struct line_data describing the lines.
01460    Store the length of that vector into *NLINESP.  */
01461 
01462 struct line_data *
01463 findlines (char *data, int size, int *nlinesp)
01464 {
01465   int i;
01466   int lineflag = 1;
01467   int lines_allocated = 511;
01468   int filled = 0;
01469   struct line_data *lines
01470     = xmalloc ((lines_allocated + 1) * sizeof (struct line_data));
01471 
01472   for (i = 0; i < size; i++)
01473     {
01474       if (lineflag)
01475         {
01476           if (filled == lines_allocated)
01477             {
01478               /* try to keep things somewhat page-aligned */
01479               lines_allocated = ((lines_allocated + 1) * 2) - 1;
01480               lines = xrealloc (lines, (lines_allocated + 1)
01481                                        * sizeof (struct line_data));
01482             }
01483           lines[filled].start = &data[i];
01484           lines[filled].add_entries_before = 0;
01485           lines[filled].add_sections_before = 0;
01486           lines[filled].delete = 0;
01487           if (filled > 0)
01488             lines[filled - 1].size
01489               = lines[filled].start - lines[filled - 1].start - 1;
01490           filled++;
01491         }
01492       lineflag = (data[i] == '\n');
01493     }
01494   if (filled > 0)
01495     lines[filled - 1].size = &data[i] - lines[filled - 1].start - lineflag;
01496 
01497   /* Do not leave garbage in the last element.  */
01498   lines[filled].start = NULL;
01499   lines[filled].add_entries_before = NULL;
01500   lines[filled].add_sections_before = 0;
01501   lines[filled].delete = 0;
01502   lines[filled].size = 0;
01503 
01504   *nlinesp = filled;
01505   return lines;
01506 }
01507 
01508 /* This is the comparison function for qsort for a vector of pointers to
01509    struct spec_section.  (Have to use const void * as the parameter type
01510    to avoid incompatible-with-qsort warnings.)
01511    Compare the section names.  */
01512 
01513 int
01514 compare_section_names (const void *p1, const void *p2)
01515 {
01516   struct spec_section **sec1 = (struct spec_section **) p1;
01517   struct spec_section **sec2 = (struct spec_section **) p2;
01518   char *name1 = (*sec1)->name;
01519   char *name2 = (*sec2)->name;
01520   return strcmp (name1, name2);
01521 }
01522 
01523 /* This is the comparison function for qsort
01524    for a vector of pointers to struct spec_entry.
01525    Compare the entries' text.  */
01526 
01527 int
01528 compare_entries_text (const void *p1, const void *p2)
01529 {
01530   struct spec_entry **entry1 = (struct spec_entry **) p1;
01531   struct spec_entry **entry2 = (struct spec_entry **) p2;
01532   char *text1 = (*entry1)->text;
01533   char *text2 = (*entry2)->text;
01534   char *colon1 = strchr (text1, ':');
01535   char *colon2 = strchr (text2, ':');
01536   int len1, len2;
01537 
01538   if (!colon1)
01539     len1 = strlen (text1);
01540   else
01541     len1 = colon1 - text1;
01542   if (!colon2)
01543     len2 = strlen (text2);
01544   else
01545     len2 = colon2 - text2;
01546   return strncmp (text1, text2, len1 <= len2 ? len1 : len2);
01547 }
01548 
01549 /* Insert ENTRY into the add_entries_before vector
01550    for line number LINE_NUMBER of the dir file.
01551    DIR_LINES and N_ENTRIES carry information from like-named variables
01552    in main.  */
01553 
01554 void
01555 insert_entry_here (struct spec_entry *entry, int line_number,
01556                    struct line_data *dir_lines, int n_entries)
01557 {
01558   int i, j;
01559 
01560   if (dir_lines[line_number].add_entries_before == 0)
01561     {
01562       dir_lines[line_number].add_entries_before
01563         = (struct spec_entry **) xmalloc (n_entries * sizeof (struct spec_entry *));
01564       for (i = 0; i < n_entries; i++)
01565         dir_lines[line_number].add_entries_before[i] = 0;
01566     }
01567 
01568   /* Find the place where this entry belongs.  If there are already
01569      several entries to add before LINE_NUMBER, make sure they are in
01570      alphabetical order.  */
01571   for (i = 0; i < n_entries; i++)
01572     if (dir_lines[line_number].add_entries_before[i] == 0
01573         || menu_line_lessp (entry->text, strlen (entry->text),
01574                             dir_lines[line_number].add_entries_before[i]->text,
01575                             strlen (dir_lines[line_number].add_entries_before[i]->text)))
01576       break;
01577 
01578   if (i == n_entries)
01579     abort ();
01580 
01581   /* If we need to plug ENTRY into the middle of the
01582      ADD_ENTRIES_BEFORE array, move the entries which should be output
01583      after this one down one notch, before adding a new one.  */
01584   if (dir_lines[line_number].add_entries_before[i] != 0)
01585     for (j = n_entries - 1; j > i; j--)
01586       dir_lines[line_number].add_entries_before[j]
01587         = dir_lines[line_number].add_entries_before[j - 1];
01588 
01589   dir_lines[line_number].add_entries_before[i] = entry;
01590 }