/* tsxutl.c */

/*
 * TSX+ Utilities
 */

#include <stdio.h>
#include <string.h>

#include "os.h"
#include "tsxutl.h"
#include "rtfile.h"
#include "flinfo.h"
#include "logdsk.h"
#include "detach.h"
#include "prgreq.h"
#include "suspnd.h"

int	execmd   = EXECMDFIL;	/* execute command file */
int	delcmd   = DELCMDFIL;	/* delete  command file */

char	errstr[80];		/* all encompassing error report string */
int	subdnest = SUBDNEST;	/* maximum nesting of subdirectories */
int	subdbase = SUBDBASE;	/* first 'ld' unit */
int	subdlevl = 0;		/* subdirectory level */

static char defdev[6] = { 'd', 'k', ':', 0, 0, 0 };
static char lclname[16];
static char rmtname[16];
static char filename[16];
static FILE *iop = NULL;

static char device[6] = { 'd', 'k', ':', 0, 0, 0 };
static char subnam[8][8];
static char subext[8][4];
static char subfil[16];
static char slash = '/';

static char savpath[94];

char *filtxt[] = {
#if ts$sys
	"TSX-Plus filespec   = {dev:}{name}{.ext}{[xx]}",
#endif
#if rt$sys
	"RT-11 filespec   = {dev:}{name}{.ext}{[xx]}",
#endif
	"  where   {dev:} = 1 to 3 characters  (1st alpha)",
	"          {name} = 1 to 6 characters  (*=all,?=wild,%=wild)",
	"          {.ext} = 1 to 3 characters  (*=all,?=wild,%=wild)",
	"          {[xx]} = file size allocation, -1 = all available",
	"  note:     dev: => dev:*.*   /   * => *.*",
#if ts$sys
	"  subdirectories => dev:/sub1/sub2/.../{name}{.ext}{[xx]} (or \\)",
#endif
	"",
	0
	};

/*
 *  rtusage
 *
 *  printout the filespec data
 */
VOID rtusage()
{
	register char **dp;

	for (dp = filtxt; *dp; dp++) {
		RTPRINT("%s\r\n", *dp);
	}
}

/*
 * Local filespec
 */
char *lclfile(s)
char *s;
{
	strcpy(lclname,rtfile(s,defdev));
	return(lclname);
}

/*
 * Remote filespec
 */
char *rmtfile(s)
char *s;
{
	register char *t,*u;

	/*
	 * discard device spec and any subdirectories
	 */
	t = s;
	while(
		(u = strrchr(t,':')) != NULL ||
		(u = strrchr(t,'/')) != NULL ||
		(u = strrchr(t,'\\')) != NULL
	) { t = ++u; }

	rtparse(t);
	sprintf(rmtname,"%s.%s", rtname(),rtext());
	return(rmtname);
}

/*
 * Open a local file
 *
 * For reads, open file and return file handle
 *
 * For writes, process according to mode and return file handle
 *
 * If the first character of the 'iotype' string is an 'x'
 * then the file length of "[-1]" is added to all 'write' file
 * names to bypass any TSX+ file size limitations.
 *
 *
 * If mode == 0 then just open a new file,
 * overwriting any existing file of the same name.
 *
 * Device access := 'dev:[-1]
 *
 * If mode != 0
 *
 *    If a device write then query user
 *	^C or No: exit with abort.
 *
 *    then if ask != 0
 *	check for file existence,
 *	if exists, ask for a new file name
 *	response:
 *		CR: overwrite the existing file.
 *		filename: loop to check for file existence.
 *		^C: exit with abort.
 *
 *    else:
 *	check for file existence,
 *	if exists, create a sequential new file name
 *	loop to check for file existence.
 *
 */
