
/************************************************************************/

/* 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 <io.h>):	*/
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  ************************/


