/* --------------------------------------------------------------------
 * FS1.C - PathMaster(tm) Super File Selector control code.
 * Copyright © 1987-1989 by Justin V. McCormick.  All Rights Reserved.
 * -------------------------------------------------------------------- */

#include <exec/types.h>
#include <exec/nodes.h>
#include <exec/lists.h>
#include <exec/memory.h>
#include <exec/ports.h>
#include <exec/io.h>
#include <exec/semaphores.h>
#include <exec/libraries.h>
#include <devices/timer.h>
#include <devices/inputevent.h>
#include <graphics/gfx.h>
#include <graphics/view.h>
#include <graphics/rastport.h>
#include <graphics/layers.h>
#include <graphics/text.h>
#include <intuition/intuition.h>
#include <libraries/dos.h>
#include <libraries/dosextens.h>
#include <libraries/filehandler.h>
#include <stdio.h>

#include "fs.h"

#ifdef LATTICE
#define strlen strlen
#include <string.h>
#include <stdlib.h>
#include <proto/exec.h>
#include <proto/graphics.h>
#include <proto/intuition.h>
#include <proto/dos.h>
#endif

#ifdef AZTEC_C
#include <functions.h>
extern struct IORequest *CreateExtIO __ARGS((struct MsgPort *, LONG));
#endif

/* Extern CODE */
#ifdef BENCHMARK
extern VOID __stdargs StartTime __ARGS((VOID));
extern VOID __stdargs StopTime __ARGS((BYTE *));
#endif

extern BYTE *__stdargs FibFileDate __ARGS((struct DateStamp *));
extern BYTE *__stdargs myrindex __ARGS((BYTE *, LONG));
extern LONG __stdargs AllocFSFib __ARGS((VOID));
extern LONG __stdargs FSCheckFlagChange __ARGS((VOID));
extern LONG __stdargs FSGetNextFib __ARGS((VOID));
extern LONG __stdargs ioerrnum __ARGS((LONG));
extern LONG __stdargs lstrcmp __ARGS((BYTE *, BYTE *));
extern struct dev_node *__stdargs AllocDevNode __ARGS((VOID));
extern struct file_node *__stdargs AllocFileNode __ARGS((VOID));
extern VOID __stdargs CheckFSArrows __ARGS((VOID));
extern VOID __stdargs FillFileNode __ARGS((struct file_node *));
extern VOID __stdargs FreeAllDNodes __ARGS((VOID));
extern VOID __stdargs FreeAllFNodes __ARGS((VOID));
extern VOID __stdargs FreeFileSelect __ARGS((VOID));
extern VOID __stdargs FSAssignEntryText __ARGS((struct file_node *, LONG));
extern VOID __stdargs FSClearLock __ARGS((VOID));
extern VOID __stdargs FSDisableAllFGads __ARGS((VOID));
extern VOID __stdargs FSDoFileGad __ARGS((LONG));
extern VOID __stdargs FSDoGadget __ARGS((ULONG));
extern VOID __stdargs FSDoSlideGadget __ARGS((VOID));
extern VOID __stdargs FSDoSortGadget __ARGS((LONG));
extern VOID __stdargs FSEndString __ARGS((VOID));
extern VOID __stdargs FSMatchPattern __ARGS((VOID));
extern VOID __stdargs FSPutPath __ARGS((VOID));
extern VOID __stdargs FSResetKnob __ARGS((VOID));
extern VOID __stdargs FSScrollFileGads __ARGS((LONG));
extern VOID __stdargs FSSetFileGads __ARGS((VOID));
extern VOID __stdargs FSSetKnob __ARGS((VOID));
extern VOID __stdargs FSStartScrollGad __ARGS((ULONG));
extern VOID __stdargs FSUpdateSort __ARGS((VOID));
extern VOID __stdargs FSWinTitle __ARGS((VOID));
extern VOID __stdargs HCompEntry __ARGS((ULONG));
extern VOID __stdargs MakePathString __ARGS((struct FileLock *, BYTE *));
extern VOID __stdargs MyActivateGad __ARGS((struct Gadget *, struct Window *));
extern VOID __stdargs safestrcpy __ARGS((BYTE *, BYTE *, LONG));
extern VOID __stdargs SetDevGads __ARGS((VOID));


