#include "dos.h"

extern PORT	*DosPort;   /*  Our DOS port... this is slick... 	  */
extern PROC	*DosProc;   /*	Our Process			          */
extern DEVNODE  *DosNode;   /*	Our DOS node.. created by DOS for us	  */
extern DEVLIST  *DevList;   /*	Device List structure for our volume node */
extern void	*SysBase;   /*	EXEC library base		    	  */
extern DOSLIB	*DOSBase;   /*	DOS library base for debug process	  */
extern RAMFILE	RFRoot;     /*	Directory/File structure    (root node)   */
extern LIST	FHBase;     /*	Open Files				  */
extern LIST	LCBase;     /*	Open Locks				  */
extern long	TotalBytes; /*	total bytes of data in filesystem	  */
extern RAMFILE	xpath;	    /*  This is used in case of off device path   */
extern char	*buf1;	    /*  This holds the translated path names      */

RAMFILE * checkoutpath();
RAMFILE * vsearchpath();
void *DeviceProc();

/*
 *  PACKET ROUTINES.	Dos Packets are in a rather strange format as you
 *  can see by this and how the PACKET structure is extracted in the
 *  GetMsg() of the main routine.
 */

void
returnpacket(packet)
register struct DosPacket *packet;
{
    register struct Message *mess;
    register struct MsgPort *replyport;

    replyport		     = packet->dp_Port;
    mess		     = packet->dp_Link;
    packet->dp_Port	     = DosPort;
    mess->mn_Node.ln_Name    = (char *)packet;
    mess->mn_Node.ln_Succ    = NULL;
    mess->mn_Node.ln_Pred    = NULL;
    PutMsg(replyport, mess);
}

/*
 *  Are there any packets queued to our device?
 */

packetsqueued()
{
    return ((void *)DosPort->mp_MsgList.lh_Head !=
	    (void *)DosPort->mp_MsgList.lh_Tail);
}

/*
 *  DOS MEMORY ROUTINES
 *
 *  DOS makes certain assumptions about LOCKS.	A lock must minimally be
 *  a FileLock structure, with additional private information after the
 *  FileLock structure.  The longword before the beginning of the structure
 *  must contain the length of structure + 4.
 *
 *  NOTE!!!!! The workbench does not follow the rules and assumes it can
 *  copy lock structures.  This means that if you want to be workbench
 *  compatible, your lock structures must be EXACTLY sizeof(struct FileLock).
 */

void *
dosalloc(bytes)
register ulong bytes;
{
    register ulong *ptr;

    bytes += 4;
    ptr = AllocMem(bytes, MEMF_PUBLIC|MEMF_CLEAR);
    *ptr = bytes;
    return(ptr+1);
}

dosfree(ptr)
register ulong *ptr;
{
    --ptr;
    FreeMem(ptr, *ptr);
}

/*
 *  Convert a BSTR into a normal string.. copying the string into buf.
 *  I use normal strings for internal storage, and convert back and forth
 *  when required.
 */

void
btos(bstr,buf)
ubyte *bstr;
ubyte *buf;
{
    bstr = BTOC(bstr);
    bmov(bstr+1,buf,*bstr);
    buf[*bstr] = 0;
}

/*
 *  Some EXEC list handling routines not found in the EXEC library.
 */

void *
NextNode(node)
NODE *node;
{
    node = node->mln_Succ;
    if (node->mln_Succ == NULL)
	return(NULL);
    return(node);
}

void *
GetHead(list)
LIST *list;
{
    if ((void *)list->mlh_Head != (void *)&list->mlh_Tail)
	return(list->mlh_Head);
    return(NULL);
}

/*
 *  Compare two names which are at least n characters long each,
 *  ignoring case.
 */

nccmp(p1,p2,n)
register ubyte *p1, *p2;
register short n;
{
    while (--n >= 0) {
	if ((p1[n]|0x20) != (p2[n]|0x20))
	    return(0);
    }
    return(1);
}

/*
 *  Create a file or directory and link it into it's parent directory.
 */

RAMFILE *
createramfile(parentdir, type, name)
RAMFILE *parentdir;
char *name;
{
    register RAMFILE *ramfile;

    ramfile = AllocMem(sizeof(RAMFILE), MEMF_CLEAR|MEMF_PUBLIC);
    AddTail(&parentdir->list, ramfile);
    ramfile->parent = parentdir;
    ramfile->name = AllocMem(strlen(name)+1, MEMF_PUBLIC);
    strcpy(ramfile->name, name);
    ramfile->type = type;
    ramfile->protection = 0;
    NewList(&ramfile->list);
    DateStamp(&ramfile->date);
    DateStamp(&ramfile->parent->date);
    return(ramfile);
}

/*
 *  Free all data associated with a file
 */

