Annotation of 43BSDReno/contrib/emacs-18.55/man/texindex.c, revision 1.1

1.1     ! root        1: /* Prepare Tex index dribble output into an actual index.
        !             2:    Copyright (C) 1987 Free Software Foundation, Inc.
        !             3: 
        !             4:                       NO WARRANTY
        !             5: 
        !             6:   BECAUSE THIS PROGRAM IS LICENSED FREE OF CHARGE, WE PROVIDE ABSOLUTELY
        !             7: NO WARRANTY, TO THE EXTENT PERMITTED BY APPLICABLE STATE LAW.  EXCEPT
        !             8: WHEN OTHERWISE STATED IN WRITING, FREE SOFTWARE FOUNDATION, INC,
        !             9: RICHARD M. STALLMAN AND/OR OTHER PARTIES PROVIDE THIS PROGRAM "AS IS"
        !            10: WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING,
        !            11: BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
        !            12: FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY
        !            13: AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE PROGRAM PROVE
        !            14: DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR
        !            15: CORRECTION.
        !            16: 
        !            17:  IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW WILL RICHARD M.
        !            18: STALLMAN, THE FREE SOFTWARE FOUNDATION, INC., AND/OR ANY OTHER PARTY
        !            19: WHO MAY MODIFY AND REDISTRIBUTE THIS PROGRAM AS PERMITTED BELOW, BE
        !            20: LIABLE TO YOU FOR DAMAGES, INCLUDING ANY LOST PROFITS, LOST MONIES, OR
        !            21: OTHER SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
        !            22: USE OR INABILITY TO USE (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR
        !            23: DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY THIRD PARTIES OR
        !            24: A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS) THIS
        !            25: PROGRAM, EVEN IF YOU HAVE BEEN ADVISED OF THE POSSIBILITY OF SUCH
        !            26: DAMAGES, OR FOR ANY CLAIM BY ANY OTHER PARTY.
        !            27: 
        !            28:                GENERAL PUBLIC LICENSE TO COPY
        !            29: 
        !            30:   1. You may copy and distribute verbatim copies of this source file
        !            31: as you receive it, in any medium, provided that you conspicuously
        !            32: and appropriately publish on each copy a valid copyright notice
        !            33: "Copyright (C) 1987 Free Software Foundation, Inc.", and include
        !            34: following the copyright notice a verbatim copy of the above disclaimer
        !            35: of warranty and of this License.
        !            36: 
        !            37:   2. You may modify your copy or copies of this source file or
        !            38: any portion of it, and copy and distribute such modifications under
        !            39: the terms of Paragraph 1 above, provided that you also do the following:
        !            40: 
        !            41:     a) cause the modified files to carry prominent notices stating
        !            42:     that you changed the files and the date of any change; and
        !            43: 
        !            44:     b) cause the whole of any work that you distribute or publish,
        !            45:     that in whole or in part contains or is a derivative of this
        !            46:     program or any part thereof, to be licensed at no charge to all
        !            47:     third parties on terms identical to those contained in this
        !            48:     License Agreement (except that you may choose to grant more extensive
        !            49:     warranty protection to some or all third parties, at your option).
        !            50: 
        !            51:     c) You may charge a distribution fee for the physical act of
        !            52:     transferring a copy, and you may at your option offer warranty
        !            53:     protection in exchange for a fee.
        !            54: 
        !            55: Mere aggregation of another unrelated program with this program (or its
        !            56: derivative) on a volume of a storage or distribution medium does not bring
        !            57: the other program under the scope of these terms.
        !            58: 
        !            59:   3. You may copy and distribute this program (or a portion or derivative
        !            60: of it, under Paragraph 2) in object code or executable form under the terms
        !            61: of Paragraphs 1 and 2 above provided that you also do one of the following:
        !            62: 
        !            63:     a) accompany it with the complete corresponding machine-readable
        !            64:     source code, which must be distributed under the terms of
        !            65:     Paragraphs 1 and 2 above; or,
        !            66: 
        !            67:     b) accompany it with a written offer, valid for at least three
        !            68:     years, to give any third party free (except for a nominal
        !            69:     shipping charge) a complete machine-readable copy of the
        !            70:     corresponding source code, to be distributed under the terms of
        !            71:     Paragraphs 1 and 2 above; or,
        !            72: 
        !            73:     c) accompany it with the information you received as to where the
        !            74:     corresponding source code may be obtained.  (This alternative is
        !            75:     allowed only for noncommercial distribution and only if you
        !            76:     received the program in object code or executable form alone.)
        !            77: 
        !            78: For an executable file, complete source code means all the source code for
        !            79: all modules it contains; but, as a special exception, it need not include
        !            80: source code for modules which are standard libraries that accompany the
        !            81: operating system on which the executable file runs.
        !            82: 
        !            83:   4. You may not copy, sublicense, distribute or transfer this program
        !            84: except as expressly provided under this License Agreement.  Any attempt
        !            85: otherwise to copy, sublicense, distribute or transfer this program is void and
        !            86: your rights to use the program under this License agreement shall be
        !            87: automatically terminated.  However, parties who have received computer
        !            88: software programs from you with this License Agreement will not have
        !            89: their licenses terminated so long as such parties remain in full compliance.
        !            90: 
        !            91:   5. If you wish to incorporate parts of this program into other free
        !            92: programs whose distribution conditions are different, write to the Free
        !            93: Software Foundation at 675 Mass Ave, Cambridge, MA 02139.  We have not yet
        !            94: worked out a simple rule that can be stated here, but we will often permit
        !            95: this.  We will be guided by the two goals of preserving the free status of
        !            96: all derivatives of our free software and of promoting the sharing and reuse of
        !            97: software.
        !            98: 
        !            99:  In other words, you are welcome to use, share and improve this program.
        !           100:  You are forbidden to forbid anyone else to use, share and improve
        !           101:  what you give them.   Help stamp out software-hoarding!  */
        !           102: 
        !           103: 
        !           104: #include <stdio.h>
        !           105: #include <ctype.h>
        !           106: 
        !           107: #ifdef VMS
        !           108: #include <file.h>
        !           109: 
        !           110: #define EXIT_SUCCESS ((1 << 28) | 1)
        !           111: #define EXIT_FATAL ((1 << 28) | 4)
        !           112: #define unlink delete
        !           113: #define tell(fd) lseek(fd, 0L, 1)
        !           114: #else
        !           115: #include <sys/file.h>
        !           116: 
        !           117: #define EXIT_SUCCESS 0
        !           118: #define EXIT_FATAL 1
        !           119: #endif
        !           120: 
        !           121: 
        !           122: #ifndef L_XTND
        !           123: #define L_XTND 2
        !           124: #endif
        !           125: 
        !           126: /* When sorting in core, this structure describes one line
        !           127:  and the position and length of its first keyfield.  */
        !           128: 
        !           129: struct lineinfo
        !           130:   {
        !           131:     char *text;                /* The actual text of the line */
        !           132:     union
        !           133:       {                        /* The start of the key (for textual comparison) */
        !           134:        char *text;
        !           135:        long number;    /* or the numeric value (for numeric comparison) */
        !           136:       } key;
        !           137:     long keylen;       /* Length of key field */
        !           138:   };
        !           139: 
        !           140: /* This structure describes a field to use as a sort key */
        !           141: 
        !           142: struct keyfield
        !           143:   {
        !           144:     int startwords;            /* # words to skip  */
        !           145:     int startchars;            /*  and # additional chars to skip, to start of field */
        !           146:     int endwords;              /* similar, from beg (or end) of line, to find end of field */
        !           147:     int endchars;
        !           148:     char ignore_blanks;                /* Ignore spaces and tabs within the field */
        !           149:     char fold_case;            /* Convert upper case to lower before comparing */
        !           150:     char reverse;              /* Compare in reverse order */
        !           151:     char numeric;              /* Parse text as an integer and compare the integers */
        !           152:     char positional;           /* Sort according to position within the file */
        !           153:     char braced;               /* Count balanced-braced groupings as fields */
        !           154:   };
        !           155: 
        !           156: /* Vector of keyfields to use */
        !           157: 
        !           158: struct keyfield keyfields[3];
        !           159: 
        !           160: /* Number of keyfields stored in that vector.  */
        !           161: 
        !           162: int num_keyfields = 3;
        !           163: 
        !           164: /* Vector of input file names, terminated with a zero (null pointer) */
        !           165: 
        !           166: char **infiles;
        !           167: 
        !           168: /* Vector of corresponding output file names, or zero meaning default it */
        !           169: 
        !           170: char **outfiles;
        !           171: 
        !           172: /* Length of `infiles' */
        !           173: 
        !           174: int num_infiles;
        !           175: 
        !           176: /* Pointer to the array of pointers to lines being sorted */
        !           177: 
        !           178: char **linearray;
        !           179: 
        !           180: /* The allocated length of `linearray'. */
        !           181: 
        !           182: long lines;
        !           183: 
        !           184: /* Directory to use for temporary files.  On Unix, it ends with a slash.  */
        !           185: 
        !           186: char *tempdir;
        !           187: 
        !           188: /* Start of filename to use for temporary files.  */
        !           189: 
        !           190: char *tempbase;
        !           191: 
        !           192: /* Number of last temporary file.  */
        !           193: 
        !           194: int tempcount;
        !           195: 
        !           196: /* Number of last temporary file already deleted.
        !           197:  Temporary files are deleted by `flush_tempfiles' in order of creation.  */
        !           198: 
        !           199: int last_deleted_tempcount;
        !           200: 
        !           201: /* During in-core sort, this points to the base of the data block
        !           202:  which contains all the lines of data.  */
        !           203: 
        !           204: char *text_base;
        !           205: 
        !           206: /* Additional command switches */
        !           207: 
        !           208: int keep_tempfiles;    /* Nonzero means do not delete tempfiles -- for debugging */
        !           209: 
        !           210: /* Forward declarations of functions in this file */
        !           211: 
        !           212: void decode_command ();
        !           213: void sort_in_core ();
        !           214: void sort_offline ();
        !           215: char **parsefile ();
        !           216: char *find_field ();
        !           217: char *find_pos ();
        !           218: long find_value ();
        !           219: char *find_braced_pos ();
        !           220: char *find_braced_end ();
        !           221: void writelines ();
        !           222: int compare_full ();
        !           223: long readline ();
        !           224: int merge_files ();
        !           225: int merge_direct ();
        !           226: char *concat ();
        !           227: char *maketempname ();
        !           228: void flush_tempfiles ();
        !           229: char *tempcopy ();
        !           230: 
        !           231: extern char *mktemp ();
        !           232: 
        !           233: #define MAX_IN_CORE_SORT 500000
        !           234: 
        !           235: int
        !           236: main (argc, argv)
        !           237:      int argc;
        !           238:      char **argv;
        !           239: {
        !           240:   int i;
        !           241: 
        !           242:   tempcount = 0;
        !           243:   last_deleted_tempcount = 0;
        !           244: 
        !           245:   /* Describe the kind of sorting to do. */
        !           246:   /* The first keyfield uses the first braced field and folds case */
        !           247:   keyfields[0].braced = 1;
        !           248:   keyfields[0].fold_case = 1;
        !           249:   keyfields[0].endwords = -1;
        !           250:   keyfields[0].endchars = -1;
        !           251:   /* The second keyfield uses the second braced field, numerically */
        !           252:   keyfields[1].braced = 1;
        !           253:   keyfields[1].numeric = 1;
        !           254:   keyfields[1].startwords = 1;
        !           255:   keyfields[1].endwords = -1;
        !           256:   keyfields[1].endchars = -1;
        !           257:   /* The third keyfield (which is ignored while discarding duplicates)
        !           258:      compares the whole line */
        !           259:   keyfields[2].endwords = -1;
        !           260:   keyfields[2].endchars = -1;
        !           261: 
        !           262:   decode_command (argc, argv);
        !           263: 
        !           264:   tempbase = mktemp (concat ("txiXXXXXX", "", ""));
        !           265: 
        !           266:   /* Process input files completely, one by one.  */
        !           267: 
        !           268:   for (i = 0; i < num_infiles; i++)
        !           269:     {
        !           270:       int desc;
        !           271:       long ptr;
        !           272:       char *outfile;
        !           273:       char *p;
        !           274: 
        !           275:       desc = open (infiles[i], 0, 0);
        !           276:       if (desc < 0) pfatal_with_name (infiles[i]);
        !           277:       lseek (desc, 0, L_XTND);
        !           278:       ptr = tell (desc);
        !           279:       close (desc);
        !           280: 
        !           281:       outfile = outfiles[i];
        !           282:       if (!outfile)
        !           283:        {
        !           284:          outfile = concat (infiles[i], "s", "");
        !           285:        }
        !           286: 
        !           287:       if (ptr < MAX_IN_CORE_SORT)
        !           288:         /* Sort a small amount of data */
        !           289:         sort_in_core (infiles[i], ptr, outfile);
        !           290:       else
        !           291:         sort_offline (infiles[i], ptr, outfile);
        !           292:     }
        !           293: 
        !           294:   flush_tempfiles (tempcount);
        !           295:   exit (EXIT_SUCCESS);
        !           296: }
        !           297: 
        !           298: /* This page decodes the command line arguments to set the parameter variables
        !           299:  and set up the vector of keyfields and the vector of input files */
        !           300: 
        !           301: void
        !           302: decode_command (argc, argv)
        !           303:      int argc;
        !           304:      char **argv;
        !           305: {
        !           306:   int i;
        !           307:   char **ip;
        !           308:   char **op;
        !           309: 
        !           310:   /* Store default values into parameter variables */
        !           311: 
        !           312: #ifdef VMS
        !           313:   tempdir = "sys$scratch:";
        !           314: #else
        !           315:   tempdir = "/tmp/";
        !           316: #endif
        !           317: 
        !           318:   keep_tempfiles = 0;
        !           319: 
        !           320:   /* Allocate argc input files, which must be enough.  */
        !           321: 
        !           322:   infiles = (char **) xmalloc (argc * sizeof (char *));
        !           323:   outfiles = (char **) xmalloc (argc * sizeof (char *));
        !           324:   ip = infiles;
        !           325:   op = outfiles;
        !           326: 
        !           327:   /* First find all switches that control the default kind-of-sort */
        !           328: 
        !           329:   for (i = 1; i < argc; i++)
        !           330:     {
        !           331:       int tem = classify_arg (argv[i]);
        !           332:       char c;
        !           333:       char *p;
        !           334: 
        !           335:       if (tem <= 0)
        !           336:        {
        !           337:          *ip++ = argv[i];
        !           338:          *op++ = 0;
        !           339:          continue;
        !           340:        }
        !           341:       if (tem > 1)
        !           342:        {
        !           343:          if (i + 1 == argc)
        !           344:            fatal ("switch %s given with no argument following it", argv[i]);
        !           345:          else if (!strcmp (argv[i], "-T"))
        !           346:            tempdir = argv[i + 1];
        !           347:          else if (!strcmp (argv[i], "-o"))
        !           348:            *(op - 1) = argv[i + 1];
        !           349:          i += tem - 1;
        !           350:          continue;
        !           351:        }
        !           352: 
        !           353:       p = &argv[i][1];
        !           354:       while (c = *p++)
        !           355:        switch (c)
        !           356:          {
        !           357:          case 'k':
        !           358:            keep_tempfiles = 1;
        !           359:            break;
        !           360: 
        !           361:          default:
        !           362:            fatal ("invalid command switch %c", c);
        !           363:          }
        !           364:     switchdone: ;
        !           365:     }
        !           366: 
        !           367:   /* Record number of keyfields, terminate list of filenames */
        !           368: 
        !           369:   num_infiles = ip - infiles;
        !           370:   *ip = 0;
        !           371: }
        !           372: 
        !           373: /* Return 0 for an argument that is not a switch;
        !           374:  for a switch, return 1 plus the number of following arguments that the switch swallows.
        !           375: */
        !           376: 
        !           377: int
        !           378: classify_arg (arg)
        !           379:      char *arg;
        !           380: {
        !           381:   if (!strcmp (arg, "-T") || !strcmp (arg, "-o"))
        !           382:     return 2;
        !           383:   if (arg[0] == '-')
        !           384:     return 1;
        !           385:   return 0;
        !           386: }
        !           387: 
        !           388: /* Create a name for a temporary file */
        !           389: 
        !           390: char *
        !           391: maketempname (count)
        !           392:      int count;
        !           393: {
        !           394:   char tempsuffix[10];
        !           395:   sprintf (tempsuffix, "%d", count);
        !           396:   return concat (tempdir, tempbase, tempsuffix);
        !           397: }
        !           398: 
        !           399: /* Delete all temporary files up to the specified count */
        !           400: 
        !           401: void
        !           402: flush_tempfiles (to_count)
        !           403:      int to_count;
        !           404: {
        !           405:   if (keep_tempfiles) return;
        !           406:   while (last_deleted_tempcount < to_count)
        !           407:     unlink (maketempname (++last_deleted_tempcount));
        !           408: }
        !           409: 
        !           410: /* Copy an input file into a temporary file, and return the temporary file name */
        !           411: 
        !           412: #define BUFSIZE 1024
        !           413: 
        !           414: char *
        !           415: tempcopy (idesc)
        !           416:      int idesc;
        !           417: {
        !           418:   char *outfile = maketempname (++tempcount);
        !           419:   int odesc;
        !           420:   char buffer[BUFSIZE];
        !           421: 
        !           422:   odesc = open (outfile, O_WRONLY | O_CREAT, 0666);
        !           423: 
        !           424:   if (odesc < 0) pfatal_with_name (outfile);
        !           425: 
        !           426:   while (1)
        !           427:     {
        !           428:       int nread = read (idesc, buffer, BUFSIZE);
        !           429:       write (odesc, buffer, nread);
        !           430:       if (!nread) break;
        !           431:     }
        !           432: 
        !           433:   close (odesc);
        !           434: 
        !           435:   return outfile;
        !           436: }
        !           437: 
        !           438: /* Compare two lines, provided as pointers to pointers to text,
        !           439:  according to the specified set of keyfields */
        !           440: 
        !           441: int
        !           442: compare_full (line1, line2)
        !           443:      char **line1, **line2;
        !           444: {
        !           445:   int i;
        !           446: 
        !           447:   /* Compare using the first keyfield;
        !           448:      if that does not distinguish the lines, try the second keyfield; and so on. */
        !           449: 
        !           450:   for (i = 0; i < num_keyfields; i++)
        !           451:     {
        !           452:       long length1, length2;
        !           453:       char *start1 = find_field (&keyfields[i], *line1, &length1);
        !           454:       char *start2 = find_field (&keyfields[i], *line2, &length2);
        !           455:       int tem = compare_field (&keyfields[i], start1, length1, *line1 - text_base,
        !           456:                                              start2, length2, *line2 - text_base);
        !           457:       if (tem)
        !           458:        {
        !           459:          if (keyfields[i].reverse)
        !           460:            return - tem;
        !           461:           return tem;
        !           462:        }
        !           463:     }
        !           464: 
        !           465:   return 0;    /* Lines match exactly */
        !           466: }
        !           467: 
        !           468: /* Compare two lines described by structures
        !           469:  in which the first keyfield is identified in advance.
        !           470:  For positional sorting, assumes that the order of the lines in core
        !           471:  reflects their nominal order.  */
        !           472: 
        !           473: int
        !           474: compare_prepared (line1, line2)
        !           475:      struct lineinfo *line1, *line2;
        !           476: {
        !           477:   int i;
        !           478:   int tem;
        !           479:   char *text1, *text2;
        !           480: 
        !           481:   /* Compare using the first keyfield, which has been found for us already */
        !           482:   if (keyfields->positional)
        !           483:     {
        !           484:       if (line1->text - text_base > line2->text - text_base)
        !           485:        tem = 1;
        !           486:       else
        !           487:        tem = -1;
        !           488:     }
        !           489:   else if (keyfields->numeric)
        !           490:     tem = line1->key.number - line2->key.number;
        !           491:   else
        !           492:     tem = compare_field (keyfields, line1->key.text, line1->keylen, 0, line2->key.text, line2->keylen, 0);
        !           493:   if (tem)
        !           494:     {
        !           495:       if (keyfields->reverse)
        !           496:        return - tem;
        !           497:       return tem;
        !           498:     }
        !           499: 
        !           500:   text1 = line1->text;
        !           501:   text2 = line2->text;
        !           502: 
        !           503:   /* Compare using the second keyfield;
        !           504:      if that does not distinguish the lines, try the third keyfield; and so on. */
        !           505: 
        !           506:   for (i = 1; i < num_keyfields; i++)
        !           507:     {
        !           508:       long length1, length2;
        !           509:       char *start1 = find_field (&keyfields[i], text1, &length1);
        !           510:       char *start2 = find_field (&keyfields[i], text2, &length2);
        !           511:       int tem = compare_field (&keyfields[i], start1, length1, text1 - text_base,
        !           512:                                              start2, length2, text2 - text_base);
        !           513:       if (tem)
        !           514:        {
        !           515:          if (keyfields[i].reverse)
        !           516:            return - tem;
        !           517:           return tem;
        !           518:        }
        !           519:     }
        !           520: 
        !           521:   return 0;    /* Lines match exactly */
        !           522: }
        !           523: 
        !           524: /* Like compare_full but more general.
        !           525:  You can pass any strings, and you can say how many keyfields to use.
        !           526:  `pos1' and `pos2' should indicate the nominal positional ordering of
        !           527:  the two lines in the input.  */
        !           528: 
        !           529: int
        !           530: compare_general (str1, str2, pos1, pos2, use_keyfields)
        !           531:      char *str1, *str2;
        !           532:      long pos1, pos2;
        !           533:      int use_keyfields;
        !           534: {
        !           535:   int i;
        !           536: 
        !           537:   /* Compare using the first keyfield;
        !           538:      if that does not distinguish the lines, try the second keyfield; and so on. */
        !           539: 
        !           540:   for (i = 0; i < use_keyfields; i++)
        !           541:     {
        !           542:       long length1, length2;
        !           543:       char *start1 = find_field (&keyfields[i], str1, &length1);
        !           544:       char *start2 = find_field (&keyfields[i], str2, &length2);
        !           545:       int tem = compare_field (&keyfields[i], start1, length1, pos1, start2, length2, pos2);
        !           546:       if (tem)
        !           547:        {
        !           548:          if (keyfields[i].reverse)
        !           549:            return - tem;
        !           550:           return tem;
        !           551:        }
        !           552:     }
        !           553: 
        !           554:   return 0;    /* Lines match exactly */
        !           555: }
        !           556: 
        !           557: /* Find the start and length of a field in `str' according to `keyfield'.
        !           558:  A pointer to the starting character is returned, and the length
        !           559:  is stored into the int that `lengthptr' points to.  */
        !           560: 
        !           561: char *
        !           562: find_field (keyfield, str, lengthptr)
        !           563:      struct keyfield *keyfield;
        !           564:      char *str;
        !           565:      long *lengthptr;
        !           566: {
        !           567:   char *start;
        !           568:   char *end;
        !           569:   char *(*fun) ();
        !           570: 
        !           571:   if (keyfield->braced) fun = find_braced_pos;
        !           572:   else fun = find_pos;
        !           573: 
        !           574:   start = ( *fun )(str, keyfield->startwords, keyfield->startchars,
        !           575:               keyfield->ignore_blanks);
        !           576:   if (keyfield->endwords < 0)
        !           577:     {
        !           578:       if (keyfield->braced)
        !           579:        end = find_braced_end (start);
        !           580:       else
        !           581:        {
        !           582:          end = start;
        !           583:          while (*end && *end != '\n') end++;
        !           584:        }
        !           585:     }
        !           586:   else
        !           587:     {
        !           588:       end = ( *fun )(str, keyfield->endwords, keyfield->endchars, 0);
        !           589:       if (end - str < start - str) end = start;
        !           590:     }
        !           591:   *lengthptr = end - start;
        !           592:   return start;
        !           593: }
        !           594: 
        !           595: /* Find a pointer to a specified place within `str',
        !           596:  skipping (from the beginning) `words' words and then `chars' chars.
        !           597:  If `ignore_blanks' is nonzero, we skip all blanks
        !           598:  after finding the specified word.  */
        !           599: 
        !           600: char *
        !           601: find_pos (str, words, chars, ignore_blanks)
        !           602:      char *str;
        !           603:      int words, chars;
        !           604:      int ignore_blanks;
        !           605: {
        !           606:   int i;
        !           607:   char *p = str;
        !           608: 
        !           609:   for (i = 0; i < words; i++)
        !           610:     {
        !           611:       char c;
        !           612:       /* Find next bunch of nonblanks and skip them. */
        !           613:       while ((c = *p) == ' ' || c == '\t') p++;
        !           614:       while ((c = *p) && c != '\n' && !(c == ' ' || c == '\t')) p++;
        !           615:       if (!*p || *p == '\n') return p;
        !           616:     }
        !           617: 
        !           618:   while (*p == ' ' || *p == '\t') p++;
        !           619: 
        !           620:   for (i = 0; i < chars; i++)
        !           621:     {
        !           622:       if (!*p  || *p == '\n') break;
        !           623:       p++;
        !           624:     }
        !           625:   return p;
        !           626: }
        !           627: 
        !           628: /* Like find_pos but assumes that each field is surrounded by braces
        !           629:  and that braces within fields are balanced. */
        !           630: 
        !           631: char *
        !           632: find_braced_pos (str, words, chars, ignore_blanks)
        !           633:      char *str;
        !           634:      int words, chars;
        !           635:      int ignore_blanks;
        !           636: {
        !           637:   int i;
        !           638:   int bracelevel;
        !           639:   char *p = str;
        !           640:   char c;
        !           641: 
        !           642:   for (i = 0; i < words; i++)
        !           643:     {
        !           644:       bracelevel = 1;
        !           645:       while ((c = *p++) != '{' && c != '\n' && c);
        !           646:       if (c != '{')
        !           647:        return p - 1;
        !           648:       while (bracelevel)
        !           649:        {
        !           650:          c = *p++;
        !           651:          if (c == '{') bracelevel++;
        !           652:          if (c == '}') bracelevel--;
        !           653:          if (c == '\\') c = *p++;      /* \ quotes braces and \ */
        !           654:          if (c == 0 || c == '\n') return p-1;
        !           655:        }
        !           656:     }
        !           657: 
        !           658:   while ((c = *p++) != '{' && c != '\n' && c);
        !           659: 
        !           660:   if (c != '{')
        !           661:     return p-1;
        !           662: 
        !           663:   if (ignore_blanks)
        !           664:     while ((c = *p) == ' ' || c == '\t') p++;
        !           665:   
        !           666:   for (i = 0; i < chars; i++)
        !           667:     {
        !           668:       if (!*p  || *p == '\n') break;
        !           669:       p++;
        !           670:     }
        !           671:   return p;
        !           672: }
        !           673: 
        !           674: /* Find the end of the balanced-brace field which starts at `str'.
        !           675:   The position returned is just before the closing brace. */
        !           676: 
        !           677: char *
        !           678: find_braced_end (str)
        !           679:      char *str;
        !           680: {
        !           681:   int bracelevel;
        !           682:   char *p = str;
        !           683:   char c;
        !           684: 
        !           685:   bracelevel = 1;
        !           686:   while (bracelevel)
        !           687:     {
        !           688:       c = *p++;
        !           689:       if (c == '{') bracelevel++;
        !           690:       if (c == '}') bracelevel--;
        !           691:       if (c == '\\') c = *p++;
        !           692:       if (c == 0 || c == '\n') return p-1;
        !           693:     }
        !           694:   return p - 1;
        !           695: }
        !           696: 
        !           697: long
        !           698: find_value (start, length)
        !           699:      char *start;
        !           700:      long length;
        !           701: {
        !           702:   while (length != 0L) {
        !           703:     if (isdigit(*start))
        !           704:       return atol(start);
        !           705:     length--;
        !           706:     start++;
        !           707:   }
        !           708:   return 0l;
        !           709: }
        !           710: 
        !           711: /* Vector used to translate characters for comparison.
        !           712:    This is how we make all alphanumerics follow all else,
        !           713:    and ignore case in the first sorting.  */
        !           714: int char_order[256];
        !           715: 
        !           716: init_char_order ()
        !           717: {
        !           718:   int i;
        !           719:   for (i = 1; i < 256; i++)
        !           720:     char_order[i] = i;
        !           721: 
        !           722:   for (i = '0'; i <= '9'; i++)
        !           723:     char_order[i] += 512;
        !           724: 
        !           725:   for (i = 'a'; i <= 'z'; i++) {
        !           726:     char_order[i] = 512 + i;
        !           727:     char_order[i + 'A' - 'a'] = 512 + i;
        !           728:   }
        !           729: }
        !           730: 
        !           731: /* Compare two fields (each specified as a start pointer and a character count)
        !           732:  according to `keyfield'.  The sign of the value reports the relation between the fields */
        !           733: 
        !           734: int
        !           735: compare_field (keyfield, start1, length1, pos1, start2, length2, pos2)
        !           736:      struct keyfield *keyfield;
        !           737:      char *start1;
        !           738:      long length1;
        !           739:      long pos1;
        !           740:      char *start2;
        !           741:      long length2;
        !           742:      long pos2;
        !           743: {
        !           744:   if (keyfields->positional)
        !           745:     {
        !           746:       if (pos1 > pos2)
        !           747:        return 1;
        !           748:       else
        !           749:        return -1;
        !           750:     }
        !           751:   if (keyfield->numeric)
        !           752:     {
        !           753:        long value = find_value (start1, length1) - find_value (start2, length2);
        !           754:       if (value > 0) return 1;
        !           755:       if (value < 0) return -1;
        !           756:       return 0;
        !           757:     }
        !           758:   else
        !           759:     {
        !           760:       char *p1 = start1;
        !           761:       char *p2 = start2;
        !           762:       char *e1 = start1 + length1;
        !           763:       char *e2 = start2 + length2;
        !           764: 
        !           765:       int fold_case = keyfield->fold_case;
        !           766: 
        !           767:       while (1)
        !           768:        {
        !           769:          int c1, c2;
        !           770: 
        !           771:          if (p1 == e1) c1 = 0;
        !           772:          else c1 = *p1++;
        !           773:          if (p2 == e2) c2 = 0;
        !           774:          else c2 = *p2++;
        !           775: 
        !           776:          if (char_order[c1] != char_order[c2])
        !           777:            return char_order[c1] - char_order[c2];
        !           778:          if (!c1) break;
        !           779:        }
        !           780: 
        !           781:       /* Strings are equal except possibly for case.  */
        !           782:       p1 = start1;
        !           783:       p2 = start2;
        !           784:       while (1)
        !           785:        {
        !           786:          int c1, c2;
        !           787: 
        !           788:          if (p1 == e1) c1 = 0;
        !           789:          else c1 = *p1++;
        !           790:          if (p2 == e2) c2 = 0;
        !           791:          else c2 = *p2++;
        !           792: 
        !           793:          if (c1 != c2)
        !           794:            /* Reverse sign here so upper case comes out last.  */
        !           795:            return c2 - c1;
        !           796:          if (!c1) break;
        !           797:        }
        !           798: 
        !           799:       return 0;
        !           800:     }
        !           801: }
        !           802: 
        !           803: /* A `struct linebuffer' is a structure which holds a line of text.
        !           804:  `readline' reads a line from a stream into a linebuffer
        !           805:  and works regardless of the length of the line.  */
        !           806: 
        !           807: struct linebuffer
        !           808:   {
        !           809:     long size;
        !           810:     char *buffer;
        !           811:   };
        !           812: 
        !           813: /* Initialize a linebuffer for use */
        !           814: 
        !           815: void
        !           816: initbuffer (linebuffer)
        !           817:      struct linebuffer *linebuffer;
        !           818: {
        !           819:   linebuffer->size = 200;
        !           820:   linebuffer->buffer = (char *) xmalloc (200);
        !           821: }
        !           822: 
        !           823: /* Read a line of text from `stream' into `linebuffer'.
        !           824:  Return the length of the line.  */
        !           825: 
        !           826: long
        !           827: readline (linebuffer, stream)
        !           828:      struct linebuffer *linebuffer;
        !           829:      FILE *stream;
        !           830: {
        !           831:   char *buffer = linebuffer->buffer;
        !           832:   char *p = linebuffer->buffer;
        !           833:   char *end = p + linebuffer->size;
        !           834: 
        !           835:   while (1)
        !           836:     {
        !           837:       int c = getc (stream);
        !           838:       if (p == end)
        !           839:        {
        !           840:          buffer = (char *) xrealloc (buffer, linebuffer->size *= 2);
        !           841:          p += buffer - linebuffer->buffer;
        !           842:          end += buffer - linebuffer->buffer;
        !           843:          linebuffer->buffer = buffer;
        !           844:        }
        !           845:       if (c < 0 || c == '\n')
        !           846:        {
        !           847:          *p = 0;
        !           848:          break;
        !           849:        }
        !           850:       *p++ = c;
        !           851:     }
        !           852: 
        !           853:   return p - buffer;
        !           854: }
        !           855: 
        !           856: /* Sort an input file too big to sort in core.  */
        !           857: 
        !           858: void
        !           859: sort_offline (infile, nfiles, total, outfile)
        !           860:      char *infile;
        !           861:      long total;
        !           862:      char *outfile;
        !           863: {
        !           864:   int ntemps = 2 * (total + MAX_IN_CORE_SORT - 1) / MAX_IN_CORE_SORT;  /* More than enough */
        !           865:   char **tempfiles = (char **) xmalloc (ntemps * sizeof (char *));
        !           866:   FILE *istream = fopen (infile, "r");
        !           867:   int i;
        !           868:   struct linebuffer lb;
        !           869:   long linelength;
        !           870:   int failure = 0;
        !           871: 
        !           872:   initbuffer (&lb);
        !           873: 
        !           874:   /* Read in one line of input data.  */
        !           875: 
        !           876:   linelength = readline (&lb, istream);
        !           877: 
        !           878:   if (lb.buffer[0] != '\\')
        !           879:     {
        !           880:       error ("%s: not a texinfo index file", infile);
        !           881:       return;
        !           882:     }
        !           883: 
        !           884:   /* Split up the input into `ntemps' temporary files, or maybe fewer,
        !           885:     and put the new files' names into `tempfiles' */
        !           886: 
        !           887:   for (i = 0; i < ntemps; i++)
        !           888:     {
        !           889:       char *outname = maketempname (++tempcount);
        !           890:       FILE *ostream = fopen (outname, "w");
        !           891:       long tempsize = 0;
        !           892: 
        !           893:       if (!ostream) pfatal_with_name (outname);
        !           894:       tempfiles[i] = outname;
        !           895: 
        !           896:       /* Copy lines into this temp file as long as it does not make file "too big"
        !           897:        or until there are no more lines.  */
        !           898: 
        !           899:       while (tempsize + linelength + 1 <= MAX_IN_CORE_SORT)
        !           900:        {
        !           901:          tempsize += linelength + 1;
        !           902:          fputs (lb.buffer, ostream);
        !           903:          putc ('\n', ostream);
        !           904: 
        !           905:          /* Read another line of input data.  */
        !           906: 
        !           907:          linelength = readline (&lb, istream);
        !           908:          if (!linelength && feof (istream)) break;
        !           909: 
        !           910:          if (lb.buffer[0] != '\\')
        !           911:            {
        !           912:              error ("%s: not a texinfo index file", infile);
        !           913:              failure = 1;
        !           914:              goto fail;
        !           915:            }
        !           916:        }
        !           917:       fclose (ostream);
        !           918:       if (feof (istream)) break;
        !           919:     }
        !           920: 
        !           921:   free (lb.buffer);
        !           922: 
        !           923:  fail:
        !           924:   /* Record number of temp files we actually needed.  */
        !           925: 
        !           926:   ntemps = i;
        !           927: 
        !           928:   /* Sort each tempfile into another tempfile.
        !           929:     Delete the first set of tempfiles and put the names of the second into `tempfiles' */
        !           930: 
        !           931:   for (i = 0; i < ntemps; i++)
        !           932:     {
        !           933:       char *newtemp = maketempname (++tempcount);
        !           934:       sort_in_core (&tempfiles[i], MAX_IN_CORE_SORT, newtemp);
        !           935:       if (!keep_tempfiles)
        !           936:         unlink (tempfiles[i]);
        !           937:       tempfiles[i] = newtemp;
        !           938:     }
        !           939: 
        !           940:   if (failure)
        !           941:     return;
        !           942: 
        !           943:   /* Merge the tempfiles together and indexify */
        !           944: 
        !           945:   merge_files (tempfiles, ntemps, outfile);
        !           946: }
        !           947: 
        !           948: /* Sort `infile', whose size is `total',
        !           949:  assuming that is small enough to be done in-core,
        !           950:  then indexify it and send the output to `outfile' (or to stdout).  */
        !           951: 
        !           952: void
        !           953: sort_in_core (infile, total, outfile)
        !           954:      char *infile;
        !           955:      long total;
        !           956:      char *outfile;
        !           957: {
        !           958:   char **nextline;
        !           959:   char *data = (char *) xmalloc (total + 1);
        !           960:   char *file_data;
        !           961:   long file_size;
        !           962:   int i;
        !           963:   FILE *ostream = stdout;
        !           964:   struct lineinfo *lineinfo;
        !           965: 
        !           966:   /* Read the contents of the file into the moby array `data' */
        !           967: 
        !           968:   int desc = open (infile, 0, 0);
        !           969: 
        !           970:   if (desc < 0)
        !           971:     fatal ("failure reopening %s", infile);
        !           972:   for (file_size = 0; ; )
        !           973:     {
        !           974:       if ((i = read (desc, data + file_size, total - file_size)) <= 0)
        !           975:        break;
        !           976:       file_size += i;
        !           977:     }
        !           978:   file_data = data;
        !           979:   data[file_size] = 0;
        !           980: 
        !           981:   close (desc);
        !           982: 
        !           983:   if (file_size > 0 && data[0] != '\\')
        !           984:     {
        !           985:       error ("%s: not a texinfo index file", infile);
        !           986:       return;
        !           987:     }
        !           988: 
        !           989:   init_char_order ();
        !           990: 
        !           991:   /* Sort routines want to know this address */
        !           992: 
        !           993:   text_base = data;
        !           994: 
        !           995:   /* Create the array of pointers to lines, with a default size frequently enough.  */
        !           996: 
        !           997:   lines = total / 50;
        !           998:   if (!lines) lines = 2;
        !           999:   linearray = (char **) xmalloc (lines * sizeof (char *));
        !          1000: 
        !          1001:   /* `nextline' points to the next free slot in this array.
        !          1002:      `lines' is the allocated size.  */
        !          1003: 
        !          1004:   nextline = linearray;
        !          1005: 
        !          1006:   /* Parse the input file's data, and make entries for the lines.  */
        !          1007: 
        !          1008:   nextline = parsefile (infile, nextline, file_data, file_size);
        !          1009:   if (nextline == 0)
        !          1010:     {
        !          1011:       error ("%s: not a texinfo index file", infile);
        !          1012:       return;
        !          1013:     }
        !          1014: 
        !          1015:   /* Sort the lines */
        !          1016: 
        !          1017:   /* If we have enough space, find the first keyfield of each line in advance.
        !          1018:     Make a `struct lineinfo' for each line, which records the keyfield
        !          1019:     as well as the line, and sort them.  */
        !          1020: 
        !          1021:   lineinfo = (struct lineinfo *) malloc ((nextline - linearray) * sizeof (struct lineinfo));
        !          1022: 
        !          1023:   if (lineinfo)
        !          1024:     {
        !          1025:       struct lineinfo *lp;
        !          1026:       char **p;
        !          1027: 
        !          1028:       for (lp = lineinfo, p = linearray; p != nextline; lp++, p++)
        !          1029:        {
        !          1030:          lp->text = *p;
        !          1031:          lp->key.text = find_field (keyfields, *p, &lp->keylen);
        !          1032:          if (keyfields->numeric)
        !          1033:            lp->key.number = find_value (lp->key.text, lp->keylen);
        !          1034:        }
        !          1035: 
        !          1036:       qsort (lineinfo, nextline - linearray, sizeof (struct lineinfo), compare_prepared);
        !          1037: 
        !          1038:       for (lp = lineinfo, p = linearray; p != nextline; lp++, p++)
        !          1039:        *p = lp->text;
        !          1040: 
        !          1041:       free (lineinfo);
        !          1042:     }
        !          1043:   else
        !          1044:     qsort (linearray, nextline - linearray, sizeof (char *), compare_full);
        !          1045: 
        !          1046:   /* Open the output file */
        !          1047: 
        !          1048:   if (outfile)
        !          1049:     {
        !          1050:       ostream = fopen (outfile, "w");
        !          1051:       if (!ostream)
        !          1052:        pfatal_with_name (outfile);
        !          1053:     }
        !          1054: 
        !          1055:   writelines (linearray, nextline - linearray, ostream);
        !          1056:   if (outfile) fclose (ostream);
        !          1057: 
        !          1058:   free (linearray);
        !          1059:   free (data);
        !          1060: }
        !          1061: 
        !          1062: /* Parse an input string in core into lines.
        !          1063:    DATA is the input string, and SIZE is its length.
        !          1064:    Data goes in LINEARRAY starting at NEXTLINE.
        !          1065:    The value returned is the first entry in LINEARRAY still unused.
        !          1066:    Value 0 means input file contents are invalid.  */
        !          1067: 
        !          1068: char **
        !          1069: parsefile (filename, nextline, data, size)
        !          1070:      char *filename;
        !          1071:      char **nextline;
        !          1072:      char *data;
        !          1073:      long size;
        !          1074: {
        !          1075:   char *p, *end;
        !          1076:   char **line = nextline;
        !          1077: 
        !          1078:   p = data;
        !          1079:   end = p + size;
        !          1080:   *end = 0;
        !          1081: 
        !          1082:   while (p != end)
        !          1083:     {
        !          1084:       if (p[0] != '\\')
        !          1085:        return 0;
        !          1086: 
        !          1087:       *line = p;
        !          1088:       while (*p && *p != '\n') p++;
        !          1089:       if (p != end) p++;
        !          1090: 
        !          1091:       line++;
        !          1092:       if (line == linearray + lines)
        !          1093:        {
        !          1094:          char **old = linearray;
        !          1095:          linearray = (char **) xrealloc (linearray, sizeof (char *) * (lines *= 4));
        !          1096:          line += linearray - old;
        !          1097:        }
        !          1098:     }
        !          1099: 
        !          1100:   return line;
        !          1101: }
        !          1102: 
        !          1103: /* Indexification is a filter applied to the sorted lines
        !          1104:  as they are being written to the output file.
        !          1105:  Multiple entries for the same name, with different page numbers,
        !          1106:  get combined into a single entry with multiple page numbers.
        !          1107:  The first braced field, which is used for sorting, is discarded.
        !          1108:  However, its first character is examined, folded to lower case,
        !          1109:  and if it is different from that in the previous line fed to us
        !          1110:  a \initial line is written with one argument, the new initial.
        !          1111: 
        !          1112:  If an entry has four braced fields, then the second and third
        !          1113:  constitute primary and secondary names.
        !          1114:  In this case, each change of primary name
        !          1115:  generates a \primary line which contains only the primary name,
        !          1116:  and in between these are \secondary lines which contain
        !          1117:  just a secondary name and page numbers.
        !          1118: */
        !          1119: 
        !          1120: /* The last primary name we wrote a \primary entry for.
        !          1121:  If only one level of indexing is being done, this is the last name seen */
        !          1122: char *lastprimary;
        !          1123: int lastprimarylength;  /* Length of storage allocated for lastprimary */
        !          1124: 
        !          1125: /* Similar, for the secondary name. */
        !          1126: char *lastsecondary;
        !          1127: int lastsecondarylength;
        !          1128: 
        !          1129: /* Zero if we are not in the middle of writing an entry.
        !          1130:  One if we have written the beginning of an entry but have not
        !          1131:   yet written any page numbers into it.
        !          1132:  Greater than one if we have written the beginning of an entry
        !          1133:   plus at least one page number. */
        !          1134: int pending;
        !          1135: 
        !          1136: /* The initial (for sorting purposes) of the last primary entry written.
        !          1137:  When this changes, a \initial {c} line is written */
        !          1138: 
        !          1139: char * lastinitial;
        !          1140: 
        !          1141: int lastinitiallength;
        !          1142: 
        !          1143: /* When we need a string of length 1 for the value of lastinitial,
        !          1144:    store it here.  */
        !          1145: 
        !          1146: char lastinitial1[2];
        !          1147: 
        !          1148: /* Initialize static storage for writing an index */
        !          1149: 
        !          1150: void
        !          1151: init_index ()
        !          1152: {
        !          1153:   pending = 0;
        !          1154:   lastinitial = lastinitial1;
        !          1155:   lastinitial1[0] = 0;
        !          1156:   lastinitial1[1] = 0;
        !          1157:   lastinitiallength = 0;
        !          1158:   lastprimarylength = 100;
        !          1159:   lastprimary = (char *) xmalloc (lastprimarylength + 1);
        !          1160:   bzero (lastprimary, lastprimarylength + 1);
        !          1161:   lastsecondarylength = 100;
        !          1162:   lastsecondary = (char *) xmalloc (lastsecondarylength + 1);
        !          1163:   bzero (lastsecondary, lastsecondarylength + 1);
        !          1164: }
        !          1165: 
        !          1166: /* Indexify.  Merge entries for the same name,
        !          1167:  insert headers for each initial character, etc.  */
        !          1168: 
        !          1169: indexify (line, ostream)
        !          1170:      char *line;
        !          1171:      FILE *ostream;
        !          1172: {
        !          1173:   char *primary, *secondary, *pagenumber;
        !          1174:   int primarylength, secondarylength, pagelength;
        !          1175:   int len = strlen (line);
        !          1176:   int nosecondary;
        !          1177:   int initiallength;
        !          1178:   char *initial;
        !          1179:   char initial1[2];
        !          1180:   register char *p;
        !          1181: 
        !          1182:   /* First, analyze the parts of the entry fed to us this time */
        !          1183: 
        !          1184:   p = find_braced_pos (line, 0, 0, 0);
        !          1185:   if (*p == '{')
        !          1186:     {
        !          1187:       initial = p;
        !          1188:       /* Get length of inner pair of braces starting at p,
        !          1189:         including that inner pair of braces.  */
        !          1190:       initiallength = find_braced_end (p + 1) + 1 - p;
        !          1191:     }
        !          1192:   else
        !          1193:     {
        !          1194:       initial = initial1;
        !          1195:       initial1[0] = *p;
        !          1196:       initial1[1] = 0;
        !          1197:       initiallength = 1;
        !          1198: 
        !          1199:       if (initial1[0] >= 'a' && initial1[0] <= 'z')
        !          1200:        initial1[0] -= 040;
        !          1201:     }
        !          1202: 
        !          1203:   pagenumber = find_braced_pos (line, 1, 0, 0);
        !          1204:   pagelength = find_braced_end (pagenumber) - pagenumber;
        !          1205:   if (pagelength == 0)
        !          1206:     abort ();
        !          1207: 
        !          1208:   primary = find_braced_pos (line, 2, 0, 0);
        !          1209:   primarylength = find_braced_end (primary) - primary;
        !          1210: 
        !          1211:   secondary = find_braced_pos (line, 3, 0, 0);
        !          1212:   nosecondary = !*secondary;
        !          1213:   if (!nosecondary)
        !          1214:     secondarylength = find_braced_end (secondary) - secondary;
        !          1215: 
        !          1216:   /* If the primary is different from before, make a new primary entry */
        !          1217:   if (strncmp (primary, lastprimary, primarylength))
        !          1218:     {
        !          1219:       /* Close off current secondary entry first, if one is open */
        !          1220:       if (pending)
        !          1221:        {
        !          1222:          fputs ("}\n", ostream);
        !          1223:          pending = 0;
        !          1224:        }
        !          1225: 
        !          1226:       /* If this primary has a different initial, include an entry for the initial */
        !          1227:       if (initiallength != lastinitiallength ||
        !          1228:          strncmp (initial, lastinitial, initiallength))
        !          1229:        {
        !          1230:          fprintf (ostream, "\\initial {");
        !          1231:          fwrite (initial, 1, initiallength, ostream);
        !          1232:          fprintf (ostream, "}\n", initial);
        !          1233:          if (initial == initial1)
        !          1234:            {
        !          1235:              lastinitial = lastinitial1;
        !          1236:              *lastinitial1 = *initial1;
        !          1237:            }
        !          1238:          else
        !          1239:            {
        !          1240:              lastinitial = initial;
        !          1241:            }
        !          1242:          lastinitiallength = initiallength;
        !          1243:        }
        !          1244: 
        !          1245:       /* Make the entry for the primary.  */
        !          1246:       if (nosecondary)
        !          1247:        fputs ("\\entry {", ostream);
        !          1248:       else
        !          1249:        fputs ("\\primary {", ostream);
        !          1250:       fwrite (primary, primarylength, 1, ostream);
        !          1251:       if (nosecondary)
        !          1252:        {
        !          1253:          fputs ("}{", ostream);
        !          1254:          pending = 1;
        !          1255:        }
        !          1256:       else
        !          1257:        fputs ("}\n", ostream);
        !          1258: 
        !          1259:       /* Record name of most recent primary */
        !          1260:       if (lastprimarylength < primarylength)
        !          1261:        {
        !          1262:           lastprimarylength = primarylength + 100;
        !          1263:          lastprimary = (char *) xrealloc (lastprimary,
        !          1264:                                           1 + lastprimarylength);
        !          1265:        }
        !          1266:       strncpy (lastprimary, primary, primarylength);
        !          1267:       lastprimary[primarylength] = 0;
        !          1268: 
        !          1269:       /* There is no current secondary within this primary, now */
        !          1270:       lastsecondary[0] = 0;
        !          1271:     }
        !          1272: 
        !          1273:   /* Should not have an entry with no subtopic following one with a subtopic */
        !          1274: 
        !          1275:   if (nosecondary && *lastsecondary)
        !          1276:     error ("entry %s follows an entry with a secondary name", line);
        !          1277: 
        !          1278:   /* Start a new secondary entry if necessary */
        !          1279:   if (!nosecondary && strncmp (secondary, lastsecondary, secondarylength))
        !          1280:     {
        !          1281:       if (pending)
        !          1282:        {
        !          1283:          fputs ("}\n", ostream);
        !          1284:          pending = 0;
        !          1285:        }
        !          1286: 
        !          1287:       /* Write the entry for the secondary.  */
        !          1288:       fputs ("\\secondary {", ostream);
        !          1289:       fwrite (secondary, secondarylength, 1, ostream);
        !          1290:       fputs ("}{", ostream);
        !          1291:       pending = 1;
        !          1292: 
        !          1293:       /* Record name of most recent secondary */
        !          1294:       if (lastsecondarylength < secondarylength)
        !          1295:        {
        !          1296:           lastsecondarylength = secondarylength + 100;
        !          1297:          lastsecondary = (char *) xrealloc (lastsecondary,
        !          1298:                                           1 + lastsecondarylength);
        !          1299:        }
        !          1300:       strncpy (lastsecondary, secondary, secondarylength);
        !          1301:       lastsecondary[secondarylength] = 0;
        !          1302:     }
        !          1303: 
        !          1304:   /* Here to add one more page number to the current entry */
        !          1305:   if (pending++ != 1)
        !          1306:     fputs (", ", ostream);     /* Punctuate first, if this is not the first */
        !          1307:   fwrite (pagenumber, pagelength, 1, ostream);
        !          1308: }
        !          1309: 
        !          1310: /* Close out any unfinished output entry */
        !          1311: 
        !          1312: void
        !          1313: finish_index (ostream)
        !          1314:      FILE *ostream;
        !          1315: {
        !          1316:   if (pending)
        !          1317:     fputs ("}\n", ostream);
        !          1318:   free (lastprimary);
        !          1319:   free (lastsecondary);
        !          1320: }
        !          1321: 
        !          1322: /* Copy the lines in the sorted order.
        !          1323:  Each line is copied out of the input file it was found in. */
        !          1324: 
        !          1325: void
        !          1326: writelines (linearray, nlines, ostream)
        !          1327:      char **linearray;
        !          1328:      int nlines;
        !          1329:      FILE *ostream;
        !          1330: {
        !          1331:   char **stop_line = linearray + nlines;
        !          1332:   char **next_line;
        !          1333: 
        !          1334:   init_index ();
        !          1335: 
        !          1336:   /* Output the text of the lines, and free the buffer space */
        !          1337: 
        !          1338:   for (next_line = linearray; next_line != stop_line; next_line++)
        !          1339:     {
        !          1340:       /* If -u was specified, output the line only if distinct from previous one.  */
        !          1341:       if (next_line == linearray
        !          1342:          /* Compare previous line with this one, using only the explicitly specd keyfields */
        !          1343:          || compare_general (*(next_line - 1), *next_line, 0L, 0L, num_keyfields - 1))
        !          1344:        {
        !          1345:          char *p = *next_line;
        !          1346:          char c;
        !          1347:          while ((c = *p++) && c != '\n');
        !          1348:          *(p-1) = 0;
        !          1349:          indexify (*next_line, ostream);
        !          1350:        }
        !          1351:     }
        !          1352: 
        !          1353:   finish_index (ostream);
        !          1354: }
        !          1355: 
        !          1356: /* Assume (and optionally verify) that each input file is sorted;
        !          1357:  merge them and output the result.
        !          1358:  Returns nonzero if any input file fails to be sorted.
        !          1359: 
        !          1360:  This is the high-level interface that can handle an unlimited number of files.  */
        !          1361: 
        !          1362: #define MAX_DIRECT_MERGE 10
        !          1363: 
        !          1364: int
        !          1365: merge_files (infiles, nfiles, outfile)
        !          1366:      char **infiles;
        !          1367:      int nfiles;
        !          1368:      char *outfile;
        !          1369: {
        !          1370:   char **tempfiles;
        !          1371:   int ntemps;
        !          1372:   int i;
        !          1373:   int value = 0;
        !          1374:   int start_tempcount = tempcount;
        !          1375: 
        !          1376:   if (nfiles <= MAX_DIRECT_MERGE)
        !          1377:     return merge_direct (infiles, nfiles, outfile);
        !          1378: 
        !          1379:   /* Merge groups of MAX_DIRECT_MERGE input files at a time,
        !          1380:      making a temporary file to hold each group's result.  */
        !          1381: 
        !          1382:   ntemps = (nfiles + MAX_DIRECT_MERGE - 1) / MAX_DIRECT_MERGE;
        !          1383:   tempfiles = (char **) xmalloc (ntemps * sizeof (char *));
        !          1384:   for (i = 0; i < ntemps; i++)
        !          1385:     {
        !          1386:       int nf = MAX_DIRECT_MERGE;
        !          1387:       if (i + 1 == ntemps)
        !          1388:        nf = nfiles - i * MAX_DIRECT_MERGE;
        !          1389:       tempfiles[i] = maketempname (++tempcount);
        !          1390:       value |= merge_direct (&infiles[i * MAX_DIRECT_MERGE], nf, tempfiles[i]);
        !          1391:     }
        !          1392: 
        !          1393:   /* All temporary files that existed before are no longer needed
        !          1394:      since their contents have been merged into our new tempfiles.
        !          1395:      So delete them.  */
        !          1396:   flush_tempfiles (start_tempcount);
        !          1397: 
        !          1398:   /* Now merge the temporary files we created.  */
        !          1399: 
        !          1400:   merge_files (tempfiles, ntemps, outfile);
        !          1401: 
        !          1402:   free (tempfiles);
        !          1403: 
        !          1404:   return value;
        !          1405: }  
        !          1406: 
        !          1407: /* Assume (and optionally verify) that each input file is sorted;
        !          1408:  merge them and output the result.
        !          1409:  Returns nonzero if any input file fails to be sorted.
        !          1410: 
        !          1411:  This version of merging will not work if the number of
        !          1412:  input files gets too high.  Higher level functions
        !          1413:  use it only with a bounded number of input files.  */
        !          1414: 
        !          1415: int
        !          1416: merge_direct (infiles, nfiles, outfile)
        !          1417:      char **infiles;
        !          1418:      int nfiles;
        !          1419:      char *outfile;
        !          1420: {
        !          1421:   char **ip = infiles;
        !          1422:   struct linebuffer *lb1, *lb2;
        !          1423:   struct linebuffer **thisline, **prevline;
        !          1424:   FILE **streams;
        !          1425:   int i;
        !          1426:   int nleft;
        !          1427:   int lossage = 0;
        !          1428:   int *file_lossage;
        !          1429:   struct linebuffer *prev_out = 0;
        !          1430:   FILE *ostream = stdout;
        !          1431: 
        !          1432:   if (outfile)
        !          1433:     {
        !          1434:       ostream = fopen (outfile, "w");
        !          1435:     }
        !          1436:   if (!ostream) pfatal_with_name (outfile);
        !          1437: 
        !          1438:   init_index ();
        !          1439: 
        !          1440:   if (nfiles == 0)
        !          1441:     {
        !          1442:       if (outfile)
        !          1443:         fclose (ostream);
        !          1444:       return 0;
        !          1445:     }
        !          1446: 
        !          1447:   /* For each file, make two line buffers.
        !          1448:      Also, for each file, there is an element of `thisline'
        !          1449:      which points at any time to one of the file's two buffers,
        !          1450:      and an element of `prevline' which points to the other buffer.
        !          1451:      `thisline' is supposed to point to the next available line from the file,
        !          1452:      while `prevline' holds the last file line used,
        !          1453:      which is remembered so that we can verify that the file is properly sorted. */
        !          1454: 
        !          1455:   /* lb1 and lb2 contain one buffer each per file */
        !          1456:   lb1 = (struct linebuffer *) xmalloc (nfiles * sizeof (struct linebuffer));
        !          1457:   lb2 = (struct linebuffer *) xmalloc (nfiles * sizeof (struct linebuffer));
        !          1458: 
        !          1459:   /* thisline[i] points to the linebuffer holding the next available line in file i,
        !          1460:      or is zero if there are no lines left in that file.  */
        !          1461:   thisline = (struct linebuffer **) xmalloc (nfiles * sizeof (struct linebuffer *));
        !          1462:   /* prevline[i] points to the linebuffer holding the last used line from file i.
        !          1463:      This is just for verifying that file i is properly sorted.  */
        !          1464:   prevline = (struct linebuffer **) xmalloc (nfiles * sizeof (struct linebuffer *));
        !          1465:   /* streams[i] holds the input stream for file i.  */
        !          1466:   streams = (FILE **) xmalloc (nfiles * sizeof (FILE *));
        !          1467:   /* file_lossage[i] is nonzero if we already know file i is not properly sorted.  */
        !          1468:   file_lossage = (int *) xmalloc (nfiles * sizeof (int));
        !          1469: 
        !          1470:   /* Allocate and initialize all that storage */
        !          1471: 
        !          1472:   for (i = 0; i < nfiles; i++)
        !          1473:     {
        !          1474:       initbuffer (&lb1[i]);
        !          1475:       initbuffer (&lb2[i]);
        !          1476:       thisline[i] = &lb1[i];
        !          1477:       prevline[i] = &lb2[i];
        !          1478:       file_lossage[i] = 0;
        !          1479:       streams[i] = fopen (infiles[i], "r");
        !          1480:       if (!streams[i])
        !          1481:        pfatal_with_name (infiles[i]);
        !          1482: 
        !          1483:       readline (thisline[i], streams[i]);
        !          1484:     }
        !          1485: 
        !          1486:   /* Keep count of number of files not at eof */
        !          1487:   nleft = nfiles;
        !          1488: 
        !          1489:   while (nleft)
        !          1490:     {
        !          1491:       struct linebuffer *best = 0;
        !          1492:       struct linebuffer *exch;
        !          1493:       int bestfile = -1;
        !          1494:       int i;
        !          1495: 
        !          1496:       /* Look at the next avail line of each file; choose the least one.  */
        !          1497: 
        !          1498:       for (i = 0; i < nfiles; i++)
        !          1499:        {
        !          1500:          if (thisline[i] &&
        !          1501:              (!best ||
        !          1502:               0 < compare_general (best->buffer, thisline[i]->buffer,
        !          1503:                                    (long) bestfile, (long) i, num_keyfields)))
        !          1504:            {
        !          1505:              best = thisline[i];
        !          1506:              bestfile = i;
        !          1507:            }
        !          1508:        }
        !          1509: 
        !          1510:       /* Output that line, unless it matches the previous one and we don't want duplicates */
        !          1511: 
        !          1512:       if (!(prev_out &&
        !          1513:            !compare_general (prev_out->buffer, best->buffer, 0L, 1L, num_keyfields - 1)))
        !          1514:        indexify (best->buffer, ostream);
        !          1515:       prev_out = best;
        !          1516: 
        !          1517:       /* Now make the line the previous of its file, and fetch a new line from that file */
        !          1518: 
        !          1519:       exch = prevline[bestfile];
        !          1520:       prevline[bestfile] = thisline[bestfile];
        !          1521:       thisline[bestfile] = exch;
        !          1522: 
        !          1523:       while (1)
        !          1524:        {
        !          1525:          /* If the file has no more, mark it empty */
        !          1526: 
        !          1527:          if (feof (streams[bestfile]))
        !          1528:            {
        !          1529:              thisline[bestfile] = 0;
        !          1530:              nleft--;          /* Update the number of files still not empty */
        !          1531:              break;
        !          1532:            }
        !          1533:          readline (thisline[bestfile], streams[bestfile]);
        !          1534:          if (thisline[bestfile]->buffer[0] || !feof (streams[bestfile])) break;
        !          1535:        }
        !          1536:     }
        !          1537: 
        !          1538:   finish_index (ostream);
        !          1539: 
        !          1540:   /* Free all storage and close all input streams */
        !          1541: 
        !          1542:   for (i = 0; i < nfiles; i++)
        !          1543:     {
        !          1544:       fclose (streams[i]);
        !          1545:       free (lb1[i].buffer);
        !          1546:       free (lb2[i].buffer);
        !          1547:     }
        !          1548:   free (file_lossage);
        !          1549:   free (lb1);
        !          1550:   free (lb2);
        !          1551:   free (thisline);
        !          1552:   free (prevline);
        !          1553:   free (streams);
        !          1554: 
        !          1555:   if (outfile)
        !          1556:     fclose (ostream);
        !          1557: 
        !          1558:   return lossage;
        !          1559: }
        !          1560: 
        !          1561: /* Print error message and exit.  */
        !          1562: 
        !          1563: fatal (s1, s2)
        !          1564:      char *s1, *s2;
        !          1565: {
        !          1566:   error (s1, s2);
        !          1567:   exit (EXIT_FATAL);
        !          1568: }
        !          1569: 
        !          1570: /* Print error message.  `s1' is printf control string, `s2' is arg for it. */
        !          1571: 
        !          1572: error (s1, s2)
        !          1573:      char *s1, *s2;
        !          1574: {
        !          1575:   printf ("texindex: ");
        !          1576:   printf (s1, s2);
        !          1577:   printf ("\n");
        !          1578: }
        !          1579: 
        !          1580: perror_with_name (name)
        !          1581:      char *name;
        !          1582: {
        !          1583: #ifdef VMS
        !          1584: #include <errno.h>
        !          1585:   extern noshare int sys_nerr;
        !          1586:   extern noshare char *sys_errlist[];
        !          1587: #else
        !          1588:   extern int errno, sys_nerr;
        !          1589:   extern char *sys_errlist[];
        !          1590: #endif
        !          1591:   char *s;
        !          1592: 
        !          1593:   if (errno < sys_nerr)
        !          1594:     s = concat ("", sys_errlist[errno], " for %s");
        !          1595:   else
        !          1596:     s = "cannot open %s";
        !          1597:   error (s, name);
        !          1598: }
        !          1599: 
        !          1600: pfatal_with_name (name)
        !          1601:      char *name;
        !          1602: {
        !          1603:   extern int errno, sys_nerr;
        !          1604:   extern char *sys_errlist[];
        !          1605:   char *s;
        !          1606: 
        !          1607:   if (errno < sys_nerr)
        !          1608:     s = concat ("", sys_errlist[errno], " for %s");
        !          1609:   else
        !          1610:     s = "cannot open %s";
        !          1611:   fatal (s, name);
        !          1612: }
        !          1613: 
        !          1614: /* Return a newly-allocated string whose contents concatenate those of s1, s2, s3.  */
        !          1615: 
        !          1616: char *
        !          1617: concat (s1, s2, s3)
        !          1618:      char *s1, *s2, *s3;
        !          1619: {
        !          1620:   int len1 = strlen (s1), len2 = strlen (s2), len3 = strlen (s3);
        !          1621:   char *result = (char *) xmalloc (len1 + len2 + len3 + 1);
        !          1622: 
        !          1623:   strcpy (result, s1);
        !          1624:   strcpy (result + len1, s2);
        !          1625:   strcpy (result + len1 + len2, s3);
        !          1626:   *(result + len1 + len2 + len3) = 0;
        !          1627: 
        !          1628:   return result;
        !          1629: }
        !          1630: 
        !          1631: /* Like malloc but get fatal error if memory is exhausted.  */
        !          1632: 
        !          1633: int
        !          1634: xmalloc (size)
        !          1635:      int size;
        !          1636: {
        !          1637:   int result = malloc (size);
        !          1638:   if (!result)
        !          1639:     fatal ("virtual memory exhausted", 0);
        !          1640:   return result;
        !          1641: }
        !          1642: 
        !          1643: 
        !          1644: int
        !          1645: xrealloc (ptr, size)
        !          1646:      char *ptr;
        !          1647:      int size;
        !          1648: {
        !          1649:   int result = realloc (ptr, size);
        !          1650:   if (!result)
        !          1651:     fatal ("virtual memory exhausted");
        !          1652:   return result;
        !          1653: }
        !          1654: 
        !          1655: bzero (b, length)
        !          1656:      register char *b;
        !          1657:      register int length;
        !          1658: {
        !          1659: #ifdef VMS
        !          1660:   short zero = 0;
        !          1661:   long max_str = 65535;
        !          1662: 
        !          1663:   while (length > max_str) {
        !          1664:     (void) LIB$MOVC5 (&zero, &zero, &zero, &max_str, b);
        !          1665:     length -= max_str;
        !          1666:     b += max_str;
        !          1667:   }
        !          1668:   (void) LIB$MOVC5 (&zero, &zero, &zero, &length, b);
        !          1669: #else
        !          1670:   while (length-- > 0)
        !          1671:     *b++ = 0;
        !          1672: #endif /* not VMS */
        !          1673: }

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.