/* Extern DATA from fsdata.c */
#ifdef STANDALONE
extern struct TextFont *FSTextFont;
extern struct TextAttr Def8Text;
#else
extern struct TextFont Def8TextData;
#endif

extern BPTR FSLock;
extern BYTE *fserrmsgs[];
extern BYTE *FSPathBuf;
extern BYTE *OldFSPathBuf;
extern BYTE fserrmsg22[];
extern BYTE fserrmsg24[];
extern BYTE fserrmsg27[];
extern BYTE fserrmsg28[];
extern BYTE fserrmsg29[];
extern BYTE FSIgnorePat[];
extern BYTE FSPatternUndoBuffer[];
extern BYTE FSWinTitleStr[];
extern BYTE FSwstr[];
extern BYTE RamDirNameStr[];
extern BYTE SplatStr[];
extern BYTE TimerName[];
extern struct DateStamp FSdirstamp;
extern struct dev_node *lastdev;
extern struct DosLibrary *DOSBase;
extern struct FileInfoBlock *FSFib;
extern struct file_node *FSFileNodes[];
extern struct file_node *topfin;
extern struct FSRequest *FSReq;
extern struct Gadget FSFileGad;
extern struct Gadget FSPathGad;
extern struct Gadget FSSelectGad;
extern struct Gadget FSUserGad;
extern struct IntuiText FSCancelTxt;
extern struct IntuiText FSFileTxt;
extern struct IntuiText FSPathTxt;
extern struct IntuiText FSSelectTxt;
extern struct IntuiText FSUserTxt;
extern struct List *devList;
extern struct List *fnList;
extern struct MsgPort *FSTimerPort;
extern struct NewWindow FSnw;
extern struct RastPort *FSRPort;
extern struct StringInfo FSFileInfo;
extern struct StringInfo FSPathInfo;
extern struct StringInfo FSPatternInfo;
extern struct timerequest *FSDelayReq;
extern struct Window *FSWin;
extern UBYTE FSFileLabelStr[];
extern UBYTE FSPathLabelStr[];
extern ULONG fscursecs, fscurmicros;
extern ULONG fsoldsecs, fsoldmicros;
extern ULONG FSSignal;
extern ULONG FSSignalMask;
extern ULONG FSUserSignal;
extern UWORD fsflags;
extern WORD FSCountDown;
extern WORD fsdirlocked;
extern WORD FSDone;
extern WORD fsnumentries;
extern WORD fsstartedstr;
extern WORD fstitlelength;
extern WORD fstitstatus;
extern WORD fsvirgindir;

/* Local CODE */
LONG CreateVBTimer __ARGS((BYTE *, struct MsgPort **, struct timerequest **));
LONG FSGetDevs __ARGS((VOID));
LONG FSGrabEntry __ARGS((VOID));
LONG FSLockDir __ARGS((VOID));
LONG FSTestOldLock __ARGS((VOID));
LONG InitFSVBDelay __ARGS((VOID));
LONG InitFSWindow __ARGS((VOID));
VOID AbortAsyncIO __ARGS((struct IORequest *));
VOID CheckFSIDCMP __ARGS((VOID));
VOID FreeFSVBDelay __ARGS((VOID));
VOID FreeVBTimer __ARGS((struct MsgPort **, struct timerequest **));
VOID __stdargs FSVBDelay __ARGS((ULONG));

LONG FileSelect __ARGS((struct FSRequest *));
/* --------------------------------------------------------------------
 *                       FileSelect
 * -------------------------------------------------------------------- */
LONG FileSelect(fsrequest)
  struct FSRequest *fsrequest;
{
  LONG i, status, newpat;
  ULONG signals;

/* (Re)Initialize things */
  FSReq = fsrequest;			/* Init global filereq struct pointer */

/* Set pattern to match files */
  newpat = 0;				/* Assume no pattern changes at first */
  FSPatternInfo.Buffer = (UBYTE *)fsrequest->matchpattern;
  FSPatternInfo.BufferPos = strlen(FSPatternInfo.Buffer);
  if (FSPatternInfo.Buffer[0] == 0)	/* Put splat back if no matchpattern  */
    (VOID)strcpy(FSPatternInfo.Buffer, SplatStr);
  
