|
|
DataMuseum.dkPresents historical artifacts from the history of: Commodore CBM-900 |
This is an automatic "excavation" of a thematic subset of
See our Wiki for more about Commodore CBM-900 Excavated with: AutoArchaeologist - Free & Open Source Software. |
top - metrics - download
Length: 23481 (0x5bb9)
Types: TextFile
Notes: UNIX file
Names: »sort.c«
└─⟦f27320a65⟧ Bits:30001972 Commodore 900 hard disk image with partial source code
└─⟦f4b8d8c84⟧ UNIX Filesystem
└─⟦this⟧ »cmd/sort.c«
/*
* The sort command.
* It does unique sorting, merges, and
* ordinary sorts with zillions of
* ways of specifying the sort keys.
*/
#include <stdio.h>
#include <ctype.h>
#include <mdata.h>
#include <signal.h>
#include <types.h>
#define NREC 400 /* Longest key record */
#define NSEL 20 /* Number of records in selection list */
#define NPOS 20 /* Number of positionals */
#define NTFILE 6 /* Number of intermediate files */
#define MORDER (NTFILE-1) /* Order of polyphase merge */
#define NDIST (sizeof(dists)/sizeof(dists[0]))
#define NCSET (MAXUCHAR+1) /* Size of char set */
#define NSBRK 1024 /* Amount to add at a time */
#define rfree(p) {p->r_next=frlist;frlist=p;} /* Free a run */
/* Key ordering flags (global and field skips) */
#define KBLANK 01 /* Ignore leading blanks */
#define KDICT 02 /* Dictionary order (letters, digits, blanks) */
#define KFOLD 04 /* Fold upper case onto lower case */
#define KIGNORE 010 /* Ignore non-ascii characters */
#define KNUM 020 /* Numeric sort - skip leading blanks */
#define KREV 040 /* Reverse order of sort */
/*
* Mapping table to speed up folding.
* Initialised by `sortinit'.
*/
char tabfold[NCSET];
/*
* The temp file structure. One for each file
* contains the list-head for the runs and
* the FILE stream pointer.
*/
typedef struct TFILE {
struct RUN *tf_runs;
FILE *tf_fp;
int tf_nrun;
size_t tf_start;
} TFILE;
TFILE tfiles[NTFILE];
/*
* The structure of each run. contains
* a pointer to the next and the start
* and length of the run.
*/
typedef struct RUN {
struct RUN *r_next;
int r_length; /* Number of records in run */
} RUN;
/*
* Entries in positional (+m.n-m.n) parameters
*/
typedef struct POS {
int p_sflags; /* Start flags */
int p_sm; /* fields */
int p_sn; /* chars */
int p_eflags; /* End flags */
int p_em; /* Ending fields */
int p_en; /* Ending chars */
} POS;
POS pos[NPOS];
POS *posp = &pos[-1];
/*
* Numeric field breakout structure.
*/
typedef struct NUM {
int n_sign; /* Sign */
int n_magn; /* Integer magnitude */
int n_fmagn; /* Fractional magnitude */
char *n_bgn; /* beginning */
char *n_end; /* ending */
} NUM;
char **flist;
char *deflist[] = {
"-", NULL
};
/*
* This is the best distribution of runs for
* the polyphase merge. It is a sort of n-way
* distribution of the Fibonacci number sequence.
* Each level contains a total number of runs
* and a way to subdivide them to this. Dummy
* runs are added to round out the initial distribution.
* Each successive distribution vector (m0,m1,m2,m3,m4)
* is obtained by cross product with this matrix:
* 1 1 1 1 1
* 1 0 0 0 0
* 0 1 0 0 0
* 0 0 1 0 0
* 0 0 0 1 0
* which can be generalised to any order of merge (other
* than 5-way).
* Also no more entries are given as this is
* likely already overkill for sorting during
* the lifetime of most machines.
*/
struct dists {
int d_totruns; /* Total of next 5 elements */
int d_runs[MORDER]; /* initial distribution */
} dists[] = {
1, 1, 0, 0, 0, 0,
5, 1, 1, 1, 1, 1,
9, 2, 2, 2, 2, 1,
17, 4, 4, 4, 3, 2,
33, 8, 8, 7, 6, 4,
65, 16, 15, 14, 12, 8,
129, 31, 30, 28, 24, 16,
253, 61, 59, 55, 47, 31,
497, 120, 116, 108, 92, 61,
977, 236, 228, 212, 181, 120,
1921, 464, 448, 417, 356, 236,
3777, 912, 881, 820, 700, 464,
7425, 1793, 1732, 1612, 1376, 912,
14597, 3525, 3405, 3169, 2705, 1793,
28697, 6930, 6694, 6230, 5318, 3525,
};
struct dists *savedsp; /* Save current distribution level */
char *outname; /* Output other than stdout */
char *tempdir; /* Other than default temp directory */
char template[100];
char obuf[BUFSIZ];
char ibuf[BUFSIZ];
/*
* Structure for each input record
* in natural selection. First
* an insertion sort fills the
* tree and then all records
* are input and output until
* too many are out of order.
*/
typedef struct SEL {
char *s_inb; /* Input buffer pointer */
int s_length; /* Length of run in selection */
FILE *s_fp; /* File pointer of run */
} SEL;
RUN *frlist; /* Free run list */
SEL sel[NSEL];
SEL lastrec;
char inbuf[NSEL+1][NREC];
#define LASTREC lastrec.s_inb /* Buffer pointer of last written record */
char temperr[] = "Temporary file open error";
char tmpwerr[] = "Temporary file write error";
char nomem[] = "Out of memory";
int pid; /* Current process ID */
char tabc; /* Tab character */
int cflag; /* Check ordering only */
int mflag; /* Merge only */
int uflag; /* Unique sort */
int kflags; /* Flags to control order to sort */
char *sgets();
RUN *copyfile();
RUN *ralloc();
char *alloc();
int rmexit();
int kcompar();
int strcmp();
int rstrcmp();
int (*compar)() = kcompar;
char *sprintf();
main(argc, argv)
int argc;
char *argv[];
{
register char *ap;
char dummyop[2];
setbuf(stdin, ibuf);
setbuf(stdout, obuf);
setbuf(stderr, NULL);
protect(SIGINT);
protect(SIGHUP);
protect(SIGPIPE);
protect(SIGTERM);
while (argc>1 && (*argv[1]=='-' || *argv[1]=='+')) {
if (*argv[1] == '+') {
readskip(argv[1]);
argv++;
argc--;
if (argc>1 && argv[1][0]=='-' && isdigit(argv[1][1])) {
readskip(argv[1]);
argv++;
argc--;
}
continue;
}
for (ap = &argv[1][1]; *ap != '\0'; ap++)
switch (*ap) {
/*
* Non-ordering options.
*/
case 'c': /* Check ordering only */
cflag = 1;
break;
case 'm': /* Merge only */
mflag = 1;
break;
case 'o': /* Output other than stdout */
if (outname != NULL)
serr("Only one output name allowed");
if (--argc < 2)
usage();
argv++;
outname = argv[1];
break;
case 'T': /* Alternative temp directory */
if (tempdir != NULL)
serr("Only one `-T' allowed");
if (--argc < 2)
usage();
argv++;
tempdir = argv[1];
break;
case 'u': /* Unique sort */
uflag = 1;
break;
/*
* Lexicographic ordering options.
*/
case 't': /* Tab character */
if ((tabc = *++ap) == '\0')
usage();
break;
default:
dummyop[0] = *ap;
dummyop[1] = '\0';
opts(dummyop, &kflags);
}
argc--;
argv++;
}
if (argc > 1)
flist = argv+1; else
flist = deflist;
sortinit();
rmexit(sort());
}
/*
* Initialise tables that speed up
* special orderings.
*/
sortinit()
{
register int c;
register char *cp;
cp = tabfold;
for (c=0; c<NCSET; c++)
*cp++ = (isascii(c) && isupper(c)) ? tolower(c) : c;
}
/*
* Read in the ordering options into
* the int that is referenced by `flagp'
* from the string `s'. Used both for
* global options and with skip options.
*/
opts(s, flagp)
register char *s;
register int *flagp;
{
*flagp = 0;
while (*s)
switch (*s++) {
case 'b':
*flagp |= KBLANK;
break;
case 'd':
*flagp |= KDICT;
break;
case 'f':
*flagp |= KFOLD;
break;
case 'i':
*flagp |= KIGNORE;
break;
case 'n':
*flagp |= KNUM;
break;
case 'r':
*flagp |= KREV;
break;
default:
usage();
}
}
/*
* Read in the skip (either the `+' or
* the `-' kind). Syntax check it and
* store it away.
*/
readskip(s)
register char *s;
{
register int n;
register int plusskip = 0;
plusskip = *s++ == '+';
if (plusskip) {
if (++posp >= &pos[NPOS])
serr("Too many positional parameters");
posp->p_em = MAXINT;
}
for (n=0; isdigit(*s); )
n = n*10 + *s++ - '0';
if (plusskip)
posp->p_sm = n; else
posp->p_em = n;
if (*s == '.') {
s++;
for (n=0; isdigit(*s); )
n = n*10 + *s++ - '0';
if (plusskip)
posp->p_sn = n; else
posp->p_en = n;
}
opts(s, plusskip ? &posp->p_sflags : &posp->p_eflags);
}
/*
* Actually figure out what kind of
* sorting we have to do.
*/
sort()
{
register FILE *fp;
/*
* Optimisation for simple sorts
* to cut compare time down.
*/
if (posp<&pos[0] && (kflags&~KREV)==0) {
if (kflags & KREV)
compar = rstrcmp; else
compar = strcmp;
}
if (cflag) {
register char *b1, *b2;
if (mflag || outname!=NULL || uflag)
fprintf(stderr, "Checking only--some options ignored\n");
b1 = NULL;
b2 = inbuf[0];
while (sgets(b2) != NULL) {
if (b1 == NULL) {
b2 = inbuf[1];
b1 = inbuf[0];
continue;
}
if ((*compar)(b1, b2) > 0) {
fprintf(stderr, "sort: out of order at:\n");
fprintf(stderr, "%s", b2);
return (1);
}
if (b2 == inbuf[1]) {
b1 = inbuf[1];
b2 = inbuf[0];
} else {
b1 = inbuf[0];
b2 = inbuf[1];
}
}
return (0);
}
if (mflag) {
if (copyruns())
return (1);
} else {
if (selection())
return (1);
}
dummyruns();
if (merge())
return (1);
if (outname != NULL) {
if (freopen(outname, "w", stdout) != stdout)
serr("Cannot open output `%s'", outname);
}
fp = tfiles[MORDER].tf_fp;
rewind(fp);
LASTREC = NULL;
while (fgets(inbuf[0], NREC, fp) != NULL) {
if (uflag) {
if (LASTREC == NULL)
LASTREC = inbuf[1];
else if ((*compar)(LASTREC, inbuf[0]) == 0)
continue;
strcpy(LASTREC, inbuf[0]);
}
fputs(inbuf[0], stdout);
}
fflush(stdout);
if (ferror(stdout))
serr("Write error on `%s'", outname==NULL ? "(stdout)":outname);
fclose(fp);
return (0);
}
/*
* Copy the runs into the temp-files
* for already-sorted but not merged data.
*/
copyruns()
{
register char **flp = flist;
register char *fn;
register FILE *fp;
register int s = 0;
while ((fn = *flp++) != NULL) {
if (fn[0]=='-' && fn[1]=='\0')
fp = stdin;
else if ((fp = fopen(fn, "r")) == NULL) {
fprintf(stderr, "sort: cannot open `%s'\n", fn);
s = 1;
continue;
}
setbuf(fp, ibuf);
if (copyfile(fp, nextrun()) == NULL)
return (1);
if (fp != stdin)
fclose(fp);
}
return (s);
}
/*
* Calculate the next run number to use,
* based on the number that we already have.
* The dummy runs go to the left (largest
* number so they are used the most often)
* Dummy runs are installed by another routine
* after all runs are entered.
*/
nextrun()
{
register struct dists *dsp;
register int i;
for (dsp = dists; dsp < &dists[NDIST]; dsp++) {
for (i=MORDER-1; i>=0; i--) {
if (tfiles[i].tf_nrun < dsp->d_runs[i]) {
savedsp = dsp;
return (i);
}
}
}
serr("Ridiculously many runs");
}
/*
* Fill out the current distribution level
* with dummy runs.
*/
dummyruns()
{
register struct dists *dsp;
register TFILE *tfp;
register int i;
dsp = savedsp;
for (i=0; i<MORDER; i++)
for (tfp = &tfiles[i]; tfp->tf_nrun < dsp->d_runs[i]; )
tfp->tf_nrun++;
}
/*
* Copy each run file to the appropriate temp file
* given by the run number (`runno').
* Also, check during the input for the file's
* being sorted properly.
* If `ifp' is NULL, this creates an empty
* (distinguished from dummy) run.
*/
RUN *
copyfile(ifp, runno)
FILE *ifp;
int runno;
{
register RUN *arp, *rp;
register int c;
register TFILE *tfp;
register FILE *ofp;
tfp = &tfiles[runno];
tfp->tf_nrun++;
if ((ofp = tfp->tf_fp) == NULL) {
maketemp(runno);
ofp = tfp->tf_fp;
}
arp = ralloc();
arp->r_next = NULL;
arp->r_length = 0;
if (tfp->tf_runs == NULL)
tfp->tf_runs = arp;
else {
for (rp = tfp->tf_runs; rp->r_next != NULL; rp = rp->r_next)
;
rp->r_next = arp;
}
if (ifp == NULL)
return (arp);
while ((c = getc(ifp)) != EOF) {
if (c == '\n')
arp->r_length++;
putc(c, ofp);
}
fflush(ofp);
if (ferror(ofp))
serr(tmpwerr);
return (arp);
}
/*
* Use selection to create the initial runs.
*/
selection()
{
register TFILE *tfp;
register RUN *rp;
register int i;
register int nsel;
for (;;) {
for (i=0; i<NSEL; i++)
sel[i].s_inb = inbuf[i];
LASTREC = NULL;
rp = copyfile(NULL, i = nextrun());
tfp = &tfiles[i];
for (nsel = 0; sgets(inbuf[nsel])!=NULL; ) {
insert(nsel);
if (++nsel >= NSEL)
break;
}
for (i=0; i<nsel; i++) {
if (uflag) {
if (LASTREC != NULL)
if ((*compar)(LASTREC, sel[i].s_inb)==0)
continue;
LASTREC = sel[i].s_inb;
}
fputs(sel[i].s_inb, tfp->tf_fp);
rp->r_length++;
}
if (nsel < NSEL)
break;
}
return (0);
}
/*
* Insert the item at position `n'
* into the sel table in sorted order.
*/
insert(n)
int n;
{
register SEL *sp1, *sp2;
register char *tmp;
sp2 = &sel[n];
for (sp1 = &sel[0]; sp1 < sp2; sp1++)
if ((*compar)(sp1->s_inb, sp2->s_inb) > 0) {
tmp = sp2->s_inb;
for (; sp2 > sp1; sp2--)
sp2->s_inb = (sp2-1)->s_inb;
sp1->s_inb = tmp;
break;
}
}
/*
* Merge the data
* The algorithm is polyphase merge from
* Knuth.
*/
merge()
{
register int i, j;
register int nr;
TFILE temptf;
nr = 0;
j = 0;
for (i=0; i<MORDER; i++)
if (tfiles[i].tf_nrun) {
j = i;
nr += tfiles[i].tf_nrun;
}
if (nr <= 1) {
tfiles[NTFILE-1] = tfiles[j];
return (0);
}
for (i=0; i<NTFILE; i++)
if (tfiles[i].tf_fp == NULL)
maketemp(i);
for (;;) {
mergestep();
nr = 0;
for (i=0; i<MORDER; i++) {
nr += tfiles[i].tf_nrun;
if (tfiles[i].tf_nrun == 0)
j = i;
}
if (nr <= 1)
break;
/*
* Exchange output and
* zeroed one so output
* is always in a fixed place.
*/
temptf = tfiles[MORDER];
tfiles[MORDER] = tfiles[j];
tfiles[j] = temptf;
}
return (0);
}
/*
* Do one step of the polyphase merge. The calling
* routine has arranged that the output is
* position MORDER and the inputs are the first
* MORDER positions.
*/
mergestep()
{
register int min;
register int i;
register RUN *rp;
register FILE *ofp;
register int len;
register RUN *orp;
rewind(tfiles[MORDER].tf_fp);
tfiles[MORDER].tf_start = 0;
min = tfiles[0].tf_nrun;
fseek(tfiles[0].tf_fp, tfiles[0].tf_start, 0);
for (i=1; i<MORDER; i++) {
fseek(tfiles[i].tf_fp, tfiles[i].tf_start, 0);
if (tfiles[i].tf_nrun < min)
min = tfiles[i].tf_nrun;
}
while (min-- > 0) {
orp = copyfile(NULL, MORDER);
ofp = tfiles[MORDER].tf_fp;
len = 0;
for (i=0; i<MORDER; i++) {
if ((rp = tfiles[i].tf_runs) != NULL) {
tfiles[i].tf_runs = rp->r_next;
len += rp->r_length;
sel[i].s_length = rp->r_length;
rfree(rp);
} else
sel[i].s_length = 0;
tfiles[i].tf_nrun--;
sel[i].s_inb = inbuf[i];
sel[i].s_fp = tfiles[i].tf_fp;
}
orp->r_length = len;
mergeread();
}
for (i=0; i<MORDER; i++)
tfiles[i].tf_start = ftell(tfiles[i].tf_fp);
}
/*
* Do the 5-way (MORDER) merge on one set
* of runs which are described in the `sel'
* struct array.
*/
mergeread()
{
register SEL *sp;
register SEL *minp;
register int neof = 0;
for (sp = sel; sp < &sel[MORDER]; sp++) {
if (sp->s_length == 0) {
neof++;
sp->s_inb = NULL;
} else {
fgets(sp->s_inb, NREC, sp->s_fp);
sp->s_length--;
}
}
while (neof < MORDER) {
minp = NULL;
for (sp = sel; sp < &sel[MORDER]; sp++) {
if (sp->s_inb == NULL)
continue;
if (minp == NULL) {
minp = sp;
continue;
}
if ((*compar)(sp->s_inb, minp->s_inb) <= 0)
minp = sp;
}
fputs(minp->s_inb, tfiles[NTFILE-1].tf_fp);
if (minp->s_length-- == 0) {
minp->s_inb = NULL;
neof++;
} else
fgets(minp->s_inb, NREC, minp->s_fp);
}
}
/*
* Get the next input character. This
* automatically goes from one file to the
* next on EOF and only returns EOF at real
* end of file.
*/
sgetc()
{
static FILE *fp;
register int c;
again:
if (fp == NULL)
if (*flist == NULL)
return (EOF);
else {
if ((*flist)[0]=='-' && (*flist)[1]=='\0')
fp = stdin;
else if ((fp = fopen(*flist, "r")) == NULL)
fprintf(stderr, "sort: cannot open %s\n", *flist);
flist++;
setbuf(fp, ibuf);
goto again;
}
if ((c = getc(fp)) == EOF) {
if (fp != stdin)
fclose(fp);
fp = NULL;
goto again;
}
return (c);
}
/*
* Get a string from sort input. NULL on EOF, leave
* trailing newlines on.
*/
char *
sgets(as)
char *as;
{
register unsigned max = NREC;
register int c;
register char *s;
s = as;
while (--max>0 && (c = sgetc()) != EOF)
if ((*s++ = c) == '\n')
break;
if (max == 0)
serr("input record too long");
*s = '\0';
return (c==EOF && s==as ? NULL : as);
}
/*
* Compare keys in strings `s1' and `s2'
* taking into account all of the key selection
* options and positional fields.
* All comparison routines return -1 or <, 0 for equal,
* and 1 for >.
*/
kcompar(s1, s2)
char *s1;
char *s2;
{
char *fskip();
register POS *pp;
register char *ep1, *ep2;
register int ret = 0;
register char *p1, *p2;
if (posp < &pos[0]) {
for (ep1=s1; *ep1++ != '\0'; )
;
ep1--;
for (ep2 = s2; *ep2++ != '\0'; )
;
ep2--;
return (fcompar(s1, ep1, s2, ep2, kflags));
}
for (pp = &pos[0]; pp <= posp; pp++) {
register int sflags, eflags;
if ((sflags = pp->p_sflags) == 0)
sflags = kflags;
if ((eflags = pp->p_eflags) == 0)
eflags = kflags;
p1 = fskip(s1, pp->p_sm, pp->p_sn, sflags);
p2 = fskip(s2, pp->p_sm, pp->p_sn, sflags);
ep1 = fskip(s1, pp->p_em, pp->p_en, eflags);
ep2 = fskip(s2, pp->p_em, pp->p_en, eflags);
ret = fcompar(p1, ep1, p2, ep2, sflags|eflags);
if (ret)
break;
}
return (ret);
}
/*
* Skip fields and space, returning the new pointer.
* Arguments are `s' for string start, `m' and `n' from
* the positional `m.n' format.
* `f' is the flags - only `b' is
* significant here.
*/
char *
fskip(s, m, n, f)
register char *s;
register int m;
register int n;
register int f;
{
while (m--) {
if (tabc) {
while (*s!=tabc && *s!='\0')
s++;
if (*s != '\0')
s++;
else
break;
} else {
while (*s==' ' || *s=='\t')
s++;
while (*s!=' ' && *s!='\t' && *s!='\0')
s++;
if (*s == '\0')
break;
if (m == 0)
while (*s==' ' || *s=='\t')
s++;
}
}
if (f & KBLANK)
while (*s==' ' || *s=='\t')
s++;
while (n--) {
if (*s == '\0')
break;
s++;
}
return (s);
}
/*
* Compare for the finally found field.
* This takes into account all of the options
* and the end and start of each string.
*/
fcompar(s1, e1, s2, e2, flags)
char *s1, *e1;
char *s2, *e2;
int flags;
{
register char *p1, *p2;
register int ret = 0;
p1 = s1;
p2 = s2;
if (flags & (KBLANK|KNUM)) {
while (*p1==' ' || *p1=='\t')
p1++;
while (*p2==' ' || *p2=='\t')
p2++;
}
if (flags & KNUM) {
NUM n1, n2;
numpars(p1, e1, &n1);
numpars(p2, e2, &n2);
/* Compare integer magnitudes and signs */
ret = n1.n_sign*n1.n_magn - n2.n_sign*n2.n_magn;
if (ret != 0)
ret = (ret < 0) ? -1 : 1;
else {
/* Compare integer parts */
p1 = n1.n_bgn;
p2 = n2.n_bgn;
while (--n1.n_magn >= 0 && ret == 0)
if (*p1 > *p2)
ret = n1.n_sign;
else if (*p1 < *p2)
ret = - n1.n_sign;
else {
p1 += 1;
p2 += 1;
}
}
if (ret == 0)
/* Compare fractional magnitudes and signs */
ret = n1.n_sign*n1.n_fmagn - n2.n_sign*n2.n_fmagn;
if (ret != 0)
ret = ret < 0 ? -1 : 1;
else {
/* Compare fractional parts */
e1 = n1.n_end;
e2 = n2.n_end;
while (p1 < e1 && p2 < e2 && ret == 0)
if (*p1 > *p2)
ret = n1.n_sign;
else if (*p1 < *p2)
ret = -n1.n_sign;
else {
p1 += 1;
p2 += 1;
}
}
if (ret == 0) {
if (p1 < e1)
ret = n1.n_sign;
else
ret = - n1.n_sign;
}
} else {
register int c1, c2;
if (flags & (KDICT|KIGNORE)) {
for (;;) {
for (; p1<e1; p1++) {
if (flags&KIGNORE && !isprint(*p1))
continue;
if (flags & KDICT)
if (!(isspace(*p1)
|| isalnum(*p1)))
continue;
if (flags & KFOLD)
c1 = tabfold[*p1++]; else
c1 = *p1++;
break;
}
for (; p2<e2; p2++) {
if (flags&KIGNORE && !isprint(*p2))
continue;
if (flags & KDICT)
if (!(isalnum(*p2)
|| isspace(*p2)))
continue;
if (flags & KFOLD)
c2 = tabfold[*p2++]; else
c2 = *p2++;
break;
}
if (p1>=e1 || p2>=e2)
break;
if (c1 != c2)
break;
}
} else if (flags & KFOLD) {
for (;;) {
c1 = tabfold[*p1++];
c2 = tabfold[*p2++];
if (p1>=e1 || p2>=e2)
break;
if (c1 != c2)
break;
}
} else {
for (;;) {
c1 = *p1++;
c2 = *p2++;
if (p1>=e1 || p2>=e2)
break;
if (c1 != c2)
break;
}
}
if (p1<=e1 && p2<=e2) {
if (c1 < c2)
ret--;
else if (c1 > c2)
ret++;
} else if (p1 > e1) {
ret++;
} else
ret --;
}
if (flags & KREV)
return (-ret);
return (ret);
}
/*
* Parse a numeric field into sign, magnitude, integer and fractional parts.
*/
numpars(p, ep, np)
register char *p;
register char *ep;
register NUM *np;
{
char *bp;
char *fbgn;
int fsum;
bp = p;
fbgn = NULL;
fsum = 0;
np->n_sign = 1;
np->n_magn = 0;
np->n_fmagn = 16000;
np->n_bgn = NULL;
for ( ; p < ep; p += 1)
if (*p == '0') {
if (fbgn != NULL) {
if (fsum == 0)
np->n_fmagn -= 1;
} else if (np->n_magn != 0)
np->n_magn += 1;
} else if (isdigit(*p)) {
if (fbgn != NULL)
fsum += 1;
else if (np->n_magn++ == 0)
np->n_bgn = p;
} else if (*p == '-') {
if (p != bp)
break;
np->n_sign = -1;
} else if (*p == '.') {
if (fbgn != NULL || p+1 == ep)
break;
fbgn = p+1;
} else {
break;
}
np->n_end = p;
if (fsum == 0) {
np->n_fmagn = 0;
fbgn = NULL;
}
if (np->n_bgn == NULL)
np->n_bgn = fbgn;
if (np->n_bgn == NULL)
np->n_bgn = p;
}
/*
* Reversed version of strcmp. If this
* were more common, it could be written
* out in full to save the extra routine call.
*/
rstrcmp(s1, s2)
char *s1, *s2;
{
return (-strcmp(s1, s2));
}
/*
* Make temporary file `n'.
* Called as they are first needed.
*/
maketemp(n)
{
static int first = 1;
char tempname[120];
register FILE *fp;
if (pid == 0) {
pid = getpid();
sprintf(template, "%s/sort%d%%c",
tempdir==NULL ? "/tmp" : tempdir, pid);
}
sprintf(tempname, template, n+'a');
if ((fp = fopen(tempname, "w")) == NULL) {
if (first && tempdir==NULL) {
sprintf(template, "/usr/tmp/sort%d%%c", pid);
sprintf(tempname, template, n+'a');
if ((fp = fopen(tempname, "w")) == NULL)
serr(temperr);
} else
serr(temperr);
}
fclose(fp);
if ((tfiles[n].tf_fp = fopen(tempname, "r+w")) == NULL)
serr(temperr);
setbuf(tfiles[n].tf_fp, alloc(BUFSIZ));
first = 0;
}
/* Errors and usage messages */
usage()
{
fprintf(stderr, "Usage: sort [options] [+pos1 [-pos2]] ... [file ...]\n");
fprintf(stderr, "Options: [-mubdfinr] [-tx] [-T directory] [-o name]\n");
exit(1);
}
/* VARARGS */
serr(x)
{
fprintf(stderr, "sort: %r\n", &x);
rmexit(1);
}
/*
* Exit, removing the tempfiles.
*/
rmexit(s)
int s;
{
register int c;
char tempname[120];
for (c='a'; c<'a'+NTFILE; c++)
unlink(sprintf(tempname, template, c));
_exit(s);
}
/*
* Protect from the specified signal number.
* This makes that signal call the cleanup
* routine, unless that signal was already ignored.
*/
protect(signo)
register int signo;
{
if (signal(signo, SIG_IGN) != SIG_IGN)
signal(signo, rmexit);
}
/*
* Allocate a new run. Uses our own threaded free list
* and sbrk-style alloc when none of these.
*/
RUN *
ralloc()
{
register RUN *p;
if ((p = frlist) != NULL) {
frlist = p->r_next;
return (p);
}
return ((RUN *)alloc(sizeof (RUN)));
}
/*
* This allocator simply does sbrk calls so that
* it can grow (easily) the space as it needs without
* threading it. It also checks and prints an error
* if out of space.
*/
char *
alloc(nb)
register unsigned nb;
{
static int *rp, *ep;
register int *cp;
if ((nb = (nb+sizeof(int)-1)/sizeof(int)) == 0)
serr(nomem);
if (rp==NULL || rp+nb>=ep) {
if ((ep = (int *)sbrk(NSBRK)) == NULL)
serr(nomem);
if (rp == NULL)
rp = ep;
ep += NSBRK/sizeof(int);
}
cp = rp;
rp += nb;
return (cp);
}