void
freedata(ramfile)
RAMFILE *ramfile;
{
    FENTRY *fen;

    TotalBytes -= ramfile->bytes;
    while (fen = RemHead(&ramfile->list)) {
/*DB*/	dbprintf("FREE FEN: %08lx %08lx %ld\n", fen, fen->buf, fen->bytes);
	FreeMem(fen->buf, fen->bytes);
	FreeMem(fen, sizeof(*fen));
    }
    ramfile->bytes = 0;
    DateStamp(&ramfile->date);
    DateStamp(&ramfile->parent->date);
}

/*
 *  Unlink and remove a file.  Any data associated with the file or
 *  directory has already been freed up.
 */

void
freeramfile(ramfile)
RAMFILE *ramfile;
{
    Remove(ramfile);		/*  unlink from parent directory    */
    if (ramfile->name)
	FreeMem(ramfile->name,strlen(ramfile->name)+1);
    if (ramfile->comment)
	FreeMem(ramfile->comment,strlen(ramfile->comment)+1);
    FreeMem(ramfile,sizeof(*ramfile));
}

/*
 *  The lock function.	The file has already been checked to see if it
 *  is lockable given the mode.
 */

LOCK *
ramlock(ramfile, mode)
RAMFILE *ramfile;
{
    LOCK *lock = dosalloc(sizeof(LOCK));
    LOCKLINK *ln;

    if (mode != ACCESS_WRITE)
	mode = ACCESS_READ;
    ln = AllocMem(sizeof(LOCKLINK), MEMF_PUBLIC);
    AddHead(&LCBase,ln);
    ln->lock = lock;
    lock->fl_Link= (long)ln;
    lock->fl_Key = (long)ramfile;
    lock->fl_Access = mode;
    lock->fl_Task = DosPort;
    lock->fl_Volume = (BPTR)CTOB(DosNode);
    if (mode == ACCESS_READ)
	++ramfile->locks;
    else
	ramfile->locks = -1;
    return(lock);
}

void
ramunlock(lock)
LOCK *lock;
{
    RAMFILE *file = (RAMFILE *)lock->fl_Key;

    Remove(lock->fl_Link);			/* unlink from list */
    FreeMem(lock->fl_Link, sizeof(LOCKLINK));	/* free link node   */
    if (lock->fl_Access == ACCESS_READ) 	/* undo lock effect */
	--file->locks;
    else
	file->locks = 0;
    dosfree(lock);				/* free lock	    */
}

/*
 *  GETLOCKFILE(bptrlock)
 *
 *  Return the RAMFILE entry (file or directory) associated with the
 *  given lock, which is passed as a BPTR.
 *
 *  According to the DOS spec, the only way a NULL lock will ever be
 *  passed to you is if the DosNode->dn_Lock is NULL, but I'm not sure.
 *  In anycase, If a NULL lock is passed to me I simply assume it means
 *  the root directory of the RAM disk.
 */

RAMFILE *
getlockfile(lock)
void *lock;		/*  actually BPTR to LOCK */
{
    register LOCK *rl = BTOC(lock);

    if (rl) return((RAMFILE *)rl->fl_Key);
    return(&RFRoot);
}

/*
 *  Search the specified path beginning at the specified directory.
 *  The directory pointer is updated to the directory containing the
 *  actual file.  Return the file node or NULL if not found.  If the
 *  path is illegal (an intermediate directory was not found), set *ppar
 *  to NULL and return NULL.
 *
 *  *ppar may also be set to NULL if the search path IS the root.
 *
 *  If pptr not NULL, Set *pptr to the final component in the path.
 */

char *rindex();

RAMFILE *
searchpath(ppar,buf,pptr)
RAMFILE **ppar;
char *buf;
char **pptr;
{
    RAMFILE *file = *ppar;
    RAMFILE *srch;
    short len;
    char *ptr;

    ptr = rindex(buf,':');
    if (ptr) buf = ptr+1;

    *ppar = NULL;
    for (;*buf && file;) {
	ptr = getpathelement(&buf,&len);
	if (*ptr == '/') {          /*  go back a directory */
	    if (!file->parent) {    /*	no parent directory */
		return(NULL);
	    }
	    file = file->parent;
	    continue;
	}
	if (file->type == FILE_FILE)
	    return(checkoutpath(file,ptr));
	for (srch = GetHead(&file->list); srch; srch = NextNode(srch)) {
	    if (srch->type && strlen(srch->name) == len && nccmp(srch->name, ptr, len)) {
		file = srch;	    /*	element found	    */
		break;
	    }
	}
	if (srch == NULL) {
	    if (*buf == 0)	/*  Element not found.	If it was the final */
		*ppar = file;	/*  element the parent directory is valid   */
	    if (pptr)
		*pptr = ptr;
	    return(NULL);
	}
    }
    if (pptr)
	*pptr = ptr;
    *ppar = file->parent;
    return(file);
}