FILE *rtopen(fname,iotype,mode,ask)
char *fname;
char *iotype;
int mode,ask;
{
	char iname[50],lname[50],temp[50];
	char *p;
	register int i,j,digit,xwrite;
	FILE *fp;

	if((p=setpath(fname)) == NULL) {
		ERRPRINT();
		fp = NULL;
		goto lcerr;
	}

	strcpy(iname,(char *) lclfile(p));
	strcpy(lname,iname);

	if(*iotype == 'x') {
		iotype++;
		xwrite = 1;
	} else {
		xwrite = 0;
	}

	/*
	 * Device Access: 'dev:[-1]'  =>  'dev:.   [65535]'
	 */
	if((p = strchr(iname,':')) != NULL && strneq(p,":.   ",5)) {
		if(streq(p,":.   [65535]")) {
			xwrite = 2;
		} else {
			fp = NULL;
			goto lcerr;
		}
	}

	if(strchr(iotype,'r') != NULL) {
		goto lcread;
	}

	digit = 0;

lcloop:	if(mode) {
		if(xwrite == 2) {
			RTPRINT("Overwrite device '%s:' (y/n) -> ", rtdev());
			if(!RTRESPONSE(temp,49)) {
				if(*temp == 'y' || *temp == 'Y') {
					goto lcwrit;
				}
			}
			fp = -1;
			goto lcerr;
		}
		fp = fopen(lname,"rn");
		if(fp == NULL) {
			if(strcmp(iname,lname)) {
				RTPRINT("File name changed to %s\r\n",
					rmtfile(lname));
			}
			goto lcwrit;
		}
		fclose(fp);
		if(ask) {
			RTPRINT("File %s exists\r\n", rmtfile(lname));
			RTPRINT("Enter a new filename (CR to overwrite) -> ");
			if(RTRESPONSE(temp,49)) {
				fp = -1;
				goto lcerr;
			}
			if(*temp) {
				strcpy(lname,(char *) lclfile(temp));
				goto lcloop;
			}
			goto lcwrit;
		} else {
			sprintf(temp,"%d",digit++);
			i = strlen(temp);
			rtparse(iname);
			p = rtname();
			while((i+(j = strlen(p)))>6) {
				*(p+j-1) = '\0';
			}
			strcat(p,temp);
			strcpy(lname,rtstring());
			goto lcloop;
		}
	}
lcwrit:	if(xwrite == 1) {
		strcat(lname,"[-1]");
	}
lcread:	fp = fopen(lname,iotype);
	if(fp == NULL) {
lcerr:		respath();
	}
	return(fp);
}

/*
 * rtclose
 *
 * close the open file, delete file if of zero length.
 */
int rtclose(fp)
register FILE *fp;
{
	char fname[24];
	register int size,status;

	if(fp == NULL)
		return(0);

	if(fp->_flag & _IOWRT) {
		/*
		 * File open for writing
		 */
		strcpy(fname,fp->io_name);
		status = fclose(fp);
		fp = fopen(fname,"rn");
		if(fp != NULL) {
			size = fp->io_size;
			fclose(fp);
			if(size == 0)
				delete(fname);
		}
	} else {
		/*
		 * File open for reading
		 */
		status = fclose(fp);
	}
	respath();
	return(status);
}

/*
 * Check for wild card matches
 */
char *firstname(st)
char *st;
{
	if((iop = fwild(rtwfile(st,defdev), "r"))==NULL)
		return(NULL);
	return(nextname());
}

/*
 * Get next matching file name
 */
char *nextname()
{
	register char *p,*q;

	if(fnext(iop)!=NULL) {
		q = filename;
		p = iop->io_name;
		do {
			*q++ = tolower(*p);
		} while(*p++);
		return(filename);
	} else {
		iop = NULL;
		return(NULL);
	}
}

/*
 * Close open name file on aborts
 */
char *lastname()
{
	if(iop!=NULL) {
		fclose(iop);
		iop = NULL;
	}
	return(NULL);
}

/*
 * File Scanner
 *
 * If not a wild card process, verifies existance of file
 * If a wild card process, returns first matching file
 * If file does not exist or no files match, returns NULL
 */
char *filnam(st,p,w)
char *st;
register char *p;
int w;
{
	char xp[106];

	if(w) {
		if(p == NULL) {
			/*
			 * get first name,
			 */
			if((p = firstname(st)) == NULL) {
				/*
				 * if no expansions
				 */
				sprintf(errstr,"No file(s) match %s",
					expand(xp,rtwfile(st,defdev)));
			}
		} else {
			/*
			 * get next name
			 */
			p = nextname();
		}
	} else {
		/*
		 * no expansion
		 */
		p = rtfile(st,defdev);
		if((iop = fopen(p,"r")) == NULL) {
			sprintf(errstr,"No File %s", expand(xp,p));
			p = NULL;
		} else {
			fclose(iop);
		}
	}
	return(p);
}