  if (lstrcmp(FSPatternUndoBuffer, FSPatternInfo.Buffer) != 0)
  {
    safestrcpy(FSPatternUndoBuffer, FSPatternInfo.Buffer, (LONG)(MATCHSTRSIZE - 1));
    newpat = 1;
  }

/* Set pattern to ignore files */
  if (lstrcmp(FSIgnorePat, fsrequest->ignorepattern) != 0)
  {
    safestrcpy(FSIgnorePat, fsrequest->ignorepattern, (LONG)(FILESTRSIZE - 1));
    newpat = 1;
  }

/* Update fsflags if FSReq->flags has changed, reset state if needed */
  if (FSCheckFlagChange() != 0)
    newpat = 1;

/* Set initial directory */
  FSPathInfo.Buffer = (UBYTE *)fsrequest->dirname;
  FSPathInfo.BufferPos = strlen(FSPathInfo.Buffer);
  if (lstrcmp(FSPathInfo.Buffer, FSPathBuf) != 0)
  {
    fsvirgindir = 1;
  }

/* Set initial filename */
  FSFileInfo.Buffer = (UBYTE *)fsrequest->filename;
  FSFileInfo.BufferPos = strlen(FSFileInfo.Buffer);

/* Open and initialize everything, start IDCMP processing */
  status = InitFSWindow();
  if (status != 0)
  {
    FSWinTitle();

/* Redisplay buffered data if still valid */
    if (FSTestOldLock() != 0 && fsvirgindir <= 0)
    {
      if (newpat != 0) 		/* Pattern changed, but still have old data */
      {
        FSMatchPattern();
      }
      else			/* Otherwise just redraw them from static data */
      {
        FSSetFileGads();
        FSDisableAllFGads();
      }
    }

    FSDone = 0;
    while (FSDone == 0)
    {
      if (fsvirgindir != 0)
      {
        if (FSGrabEntry() == 0)
          goto FSFailed;
        signals = FSSignal | FSUserSignal;
      }
      else
      {
        signals = Wait((LONG)FSSignalMask);
      }

      if ((signals & FSUserSignal) != 0)
      {
        i = (FSReq->userfunc)(FSReq, FSWin);
        if (i != 0)
          fsvirgindir = 1;
      }

      if ((signals & FSSignal) != 0)
        CheckFSIDCMP();
    }
  }

/***********/
  FSFailed:
/***********/
  if (FSRPort != 0)
  {
    FreeMem(FSRPort, (LONG)sizeof(struct RastPort));
    FSRPort = 0;
  }
#ifdef STANDALONE
  if (FSTextFont != 0)
  {
    CloseFont(FSTextFont);
    FSTextFont = 0;
  }
#endif
  if (FSWin != 0)
  {
    FSReq->leftedge = FSWin->LeftEdge;
    FSReq->topedge = FSWin->TopEdge;
    CloseWindow(FSWin);
    FSWin = 0;
  }
#ifdef STANDALONE
  FreeFSVBDelay();
#endif
  return(status);
}

