;/* Find.c - Amiga Mail ExAll() example.
lc -cfis -v -d0 -b0 -j73 Find.c
blink from Find.o to Find lib lib:amiga.lib; if you don't have pragmas
quit
 *
 * Pure code if pragmas are used.
 * Tuesday, 16-Jul-91 16:21:14, Ewout
 *
 * Compiled with SAS/C 5.10a:
 */
/* (c)  Copyright 1991 Commodore-Amiga, Inc.   All rights reserved.
The information contained herein is subject to change without notice,
and is provided "as is" without warranty of any kind, either expressed
or implied.  The entire risk as to the use of this information is
assumed by the user.
*/

#include <exec/memory.h>
#include <dos/dosextens.h>
#include <dos/rdargs.h>
#include <dos/exall.h>

#include <clib/exec_protos.h>
#include <clib/dos_protos.h>
#include <clib/utility_protos.h>

/* undef PRAGMAS if you don't have them */
/* #define PRAGMAS */
#undef PRAGMAS
#ifdef PRAGMAS
#include <pragmas/exec_pragmas.h>
#include <pragmas/dos_pragmas.h>
#include <pragmas/utility_pragmas.h>
#else
struct ExecBase *SysBase;
struct DosLibrary *DOSBase;
struct Library *UtilityBase;

#endif

/* ExAll buffersize to receive data in */
#define BUFFERSIZE 512
/* Default buffersize for hold fully qualified filenames */
#define NAMEBUFFERSIZE 512

/* Used to pass data around to functions */
struct FindControl
{
  struct DosLibrary *fc_DOSBase;
  UBYTE          *fc_Parsebuffer;       /* Buffer which contains the parsed pattern */
  ULONG           fc_Parselength;       /* The length of this buffer */
  UBYTE          *fc_Namebuffer;        /* Buffer to hold the filename */
  ULONG           fc_Namelength;        /* The length of that buffer */
  BOOL            fc_Files;  /* BOOLEAN which tells if we should only look for files */
  BOOL            fc_Dirs;   /* BOOLEAN which tells if we should only look for dirs */
  BOOL            fc_All;    /* ALL keyword? */
};

static UBYTE   *VersTag = "\0$VER: Find 37.1 (16.07.91)";

LONG            main(VOID);
LONG            ScanDirectory(struct FindControl *, UBYTE *);
BOOL            IsAssign(struct FindControl *, UBYTE *);
LONG            MultiScanDirectory(struct FindControl *, UBYTE *);
UWORD           StrLen(UBYTE *);

