#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-------");
    }
}

