193143Sru/* $FreeBSD$ */ 221495Sjmacd/* install-info -- create Info directory entry(ies) for an Info file. 3146521Sru $Id: install-info.c,v 1.12 2004/04/11 17:56:47 karl Exp $ 421495Sjmacd 5146521Sru Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004 Free Software 6114478Sru Foundation, Inc. 721495Sjmacd 842664Smarkm This program is free software; you can redistribute it and/or modify 942664Smarkm it under the terms of the GNU General Public License as published by 1042664Smarkm the Free Software Foundation; either version 2 of the License, or 1142664Smarkm (at your option) any later version. 1221495Sjmacd 1342664Smarkm This program is distributed in the hope that it will be useful, 1442664Smarkm but WITHOUT ANY WARRANTY; without even the implied warranty of 1542664Smarkm MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 1642664Smarkm GNU General Public License for more details. 1721495Sjmacd 1842664Smarkm You should have received a copy of the GNU General Public License 1942664Smarkm along with this program; if not, write to the Free Software 2042664Smarkm Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.*/ 2121495Sjmacd 2242664Smarkm#include "system.h" 2321495Sjmacd#include <getopt.h> 2421495Sjmacd 2556165Srustatic char *progname = "install-info"; 2656165Srustatic char *default_section = NULL; 2721495Sjmacd 28146521Srustruct spec_entry; 29146521Srustruct spec_section; 3021495Sjmacd 31146521Srustruct line_data *findlines (char *data, int size, int *nlinesp); 32146521Sruvoid insert_entry_here (struct spec_entry *entry, int line_number, 33146521Sru struct line_data *dir_lines, int n_entries); 34146521Sruint compare_section_names (const void *s1, const void *s2); 35146521Sruint compare_entries_text (const void *e1, const void *e2); 3621495Sjmacd 3721495Sjmacd/* Data structures. */ 3821495Sjmacd 3921495Sjmacd 4042664Smarkm/* Record info about a single line from a file as read into core. */ 4121495Sjmacdstruct line_data 4221495Sjmacd{ 4321495Sjmacd /* The start of the line. */ 4421495Sjmacd char *start; 4521495Sjmacd /* The number of characters in the line, 4621495Sjmacd excluding the terminating newline. */ 4721495Sjmacd int size; 4821495Sjmacd /* Vector containing pointers to the entries to add before this line. 4921495Sjmacd The vector is null-terminated. */ 5021495Sjmacd struct spec_entry **add_entries_before; 5121495Sjmacd /* 1 means output any needed new sections before this line. */ 5221495Sjmacd int add_sections_before; 5321495Sjmacd /* 1 means don't output this line. */ 5421495Sjmacd int delete; 5521495Sjmacd}; 5621495Sjmacd 5742664Smarkm 5821495Sjmacd/* This is used for a list of the specified menu section names 5921495Sjmacd in which entries should be added. */ 6021495Sjmacdstruct spec_section 6121495Sjmacd{ 6221495Sjmacd struct spec_section *next; 6321495Sjmacd char *name; 6421495Sjmacd /* 1 means we have not yet found an existing section with this name 6521495Sjmacd in the dir file--so we will need to add a new section. */ 6621495Sjmacd int missing; 6721495Sjmacd}; 6821495Sjmacd 6942664Smarkm 7021495Sjmacd/* This is used for a list of the entries specified to be added. */ 7121495Sjmacdstruct spec_entry 7221495Sjmacd{ 7321495Sjmacd struct spec_entry *next; 7421495Sjmacd char *text; 7556165Sru int text_len; 7656165Sru /* A pointer to the list of sections to which this entry should be 7756165Sru added. */ 7856165Sru struct spec_section *entry_sections; 7956165Sru /* A pointer to a section that is beyond the end of the chain whose 8056165Sru head is pointed to by entry_sections. */ 8156165Sru struct spec_section *entry_sections_tail; 8221495Sjmacd}; 8342664Smarkm 8442664Smarkm 8521495Sjmacd/* This is used for a list of nodes found by parsing the dir file. */ 8621495Sjmacdstruct node 8721495Sjmacd{ 8821495Sjmacd struct node *next; 8921495Sjmacd /* The node name. */ 9021495Sjmacd char *name; 9121495Sjmacd /* The line number of the line where the node starts. 9221495Sjmacd This is the line that contains control-underscore. */ 9321495Sjmacd int start_line; 9421495Sjmacd /* The line number of the line where the node ends, 9521495Sjmacd which is the end of the file or where the next line starts. */ 9621495Sjmacd int end_line; 9721495Sjmacd /* Start of first line in this node's menu 9821495Sjmacd (the line after the * Menu: line). */ 9921495Sjmacd char *menu_start; 10021495Sjmacd /* The start of the chain of sections in this node's menu. */ 10121495Sjmacd struct menu_section *sections; 10221495Sjmacd /* The last menu section in the chain. */ 10321495Sjmacd struct menu_section *last_section; 10421495Sjmacd}; 10521495Sjmacd 10642664Smarkm 10721495Sjmacd/* This is used for a list of sections found in a node's menu. 10821495Sjmacd Each struct node has such a list in the sections field. */ 10921495Sjmacdstruct menu_section 11021495Sjmacd{ 11121495Sjmacd struct menu_section *next; 11221495Sjmacd char *name; 11321495Sjmacd /* Line number of start of section. */ 11421495Sjmacd int start_line; 11521495Sjmacd /* Line number of end of section. */ 11621495Sjmacd int end_line; 11721495Sjmacd}; 11821495Sjmacd 11956165Sru/* This table defines all the long-named options, says whether they 12056165Sru use an argument, and maps them into equivalent single-letter options. */ 12156165Sru 12256165Srustruct option longopts[] = 12356165Sru{ 12456165Sru { "delete", no_argument, NULL, 'r' }, 12556165Sru { "defentry", required_argument, NULL, 'E' }, 12656165Sru { "defsection", required_argument, NULL, 'S' }, 12756165Sru { "dir-file", required_argument, NULL, 'd' }, 12856165Sru { "entry", required_argument, NULL, 'e' }, 12956165Sru { "help", no_argument, NULL, 'h' }, 130114478Sru { "infodir", required_argument, NULL, 'D' }, 13156165Sru { "info-dir", required_argument, NULL, 'D' }, 13256165Sru { "info-file", required_argument, NULL, 'i' }, 13356165Sru { "item", required_argument, NULL, 'e' }, 13456165Sru { "quiet", no_argument, NULL, 'q' }, 13556165Sru { "remove", no_argument, NULL, 'r' }, 13656165Sru { "section", required_argument, NULL, 's' }, 13756165Sru { "version", no_argument, NULL, 'V' }, 13856165Sru { 0 } 13956165Sru}; 14056165Sru 14156165Sru/* Error message functions. */ 14256165Sru 14356165Sru/* Print error message. S1 is printf control string, S2 and S3 args for it. */ 14456165Sru 14556165Sru/* VARARGS1 */ 14656165Sruvoid 147146521Sruerror (const char *s1, const char *s2, const char *s3) 14856165Sru{ 14956165Sru fprintf (stderr, "%s: ", progname); 15056165Sru fprintf (stderr, s1, s2, s3); 15156165Sru putc ('\n', stderr); 15256165Sru} 15356165Sru 15456165Sru/* VARARGS1 */ 15556165Sruvoid 156146521Sruwarning (const char *s1, const char *s2, const char *s3) 15756165Sru{ 15856165Sru fprintf (stderr, _("%s: warning: "), progname); 15956165Sru fprintf (stderr, s1, s2, s3); 16056165Sru putc ('\n', stderr); 16156165Sru} 16256165Sru 16356165Sru/* Print error message and exit. */ 16456165Sru 16556165Sruvoid 166146521Srufatal (const char *s1, const char *s2, const char *s3) 16756165Sru{ 16856165Sru error (s1, s2, s3); 16956165Sru xexit (1); 17056165Sru} 17156165Sru 17242664Smarkm/* Return a newly-allocated string 17342664Smarkm whose contents concatenate those of S1, S2, S3. */ 17421495Sjmacdchar * 175146521Sruconcat (const char *s1, const char *s2, const char *s3) 17621495Sjmacd{ 17721495Sjmacd int len1 = strlen (s1), len2 = strlen (s2), len3 = strlen (s3); 17821495Sjmacd char *result = (char *) xmalloc (len1 + len2 + len3 + 1); 17921495Sjmacd 18021495Sjmacd strcpy (result, s1); 18121495Sjmacd strcpy (result + len1, s2); 18221495Sjmacd strcpy (result + len1 + len2, s3); 18321495Sjmacd *(result + len1 + len2 + len3) = 0; 18421495Sjmacd 18521495Sjmacd return result; 18621495Sjmacd} 18721495Sjmacd 18821495Sjmacd/* Return a string containing SIZE characters 18921495Sjmacd copied from starting at STRING. */ 19021495Sjmacd 19121495Sjmacdchar * 192146521Srucopy_string (const char *string, int size) 19321495Sjmacd{ 19421495Sjmacd int i; 19521495Sjmacd char *copy = (char *) xmalloc (size + 1); 19621495Sjmacd for (i = 0; i < size; i++) 19721495Sjmacd copy[i] = string[i]; 19821495Sjmacd copy[size] = 0; 19921495Sjmacd return copy; 20021495Sjmacd} 20121495Sjmacd 20221495Sjmacd/* Print fatal error message based on errno, with file name NAME. */ 20321495Sjmacd 20421495Sjmacdvoid 205146521Srupfatal_with_name (const char *name) 20621495Sjmacd{ 20742664Smarkm char *s = concat ("", strerror (errno), _(" for %s")); 20893143Sru fatal (s, name, 0); 20921495Sjmacd} 21021495Sjmacd 211146521Sru/* Compare the menu item names in LINE1 (line length LEN1) 212146521Sru and LINE2 (line length LEN2). Return 1 if the item name 213146521Sru in LINE1 is less, 0 otherwise. */ 214146521Sru 215146521Srustatic int 216146521Srumenu_line_lessp (char *line1, int len1, char *line2, int len2) 217146521Sru{ 218146521Sru int minlen = (len1 < len2 ? len1 : len2); 219146521Sru int i; 220146521Sru 221146521Sru for (i = 0; i < minlen; i++) 222146521Sru { 223146521Sru /* If one item name is a prefix of the other, 224146521Sru the former one is less. */ 225146521Sru if (line1[i] == ':' && line2[i] != ':') 226146521Sru return 1; 227146521Sru if (line2[i] == ':' && line1[i] != ':') 228146521Sru return 0; 229146521Sru /* If they both continue and differ, one is less. */ 230146521Sru if (line1[i] < line2[i]) 231146521Sru return 1; 232146521Sru if (line1[i] > line2[i]) 233146521Sru return 0; 234146521Sru } 235146521Sru /* With a properly formatted dir file, 236146521Sru we can only get here if the item names are equal. */ 237146521Sru return 0; 238146521Sru} 239146521Sru 240146521Sru/* Compare the menu item names in LINE1 (line length LEN1) 241146521Sru and LINE2 (line length LEN2). Return 1 if the item names are equal, 242146521Sru 0 otherwise. */ 243146521Sru 244146521Srustatic int 245146521Srumenu_line_equal (char *line1, int len1, char *line2, int len2) 246146521Sru{ 247146521Sru int minlen = (len1 < len2 ? len1 : len2); 248146521Sru int i; 249146521Sru 250146521Sru for (i = 0; i < minlen; i++) 251146521Sru { 252146521Sru /* If both item names end here, they are equal. */ 253146521Sru if (line1[i] == ':' && line2[i] == ':') 254146521Sru return 1; 255146521Sru /* If they both continue and differ, one is less. */ 256146521Sru if (line1[i] != line2[i]) 257146521Sru return 0; 258146521Sru } 259146521Sru /* With a properly formatted dir file, 260146521Sru we can only get here if the item names are equal. */ 261146521Sru return 1; 262146521Sru} 263146521Sru 264146521Sru 26521495Sjmacd/* Given the full text of a menu entry, null terminated, 26621495Sjmacd return just the menu item name (copied). */ 26721495Sjmacd 26821495Sjmacdchar * 269146521Sruextract_menu_item_name (char *item_text) 27021495Sjmacd{ 27121495Sjmacd char *p; 27221495Sjmacd 27321495Sjmacd if (*item_text == '*') 27421495Sjmacd item_text++; 27521495Sjmacd while (*item_text == ' ') 27621495Sjmacd item_text++; 27721495Sjmacd 27821495Sjmacd p = item_text; 27921495Sjmacd while (*p && *p != ':') p++; 28021495Sjmacd return copy_string (item_text, p - item_text); 28121495Sjmacd} 28221495Sjmacd 28321495Sjmacd/* Given the full text of a menu entry, terminated by null or newline, 28421495Sjmacd return just the menu item file (copied). */ 28521495Sjmacd 28621495Sjmacdchar * 287146521Sruextract_menu_file_name (char *item_text) 28821495Sjmacd{ 28921495Sjmacd char *p = item_text; 29021495Sjmacd 29121495Sjmacd /* If we have text that looks like * ITEM: (FILE)NODE..., 29221495Sjmacd extract just FILE. Otherwise return "(none)". */ 29321495Sjmacd 29421495Sjmacd if (*p == '*') 29521495Sjmacd p++; 29621495Sjmacd while (*p == ' ') 29721495Sjmacd p++; 29821495Sjmacd 29921495Sjmacd /* Skip to and past the colon. */ 30021495Sjmacd while (*p && *p != '\n' && *p != ':') p++; 30121495Sjmacd if (*p == ':') p++; 30221495Sjmacd 30321495Sjmacd /* Skip past the open-paren. */ 30421495Sjmacd while (1) 30521495Sjmacd { 30621495Sjmacd if (*p == '(') 30721495Sjmacd break; 30821495Sjmacd else if (*p == ' ' || *p == '\t') 30921495Sjmacd p++; 31021495Sjmacd else 31121495Sjmacd return "(none)"; 31221495Sjmacd } 31321495Sjmacd p++; 31421495Sjmacd 31521495Sjmacd item_text = p; 31621495Sjmacd 31721495Sjmacd /* File name ends just before the close-paren. */ 31821495Sjmacd while (*p && *p != '\n' && *p != ')') p++; 31921495Sjmacd if (*p != ')') 32021495Sjmacd return "(none)"; 32121495Sjmacd 32221495Sjmacd return copy_string (item_text, p - item_text); 32321495Sjmacd} 32456165Sru 32556165Sru 32621495Sjmacd 32756165Sru/* Return FNAME with any [.info][.gz] suffix removed. */ 32856165Sru 32956165Srustatic char * 330146521Srustrip_info_suffix (char *fname) 33156165Sru{ 33256165Sru char *ret = xstrdup (fname); 33356165Sru unsigned len = strlen (ret); 33456165Sru 33556165Sru if (len > 3 && FILENAME_CMP (ret + len - 3, ".gz") == 0) 33656165Sru { 33756165Sru len -= 3; 33856165Sru ret[len] = 0; 33956165Sru } 340114478Sru else if (len > 4 && FILENAME_CMP (ret + len - 4, ".bz2") == 0) 341114478Sru { 342114478Sru len -= 4; 343114478Sru ret[len] = 0; 344114478Sru } 34556165Sru 34656165Sru if (len > 5 && FILENAME_CMP (ret + len - 5, ".info") == 0) 34756165Sru { 34856165Sru len -= 5; 34956165Sru ret[len] = 0; 35056165Sru } 35156165Sru else if (len > 4 && FILENAME_CMP (ret + len - 4, ".inf") == 0) 35256165Sru { 35356165Sru len -= 4; 35456165Sru ret[len] = 0; 35556165Sru } 35656165Sru#ifdef __MSDOS__ 35756165Sru else if (len > 4 && (FILENAME_CMP (ret + len - 4, ".inz") == 0 35856165Sru || FILENAME_CMP (ret + len - 4, ".igz") == 0)) 35956165Sru { 36056165Sru len -= 4; 36156165Sru ret[len] = 0; 36256165Sru } 36356165Sru#endif /* __MSDOS__ */ 36456165Sru 36556165Sru return ret; 36656165Sru} 36756165Sru 36856165Sru 36956165Sru/* Return true if ITEM matches NAME and is followed by TERM_CHAR. ITEM 37056165Sru can also be followed by `.gz', `.info.gz', or `.info' (and then 37156165Sru TERM_CHAR) and still match. */ 37256165Sru 37356165Srustatic int 374146521Srumenu_item_equal (const char *item, char term_char, const char *name) 37556165Sru{ 376146521Sru int ret; 377146521Sru const char *item_basename = item; 37856165Sru unsigned name_len = strlen (name); 379146521Sru 380146521Sru /* We must compare the basename in ITEM, since we are passed the 381146521Sru basename of the original info file. Otherwise, a new entry like 382146521Sru "lilypond/lilypond" won't match "lilypond". 383146521Sru 384146521Sru Actually, it seems to me that we should really compare the whole 385146521Sru name, and not just the basename. Couldn't there be dir1/foo.info 386146521Sru and dir2/foo.info? Also, it seems like we should be using the 387146521Sru filename from the new dir entries, not the filename on the command 388146521Sru line. Not worrying about those things right now, though. --karl, 389146521Sru 26mar04. */ 390146521Sru while (*item_basename && !IS_SLASH (*item_basename) 391146521Sru && *item_basename != term_char) 392146521Sru item_basename++; 393146521Sru if (! *item_basename || *item_basename == term_char) 394146521Sru item_basename = item; /* no /, use original */ 395146521Sru else 396146521Sru item_basename++; /* have /, move past it */ 397146521Sru 39856165Sru /* First, ITEM must actually match NAME (usually it won't). */ 399146521Sru ret = strncasecmp (item_basename, name, name_len) == 0; 40056165Sru if (ret) 40156165Sru { 40256165Sru /* Then, `foobar' doesn't match `foo', so be sure we've got all of 40356165Sru ITEM. The various suffixes should never actually appear in the 40456165Sru dir file, but sometimes people put them in. */ 40556165Sru static char *suffixes[] 40656165Sru = { "", ".info.gz", ".info", ".inf", ".gz", 40756165Sru#ifdef __MSDOS__ 40856165Sru ".inz", ".igz", 40956165Sru#endif 41056165Sru NULL }; 41156165Sru unsigned i; 41256165Sru ret = 0; 41356165Sru for (i = 0; !ret && suffixes[i]; i++) 41456165Sru { 41556165Sru char *suffix = suffixes[i]; 41656165Sru unsigned suffix_len = strlen (suffix); 417146521Sru ret = strncasecmp (item_basename + name_len, suffix, suffix_len) == 0 418146521Sru && item_basename[name_len + suffix_len] == term_char; 41956165Sru } 42056165Sru } 42156165Sru 42256165Sru return ret; 42356165Sru} 42456165Sru 42556165Sru 42656165Sru 42721495Sjmacdvoid 428146521Srusuggest_asking_for_help (void) 42921495Sjmacd{ 43042664Smarkm fprintf (stderr, _("\tTry `%s --help' for a complete list of options.\n"), 43121495Sjmacd progname); 43256165Sru xexit (1); 43321495Sjmacd} 43421495Sjmacd 43521495Sjmacdvoid 436146521Sruprint_help (void) 43721495Sjmacd{ 43842664Smarkm printf (_("Usage: %s [OPTION]... [INFO-FILE [DIR-FILE]]\n\ 43921495Sjmacd\n\ 44056165SruInstall or delete dir entries from INFO-FILE in the Info directory file\n\ 44156165SruDIR-FILE.\n\ 44242664Smarkm\n\ 44321495SjmacdOptions:\n\ 44456165Sru --delete delete existing entries for INFO-FILE from DIR-FILE;\n\ 44556165Sru don't insert any new entries.\n\ 44656165Sru --defentry=TEXT like --entry, but only use TEXT if an entry\n\ 44756165Sru is not present in INFO-FILE.\n\ 44856165Sru --defsection=TEXT like --section, but only use TEXT if a section\n\ 44956165Sru is not present in INFO-FILE.\n\ 45056165Sru --dir-file=NAME specify file name of Info directory file.\n\ 45156165Sru This is equivalent to using the DIR-FILE argument.\n\ 45256165Sru --entry=TEXT insert TEXT as an Info directory entry.\n\ 45356165Sru TEXT should have the form of an Info menu item line\n\ 45456165Sru plus zero or more extra lines starting with whitespace.\n\ 45556165Sru If you specify more than one entry, they are all added.\n\ 45656165Sru If you don't specify any entries, they are determined\n\ 45756165Sru from information in the Info file itself.\n\ 45856165Sru --help display this help and exit.\n\ 45956165Sru --info-file=FILE specify Info file to install in the directory.\n\ 46056165Sru This is equivalent to using the INFO-FILE argument.\n\ 46156165Sru --info-dir=DIR same as --dir-file=DIR/dir.\n\ 46256165Sru --item=TEXT same as --entry TEXT.\n\ 46356165Sru An Info directory entry is actually a menu item.\n\ 46456165Sru --quiet suppress warnings.\n\ 46556165Sru --remove same as --delete.\n\ 46656165Sru --section=SEC put this file's entries in section SEC of the directory.\n\ 46756165Sru If you specify more than one section, all the entries\n\ 46856165Sru are added in each of the sections.\n\ 46956165Sru If you don't specify any sections, they are determined\n\ 47056165Sru from information in the Info file itself.\n\ 47156165Sru --version display version information and exit.\n\ 472100517Sru"), progname); 473100517Sru 474100517Sru puts (_("\n\ 47556165SruEmail bug reports to bug-texinfo@gnu.org,\n\ 47656165Srugeneral questions and discussion to help-texinfo@gnu.org.\n\ 477100517SruTexinfo home page: http://www.gnu.org/software/texinfo/")); 47821495Sjmacd} 47921495Sjmacd 48021495Sjmacd 48142664Smarkm/* If DIRFILE does not exist, create a minimal one (or abort). If it 48242664Smarkm already exists, do nothing. */ 48342664Smarkm 48442664Smarkmvoid 485146521Sruensure_dirfile_exists (char *dirfile) 48642664Smarkm{ 48742664Smarkm int desc = open (dirfile, O_RDONLY); 48842664Smarkm if (desc < 0 && errno == ENOENT) 48942664Smarkm { 49042664Smarkm FILE *f; 49142664Smarkm char *readerr = strerror (errno); 49242664Smarkm close (desc); 49342664Smarkm f = fopen (dirfile, "w"); 49442664Smarkm if (f) 49542664Smarkm { 49656165Sru fprintf (f, _("This is the file .../info/dir, which contains the\n\ 49742664Smarkmtopmost node of the Info hierarchy, called (dir)Top.\n\ 49842664SmarkmThe first time you invoke Info you start off looking at this node.\n\ 499146521Sru\x1f\n\ 50056165Sru%s\tThis is the top of the INFO tree\n\ 50142664Smarkm\n\ 50242664Smarkm This (the Directory node) gives a menu of major topics.\n\ 50342664Smarkm Typing \"q\" exits, \"?\" lists all Info commands, \"d\" returns here,\n\ 50442664Smarkm \"h\" gives a primer for first-timers,\n\ 50542664Smarkm \"mEmacs<Return>\" visits the Emacs manual, etc.\n\ 50642664Smarkm\n\ 50742664Smarkm In Emacs, you can click mouse button 2 on a menu item or cross reference\n\ 50842664Smarkm to select it.\n\ 50942664Smarkm\n\ 510114478Sru%s\n\ 511114478Sru"), "File: dir,\tNode: Top", /* These keywords must not be translated. */ 512114478Sru "* Menu:" 513114478Sru); 51442664Smarkm if (fclose (f) < 0) 51542664Smarkm pfatal_with_name (dirfile); 51642664Smarkm } 51742664Smarkm else 51842664Smarkm { 51942664Smarkm /* Didn't exist, but couldn't open for writing. */ 52042664Smarkm fprintf (stderr, 52142664Smarkm _("%s: could not read (%s) and could not create (%s)\n"), 52242664Smarkm dirfile, readerr, strerror (errno)); 52356165Sru xexit (1); 52442664Smarkm } 52542664Smarkm } 52642664Smarkm else 52742664Smarkm close (desc); /* It already existed, so fine. */ 52842664Smarkm} 52942664Smarkm 53056165Sru/* Open FILENAME and return the resulting stream pointer. If it doesn't 53156165Sru exist, try FILENAME.gz. If that doesn't exist either, call 53256165Sru CREATE_CALLBACK (with FILENAME as arg) to create it, if that is 53356165Sru non-NULL. If still no luck, fatal error. 53421495Sjmacd 53556165Sru If we do open it, return the actual name of the file opened in 53656165Sru OPENED_FILENAME and the compress program to use to (de)compress it in 53756165Sru COMPRESSION_PROGRAM. The compression program is determined by the 53856165Sru magic number, not the filename. */ 53956165Sru 54056165SruFILE * 541146521Sruopen_possibly_compressed_file (char *filename, 542146521Sru void (*create_callback) (char *), 543146521Sru char **opened_filename, char **compression_program, int *is_pipe) 54421495Sjmacd{ 54556165Sru char *local_opened_filename, *local_compression_program; 54656165Sru int nread; 54756165Sru char data[4]; 54856165Sru FILE *f; 54942664Smarkm 55056165Sru /* We let them pass NULL if they don't want this info, but it's easier 55156165Sru to always determine it. */ 55256165Sru if (!opened_filename) 55356165Sru opened_filename = &local_opened_filename; 55456165Sru 55556165Sru *opened_filename = filename; 55656165Sru f = fopen (*opened_filename, FOPEN_RBIN); 55756165Sru if (!f) 55856165Sru { 55956165Sru *opened_filename = concat (filename, ".gz", ""); 56056165Sru f = fopen (*opened_filename, FOPEN_RBIN); 561114478Sru if (!f) 562114478Sru { 563114478Sru free (*opened_filename); 564114478Sru *opened_filename = concat (filename, ".bz2", ""); 565114478Sru f = fopen (*opened_filename, FOPEN_RBIN); 566114478Sru } 567114478Sru 56856165Sru#ifdef __MSDOS__ 56956165Sru if (!f) 57056165Sru { 57156165Sru free (*opened_filename); 57256165Sru *opened_filename = concat (filename, ".igz", ""); 57356165Sru f = fopen (*opened_filename, FOPEN_RBIN); 57456165Sru } 57556165Sru if (!f) 57656165Sru { 57756165Sru free (*opened_filename); 57856165Sru *opened_filename = concat (filename, ".inz", ""); 57956165Sru f = fopen (*opened_filename, FOPEN_RBIN); 58056165Sru } 58156165Sru#endif 58256165Sru if (!f) 58356165Sru { 58456165Sru if (create_callback) 58556165Sru { /* That didn't work either. Create the file if we can. */ 58656165Sru (*create_callback) (filename); 58756165Sru 58856165Sru /* And try opening it again. */ 58956165Sru free (*opened_filename); 59056165Sru *opened_filename = filename; 59156165Sru f = fopen (*opened_filename, FOPEN_RBIN); 59256165Sru if (!f) 59356165Sru pfatal_with_name (filename); 59456165Sru } 59556165Sru else 59656165Sru pfatal_with_name (filename); 59756165Sru } 59856165Sru } 59956165Sru 60056165Sru /* Read first few bytes of file rather than relying on the filename. 60156165Sru If the file is shorter than this it can't be usable anyway. */ 60256165Sru nread = fread (data, sizeof (data), 1, f); 60356165Sru if (nread != 1) 60456165Sru { 60556165Sru /* Empty files don't set errno, so we get something like 60656165Sru "install-info: No error for foo", which is confusing. */ 60756165Sru if (nread == 0) 60893143Sru fatal (_("%s: empty file"), *opened_filename, 0); 60956165Sru pfatal_with_name (*opened_filename); 61056165Sru } 61156165Sru 61256165Sru if (!compression_program) 61356165Sru compression_program = &local_compression_program; 61456165Sru 61556165Sru if (data[0] == '\x1f' && data[1] == '\x8b') 61656165Sru#if STRIP_DOT_EXE 61756165Sru /* An explicit .exe yields a better diagnostics from popen below 61856165Sru if they don't have gzip installed. */ 61956165Sru *compression_program = "gzip.exe"; 62056165Sru#else 62156165Sru *compression_program = "gzip"; 62256165Sru#endif 623114478Sru else if(data[0] == 'B' && data[1] == 'Z' && data[2] == 'h') 624114478Sru#ifndef STRIP_DOT_EXE 625114478Sru *compression_program = "bzip2.exe"; 626114478Sru#else 627114478Sru *compression_program = "bzip2"; 628114478Sru#endif 629114478Sru else if(data[0] == 'B' && data[1] == 'Z' && data[2] == '0') 630114478Sru#ifndef STRIP_DOT_EXE 631114478Sru *compression_program = "bzip.exe"; 632114478Sru#else 633114478Sru *compression_program = "bzip"; 634114478Sru#endif 63556165Sru else 63656165Sru *compression_program = NULL; 63756165Sru 63856165Sru if (*compression_program) 63956165Sru { /* It's compressed, so fclose the file and then open a pipe. */ 64056165Sru char *command = concat (*compression_program," -cd <", *opened_filename); 64156165Sru if (fclose (f) < 0) 64256165Sru pfatal_with_name (*opened_filename); 64356165Sru f = popen (command, "r"); 64456165Sru if (f) 64556165Sru *is_pipe = 1; 64656165Sru else 64756165Sru pfatal_with_name (command); 64856165Sru } 64956165Sru else 65056165Sru { /* It's a plain file, seek back over the magic bytes. */ 65156165Sru if (fseek (f, 0, 0) < 0) 65256165Sru pfatal_with_name (*opened_filename); 65356165Sru#if O_BINARY 65456165Sru /* Since this is a text file, and we opened it in binary mode, 65556165Sru switch back to text mode. */ 65656165Sru f = freopen (*opened_filename, "r", f); 65756165Sru#endif 65856165Sru *is_pipe = 0; 65956165Sru } 66056165Sru 66156165Sru return f; 66256165Sru} 66321495Sjmacd 66456165Sru/* Read all of file FILENAME into memory and return the address of the 66556165Sru data. Store the size of the data into SIZEP. If need be, uncompress 66656165Sru (i.e., try FILENAME.gz et al. if FILENAME does not exist) and store 66756165Sru the actual file name that was opened into OPENED_FILENAME (if it is 66856165Sru non-NULL), and the companion compression program (if any, else NULL) 66956165Sru into COMPRESSION_PROGRAM (if that is non-NULL). If trouble, do 67056165Sru a fatal error. */ 67156165Sru 67256165Sruchar * 673146521Srureadfile (char *filename, int *sizep, 674146521Sru void (*create_callback) (char *), char **opened_filename, 675146521Sru char **compression_program) 67656165Sru{ 67756165Sru char *real_name; 67856165Sru FILE *f; 67956165Sru int pipe_p; 68056165Sru int filled = 0; 68156165Sru int data_size = 8192; 68256165Sru char *data = xmalloc (data_size); 68356165Sru 68456165Sru /* If they passed the space for the file name to return, use it. */ 68556165Sru f = open_possibly_compressed_file (filename, create_callback, 68656165Sru opened_filename ? opened_filename 68756165Sru : &real_name, 68856165Sru compression_program, &pipe_p); 68956165Sru 69056165Sru for (;;) 69156165Sru { 69256165Sru int nread = fread (data + filled, 1, data_size - filled, f); 69356165Sru if (nread < 0) 69456165Sru pfatal_with_name (real_name); 69556165Sru if (nread == 0) 69656165Sru break; 69756165Sru 69856165Sru filled += nread; 69956165Sru if (filled == data_size) 70056165Sru { 70156165Sru data_size += 65536; 70256165Sru data = xrealloc (data, data_size); 70356165Sru } 70456165Sru } 70556165Sru 70656165Sru /* We'll end up wasting space if we're not passing the filename back 70756165Sru and it is not just FILENAME, but so what. */ 70856165Sru /* We need to close the stream, since on some systems the pipe created 70956165Sru by popen is simulated by a temporary file which only gets removed 71056165Sru inside pclose. */ 71156165Sru if (pipe_p) 71256165Sru pclose (f); 71356165Sru else 71456165Sru fclose (f); 71556165Sru 71656165Sru *sizep = filled; 71756165Sru return data; 71856165Sru} 71956165Sru 72056165Sru/* Output the old dir file, interpolating the new sections 72156165Sru and/or new entries where appropriate. If COMPRESSION_PROGRAM is not 72256165Sru null, pipe to it to create DIRFILE. Thus if we read dir.gz on input, 72356165Sru we'll write dir.gz on output. */ 72456165Sru 72556165Srustatic void 726146521Sruoutput_dirfile (char *dirfile, int dir_nlines, struct line_data *dir_lines, 727146521Sru int n_entries_to_add, struct spec_entry *entries_to_add, 728146521Sru struct spec_section *input_sections, char *compression_program) 72956165Sru{ 73056165Sru int i; 73156165Sru FILE *output; 73256165Sru 73356165Sru if (compression_program) 73456165Sru { 73556165Sru char *command = concat (compression_program, ">", dirfile); 73656165Sru output = popen (command, "w"); 73756165Sru } 73856165Sru else 73956165Sru output = fopen (dirfile, "w"); 74056165Sru 74156165Sru if (!output) 74256165Sru { 74356165Sru perror (dirfile); 74456165Sru xexit (1); 74556165Sru } 74656165Sru 74756165Sru for (i = 0; i <= dir_nlines; i++) 74856165Sru { 74956165Sru int j; 75056165Sru 75156165Sru /* If we decided to output some new entries before this line, 75256165Sru output them now. */ 75356165Sru if (dir_lines[i].add_entries_before) 75456165Sru for (j = 0; j < n_entries_to_add; j++) 75556165Sru { 75656165Sru struct spec_entry *this = dir_lines[i].add_entries_before[j]; 75756165Sru if (this == 0) 75856165Sru break; 75956165Sru fputs (this->text, output); 76056165Sru } 76156165Sru /* If we decided to add some sections here 76256165Sru because there are no such sections in the file, 76356165Sru output them now. */ 76456165Sru if (dir_lines[i].add_sections_before) 76556165Sru { 76656165Sru struct spec_section *spec; 76756165Sru struct spec_section **sections; 76856165Sru int n_sections = 0; 76956165Sru struct spec_entry *entry; 77056165Sru struct spec_entry **entries; 77156165Sru int n_entries = 0; 77256165Sru 77356165Sru /* Count the sections and allocate a vector for all of them. */ 77456165Sru for (spec = input_sections; spec; spec = spec->next) 77556165Sru n_sections++; 77656165Sru sections = ((struct spec_section **) 77756165Sru xmalloc (n_sections * sizeof (struct spec_section *))); 77856165Sru 77956165Sru /* Fill the vector SECTIONS with pointers to all the sections, 78056165Sru and sort them. */ 78156165Sru j = 0; 78256165Sru for (spec = input_sections; spec; spec = spec->next) 78356165Sru sections[j++] = spec; 78456165Sru qsort (sections, n_sections, sizeof (struct spec_section *), 78556165Sru compare_section_names); 78656165Sru 78756165Sru /* Count the entries and allocate a vector for all of them. */ 78856165Sru for (entry = entries_to_add; entry; entry = entry->next) 78956165Sru n_entries++; 79056165Sru entries = ((struct spec_entry **) 79156165Sru xmalloc (n_entries * sizeof (struct spec_entry *))); 79256165Sru 79356165Sru /* Fill the vector ENTRIES with pointers to all the sections, 79456165Sru and sort them. */ 79556165Sru j = 0; 79656165Sru for (entry = entries_to_add; entry; entry = entry->next) 79756165Sru entries[j++] = entry; 79856165Sru qsort (entries, n_entries, sizeof (struct spec_entry *), 79956165Sru compare_entries_text); 80056165Sru 80156165Sru /* Generate the new sections in alphabetical order. In each 80256165Sru new section, output all of the entries that belong to that 80356165Sru section, in alphabetical order. */ 80456165Sru for (j = 0; j < n_sections; j++) 80556165Sru { 80656165Sru spec = sections[j]; 80756165Sru if (spec->missing) 80856165Sru { 80956165Sru int k; 81056165Sru 81156165Sru putc ('\n', output); 81256165Sru fputs (spec->name, output); 81356165Sru putc ('\n', output); 81456165Sru for (k = 0; k < n_entries; k++) 81556165Sru { 81656165Sru struct spec_section *spec1; 81756165Sru /* Did they at all want this entry to be put into 81856165Sru this section? */ 81956165Sru entry = entries[k]; 82056165Sru for (spec1 = entry->entry_sections; 82156165Sru spec1 && spec1 != entry->entry_sections_tail; 82256165Sru spec1 = spec1->next) 82356165Sru { 82456165Sru if (!strcmp (spec1->name, spec->name)) 82556165Sru break; 82656165Sru } 82756165Sru if (spec1 && spec1 != entry->entry_sections_tail) 82856165Sru fputs (entry->text, output); 82956165Sru } 83056165Sru } 83156165Sru } 83256165Sru 83356165Sru free (entries); 83456165Sru free (sections); 83556165Sru } 83656165Sru 83756165Sru /* Output the original dir lines unless marked for deletion. */ 83856165Sru if (i < dir_nlines && !dir_lines[i].delete) 83956165Sru { 84056165Sru fwrite (dir_lines[i].start, 1, dir_lines[i].size, output); 84156165Sru putc ('\n', output); 84256165Sru } 84356165Sru } 84456165Sru 84556165Sru /* Some systems, such as MS-DOS, simulate pipes with temporary files. 84656165Sru On those systems, the compressor actually gets run inside pclose, 84756165Sru so we must call pclose. */ 84856165Sru if (compression_program) 84956165Sru pclose (output); 85056165Sru else 85156165Sru fclose (output); 85256165Sru} 85356165Sru 85456165Sru/* Parse the input to find the section names and the entry names it 85556165Sru specifies. Return the number of entries to add from this file. */ 85642664Smarkmint 857146521Sruparse_input (const struct line_data *lines, int nlines, 858146521Sru struct spec_section **sections, struct spec_entry **entries) 85956165Sru{ 86056165Sru int n_entries = 0; 86156165Sru int prefix_length = strlen ("INFO-DIR-SECTION "); 86256165Sru struct spec_section *head = *sections, *tail = NULL; 86356165Sru int reset_tail = 0; 86456165Sru char *start_of_this_entry = 0; 86556165Sru int ignore_sections = *sections != 0; 86656165Sru int ignore_entries = *entries != 0; 86756165Sru 86856165Sru int i; 86956165Sru 87056165Sru if (ignore_sections && ignore_entries) 87156165Sru return 0; 87256165Sru 87356165Sru /* Loop here processing lines from the input file. Each 87456165Sru INFO-DIR-SECTION entry is added to the SECTIONS linked list. 87556165Sru Each START-INFO-DIR-ENTRY block is added to the ENTRIES linked 87656165Sru list, and all its entries inherit the chain of SECTION entries 87756165Sru defined by the last group of INFO-DIR-SECTION entries we have 87856165Sru seen until that point. */ 87956165Sru for (i = 0; i < nlines; i++) 88056165Sru { 88156165Sru if (!ignore_sections 88256165Sru && !strncmp ("INFO-DIR-SECTION ", lines[i].start, prefix_length)) 88356165Sru { 88456165Sru struct spec_section *next 88556165Sru = (struct spec_section *) xmalloc (sizeof (struct spec_section)); 88656165Sru next->name = copy_string (lines[i].start + prefix_length, 88756165Sru lines[i].size - prefix_length); 88856165Sru next->next = *sections; 88956165Sru next->missing = 1; 89056165Sru if (reset_tail) 89156165Sru { 89256165Sru tail = *sections; 89356165Sru reset_tail = 0; 89456165Sru } 89556165Sru *sections = next; 89656165Sru head = *sections; 89756165Sru } 89856165Sru /* If entries were specified explicitly with command options, 89956165Sru ignore the entries in the input file. */ 90056165Sru else if (!ignore_entries) 90156165Sru { 90256165Sru if (!strncmp ("START-INFO-DIR-ENTRY", lines[i].start, lines[i].size) 90356165Sru && sizeof ("START-INFO-DIR-ENTRY") - 1 == lines[i].size) 90456165Sru { 90556165Sru if (!*sections) 90656165Sru { 90756165Sru /* We found an entry, but didn't yet see any sections 90856165Sru specified. Default to section "Miscellaneous". */ 90956165Sru *sections = (struct spec_section *) 91056165Sru xmalloc (sizeof (struct spec_section)); 91156165Sru (*sections)->name = 91256165Sru default_section ? default_section : "Miscellaneous"; 91356165Sru (*sections)->next = 0; 91456165Sru (*sections)->missing = 1; 91556165Sru head = *sections; 91656165Sru } 91756165Sru /* Next time we see INFO-DIR-SECTION, we will reset the 91856165Sru tail pointer. */ 91956165Sru reset_tail = 1; 92056165Sru 92156165Sru if (start_of_this_entry != 0) 92293143Sru fatal (_("START-INFO-DIR-ENTRY without matching END-INFO-DIR-ENTRY"), 0, 0); 92356165Sru start_of_this_entry = lines[i + 1].start; 92456165Sru } 92556165Sru else if (start_of_this_entry) 92656165Sru { 92756165Sru if ((!strncmp ("* ", lines[i].start, 2) 92856165Sru && lines[i].start > start_of_this_entry) 92956165Sru || (!strncmp ("END-INFO-DIR-ENTRY", 93056165Sru lines[i].start, lines[i].size) 93156165Sru && sizeof ("END-INFO-DIR-ENTRY") - 1 == lines[i].size)) 93256165Sru { 93356165Sru /* We found an end of this entry. Allocate another 93456165Sru entry, fill its data, and add it to the linked 93556165Sru list. */ 93656165Sru struct spec_entry *next 93756165Sru = (struct spec_entry *) xmalloc (sizeof (struct spec_entry)); 93856165Sru next->text 93956165Sru = copy_string (start_of_this_entry, 94056165Sru lines[i].start - start_of_this_entry); 94156165Sru next->text_len = lines[i].start - start_of_this_entry; 94256165Sru next->entry_sections = head; 94356165Sru next->entry_sections_tail = tail; 94456165Sru next->next = *entries; 94556165Sru *entries = next; 94656165Sru n_entries++; 94756165Sru if (!strncmp ("END-INFO-DIR-ENTRY", 94856165Sru lines[i].start, lines[i].size) 94956165Sru && sizeof ("END-INFO-DIR-ENTRY") - 1 == lines[i].size) 95056165Sru start_of_this_entry = 0; 95156165Sru else 95256165Sru start_of_this_entry = lines[i].start; 95356165Sru } 95456165Sru else if (!strncmp ("END-INFO-DIR-ENTRY", 95556165Sru lines[i].start, lines[i].size) 95656165Sru && sizeof ("END-INFO-DIR-ENTRY") - 1 == lines[i].size) 95793143Sru fatal (_("END-INFO-DIR-ENTRY without matching START-INFO-DIR-ENTRY"), 0, 0); 95856165Sru } 95956165Sru } 96056165Sru } 96156165Sru if (start_of_this_entry != 0) 96293143Sru fatal (_("START-INFO-DIR-ENTRY without matching END-INFO-DIR-ENTRY"), 963114478Sru 0, 0); 96456165Sru 96556165Sru /* If we ignored the INFO-DIR-ENTRY directives, we need now go back 96656165Sru and plug the names of all the sections we found into every 96756165Sru element of the ENTRIES list. */ 96856165Sru if (ignore_entries && *entries) 96956165Sru { 97056165Sru struct spec_entry *entry; 97156165Sru 97256165Sru for (entry = *entries; entry; entry = entry->next) 97356165Sru { 97456165Sru entry->entry_sections = head; 97556165Sru entry->entry_sections_tail = tail; 97656165Sru } 97756165Sru } 97856165Sru 97956165Sru return n_entries; 98056165Sru} 98156165Sru 98256165Sru/* Parse the dir file whose basename is BASE_NAME. Find all the 98356165Sru nodes, and their menus, and the sections of their menus. */ 98456165Sruint 985146521Sruparse_dir_file (struct line_data *lines, int nlines, struct node **nodes, 986146521Sru const char *base_name) 98756165Sru{ 98856165Sru int node_header_flag = 0; 98956165Sru int something_deleted = 0; 99056165Sru int i; 99156165Sru 99256165Sru *nodes = 0; 99356165Sru for (i = 0; i < nlines; i++) 99456165Sru { 99556165Sru /* Parse node header lines. */ 99656165Sru if (node_header_flag) 99756165Sru { 99856165Sru int j, end; 99956165Sru for (j = 0; j < lines[i].size; j++) 100056165Sru /* Find the node name and store it in the `struct node'. */ 100156165Sru if (!strncmp ("Node:", lines[i].start + j, 5)) 100256165Sru { 100356165Sru char *line = lines[i].start; 100456165Sru /* Find the start of the node name. */ 100556165Sru j += 5; 100656165Sru while (line[j] == ' ' || line[j] == '\t') 100756165Sru j++; 100856165Sru /* Find the end of the node name. */ 100956165Sru end = j; 101056165Sru while (line[end] != 0 && line[end] != ',' && line[end] != '\n' 101156165Sru && line[end] != '\t') 101256165Sru end++; 101356165Sru (*nodes)->name = copy_string (line + j, end - j); 101456165Sru } 101556165Sru node_header_flag = 0; 101656165Sru } 101756165Sru 101856165Sru /* Notice the start of a node. */ 101956165Sru if (*lines[i].start == 037) 102056165Sru { 102156165Sru struct node *next = (struct node *) xmalloc (sizeof (struct node)); 102256165Sru 102356165Sru next->next = *nodes; 102456165Sru next->name = NULL; 102556165Sru next->start_line = i; 102656165Sru next->end_line = 0; 102756165Sru next->menu_start = NULL; 102856165Sru next->sections = NULL; 102956165Sru next->last_section = NULL; 103056165Sru 103156165Sru if (*nodes != 0) 103256165Sru (*nodes)->end_line = i; 103356165Sru /* Fill in the end of the last menu section 103456165Sru of the previous node. */ 103556165Sru if (*nodes != 0 && (*nodes)->last_section != 0) 103656165Sru (*nodes)->last_section->end_line = i; 103756165Sru 103856165Sru *nodes = next; 103956165Sru 104056165Sru /* The following line is the header of this node; 104156165Sru parse it. */ 104256165Sru node_header_flag = 1; 104356165Sru } 104456165Sru 104556165Sru /* Notice the lines that start menus. */ 104656165Sru if (*nodes != 0 && !strncmp ("* Menu:", lines[i].start, 7)) 104756165Sru (*nodes)->menu_start = lines[i + 1].start; 104856165Sru 104956165Sru /* Notice sections in menus. */ 105056165Sru if (*nodes != 0 105156165Sru && (*nodes)->menu_start != 0 105256165Sru && *lines[i].start != '\n' 105356165Sru && *lines[i].start != '*' 105456165Sru && *lines[i].start != ' ' 105556165Sru && *lines[i].start != '\t') 105656165Sru { 105756165Sru /* Add this menu section to the node's list. 105856165Sru This list grows in forward order. */ 105956165Sru struct menu_section *next 106056165Sru = (struct menu_section *) xmalloc (sizeof (struct menu_section)); 106156165Sru 106256165Sru next->start_line = i + 1; 106356165Sru next->next = 0; 106456165Sru next->end_line = 0; 106556165Sru next->name = copy_string (lines[i].start, lines[i].size); 106656165Sru if ((*nodes)->sections) 106756165Sru { 106856165Sru (*nodes)->last_section->next = next; 106956165Sru (*nodes)->last_section->end_line = i; 107056165Sru } 107156165Sru else 107256165Sru (*nodes)->sections = next; 107356165Sru (*nodes)->last_section = next; 107456165Sru } 107556165Sru 107656165Sru /* Check for an existing entry that should be deleted. 107756165Sru Delete all entries which specify this file name. */ 107856165Sru if (*lines[i].start == '*') 107956165Sru { 108056165Sru char *q; 108156165Sru char *p = lines[i].start; 108256165Sru 108356165Sru p++; /* skip * */ 108456165Sru while (*p == ' ') p++; /* ignore following spaces */ 108556165Sru q = p; /* remember this, it's the beginning of the menu item. */ 108656165Sru 108756165Sru /* Read menu item. */ 108856165Sru while (*p != 0 && *p != ':') 108956165Sru p++; 109056165Sru p++; /* skip : */ 109156165Sru 109256165Sru if (*p == ':') 109356165Sru { /* XEmacs-style entry, as in * Mew::Messaging. */ 109456165Sru if (menu_item_equal (q, ':', base_name)) 109556165Sru { 109656165Sru lines[i].delete = 1; 109756165Sru something_deleted = 1; 109856165Sru } 109956165Sru } 110056165Sru else 110156165Sru { /* Emacs-style entry, as in * Emacs: (emacs). */ 110256165Sru while (*p == ' ') p++; /* skip spaces after : */ 110356165Sru if (*p == '(') /* if at parenthesized (FILENAME) */ 110456165Sru { 110556165Sru p++; 110656165Sru if (menu_item_equal (p, ')', base_name)) 110756165Sru { 110856165Sru lines[i].delete = 1; 110956165Sru something_deleted = 1; 111056165Sru } 111156165Sru } 111256165Sru } 111356165Sru } 111456165Sru 111556165Sru /* Treat lines that start with whitespace 111656165Sru as continuations; if we are deleting an entry, 111756165Sru delete all its continuations as well. */ 111856165Sru else if (i > 0 && (*lines[i].start == ' ' || *lines[i].start == '\t')) 111956165Sru { 112056165Sru lines[i].delete = lines[i - 1].delete; 112156165Sru } 112256165Sru } 112356165Sru 112456165Sru /* Finish the info about the end of the last node. */ 112556165Sru if (*nodes != 0) 112656165Sru { 112756165Sru (*nodes)->end_line = nlines; 112856165Sru if ((*nodes)->last_section != 0) 112956165Sru (*nodes)->last_section->end_line = nlines; 113056165Sru } 113156165Sru 113256165Sru return something_deleted; 113356165Sru} 113456165Sru 113556165Sruint 1136146521Srumain (int argc, char **argv) 113721495Sjmacd{ 113856165Sru char *opened_dirfilename; 113956165Sru char *compression_program; 114056165Sru char *infile_sans_info; 114121495Sjmacd char *infile = 0, *dirfile = 0; 114221495Sjmacd 114321495Sjmacd /* Record the text of the Info file, as a sequence of characters 114421495Sjmacd and as a sequence of lines. */ 114556165Sru char *input_data = NULL; 114656165Sru int input_size = 0; 114756165Sru struct line_data *input_lines = NULL; 114856165Sru int input_nlines = 0; 114921495Sjmacd 115021495Sjmacd /* Record here the specified section names and directory entries. */ 115121495Sjmacd struct spec_section *input_sections = NULL; 115221495Sjmacd struct spec_entry *entries_to_add = NULL; 115321495Sjmacd int n_entries_to_add = 0; 115456165Sru struct spec_entry *default_entries_to_add = NULL; 115556165Sru int n_default_entries_to_add = 0; 115621495Sjmacd 115721495Sjmacd /* Record the old text of the dir file, as plain characters, 115821495Sjmacd as lines, and as nodes. */ 115921495Sjmacd char *dir_data; 116021495Sjmacd int dir_size; 116121495Sjmacd int dir_nlines; 116221495Sjmacd struct line_data *dir_lines; 116321495Sjmacd struct node *dir_nodes; 116421495Sjmacd 116521495Sjmacd /* Nonzero means --delete was specified (just delete existing entries). */ 116621495Sjmacd int delete_flag = 0; 116721495Sjmacd int something_deleted = 0; 116821495Sjmacd /* Nonzero means -q was specified. */ 116921495Sjmacd int quiet_flag = 0; 117021495Sjmacd 117121495Sjmacd int i; 117221495Sjmacd 117342664Smarkm#ifdef HAVE_SETLOCALE 117442664Smarkm /* Set locale via LC_ALL. */ 117542664Smarkm setlocale (LC_ALL, ""); 117642664Smarkm#endif 117742664Smarkm 117842664Smarkm /* Set the text message domain. */ 117942664Smarkm bindtextdomain (PACKAGE, LOCALEDIR); 118042664Smarkm textdomain (PACKAGE); 118142664Smarkm 118221495Sjmacd while (1) 118321495Sjmacd { 118421495Sjmacd int opt = getopt_long (argc, argv, "i:d:e:s:hHr", longopts, 0); 118521495Sjmacd 118621495Sjmacd if (opt == EOF) 118721495Sjmacd break; 118821495Sjmacd 118921495Sjmacd switch (opt) 119021495Sjmacd { 119121495Sjmacd case 0: 119221495Sjmacd /* If getopt returns 0, then it has already processed a 119321495Sjmacd long-named option. We should do nothing. */ 119421495Sjmacd break; 119521495Sjmacd 119621495Sjmacd case 1: 119721495Sjmacd abort (); 119821495Sjmacd 119921495Sjmacd case 'd': 120021495Sjmacd if (dirfile) 120121495Sjmacd { 1202146521Sru fprintf (stderr, _("%s: already have dir file: %s\n"), 1203146521Sru progname, dirfile); 120421495Sjmacd suggest_asking_for_help (); 120521495Sjmacd } 120621495Sjmacd dirfile = optarg; 120721495Sjmacd break; 120821495Sjmacd 120921495Sjmacd case 'D': 121021495Sjmacd if (dirfile) 121121495Sjmacd { 1212146521Sru fprintf (stderr, _("%s: already have dir file: %s\n"), 1213146521Sru progname, dirfile); 121421495Sjmacd suggest_asking_for_help (); 121521495Sjmacd } 121621495Sjmacd dirfile = concat (optarg, "", "/dir"); 121721495Sjmacd break; 121821495Sjmacd 121943579Speter case 'E': 122021495Sjmacd case 'e': 122121495Sjmacd { 122221495Sjmacd struct spec_entry *next 122321495Sjmacd = (struct spec_entry *) xmalloc (sizeof (struct spec_entry)); 122456165Sru int olen = strlen (optarg); 122556165Sru if (! (*optarg != 0 && optarg[olen - 1] == '\n')) 122656165Sru { 122756165Sru optarg = concat (optarg, "\n", ""); 122856165Sru olen++; 122956165Sru } 123021495Sjmacd next->text = optarg; 123156165Sru next->text_len = olen; 123256165Sru next->entry_sections = NULL; 123356165Sru next->entry_sections_tail = NULL; 123456165Sru if (opt == 'e') 123556165Sru { 123656165Sru next->next = entries_to_add; 123756165Sru entries_to_add = next; 123856165Sru n_entries_to_add++; 123956165Sru } 124056165Sru else 124156165Sru { 124256165Sru next->next = default_entries_to_add; 124356165Sru default_entries_to_add = next; 124456165Sru n_default_entries_to_add++; 124556165Sru } 124621495Sjmacd } 124721495Sjmacd break; 124821495Sjmacd 124921495Sjmacd case 'h': 125021495Sjmacd case 'H': 125121495Sjmacd print_help (); 125256165Sru xexit (0); 125321495Sjmacd 125421495Sjmacd case 'i': 125521495Sjmacd if (infile) 125621495Sjmacd { 125742664Smarkm fprintf (stderr, _("%s: Specify the Info file only once.\n"), 125821495Sjmacd progname); 125921495Sjmacd suggest_asking_for_help (); 126021495Sjmacd } 126121495Sjmacd infile = optarg; 126221495Sjmacd break; 126321495Sjmacd 126421495Sjmacd case 'q': 126521495Sjmacd quiet_flag = 1; 126621495Sjmacd break; 126721495Sjmacd 126821495Sjmacd case 'r': 126921495Sjmacd delete_flag = 1; 127021495Sjmacd break; 127121495Sjmacd 127221495Sjmacd case 's': 127321495Sjmacd { 127421495Sjmacd struct spec_section *next 127521495Sjmacd = (struct spec_section *) xmalloc (sizeof (struct spec_section)); 127621495Sjmacd next->name = optarg; 127721495Sjmacd next->next = input_sections; 127821495Sjmacd next->missing = 1; 127921495Sjmacd input_sections = next; 128021495Sjmacd } 128121495Sjmacd break; 128221495Sjmacd 128356165Sru case 'S': 128456165Sru default_section = optarg; 128556165Sru break; 128656165Sru 128721495Sjmacd case 'V': 128842664Smarkm printf ("install-info (GNU %s) %s\n", PACKAGE, VERSION); 128956165Sru puts (""); 1290146521Sru puts ("Copyright (C) 2004 Free Software Foundation, Inc."); 1291146521Sru printf (_("There is NO warranty. You may redistribute this software\n\ 129221495Sjmacdunder the terms of the GNU General Public License.\n\ 1293146521SruFor more information about these matters, see the files named COPYING.\n")); 129456165Sru xexit (0); 129521495Sjmacd 129621495Sjmacd default: 129721495Sjmacd suggest_asking_for_help (); 129821495Sjmacd } 129921495Sjmacd } 130021495Sjmacd 130121495Sjmacd /* Interpret the non-option arguments as file names. */ 130221495Sjmacd for (; optind < argc; ++optind) 130321495Sjmacd { 130421495Sjmacd if (infile == 0) 130521495Sjmacd infile = argv[optind]; 130621495Sjmacd else if (dirfile == 0) 130721495Sjmacd dirfile = argv[optind]; 130821495Sjmacd else 130993143Sru error (_("excess command line argument `%s'"), argv[optind], 0); 131021495Sjmacd } 131121495Sjmacd 131221495Sjmacd if (!infile) 131393143Sru fatal (_("No input file specified; try --help for more information."), 1314114478Sru 0, 0); 131521495Sjmacd if (!dirfile) 131693143Sru fatal (_("No dir file specified; try --help for more information."), 0, 0); 131721495Sjmacd 131856165Sru /* Read the Info file and parse it into lines, unless we're deleting. */ 131956165Sru if (!delete_flag) 132021495Sjmacd { 132156165Sru input_data = readfile (infile, &input_size, NULL, NULL, NULL); 132256165Sru input_lines = findlines (input_data, input_size, &input_nlines); 132321495Sjmacd } 132421495Sjmacd 132556165Sru i = parse_input (input_lines, input_nlines, 132656165Sru &input_sections, &entries_to_add); 132756165Sru if (i > n_entries_to_add) 132856165Sru n_entries_to_add = i; 132956165Sru else if (n_entries_to_add == 0) 133021495Sjmacd { 133156165Sru entries_to_add = default_entries_to_add; 133256165Sru n_entries_to_add = n_default_entries_to_add; 133321495Sjmacd } 133421495Sjmacd 133556165Sru if (!delete_flag) 133656165Sru { 133756165Sru if (entries_to_add == 0) 133856165Sru { /* No need to abort here, the original info file may not 133956165Sru have the requisite Texinfo commands. This is not 134056165Sru something an installer should have to correct (it's a 134156165Sru problem for the maintainer), and there's no need to cause 134256165Sru subsequent parts of `make install' to fail. */ 134393143Sru warning (_("no info dir entry in `%s'"), infile, 0); 134456165Sru xexit (0); 134556165Sru } 134621495Sjmacd 134756165Sru /* If the entries came from the command-line arguments, their 134856165Sru entry_sections pointers are not yet set. Walk the chain of 134956165Sru the entries and for each entry update entry_sections to point 135056165Sru to the head of the list of sections where this entry should 135156165Sru be put. Note that all the entries specified on the command 135256165Sru line get put into ALL the sections we've got, either from the 135356165Sru Info file, or (under --section) from the command line, 135456165Sru because in the loop below every entry inherits the entire 135556165Sru chain of sections. */ 135656165Sru if (n_entries_to_add > 0 && entries_to_add->entry_sections == NULL) 135721495Sjmacd { 135856165Sru struct spec_entry *ep; 135956165Sru 136056165Sru /* If we got no sections, default to "Miscellaneous". */ 136156165Sru if (input_sections == NULL) 136221495Sjmacd { 136356165Sru input_sections = (struct spec_section *) 136456165Sru xmalloc (sizeof (struct spec_section)); 136556165Sru input_sections->name = 136656165Sru default_section ? default_section : "Miscellaneous"; 136756165Sru input_sections->next = NULL; 136856165Sru input_sections->missing = 1; 136921495Sjmacd } 137056165Sru for (ep = entries_to_add; ep; ep = ep->next) 137156165Sru ep->entry_sections = input_sections; 137221495Sjmacd } 137321495Sjmacd } 137421495Sjmacd 137521495Sjmacd /* Now read in the Info dir file. */ 137656165Sru dir_data = readfile (dirfile, &dir_size, ensure_dirfile_exists, 137756165Sru &opened_dirfilename, &compression_program); 137821495Sjmacd dir_lines = findlines (dir_data, dir_size, &dir_nlines); 137921495Sjmacd 138021495Sjmacd /* We will be comparing the entries in the dir file against the 138156165Sru current filename, so need to strip off any directory prefix and/or 138256165Sru [.info][.gz] suffix. */ 138321495Sjmacd { 138456165Sru char *infile_basename = infile + strlen (infile); 138521495Sjmacd 138656165Sru if (HAVE_DRIVE (infile)) 1387114478Sru infile += 2; /* get past the drive spec X: */ 138856165Sru 138956165Sru while (infile_basename > infile && !IS_SLASH (infile_basename[-1])) 139056165Sru infile_basename--; 139156165Sru 139256165Sru infile_sans_info = strip_info_suffix (infile_basename); 139321495Sjmacd } 139421495Sjmacd 139556165Sru something_deleted 139656165Sru = parse_dir_file (dir_lines, dir_nlines, &dir_nodes, infile_sans_info); 139721495Sjmacd 139821495Sjmacd /* Decide where to add the new entries (unless --delete was used). 139921495Sjmacd Find the menu sections to add them in. 140021495Sjmacd In each section, find the proper alphabetical place to add 140121495Sjmacd each of the entries. */ 140221495Sjmacd if (!delete_flag) 140321495Sjmacd { 140421495Sjmacd struct node *node; 140521495Sjmacd struct menu_section *section; 140621495Sjmacd struct spec_section *spec; 140721495Sjmacd 140821495Sjmacd for (node = dir_nodes; node; node = node->next) 140921495Sjmacd for (section = node->sections; section; section = section->next) 141021495Sjmacd { 141121495Sjmacd for (i = section->end_line; i > section->start_line; i--) 141221495Sjmacd if (dir_lines[i - 1].size != 0) 141321495Sjmacd break; 141421495Sjmacd section->end_line = i; 141521495Sjmacd 141621495Sjmacd for (spec = input_sections; spec; spec = spec->next) 141721495Sjmacd if (!strcmp (spec->name, section->name)) 141821495Sjmacd break; 141921495Sjmacd if (spec) 142021495Sjmacd { 142121495Sjmacd int add_at_line = section->end_line; 142221495Sjmacd struct spec_entry *entry; 142321495Sjmacd /* Say we have found at least one section with this name, 142421495Sjmacd so we need not add such a section. */ 142521495Sjmacd spec->missing = 0; 142621495Sjmacd /* For each entry, find the right place in this section 142721495Sjmacd to add it. */ 142821495Sjmacd for (entry = entries_to_add; entry; entry = entry->next) 142921495Sjmacd { 143056165Sru /* Did they at all want this entry to be put into 143156165Sru this section? */ 143256165Sru for (spec = entry->entry_sections; 143356165Sru spec && spec != entry->entry_sections_tail; 143456165Sru spec = spec->next) 143556165Sru { 143656165Sru if (!strcmp (spec->name, section->name)) 143756165Sru break; 143856165Sru } 143956165Sru if (!spec || spec == entry->entry_sections_tail) 144056165Sru continue; 1441116531Sru 144221495Sjmacd /* Subtract one because dir_lines is zero-based, 144321495Sjmacd but the `end_line' and `start_line' members are 144421495Sjmacd one-based. */ 144521495Sjmacd for (i = section->end_line - 1; 144621495Sjmacd i >= section->start_line - 1; i--) 144721495Sjmacd { 144821495Sjmacd /* If an entry exists with the same name, 144921495Sjmacd and was not marked for deletion 145021495Sjmacd (which means it is for some other file), 145121495Sjmacd we are in trouble. */ 145221495Sjmacd if (dir_lines[i].start[0] == '*' 145356165Sru && menu_line_equal (entry->text, entry->text_len, 145421495Sjmacd dir_lines[i].start, 145521495Sjmacd dir_lines[i].size) 145621495Sjmacd && !dir_lines[i].delete) 145756535Sru { 145856535Sru if (quiet_flag) 145956535Sru dir_lines[i].delete = 1; 146056535Sru else 146156535Sru fatal (_("menu item `%s' already exists, for file `%s'"), 146221495Sjmacd extract_menu_item_name (entry->text), 146321495Sjmacd extract_menu_file_name (dir_lines[i].start)); 146456535Sru } 146521495Sjmacd if (dir_lines[i].start[0] == '*' 146656165Sru && menu_line_lessp (entry->text, entry->text_len, 146721495Sjmacd dir_lines[i].start, 146821495Sjmacd dir_lines[i].size)) 146921495Sjmacd add_at_line = i; 147021495Sjmacd } 147121495Sjmacd insert_entry_here (entry, add_at_line, 147221495Sjmacd dir_lines, n_entries_to_add); 147321495Sjmacd } 147421495Sjmacd } 147521495Sjmacd } 147621495Sjmacd 147721495Sjmacd /* Mark the end of the Top node as the place to add any 147821495Sjmacd new sections that are needed. */ 147921495Sjmacd for (node = dir_nodes; node; node = node->next) 148021495Sjmacd if (node->name && strcmp (node->name, "Top") == 0) 148121495Sjmacd dir_lines[node->end_line].add_sections_before = 1; 148221495Sjmacd } 148321495Sjmacd 148421495Sjmacd if (delete_flag && !something_deleted && !quiet_flag) 148593143Sru warning (_("no entries found for `%s'; nothing deleted"), infile, 0); 148621495Sjmacd 148756165Sru output_dirfile (opened_dirfilename, dir_nlines, dir_lines, n_entries_to_add, 148856165Sru entries_to_add, input_sections, compression_program); 148921495Sjmacd 149056165Sru xexit (0); 1491116531Sru return 0; /* Avoid bogus warnings. */ 149221495Sjmacd} 149321495Sjmacd 149421495Sjmacd/* Divide the text at DATA (of SIZE bytes) into lines. 149521495Sjmacd Return a vector of struct line_data describing the lines. 149621495Sjmacd Store the length of that vector into *NLINESP. */ 149721495Sjmacd 149821495Sjmacdstruct line_data * 1499146521Srufindlines (char *data, int size, int *nlinesp) 150021495Sjmacd{ 150156165Sru int i; 150256165Sru int lineflag = 1; 150356165Sru int lines_allocated = 511; 150421495Sjmacd int filled = 0; 150556165Sru struct line_data *lines 150656165Sru = xmalloc ((lines_allocated + 1) * sizeof (struct line_data)); 150721495Sjmacd 150821495Sjmacd for (i = 0; i < size; i++) 150921495Sjmacd { 151021495Sjmacd if (lineflag) 151121495Sjmacd { 151221495Sjmacd if (filled == lines_allocated) 151321495Sjmacd { 151456165Sru /* try to keep things somewhat page-aligned */ 151556165Sru lines_allocated = ((lines_allocated + 1) * 2) - 1; 151656165Sru lines = xrealloc (lines, (lines_allocated + 1) 151756165Sru * sizeof (struct line_data)); 151821495Sjmacd } 151921495Sjmacd lines[filled].start = &data[i]; 152021495Sjmacd lines[filled].add_entries_before = 0; 152121495Sjmacd lines[filled].add_sections_before = 0; 152221495Sjmacd lines[filled].delete = 0; 152321495Sjmacd if (filled > 0) 152421495Sjmacd lines[filled - 1].size 152521495Sjmacd = lines[filled].start - lines[filled - 1].start - 1; 152621495Sjmacd filled++; 152721495Sjmacd } 152821495Sjmacd lineflag = (data[i] == '\n'); 152921495Sjmacd } 153021495Sjmacd if (filled > 0) 153121495Sjmacd lines[filled - 1].size = &data[i] - lines[filled - 1].start - lineflag; 153221495Sjmacd 153321495Sjmacd /* Do not leave garbage in the last element. */ 153421495Sjmacd lines[filled].start = NULL; 153521495Sjmacd lines[filled].add_entries_before = NULL; 153621495Sjmacd lines[filled].add_sections_before = 0; 153721495Sjmacd lines[filled].delete = 0; 153821495Sjmacd lines[filled].size = 0; 153921495Sjmacd 154021495Sjmacd *nlinesp = filled; 154121495Sjmacd return lines; 154221495Sjmacd} 154321495Sjmacd 1544146521Sru/* This is the comparison function for qsort for a vector of pointers to 1545146521Sru struct spec_section. (Have to use const void * as the parameter type 1546146521Sru to avoid incompatible-with-qsort warnings.) 154721495Sjmacd Compare the section names. */ 154821495Sjmacd 154921495Sjmacdint 1550146521Srucompare_section_names (const void *p1, const void *p2) 155121495Sjmacd{ 1552146521Sru struct spec_section **sec1 = (struct spec_section **) p1; 1553146521Sru struct spec_section **sec2 = (struct spec_section **) p2; 155421495Sjmacd char *name1 = (*sec1)->name; 155521495Sjmacd char *name2 = (*sec2)->name; 155621495Sjmacd return strcmp (name1, name2); 155721495Sjmacd} 155821495Sjmacd 155956165Sru/* This is the comparison function for qsort 156056165Sru for a vector of pointers to struct spec_entry. 156156165Sru Compare the entries' text. */ 156256165Sru 156356165Sruint 1564146521Srucompare_entries_text (const void *p1, const void *p2) 156556165Sru{ 1566146521Sru struct spec_entry **entry1 = (struct spec_entry **) p1; 1567146521Sru struct spec_entry **entry2 = (struct spec_entry **) p2; 156856165Sru char *text1 = (*entry1)->text; 156956165Sru char *text2 = (*entry2)->text; 157056165Sru char *colon1 = strchr (text1, ':'); 157156165Sru char *colon2 = strchr (text2, ':'); 157256165Sru int len1, len2; 157356165Sru 157456165Sru if (!colon1) 157556165Sru len1 = strlen (text1); 157656165Sru else 157756165Sru len1 = colon1 - text1; 157856165Sru if (!colon2) 157956165Sru len2 = strlen (text2); 158056165Sru else 158156165Sru len2 = colon2 - text2; 158256165Sru return strncmp (text1, text2, len1 <= len2 ? len1 : len2); 158356165Sru} 158456165Sru 158521495Sjmacd/* Insert ENTRY into the add_entries_before vector 158621495Sjmacd for line number LINE_NUMBER of the dir file. 158721495Sjmacd DIR_LINES and N_ENTRIES carry information from like-named variables 158821495Sjmacd in main. */ 158921495Sjmacd 159021495Sjmacdvoid 1591146521Sruinsert_entry_here (struct spec_entry *entry, int line_number, 1592146521Sru struct line_data *dir_lines, int n_entries) 159321495Sjmacd{ 159456165Sru int i, j; 159521495Sjmacd 159621495Sjmacd if (dir_lines[line_number].add_entries_before == 0) 159721495Sjmacd { 159821495Sjmacd dir_lines[line_number].add_entries_before 159921495Sjmacd = (struct spec_entry **) xmalloc (n_entries * sizeof (struct spec_entry *)); 160021495Sjmacd for (i = 0; i < n_entries; i++) 160121495Sjmacd dir_lines[line_number].add_entries_before[i] = 0; 160221495Sjmacd } 160321495Sjmacd 160456165Sru /* Find the place where this entry belongs. If there are already 160556165Sru several entries to add before LINE_NUMBER, make sure they are in 160656165Sru alphabetical order. */ 160721495Sjmacd for (i = 0; i < n_entries; i++) 160856165Sru if (dir_lines[line_number].add_entries_before[i] == 0 160956165Sru || menu_line_lessp (entry->text, strlen (entry->text), 161056165Sru dir_lines[line_number].add_entries_before[i]->text, 161156165Sru strlen (dir_lines[line_number].add_entries_before[i]->text))) 161221495Sjmacd break; 161321495Sjmacd 161421495Sjmacd if (i == n_entries) 161521495Sjmacd abort (); 161621495Sjmacd 161756165Sru /* If we need to plug ENTRY into the middle of the 161856165Sru ADD_ENTRIES_BEFORE array, move the entries which should be output 161956165Sru after this one down one notch, before adding a new one. */ 162056165Sru if (dir_lines[line_number].add_entries_before[i] != 0) 162156165Sru for (j = n_entries - 1; j > i; j--) 162256165Sru dir_lines[line_number].add_entries_before[j] 162356165Sru = dir_lines[line_number].add_entries_before[j - 1]; 162456165Sru 162521495Sjmacd dir_lines[line_number].add_entries_before[i] = entry; 162621495Sjmacd} 1627