LONG
main(VOID)
{
#ifdef PRAGMAS
  struct DosLibrary *DOSBase;
  struct Library *UtilityBase;

#endif
  struct RDArgs  *readargs;
  LONG            rargs[6];
  struct FindControl *fc;
  UBYTE          *pattern, **directories;
  struct Process *process;
  APTR            windowptr;
  COUNT           i;
  LONG            rc = 0, error = 0, fatalerror = 0;

#ifndef PRAGMAS
  /* set up SysBase */
  SysBase = (*((struct Library **) 4));
#endif

  /* Fail silently if < 37 */
  if (DOSBase = (struct DosLibrary *) OpenLibrary("dos.library", 37))
  {
    UtilityBase = DOSBase->dl_UtilityBase;

    rargs[0] = 0L;
    rargs[1] = 0L;
    rargs[2] = 0L;
    rargs[3] = 0L;
    rargs[4] = 0L;
    rargs[5] = 0L;

    if (readargs =
        ReadArgs("PATTERN/A,DIRECTORY/A/M,FILES/S,DIRS/S,ALL/S,BUFFER/K/N",
                 rargs,
                 NULL))
    {

      if (fc = AllocMem(sizeof(struct FindControl), MEMF_CLEAR))
      {

#ifdef PRAGMAS
        fc->fc_DOSBase = DOSBase;
#endif

        pattern = (UBYTE *) rargs[0];

        fc->fc_Parselength = StrLen(pattern) * 3;
        if (fc->fc_Parsebuffer = AllocMem(fc->fc_Parselength, MEMF_CLEAR))
        {

          /* Make pattern uppercase for possible character classes */
          i = 0;
          while (pattern[i])
            pattern[i] = ToUpper(pattern[i++]);

          if ((ParsePatternNoCase(pattern,
                                  fc->fc_Parsebuffer,
                                  fc->fc_Parselength)) != -1)
          {

            directories = (UBYTE **) rargs[1];

            fc->fc_Files = (BOOL) rargs[2];
            fc->fc_Dirs = (BOOL) rargs[3];
            fc->fc_All = (BOOL) rargs[4];

            /*
             * Both set or cleared, clear both anyway. Easier checking later on.
             */
            if (fc->fc_Files == fc->fc_Dirs)
              fc->fc_Files = fc->fc_Dirs = FALSE;

            if (rargs[5])
              fc->fc_Namelength = *((LONG *) rargs[5]);

            if (fc->fc_Namelength < NAMEBUFFERSIZE || fc->fc_Namelength > 4096)
              fc->fc_Namelength = NAMEBUFFERSIZE;

            if (fc->fc_Namebuffer = AllocMem(fc->fc_Namelength, MEMF_CLEAR))
            {
              process = (struct Process *) FindTask(NULL);
              windowptr = process->pr_WindowPtr;
              process->pr_WindowPtr = (APTR) - 1L;

              while (*directories)
              {

                /*
                 * Check if this is a standalone assign which appears in the assign
                 * list?
                 */
                if (IsAssign(fc, *directories))
                  error = MultiScanDirectory(fc, *directories++);
                else
                  error = ScanDirectory(fc, *directories++);

                if (error != 0)
                  break;
              }

              process->pr_WindowPtr = windowptr;

              FreeMem(fc->fc_Namebuffer, fc->fc_Namelength);
            }
            else
              fatalerror = ERROR_NO_FREE_STORE;
          }
          else
            fatalerror = ERROR_BAD_TEMPLATE;

          FreeMem(fc->fc_Parsebuffer, fc->fc_Parselength);
        }
        else
          fatalerror = ERROR_NO_FREE_STORE;
        FreeMem(fc, sizeof(struct FindControl));
      }
      else
        fatalerror = ERROR_NO_FREE_STORE;

      FreeArgs(readargs);

    }
    else
      fatalerror = IoErr();

    /*
     * Error handling: To be informative, errors are shown while scanning, so the
     * file name which caused the error can be displayed. Other errors are shown
     * here. Errors which occured in the main loop are considered fatal, others
     * (except BREAK) just error.
     */

    if (fatalerror)
    {
      error = fatalerror;
      PrintFault(fatalerror, NULL);
    }

    SetIoErr(error);
    if (error != 0)
    {
      if (fatalerror)
        rc = RETURN_FAIL;
      else if (error == ERROR_BREAK)
        rc = RETURN_WARN;
      else
        rc = RETURN_ERROR;
    }

    CloseLibrary((struct Library *) DOSBase);
  }
  else
    rc = RETURN_FAIL;
  return (rc);
}


LONG
ScanDirectory(struct FindControl * fc, UBYTE * source)
{
#ifdef PRAGMAS
  struct DosLibrary *DOSBase = fc->fc_DOSBase;

#endif
  LONG            vargs[1];
  struct ExAllControl *excontrol;
  struct ExAllData *ead, *buffer;
  BPTR            sourcelock, namelock, olddirlock;
  BOOL            exmore;
  LONG            error;

  /*
   * Because this function may be recursively, get a fresh buffer per function call.
   */
  if (buffer = AllocMem(BUFFERSIZE, MEMF_CLEAR))
  {

    /* Get a lock on the start directory and make it the current directory */
    if (sourcelock = Lock(source, SHARED_LOCK))
    {
      olddirlock = CurrentDir(sourcelock);

      if (excontrol = AllocDosObject(DOS_EXALLCONTROL, NULL))
      {

        do
        {
          /* Get both file name and type to support FILES/DIRS kewords */
          exmore = ExAll(sourcelock, buffer, BUFFERSIZE, ED_TYPE, excontrol);
          error = IoErr();
          if ((exmore == NULL && (error != ERROR_NO_MORE_ENTRIES)))
          {
            PrintFault(error, source);
            break;
          }
          if (excontrol->eac_Entries == 0)
            continue;

          ead = buffer;
          do
          {

            /* Check for CTRL-C */
            if (SetSignal(0L, SIGBREAKF_CTRL_C) & SIGBREAKF_CTRL_C)
            {
              error = ERROR_BREAK;
              PrintFault(error, NULL);
              exmore = FALSE;
              break;
            }

            /*
             * Check if this one matches. If it does see if it is of the right type.
             */
            if (MatchPatternNoCase(fc->fc_Parsebuffer, ead->ed_Name))
            {
              if ((ead->ed_Type < 0 && fc->fc_Dirs == FALSE)
                  || (ead->ed_Type > 0 && fc->fc_Files == FALSE))
              {
                /* It is. Lock it and get the fully qualified file name */
                if (namelock = Lock(ead->ed_Name, SHARED_LOCK))
                {
                  if ((NameFromLock(namelock,
                                    fc->fc_Namebuffer,
                                    fc->fc_Namelength)) == DOSTRUE)
                  {
                    vargs[0] = (LONG) fc->fc_Namebuffer;
                    VFPrintf(Output(), "%s\n", vargs);
                  }
                  else
                  {
                    error = IoErr();
                    PrintFault(error, ead->ed_Name);
                  }
                  UnLock(namelock);
                }
                else
                {
                  error = IoErr();
                  PrintFault(error, ead->ed_Name);
                }
              }
            }

            /*
             * If the ALL keyword is used and this is a directory, step in it by
             * calling this function recursively.
             */
            if (ead->ed_Type > 0 && fc->fc_All)
            {
              error = ScanDirectory(fc, ead->ed_Name);
              if (error != 0)
              {
                exmore = FALSE;
                break;
              }
            }
            ead = ead->ed_Next;
          } while (ead);
        } while (exmore);

        FreeDosObject(DOS_EXALLCONTROL, excontrol);

      }
      else
        error = ERROR_NO_FREE_STORE;

      CurrentDir(olddirlock);
      UnLock(sourcelock);
    }
    else
    {
      error = IoErr();
      PrintFault(error, source);
    }
    FreeMem(buffer, BUFFERSIZE);
  }
  else
    error = ERROR_NO_FREE_STORE;

  if (error == ERROR_NO_MORE_ENTRIES)
    error = 0;
  else if (error == ERROR_NO_FREE_STORE)
    PrintFault(error, NULL);

  return (error);
}

