💾 Archived View for mirrors.apple2.org.za › archive › ground.icaen.uiowa.edu › unix › Bsc.v11 › bsc.… captured on 2024-07-09 at 04:50:21.

View Raw

More Information

⬅️ Previous capture (2023-01-29)

-=-=-=-=-=-=-

/*
 * Program: bsc.c
 * Version: 1.1
 * Author:  Neil Parker
 * Files:   bsc.c     (source code)
 *	    crc.h     (CRC generation routine)
 *	    pftypes.h (ProDOS file types)
 * Purpose: Create BinSCII files on UNIX or VMS
 *
 * This program is freeware.  It comes with no warranty whatsoever; use
 * at your own risk.
 *
 * Version 1.1: 21 Feb 1992
 *      Added -n (name), -d (debug), and -b (binary) flags.
 *      Added support for VMS (it's still icky, but it works after a fashion).
 *      Increased ugliness coefficient of source code :-).
 *
 * Version 1.0: 14 Mar 1991
 *      Original release--UNIX only.
 */
#include <stdio.h>

#if defined(STRING)||defined(vms)
#include <string.h>
#define rindex strrchr
#else
#include <strings.h>
#endif

#if defined(MEMCPY)||defined(vms)
#ifdef MEMORY_H
#include <memory.h>
#endif
#define bcopy(from,to,length) memcpy((to),(from),(length))
#define bzero(buf,length) memset((buf),0,(length))
#endif

#include <sys/types.h>
#include <sys/stat.h>
#include <time.h>
#include <ctype.h>
#include "crc.h"
#include "pftypes.h"

unsigned int getnumber(),getftype();

unsigned char inbuf[49],   /* buffer for reading from input */
	      header[28];  /* buffer for BinSCII header */

char outbuf[65],           /* buffer for coded output lines */
     binsciifile[256],     /* buffer for ProDOS filename in header */
     *infile,              /* name of input file */
     outfile[256],         /* name of current output file */
     pname[16],            /* ProDOS file name for BinSCII header */
#ifndef vms
     *progname,            /* program name, for error messages */
#endif
     *filestart="FiLeStArTfIlEsTaRt", /* BinSCII intro marker */
     *alphabet=            /* code alphabet */
     "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789()";

unsigned int crc,          /* running CRC counter */
	     processed,    /* number of bytes already processed */
	     remaining,    /* number of bytes yet to be processed */
	     auxtype=0,    /* ProDOS aux type (default=0) */
	     access=0xc3,  /* ProDOS access mode (default=dnwr) */
	     filetype=6,   /* ProDOS file type (default=bin) */
	     storagetype,  /* ProDOS file storage type */
	     blocks,       /* number of blocks in ProDOS file */
	     seqno,        /* output file sequence number */
	     segsplit=0,   /* output segment split count (default=0) */
	     segno,        /* output segment number */
#if defined(FOPEN_NEEDS_B)||defined(vms)
	     bflag=0,      /* set to 1 if -b flag found */
#endif
	     nflag=0,      /* set to 1 if -n flag found */
	     dflag=0;      /* set to 1 if -d flag found */

long filesize,             /* input file size */
     segmentlen;           /* count of bytes remaining in current segment */

FILE *fi,                  /* input file pointer */
     *fo;                  /* current output file pointer */

