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