RAMFILE *
vsearchpath(ppar,buf,pptr)
RAMFILE **ppar;
char *buf;
char **pptr;
{
    RAMFILE *file = *ppar;
    RAMFILE *srch;
    short len;
    char *ptr;

    ptr = rindex(buf,':');	/* this fixes up the colon treatment */
    if (ptr) buf = ptr+1;

    *ppar = NULL;
    for (;*buf && file;) {
	ptr = getpathelement(&buf,&len);
	if (*ptr == '/') {          /*  go back a directory */
	    if (!file->parent) {    /*	no parent directory */
		return(NULL);
	    }
	    file = file->parent;
	    continue;
	}
	if (file->type == FILE_FILE)
	    return(NULL);
	for (srch = GetHead(&file->list); srch; srch = NextNode(srch)) {
	    if (srch->type && strlen(srch->name) == len && nccmp(srch->name, ptr, len)) {
		file = srch;	    /*	element found	    */
		break;
	    }
	}
	if (srch == NULL) {
	    if (*buf == 0)	/*  Element not found.	If it was the final */
		*ppar = file;	/*  element the parent directory is valid   */
	    if (pptr)
		*pptr = ptr;
	    return(NULL);
	}
    }
    if (pptr)
	*pptr = ptr;
    *ppar = file->parent;
    return(file);
}
typedef struct {
	FENTRY *fentry;
	long offset;
} SFH;	/* simple file handle */

RAMFILE *
checkoutpath(file,path)
RAMFILE *file;
char *path;
{
	SFH fh;
	int lck;

	fh.fentry = GetHead(&file->list);
	fh.offset = 0;
	while (getnexttry(&fh,buf1)) {
		strcat(buf1,path);
		if (DeviceProc(buf1) == DosPort) {
			/* refers to us.. ignore */
			continue;
		}
		if (!index(buf1,':')) {
			/* not an absolute path */
			continue;
		}
		if (lck = Lock(buf1,ACCESS_READ)) {
			UnLock(lck);
			return(&xpath);
		}
	}
	return(0);
}

getnexttry(fh,buf)
register SFH *fh;
register char *buf;
{
	register int i;
	register FENTRY *fe;

	fe = fh->fentry;
	i  = fh->offset;

	for (fe = fh->fentry; fe ; fe = NextNode(fe)) {
		for (; i<fe->bytes; i++) {
			*buf = fe->buf[i];
			if (*buf == '\n') {
				*buf = 0;
				fh->fentry = fe;
				fh->offset = ++i;
				return(1);
			}
			buf++;
		}
		i=0;
	}
	return(0);
}


/*
 *  Return the next path element in the string.  The routine effectively
 *  removes any trailing '/'s, but treats ':' as part of the next component
 *  (i.e. ':' is checked and skipped in SEARCHPATH()).
 */

char *
getpathelement(pstr,plen)
char **pstr;
short *plen;
{
    char *base;
    register char *ptr = *pstr;
    register short len = 0;

    if (*(base = ptr)) {
	if (*ptr == '/') {
	    ++ptr;
	    ++len;
	} else {
	    while (*ptr && *ptr != '/') {
		++ptr;
		++len;
	    }
	    if (*ptr == '/')
		++ptr;
	}
    }
    *pstr = ptr;
    *plen = len;
    return(base);
}


char *
typetostr(ty)
{
    switch(ty) {
    case ACTION_DIE:		return("DIE");
    case ACTION_OPENRW: 	return("OPEN-RW");
    case ACTION_OPENOLD:	return("OPEN-OLD");
    case ACTION_OPENNEW:	return("OPEN-NEW");
    case ACTION_READ:		return("READ");
    case ACTION_WRITE:		return("WRITE");
    case ACTION_CLOSE:		return("CLOSE");
    case ACTION_SEEK:		return("SEEK");
    case ACTION_EXAMINE_NEXT:	return("EXAMINE NEXT");
    case ACTION_EXAMINE_OBJECT: return("EXAMINE OBJ");
    case ACTION_INFO:		return("INFO");
    case ACTION_DISK_INFO:	return("DISK INFO");
    case ACTION_PARENT: 	return("PARENTDIR");
    case ACTION_DELETE_OBJECT:	return("DELETE");
    case ACTION_CREATE_DIR:	return("CREATEDIR");
    case ACTION_LOCATE_OBJECT:	return("LOCK");
    case ACTION_COPY_DIR:	return("DUPLOCK");
    case ACTION_FREE_LOCK:	return("FREELOCK");
    case ACTION_SET_PROTECT:	return("SETPROTECT");
    case ACTION_SET_COMMENT:	return("SETCOMMENT");
    case ACTION_RENAME_OBJECT:	return("RENAME");
    case ACTION_INHIBIT:	return("INHIBIT");
    case ACTION_RENAME_DISK:	return("RENAME DISK");
    case ACTION_MORECACHE:	return("MORE CACHE");
    case ACTION_WAIT_CHAR:	return("WAIT FOR CHAR");
    case ACTION_FLUSH:		return("FLUSH");
    case ACTION_RAWMODE:	return("RAWMODE");
    default:			return("---------UNKNOWN-------");
    }
}

