#include <stdio.h>
#include <telink.h>

/* Basic File I/O functions for BDS 'C', to replace the library functions.
These do UNIX like reads and writes. (Byte counts, not sectors.) 


		Tom Jennings 30 June 83

	The maximum number of open files is defined by MAXFILES, below.

xinit()				Initialize the file system by clearing
				all handles and counts. MUST be called
				before any routines are used. Calling it
				later will abort all open files.

handle= xopen(name,access)	Open the file for reading or writing.
int handle;			'access' is ignored; no text translation
char *name;			is done. Returns -1 if error.

handle= xcreate(name,access)	Similar to above, creates a new file,
				whose length is zero. Any previous file
				is deleted.

val= xread(handle,buffer,count) Read or write chars to the specified
int val,count,handle;		file. 'val' returns 'count' if the
char *buffer;			read or write was sucessful.

error= xclose(handle)		Close the file. Returns -1 if error.

found= xfind(path,name,flag)	Search for filenames. 'flag' should be
int found,flag;			0 for the first call, non-zero there-
char *path,*name;		after. If 'found' returns non-zero, then
				a filename matching 'path' was found on the
				disk, and is returned in 'name'. 'path'
				may contain wildcards and a drive spec.
				'name' must be 14 chars long. A maximum of
				MAXNAMES (below) will be handled. The
				two names are "normal" null terminated names,
				with a dot if appicable. The drive spec 
				is stripped off. Upper and lower case is
				handled by the function.

*/
#define MAXFILES 8

struct _xiobuf {		/* Define the stuff for each file. */
	int hndle;		/* the associated file handle, */
	char fcb[36];		/* The opened FCB, */
	int bufcnt;		/* # bytes in the buffer, */
	char buff[128];		/* the data buffer, */
	int dirty;		/* 1 if written, */
} xfiles[MAXFILES];

/* List of found names from the search first/next. */

#define MAXNAMES 128
int numnames;			/* number of names in pile, */
int namenum;			/* current name from pile, */
char names[MAXNAMES][11];	/* where the names are, */

/* Initialize the file system. */

xinit() {
int i;

	for (i= 0; i < MAXFILES; i++)
		xfiles[i].hndle= -1;
	return;
}

/* Open a file. Return -1 if file not found, or no handles available. */

xopen(name,access)
char *name;
int access;
{
int handle;			/* handle to return to caller, */
int i;

	handle= gethandle(-1);	/* find a free handle, */
	if (handle == -1)
		return(handle);	/* none available. */

	for (i= 0; i < 36; i++)	/* clear out the FCB, */
		xfiles[handle].fcb[i]= 0;

	xfiles[handle].fcb[0]= 0; /* default drive, */
	if (name[1] == ':') {	/* if a drive spec, */
		xfiles[handle].fcb[0]= name[0] - '@';
		++name; ++name;	/* install it, */
	}
	to_fcb(name,&xfiles[handle].fcb[1]);
	if (bdos(15,&(xfiles[handle].fcb[0])) == 255)
		return(-1);

	xfiles[handle].hndle= handle;	/* mark which handle, */
	xfiles[handle].bufcnt= 0;	/* empty buffer, */
	xfiles[handle].dirty= 0;
	return(handle);
}
/* Close the specified file. */

xclose(handle)
int handle;
{
int err;

	if (gethandle(handle) == -1)
		return(-1);		/* check handle, */

	err= 0;				/* assume no error, */
	if (xfiles[handle].dirty) {	/* if stuff in buffer, */
		bdos(26,&xfiles[handle].buff[0]);
		err= bdos(21,&xfiles[handle].fcb[0]) ? -1 : 0;
	}
	xfiles[handle].dirty= 0;
	xfiles[handle].bufcnt= 0;
	xfiles[handle].hndle= 0;
	bdos(16,&xfiles[handle].fcb[0]);	/* close file. */
	return(err);
}
/* Create the specified file. */

xcreate(name,access)
char *name;
int access;
{
int handle;
int i;
	handle= gethandle(-1);
	if (handle == -1)
		return(-1);

	for (i= 0; i < 36; i++)			/* clear the FCB, */
		xfiles[handle].fcb[i]= 0;

	if (name[1] == ':') {			/* if :, then drive, */
		xfiles[handle].fcb[0]= name[0] - '@';
		++name; ++name;
	} else
		xfiles[handle].fcb[0]= 0;	/* default drive, */

	to_fcb(name,&xfiles[handle].fcb[1]);
	xfiles[handle].fcb[12]= '?';		/* delete all extents, */
	bdos(19,&xfiles[handle].fcb[0]);	/* delete file, */
	xfiles[handle].fcb[12]= 0;		/* zero extent, */
	if (bdos(22,&xfiles[handle].fcb[0]) == 255) /* create file, */
		return(-1);
	xfiles[handle].hndle= handle;
	xfiles[handle].bufcnt= 0;
	xfiles[handle].dirty= 0;
	return(handle);
}

