[net.micro.cpm] crck2.c

W8SDZ%MIT-MC@sri-unix.UUCP (02/05/84)

From:  Keith Petersen <W8SDZ @ MIT-MC>

/*
 *    crck.c  --  Unix version of the CP/M utility
 *
 *    Usage:
 *        crck [-t|c|u|i] [filename ... ]
 *
 *    Calculates a 16-bit cyclic redundancy check on its input files and
 *    prints the resulting number in hexadecimal.  CRCK is manually invoked
 *    on two versions of the same file to ensure that they are the same.
 *    Prints the the filename, size in bytes or sectors depending on the
 *    option flag, and the CRC.
 *
 *    Options:
 *        -t    Perform CRCK on a CP/M text file that is stored as a Unix
 *              file.  CP/M record delimters are substituted for Unix 
 *		newlines and the file is padded to a 128-byte sector
 *  		boundary with ^Z's.
 *
 *	  -u    Perform CRCK on a regular Unix file.  Takes whatever is there.
 *
 *	  -c    Perform CRCK on a CP/M ".COM" file.  Handled the same as
 *		the -u flag except sectors are reported rather than bytes.
 *
 *	  -i    Perform CRCK on a CP/M ".COM" file stored in ITS binary 
 *		format.  Ignores the first four bytes of the file.  Implies
 *  		the -c option.
 * 
 *	  If no option flag is chosen, the default option is -u.
 *    
 *    Wildcards may, of course, be used and the program may be used in 
 *    pipelines.
 *
 *
 *----------------------------------------------------------------------------
 *    Version 2.0  mods by Ben Goldfarb (decvax!ucf-cs!goldfarb or
 *                                            Goldfarb.ucf-cs @ Rand-Relay)
 *
 *		   Added CP/M text and binary file modes and changed to 
 *		   use standard stream I/O.
 */

#include <stdio.h>

#define VERSION 20		/*  Version 2.0				*/


#define SECSIZ	128		/*  CP/M sector size for fill  		*/
#define CTLZ 	'Z' - 0x40	/*  Fill character for text files	*/ 

int cflag = 0;			/*  CP/M binary file			*/
int tflag = 0;			/*  CP/M text file			*/
int uflag = 0;			/*  Unix file				*/
int iflag = 0;			/*  ITS Binary file			*/

unsigned short crck();		/*  CRC calculation function		*/


main(argc, argv)
int argc;
char **argv;
{
	int err = 0;

	if ((++argv)[0][0] == '-') {
		switch (argv[0][1]) {
		  case 't':
			++tflag;
			break;
		  case 'c': 
			++cflag;
			break;
		  case 'u':
			++uflag;
			break;
		  case 'i':
			++iflag; ++cflag;
			break;
		  default:
			goto usage;
		}
		--argc; ++argv;
	}
	else  /* for right now, "Unix mode" is the default */
		++uflag;

	if (isatty(2)) {  /*  print header if not in a pipe  */
		printf("CRCK program for Unix\n");
		printf("Version %1d.%1d\n\n", VERSION/10, VERSION%10);
		if (iflag) printf("ITS mode selected\n");
		if (tflag) printf("CP/M text mode selected\n");
		if (cflag) printf("CP/M \".COM\" mode selected\n");
		if (uflag) printf("Unix file mode selected\n");
		putchar('\n');
	}

	if (argc == 1 ) {  /*  we're just taking stdin from somewhere  */
		docrck("stdin");
		exit(0);
	}
	else {  /*  we're doing a named file or a list of same  */
		for (; --argc; ++argv) {
			err = 0;
			if (freopen(*argv, "r", stdin) == NULL) {
				perror(*argv);
				++err;
			}
			if (!err)
				docrck(*argv);
		}
		exit(0);
	}

usage:
	fprintf(stderr, "Usage: crck [-t|c|u|i] [filename ... ]\n");
	exit(-100);
}

docrck(name) /* Calculate and print a "CRCK" for stdin stream. */
char *name;
{
	int icount;
	long nsec;
	unsigned short oldcrck;
	unsigned char crbuf[SECSIZ];
	unsigned char *bufend = crbuf + SECSIZ;

	register c;
	register unsigned char *cp;

	oldcrck = 0;
	nsec = 0L;
	cp = crbuf;
	if (tflag) {
	    /*
   	     *  To calculate CRC for CP/M text files that are
	     *  presently stored in the form of Unix text files, 
	     *  an ASCII CR must be inserted in the buffer before 
             *  each '\n'.  Then the file must  be padded out to 
	     *  an even SECSIZ boundary with CTLZ's.
	     */
            while ((c = getchar()) != EOF) {
		if (((*cp++ = c) == '\n') && tflag) {  /*  need to add \r */
			*(cp - 1) = '\r';
			if (cp >= bufend) {  /* sector boundary */
				oldcrck = crck(crbuf, SECSIZ, oldcrck);
				++nsec;
				cp = crbuf;
			}
			*cp++ = '\n';
		}
		if (cp >= bufend) {  /* sector boundary */
			oldcrck = crck(crbuf, SECSIZ, oldcrck);
			++nsec;
			cp = crbuf;
		}
	    }

	    if (cp != crbuf) { /* EOF during partial sector  */
		register fillcnt;

		/*  Fill from current pointer pos to end of buffer  */
		if (tflag)
			for (fillcnt = bufend - cp; fillcnt--; *cp++ = CTLZ)
					;

		/*  Need to update CRC for final sector. */
		oldcrck = crck(crbuf, cp-crbuf, oldcrck);
		++nsec;
	    }
	}
	else {  /* uflag or cflag  -- do a quick crck  */
	    unsigned char tbuf[BUFSIZ];
	    int count = 0;
	    
	    if (iflag) /* skip first four bytes of ITS binary file */
		for (icount = 0; 
		    (c = getchar()) != EOF && icount < 4;
		    icount++)
			;
	    while ((count = fread(tbuf, 1, BUFSIZ, stdin)) > 0) {
		oldcrck = crck(tbuf, count, oldcrck);
		if (!cflag)
			nsec += count;  /* here nsec is for BYTES  */
		else
			nsec += ((count + SECSIZ - 1) / SECSIZ);
	    }
	}

	printf("%-18s", name);
	printf(" (%7ld %-7s)   ==>   %04x\n", 
		nsec, uflag ? "bytes" : "sectors", oldcrck);
}

unsigned short
crck(crbuf, count, ldcrc)  /* uses algorithm of CRCK.COM prior to V5.0 */
register unsigned char *crbuf;
register count;
register unsigned short ldcrc;
{
/*
 *  This routine copyright (c) 1983, Computer Development, Inc., 
 *  Beaverton, OR, USA.
 *  All rights reserved.
 */
	register unsigned short n;

	while (--count >= 0) {
		n = (ldcrc << 1);
		n = (n & 0xff00) | (( n + *crbuf++ ) & 0x00ff);
		if(ldcrc & 0x8000)
			n ^= 0xa097;
		ldcrc = n;
	}
	return(ldcrc);		
}