/*
 * Return file size
 */
int filsiz()
{
	if(iop != NULL) {
		return(iop->io_size);
	} else {
		return(0);
	}
}

/*
 * Return File IOP
 */
FILE *filiop()
{
	return(iop);
}

/*
 * Change local directory
 */
int cd(str)
char *str;
{
	return(lgcdsk(str,1));
}

/*
 * Logical Disk Directory
 */
int lgcdsk(str,flag)
register char *str;
int flag;
{
	register char *s,*t;
	char sl[2];
	char st[106];
#if ts$sys
	char tm1[20];
	char tm2[20];
#endif
	register int i;

	errstr[0] = '\0';
	subfil[0] = '\0';

	*st = '\0';
	strcpy(sl,(strchr(str,'\\') != NULL) ? "\\" : "/");
	/*
	 * Check for device specification
	 */
	if((t = strchr(str,':')) == NULL) {
		t = str;
	} else {
		if((*str == '\\') || (*str == '/')) {
			strncat(st,str+1,++t - (str+1));
		} else {
			strncat(st,str,++t - str);
		}
	}
	/*
	 * Delimit any file specification
	 */
	if(*t && *t != '\\' && *t != '/' && *t != '.')
		strcat(st,sl);
	/*
	 * Copy remainder of string
	 */
	strncat(st,t,sizeof(st)-2 - (t - str));
	st[sizeof(st)-2] = '\0';
	/*
	 * Encapsulate with delimeter if this is a directory spec
	 * and there are subdirectorys
	 */
	if(flag && (strchr(st,'\\') != NULL || strchr(st,'/') != NULL)) {
		strcat(st,sl);
	}
	/*
	 * Replace '/' with '\\'
	 * Convert to lower case
	 */
	for(t=st; *t!='\0'; t++) {
		if(*t == '/' || *t == '\\') {
			slash = *t;
			*t = '\\';
		} else {
			*t = tolower(*t);
		}
	}
	/*
	 * cd .
	 */
	if(!strcmp(st,".")) {
		*st = '\0';
	}
	/*
	 * cd ..
	 */
	if(!strcmp(st,"..")) {
#if rt$sys
		sprintf(errstr,
		/* */
		"\_cd .. not supported under RT-11");
		/* */
		return(1);
#endif
#if ts$sys
		if(subdlevl) {
			if(dismnt(--subdlevl))
				return(1);
			if(subdlevl) {
				sprintf(defdev,"ld%1d:", subdlevl-1+subdbase);
			} else {
				strcpy(defdev,device);
			}
			return(0);
		} else {
			sprintf(errstr,
			/* */
			".. ignored, not in a subdirectory");
			/* */
			return(1);
		}
#endif
	}
	/*
	 * Scan for Physical Device delimeter
	 */
	if((t = strchr(st,':')) != NULL) {
		/*
		 * Test for a valid device
		 */
		switch(tstdev(st)) {
		case 1:
			sprintf(errstr,
			/* */
			"Invalid device specification");
			/* */
			return(1);

		case 2:
			sprintf(errstr,
			/* */
			"Non existant device");
			/* */
			return(1);

		default:
			break;
		}
		/*
		 * Dismount all previously mounted logical units
		 */
		for(i=subdlevl; i>0;  ) {
			if(dismnt(--i))
				return(1);
		}
		subdlevl = 0;
		t++;
	} else {
		t = st;
	}
	/*
	 * ..\
	 */
	if(t[0]=='.' && t[1]=='.') {
#if rt$sys
		sprintf(errstr,
		/* */
		"\_cd .. not supported under RT-11");
		/* */
		return(1);
#endif
#if ts$sys
		t += 2;
		if(subdlevl) {
			if(dismnt(--subdlevl))
				return(1);
			if(subdlevl) {
				sprintf(defdev,"ld%1d:", subdlevl-1+subdbase);
			} else {
				strcpy(defdev,device);
			}
		}
#endif
	}
	/*
	 * Extract each subdirectory
	 */
#if rt$sys
	if((t = strchr(s=t,'\\')) != NULL && strchr(s=++t,'\\') != NULL) {
		sprintf(errstr,
		/* */
		"\_cd /.../ not supported under RT-11");
		/* */
		return(1);
	}
#endif
#if ts$sys
	while((t = strchr(s=t,'\\')) != NULL && strchr(s=++t,'\\') != NULL) {
		/*
		 * Extract subdirectory
		 */
		for(s=tm1,i=0; *t!='\0' && *t!='\\' && i<19; i++) {
			*s++ = *t++;
		}
		*s = '\0';
		/*
		 * A null entry terminates scan
		 */
		if(tm1[0] == '\0') {
			return(0);
		}
		/*
		 * An embedded device is not allowed
		 */
		if(strchr(tm1,':') != NULL) {
			sprintf(errstr,
			/* */
			"'dev:' not allowed in subdirectory");
			/* */
			return(1);
		}
		if(subdlevl >= subdnest) {
			sprintf(errstr,
			/* */
			"Subdirectory nesting may not exceed %d", subdnest);
			/* */
			return(1);
		}
		/*
		 * Try do dismount the current logical device (if any)
		 */
		if(dismnt(subdlevl))
			return(1);
		/*
		 * Try to mount the file as a subdirectory
		 * of the current directory. The default file
		 * type is "dsk". The complete file-spec
		 * must be converted to rad50 for mlogcl.
		 */
		sprintf(tm2,"%s.dsk", defdev);
		r50blk(tm2,tm1,tm2);
		if(mlogcl(subdlevl+subdbase,tm2,0) != 0) {
			sprintf(errstr,
			/* */
			"Subdirectory '%c%s' not found", slash,tm1);
			/* */
			return(1);
		}
		/*
		 * Verify this is a directory
		 */
		if(vlogcl(subdlevl+subdbase) != 0) {
			sprintf(errstr,
			/* */
			"%c%s is not a directory", slash,tm1);
			/* */
			return(1);
		}
		/*
		 * Update directory tree
		 */
		sprintf(defdev,"ld%1d:", subdlevl+subdbase);
		strcpy(subnam[subdlevl],rtname());
		strcpy(subext[subdlevl],rtext());
		/*
		 * Update subdirectory level
		 */
		subdlevl += 1;
	}
#endif
	/*
	 * Assume the trailing non bracketed
	 * string is a file name
	 */
	strncpy(subfil,s,16);
	subfil[15] = '\0';
	return(0);
}