/* Read from a file. */

xread(handle,buffer,count)
int handle;
char *buffer;
int count;
{
int err,i;

	i= 0;
	if (gethandle(handle) == -1)
		return(0);
	while (count) {
		if (xfiles[handle].bufcnt) { /* if not empty, */
			*buffer++= xfiles[handle].buff[128 - xfiles[handle].bufcnt--];
			i++;			/* read from buffer, */
			--count;		/* read one, */
		} else {
			bdos(26,&xfiles[handle].buff[0]); /* DMA address, */
			err= bdos(20,&xfiles[handle].fcb[0]);/* fill buffer, */
			if (err)
				break;
			xfiles[handle].bufcnt= 128;	/* set new count, */
		}
	}
	return(i);
}

/* Write chars to the specified file. */

xwrite(handle,buffer,count)
int handle;
char *buffer;
int count;
{
int i,err,j;

	if (gethandle(handle) == -1)	/* find it first, */
		return(0);		/* doesnt exist! */

	i= 0;				/* # byte written, */
	while (count) {
		if (xfiles[handle].bufcnt < 128) {
			xfiles[handle].buff[xfiles[handle].bufcnt++]= *buffer++;
			xfiles[handle].dirty= 1;
			++i;
			--count;
		} else {
			xfiles[handle].bufcnt= 0; /* buffer empty, */
			xfiles[handle].dirty= 0;  /* buffer written, */
			bdos(26,&xfiles[handle].buff[0]); /* set DMA address, */
			if (bdos(21,&xfiles[handle].fcb[0]))
				break;
		}
	}
	return(i);			/* return number written, */
}

/* Search first/next. The first time causes a disk search, and returns one
name. Search nexts just return one name from the pile. */

xfind(path,name,flag)
char *path,*name;
int flag;
{
int i,v;
char *p;

if (flag == 0) {			/* if first time, */

	numnames= 0;			/* empty list, */
	namenum= 0;			/* start with the 1st. */
	for (i= 0; i < 32; i++)		/* clear out FCB, */
		xffcb[i]= 0;
	for (i= 1; i < 12; i++)		/* blank name, */
		xffcb[i]= ' ';

	setfcb(xffcb,path);		/* make an FCB, */
	if ((v= bdos(17,xffcb)) == 255)	/* search first, */
		return(0);		/* no match. */
	do {
		p= cast(0x80 + 1 + (32 * v));
		for (i= 0; i < 12; i++)	/* copy in name only, */
			names[numnames][i]= p[i];
		++numnames;		/* find all others */
		v= bdos(18,xffcb);	/* search next, */
	} while ((v != 255) && (numnames < MAXNAMES));
}
/* Search next. Just get a name from the list. */

if (numnames) {
	from_fcb(names[namenum],name);
	--numnames; ++namenum;
	return(1);
}
return(0);
}
/* Do a type conversion: BDS has no cast operator. */

cast(obj)
char *obj;
{	return(obj);
}
/* Look for the handle number in the file structure. Assigned handles
start at 0; unallocated handles are -1. Returns either the handle # 
(index into the structure) or -1 if no match. */

gethandle(handle) 
int handle;
{
int i;
	for (i= 0; i < MAXFILES; i++) {
		if (xfiles[i].hndle == handle)
			return(i);
	}
	return(-1);
}
/* Convert a normal asciz string to MSDOS/CPM FCB format. Make the filename
portion 8 characters, extention 3 maximum. */

to_fcb(inname,outname)
char *inname;
char outname[];
{
char c;
int i;

	for (i= 0; i < 11; i++)
		outname[i]= ' ';		/* clear out name, */
	outname[i]= '\0';			/* null terminate, */
	for (i= 0; i < 11; i++) {
		if (*inname == '\0')		/* if null, */
			outname[i]= ' ';	/* pad with blanks, */
		else if (*inname == '.') {	/* if a dot, */
			++inname;		/* skip it, */
			i= 7;			/* skip to extention, */
		} else {
			outname[i]= toupper(*inname);
			++inname;
		}
	}
	return;
}
/* Convert a CP/M like filename to a normal ASCIZ name. */

from_fcb(inname,outname)
char *inname,*outname;
{
int i;
char c;

	for (i= 8; i != 0; i--) {		/* do name portion, */
		c= filtchar(*inname++);		/* filter it, */
		if (c != ' ') 			/* if not space, */
			*outname++= c;		/* set it, */
	}					/* do all 8 chars, */
	*outname++= '.';			/* add the dot, */

	for (i= 3; i != 0; i--) {		/* do extention, */
		c= filtchar(*inname++);		/* filter it, */
		if (c == ' ')
			break;
		*outname++= c;
	}
	*outname= '\0';				/* terminate it, */
	return;
}
/* Process a filename character. */

char filtchar(c)
char c;
{	c&= 0x7f;
	return(toupper(c));
}
          
