/****************************************************************************** * * * sort.c version 1.0 of 22 Januari 1989 (C) L.J.M. de Wit 1989 * * * * This software may be used and distributed freely if not used commercially * * and the originator (me) is mentioned in the source (just leave this 9 line * * header intact). * * * ****************************************************************************** * * sort.c: basic sort functions */ #include #include #include "sortmain.h" #include "sortfile.h" #define LINELEN 256 /* Max length of input lines*/ #define MAXNAMLEN 80 /* Max length of filename */ #define MAXMERGE 8 /* Max. no of files to be */ /* simultaneously merged */ static int (*cmp)(); /* Ptr to compare function */ static void sortsub(), /* Recursive quicksort */ merge(); /* Merge sorted files */ static void tempname(); /* Creates temporary name */ static int isort(); /* Initial sort of file(s) */ static char tmpdir[MAXNAMLEN]; /* Name of temp files dir */ static char insave[2][LINELEN]; /* Used by unique */ void settemp(tempdir) /* Sets the temp files dir */ char *tempdir; { strcpy(tmpdir,tempdir); } static void sortsub(sbase,stop) /* Sort the pointers */ char **sbase, **stop; /* between sbase and stop */ { register char **base = sbase, /* All below base and above */ **top = stop, /* top is kept sorted with */ **mid, /* respect to element *mid */ *tmp; /* For swapping values */ register long compfunc = (long)cmp; /* Kludge to use extra reg. */ #define CMP (*(int (*)())compfunc) /* Replaces (*cmp) */ mid = base + ((top - base) >> 1); /* Choose pivot */ --top; if (top - base >= 6) { /* Make sure pivot mid value*/ if (CMP(*base,*mid) > 0) { if (CMP(*mid,*top) <= 0) { if (CMP(*base,*top) > 0) { tmp = *mid; /* Swap *mid and *top */ *mid = *top; *top = tmp; } else { tmp = *mid; /* Swap *base and *mid */ *mid = *base; *base = tmp; } } } else if (CMP(*mid,*top) > 0) { tmp = *mid; /* Swap *mid and *top */ *mid = *top; *top = tmp; } } else { /* <= 6 elts: use bubblesort*/ for ( ; base < top; --top) { for (mid = base; mid < top; mid++) { if (CMP(*mid,mid[1]) > 0) { tmp = *mid; *mid = mid[1]; mid[1] = tmp; } } } return; /* and return ... */ } do { for ( ; base < mid && CMP(*base,*mid) <= 0; base++) ; for ( ; top > mid && CMP(*mid,*top) <= 0; --top) ; if (base < top) { tmp = *base; *base = *top; *top = tmp; /* Swap *base and *top */ if (mid == top) { mid = base; --top; } else if (mid == base) { mid = top; base++; } } } while (base < top); /* Until all sorted to *mid */ if (mid - sbase > 1) { /* Sort lower half if >1 elt*/ sortsub(sbase,mid); } if (stop - mid > 1) { /* Sort upper half if >1 elt*/ sortsub(mid,stop); } } static void merge(infnames,level, startno,nmerge,outfname) /* Merge sorted input files */ char **infnames; /* Input file names array */ int level, /* No. of times merged */ startno, /* Sequence no. within level*/ nmerge; /* No. of files to merge */ char *outfname; /* File to be written to */ { static char *inbuf[MAXMERGE]; /* (0)Ptrs to input buffers */ static FILE *infp[MAXMERGE], *outfp; /* Input and output FILE *'s*/ char namebuf[MAXNAMLEN]; /* To store temp file names */ char *nameptr; int cur, /* Number of current file */ i, j, nbusy = 0, /* # of input files in use */ tmp; int order[MAXMERGE]; /* Ordering of file numbers */ char *s; long avail; avail = (Malloc(-1) - 20480) & ~1; /* Get free - 20K */ avail /= nmerge + 1; avail -= avail % 2048; if (outfname == (char *)0) { outfp = stdout; /* stdout is default */ } else { outfp = fopen(outfname,"w"); if (outfp == (FILE *)0) { error("%s: cannot open for write\n",outfname); } } if ((s = (char *)Malloc(avail)) == (char *)-1) { /* Grab it */ error("memory allocation failed\n",(char *)0); } setbuffer(outfp,s,avail); if (level < 0) { if (*infnames != (char *)0) { for (i = 0; i < startno; i++) { infnames++; } } } for (i = 0; i < nmerge; i++) { /* Open each input file */ if (level < 0) { nameptr = *infnames++; } else { tempname(namebuf,level,startno+i); nameptr = namebuf; } if (nameptr == (char *)0 || !strcmp(nameptr,"-")) { infp[i] = stdin; /* stdin is default */ } else { infp[i] = fopen(nameptr,"r"); if (infp[i] == (FILE *)0) { error("%s: cannot open for read\n",nameptr); } } if ((s = (char *)Malloc(avail)) == (char *)-1) { /* Grab it */ error("memory allocation failed\n",(char *)0); } setbuffer(infp[i],s,avail); if (inbuf[i] == (char *)0) { /* Possibly allocate buffer */ if ((inbuf[i] = (char *)malloc(LINELEN)) == (char *)0) { error("memory allocation failed\n",(char *)0); } } if (fgets(inbuf[i],LINELEN,infp[i]) != (char *)0) { /* Read 1st line*/ order[nbusy++] = i; /* Enter its seq no. */ for (j = nbusy - 1; j >= 1 && /* Put in place in order */ (*cmp)(inbuf[order[j-1]],inbuf[order[j]]) > 0; --j) { tmp = order[j-1]; order[j-1] = order[j]; order[j] = tmp; } } } if ((options & UNIQUE) && nbusy > 0) { insave[0][0] = inbuf[order[0]][0] + 1; /* Force difference */ } while (nbusy > 0) { cur = order[0]; /* No. of lowest ordered */ if (options & UNIQUE) { if ((*cmp)(inbuf[cur],insave[0])) { /* Differs from previous one*/ fputs(inbuf[cur],outfp); /* Write out lowest ordered */ strcpy(insave[0],inbuf[cur]); } } else { fputs(inbuf[cur],outfp); /* Write out lowest ordered */ } if (fgets(inbuf[cur],LINELEN,infp[cur]) == (char *)0) {/* No next */ for (i = 1; i < nbusy; i++) { /* Reorder rest */ order[i-1] = order[i]; } --nbusy; } else { /* Binary search */ if (nbusy > 1 && (*cmp)(inbuf[cur],inbuf[order[1]]) > 0) { if (nbusy > 2) { int low, high = nbusy - 1, mid; if ((*cmp)(inbuf[cur],inbuf[order[high]]) >= 0) { low = high; } else { low = 1; while (high - low > 1) { mid = (high + low) >> 1; if ((*cmp)(inbuf[cur],inbuf[order[mid]]) >= 0) { low = mid; } else { high = mid; } } } for (i = 0; i < low; i++) { /* Shift elements 1..low to */ order[i] = order[i+1]; /* 0..low-1 to accomodate */ } order[low] = cur; /* for the new index cur */ } else { order[0] = order[1]; order[1] = cur; } } } } for (i = 0; i < nmerge; i++) { fclose(infp[i]); /* Close each input file */ if (level >= 0) { /* (also frees Malloced buf)*/ tempname(namebuf,level,startno+i); unlink(namebuf); /* and delete if temp file */ } } fclose(outfp); /* Close output file */ } void allsort(infnames,destname,compar) /* Main sort entry point */ char **infnames, /* Array of input filenames */ *destname; /* Destination file name */ int (*compar)(); /* Comparision function */ { char **infs; char outfname[MAXNAMLEN]; /* For temp output filenames*/ int level = 0, /* Merge level */ nfiles, /* No. of files to merge */ startno, /* Order no. within nfiles */ nmerge = 0; /* # of files to merge at a */ /* time, initialized to */ /* hush the compiler */ int sameout = 0; /* same in as outfile? */ int merging; cmp = compar; /* Set compare function used*/ /* throughout this module */ nfiles = 0; for (infs = infnames; *infs != (char *)0; infs++) { nfiles++; if (destname != (char *)0 && !strcmp(destname,*infs)) { sameout = 1; } } if (nfiles == 0) { nfiles = 1; } if (options & MERGEONLY) { level = -1; merging = 1; } else { nfiles = isort(infnames,destname); /* # Files left after incore*/ merging = nfiles > 1; level = 0; } while (merging) { merging = nfiles > MAXMERGE || (level < 0 && sameout); for (startno = 0; startno < nfiles; startno += nmerge) { if ((nmerge = nfiles - startno) > MAXMERGE) { nmerge = MAXMERGE; /* Use at most MAXMERGE */ } tempname(outfname,level+1,startno/MAXMERGE); merge(infnames,level,startno,nmerge, (merging) ? outfname : destname); } nfiles = 1 + (nfiles-1)/MAXMERGE; level++; /* Next merge level */ } } static void tempname(buf,level,seqno) /* Create name of temp file */ char *buf; /* Will contain name formed */ int level, seqno; /* Merge level & seq. no. */ { sprintf(buf,"%s\\sort%04d.%03d",tmpdir,level,seqno); } static FILE *reopen(infp,fname) /* Open input file */ FILE *infp; /* Fileptr to possibly close*/ char *fname; /* File to be opened */ { if (infp != (FILE *)0) { /* Close a valid file ptr */ fclose(infp); } if (!strcmp(fname,"-")) { return stdin; } if ((infp = fopen(fname,"r")) == (FILE *)0) { error("%s: cannot open for read\n",fname); } return infp; } static int isort(infnames,dd€ژBOددددددددددددہ@د’‚‚د‚ہ‚O„پدددہہpŒژدہہ‚ˆ€ژAددددددددددددددددددددددددددددددددہ@د‍9GSà3à€!àsâ €bًََََ03ًَََََََ"b`#€ًَََََََََََََََََََََََََََََ3و €bà`à €bل"`sà`ًَ0 3َََو$LE *infp, *outfp; /( Input, output file ptr */ int avail, /* Available free space */ seqno = 0, /* To count multiple writes */ isfree; /* Not still used from avail*/ char *sortdata, /* Start of usable space */ *snext, /* First unused position */ **sorttop, /* Top of usable space */ **sortbase; /* Start of pointer space */ char outnambuf[MAXNAMLEN], /* To create temp name in */ *outfname, /* Name of output file */ *fromfile; /* Name of input file */ if (*infnames == (char *)0) { fromfile = "standard input"; infp = stdin; } else { infp = reopen((FILE *)0,fromfile = *infnames++); } if (options & CHECKONLY) { /* Check if input ordered */ int next = 1; while (fgets(insave[0],sizeof(insave[0]),infp) == (char *)0) { if (*infnames == (char *)0) { exit(0); /* All input files empty */ } else { infp = reopen((FILE *)0,fromfile = *infnames++); } } /* 1st line€دئد€€ہپ‚@ہpدددددددد‰€O€pدددددددددددد‚دأˆ if (infp[i] == (FILE *)0) { error("%s: cannot open for read\n",nameptr); } } if ((s = (char *)Malloc(avail)) == (char *)-1) { /* Grab it */ error("memory allocation failed\n",(char *)0); } setbuffer(infp[i],s,avail); if (inbuf[i] == (char *)0) { /* Possibly allocate buffer */ if ((inbuf[i] = (char *)malloc(LINELEN)) == (char *)0) { error("memory allocation failed\n",(char *)0); } } if (fgets(inbuf[i],LINELEN,infp[i]) != (char *)0) { /* Read 1st line*/ order[nbusy++] = i; /* Enter its seq no. */ for (j = nbusy - 1; j >= 1 && /* Put in place in order */ (*cmp)(inbuf[order[j-1]],inbuf[order[j]]) > 0; --j) { tmp = order[j-1]; order[j-1] = order[j]; order[j] = tmp; } } } if ((options & UNIQUE) && nbusy > 0) { insave[0][0] = inbuf[order[0]][0] + 1; /* Force difference */ } while (nbusy > 0) { cur = order[0]; /* No. of lowest ordered */ if (options & UNIQUE) { if ((*cmp)(inbuf[cur],insave[0])) { /* Differs from previous one*/ fputs(inbuf[cur],outfp); /* Write out lowest ordered */ strcpy(insave[0],inbuf[cur]); } } else { fputs(inbuf[cur],outfp); /* Write out lowest ordered */ rrent ptr */ snext += strlen(snext) + 1; /* Skip the line read */ isfree = (char *)(sortbase) - snext; } if (sortbase == sorttop && seqno > 0) { break; /* No lines this time */ } sortsub(sortbase,sorttop); tempname(outnambuf,0,seqno++); outfname = outnambuf; if (feof(infp)) { if (*infnames == (char *)0) { if (seqno == 1) { /* All read the first time: */ outfname = destname; /* Write to file destname */ } } else { /* Open next input file */ infp = reopen(infp,fromfile = *infnames++); } } if (outfname == (char *)0) { /* Open output file */ outfp = stdout; } else { outfp = fopen(outfname,"w"); if (outfp == (FILE *)0) { error("%s: cannot open for write\n",outfname); } } if ((options & UNIQUE) && sorttop > sortbase) { insave[0][0] = sortbase[0][0] + 1; /* Force difference */ for ( ; sortbase < sorttop; sortbase++) { if ((*cmp)(*sortbase,insave[0])) { /* != previous */ fputs(*sortbase,outfp); strcpy(insave[0],*sortbase); } } } else { for ( ; sortbase < sorttop; sortbase++) { fputs(*sortbase,outfp); } } fclose(outfp); } while (!feof(infp)); fclose(infp); /* Close last input file */ Mfree(sortdata); /* Release Malloced mem */ return seqno; /* No of output files used */ }