|
|
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.