home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
CP/M
/
CPM_CDROM.iso
/
mbug
/
mbug051.arc
/
DISKNUM.C
< prev
next >
Wrap
Text File
|
1979-12-31
|
10KB
|
307 lines
/* DISKNUM.C 87-05-21 by Simon Gerraty
* This program will read a file such as MAST.CAT produced by ncat.com,
* and produce a numerically sorted list of disk names
*/
#include <local.h>
/* local.h contains the following commonly used #include directives
#include <stdio.h>
#include <cpm.h>
#include <ctype.h>
* commonly used #defines
#define TRUE 1
#define FALSE 0
#define uchar unsigned char
/* CONTROL CODES FOR ADM-3A recognised by MICROBEE */
#define bel() putchar('\007') /* Ring bel, what else? */
#define cls() putchar('\032') /* Clear Screen & Home */
#define home() putchar('\036') /* Home Cursor */
#define curup() putchar('\013') /* Cursor up one line */
#define curdn() putchar('\012') /* Cursor down one line */
#define curlef() putchar('\010') /* Cursor left one char */
#define currit() putchar('\014') /* Cursor right one char */
* end of local.h
*/
/*
* defines here help make changes easier and help remove 'magic numbers' from
* the rest of the program
*/
#define PROGNAME "DiskNum"
#define VER "1"
#define SUBVER "01"
#define DATE "22-May-87"
#define COPYRIT "Copyright (C) 1987, Simon Gerraty"
#define PURPOSE ""
#define MAX 128
#define MAX_MISS 60
/* global variables (those defined outside of any function) are not
* generally good practice. functions should be written such that all
* values they care about are either handed to them as arguments, or
* generated internally. See for example strcspn() at the end of the
* listing. Functions written this way can be compiled, stored in a
* library and never looked at again, yet they are available as building
* blocks for other programs. Anyway globals are bad news, but I am
* lazy, so I use them for flags etc.
*/
static char dbug = FALSE;
static char verbose = FALSE;
static char ascending = TRUE;
/* each C function is defined with an argument list between ()s. Many
* functions have no arguments eg foo(). Every program has a function
* main() which has two arguments. Argv is an array of pointers to the
* string arguments (supplied on the command line). Argc is an integer and
* is equal to the number of arguments pointed to by argv
*/
main(argc, argv)
int argc;
char *argv[];
{
/*
* all variables that will be used in this function must be declared
* before use. NOTE: that functions that return other than ints must
* also be declared before use - or the compiler will tell you about it!
*/
char *malloc(), *s, *ln, *l;
char finished, changed, *fgets();
FILE *fopen(), *inpfp, *outfp;
short n, m, i, cnt, iter;
char *inf, *outf, *line[200];
/* announce ourselves to the world */
signon();
/* process any flags on the command line */
while (--argc > 0 && (*++argv)[0] == '-')
{
s = argv[0]+1;
switch (*s)
{
case 'R':
ascending = FALSE;
break;
case 'V':
verbose = TRUE;
break;
case 'D':
dbug = TRUE;
break;
default:
fprintf(stderr, "%s: illegal option %c\n", PROGNAME, *s);
break;
}
}
/* restore argc & argv to compensate for while() above */
argc++;
argv--;
/* if we have not got the right number of arguments then die! */
if (argc < 3)
usage();
/* open the files we need be sure that all goes well */
if ((inpfp = fopen((inf = *++argv), "r")) == (FILE *)NULL)
errexit(1, "can't open input file");
if ((outfp = fopen((outf = *++argv), "w")) == (FILE *)NULL)
errexit(1, "can't open output file");
if (verbose)
printf("Scanning Input file %s\n", inf);
cnt = iter = 0;
finished = FALSE;
while (!(finished))
{
ln = malloc(MAX);
/* allocate memory for the next input line */
l = fgets(ln, MAX, inpfp);
if ((m = strtest(ln, ".FRE"))) /* test if it's one we want */
{
finished = FALSE;
/* we are only interested in the part after the ',' */
m = strcspn(ln, ",");
/* move the pointer to just after the ',' */
ln += ++m;
line[i++] = ln;
/* line[] is an array of pointers to char strings here we put a pointer to
* the current line (ln) into the array. Using the array of pointers will
* make sorting much more simple - as we shall see.
*/
}
else
if (cnt++ > MAX_MISS)
finished = TRUE;
/* since we know that the lines we want are a contiguous block that may
* have an exception list before it, we allow to read MAX_MISS lines that
* do not meet our criteria before stopping. after all we don't want to
* read all of an 80k catalogue file!
*/
}
/* close the input file, since we are finished with it */
fclose(inpfp);
/* if we think we read more than 200 lines with .FRE in them, then
* something probably went wrong - in any case we have not allowed for
* sorting more than 200 names, so exit gracefully!
*/
if ((m = --i) > 200)
errexit(1, "$&'#)$(! I don't understand it!");
if (verbose)
printf("OK, Now sorting the list of names\n");
/* now we want to sort our list of names, which remember are pointed to by
* the array of pointers line[]. Generally the algorithm used will take
* less than n iterations to sort n items. So we continue to shuffle the
* list while we detect that it is changing, and while the number of
* iterations is less than 2*n. The following code, passes pointers to
* two successive pointers in the array line[] to the function swap().
* We pass pointers to pointers so that swap() can 'swap' them if required.
* In C the only way for a called function to affect any objects (eg
* variables) other than globals or ones local to the called function, is
* if it is passed pointers to the objects.
*/
/* be sure to set the initial condition for the while loop */
changed = TRUE;
while ((changed) && iter++ < (m 2))
{
changed = FALSE;
for (i=0; i < m; i++)
if ((n = swap(&line[i], &line[(i + 1)])) == TRUE)
changed = TRUE;
}
if (verbose)
printf("Finished! %d sorted in %d iterations\n", m, iter);
fprintf(outfp, "List of disk names from %s\n\n", inf);
for (i = 0; i <= m; i++)
{
/* C has no built in IO support, but standard library functions such as
* printf() and fprintf() allow very sophisticated output formatting.
*/
fprintf(outfp, " %2d\t%15s", i, line[i]);
/* if there is a gap in the numeric sequence between i and (i+1) then
* print a blank line
*/
if ((n = isgap(line[i], line[(i + 1)])) == TRUE)
fprintf(outfp, "\n");
}
/* now close the output file 'cos we're nearly done! */
fclose(outfp);
} /* end of function main() */
/* now the functions called in main must be supplied, either in this
* file or in others which will be compiled separately and 'linked' with
* the compiled code from this file.
*/
/* swap() takes 2 pointers to pointers, compares the contents of the
* strings they point to, and swaps them (the pointers) if the strings
* are out of sequence. The strings always remain in the memory to which
* they were read, and the pointers (which will control the order in
* which the strings are printed) are moved about. Shuffling two byte
* pointers is a lot quicker (and easier) than shuffling 80 character
* strings!
*/
swap(s1, s2)
char **s1, **s2;
{
char swp, *tmp;
short n1, n2;
n1 = value(*s1);
n2 = value(*s2);
if (n1 > n2)
swp = (ascending) ? TRUE : FALSE;
else
swp = (ascending) ? FALSE : TRUE;
if (swp)
{
tmp = *s1;
*s1 = *s2;
*s2 = tmp;
n1 = TRUE;
}
/* let the calling function know whether we swapped or not */
return(swp);
}
/* isgap() takes two string pointers, and checks whether the gap
* between them is not more than one.
*/
isgap(s1, s2)
char *s1, *s2;
{
short n1, n2;
n1 = value(s1);
n2 = value(s2);
if (ascending)
n1 = (++n1 < n2) ? TRUE : FALSE;
else
n1 = (n1 > ++n2) ? TRUE : FALSE;
return(n1);
}
/* take a string, find the numeric bit at the end,
* and return it as an int
*/
value(s)
char *s;
{
short l;
s += (l = strcspn(s, ".")); /* skip over everything */
l = atoi(++s); /* upto and including the '.' */
return(l);
}
/* the name says it all! */
signon()
{
fprintf(stderr, "\n\n%s V%s.%s %s", PROGNAME, VER, SUBVER, DATE);
fprintf(stderr, " %s\n%s\n\n", COPYRIT, PURPOSE);
}
/* same here */
usage()
{
bel();
fprintf(stderr, "\nUsage:\t\t%s ", PROGNAME);
fprintf(stderr, "<input file> <output file>");
exit(1);
}
/* this one is really only useful in environments which support multi-
* tasking, and/or spawned processes eg Unix, MS-DOS etc The idea is to
* allow the program to exit with a return code that will signal to the
* parent process, whether things worked ok or whether
* it died with it's leg in the air.
*/
errexit(l, s)
short l;
char *s;
{
fprintf(stderr, "%s: %s\n", PROGNAME, s);
exit(l);
}
/* test s1 for the presence of s2
*/
strtest(s1, s2)
char *s1, *s2;
{
char c;
short l, i = 0;
l = strlen(s2);
while ((c = *s1++) && i < l)
{
if (c == s2[i])
i++;
else
i=0;
}
return((i < l) ? FALSE : TRUE); /* did we find it or not? */
}
/* return the length of the 1st span of s1 that contains none of the
* chars in s2. This function would normally be written for performance
* and placed in a library, and never looked at again!
*/
strcspn(s1, s2)
char *s1, *s2;
{
char c, finished;
short i, l = 0;
finished = FALSE;
while (!(finished))
{
c = s1[l++];
for (i=0; i < strlen(s2); i++)
if (c == s2[i])
finished = TRUE;
}
return(--l);
}
/* That's all folks! */
2)
char *s1, *s2;
{
char c;
short l, i