/*
 * Build the rad50 file block
 */
VOID r50blk(outstr,str1,str2)
char *outstr,*str1,*str2;
{
	char str[14];

	/*
	 * Note:  This routine modifies the data in
	 *	  rtstring[], the result of many RTFILE
	 *	  operations. Save the string if required.
	 */
	/*
	 * Generate a complete file spec
	 * from the two filespec strings.
	 */
	rtparse(rtfile(str1,str2));
	/*
	 * Convert to rad50
	 */
	sprintf(str,"%-3.3s%-6.6s%-3.3s", rtdev(),rtname(),rtext());
	ascr50(12,str,outstr);
}

/*
 * Dismount logical device associated with subdlevl
 */
dismnt(i)
int i;
{
	if(dlogcl(i+subdbase) == 4) {
		sprintf(errstr,
		/* */
		"File open on subdirectory %s.%s / [ld%1d:]",
			subnam[i], subext[i], i+subdbase);
		/* */
		return(1);
	}
	return(0);
}

/*
 * Test the Device or File and Set the Default Directory
 */
int tstdev(st)
register char *st;
{
	char t[6];
	register char *q;
	register int i;
	FILE *fp;

	for(i=0,q=t; *st && i<4; i++,q++,st++)
		*q = tolower(*st);
	*q = '\0';

	q = strchr(t,':');
	if(q==NULL || q==t || (q-t)>3) {
		return(1);
	}

	q = rtwfile(t,defdev);

	if((fp = fwild(q, "r"))==NULL) {
		return(2);
	}
	fclose(fp);

	sprintf(defdev,"%s:",rtdev());
	sprintf(device,"%s:",rtdev());
	return(0);
}

/*
 * Get default directory
 */
char *getdef()
{
	return(defdev);
}

/*
 * Expand default path
 */
