home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
OS/2 Shareware BBS: 3 Comm
/
03-Comm.zip
/
QSORT21.ZIP
/
QSORT.C
< prev
next >
Wrap
Text File
|
1989-11-29
|
14KB
|
470 lines
/* QSort/2 2.1, vaguely based on Use_Sort version 2.3, 25 November 1988 */
/*
This program sorts a text file (FIDOUSER.LST) according to specified
sort keys. Valid key specifications (following input, output) are:
/ = begin key field definition
+|- = direction, ascending|descending
C = starting column number, beginning with 1
: = end of column, beginning of length
L = length of key field, beginning with 1
... = repeating for up to 10 key fields.
For example, ParseLst calls QSort(/2) with this command line:
QSort(/2) FidoUser.$$1 /+1:41 /-61:5 /-41:13 /+55:6
Which will cause the lines in FidoUser.$$1 to be ordered by those
fields, with equal key fields causing the next field to be tested.
When all the specified key field tests are done, otherwise equal
lines will be left as is.
-----------------------------------------------------------------------------
QSort/2 is written in IBM C/2 1.1, roughly equivalent to MSC 5.1, but will
be intended to run solely in OS/2 Protected mode, duplicating the function
of Ben Baker's excellent DOS-only QSORT program regarding ParseLst.
In the spirit of BBS (Bit Bucket Software), this program is also entered
into the Public Domain. This means that if you mess with it, and break
it, you own both pieces.
-----------------------------------------------------------------------------
[Notice in the original source file from which QSort/2 is derived:]
Copyright (c) 1988 by Jeffrey J. Nonken. Released to public domain
September 1988. This is public domain; no strings, no bullshit. Do what you
want with it. Nobody owns it.
However, I would like it if you would refer all changes and enhancements to
me. This way I can maintain it properly. Thanks.
Jeffrey J. Nonken: sysop of Opus 103/522 Orange, Ca. (714)997-8958
Please send correspondence to:
507 Ave. Presidio
San Clemente, Ca. 92672
This was [originally] written in Microsoft C 5.1.
*/
#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>
#include <sys\types.h>
#include <sys\stat.h>
#include <string.h>
#include <io.h>
#include <ctype.h>
#include "stuff.h"
#define lint long int
#define word unsigned int
#define byte unsigned char
#define NEAR near
/* #define DEBUG debug */
typedef struct
{
int key; /* ASCENDING=0, DESCENDING!=0 */
int scol; /* Key starting column, 0-based */
int klen; /* Key field length in columns */
} sortkey;
sortkey keys[10];
char far ** NEAR ptr = NULL;
lint U1O = -1;
lint U2O = -1;
char one_record[MAX_REC];
int numkeys=0; /* Number of keys defined */
word file_size; /* Number of records in file */
int flag = FALSE;
int show=FALSE; /* Show-only switch */
int slash=FALSE; /* key entered switch */
FILE_TYPE input_file;
FILE_TYPE output_file;
FILE_TYPE temp_out;
FILE * NEAR old_file; /* file to sort */
FILE * NEAR new_file; /* destination of sorted file. */
/****************************************************************************
Comparison routine for qsort.
****************************************************************************/
int compare(char far **user1,char far **user2);
/* Return *user1-*user2 for ascending, reverse for descending */
int compare(char far **user1,char far **user2)
{
register int j;
register int k;
sortkey *SK; /* pointer to successive entries */
char far *U1; /* de-referenced pointer to *user1 */
char far *U2; /* de-referenced pointer to *user2 */
/*
Not much is left of the original algorithm referred to below. If you
cook up anything better, here's where the biggest buy-back will come.
This routine is passed the NEAR array entries of FAR pointers to the
in-memory lines to be compared by the "numkeys" number of "keys".
Comment from the original USE-SORT 2.3, updated:
This may take a little explanation. k goes through the sort key array,
picking out the next sort key. j is simply the result of each compare.
i is used to perform the compare based on the entry in sortkey keys.
Each loop through tests one key. As long as j == 0, the entries are
identical. If j != 0 then there was a difference, and we drop through
to check for descending order and return the result. If j never becomes
non-0 we will reach the end of order[] and return a 0 result.
I lifted this algorithm from Mike Housky's PAKSORT program. Thanks, Mike.
*/
/* Get the two records to be compared */
SK = keys;
#ifdef DEBUG
(void)printf("U1 = %s\n",*user1);
(void)printf("U2 = %s\n",*user2);
#endif
for (j = k = 0; (k < numkeys) && (!j); ++k)
{
#ifdef DEBUG
(void) printf("scol = %d, klen = %d, user1 len = %d\n",SK->scol,SK->klen,strlen(*user1));
#endif
if (SK->scol < (int)(strlen(*user1)))
{
U1 = *user1 + SK->scol;
U2 = *user2 + SK->scol;
if (j = strnicmp( U1, U2, SK->klen))
{
if (SK->key)
j = -j;
break;
}
}
++SK;
}
#ifdef DEBUG
(void)printf("compare = %d\n", j);
#endif
if (j)
{
flag = TRUE;
}
return(j);
}
/* #pragma check_stack- */
/* Now write the new file based on pointers in ptr[] */
int write_new_file(void);
int write_new_file(void)
{
register word i;
char far **pntr;
(void) printf(" Writing new file...");
if ((new_file = fopen(temp_out.filename,"w")) == NULL)
{
(void) fprintf (stderr,"Cannot open output file %s!\n", temp_out.filename);
return(3);
}
pntr = ptr;
for ( i = 0; i < file_size; ++i)
{
if (fputs (*(pntr++), new_file))
{
(void) fprintf(stderr,"Error writing output file!\n");
(void) fclose(new_file);
return(3);
}
}
(void) fclose (new_file);
if ( strcmp ( output_file.filename, temp_out.filename) != 0 )
{
(void) printf (" Renaming...");
unlink (output_file.filename);
rename (temp_out.filename, output_file.filename);
}
(void) printf(" done.\n");
return(0);
}
/* #pragma check_stack+ */
/****************************************************************************
Step 1: build array of pointers to in-memory far lines of the old file.
Step 2: quicksort.
Step 3: if not already in order, write the lines in array order.
****************************************************************************/
int do_sort(void);
int do_sort()
{
lint i;
int j;
struct stat stat_buff;
sortkey *kp;
char far ** pntr;
if (show == TRUE)
{
for (j = 0; j < numkeys; ++j)
{
kp = &keys[j];
(void) printf("key %d is %c, from %d for %d columns\n", j+1, kp->key ? '-' : '+', kp->scol + 1, kp->klen);
}
}
old_file = fopen(input_file.filename,"r"); /* open for text */
if (old_file == NULL) /* was there one? */
{
(void) fprintf(stderr,"Cannot open file %s!\n",input_file.filename);
return(2);
}
(void) stat(input_file.filename,&stat_buff); /* how big? */
(void) fgets(one_record, (int) sizeof(one_record), old_file);
file_size = (word)(((lint)stat_buff.st_size) / ((lint) ((lint)strlen(one_record))+1L));
(void) fseek(old_file,0L,SEEK_SET); /* rec 0 */
if ((int) strlen(one_record) >= (int) sizeof(one_record)) /* check line len */
{
(void) fprintf(stderr,"Lines too long to sort.\n"); /* too long to sort */
(void) fclose(old_file);
return(1);
}
if (file_size == 0) /* # entries */
{
(void) fprintf(stderr,"Empty file.\n"); /* empty, nothing done */
(void) fclose(old_file);
return(1);
}
(void) printf("File has %d entries, %d long each.\n",file_size, strlen(one_record)+1);
ptr = (char far **)calloc(file_size,sizeof(char far *));
if (ptr == NULL) /* allocate array of far pointers */
{
(void) fprintf(stderr,"Cannot allocate enough memory for pointers!\n");
(void) fclose(old_file);
return(2);
}
(void) printf(" Reading old file...");
pntr = ptr;
for (i = 0; i < file_size; ++i)
{
if (!(*pntr = (char far *) _fmalloc(sizeof(one_record))))
{
(void) fprintf(stderr,"Cannot allocate enough memory for records!\n");
(void) fclose(old_file);
return(2);
}
if (!(fgets(*(pntr++), (int) sizeof(one_record), old_file)))
{
(void) fprintf(stderr,"Error reading input file!\n");
(void) fclose(old_file);
return(2);
}
}
(void) fclose(old_file);
/*
Rather than sort the array of entries, we sort the array of pointers.
This allows us to sort a near array of char far lines.
Huge pointer arithmetic is expensive.
*/
(void) printf(" Sorting old file...");
#ifdef DEBUG
(void) printf("\nnumkeys = %d\n",numkeys);
#endif
qsort((void far *)&ptr[0], (size_t)file_size, sizeof(char far *), compare);
if (!flag)
{
(void) printf("\nFile already sorted! Nothing done.\n");
return(0);
}
return(write_new_file());
}
/****************************************************************************
Process command-line parameters.
input_file = replaces input and output (stdin, stdout) filenames
output_file= replaces output (copied from input) filename
? = print help screen and exit
/? = display keys after parsing, without sorting
/+ = key field, ascending
/- = key field, descending
sort key format:
/sccc:lll
s = sequence (Ascending or Descending)
c = scol, starting column, 0-based
l = klen, length of key field in columns
***************************************************************************/
int process_args(char *arg);
int process_args(char *arg)
{
char *tok;
static char token[] = " ";
sortkey *kp;
int slen;
kp = &keys[numkeys]; /* init to next entry in keys */
tok = strtok(arg,"\n");
if ((tok != NULL) && (*tok != '\0'))
{
switch(toupper(*tok))
{
case '/':
slash = TRUE;
++tok;
switch(toupper(*tok))
{
case '?':
if (show || (*(++tok) != '\0'))
{
(void) fprintf(stderr,"Invalid sort criterion: /?.\n");
return(1);
}
show = TRUE;
break;
case '-': /* Process descending key */
case '+': /* Process ascending key */
if (++numkeys > 9)
{
(void) fprintf(stderr,"Too many sort keys.\n");
return(1);
}
kp->key = *tok - '+';
++tok;
slen = strcspn(tok, ":");
if ((strchr (tok, (int) ':') == NULL) || (slen < 1) || (slen > 3))
{
(void) fprintf(stderr,"Invalid sort criterion: /%s.\n",--tok);
return(1);
}
(void) strncpy( token, tok, (size_t) slen);
kp->scol = atoi(token) - 1;
tok += slen + 1;
slen = (int) strlen(tok);
if ((slen < 1) || (slen > 3))
{
(void) fprintf(stderr,"Invalid sort criterion: /...%s.\n",--tok);
return(1);
}
kp->klen = atoi(tok);
break;
default: /* Oops */
(void) fprintf(stderr,"Invalid sort criterion: /%s.\n",tok);
return(1);
}
break;
case '?':
(void) fprintf(stderr,"Invalid sort criterion: %s.\n",tok);
return(1);
default: /* Process filein, fileout */
if (!slash)
{
if (strcmp( input_file.filename, "stdin") == 0)
{
(void) strcpy( input_file.filename, tok);
(void) strcpy( output_file.filename, tok);
(void) strcpy( temp_out.filename, "QSORT.$$$");
break;
}
if (strcmp( output_file.filename, input_file.filename) == 0)
{
(void) strcpy( output_file.filename, tok);
(void) strcpy( temp_out.filename, tok);
break;
}
}
else
{
(void) fprintf(stderr,"Invalid sort criterion: %s.\n",tok);
return(1);
}
}
}
return(0);
}
/****************************************************************************
Display this if ? was specified.
****************************************************************************/
void print_help(void);
void print_help()
{
(void) printf("QSort/2 Syntax: [input [output]] [?|keys]\n\n");
(void) printf("Copyright 1989 by William R. Andrus, released to Public Domain.\n\n");
(void) printf("QSort/2 will sort a file by up to 10 key fields. Its intended for\n");
(void) printf("use by ParseLst in OS/2 Protected Mode. If neither filename is\n");
(void) printf("given, they default to stdin and stdout. If one filename is given,\n");
(void) printf("it becomes both the input and output filename. Key fields are\n");
(void) printf("specified in descending order of priority. Valid keys are:\n");
(void) printf(" /s[c[c[c]]:l[l[l]]]\n");
(void) printf(" / : identifies a key\n");
(void) printf(" s : one of + (ascending), - (descending), or ? (show)\n");
(void) printf(" c : beginning column number, 1-999\n");
(void) printf(" l : length of field, 1-999\n");
(void) printf("Each key must contain a +, -, or ?, with no spaces permitted.\n");
(void) printf("If a key contains a + or -, the c:l must be specified.");
(void) printf("If a key contains ?, no other data is permitted.");
(void) printf("If a key containing ? is given, no sort is performed but the list of\n");
(void) printf("keys is displayed on stdout.\n\n");
(void) printf("QSort/2 requires fixed length records, up to 128 characters in length.\n\n");
exit(0);
}
/****************************************************************************
Main program.
****************************************************************************/
main(int,char **);
main(argc,argv)
int argc;
char *argv[];
{
int i, j;
if (argc > 1) if (argv[1][0] == '?')
print_help();
(void) printf("QSort/2: OS/2 Text file sort program.\n");
(void) printf("Version 2.1 29 November 1989\n\n");
/* Initialize file names */
(void) strcpy( input_file.filename, "stdin");
(void) strcpy( output_file.filename, "stdout");
(void) strcpy( temp_out.filename, "stdout");
for (i = 1; i < argc; ++i)
if (process_args(argv[i]))
return(1);
i = do_sort();
if (i)
return(i);
if (ptr != NULL) {
for (j = 0; j < file_size; ++j) {
if (ptr[j])
_ffree(ptr[j]);
}
free((char *)ptr);
}
(void) fcloseall();
return(i);
}