;/* 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:
 */
/*
Copyright (c) 1991 Commodore-Amiga, Inc.

This example is provided in electronic form by Commodore-Amiga,
Inc. for use with the Amiga Mail Volume II technical publication.
Amiga Mail Volume II contains additional information on the correct
usage of the techniques and operating system functions presented in
these examples.  The source and executable code of these examples may
only be distributed in free electronic form, via bulletin board or
as part of a fully non-commercial and freely redistributable
diskette.  Both the source and executable code (including comments)
must be included, without modification, in any copy.  This example
may not be published in printed form or distributed with any
commercial product. However, the programming techniques and support
routines set forth in these examples may be used in the development
of original executable software products for Commodore Amiga
computers.

All other rights reserved.

This example is provided "as-is" and is subject to change; no
warranties are made.  All use is at your own risk. No liability or
responsibility is assumed.
*/

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