char *getpath(st)
char *st;
{
	char s[12];
	register int i;

	sprintf(st,"%s", device);
	for(i=0; i<subdlevl ;++i) {
		if(streq(subext[i],"dsk") == TRUE) {
			sprintf(s,"%c%s", slash,subnam[i]);
		} else {
			sprintf(s,"%c%s.%s", slash,subnam[i],subext[i]);
		}
		strcat(st,s);
	}
	return(st);
}

/*
 * Find Home Directory
 */
char *gethome(st)
char *st;
{
	register int i;
	register int **list;
	register char *filspec;
	char spec[14],dev[6],name[8],ext[4];

	list = hlogcl();

	for(i=0; (filspec = (char *) list[i]) != NULL; i++) {
		r50toa(spec,filspec,4);
		if(!i) {
			argscan(dev,&spec[0],3);
			sprintf(st,"%s:", dev);
		}
		argscan(name,&spec[3],6);
		if(*name) {
			argscan(ext,&spec[9],3);
			if(*ext && strcmp(ext,"DSK")) {
				sprintf(spec,"%c%s.%s", slash,name,ext);
			} else {
				sprintf(spec,"%c%s", slash,name);
			}
			strcat(st,spec);
		}
	}
	for(filspec=st; *filspec; filspec++) {
		*filspec = tolower(*filspec);
	}
	return(st);
}		

/*
 * Scan converted rad50 string
 */
int argscan(d,s,cnt)
char *d,*s;
int cnt;
{
	register int i;

	for(i=0; i<cnt; i++) {
		if(*s!='\040') {
			*d++ = *s++;
		}
	}
	*d++ = '\0';
}

/*
 * General dispatch routine using subdirectory processing
 */
int dispatch(call,st)
int (*call) ();
char *st;
{
	char *p;

	if((p=setpath(st)) != NULL) {
		(*call) (p);
	}
	ERRPRINT();
	respath();
}

/*
 * Set path and return filespec
 */
char *setpath(st)
char *st;
{
	if(streq(st,".")) {
		*st = '\0';
	}
	if(strchr(st,'\\')!=NULL ||
		strchr(st,'/')!=NULL ||
			(st[0]=='.' && st[1]=='.')) {
		getpath(savpath);
		if(lgcdsk(st,0)) {
			return(NULL);
		}
		return(subfil);
	}
	return(st);
}

/*
 * Restore path
 */
VOID respath()
{
	if(*savpath) {
		cd(savpath);
		savpath[0] = '\0';
	}
}

/*
 * Expand path with file name
 */
char *expand(xp,st)
char *xp;
char *st;
{
	register char *t;
	char dp[94];

	t = strchr(st,':');
	if(t!=NULL && !strncmp(st,defdev,(int) t-st+1)) {
		st = getpath(dp);
		if(strchr(st,slash) == NULL) {
			sprintf(xp,"%s%s", st,++t);
		} else {
			sprintf(xp,"%s%c%s", st,slash,++t);
		}
	} else {
		strcpy(xp,st);
	}
	return(xp);
}

/*
 * Check if filespec is wild and wflag is on
 *
 *	return	 1	filespec is wild & wflag is on
 *		 0	filespec is not wild
 *	error	-1	filespec is wild & wflag is off
 */
static int chkwild(st,wflag)
char *st;
int wflag;
{
	register int wspec;

	if((wspec = rtwild(rtparse(st,defdev))) && !wflag) {
		sprintf(errstr,
		/* */
		"Wild cards are not enabled");
		/* */
		return(-1);
	}
	return(wspec);
}

/*
 * Delete file/directory utility
 *
 *	The delete utility builds a list of files to delete
 *	keeping a record of the number of files skipped.
 *	After filling the list it deletes the files then rescans
 *	the directory skipping the skipped files and repeats
 *	the process.  This is done because TSX+ may compact
 *	the directory as the files are deleted.  A continuous
 *	scan and delete will miss files.
 */