/* -------------------------------------------------------------------- */
LONG InitFSWindow()
{
  LONG i, xinc, xleft, xmax;
  struct Gadget *tgad;

/* Initialize a timer device for delays */
#ifdef STANDALONE
  if (InitFSVBDelay() == 0)
  {
    return(0L);
  }
#endif

/* Initialize FSWin title string */
  safestrcpy(FSWinTitleStr, FSReq->titlestr, 28L);
  (VOID)strcat(FSWinTitleStr, " ");
  fstitlelength = strlen(FSWinTitleStr);

  fserrmsgs[27] = FSReq->readingstr;
  fserrmsgs[28] = FSReq->sortingstr;
  fserrmsgs[22] = FSReq->emptydirstr;
  fserrmsgs[24] = FSReq->nomatchstr;
  fserrmsgs[29] = FSReq->selectfilestr;
  if (fserrmsgs[27] == 0)
    fserrmsgs[27] = fserrmsg27;
  if (fserrmsgs[28] == 0)
    fserrmsgs[28] = fserrmsg28;
  if (fserrmsgs[22] == 0)
    fserrmsgs[22] = fserrmsg22;
  if (fserrmsgs[24] == 0)
    fserrmsgs[24] = fserrmsg24;
  if (fserrmsgs[29] == 0)
    fserrmsgs[29] = fserrmsg29;

  if (FSReq->pathgadstr != 0)
    FSPathTxt.IText = (UBYTE *)FSReq->pathgadstr;
  else
    FSPathTxt.IText = FSPathLabelStr;

  if (FSReq->filegadstr != 0)
    FSFileTxt.IText = (UBYTE *)FSReq->filegadstr;
  else
    FSFileTxt.IText = FSFileLabelStr;

/* Set our directory gadget names to whatever the user has out there */
  if (devList == 0)	/* If already set, don't reset */
  {
    if (FSGetDevs() == 0)
    {
      return(0L);
    }
    SetDevGads();
  }

/* Set Select and Cancel gadget texts */
  FSSelectTxt.IText = (UBYTE *)FSReq->selectstr;
  FSSelectTxt.LeftEdge = 37 - strlen(FSReq->selectstr) * 4;

  FSCancelTxt.IText = (UBYTE *)FSReq->cancelstr;
  FSCancelTxt.LeftEdge = 37 - strlen(FSReq->cancelstr) * 4;

/* Center the Select, Cancel, and LastDir gadgets */
  if (FSReq->specgadstr != 0)
  {
    FSnw.FirstGadget = &FSUserGad;
    FSUserTxt.IText = (UBYTE *)FSReq->specgadstr;
    FSUserTxt.LeftEdge = 37 - strlen(FSReq->specgadstr) * 4;
    xleft = 4;
    xmax = 4;
    xinc = 75;
  }
  else
  {
    FSnw.FirstGadget = &FSSelectGad;
    xleft = 38;
    xmax = 3;
    xinc = 95;
  }

  tgad = FSnw.FirstGadget;
  for (i = 0; i < xmax; i++)
  {
    tgad->LeftEdge = xleft + i * xinc;
    tgad = tgad->NextGadget;
  }

/* Determine which screen to open the FileSelect Window on */
  if (FSReq->userscreen == 0)
  {
    FSnw.Type = WBENCHSCREEN;
    FSnw.Screen = 0;
  }
  else
  {
    FSnw.Type = CUSTOMSCREEN;
    FSnw.Screen = FSReq->userscreen;
  }
  FSnw.LeftEdge = FSReq->leftedge;
  FSnw.TopEdge = FSReq->topedge;
  if (FSReq->windowflags != 0)
    FSnw.Flags = FSReq->windowflags;

  if ((FSWin = OpenWindow(&FSnw)) == 0)
  {
    return(0L);
  }
  else
  {
/* Allocate/clone/initialize RastPort copy for rendering Text */
#ifdef STANDALONE
    if ((FSTextFont = OpenFont(&Def8Text)) == 0)
      return(0L);
#endif

    if ((FSRPort = (struct RastPort *)AllocMem((LONG)sizeof(struct RastPort), 0L)) == 0)
      return(0L);
    CopyMem((BYTE *)FSWin->RPort, (BYTE *)FSRPort, (LONG)sizeof(struct RastPort));

#ifdef STANDALONE
    SetFont(FSRPort, FSTextFont);
#else
    SetFont(FSRPort, &Def8TextData);
#endif

    if (FSReq->initfunc != 0)
      (VOID)(FSReq->initfunc)(FSReq, FSWin);

    FSSignal = 1L << FSWin->UserPort->mp_SigBit;
    FSSignalMask = FSSignal;
    if (FSReq->userport != 0 && FSReq->userfunc != 0)
    {
      FSUserSignal = 1L << FSReq->userport->mp_SigBit;
      FSSignalMask |= FSUserSignal;
    }
    else
      FSUserSignal = 0;

    ScreenToFront(FSWin->WScreen);

    PrintIText(FSWin->RPort, &FSPathTxt, 4L, 125L);

    MyActivateGad(&FSFileGad, FSWin);
    FSCountDown = 0;
    return(1L);
  }
}

