💾 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.
⬅️ 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=='