int delfil(dflag,st,pflag,wflag)
int dflag;
char *st;
int pflag,wflag;
{
	char xp[106];
	char fillst[8][16];
	char answer[20];
	char dblk[8];
	char *p;
	register i,delcnt,ttlcnt;
	int  aflag,ldunit,skpcnt,wspec;
	
	ttlcnt = skpcnt = 0;
	aflag = 0;
	errstr[0] = '\0';

	/*
	 * Wildcards not allowed unless enabled
	 */
	if((wspec = chkwild(st,wflag)) < 0)
		return(1);

	do {

	/*
	 * no names expanded yet
	 */
	p = NULL;
	delcnt = 0;
	/*
	 * skip previously scanned files
	 */
	for(i=0; i<skpcnt; i++) {
		p = filnam(st,p,wflag);
	}

	do {

	aflag = RTFNDBRK;

	/*
	 * verify file exists / get matching file
	 */
	if((p = filnam(st,p,wflag)) == NULL) {
		if(ttlcnt)
			errstr[0] = '\0';
		continue;
	}

	expand(xp,p);
	strcpy(fillst[delcnt],p);

	/*
	 * verify a directory if directory delete
	 */
	if(dflag) {
		if(subdlevl >= subdnest) {
			sprintf(errstr,
			/* */
			"Subdirectory nesting may not exceed %d", subdnest);
			/* */
			aflag++;
			continue;
		}
		r50blk(dblk,fillst[delcnt],defdev);
		ldunit = subdlevl + subdbase;
		dlogcl(ldunit);
		if(!(i = mlogcl(ldunit,dblk,0))) {
			if((i = vlogcl(ldunit)) && !wspec) {
				sprintf(errstr,
				/* */
				"%s is not a directory", xp);
				/* */
			}
			dlogcl(ldunit);
		}
		if(i)
			continue;
	}

	/*
	 * process file
	 */
	if(pflag) {
		/*
		 * check
		 */
		RTPRINT("Delete %s %s ? ", (dflag ? "directory" : "file"),xp);
		if(RTRESPONSE(answer,19)) {
			/*
			 * no more processing
			 */
			lastname();
			p = NULL;
			aflag++;
			continue;
		}
		if(	strchr(answer,'y') == NULL &&
			strchr(answer,'Y') == NULL) {
			skpcnt++;
			continue;
		}
	}

	delcnt++;
	ttlcnt++;

	} while(p != NULL && delcnt<8 && wflag && !aflag);

	if(p != NULL)
		lastname();

	for(i=0; i<delcnt; i++) {
		if(dflag) {
			r50blk(dblk,fillst[i],defdev);
			fprot(dblk,0);
		}
		expand(xp,fillst[i]);
		if(delete(fillst[i])) {
			if(!pflag) {
			RTPRINT("%s %s deleted\r\n",
				(dflag ? "Directory" : "File"),xp);
			}
		} else {
			sprintf(errstr,"File %s protected\r\n", xp);
			skpcnt++;
		}
	}

	} while(p != NULL && wflag && !aflag);

	if(aflag && RTFNDBRK) {
		RTPRINT("---delete aborted---\r\n\r\n");
	}
	return((errstr[0]!='\0') ? 1 : 0);
}

/*
 * Display a directory
 */
VOID dspdir(st)
char *st;
{
	register char *q;
	register int cnt,blks;
	char xp[106];
	FILE *fp;

	q = rtwfile(st,defdev);

	if((fp = fwild(q, "r"))==NULL) {
		RTPRINT("\r\nNo Directory [%s]\r\n", expand(xp,q));
		return;
	}

	RTPRINT("\r\n");
	for(cnt=0,blks=0; fnext(fp)!=NULL && !RTFNDBRK; cnt++) {
		for(q=fp->io_name; *q; q++)
			*q = tolower(*q);
		rtparse(fp->io_name,"");
		flinfo(fp);
		blks += flsize;
		if(cnt%2 == 1)
			RTPRINT("      ");
		RTPRINT("%-6.6s.%-3.3s %6.6u%s %02.2u-%3.3s-%04.4u",
			rtname(),
			rtext(),
			flsize,
			flprot,
			flday,
			flmnth,
			flyear);
		if(cnt%2 == 1)
			RTPRINT("\r\n");
	}
	if(cnt%2 != 0)
		RTPRINT("\r\n");

	if(fp!=NULL)
		fclose(fp);

	if(fndbrk) {
		RTPRINT("---listing aborted---\r\n\r\n");
	} else {
		q = rtwfile(st,defdev);
		RTPRINT(
		"\r\nDirectory [%s] / %d Files / %u Blocks\r\n\r\n",
			expand(xp,q),cnt,blks);
	}
}