/* -------------------------------------------------------------------- */
LONG CreateVBTimer(name, pport, ptreq)
  BYTE *name;
  struct MsgPort **pport;
  struct timerequest **ptreq;
{
  if ((*pport = CreatePort(name, 0L)) != 0)
  {
    if ((*ptreq = (struct timerequest *)CreateExtIO(*pport, (LONG)sizeof(struct timerequest))) != 0)
    {
      if (OpenDevice(TimerName, (LONG)UNIT_VBLANK, (struct IORequest *)*ptreq, 0L) == 0)
      {
        return(1L);
      }
    }
  }
  return(0L);
}

/* --------------------------------------------------------------------
 * Initialize a VBLANK timer for scroll arrow timings
 * -------------------------------------------------------------------- */
LONG InitFSVBDelay()
{
  return(CreateVBTimer(0L, &FSTimerPort, &FSDelayReq));
}

/* --------------------------------------------------------------------
 * Free above timer.
 * -------------------------------------------------------------------- */
VOID FreeFSVBDelay()
{
  FreeVBTimer(&FSTimerPort, &FSDelayReq);
}

/* -------------------------------------------------------------------- */
VOID FreeVBTimer(pport, ptreq)
  struct MsgPort **pport;
  struct timerequest **ptreq;
{
  struct timerequest *treq;

  if (*pport != 0)
  {
    if ((treq = *ptreq) != 0)
    {
      if (treq->tr_node.io_Device != 0)
      {
        AbortAsyncIO((struct IORequest *)treq);
        CloseDevice((struct IORequest *)treq);
      }
      DeleteExtIO((struct IORequest *)treq);
      *ptreq = 0;
    }
    DeletePort(*pport);
    *pport = 0;
  }
}

/* -------------------------------------------------------------------- */
VOID __stdargs FSVBDelay(timedelay)
  ULONG timedelay;
{
  FSDelayReq->tr_node.io_Command = TR_ADDREQUEST;
  FSDelayReq->tr_time.tv_micro = timedelay * 20000;
  FSDelayReq->tr_time.tv_secs = 0;
  (VOID)DoIO((struct IORequest *)FSDelayReq);
}

/* --------------------------------------------------------------------
 * Clean out any pending async IO in a given IORequest type struct.
 * --------------------------------------------------------------------	*/
VOID AbortAsyncIO(req)
  struct IORequest *req;
{
  if (req->io_Command != 0 && CheckIO(req) == 0)
  {
    (VOID)AbortIO(req);
    (VOID)WaitIO(req);
  }
}

/* -------------------------------------------------------------------- */
LONG FSGrabEntry()
{
  struct file_node *tnode;

  if (fsvirgindir > 0)		/* Brand new list */
  {
    fstitstatus = 27;		/* Set our windowtitle to Reading */
    FSWinTitle();

    FreeAllFNodes();		/* Clean out old list */
    FSSetKnob();		/* Show full height knob now */
    FSDisableAllFGads();

    if (FSLockDir() == 0)	/* Try to lock the new path */
      goto CrapOut1; 		/* Lock failed */
  }

  if (fnList == 0)
  {
    if ((fnList = AllocMem ((LONG)sizeof(struct MinList), 0L)) == 0)
      goto CrapOut0;
    NewList (fnList);
  }

  if (FSGetNextFib() <= 0) 	/* Try to Examine entry */
    goto CrapOut1;		/* We are done!? */

/* Initialize new node */
  if ((tnode = AllocFileNode()) == 0)
    goto CrapOut0;

  if (fsvirgindir > 0)		/* New list, needs new head! */
    fsvirgindir = -1; 		/* Still TRUE, but avoid relocking dir */
  
/* Fill in the info */
  FillFileNode(tnode);
  return(1L);

CrapOut0:
  FreeFileSelect();
  fstitstatus = 26;
  FSWinTitle();
  return(0L);

CrapOut1:
  if ((fsflags & FS_DELAYED_SORT) == 0)
    FSUpdateSort();
  if (fsnumentries > 10 && (fsnumentries & 0x01) != 0)
    FSResetKnob();
  fsvirgindir = 0;
  FSClearLock();
  FSWinTitle();
  return(1L);
}

