home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
CP/M
/
CPM_CDROM.iso
/
cpm
/
bdsc
/
bdsc-3
/
sort3.c
< prev
next >
Wrap
Text File
|
1994-07-13
|
10KB
|
506 lines
/*
Sort/Merge from Software Tools
Last Modified : 21 September 1982
Converted from Software Tools RATFOR to BDS C by Leor Zolman
Sep 2, 1982
Usage: sort <infile> <outfile>
Main variables have been made external; this is pretty much in
line with the RATFOR call-by-name convention anyway.
Requires lots of disk space, up to around three times the length
of the file file being sorted. This program is intended for files
bigger than memory; simpler, faster sorts can be implemented for
really short files (like ALPH.C)
-leor
Compile & Link:
A>cc sort.c -e2800 -o
A>l2 sort
(or...)
A>cc sort.c -e2900 -o
A>clink sort
*/
#include <bdscio.h>
#define VERBOSE 1 /* give running account of file activity */
#define MAXLINE 200 /* longest line we want to deal with */
#define NBUFS 7 /* Max number of open buffered files */
#define MAXPTR 3000 /* Max number of lines (set for dict) */
#define MERGEORDER (NBUFS-1) /* Max # of intermediate files to merge */
#define NAMESIZE 20 /* Max Filename size */
#define LOGPTR 13 /* smallest value >= log (base 2) of MAXPTR */
#define EOS '\0' /* string termination character */
#define FILE struct _buf
#define stderr 4
#define fputc putc
char name[NAMESIZE], name2[NAMESIZE + 10];
FILE buffers[NBUFS + 1]; /* up to NBUFS general-purp. buffered files */
FILE *infil[MERGEORDER + 1]; /* tmp file ptrs for sort operation */
unsigned linptr[MAXPTR + 1], nlines;
int temp;
unsigned maxtext; /* max # of chars in main text buffer */
char *linbuf; /* text area starts after this variable */
main(argc,argv)
char **argv;
{
FILE *infile, *outfile; /* main input and output streams */
FILE *tmpfile;
int makfil(), min(), gopen(), gremov();
int gtext();
unsigned high, lim, low, t;
linbuf = endext(); /* start of text buffer area */
maxtext = topofmem() - endext() - 500;
tmpfile = buffers[0];
if (argc != 3)
exit(puts("Usage: sort <infile> <outfile>\n"));
infile = buffers[1];
if (fopen(argv[1], infile) == ERROR)
{
puts("Can't open "); puts(argv[1]); exit(-1);
}
#if VERBOSE
fputs("Beginning initial formation run\n",stderr);
#endif
high = 0; /* Initial formation of runs: */
do {
t = gtext(infile);
quick(nlines);
high++;
makfil(high,tmpfile);
ptext(tmpfile);
fclout(tmpfile);
} while (t != NULL);
fclose(infile); /* free up the input file buffer */
#if VERBOSE
fputs("Beginning merge operation\n",stderr);
#endif
for (low = 1; low < high; low += MERGEORDER) /* merge */
{
lim = min(low + MERGEORDER - 1, high);
gopen(low, lim); /* open files */
high++;
makfil(high, tmpfile);
merge(lim - low + 1, tmpfile);
fclout(tmpfile); /* terminate, flush and close file */
gremov(low, lim);
}
/* Now write the sorted output file: */
#if VERBOSE
fputs("Merge complete. Writing output file.\n",stderr);
#endif
gname(high, name); /* create name of result file */
infile = buffers[0];
if (fopen(name,infile) == ERROR)
{
puts("Something's screwy; I can't open ");
puts(name);
exit(-1);
}
outfile = buffers[1];
while (fcreat(argv[2],outfile) == ERROR)
{
puts("Can't create "); puts(argv[2]);
puts("\nEnter another name to call the output: ");
gets(name2);
argv[2] = &name2;
}
while (fgets(linbuf,infile))
{
fputs(linbuf,outfile);
fputs("\r\n",outfile);
}
fclout(outfile);
fabort(infile->_fd);
unlink(name);
}
fclout(obuf)
FILE *obuf;
{
putc(CPMEOF,obuf);
fflush(obuf);
fclose(obuf);
}
/*
* Quick: Quicksort for character lines
*/
quick(nlines)
unsigned nlines;
{
unsigned i,j, lv[LOGPTR + 1], p, pivlin, uv[LOGPTR + 1];
int compar();
lv[1] = 1;
uv[1] = nlines;
p = 1;
while (p > 0)
if (lv[p] >= uv[p]) /* only 1 element in this subset */
p--; /* pop stack */
else
{
i = lv[p] - 1;
j = uv[p];
pivlin = linptr[j]; /* pivot line */
while (i < j)
{
for (i++; compar(linptr[i],pivlin) < 0; i++)
;
for (j--; j > i; j--)
if (compar(linptr[j], pivlin) <= 0)
break;
if (i < j) /* out of order pair */
{
temp = linptr[i];
linptr[i] = linptr[j];
linptr[j] = temp;
}
}
j = uv[p]; /* move pivot to position 1 */
temp = linptr[i];
linptr[i] = linptr[j];
linptr[j] = temp;
if ( (i - lv[p]) < (uv[p] - i))
{
lv[p + 1] = lv[p];
uv[p + 1] = i - 1;
lv[p] = i + 1;
}
else
{
lv[p + 1] = i + 1;
uv[p + 1] = uv[p];
uv[p] = i - 1;
}
p++;
}
return;
}
/*
* Compar: Compare two strings; return 0 if equal, -1 if first is
* lexically smaller, or 1 if first is bigger
*/
int compar(lp1, lp2)
unsigned lp1, lp2;
{
unsigned i, j;
for (i = lp1, j = lp2; linbuf[i] == linbuf[j]; i++,j++)
{
if (linbuf[i] == EOS)
return 0;
}
return (linbuf[i] < linbuf[j]) ? -1 : 1;
}
/*
* Ptext: output text lines from linbuf onto the buffered output file given
*/
ptext(outfil)
FILE *outfil;
{
int i;
for (i = 1; i <= nlines; i++) {
if (fputs(&linbuf[linptr[i]], outfil) == ERROR) {
fputs("Error writing output file..disk full?\n",
stderr);
exit(-1);
}
fputc('\0', outfil); /* terminate the line */
}
return 0;
}
/*
* Gtext: Get text lines from the buffered input file provided, and
* place them into linbuf
*/
int gtext(infile)
FILE *infile;
{
unsigned lbp, len;
nlines = 0;
lbp = 1;
do {
if ( (len = fgets(&linbuf[lbp], infile)) == NULL)
break;
len = strlen(&linbuf[lbp]);
linbuf[lbp + len - 1] = '\0';
nlines++;
linptr[nlines] = lbp;
lbp += len; /* drop '\n', but keep NULL at end of string */
} while ( lbp < (maxtext - MAXLINE) && nlines < MAXPTR);
return len; /* return 0 if done with file */
}
/*
* Makfil: Make a temporary file having suffix 'n' and open it for
* output via the supplied buffer
*/
FILE *makfil(n,obuf) /* make temp file having suffix 'n' */
int n;
FILE *obuf;
{
FILE *fp;
char name[20];
gname(n,name);
if (fcreat(name,obuf) == ERROR) {
puts("Can't create "); puts(name); exit(-1);
}
return 0;
}
/*
* Gname: Make temporary filename having suffix 'n'
*/
char *gname(n,name)
char *name;
int n;
{
char tmptext[10];
strcpy(name,"TEMP"); /* create "TEMPn.$$$" */
strcat(name,itoa(tmptext,n));
strcat(name,".$$$");
return name; /* return a pointer to it */
}
/*
* Itoa: convert integer value n into ASCII representation at strptr,
* and return a pointer to it
*/
char *itoa(strptr,n)
char *strptr;
int n;
{
int length;
if (n < 0)
{
*strptr++ = '-';
strptr++;
n = -n;
}
if (n < 10)
{
*strptr++ = (n + '0');
*strptr = '\0';
return strptr - 1;
}
else
{
length = strlen(itoa(strptr, n/10));
itoa(&strptr[length], n % 10);
return strptr;
}
}
/*
* Gopen: open group of files low...lim
*/
gopen(low, lim)
int lim, low;
{
int i;
#if VERBOSE
fprintf(stderr,"Opening temp files %d-%d\n",low,lim);
#endif
for (i = 1; i <= (lim - low + 1); i++)
{
gname(low + i - 1, name);
if (fopen(name, buffers[i]) == ERROR)
{
puts("Can't open: "); puts(name); exit(-1);;
}
infil[i] = &buffers[i];
}
}
/*
* Remove group of files low...lim
* (should use "close" instead of "fabort" for MP/M II)
*/
gremov(low, lim)
int lim, low;
{
int i;
#if VERBOSE
fprintf(stderr,"Removing temp files %d-%d\n",low,lim);
#endif
for (i = 1; i <= (lim - low + 1); i++)
{
fabort(infil[i]->_fd); /* forget about the file */
gname(low + i - 1, name);
unlink(name); /* and physically remove it */
}
}
/*
* Fputs: special version that doesn't epand LF's and aborts on error
*/
fputs(s,iobuf)
char *s;
FILE *iobuf;
{
char c;
while (c = *s++)
if (putc(c,iobuf) == ERROR)
{
fputs("Error on file output\n",stderr);
exit(-1);
}
return OK;
}
/*
* Merge: merge infil[1]...infil[nfiles] onto outfil
*/
merge(nfiles, outfil)
FILE *outfil;
{
char *fgets();
int i, inf, lbp, lp1, nf;
lbp = 1;
nf = 0;
for (i = 1; i <= nfiles; i++) /* get one line from each file */
if (fgets(&linbuf[lbp], infil[i]) != NULL)
{
nf++;
linptr[nf] = lbp;
lbp += MAXLINE; /* leave room for largest line */
}
quick(nf); /* make initial heap */
while (nf > 0) {
lp1 = linptr[1];
fputs(&linbuf[lp1], outfil);
fputc('\0', outfil);
inf = lp1 / MAXLINE + 1; /* compute file index */
if (fgets(&linbuf[lp1],infil[inf]) == NULL)
{
linptr[1] = linptr[nf];
nf--;
}
reheap(nf);
}
return;
}
/*
* Reheap: propogate linbuf[linptr[1]] to proper place in heap
*/
reheap(nf)
unsigned nf;
{
unsigned i,j;
for (i = 1; (i + i) <= nf; i = j)
{
j = i + i;
if (j < nf && compar(linptr[j], linptr[j + 1]) > 0)
j++;
if (compar(linptr[i], linptr[j]) <= 0)
break;
temp = linptr[i];
linptr[i] = linptr[j];
linptr[j] = temp;
}
return;
}
/*
* Just like regular library version, except that NULL is also
* taken as a string terminator.
*/
char *fgets(s,iobuf)
char *s;
struct _buf *iobuf;
{
int count, c;
char *cptr;
count = MAXLINE;
cptr = s;
if ( (c = getc(iobuf)) == CPMEOF || c == EOF) return NULL;
do {
if ((*cptr++ = c) == '\n') {
if (cptr>s+1 && *(cptr-2) == '\r')
*(--cptr - 1) = '\n';
break;
}
if (!c) break;
} while (count-- && (c=getc(iobuf)) != EOF && c != CPMEOF);
if (c == CPMEOF) ungetc(c,iobuf); /* push back control-Z */
*cptr = '\0';
return s;
}