/*
 * Make the directory by executing a
 * command file in a detached job
 */
int mkdir(st)
char *st;
{
	char *r,s[24],t[24];
	char u[40];
	register int i;
	register unsigned int a;

	/*
	 * Fill in defaults, strip allocation
	 */
	strcpy(s,rtfile(st,".dsk"));
	strcpy(t,rtfile(s,defdev));
	if (a = rtalloc()) {
		r = strchr(s,'[');
		*r = '\0';
		r = strchr(t,'[');
		*r = '\0';
	}

	/*
	 * Check for a file name
	 */
	if(*st == '\0') {
		sprintf(errstr,
	/* */
	"Subdirectory name required");
	/* */
	} else
	/*
	 * Check for a wildcard filespec
	 */
	if(chkwild(t,1)) {
		sprintf(errstr,
		/* */
		"Wildcards not allowed");
		/* */
	} else
	/*
	 * Require a minimum allocation
	 */
	if(a < 20) {
		sprintf(errstr,
	/* */
	"Directory creation requires allocation --> name[n]  (n >= 20)");
	/* */
	} else
	/*
	 * Check that file does not exist
	 */
	if((iop = fopen(t,"r")) != NULL) {
		fclose(iop);
		iop = NULL;
		sprintf(errstr,
	/* */
	"Subdirectory file exists");
	/* */
	} else
	/*
	 * Check nesting of subdirectories
	 */
	if(subdlevl >= subdnest) {
		sprintf(errstr,
	/* */
	"Subdirectory nesting may not exceed %d", subdnest);
	/* */
	} else {
		sprintf(u,"create %s/allocate:%u\r\n", s,a);
		i = bldcmd(u,0);
		sprintf(u,"mount  ld%1d: %s\r\n", subdlevl+subdbase,s);
		i = bldcmd(u,i);
		sprintf(u,"r dup  ld%1d:/z/y\r\n", subdlevl+subdbase);
		wrtcmd(bldcmd(u,i));
		/*
		 * Check that directory file does exist
		 */
		if((iop = fopen(t,"r")) != NULL) {
			fclose(iop);
			iop = NULL;
			return(0);
		}
		sprintf(errstr,
	/* */
	"Failed to Create Subdirectory %s", st);
	/* */
	}
	return(1);
}

/*
 * Rename a file
 */
int rename(str1,str2,pflag,wflag)
char *str1,*str2;
int pflag,wflag;
{
	register char *p;
	char dblk[16];
	char oldnam[8],oldext[4];
	char newnam[8],newext[4];
	char template[16];	
	char pstr[16];
	char xp1[106],xp2[106];
	char answer[20];
	register int wnam,wext;

	/*
	 * Wildcards not allowed unless enabled
	 */
	if((chkwild(str1,wflag) < 0) || (chkwild(str2,wflag) < 0))
		return(1);

	/*
	 * Parse old name to check for wild cards
	 */
	rtparse(rtwfile(str1,defdev));
	strcpy(oldnam,rtname());
	strcpy(oldext,rtext());
	/*
	 * Parse new name to check for wild cards
	 */
	rtparse(rtwfile(rmtfile(str2),defdev));
	strcpy(newnam,rtname());
	strcpy(newext,rtext());
	wnam = streq(newnam,"*");
	wext = streq(newext,"*");
	/*
	 * Check wild card usage
	 */
	if(	(iswild(oldnam) && !wnam) || (iswild(oldext) && !wext) ||
		(iswild(newnam) && !wnam) || (iswild(newext) && !wext)) {
		sprintf(errstr,"Invalid use of wildcards");
		return(1);
	}
	/*
	 * Build a filespec template
	 */
	sprintf(template,"%s.%s", (wnam ? "" : newnam),(wext ? "" : newext));

	/*
	 * no names expanded yet
	 */
	p = NULL;

	do {

		/*
		 * verify file exists / get matching file
		 */
		if((p = filnam(str1,p,wflag)) == NULL)
			continue;

		strcpy(pstr,p);
		expand(xp1,pstr);
		expand(xp2,rtfile(template,pstr));
		/*
		 * process file
		 */
		if(pflag) {
			/*
			 * check
			 */
			RTPRINT("Rename %s\r\n", xp1);
			RTPRINT("To     %s ? ", xp2);
			if(RTRESPONSE(answer,19)) {
				/*
				 * no more processing
				 */
				lastname();
				p = NULL;
				continue;
			}
			if(	strchr(answer,'y') == NULL &&
				strchr(answer,'Y') == NULL) {
				continue;
			}
		}
		/*
		 * The initial and final file-specs must be
		 * converted to rad50 for frename().
		 */
		r50blk(&dblk[0],pstr,defdev);
		r50blk(&dblk[8],template,pstr);
		if(frename(dblk)) {
			if(wflag) {
				RTPRINT(
					"Protected File %s Exists\r\n", xp2);
			} else {
				sprintf(errstr,
					"Protected File %s Exists\r\n", xp2);
				return(1);
			}
		} else
		if(!pflag) {
			RTPRINT("File       %s\r\n", xp1);
			RTPRINT("Renamed to %s\r\n", xp2);
		}

	} while(wflag && p != NULL);

	return(0);
}

