/* * 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 #if defined(STRING)||defined(vms) #include #define rindex strrchr #else #include #endif #if defined(MEMCPY)||defined(vms) #ifdef MEMORY_H #include #endif #define bcopy(from,to,length) memcpy((to),(from),(length)) #define bzero(buf,length) memset((buf),0,(length)) #endif #include #include #include #include #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>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 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] [-a] [-m] [-n] [-s] [-b] \n"); fprintf(stderr," -h (for help)\n"); exit(1); } #else usage() { #ifdef FOPEN_NEEDS_B fprintf(stderr, "usage: %s [-t] [-a] [-m] [-n] [-s] [-b] \n", progname); #else fprintf(stderr, "usage: %s [-t ] [-a ] [-m ] [-n ] [-s ] \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 [] \n"); #else printf("Usage: %s [] \n",progname); #endif printf("args: -t = set ProDOS file type to \n"); printf(" -a = set ProDOS aux type to \n"); printf(" -m = set ProDOS access mode-- may be any\n"); printf(" combination of d, n, b, i, w, or r\n"); printf(" -n = set ProDOS file name to \n"); printf(" -s = set number of segs/output file to \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 /* 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