|
|
1.1 ! root 1: /* ! 2: * The sort command. ! 3: * It does unique sorting, merges, and ! 4: * ordinary sorts with zillions of ! 5: * ways of specifying the sort keys. ! 6: */ ! 7: ! 8: #include <stdio.h> ! 9: #include <ctype.h> ! 10: #include <sys/mdata.h> ! 11: #ifdef COHERENT ! 12: #include <signal.h> ! 13: #endif ! 14: #include <sys/types.h> ! 15: ! 16: #define NREC 400 /* Longest key record */ ! 17: #define NSEL 20 /* Number of records in selection list */ ! 18: #define NPOS 20 /* Number of positionals */ ! 19: #define NTFILE 6 /* Number of intermediate files */ ! 20: #define MORDER (NTFILE-1) /* Order of polyphase merge */ ! 21: #define NDIST (sizeof(dists)/sizeof(dists[0])) ! 22: #define NCSET (MAXUCHAR+1) /* Size of char set */ ! 23: #define NSBRK 1024 /* Amount to add at a time */ ! 24: #define BADSBRK ((char *) -1) /* fail from sbrk() */ ! 25: ! 26: #define rfree(p) {p->r_next=frlist;frlist=p;} /* Free a run */ ! 27: ! 28: /* Key ordering flags (global and field skips) */ ! 29: #define KBLANK 01 /* Ignore leading blanks */ ! 30: #define KDICT 02 /* Dictionary order (letters, digits, blanks) */ ! 31: #define KFOLD 04 /* Fold upper case onto lower case */ ! 32: #define KIGNORE 010 /* Ignore non-ascii characters */ ! 33: #define KNUM 020 /* Numeric sort - skip leading blanks */ ! 34: #define KREV 040 /* Reverse order of sort */ ! 35: ! 36: /* ! 37: * Mapping table to speed up folding. ! 38: * Initialised by `sortinit'. ! 39: */ ! 40: char tabfold[NCSET]; ! 41: ! 42: /* ! 43: * The temp file structure. One for each file ! 44: * contains the list-head for the runs and ! 45: * the FILE stream pointer. ! 46: */ ! 47: typedef struct TFILE { ! 48: struct RUN *tf_runs; ! 49: FILE *tf_fp; ! 50: int tf_nrun; ! 51: fsize_t tf_start; ! 52: } TFILE; ! 53: ! 54: TFILE tfiles[NTFILE]; ! 55: ! 56: /* ! 57: * The structure of each run. contains ! 58: * a pointer to the next and the start ! 59: * and length of the run. ! 60: */ ! 61: typedef struct RUN { ! 62: struct RUN *r_next; ! 63: int r_length; /* Number of records in run */ ! 64: } RUN; ! 65: ! 66: /* ! 67: * Entries in positional (+m.n-m.n) parameters ! 68: */ ! 69: typedef struct POS { ! 70: int p_sflags; /* Start flags */ ! 71: int p_sm; /* fields */ ! 72: int p_sn; /* chars */ ! 73: int p_eflags; /* End flags */ ! 74: int p_em; /* Ending fields */ ! 75: int p_en; /* Ending chars */ ! 76: } POS; ! 77: ! 78: POS pos[NPOS]; ! 79: POS *posp = &pos[-1]; ! 80: ! 81: /* ! 82: * Numeric field breakout structure. ! 83: */ ! 84: typedef struct NUM { ! 85: int n_sign; /* Sign */ ! 86: int n_magn; /* Integer magnitude */ ! 87: int n_fmagn; /* Fractional magnitude */ ! 88: char *n_bgn; /* beginning */ ! 89: char *n_end; /* ending */ ! 90: } NUM; ! 91: ! 92: char **flist; ! 93: char *deflist[] = { ! 94: "-", NULL ! 95: }; ! 96: ! 97: /* ! 98: * This is the best distribution of runs for ! 99: * the polyphase merge. It is a sort of n-way ! 100: * distribution of the Fibonacci number sequence. ! 101: * Each level contains a total number of runs ! 102: * and a way to subdivide them to this. Dummy ! 103: * runs are added to round out the initial distribution. ! 104: * Each successive distribution vector (m0,m1,m2,m3,m4) ! 105: * is obtained by cross product with this matrix: ! 106: * 1 1 1 1 1 ! 107: * 1 0 0 0 0 ! 108: * 0 1 0 0 0 ! 109: * 0 0 1 0 0 ! 110: * 0 0 0 1 0 ! 111: * which can be generalised to any order of merge (other ! 112: * than 5-way). ! 113: * Also no more entries are given as this is ! 114: * likely already overkill for sorting during ! 115: * the lifetime of most machines. ! 116: */ ! 117: struct dists { ! 118: int d_totruns; /* Total of next 5 elements */ ! 119: int d_runs[MORDER]; /* initial distribution */ ! 120: } dists[] = { ! 121: 1, 1, 0, 0, 0, 0, ! 122: 5, 1, 1, 1, 1, 1, ! 123: 9, 2, 2, 2, 2, 1, ! 124: 17, 4, 4, 4, 3, 2, ! 125: 33, 8, 8, 7, 6, 4, ! 126: 65, 16, 15, 14, 12, 8, ! 127: 129, 31, 30, 28, 24, 16, ! 128: 253, 61, 59, 55, 47, 31, ! 129: 497, 120, 116, 108, 92, 61, ! 130: 977, 236, 228, 212, 181, 120, ! 131: 1921, 464, 448, 417, 356, 236, ! 132: 3777, 912, 881, 820, 700, 464, ! 133: 7425, 1793, 1732, 1612, 1376, 912, ! 134: 14597, 3525, 3405, 3169, 2705, 1793, ! 135: 28697, 6930, 6694, 6230, 5318, 3525 ! 136: }; ! 137: ! 138: struct dists *savedsp; /* Save current distribution level */ ! 139: ! 140: char *outname; /* Output other than stdout */ ! 141: char *tempdir; /* Other than default temp directory */ ! 142: char template[100]; ! 143: char obuf[BUFSIZ]; ! 144: char ibuf[BUFSIZ]; ! 145: ! 146: /* ! 147: * Structure for each input record ! 148: * in natural selection. First ! 149: * an insertion sort fills the ! 150: * tree and then all records ! 151: * are input and output until ! 152: * too many are out of order. ! 153: */ ! 154: typedef struct SEL { ! 155: char *s_inb; /* Input buffer pointer */ ! 156: int s_length; /* Length of run in selection */ ! 157: FILE *s_fp; /* File pointer of run */ ! 158: } SEL; ! 159: ! 160: RUN *frlist; /* Free run list */ ! 161: SEL sel[NSEL]; ! 162: SEL lastrec; ! 163: char inbuf[NSEL+1][NREC]; ! 164: #define LASTREC lastrec.s_inb /* Buffer pointer of last written record */ ! 165: long inline; /* Record number of input line, for error recovery */ ! 166: char temperr[] = "Temporary file open error"; ! 167: char tmpwerr[] = "Temporary file write error"; ! 168: char nomem[] = "Out of memory"; ! 169: int pid; /* Current process ID */ ! 170: char tabc; /* Tab character */ ! 171: int cflag; /* Check ordering only */ ! 172: int mflag; /* Merge only */ ! 173: int uflag; /* Unique sort */ ! 174: int kflags; /* Flags to control order to sort */ ! 175: ! 176: char *sgets(); ! 177: RUN *copyfile(); ! 178: RUN *ralloc(); ! 179: char *alloc(); ! 180: int rmexit(); ! 181: int kcompar(); ! 182: int strcmp(); ! 183: int rstrcmp(); ! 184: int (*compar)() = kcompar; ! 185: char *sprintf(); ! 186: ! 187: main(argc, argv) ! 188: int argc; ! 189: char *argv[]; ! 190: { ! 191: register char *ap; ! 192: char dummyop[2]; ! 193: ! 194: setbuf(stdin, ibuf); ! 195: setbuf(stdout, obuf); ! 196: setbuf(stderr, NULL); ! 197: #ifdef COHERENT ! 198: protect(SIGINT); ! 199: protect(SIGHUP); ! 200: protect(SIGPIPE); ! 201: protect(SIGTERM); ! 202: #endif ! 203: while (argc>1 && (*argv[1]=='-' || *argv[1]=='+')) { ! 204: if (*argv[1] == '+') { ! 205: readskip(argv[1]); ! 206: argv++; ! 207: argc--; ! 208: if (argc>1 && argv[1][0]=='-' && isdigit(argv[1][1])) { ! 209: readskip(argv[1]); ! 210: argv++; ! 211: argc--; ! 212: } ! 213: continue; ! 214: } ! 215: for (ap = &argv[1][1]; *ap != '\0'; ap++) ! 216: switch (*ap) { ! 217: ! 218: /* ! 219: * Non-ordering options. ! 220: */ ! 221: case 'c': /* Check ordering only */ ! 222: cflag = 1; ! 223: break; ! 224: ! 225: case 'm': /* Merge only */ ! 226: mflag = 1; ! 227: break; ! 228: ! 229: case 'o': /* Output other than stdout */ ! 230: if (outname != NULL) ! 231: serr("Only one output name allowed"); ! 232: if (--argc < 2) ! 233: usage(); ! 234: argv++; ! 235: outname = argv[1]; ! 236: break; ! 237: ! 238: case 'T': /* Alternative temp directory */ ! 239: if (tempdir != NULL) ! 240: serr("Only one `-T' allowed"); ! 241: if (--argc < 2) ! 242: usage(); ! 243: argv++; ! 244: tempdir = argv[1]; ! 245: break; ! 246: ! 247: case 'u': /* Unique sort */ ! 248: uflag = 1; ! 249: break; ! 250: ! 251: /* ! 252: * Lexicographic ordering options. ! 253: */ ! 254: case 't': /* Tab character */ ! 255: if ((tabc = *++ap) == '\0') ! 256: usage(); ! 257: break; ! 258: ! 259: default: ! 260: dummyop[0] = *ap; ! 261: dummyop[1] = '\0'; ! 262: opts(dummyop, &kflags); ! 263: } ! 264: argc--; ! 265: argv++; ! 266: } ! 267: if (argc > 1) ! 268: flist = argv+1; else ! 269: flist = deflist; ! 270: sortinit(); ! 271: rmexit(sort()); ! 272: } ! 273: ! 274: /* ! 275: * Initialise tables that speed up ! 276: * special orderings. ! 277: */ ! 278: sortinit() ! 279: { ! 280: register int c; ! 281: register char *cp; ! 282: ! 283: cp = tabfold; ! 284: for (c=0; c<NCSET; c++) ! 285: *cp++ = (isascii(c) && isupper(c)) ? tolower(c) : c; ! 286: } ! 287: ! 288: /* ! 289: * Read in the ordering options into ! 290: * the int that is referenced by `flagp' ! 291: * from the string `s'. Used both for ! 292: * global options and with skip options. ! 293: */ ! 294: opts(s, flagp) ! 295: register char *s; ! 296: register int *flagp; ! 297: { ! 298: while (*s) ! 299: switch (*s++) { ! 300: case 'b': ! 301: *flagp |= KBLANK; ! 302: break; ! 303: ! 304: case 'd': ! 305: *flagp |= KDICT; ! 306: break; ! 307: ! 308: case 'f': ! 309: *flagp |= KFOLD; ! 310: break; ! 311: ! 312: case 'i': ! 313: *flagp |= KIGNORE; ! 314: break; ! 315: ! 316: case 'n': ! 317: *flagp |= KNUM; ! 318: break; ! 319: ! 320: case 'r': ! 321: *flagp |= KREV; ! 322: break; ! 323: ! 324: default: ! 325: usage(); ! 326: } ! 327: } ! 328: ! 329: /* ! 330: * Read in the skip (either the `+' or ! 331: * the `-' kind). Syntax check it and ! 332: * store it away. ! 333: */ ! 334: readskip(s) ! 335: register char *s; ! 336: { ! 337: register int n; ! 338: register int plusskip = 0; ! 339: ! 340: plusskip = *s++ == '+'; ! 341: if (plusskip) { ! 342: if (++posp >= &pos[NPOS]) ! 343: serr("Too many positional parameters"); ! 344: posp->p_em = MAXINT; ! 345: } ! 346: for (n=0; isdigit(*s); ) ! 347: n = n*10 + *s++ - '0'; ! 348: if (plusskip) ! 349: posp->p_sm = n; else ! 350: posp->p_em = n; ! 351: if (*s == '.') { ! 352: s++; ! 353: for (n=0; isdigit(*s); ) ! 354: n = n*10 + *s++ - '0'; ! 355: if (plusskip) ! 356: posp->p_sn = n; else ! 357: posp->p_en = n; ! 358: } ! 359: opts(s, plusskip ? &posp->p_sflags : &posp->p_eflags); ! 360: } ! 361: ! 362: /* ! 363: * Actually figure out what kind of ! 364: * sorting we have to do. ! 365: */ ! 366: sort() ! 367: { ! 368: register FILE *fp; ! 369: ! 370: /* ! 371: * Optimisation for simple sorts ! 372: * to cut compare time down. ! 373: */ ! 374: if (posp<&pos[0] && (kflags&~KREV)==0) { ! 375: if (kflags & KREV) ! 376: compar = rstrcmp; else ! 377: compar = strcmp; ! 378: } ! 379: if (cflag) { ! 380: register char *b1, *b2; ! 381: ! 382: if (mflag || outname!=NULL || uflag) ! 383: fprintf(stderr, "Checking only--some options ignored\n"); ! 384: b1 = NULL; ! 385: b2 = inbuf[0]; ! 386: while (sgets(b2) != NULL) { ! 387: if (b1 == NULL) { ! 388: b2 = inbuf[1]; ! 389: b1 = inbuf[0]; ! 390: continue; ! 391: } ! 392: if ((*compar)(b1, b2) > 0) { ! 393: fprintf(stderr, "sort: out of order at:\n"); ! 394: fprintf(stderr, "%s", b2); ! 395: return (1); ! 396: } ! 397: if (b2 == inbuf[1]) { ! 398: b1 = inbuf[1]; ! 399: b2 = inbuf[0]; ! 400: } else { ! 401: b1 = inbuf[0]; ! 402: b2 = inbuf[1]; ! 403: } ! 404: } ! 405: return (0); ! 406: } ! 407: if (mflag) { ! 408: if (copyruns()) ! 409: return (1); ! 410: } else { ! 411: if (selection()) ! 412: return (1); ! 413: } ! 414: dummyruns(); ! 415: if (merge()) ! 416: return (1); ! 417: if (outname != NULL) { ! 418: if (freopen(outname, "w", stdout) != stdout) ! 419: serr("Cannot open output `%s'", outname); ! 420: } ! 421: fp = tfiles[MORDER].tf_fp; ! 422: rewind(fp); ! 423: LASTREC = NULL; ! 424: while (fgets(inbuf[0], NREC, fp) != NULL) { ! 425: if (uflag) { ! 426: if (LASTREC == NULL) ! 427: LASTREC = inbuf[1]; ! 428: else if ((*compar)(LASTREC, inbuf[0]) == 0) ! 429: continue; ! 430: strcpy(LASTREC, inbuf[0]); ! 431: } ! 432: fputs(inbuf[0], stdout); ! 433: } ! 434: fflush(stdout); ! 435: if (ferror(stdout)) ! 436: serr("Write error on `%s'", outname==NULL ? "(stdout)":outname); ! 437: fclose(fp); ! 438: return (0); ! 439: } ! 440: ! 441: /* ! 442: * Copy the runs into the temp-files ! 443: * for already-sorted but not merged data. ! 444: */ ! 445: copyruns() ! 446: { ! 447: register char **flp = flist; ! 448: register char *fn; ! 449: register FILE *fp; ! 450: register int s = 0; ! 451: ! 452: while ((fn = *flp++) != NULL) { ! 453: if (fn[0]=='-' && fn[1]=='\0') ! 454: fp = stdin; ! 455: else if ((fp = fopen(fn, "r")) == NULL) { ! 456: fprintf(stderr, "sort: cannot open `%s'\n", fn); ! 457: s = 1; ! 458: continue; ! 459: } ! 460: setbuf(fp, ibuf); ! 461: if (copyfile(fp, nextrun()) == NULL) ! 462: return (1); ! 463: if (fp != stdin) ! 464: fclose(fp); ! 465: } ! 466: return (s); ! 467: } ! 468: ! 469: /* ! 470: * Calculate the next run number to use, ! 471: * based on the number that we already have. ! 472: * The dummy runs go to the left (largest ! 473: * number so they are used the most often) ! 474: * Dummy runs are installed by another routine ! 475: * after all runs are entered. ! 476: */ ! 477: nextrun() ! 478: { ! 479: register struct dists *dsp; ! 480: register int i; ! 481: ! 482: for (dsp = dists; dsp < &dists[NDIST]; dsp++) { ! 483: for (i=MORDER-1; i>=0; i--) { ! 484: if (tfiles[i].tf_nrun < dsp->d_runs[i]) { ! 485: savedsp = dsp; ! 486: return (i); ! 487: } ! 488: } ! 489: } ! 490: serr("Ridiculously many runs"); ! 491: } ! 492: ! 493: /* ! 494: * Fill out the current distribution level ! 495: * with dummy runs. ! 496: */ ! 497: dummyruns() ! 498: { ! 499: register struct dists *dsp; ! 500: register TFILE *tfp; ! 501: register int i; ! 502: ! 503: dsp = savedsp; ! 504: for (i=0; i<MORDER; i++) ! 505: for (tfp = &tfiles[i]; tfp->tf_nrun < dsp->d_runs[i]; ) ! 506: tfp->tf_nrun++; ! 507: } ! 508: ! 509: /* ! 510: * Copy each run file to the appropriate temp file ! 511: * given by the run number (`runno'). ! 512: * Also, check during the input for the file's ! 513: * being sorted properly. ! 514: * If `ifp' is NULL, this creates an empty ! 515: * (distinguished from dummy) run. ! 516: */ ! 517: RUN * ! 518: copyfile(ifp, runno) ! 519: FILE *ifp; ! 520: int runno; ! 521: { ! 522: register RUN *arp, *rp; ! 523: register int c; ! 524: register TFILE *tfp; ! 525: register FILE *ofp; ! 526: ! 527: tfp = &tfiles[runno]; ! 528: tfp->tf_nrun++; ! 529: if ((ofp = tfp->tf_fp) == NULL) { ! 530: maketemp(runno); ! 531: ofp = tfp->tf_fp; ! 532: } ! 533: arp = ralloc(); ! 534: arp->r_next = NULL; ! 535: arp->r_length = 0; ! 536: if (tfp->tf_runs == NULL) ! 537: tfp->tf_runs = arp; ! 538: else { ! 539: for (rp = tfp->tf_runs; rp->r_next != NULL; rp = rp->r_next) ! 540: ; ! 541: rp->r_next = arp; ! 542: } ! 543: if (ifp == NULL) ! 544: return (arp); ! 545: while ((c = getc(ifp)) != EOF) { ! 546: if (c == '\n') ! 547: arp->r_length++; ! 548: putc(c, ofp); ! 549: } ! 550: fflush(ofp); ! 551: if (ferror(ofp)) ! 552: serr(tmpwerr); ! 553: return (arp); ! 554: } ! 555: ! 556: /* ! 557: * Use selection to create the initial runs. ! 558: */ ! 559: selection() ! 560: { ! 561: register TFILE *tfp; ! 562: register RUN *rp; ! 563: register int i; ! 564: register int nsel; ! 565: ! 566: for (;;) { ! 567: for (i=0; i<NSEL; i++) ! 568: sel[i].s_inb = inbuf[i]; ! 569: LASTREC = NULL; ! 570: rp = copyfile(NULL, i = nextrun()); ! 571: tfp = &tfiles[i]; ! 572: for (nsel = 0; sgets(inbuf[nsel])!=NULL; ) { ! 573: insert(nsel); ! 574: if (++nsel >= NSEL) ! 575: break; ! 576: } ! 577: for (i=0; i<nsel; i++) { ! 578: if (uflag) { ! 579: if (LASTREC != NULL) ! 580: if ((*compar)(LASTREC, sel[i].s_inb)==0) ! 581: continue; ! 582: LASTREC = sel[i].s_inb; ! 583: } ! 584: fputs(sel[i].s_inb, tfp->tf_fp); ! 585: rp->r_length++; ! 586: } ! 587: if (nsel < NSEL) ! 588: break; ! 589: } ! 590: return (0); ! 591: } ! 592: ! 593: /* ! 594: * Insert the item at position `n' ! 595: * into the sel table in sorted order. ! 596: */ ! 597: insert(n) ! 598: int n; ! 599: { ! 600: register SEL *sp1, *sp2; ! 601: register char *tmp; ! 602: ! 603: sp2 = &sel[n]; ! 604: for (sp1 = &sel[0]; sp1 < sp2; sp1++) ! 605: if ((*compar)(sp1->s_inb, sp2->s_inb) > 0) { ! 606: tmp = sp2->s_inb; ! 607: for (; sp2 > sp1; sp2--) ! 608: sp2->s_inb = (sp2-1)->s_inb; ! 609: sp1->s_inb = tmp; ! 610: break; ! 611: } ! 612: } ! 613: ! 614: /* ! 615: * Merge the data ! 616: * The algorithm is polyphase merge from ! 617: * Knuth. ! 618: */ ! 619: merge() ! 620: { ! 621: register int i, j; ! 622: register int nr; ! 623: TFILE temptf; ! 624: ! 625: nr = 0; ! 626: j = 0; ! 627: for (i=0; i<MORDER; i++) ! 628: if (tfiles[i].tf_nrun) { ! 629: j = i; ! 630: nr += tfiles[i].tf_nrun; ! 631: } ! 632: if (nr <= 1) { ! 633: tfiles[NTFILE-1] = tfiles[j]; ! 634: return (0); ! 635: } ! 636: for (i=0; i<NTFILE; i++) ! 637: if (tfiles[i].tf_fp == NULL) ! 638: maketemp(i); ! 639: for (;;) { ! 640: mergestep(); ! 641: nr = 0; ! 642: for (i=0; i<MORDER; i++) { ! 643: nr += tfiles[i].tf_nrun; ! 644: if (tfiles[i].tf_nrun == 0) ! 645: j = i; ! 646: } ! 647: if (nr <= 1) ! 648: break; ! 649: /* ! 650: * Exchange output and ! 651: * zeroed one so output ! 652: * is always in a fixed place. ! 653: */ ! 654: temptf = tfiles[MORDER]; ! 655: tfiles[MORDER] = tfiles[j]; ! 656: tfiles[j] = temptf; ! 657: } ! 658: return (0); ! 659: } ! 660: ! 661: /* ! 662: * Do one step of the polyphase merge. The calling ! 663: * routine has arranged that the output is ! 664: * position MORDER and the inputs are the first ! 665: * MORDER positions. ! 666: */ ! 667: mergestep() ! 668: { ! 669: register int min; ! 670: register int i; ! 671: register RUN *rp; ! 672: register FILE *ofp; ! 673: register int len; ! 674: register RUN *orp; ! 675: ! 676: rewind(tfiles[MORDER].tf_fp); ! 677: tfiles[MORDER].tf_start = 0; ! 678: min = tfiles[0].tf_nrun; ! 679: fseek(tfiles[0].tf_fp, tfiles[0].tf_start, 0); ! 680: for (i=1; i<MORDER; i++) { ! 681: fseek(tfiles[i].tf_fp, tfiles[i].tf_start, 0); ! 682: if (tfiles[i].tf_nrun < min) ! 683: min = tfiles[i].tf_nrun; ! 684: } ! 685: while (min-- > 0) { ! 686: orp = copyfile(NULL, MORDER); ! 687: ofp = tfiles[MORDER].tf_fp; ! 688: len = 0; ! 689: for (i=0; i<MORDER; i++) { ! 690: if ((rp = tfiles[i].tf_runs) != NULL) { ! 691: tfiles[i].tf_runs = rp->r_next; ! 692: len += rp->r_length; ! 693: sel[i].s_length = rp->r_length; ! 694: rfree(rp); ! 695: } else ! 696: sel[i].s_length = 0; ! 697: tfiles[i].tf_nrun--; ! 698: sel[i].s_inb = inbuf[i]; ! 699: sel[i].s_fp = tfiles[i].tf_fp; ! 700: } ! 701: orp->r_length = len; ! 702: mergeread(); ! 703: } ! 704: for (i=0; i<MORDER; i++) ! 705: tfiles[i].tf_start = ftell(tfiles[i].tf_fp); ! 706: } ! 707: ! 708: /* ! 709: * Do the 5-way (MORDER) merge on one set ! 710: * of runs which are described in the `sel' ! 711: * struct array. ! 712: */ ! 713: mergeread() ! 714: { ! 715: register SEL *sp; ! 716: register SEL *minp; ! 717: register int neof = 0; ! 718: ! 719: for (sp = sel; sp < &sel[MORDER]; sp++) { ! 720: if (sp->s_length == 0) { ! 721: neof++; ! 722: sp->s_inb = NULL; ! 723: } else { ! 724: fgets(sp->s_inb, NREC, sp->s_fp); ! 725: sp->s_length--; ! 726: } ! 727: } ! 728: while (neof < MORDER) { ! 729: minp = NULL; ! 730: for (sp = sel; sp < &sel[MORDER]; sp++) { ! 731: if (sp->s_inb == NULL) ! 732: continue; ! 733: if (minp == NULL) { ! 734: minp = sp; ! 735: continue; ! 736: } ! 737: if ((*compar)(sp->s_inb, minp->s_inb) <= 0) ! 738: minp = sp; ! 739: } ! 740: fputs(minp->s_inb, tfiles[NTFILE-1].tf_fp); ! 741: if (minp->s_length-- == 0) { ! 742: minp->s_inb = NULL; ! 743: neof++; ! 744: } else ! 745: fgets(minp->s_inb, NREC, minp->s_fp); ! 746: } ! 747: } ! 748: ! 749: /* ! 750: * Get the next input character. This ! 751: * automatically goes from one file to the ! 752: * next on EOF and only returns EOF at real ! 753: * end of file. ! 754: */ ! 755: sgetc() ! 756: { ! 757: static FILE *fp; ! 758: register int c; ! 759: ! 760: again: ! 761: if (fp == NULL) ! 762: if (*flist == NULL) ! 763: return (EOF); ! 764: else { ! 765: if ((*flist)[0]=='-' && (*flist)[1]=='\0') ! 766: fp = stdin; ! 767: else if ((fp = fopen(*flist, "r")) == NULL) ! 768: fprintf(stderr, "sort: cannot open %s\n", *flist); ! 769: flist++; ! 770: setbuf(fp, ibuf); ! 771: goto again; ! 772: } ! 773: if ((c = getc(fp)) == EOF) { ! 774: if (fp != stdin) ! 775: fclose(fp); ! 776: fp = NULL; ! 777: goto again; ! 778: } ! 779: return (c); ! 780: } ! 781: ! 782: /* ! 783: * Get a string from sort input. NULL on EOF, leave ! 784: * trailing newlines on. ! 785: */ ! 786: char * ! 787: sgets(as) ! 788: char *as; ! 789: { ! 790: register unsigned max = NREC; ! 791: register int c; ! 792: register char *s; ! 793: ! 794: s = as; ! 795: while (--max>0 && (c = sgetc()) != EOF) ! 796: if ((*s++ = c) == '\n') { ! 797: inline += 1; ! 798: break; ! 799: } ! 800: if (max == 0) ! 801: serr("input record #%ld exceeds maximum length %d", ! 802: inline + 1, NREC); ! 803: *s = '\0'; ! 804: return (c==EOF && s==as ? NULL : as); ! 805: } ! 806: ! 807: /* ! 808: * Compare keys in strings `s1' and `s2' ! 809: * taking into account all of the key selection ! 810: * options and positional fields. ! 811: * All comparison routines return -1 or <, 0 for equal, ! 812: * and 1 for >. ! 813: */ ! 814: kcompar(s1, s2) ! 815: char *s1; ! 816: char *s2; ! 817: { ! 818: char *fskip(); ! 819: register POS *pp; ! 820: register char *ep1, *ep2; ! 821: register int ret = 0; ! 822: register char *p1, *p2; ! 823: ! 824: if (posp < &pos[0]) { ! 825: for (ep1=s1; *ep1++ != '\0'; ) ! 826: ; ! 827: ep1--; ! 828: for (ep2 = s2; *ep2++ != '\0'; ) ! 829: ; ! 830: ep2--; ! 831: return (fcompar(s1, ep1, s2, ep2, kflags)); ! 832: } ! 833: for (pp = &pos[0]; pp <= posp; pp++) { ! 834: register int sflags, eflags; ! 835: ! 836: if ((sflags = pp->p_sflags) == 0) ! 837: sflags = kflags; ! 838: if ((eflags = pp->p_eflags) == 0) ! 839: eflags = kflags; ! 840: p1 = fskip(s1, pp->p_sm, pp->p_sn, sflags); ! 841: p2 = fskip(s2, pp->p_sm, pp->p_sn, sflags); ! 842: ep1 = fskip(s1, pp->p_em, pp->p_en, eflags); ! 843: ep2 = fskip(s2, pp->p_em, pp->p_en, eflags); ! 844: ret = fcompar(p1, ep1, p2, ep2, sflags|eflags); ! 845: if (ret) ! 846: break; ! 847: } ! 848: return (ret); ! 849: } ! 850: ! 851: /* ! 852: * Skip fields and space, returning the new pointer. ! 853: * Arguments are `s' for string start, `m' and `n' from ! 854: * the positional `m.n' format. ! 855: * `f' is the flags - only `b' is ! 856: * significant here. ! 857: */ ! 858: char * ! 859: fskip(s, m, n, f) ! 860: register char *s; ! 861: register int m; ! 862: register int n; ! 863: register int f; ! 864: { ! 865: while (m--) { ! 866: if (tabc) { ! 867: while (*s!=tabc && *s!='\0') ! 868: s++; ! 869: if (*s != '\0') ! 870: s++; ! 871: else ! 872: break; ! 873: } else { ! 874: while (*s==' ' || *s=='\t') ! 875: s++; ! 876: while (*s!=' ' && *s!='\t' && *s!='\0') ! 877: s++; ! 878: if (*s == '\0') ! 879: break; ! 880: if (m == 0) ! 881: while (*s==' ' || *s=='\t') ! 882: s++; ! 883: } ! 884: } ! 885: if (f & KBLANK) ! 886: while (*s==' ' || *s=='\t') ! 887: s++; ! 888: while (n--) { ! 889: if (*s == '\0') ! 890: break; ! 891: s++; ! 892: } ! 893: return (s); ! 894: } ! 895: ! 896: /* ! 897: * Compare for the finally found field. ! 898: * This takes into account all of the options ! 899: * and the end and start of each string. ! 900: */ ! 901: fcompar(s1, e1, s2, e2, flags) ! 902: char *s1, *e1; ! 903: char *s2, *e2; ! 904: int flags; ! 905: { ! 906: register char *p1, *p2; ! 907: register int ret = 0; ! 908: ! 909: p1 = s1; ! 910: p2 = s2; ! 911: if (flags & (KBLANK|KNUM)) { ! 912: while (*p1==' ' || *p1=='\t') ! 913: p1++; ! 914: while (*p2==' ' || *p2=='\t') ! 915: p2++; ! 916: } ! 917: if (flags & KNUM) { ! 918: NUM n1, n2; ! 919: ! 920: numpars(p1, e1, &n1); ! 921: numpars(p2, e2, &n2); ! 922: /* Compare integer magnitudes and signs */ ! 923: ret = n1.n_sign*n1.n_magn - n2.n_sign*n2.n_magn; ! 924: if (ret != 0) ! 925: ret = (ret < 0) ? -1 : 1; ! 926: else { ! 927: /* Compare integer parts */ ! 928: p1 = n1.n_bgn; ! 929: p2 = n2.n_bgn; ! 930: while (--n1.n_magn >= 0 && ret == 0) ! 931: if (*p1 > *p2) ! 932: ret = n1.n_sign; ! 933: else if (*p1 < *p2) ! 934: ret = - n1.n_sign; ! 935: else { ! 936: p1 += 1; ! 937: p2 += 1; ! 938: } ! 939: } ! 940: if (ret == 0) ! 941: /* Compare fractional magnitudes and signs */ ! 942: ret = n1.n_sign*n1.n_fmagn - n2.n_sign*n2.n_fmagn; ! 943: if (ret != 0) ! 944: ret = ret < 0 ? -1 : 1; ! 945: else { ! 946: /* Compare fractional parts */ ! 947: e1 = n1.n_end; ! 948: e2 = n2.n_end; ! 949: while (p1 < e1 && p2 < e2 && ret == 0) ! 950: if (*p1 > *p2) ! 951: ret = n1.n_sign; ! 952: else if (*p1 < *p2) ! 953: ret = -n1.n_sign; ! 954: else { ! 955: p1 += 1; ! 956: p2 += 1; ! 957: } ! 958: } ! 959: if (ret == 0) { ! 960: if (p1 < e1) ! 961: ret = n1.n_sign; ! 962: else ! 963: ret = - n1.n_sign; ! 964: } ! 965: } else { ! 966: register int c1, c2; ! 967: ! 968: if (flags & (KDICT|KIGNORE)) { ! 969: for (;;) { ! 970: for (; p1<e1; p1++) { ! 971: if (flags&KIGNORE && !isprint(*p1)) ! 972: continue; ! 973: if (flags & KDICT) ! 974: if (!(isspace(*p1) ! 975: || isalnum(*p1))) ! 976: continue; ! 977: if (flags & KFOLD) ! 978: c1 = tabfold[*p1++]; else ! 979: c1 = *p1++; ! 980: break; ! 981: } ! 982: for (; p2<e2; p2++) { ! 983: if (flags&KIGNORE && !isprint(*p2)) ! 984: continue; ! 985: if (flags & KDICT) ! 986: if (!(isalnum(*p2) ! 987: || isspace(*p2))) ! 988: continue; ! 989: if (flags & KFOLD) ! 990: c2 = tabfold[*p2++]; else ! 991: c2 = *p2++; ! 992: break; ! 993: } ! 994: if (p1>=e1 || p2>=e2) ! 995: break; ! 996: if (c1 != c2) ! 997: break; ! 998: } ! 999: } else if (flags & KFOLD) { ! 1000: for (;;) { ! 1001: c1 = tabfold[*p1++]; ! 1002: c2 = tabfold[*p2++]; ! 1003: if (p1>=e1 || p2>=e2) ! 1004: break; ! 1005: if (c1 != c2) ! 1006: break; ! 1007: } ! 1008: } else { ! 1009: for (;;) { ! 1010: c1 = *p1++; ! 1011: c2 = *p2++; ! 1012: if (p1>=e1 || p2>=e2) ! 1013: break; ! 1014: if (c1 != c2) ! 1015: break; ! 1016: } ! 1017: } ! 1018: if (p1<=e1 && p2<=e2) { ! 1019: if (c1 < c2) ! 1020: ret--; ! 1021: else if (c1 > c2) ! 1022: ret++; ! 1023: } else if (p1 > e1) { ! 1024: ret++; ! 1025: } else ! 1026: ret --; ! 1027: } ! 1028: if (flags & KREV) ! 1029: return (-ret); ! 1030: return (ret); ! 1031: } ! 1032: ! 1033: /* ! 1034: * Parse a numeric field into sign, magnitude, integer and fractional parts. ! 1035: */ ! 1036: numpars(p, ep, np) ! 1037: register char *p; ! 1038: register char *ep; ! 1039: register NUM *np; ! 1040: { ! 1041: char *bp; ! 1042: char *fbgn; ! 1043: int fsum; ! 1044: ! 1045: bp = p; ! 1046: fbgn = NULL; ! 1047: fsum = 0; ! 1048: np->n_sign = 1; ! 1049: np->n_magn = 0; ! 1050: np->n_fmagn = 16000; ! 1051: np->n_bgn = NULL; ! 1052: for ( ; p < ep; p += 1) ! 1053: if (*p == '0') { ! 1054: if (fbgn != NULL) { ! 1055: if (fsum == 0) ! 1056: np->n_fmagn -= 1; ! 1057: } else if (np->n_magn != 0) ! 1058: np->n_magn += 1; ! 1059: } else if (isdigit(*p)) { ! 1060: if (fbgn != NULL) ! 1061: fsum += 1; ! 1062: else if (np->n_magn++ == 0) ! 1063: np->n_bgn = p; ! 1064: } else if (*p == '-') { ! 1065: if (p != bp) ! 1066: break; ! 1067: np->n_sign = -1; ! 1068: } else if (*p == '.') { ! 1069: if (fbgn != NULL || p+1 == ep) ! 1070: break; ! 1071: fbgn = p+1; ! 1072: } else { ! 1073: break; ! 1074: } ! 1075: np->n_end = p; ! 1076: if (fsum == 0) { ! 1077: np->n_fmagn = 0; ! 1078: fbgn = NULL; ! 1079: } ! 1080: if (np->n_bgn == NULL) ! 1081: np->n_bgn = fbgn; ! 1082: if (np->n_bgn == NULL) ! 1083: np->n_bgn = p; ! 1084: } ! 1085: ! 1086: ! 1087: /* ! 1088: * Reversed version of strcmp. If this ! 1089: * were more common, it could be written ! 1090: * out in full to save the extra routine call. ! 1091: */ ! 1092: rstrcmp(s1, s2) ! 1093: char *s1, *s2; ! 1094: { ! 1095: return (-strcmp(s1, s2)); ! 1096: } ! 1097: ! 1098: /* ! 1099: * Make temporary file `n'. ! 1100: * Called as they are first needed. ! 1101: */ ! 1102: maketemp(n) ! 1103: { ! 1104: #ifdef COHERENT ! 1105: static int first = 1; ! 1106: char tempname[120]; ! 1107: register FILE *fp; ! 1108: ! 1109: if (pid == 0) { ! 1110: pid = getpid(); ! 1111: sprintf(template, "%s/sort%d%%c", ! 1112: tempdir==NULL ? "/tmp" : tempdir, pid); ! 1113: } ! 1114: sprintf(tempname, template, n+'a'); ! 1115: if ((fp = fopen(tempname, "w")) == NULL) { ! 1116: if (first && tempdir==NULL) { ! 1117: sprintf(template, "/usr/tmp/sort%d%%c", pid); ! 1118: sprintf(tempname, template, n+'a'); ! 1119: if ((fp = fopen(tempname, "w")) == NULL) ! 1120: serr(temperr); ! 1121: } else ! 1122: serr(temperr); ! 1123: } ! 1124: fclose(fp); ! 1125: if ((tfiles[n].tf_fp = fopen(tempname, "r+w")) == NULL) ! 1126: serr(temperr); ! 1127: setbuf(tfiles[n].tf_fp, alloc(BUFSIZ)); ! 1128: first = 0; ! 1129: #else ! 1130: char tempname[120]; ! 1131: ! 1132: sprintf(tempname, "%ssortwrk%c", ! 1133: tempdir==NULL ? "" : tempdir, n+'a'); ! 1134: if((tfiles[n].tf_fp=fopen(tempname, "wr"))==NULL) ! 1135: serr(temperr); ! 1136: setbuf(tfiles[n].tf_fp, alloc(BUFSIZ)); ! 1137: #endif ! 1138: } ! 1139: ! 1140: /* Errors and usage messages */ ! 1141: usage() ! 1142: { ! 1143: fprintf(stderr, "Usage: sort [options] [+pos1 [-pos2]] ... [file ...]\n"); ! 1144: fprintf(stderr, "Options: [-mubdfinr] [-tx] [-T directory] [-o name]\n"); ! 1145: exit(1); ! 1146: } ! 1147: ! 1148: /* VARARGS */ ! 1149: serr(x) ! 1150: { ! 1151: fprintf(stderr, "sort: %r\n", &x); ! 1152: rmexit(1); ! 1153: } ! 1154: ! 1155: /* ! 1156: * Exit, removing the tempfiles. ! 1157: */ ! 1158: rmexit(s) ! 1159: int s; ! 1160: { ! 1161: register int c; ! 1162: char tempname[120]; ! 1163: ! 1164: for (c='a'; c<'a'+NTFILE; c++) { ! 1165: #ifdef COHERENT ! 1166: sprintf(tempname, template, c); ! 1167: #else ! 1168: sprintf(tempname, "%ssortwrk%c", ! 1169: tempdir==NULL ? "" : tempdir, c); ! 1170: #endif ! 1171: unlink(tempname); ! 1172: } ! 1173: #ifdef COHERENT ! 1174: _exit(s); ! 1175: #else ! 1176: exit(s); ! 1177: #endif ! 1178: } ! 1179: ! 1180: #if COHERENT ! 1181: /* ! 1182: * Protect from the specified signal number. ! 1183: * This makes that signal call the cleanup ! 1184: * routine, unless that signal was already ignored. ! 1185: */ ! 1186: protect(signo) ! 1187: register int signo; ! 1188: { ! 1189: if (signal(signo, SIG_IGN) != SIG_IGN) ! 1190: signal(signo, rmexit); ! 1191: } ! 1192: #endif ! 1193: ! 1194: /* ! 1195: * Allocate a new run. Uses our own threaded free list ! 1196: * and sbrk-style alloc when none of these. ! 1197: */ ! 1198: RUN * ! 1199: ralloc() ! 1200: { ! 1201: register RUN *p; ! 1202: ! 1203: if ((p = frlist) != NULL) { ! 1204: frlist = p->r_next; ! 1205: return (p); ! 1206: } ! 1207: return ((RUN *)alloc(sizeof (RUN))); ! 1208: } ! 1209: ! 1210: /* ! 1211: * This allocator simply does sbrk calls so that ! 1212: * it can grow (easily) the space as it needs without ! 1213: * threading it. It also checks and prints an error ! 1214: * if out of space. ! 1215: */ ! 1216: char * ! 1217: alloc(nb) ! 1218: register unsigned nb; ! 1219: { ! 1220: static int *rp, *ep; ! 1221: register int *cp; ! 1222: ! 1223: if ((nb = (nb+sizeof(int)-1)/sizeof(int)) == 0) ! 1224: serr(nomem); ! 1225: if (rp==NULL || rp+nb>=ep) { ! 1226: if ((ep = (int *)sbrk(NSBRK)) == BADSBRK) ! 1227: serr(nomem); ! 1228: if (rp == NULL) ! 1229: rp = ep; ! 1230: ep += NSBRK/sizeof(int); ! 1231: } ! 1232: cp = rp; ! 1233: rp += nb; ! 1234: return (cp); ! 1235: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.