/*
 * Set or Clear the protection flag on a file
 */
VOID setclrp(st,pflag,wflag)
char *st;
int pflag,wflag;
{
	char *p,dblk[8];
	
	/*
	 * Wildcards not allowed unless enabled
	 */
	if(chkwild(st,wflag) < 0)
		return;

	/*
	 * no names expanded yet
	 */
	p = NULL;

	do {
		/*
		 * verify file exists / get matching file
		 */
		if((p = filnam(st,p,wflag)) == NULL)
			continue;

		/*
		 * The complete file-spec must be
		 * converted to rad50 for fprot().
		 */
		r50blk(dblk,p,defdev);
		fprot(dblk,pflag);
	} while(wflag && p != NULL);
}

/*
 * Build the command buffer
 */
int bldcmd(st,i)
char *st;
register int i;
{
	char s[40];
	register int j;

	if(i+strlen(st) >= RTBUFSZ) {
		wrtcmd(i);
		i = 0;
	}
	/*
	 * Create directory link section
	 */
	if(!i) {
	sprintf(s,"! TSXUTL Command File\r\n");		i = addcmd(s,i);
	sprintf(s,"set error error\r\n");		i = addcmd(s,i);
	sprintf(s,"assign %s dk:\r\n", device);		i = addcmd(s,i);
	for(j=0; j<subdlevl; j++) {
		sprintf(s,"mount  ld%1d: %s.%s\r\n",
		j+subdbase,subnam[j],subext[j]);	i = addcmd(s,i);
		sprintf(s,"assign ld%1d: dk:\r\n",
		j+subdbase);				i = addcmd(s,i);
	}
	}

	return(addcmd(st,i));
}

/*
 * Add to command buffer
 */
int addcmd(st,i)
char *st;
int i;
{
	register int j;

	j = strlen(st);
	strcpy(RTBUFFR+i,st);
	return(i+j);
}

/*
 * Execute command buffer
 */
int wrtcmd(i)
int i;
{
	register int j;
	char cmdfil[16];
	register FILE *cf;

	if(i) {
		/*
		 * Create a unique file name for command file
		 */
		sprintf(cmdfil,"wf:jobnum.%03d", tljobn);
		/*
		 * Open command file
		 */
		if((cf = fopen(cmdfil, "wn")) == NULL) {
			sprintf(errstr,
			/* */
			"Unable to open command file");
			/* */
			return(0);
		}
		j  = !fwrite(xs,i,1,cf);
		j |=  fclose(cf);
		if(j) {
			sprintf(errstr,
			/* */
			"Error writing command file");
			/* */
			return(0);
		}
		if(execmd) {
			/*
			 * Start detached job
			 */
			if(j = strtdj(cmdfil)) {
				/*
				 * Wait for detached job to complete
				 */
				while(statdj(j)) {
					suspnd(0);
				}
			} else {
				sprintf(errstr,
				/* */
				"Failed to start detached job");
				/* */
			}
			if(delcmd) {
				delete(cmdfil);
			}
		}
	}
	return(0);
}
                                                                                                                                                                                                          