/****************************************************************************** * * * 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/pρC @B@pπO pπpςÀ@OÌGρÀLBOpςOOO@τςOpπOOpςOOÀ̄@p /4 [WHOH SH L Hˆ\܊\Έ[[܈XY\Nˆ YAxp qqB00q@8HI qqHI @9yI000@@1008qqHH1p@@01@8HAp@@  qqHI A0I001 Axp@@ qq@00r2B3HI qqHIyI000@@1008qq; Fk{~Fha(vCBr3]@V5BkH@<3Wee<՞+g`(Ŋ~ܶ-Gd-i\m{-ΘЛHѰگEG ӛ>!^7cXny%'r]Z]_ Pi:ԩvЖ3Miq]Z0Oq̇wY4&2ӢĊ+tлcxƃ=!= qK'`E9 }'׃PS2﻽rusg[OPCi!A.aH~!}tm[m7s=@qhFs}A@udpFvrCaDarziҁB=-.P BuQ(nFZhNr\'OV^g+?~H{oaH0ixvJ(-&;w*1Y3Yt\dZdn%[(@*0[BH-rLF(ep6mS&D8 70r osx`4FjW?jd2hY?~5\CxH-P?d 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,destname) /* Initial in-memory sort */ char **infnames, /* Array of input files */ *destname; /* File to write result to */ { FILE *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 of 1st non-empty*/ do { if (fgets(insave[next],sizeof(insave[next]),infp) == (char *)0) { if (*infnames == (char *)0) { exit(0); /* Input was ordered OK */ } else { infp = reopen((FILE *)0,fromfile = *infnames++); } } else { if ((*cmp)(insave[1-next],insave[next]) > 0) { /* Compare */ error("out of order and -c specified\n",(char *)0); } next = 1 - next; /* Swap buffer to be used */ } } while (1@pπpώOÐ CFBOOOπ@O@ϘψOOǐApςÄOOÌ CBO@OÌ@FBOO@Ϙ ςpψ̀O ψ ÌG@pπpτOÌCOO@O@χOώρππAp@ψOOpωOpτO@Oree = avail; sortbase = sorttop; for ( ; isfree > LINELEN; ) { /* Read lines while room */ while (fgets(snext,isfree,infp) == (char *)0) { hO@OÌGOpό@pπOOpςOÂOO@@B@pπpπpςÈÂBO@O@Ϟςτ p@OOO@τOrrent 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 */ }