Annotation of 43BSDReno/contrib/emacs-18.55/man/texindex.c, revision 1.1.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.