/* -------------------------------------------------------------------- */
LONG FSLockDir()
{
  LONG namlen;
  struct CommandLineInterface *thiscli;
  struct Process *thisproc;
  struct Window *oldwin;
  UBYTE *tstr;

/* Inhibit System Requesters */
  thisproc = (struct Process *)FindTask(0L);
  oldwin = (struct Window *)thisproc->pr_WindowPtr;
  thisproc->pr_WindowPtr = (APTR)-1L;

/* Backup last locked pathname and set new pathname */
  (VOID)strcpy(OldFSPathBuf, FSPathBuf);
  (VOID)strcpy(FSPathBuf, FSPathInfo.Buffer);

/* Make sure we release previous lock */
  FSClearLock();

/* Allocate a fileinfoblock, if we have to */
  if (AllocFSFib() == 0)
  {
    fstitstatus = 26;
    goto SNAFUDIR;
  }

/* Lock new path */
locknewpath:
  if (FSPathBuf[0] != 0) /* Path is named, no guesswork */
  {
    FSPutPath();
    if ((FSLock = (BPTR)Lock(FSPathBuf, (LONG)ACCESS_READ)) == 0)
    {
      fstitstatus = ioerrnum(IoErr());
      goto SNAFUDIR;
    }
    fsdirlocked = 1;	/* Got a live one here! */
  }
  else			/* Use current directory */
  {
    if (thisproc->pr_CurrentDir == 0)
    {
      fstitstatus = 3;
      goto SNAFUDIR;
    }
    FSLock = (BPTR)thisproc->pr_CurrentDir;
    fsdirlocked = 0;
  }

/* Get a FileInfoBlock for this lock */
  if (Examine((BPTR)FSLock, (BPTR)FSFib) == 0)
  {
    fstitstatus = ioerrnum(IoErr());
    goto SNAFUDIR;
  }
  else
  {
    if (FSFib->fib_DirEntryType < 0)
    {
      fstitstatus = 2;
      goto SNAFUDIR;
    }
  }

/* Copy DateStamp for later comparison */
  FSdirstamp = FSFib->fib_Date;

  if (FSPathBuf[0] == (BYTE)0) /* Try to extract pathname from AmigaDOS */
  {
    if (thisproc->pr_CLI != 0) /* is this a CLI? */
    {
      thiscli = (struct CommandLineInterface *)((ULONG)thisproc->pr_CLI << 2L);
      tstr = (UBYTE *)((ULONG)thiscli->cli_SetName << 2L);
      namlen = (LONG)*tstr;
      safestrcpy(FSPathBuf, (BYTE *)((ULONG)tstr + 1L), (LONG)namlen);
    }
    else /* must be WorkBench, use Fib name */
    {
      MakePathString((struct FileLock *)FSLock, FSPathBuf);
    }
/* Copy path to StringInfo buffer, display */
    (VOID)strcpy(FSPathInfo.Buffer, FSPathBuf);
    FSPutPath();
  }
  thisproc->pr_WindowPtr = (APTR)oldwin;
  return(1L);

/* In case of dire directory problem */
SNAFUDIR:
  thisproc->pr_WindowPtr = (APTR)oldwin;
  return(0L);
}

/* --------------------------------------------------------------------
 * Called when diskremoved, to see if the disk we are displaying has
 * changed and needs to be re-read.
 * -------------------------------------------------------------------- */