main(argc,argv)
int argc;
char **argv;
{
	char *c;
	unsigned char *uc;
	unsigned int numread;
	long lastbyte;
	struct stat statbuf;
	struct tm *timebuf;

#ifndef vms

/* get program so we can print it in usage() and help() */

	if((progname=rindex(*argv,'/'))==NULL)
		progname= *argv;
	else
		progname++;

#endif

/* get and interpret command-line arguments */

	if(argc<2)
		usage();

	while(argc-- >1) {
		argv++;
		if(**argv=='-') {
			switch(argv[0][1]) {

			case 'h': /* help */
				help(); /* doesn't return */

			case 't': /* type */
				c= *argv+2;
				if(*c=='\0') {
					argv++;
					argc--;
					if(argc<1)
						usage();
					c= *argv;
				}
				filetype=getftype(c);
				break;

			case 'a': /* auxtype */
				c= *argv+2;
				if(*c=='\0') {
					argv++;
					argc--;
					if(argc<1)
						usage();
					c= *argv;
				}
				auxtype=getnumber(c);
				break;
			
			case 's': /* segments/file */
				c= *argv+2;
				if(*c=='\0') {
					argv++;
					argc--;
					if(argc<1)
						usage();
					c= *argv;
				}
				segsplit=getnumber(c);
				segsplit=segsplit==0?32767:segsplit;
				break;

			case 'n': /* ProDOS file name */
				c= *argv+2;
				if(*c=='\0') {
					argv++;
					argc--;
					if(argc<1)
						usage();
					c= *argv;
				}
				strncpy(pname,c,15);
				pname[15]='\0'; /* just to be sure */
				nflag++;
				break;

#if defined(FOPEN_NEEDS_B)||defined(vms)
			case 'b': /* treat input as binary */
				bflag++;
				break;
#endif

			case 'm': /* access mode */
				c= *argv+2;
				if(*c=='\0') {
					argv++;
					argc--;
					if(argc<1)
						usage();
					c= *argv;
				}
				access=0;
				while(*c!='\0') {
					switch(*c) {
					case 'd':
						access|=0x80;
						break;
					case 'n':
						access|=0x40;
						break;
					case 'b':
						access|=0x20;
						break;
					case 'i':
						access|=0x04;
						break;
					case 'w':
						access|=0x02;
						break;
					case 'r':
						access|=0x01;
						break;
					default:
						usage();
					}
					c++;
				}
				break;

			case 'd': /* debugging */
				dflag++;
				break;

			default:
				usage();

			} /* END switch(argv[0][1]) */

			if(argc<2) /* filename must follow options */
				usage();

		} else { /* **argv!='-' */

			infile= *argv;
			if(argc>1) /* filename must be last arg */
				usage();

		} /* END if(**argv=='-') */

	} /* END while(argc-->1) */

	if(!nflag) { /* If no -n flag, take ProDOS name from input file */

#ifdef vms

/* Extract file name portion from VMS file spec */

		if((uc=rindex(infile,':'))==NULL) /* delete NODE::DEV: */
			uc=infile;
		else
			uc++;

		if((c=rindex(uc,']'))==NULL) /* delete [dir] */
			c=uc;
		else
			c++;

#else /* not VMS */

/* Extract tail from UNIX file name */

		if((c=rindex(infile,'/'))==NULL) /* delete dirs */
			c=infile;
		else
			c++;

#endif /* VMS */

		strncpy(pname,c,15);
		pname[15]='\0'; /* just to be sure */
	} /* if (!nflag) */

	if(dflag)
		printf("*** Using ProDOS filename %s\n",pname);

	strcpy(&binsciifile[1],pname);
	c=binsciifile+1;

	if(!isalpha(*c)) /* initial invalid char -> 'X' */
		*c='X';
	for(;*c!='\0';c++)
		if(isalnum(*c)||*c=='.') {
			if(islower(*c))        /* upshift lowercase */
				*c=toupper(*c);
		} else                         /* invalid char -> '.' */
			*c='.';

	c=binsciifile+1;
	if(*c=='\0') {
		fprintf(stderr,"empty ProDOS file name\n");
		exit(1);
	}
	c[15]='\0';  /* truncate name at 15 chars */
	binsciifile[0]=alphabet[strlen(c)-1]; /* encode name length */
	strcat(c,"                "); /* 16 spaces */
	c[15]='\0';  /* pad with spaces, and truncate to 15 again */

/* Make sure input file exists, and get its size and dates */

#ifdef vms
	vms_stat(infile,&statbuf);
#else
	if(stat(infile,&statbuf)<0) {
		perror("getting input file status");
		exit(1);
	}
#endif

	if(dflag)
		printf("*** File=%s, Size=%d\n",infile,statbuf.st_size);

	filesize=statbuf.st_size;
	lastbyte=filesize-1;
	if(filesize>16777216L) { /* ProDOS can't handle files >16Mb */
		fprintf(stderr,"Input file too big for ProDOS\n");
		exit(1);
	}

	if(filesize>131072L) {
		storagetype=3; /* tree */
		blocks=3+lastbyte/131072+lastbyte/512;
	} else if(filesize>512L) {
		storagetype=2; /* sapling */
		blocks=2+lastbyte/512;
	} else {
		storagetype=1; /* seedling */
		blocks=1;
	}

/* Get file times into BinSCII header */

	timebuf=localtime(&statbuf.st_ctime);
	timebuf->tm_mon+=1; /* UNIX month is 0-11, ProDOS is 1-12 */
	header[13]=timebuf->tm_mday|(timebuf->tm_mon<<5); /* create date */
	header[14]=(timebuf->tm_mon>>3)|(timebuf->tm_year<<1);
	header[15]=timebuf->tm_min; /* create time */
	header[16]=timebuf->tm_hour;

	timebuf=localtime(&statbuf.st_mtime);
	timebuf->tm_mon+=1;
	header[17]=timebuf->tm_mday|(timebuf->tm_mon<<5); /* mod date */
	header[18]=(timebuf->tm_mon>>3)|(timebuf->tm_year<<1);
	header[19]=timebuf->tm_min; /* mod time */
	header[20]=timebuf->tm_hour;

/* Fill out remaining constant portion of header */

	header[0]=filesize&0xff;
	header[1]=(filesize>>8)&0xff;
	header[2]=(filesize>>16)&0xff;

	header[6]=access;

	header[7]=filetype;

	header[8]=auxtype&0xff;
	header[9]=(auxtype>>8)&0xff;

	header[10]=storagetype;

	header[11]=blocks&0xff;
	header[12]=(blocks>>8)&0xff;

	header[26]=0;

/* open input file; initialize counters */

#if defined(FOPEN_NEEDS_B)||defined(vms)
	if((fi=fopen(infile,bflag?"rb":"r"))==NULL) {
#else
	if((fi=fopen(infile,"r"))==NULL) {
#endif
		perror("opening input file");
		exit(1);
	}

#ifdef vms
	if((c=rindex(infile,';'))!=NULL) /* Delete version num */
		*c='\0';
#endif

	processed=0;
	remaining=filesize;
	segno=0;
	seqno=0;

	while(remaining>0) { /* loop until nothing left to process */

		segmentlen=remaining>0x3000?0x3000:remaining;
		     /* max segment size is 0x3000 */
		
		if(dflag)
			printf("*** Seg=%d, Len=%d, Rem=%d\n",seqno,
			       segmentlen,remaining);

/* see if new output file should be created--if so, form name and create
   it */

		if(segno==0) {
#ifdef vms
			sprintf(outfile,"%s-%d",infile,seqno++);
#else
			sprintf(outfile,"%s.%d",infile,seqno++);
#endif
			if((fo=fopen(outfile,"w"))==NULL) {
				sprintf(outbuf,
					"opening output file %s",outfile);
				perror(outbuf);
				exit(1);
			}
			if(dflag)
				printf("*** Outfile=%s\n",outfile);
		}

/* write headers to new file */

		fprintf(fo,"%s\n",filestart);
		fprintf(fo,"%s\n",alphabet);
		fprintf(fo,"%s",binsciifile);

/* set up variable part of header */

		header[3]=processed&0xff; /* start addr of this seg */
		header[4]=(processed>>8)&0xff;
		header[5]=(processed>>16)&0xff;

		header[21]=segmentlen&0xff; /* length of this seg */
		header[22]=(segmentlen>>8)&0xff;
		header[23]=(segmentlen>>16)&0xff;

/* compute header CRC */

		crc=0;
		for(uc=header;uc!=header+24;uc++)
			crc=updcrc(*uc,crc);

		header[24]=crc&0xff;
		header[25]=(crc>>8)&0xff;

/* code header and send it on its way */

		encode(header,outbuf,27);
		fprintf(fo,"%s\n",outbuf);

/* start working on segment data */

		crc=0;
		while(segmentlen>0) { /* loop until end of seg */

			bzero(inbuf,48); /* zero buffer in case there's
			     not enough data to fill it */

/* get 48 bytes (or less if at end) from file */

			if((fread(inbuf,1,48,fi))<0) {
				perror("reading input file");
				exit(0);
			}

/* update number of bytes read */
/* (I tried to do this using the return value from fread(), but VMS choked
   on it.) */

			numread=filesize-processed;
			if(numread>48)
				numread=48;

/* update CRC for this line */

			for(uc=inbuf;uc!=inbuf+48;uc++)
				crc=updcrc(*uc,crc);

/* code the line and send it on its way */

			encode(inbuf,outbuf,48);
			fprintf(fo,"%s\n",outbuf);

			segmentlen-=numread;
			remaining-=numread;
			processed+=numread;
		} /* END while(segmentlen>0) */

/* end of segment--code and write the CRC */

		inbuf[0]=crc&0xff;
		inbuf[1]=(crc>>8)&0xff;
		inbuf[2]=0;

		encode(inbuf,outbuf,3);
		fprintf(fo,"%s\n",outbuf);

/* if end of output file, close it and prepare for next output file */

		if(++segno==segsplit) {
			segno=0;
			fclose(fo);
		}

	} /* END while(remaining>0) */

/* all done--close and exit */

	if(segno!=0)
		fclose(fo);
	fclose(fi);
	exit(0);
}

/* subroutine to encode a line of data */

encode(input,output,length)
unsigned char *input;
char *output;
int length;
{
	int i;
	char *o;

	o=output;
	for(i=0;i<length;i+=3) {
		*o++ =alphabet[input[i+2]&0x3f];
		*o++ =alphabet[((input[i+2]>>6)|(input[i+1]<<2))&0x3f];
		*o++ =alphabet[((input[i+1]>>4)|(input[i]<<4))&0x3f];
		*o++ =alphabet[input[i]>>2];
	}
	*o='\0';
}

/* subroutine to get a file type from the command line */

unsigned int getftype(c)
char *c;
{
	int i;
	unsigned int retcode;

	for(i=0;i<FTYPESIZE;i++) /* loop over file types */
		if(!strcmp(c,filetypes[i].name)) {
			retcode=filetypes[i].code;
			return retcode&0xff;
		}
	return getnumber(c)&0xff; /* if not found, try to parse a number */
}

/* subroutine to get a decimal, hex, or octal number from command line */

unsigned int getnumber(c)
char *c;
{
	unsigned int i;

	if(*c=='


) { /* $nn -> hex */
		sscanf(c+1,"%x",&i);
		return i;
	}
	if(!isdigit(*c))
		usage();
	if(*c=='0')
		if(c[1]=='x'||c[1]=='X')
			sscanf(c+2,"%x",&i); /* 0xnn or 0Xnn -> hex */
		else
			sscanf(c,"%o",&i);   /* 0nn -> octal */
	else
		sscanf(c,"%d",&i);           /* nn -> decimal */
	return i;
}

#ifdef vms

usage()
{
	fprintf(stderr,
    "args: [-t<type>] [-a<aux>] [-m<mode>] [-n<name>] [-s<num>] [-b] <file>\n");
	fprintf(stderr,"      -h     (for help)\n");
	exit(1);
}

#else

usage()
{
#ifdef FOPEN_NEEDS_B
	fprintf(stderr,
 "usage: %s [-t<type>] [-a<aux>] [-m<mode>] [-n<name>] [-s<num>] [-b] <file>\n",
		progname);
#else
	fprintf(stderr,
 "usage: %s [-t <type>] [-a <aux>] [-m <mode>] [-n <name>] [-s <num>] <file>\n",
		progname);
#endif
	fprintf(stderr,"       %s -h     (for help)\n",progname);
	exit(1);
}

#endif

help()
{
	printf("BSC Version 1.1 by Neil Parker -- 22 Feb 1992\n");
#ifdef vms
	printf("Usage: BSC [<args>] <file>\n");
#else
	printf("Usage: %s [<args>] <file>\n",progname);
#endif
	printf("args: -t <type> = set ProDOS file type to <type>\n");
	printf("      -a <aux>  = set ProDOS aux type to <aux>\n");
	printf("      -m <mode> = set ProDOS access mode--<mode> may be any\n");
	printf("                  combination of d, n, b, i, w, or r\n");
	printf("      -n <name> = set ProDOS file name to <name>\n");
	printf("      -s <num>  = set number of segs/output file to <num>\n");
#if defined(FOPEN_NEEDS_B)||defined(vms)
	printf("      -b        = treat input file as binary file\n");
#endif
	printf("      -h        = print this help message\n");
	exit(0);
}

#ifdef vms
#include <fab.h>

/* The following piece of stupidity is intended to correct for the fact that
   the VAX C RTL's idea of file length doesn't always match the number of
   bytes that can actually be read from the file with fgets() or fread().
   If we don't make this correction, we may get extra garbage at the end of
   the output file. */

/* Unfortunately, I know of no way to do this except by brute force (and even
   that doesn't always work).  Why, why, WHY does VMS have to make life so
   difficult? */

vms_stat(filename,statbuf)
char *filename;
struct stat *statbuf;
{
	FILE *fp;
	char buf[512];
	unsigned long size=0;

/* get file stats */

	if(stat(filename,statbuf)<0) {
		perror("getting input file info");
		exit(1);
	}

/* if records are stream_CR or stream_LF, size is OK
   if -b flag was given, size is probably not OK, but we can't fix it */

	if(bflag||statbuf->st_fab_rfm==FAB$C_STMCR
	  ||statbuf->st_fab_rfm==FAB$C_STMLF)
		return;
	
/* open and read file, totalling up number of chars */

	if((fp=fopen(filename,"r"))==(FILE *)NULL) {
		perror("opening infile for size calc");
		exit(1);
	}

	while(fgets(buf,512,fp)!=(char *)NULL)
		size+=strlen(buf);

	fclose(fp);

/* fix size in status buffer */

	statbuf->st_size=size;
}

#endif