/************************************************************************/ /* File KDISK.C - Disk & File Handling Support for Kermit. Chris Kennington 5th July 1985. */ #include "stdio.h" #ifdef MPUZ80 /* 480Z */ #ifndef TINY #include "io.h" #endif #else /* Nimbus */ /* Layout of FCB (as in ): */ struct fcb { char f_driv; char f_name[8]; char f_type[3]; char f_ext; char f_resv[2]; char f_rc; char f_sydx[16]; char f_cr; unsigned f_record; char f_overfl; }; /* displacements in block returned by Find Match/Next File:- */ #define MCHATTRIB 21 #define MCHNAME 30 /* workspace for MCH* */ static char dmabuf[65]; #endif #include "ctype.h" #define DEFS2 1 #define DEFS5 1 #include "b:kext.h" #define DMA 0x0080 /* use default DMA */ /* The routines in this file provide OS-independent support on Aztec-C for the disk-maintenance functions: CHAnge (reset all drives) DIR dname/dfname ERAse dfname (DEL is synonym) HELP (print help-text; ? is synonym) REName fname fname TYPe dfname UPAth (show environment) USEr num (CP/M only) [DRIve] dname (only first 3 chars need be entered) where the valid arguments are:- dname - a valid disk-letter; fname - a valid file-name (on default disk); dfname - a valid file-name with optional disk-letter; num - a valid user-number. Standard Return-Code Byte: bit 0 - file not found bit 1 - filename invalid bit 2 - disk-letter invalid bit 3 - system file bit 4 - file is read/only bit 5 - disk is temporarily read-only (CP/M) bit 6 - wildcards in name bit 7 - directory-name (MSDOS) */ /* static variables for intercommunication */ static char *s1, *s2; /* => names 1 & 2 */ static char cmd; /* command-number */ static char *cmds[] = { /* valid commands */ "Nul", "DIR", "ERA", "REN", /* 0-3 (0 is unused) */ "TYP", "USE", "UPA", "DRI", /* 4-7 */ "DEL", "CHA", "HEL", /* 8-10 */ 0}; /* zero to terminate */ static char allfiles[] = "*.*"; static char diskro[] = "(disk r/o) "; static char exists[] = " <%s> already exists, "; static char explicit[15]; /* new filename */ static char inval[] = "invalid"; static char invalid[] = " <%s> now invalid "; static char notdel[] = " NOT deleted."; static char notren[] = " NOT renamed."; static char overw[] = "overwritten "; static char reject[] = "rejecting <%s>."; static char wildcds[] = "has wildcards"; /* working variables */ static char c, cold, rtn, rtnall, *e, *w; /* Displayed text which needs to vary between CP/M & MSDOS */ #ifdef MPUZ80 char diskcmds[] = " CHAnge DIRectory DRIve ERAse REName TYPe USEr UPAth"; char diskfail[] = "Disk failure, CP/M code %d"; char os1[] = "\r The following commands are valid (only 3 letters needed):\r\ CHAnge floppy; DRIve change; DIRectory list; ERAse file(s);"; char os2[] = "\r\ REName file; TYPe file; USEr-number; UPAth (show defaults).\ \r Wildcards (? *) accepted in DIR & ERA."; #else char diskcmds[] = " DELete DIRectory DRIve REName TYPe UPAth"; char diskfail[] = "Disk failure, MSDOS code %d"; char os1[] = "\r The following commands are valid (only 3 letters needed):\r\ DELete file(s); DRIve change; DIRectory list;"; char os2[] = "\r\ REName file; TYPe file; UPAth (show path).\ \r Wildcards (? *) accepted in DIR & DEL."; #endif char *avoid(file) /* implement collison avoidance */ /* If the requested file already exists, queries setting of nmavoid and acts accordingly; Returns 0 if file not there or may be overwritten, else address of explanatory string. */ char *file; /* file-name */ { static char ret; char c, *r2; int len; keyget(&c); /* trap ESC */ ret = filechek(0,file,work); if ( (ret & (char)0x01) != 0 ) return(0); /* no such file */ if ( (ret & (char)0x06) != 0 ) return(invalid); /* bad name */ /* file exists - see what to do about it */ outc(CR); printf(exists,file); switch(nmavoid) { /* break if OK, return if not */ case 0: /* overwrite */ txtout(overw); break; case 1: /* avoid by renaming */ if ( (len = strlen(file) - 1) < 2 ) { len = 2; file[2] = '0'; } if (file[len-1] == '.') { len += 2; file[len] = '0'; } c = file[len]; if (file[len-1] == '$') file[len] = ++c; /* increment last char */ else file[len-1] = '$'; /* or set previous to $ */ file[len+1] = 0; /* ensure closed */ if ( (r2 = avoid(file)) == 0 ) break; /* then check that */ else return(r2); case 2: /* reject incoming file */ return(reject); case 3: /* check with user */ printf("OK if %s",overw); if (confirm()) break; /* Yes */ else return(reject); default: /* unkmown setting */ return(null); } /* end switch */ vtline(LOCFILE,file); /* Make sure BDOS will not prevent overwriting */ if ( (ret & (char)0x30) != 0 ) return("<%s> is read-only "); else return(0); } /* End of avoid() */ char dirprt(name) /* list directory-matches */ /* Leaves expanded name in "explicit" and sets up "ownfcb"; Returns composite status-byte. */ char *name; { char ret; cold = 0; ret = rtn = fileok(name); outfc(CR); if ( (rtn & (char)0x07) != 0 ) return(rtn); /* not found or invalid */ else do { ret |= rtn; for (c=0; c<26; ++c) work[c] = SP; work[c] = 0; w = work; e = explicit; while( (*w = *e++) != 0) ++w; if ( (rtn & (char)0x08) != 0 ) { e = " sys"; while( (*w = *e++) != 0) ++w; } if ( (rtn & (char)0x10) != 0 ) { e = " r/o"; while( (*w = *e++) != 0) ++w; } *w = ','; txtout(work); if (++cold > 2) { /* in 3 columns of 25 ch */ if (outfc(CR) != 0) { txtout("\rList aborted"); return(ret); /* abort list */ } cold = 0; } } while ( ( (rtn = filechek(1,name,explicit)) & (char)0x01) == 0 ) ; return(ret); } /* End of dirprt() */ char filechek(mode,fname,nuname) /* verify disk-file */ /* Returns standard status-byte to give situation of file; sets up ownfcb with expanded wildcard name & checks for it; if file exists, copies explicit name into *nuname */ char mode, *fname, *nuname; { char c, disk, ret, *name; int i, w, attrib; struct fcb *fcb2; bdos(SETDMA,DMA); name = fname; /* validate disk-letter if any */ if (fname[1] == ':') { /* if disk quoted */ disk = fname[0] & 0x5f; fname += 2; /* up to filename */ if ( (disk < 'A') || (disk > 'P') ) return(0x04); /* bad disk-letter */ disk &= 0x3f; /* range 1-16 = A-P */ if (*fname == 0) { /* disk-letter only */ #ifdef MPUZ80 /* simple for CP/M */ fname = allfiles; /* point to *.* */ #else /* whole path needed for MSDOS */ e = fname; w = allfiles; while ( (*e++ = *w++) != 0 ) ; /* copy *.* into name */ #endif } } else disk = 0; ownfcb->f_driv = disk; /* set up drive-code */ /* validate name */ if ( (w = filexpand(fname,ownfcb->f_name)) < 0 ) return(0x02); /* bad name */ ret = (w > 0) ? 0x40 : 0; #ifdef MPUZ80 /* CP/M handling mode += SCHFST; /* search first or next */ if ( (i = bdos(mode,ownfcb)) == 0xff) return(0x01); /* not found */ fcb2 = DMA + (i * 32); name = fcb2->f_name; /* explicit name + bits */ for (i=0; i<11; ++i) { if (i == 8) *nuname++ = '.'; if ( (c = (name[i] & (char)0x7f)) != SP ) *nuname++ = c; } if ( *(nuname-1) == '.' ) --nuname; *nuname = 0; /* close string */ /* check for special conditions */ if ( (fcb2->f_type[0] & (char)0x80) != 0 ) ret |= 0x10; /* file r/o */ if ( (fcb2->f_type[1] & (char)0x80) != 0 ) ret |= (char)0x08; /* system file */ #else /* MSDOS handling */ fname = name; /* retreive original name */ bdos(SETDMA,dmabuf); mode += MCHFST; attrib = 0xffff; /* find all files/dirs */ if (dos(mode,0,attrib,fname,0,0) == -1) return(0x01); name = dmabuf + MCHNAME; /* name found */ attrib = *(dmabuf + MCHATTRIB); /* & its attributes */ while ( (*nuname = *name++) > SP ) ++nuname; *nuname = 0; /* overwrites first space/ctrl */ /* check for special conditions */ if ( (attrib & (char)0x01) != 0 ) ret |= (char)0x10; /* file r/o */ if ( (attrib & (char)0x06) != 0 ) ret |= (char)0x08; /* system file */ if ( (attrib & (char)0x10) != 0 ) ret |= (char)0x80; /* directory */ if ( (attrib & (char)0x08) != 0 ) { w = " (Vol-Id)"; while ( (*nuname++ = *w++) != 0 ) ; } #endif if (disk == 0) disk = bdos(CURDSK,0); /* default disk */ else --disk; /* disk now = 0-15 for A-P */ if (rochek(disk) != 0 ) ret |= (char)0x20; /* disk r/o */ return(ret); /* normal return */ } /* end of filechek() */ char fileok(nam) /* check & print if bad */ /* Leaves ownfcb set up for subsequent routines; Returns standard status-byte. */ char *nam; { char ret; ret = filechek(0,nam,explicit); if ( (ret & (char)0x20) != 0 ) txtout(diskro); if ( (ret & (char)0x06) != 0 ) printf("<%s> %s ",nam,inval); #ifndef MPUZ80 /* only for MSDOS */ else if ( (ret & (char)0x80) != 0 ) printf("(dir) "); #endif else if ( (ret & (char)0x01) != 0 ) printf("<%s> not found ",nam); return(ret); } /* end of fileok() */ filexpand(fname,nuname) /* expand & check filename */ /* returns -1 for error, 0 for OK, +1 for OK with wildcards fname must not have a disk-letter */ char *fname, *nuname; /* old & new names */ { char c, wild; int i, lim; for (i=0; i<11; ++i) /* blanx to nuname */ nuname[i] = SP; nuname[11] = 0; wild = 0; lim = 8; /* first part */ i = 0; while (i < 11) switch (c = *fname++) { case '?': /* wildcard */ wild = 1; if (i < lim) nuname[i++] = c; /* stored */ else return(-1); break; case '*': /* multi-wildcard */ wild = 1; while (i < lim) nuname[i++] = '?'; /* stored */ break; case '.': /* separator */ if ( (i == 0) || (lim == 11) ) /* leading dot or */ return(-1); /* second dot - invalid */ else { i = 8; lim = 11; } break; case 0: /* end-of-string */ if (i == 0) return(-1); /* null name */ --fname; i = 11; break; default: /* all others */ if ( ( (isalnum(c) == 0) && (c != '$') ) /* bad char */ || (i >= lim) ) /* too many chars */ return(-1); else nuname[i++] = c; /* stored */ break; } /* end switch, while */ if (*fname != 0) /* surplus char(s) */ wild = -1; return(wild); } /* end of filexpand() */ findcmd(osl) /* structure user input */ /* Sets up cmd with cmd-no, s1 & s2 => names (zero-terminated); Returns number of valid items, 0 - 3; -1 if string is null; (If command invalid, returns 0 with s2 => invalid string). */ char *osl; { static char *ospm[4]; char c; int i; cmd = 4; while (cmd-- > 0) /* clear ospm[] */ ospm[cmd] = 0; /* (leaves cmd = 0) */ decol8(osl,ospm,4); if ( (s1=ospm[0]) == 0) /* null goes back to Kermit */ return(-1); if (s1[1] == 0) switch (*s1) { /* 1-letter inputs */ case 'Q': kermkill(0); case 'K': return(-1); case '?': cmd = 10; return(1); default: return(0); } if ( (s1[1] == ':') && (s1[2] == 0) ) { /* disk-letter */ cmd = 7; return(1); } /* identify command */ i = 0; while ( (s2 = cmds[++i]) != 0 ) { s1 = ospm[0]; while ( ( (c = *s2) == *s1) && (c != 0) ) { /* match chars */ ++s1; ++s2; } if (c == 0) { /* whole command */ cmd = (char)i; break; } } if (cmd == 0) { s1 = 0; s2 = ospm[0]; return(0); /* no match */ } /* set up statics & return with count */ s1 = ospm[1]; if (*(s2 = ospm[2]) == '=') s2 = ospm[3]; return(c); } /* end of findcmd() */ char osaction() /* take action on disk/os command */ /* Get cmdline from user and take action on it; on null input return 0, else 1. */ { static FILE *tfp; int ic, disk, user; char d; txtout(oprompt); keyline(osline); upper(osline); scrline = 0; /* clear paging */ if (findcmd(osline) < 0) /* set up cmd, s1, s2 */ return(0); /* exit on null line */ if (s1 == 0) s1 = allfiles; if (s2 == 0) s2 = allfiles; switch (cmd) { case 0: /* Null command */ printmsg(" <%s> - Not a valid command.",s2); break; case 1: /* DIR */ printf(" %s Directory for %s - ", userpath(),s1); dirprt(s1); break; case 2: /* ERA */ case 8: /* DEL */ printmsg(" DELete command: "); if ( (dirprt(s1) & (char)0x30) != 0 ) { outc(CR); printf(" R/O -%s",notdel); break; } /* seek confirmation before deleting */ txtout("\r OK to delete"); if (confirm() == 0) { printf(" Files%s",notdel); break; } outc(CR); printf(" Deleting %s: ",s1); if (bdos(DELFIL,ownfcb) == (char)0x0ff ) printf("failed;%s",notdel); else txtout("Files deleted."); break; case 3: /* REN */ #ifndef MPUZ80 /* CP/M renames A <= B but MSDOS renames A => B, so switch names for MSDOS */ w = s1; s1 = s2; s2 = w; #endif printf(" RENaming %s to %s; ",s2,s1); if ( (s1[1] == ':') || (s2[1] == ':') ) { printf("Omit driveletter;%s",notren); break; } /* check new (first) name */ if ( (rtn = filexpand(s1,work)) != 0 ) { printf("%s %s; %s", s1, (rtn!=1) ? inval : wildcds, notren); break; } rtn = filechek(0,s1,explicit); if ( (rtn & (char)0x01) == 0 ) { printf("%s exists -%s",s1,notren); break; } /* check old (second) name */ if ( (rtn = filexpand(s2,explicit)) != 0 ) { printf("%s %s; %s", s2, (rtn!=1) ? inval : wildcds, notren); break; } if ( ( (rtn = fileok(s2)) & (char)0x37 ) != 0) { if ( (rtn & (char)0x10) != 0 ) txtout("R/O"); txtout(notren); break; } if ( (rtn & (char)0x08) != 0 ) txtout("(sys) "); /* move second name into fcb + 16 */ e = &(ownfcb->f_sydx); *e++ = 0; /* dummy disk */ w = work; user = 12; while (user-- != 0) *e++ = *w++; /* new name */ if (bdos(RENFIL,ownfcb) == (char)0xff) txtout(" Rename FAILED."); else txtout(" Renamed OK."); break; case 4: /* TYPE */ txtout(" "); if ( (fileok(s1) & (char)0x07) != 0) break; printf("TYPing file <%s>:\r\r", explicit); if ( (tfp = fopen(s1,"r")) == 0 ) txtout(" File inaccessible."); else while ( (ic = getc(tfp)) != EOF ) { c = ic; if (c == CTLZ) break; /* CP/M EOF */ if (outfc(ascch(c)) != 0) break; } outfc(CR); if (c == CTLZ) printf("End of File <%s>.\r",s1); fclose(tfp); break; #ifdef MPUZ80 case 5: /* USER (CP/M only) */ if ( (user = atoi(s1)) > 15 ) txtout(" User > 15 not valid"); else { bdos(GETUSR,user); printmsg(" Now: %s", userpath()); } break; #endif case 6: /* UPATH */ printf(" Environment: %s ",userpath()); break; case 7: /* DRIVE */ txtout(" DRIVE change: "); disk = (*s1 & 0x5f) - 'A'; if ( (disk < 0) || (disk > 15) ) txtout("Only A-P are valid disks "); else { bdos(SELDSK,disk); txtout(userpath()); if (rochek(disk) != 0) txtout(diskro); } break; #ifdef MPUZ80 case 9: /* CHAnge disks (CP/M only) */ bdos(SELDSK,0); /* if not on drive A, r/o not permanently removed */ printmsg(" Back on %s; Change any disk & hit RETURN ",userpath()); while (kbdin() == 0) ; bdos(RSTDRV,0xffff); break; #endif case 10: /* HELP or ? */ txtout(os1); txtout(os2); break; default: txtout("???"); break; } /* end switch */ return(1); /* normal return */ } /* end of osaction() */ rochek(d) /* check for disk r/o */ /* returns NZ if disk d is r/o */ int d; /* disknumber 0-15 */ { if (d > 15) return(-1); else #ifdef MPUZ80 return( bdoshl(GETROV,0) & (0x0001 << d) ); #else /* no-op under MSDOS */ return(0); #endif } /* end of rochek() */ char *userpath() /* return environment */ /* returns address of a string of about 20 chars containing user-number & disk etc. */ { int i; char disk, user; disk = bdos(CURDSK,0) + 'A'; #ifdef MPUZ80 /* CP/M - return disk & user-no */ user = bdos(GETUSR,0xff); sprintf(work,"User %d, drive %c: ", user, disk); #else /* MSDOS - reutrn disk only (pathname doesnt work) */ sprintf(work,"Drive %c: ", disk); #endif return(work); } /* end of userpath() */ CHAR wildex(wild,line) /* expand wildcard names */ /* Permits use of same workspace for two parameters, but "line" must be at least 85 chars long. Returns count of files expanded, with 80-bit set if still more on disk. */ char *wild, *line; { char count = 0, dsk = 0, ic = 0; char local[14], *last, *next; if (wild[1] == ':') /* disk letter */ dsk = *wild; next = local; while ( (*next++ = *wild++) != 0 ) ; last = next = line; if (dsk != 0) { /* disk-letter to first */ *(++next) = dsk; *(++next) = ':'; } if ( (filechek(0,local,++next) & (char)0x01) == 0 ) do { if ( (++count > 8) || (ic > 70) ) return(count + (char)0x7f); /* count & 0x80 */ *last = SP; /* terminator => separator */ while ( *(++next) != 0 ) ++ic; last = next++; if (dsk != 0) { /* copy disk-letter to next */ *next++ = dsk; *next++ = ':'; } } while ( (filechek(1,local,next) & (char)0x01) == 0 ) ; /* this copies next explixcit name onto line */ else *line = 0; /* null line */ return(count); } /* end of wildex() */ /***************** End of file KDISK.C ************************/