LONG FSTestOldLock()
{
  LONG locked;
  LONG status, oldtitstatus;
  struct FileInfoBlock *fib;
  struct FileLock *flock = 0;
  struct Process *proc;
  struct Window *oldwin;

  status = 0;
  locked = 0;
  oldtitstatus = fstitstatus;
  proc = (struct Process *)FindTask(0L);
  oldwin = (struct Window *)proc->pr_WindowPtr;
  proc->pr_WindowPtr = (APTR)-1L;

/* Allocate a fileinfoblock */
  if ((fib = (struct FileInfoBlock *)AllocMem((LONG)sizeof(struct FileInfoBlock), MEMF_CLEAR)) == 0)
  {
    fstitstatus = 26;
    goto BadLock;
  }

/* Try to lock path */
  if (FSPathBuf[0] != 0)	/* Path is named, no guesswork */
  {
    if ((flock = (struct FileLock *)Lock(FSPathBuf, (LONG)ACCESS_READ)) == 0)
    {
      fstitstatus = ioerrnum(IoErr());
      goto BadLock;
    }
    locked = 1;
  }
  else	/* Use current directory */
    flock = (struct FileLock *)proc->pr_CurrentDir;

/* Examine the root block */
  if (Examine((BPTR)flock, (BPTR)fib) == 0)
  {
    fstitstatus = ioerrnum(IoErr());
    goto BadLock;
  }
  else
  {
    if (fib->fib_DirEntryType < 0)
    {
      fstitstatus = 3;
      goto BadLock;
    }
  }
  if (FSdirstamp.ds_Days == fib->fib_Date.ds_Days &&
      FSdirstamp.ds_Minute == fib->fib_Date.ds_Minute &&
      FSdirstamp.ds_Tick == fib->fib_Date.ds_Tick)
    status = 1;
  else
    status = 0;

/* In case of dire directory problem */
BadLock:
  proc->pr_WindowPtr = (APTR)oldwin;
  if (locked != 0)
  {
    UnLock((BPTR)flock);
  }

  if (fib != 0)
  {
    FreeMem(fib, (LONG)sizeof(struct FileInfoBlock));
  }

  if (status == 0)
  {
    fsvirgindir = 1;
    FSdirstamp.ds_Days = FSdirstamp.ds_Minute = FSdirstamp.ds_Tick = 0;
    if (fstitstatus != oldtitstatus)
      FSWinTitle();
  }
  return(status);
}

/* --------------------------------------------------------------------
 * Constructs a circular doubly-linked list of device name nodes
 * --------------------------------------------------------------------	*/
LONG FSGetDevs()
{
  LONG blen;
  LONG ramflag = 0;		/* Flag whether RAM: device found */
  struct DeviceNode *dn;
  struct dev_node *tnode;
  struct DosInfo *di;
  struct RootNode *root;
  UBYTE *bstring;

/* Allocate a MinList for use */
  if (devList == 0)
  {
    if ((devList = AllocMem((LONG)sizeof(struct MinList), 0L)) == 0)
      return (0L);
  }
  NewList (devList);

/* Grab pointer to DosInfo device root */
  root = (struct RootNode *)DOSBase->dl_Root;
  di = (struct DosInfo *)BADDR(root->rn_Info);
  dn = (struct DeviceNode *) BADDR(di->di_DevInfo);

/* Don't let other processes muck with us during this sneaky stuff */
  Forbid();

/* Starting with root devinfo, set devnames till out of devices */
  for (; dn != 0; dn = (struct DeviceNode *)BADDR(dn->dn_Next))
  {
  /* Found a device? */
    if ((dn->dn_Type == 0) && (dn->dn_Task != 0) && (dn->dn_Name != 0))
    {
      if ((tnode = AllocDevNode()) != 0)	/* Try to allocate a node */
      {
        bstring = (UBYTE *)BADDR(dn->dn_Name);	/* Pointer to BSTR name */
        blen = (LONG)bstring[0];		/* Length of BSTR	*/
        CopyMem((BYTE *)(bstring + 1), (char *)tnode->name, (LONG)blen);
        bstring = tnode->name;
        bstring[blen] = ':';	/* Append a colon to copy of name */

        if (lstrcmp(bstring, RamDirNameStr) == 0)
          ramflag = 1;

	AddHead(devList, (struct Node *)tnode);
      }
      else	/* Can't allocate a node, free 'em all and crap out */
      {
        Permit();
        FreeAllDNodes();
        return(0L);
      }
    }
  }
  Permit();

  if (ramflag == 0)	/* Ram device wasn't out there, add it to list */
  {
    if ((tnode = AllocDevNode()) == 0)
    {
      FreeAllDNodes();
      return(0L);
    }
    (VOID)strcpy((BYTE *)tnode->name, RamDirNameStr);
    AddHead(devList, (struct Node *)tnode);
  }

  lastdev = tnode;	/* This is the first node, start display with this */
  return(1L);
}