BOOL
IsAssign(struct FindControl * fc, UBYTE * name)
{
#ifdef PRAGMAS
  struct DosLibrary *DOSBase = fc->fc_DOSBase;
  struct Library *UtilityBase = DOSBase->dl_UtilityBase;

#endif
  struct DosList *doslist;
  UBYTE          *assignname;
  UCOUNT          assignlength;
  LONG            position;
  BOOL            result = FALSE;

  /* First lets check if this resembles a devicename. */
  position = SplitName(name, ':', fc->fc_Namebuffer, 0, fc->fc_Namelength);

  if (position != -1)
  {
    /* Hmmm. */
    if (name[position] == '\0')
    {

      /*
       * I guess it does. Lets see if we can find it in the assign list. Keep the
       * DoSList locked as briefly as possible. This shouldn't take long.
       */
      if (doslist = AttemptLockDosList(LDF_ASSIGNS | LDF_READ))
      {
        while (doslist = NextDosEntry(doslist, LDF_ASSIGNS))
        {

          /* It's a BPTR */
          assignname = (UBYTE *) BADDR(doslist->dol_Name);
          assignlength = assignname[0];

          if ((Strnicmp(assignname + 1, fc->fc_Namebuffer, assignlength)) == 0)
          {
            /* Yup, it is. */
            result = TRUE;
            break;
          }
        }
        UnLockDosList(LDF_ASSIGNS | LDF_READ);
      }    /* Can't lock DosList, don't bother */
    }
  }
  return (result);
}

LONG
MultiScanDirectory(struct FindControl * fc, UBYTE * source)
{
#ifdef PRAGMAS
  struct DosLibrary *DOSBase = fc->fc_DOSBase;

#endif
  struct DevProc *cproc = NULL;
  struct MsgPort *filesystemtask;
  LONG            error;

  filesystemtask = GetFileSysTask();

  do
  {
    /* Find handler */
    if (cproc = GetDeviceProc(source, cproc))
    {
      SetFileSysTask(cproc->dvp_Port);
      if ((NameFromLock(cproc->dvp_Lock,
           fc->fc_Namebuffer,
           fc->fc_Namelength)) == DOSTRUE)
      {
        error = ScanDirectory(fc, fc->fc_Namebuffer);
      }
      else
      {
        error = IoErr();
        PrintFault(error, source);
      }

      if (error != 0)
        break;
    }
    else
    {
      error = IoErr();
      PrintFault(error, source);
    }

    /* Handle multi-assign */
  } while (cproc && (cproc->dvp_Flags & DVPF_ASSIGN));

  SetFileSysTask(filesystemtask);
  if (cproc)
    FreeDeviceProc(cproc);

  return (error);
}

UWORD
StrLen(UBYTE * string)
{
  UBYTE          *length = string + 1;

  while (*string++ != '\0');
  return ((UWORD) (string - length));
}
