/* cpm.c : i read/write/directory cpm standard format 8" diskettes */ /* edit history * * pf01 27jan85 invent xoutput() being generalised output() suitable for * binary output, then implement -x binary output command * pf02 28may85 add cputdir() to two "no more room on diskette" errors * so user gets at least what we DID write to diskette * recorded in directory of diskette. One fix each in newext() * and xoutput(). */ /*)BUILD $(PROGRAM) = cpm $(FILES) = { cpm } $(LIBS) = { lb:[1,1]cx/lb } $(TKBOPTIONS) = { TASK = ...CPM UNITS = 8 } */ #include #include #ifdef decus #ifdef rsx #include #include #include #endif #endif /* this is very primitive C so it ports to non-KR Cs like BDS */ struct dirent /* CP/M directory entry */ BEGIN char d_usernumber; /* 0xE5 means empty entry */ #define EMPTY 0xE5 char d_name[8]; char d_type[3]; char d_extent; /* 0:31 */ char d_unknown; char d_cluster; char d_lastrecord; /* 1-org highest sector number in this extent */ /* 0:128 */ char d_block[16]; /* 2:242 */ END ; /* * standard CP/M diskette has: * 77 tracks 0:76 * track 0,1 boot * track 2:76 CP/M space * each track has 26 sectors, used in order : * 0 6 12 18 ... (see cxlate()) * CP/M uses records: record 0 of track is sector 0 * record 1 of track is sector 6 * . * . * . * a block is 1024 bytes = 8 records * hence diskette has 26*75 = 1950 records * 1950/8 = 243 blocks * block 0 & 1 are directory * block 2:242 are user data * within directory, 64 slots, each of 32 bytes * one slot describes one extent * an extent may have 0:16 blocks */ #define ENTSIZ 32 /* sizeof(dirent) */ #define DIRSIZ 64 /* number of directory entries */ struct dirent directory[DIRSIZ]; #define NBLOCKS 243 /* number of blocks on diskette */ char bytmap[NBLOCKS];/* one element is 1 CP/M block 0 means this block is unused 1 : DIRSIZ means it is used by that slot of directory hence identifies which extent it is in */ #define CPMEOF 26 /* this character means 'end of file' */ /* this information comes from user's command line */ char *diskette; /* points to a diskette name */ char wname[11]; /* filename.typ 0 means this char is wild */ int wuser; /* user number mask bit n set means include user n */ /* this comes from wildcarding */ char infilnam[50]; /* print name of inputfile: a string */ #include "oscpm.h" helpless(gripe) char *gripe; BEGIN printf("%s\7\n",gripe); printf("I am CPM: I read, write or directory CP/M 8\" standard diskettes\n"); printf("usage: CPM switch cpm-wild-card-file-spec\n"); printf("switch: -b binary input: gives 128-byte records\n"); printf(" -d directory to standard output\n"); printf(" -e erase file(s) from cpm diskette\n"); printf(" -i input file(s) from cpm diskette\n"); printf(" -l long directory (and block map) to standard output\n"); printf(" -o output file(s) to CPM diskette from current logged-in area\n"); printf(" -x output binary file(s) like -b\n"); printf(" -z zap directory: refresh the disk\n"); printf("cpm-wild-file-spec:\n"); printf(" : { : } { . }\n"); printf("e.g.: DY:5:?foo?bar.x*\n"); printf("user number is assumed to be ALL unless explicitly given\n"); exit(); END main(argc,argv) int argc; char **argv; BEGIN char *p; char *q; char *w; int usrnum; char *errmsg; /* error message from bytmap construction */ int errblk; /* offending block number */ int errslt; /* offending directory slot number */ int wildmake(); /* argument to xoutput() */ int wildnext(); /* argument to xoutput() */ int osget(); /* argument to xoutput() */ int wilbmake(); /* argument to xoutput() */ int wilbnext(); /* argument to xoutput() */ int osbget(); /* argument to xoutput() */ IF (argc!=3) THEN helpless("I need exactly 2 arguments"); FI IF (argv[1][0]!='-') THEN helpless("1st argument must be a switch hence must begin with '-'"); FI IF (!isalpha(argv[1][1])) THEN helpless("2nd character of 1st argument must be a letter viz '-b' '-d' '-e' '-i' '-l' '-o'"); FI IF (strlen(argv[1])!=2) THEN helpless("2nd argument must be exactly '-' followed by a letter"); FI /* parse wild filename */ FOR (p=argv[2]; *p!=':'; p++) DO IF (!*p) THEN helpless("must have ':' after diskette name in 2nd argument"); FI OD *p++ = 0; diskette = argv[2]; FOR (q=p; isdigit(*q); q++) DO OD IF (*q==':') THEN /* we have a user # */ sscanf(p,"%d",&usrnum); IF (usrnum>15) THEN helpless("user number must be less than 16"); FI wuser = 1<d_usernumber&0xFF)); FOR (p=d->d_name,e=p+8; pd_lastrecord&0xFF); printf(" %2d%4d",d->d_extent,xsec); fsec += xsec; b = d->d_block; FOR (e=b+(xsec+7)/8,i=0; bd_lastrecord&0xFF); fsec += xsec; IF (d==(directory+DIRSIZ-1) || cfcomp(d,d+1)) THEN FOR (p=d->d_name,e=p+8; p25) THEN printf("\7CXLATE:secnum=%d.\n",secnum); exit(); FI #endif return("\1\7\15\23\31\5\13\21\27\3\11\17\25\2\10\16\24\32\6\14\22\30\4\12\20\26"[secnum]-1); END cdsort() /* sort directory */ BEGIN int cdcomp(); #ifdef debug struct dirent *d; char *p; char *b; FOR (p=b=directory; p<(b+(ENTSIZ*DIRSIZ)); p++) DO IF (!((p-b)%ENTSIZ)) THEN putchar('\n'); FI printf("%2x",0xFF&*p); OD putchar('\n'); #endif qsort(directory,DIRSIZ,ENTSIZ,cdcomp); #ifdef debug printf("ENTSIZ=%d.\n",ENTSIZ); FOR (d=directory; d0) THEN error("\7qsort failed %d.",d-directory); FI OD #endif END cdcomp(a,b) /* compare CP/M directory entries return -1:0:1 as ab check */ struct dirent *a; struct dirent *b; BEGIN int r; int ax; int bx; r = cfcomp(a,b); IF (!r) THEN ax = a -> d_extent; bx = b -> d_extent; r = ax==bx ? 0 : (axb check */ char *a; char *b; BEGIN char *l; /* limit */ FOR (l=a+12; ad_usernumber&0xFF)!=EMPTY) THEN slot = d - directory; numblks = ((d->d_lastrecord&0xFF)+7) / 8; FOR (b=d->d_block; b<(d->d_block)+numblks; b++) DO blk = (*b)&0xFF; IF (blk<243) THEN IF (bytmap[blk]) THEN *perr="block used twice"; *pslot = slot; *pblk = blk; ELSE bytmap[blk] = slot+1; FI ELSE *perr = "block number too big"; *pslot = slot; *pblk = blk; FI OD FI OD END int cmatch(d) /* TRUE if directory entry matches wild spec */ struct dirent *d; BEGIN char *p; char *w; char *e; /* end */ IF ( (d->d_usernumber&0xFF)>0xF || !((1<<(d->d_usernumber&0xFF))&wuser) ) THEN return(FALSE); FI FOR (p=d->d_name,e=d->d_name+11,w=wname; pd_name; FOR (p=outfilnam,e=q+8; qd_type; FOR (e=q+3; qd_lastrecord&0xFF); FOR (rec=0; !eofseen && recd_block[block]&0xFF)*8 + rib; cgetsec(therec,sector); (*myputsec)(§or,&eofseen); OD OD d--; (*mydone)(); ELSE printf("\7can't create file \"%s\"\n",outfilnam); FI FI OD END delete() /* delete all files that match wild filespec do NOT re-write directory */ BEGIN struct dirent *d; FOR (d=directory; d d_usernumber = EMPTY; FI OD END int cffslot() /* return -1 or 1st free directory slot # "First Free directory SLOT" */ BEGIN struct dirent *d; FOR (d=directory; dd_usernumber&0xFF)==EMPTY) THEN return(d-directory); FI OD return(-1); END struct dirent *newext(ext) /* create new extent for current file */ /* wname is the file name */ /* wuser is the user mask */ /* infilnam is its print name */ int ext; /* extent # */ BEGIN int slot; /* directory slot */ struct dirent *d; int u; int usr; slot = cffslot(); IF (slot<0) THEN printf("\nNo room in directory for extent %d. of file \"%s\"\n",ext,infilnam); cputdir(); /*pf02*/ exit(); FI d = directory + slot; IF (wuser) THEN FOR (usr=0,u=wuser; !(u&1); u>>=1) DO usr++; OD ELSE usr=0; FI d -> d_usernumber = usr; copy(d->d_name, wname, 11); d->d_extent = ext; d->d_unknown = 0; d->d_cluster = 0; d->d_lastrecord = 0; zero(d->d_block,16); return(d); END xoutput(make,next,get) /* output (wild) file(s) to CP/M diskette */ int (*make)(); /* make a wild-file-name template */ int (*next)(); /* open next file matching template, for read */ int (*get) (); /* read next 8-bit char from file */ BEGIN struct dirent *d; char *errmsg; /* error message from bytmap construction */ int errblk; /* offending block number */ int errslt; /* offending directory slot number */ char *s; /* points into sector */ char sector[128]; /* sector built up here */ int arecord; /* record # of the sector we are building in sector */ /* 0 means we haven't built anything yet: don't write anything to diskette */ int extent; /* 0:31 current extent number in file */ int record; /* 0:7 current record number in block */ int block; /* 0:15 current block number in extent */ int ablock; /* block number of block we are writing */ int c; (*make)(); /*pf01*/ WHILE ((*next)()) /*pf01*/ DO /* now have new wname, infilnam */ /* file is opened */ /* delete existing destination file if any */ FOR (d=directory; d d_usernumber=EMPTY; FI OD cbytmap(&errmsg,&errblk,&errslt); /* build byte map */ IF (*errmsg) THEN printf("\7error %s block=%d. directory-slot=%d.\n",errmsg,errblk,errslt); FI /* now we know what free blocks we have */ arecord= record= block= 0; extent = -1; FOR (s=sector; s=0) /*pf01*/ DO IF (s==sector) THEN /* new record begun */ IF (!record) THEN /* new block begun */ IF (!block) THEN /* new extent begun */ d = newext(++extent); FI IF (ablock=cffblock()) THEN d->d_block[block]=ablock; bytmap[ablock] = d-directory+1; ELSE printf("\7No room on diskette for block %d. of extent %d. of file \"%s\"\n",block,extent,infilnam); cputdir(); /*pf02*/ exit(); FI IF ((++block)>15) THEN block=0; FI FI arecord = ablock*8 + record; IF ((++record)>7) THEN record=0; FI d -> d_lastrecord += 1; FI *s = c; IF ((++s)>(sector+127)) THEN cputsec(arecord,sector); FOR (s=sector; s