/* -------------------------------------------------------------------- */
VOID CheckFSIDCMP()
{
  BYTE *tstr1, *tstr2;
  LONG my;
  struct Gadget *tgadget;
  struct IntuiMessage *imsg;
  ULONG class, qualifier;
  ULONG code;
  ULONG secs, micros;

  while ((imsg = (struct IntuiMessage *)GetMsg(FSWin->UserPort)) != 0)
  {
    class = imsg->Class;
    qualifier = imsg->Qualifier;
    code = imsg->Code;
    micros = imsg->Micros;
    secs = imsg->Seconds;
    my = imsg->MouseY;

    tgadget = (struct Gadget *)imsg->IAddress;
    ReplyMsg((struct Message *)imsg);

    switch (class)
    {
      case INTUITICKS:
        CheckFSArrows();
        break;
      case RAWKEY:
        switch (code)
        {
          case 0x45:			/* Escape */
            FSDone = 1;
            FSReq->fullname[0] = (BYTE)0;
            break;
          case 0x4c:			/* Up cursor 	*/
            FSScrollFileGads(1L);
            break;
          case 0x4d:			/* Down cursor 	*/
            FSScrollFileGads(0L);
            break;
        }
        break;
      case MOUSEBUTTONS:
        if (code == SELECTDOWN)
        {
          if (fsstartedstr != 0)
            FSEndString();
        }
        else if (code == MENUUP)
        {
          if (fsstartedstr != 2)
            MyActivateGad(&FSPathGad, FSWin);
          else
            MyActivateGad(&FSFileGad, FSWin);
        }
        break;
      case GADGETDOWN:
        if (fsstartedstr != 0)
        {
          FSEndString();
        }
        code = tgadget->GadgetID;
        switch (code)
        {
          case 10:		/* Alpha */
          case 11:		/* Size  */
          case 12:		/* Time  */
            FSDoSortGadget((LONG)code - 10);
            break;
          case 30:		/* FSPatternGad	*/
            fsstartedstr = 3;
            break;
          case 31:		/* FSFileGad */
            fsstartedstr = 1;
            break;
          case 32:		/* FSPathGad */
           fsstartedstr = 2;
           break;
          case 33:		/* SlideGad 	*/
          case 40:		/* Up Arrow 	*/
          case 41:		/* Down Arrow	*/
          case 42:		/* Up Device	*/
          case 43:		/* Down Device	*/
            FSStartScrollGad(code);
            break;
          case 50:		/* FSFileGad0	*/
            code = (my - 12) / 11;
            if (FSFileNodes[code] != 0)
            {
              fscursecs = secs;
              fscurmicros = micros;
              HCompEntry(code);
              FSVBDelay(5L);
              HCompEntry(code);
              FSDoFileGad((LONG)my);
              fsoldsecs = secs;
              fsoldmicros = micros;
            }
            break;
        }
        break;
      case GADGETUP:
        code = tgadget->GadgetID;
        if (fsstartedstr != 0)
        {
          if ((qualifier & 0x0003) != 0)
          {
            if (fsstartedstr == 1)
            {
              MyActivateGad(&FSPathGad, FSWin);
            }
            else
            {
              MyActivateGad(&FSFileGad, FSWin);
            }
            code = 100;
          }
          else
          {
            if (fsstartedstr == 1)
            {
              if ((tstr1 = myrindex(FSFileInfo.Buffer, (LONG)'/')) != 0)
              {
                *tstr1 = '\x0';
                tstr1++;
                (VOID)strcpy(FSwstr, tstr1);
              }
              else
              {
                if ((tstr2 = myrindex(FSFileInfo.Buffer, (LONG)':')) != 0)
                {
                  tstr2++;
                  (VOID)strcpy(FSwstr, tstr2);
                  *tstr2 = (BYTE)0;
                }
              }
              if (tstr1 != 0 || tstr2 != 0)
              {
                (VOID)strcpy(FSPathInfo.Buffer, FSFileInfo.Buffer);
                (VOID)strcpy(FSPathBuf, FSFileInfo.Buffer);
                (VOID)strcpy(FSFileInfo.Buffer, FSwstr);
                FSFileInfo.BufferPos = strlen(FSFileInfo.Buffer);
                RefreshGList(&FSFileGad, FSWin, 0L, 2L);
                MyActivateGad(&FSFileGad, FSWin);
                fsvirgindir = 1;
              }
            }
          }
          fsstartedstr = 0;
        }
        FSDoGadget(code);
        break;
      case DISKREMOVED:
      case DISKINSERTED:
        if (fsvirgindir == 0)	 /* If the disk has stopped spinning...    */
          (VOID)FSTestOldLock(); /* See if we can lock our old place still */
        break;
    }
  }
}

