/* ArpFileScan 
 * Filename:    ArpFileScan
 * Author:      Mark R. Rinfret
 *
 * ArpFileScan builds an expanded list of filenames from a filename argument list. 
 * It is written to be entirely reentrant.
 */
#include <stdlib.h>
#include <fcntl.h>
#include <ctype.h>
#include <string.h>
#include <exec/types.h>
#include <libraries/arpbase.h>

/*  The UserAnchor structure is used by ARP's FindFirst/FindNext routines. */

struct UserAnchor {
    struct AnchorPath   ua_AP;
    BYTE                moreMem[255];
};

/*  The ListInfo structure tracks our filename list building process. */

typedef struct ListInfo {
    char                    **theList;
    int                     slotCount;          /* number of slots in the list. */
    int                     entryCount;         /* number of slots filled */
    struct DefaultTracker   *tracker;
    struct UserAnchor       anchor;
    } ListInfo;

/*  FUNCTION
 *      StoreTracker - get the LastTracker value in register a1.
 *
 *  SYNOPSIS
 *      struct DefaultTracker *StoreTracker(void);
 *
 *  DESCRIPTION
 *      StoreTracker is a simple function written in assembly language to provide
 *      access to the LastTracker item. This method was implemented because the
 *      ARP method (via IoErr()) is unreliable at this time.
 *
 *      StoreTracker *MUST* be called immediately after a call to an ARP library
 *      function which allocates a tracker. Otherwise, its results are unreliable.
 */

struct DefaultTracker*
StoreTracker(void)
{
    #asm
       move.l  a1,d0   ; Simulate function result. Is this Lattice-compatible?
    #endasm
}

/*  FUNCTION
 *      AddFile - add a filename to the filename list.
 *
 *  SYNOPSIS
 *      static int AddFile(ListInfo *fileList, const char *theFile);
 *
 *  DESCRIPTION
 *      AddFile attempts to add <theFile> to the filename list. The list will be
 *      expanded if necessary. 
 *
 *      AddFile returns zero if successful or 1 on error.
 */
  
static int
AddFile(ListInfo *fileList, const char *theFile)
{
    char    *fName;                 /* for a copy of theFile */
    char    **newList;
    int     newCount;
    size_t  newSize;
    struct DefaultTracker *newTracker;

    if (fileList->entryCount >= fileList->slotCount) { /* Expand the list? */
        newCount = fileList->slotCount + 50;
        newSize = sizeof(char *) * newCount;
        newList = ArpAlloc(newSize);
        newTracker = StoreTracker();
        if (newList == NULL) return 1;   /* Error! */
        fileList->slotCount = newCount;
        if (fileList->theList) {
            /* Copy old stuff to new list. */
            memcpy(newList, fileList->theList, newSize);
            /* Dispose of the old list. */
            FreeTrackedItem(fileList->tracker);
            fileList->tracker = newTracker;
        }
        fileList->theList = newList;
    }

    fName = ArpAlloc(strlen(theFile)+1);
    if (! fName) return 1;
    strcpy(fName, theFile);
    fileList->theList[fileList->entryCount++] = fName;
    return 0;
}


/*  FUNCTION
 *      Compare - perform case-insensitive compare of two filename strings.
 *
 *  SYNOPSIS
 *      static int Compare(const void *a, const void *b);
 *
 *  DESCRIPTION
 *      Compare simply provides an interface between qsort() and a string
 *      comparison routine. It returns the result of the string comparison.
 */

static int
Compare(const void *a, const void *b)
{
    return Strcmp(*(char **)a, *(char **)b);
}


/*  FUNCTION
 *      ArpFileScan - scan, expand and optionally sort a file specification list.
 *
 *  SYNOPSIS
 *      char **ArpFileScan(char **rawList, int rawCount, int *newCount; 
 *                         int sortFiles);
 *
 *  DESCRIPTION
 *      ArpFileScan treats each entry in <rawList> as a file specification and
 *      attempts to expand it into multiple filenames. The number of entries
 *      in <rawList> is defined by <rawCount>. If successful, ArpFileScan will
 *      return a pointer to a new list of filenames, passing the number of entries
 *      via <newCount>. If <sortFiles> is non-zero, the list will be sorted.
 *
 *      If ArpFileScan is unable to perform an allocation, NULL is returned.
 */

char **
ArpFileScan(char **rawList, int rawCount, int *newCount, int sortFiles)
{
    ListInfo    fileList;           /* Stack-based for reentrancy. */
    int         rawIndex = 0;
    int         result;
    char        *rawName;
    
    fileList.theList = NULL;       /* Initialize the list structure. */
    fileList.slotCount = 0;
    fileList.entryCount = 0;
    fileList.anchor.ua_AP.ap_StrLen = 255;  /* Want full path built. */
    fileList.anchor.ua_AP.ap_BreakBits |=  (SIGBREAKF_CTRL_C | SIGBREAKF_CTRL_D);

    while (rawIndex < rawCount) {
        rawName = rawList[rawIndex++];
        result = FindFirst(rawName, (struct AnchorPath *) &fileList.anchor);
        while (result == 0) {
            if (AddFile(&fileList, (const char *) &fileList.anchor.ua_AP.ap_Buf))
                return NULL;
            result = FindNext((struct AnchorPath *) &fileList.anchor);
        }
    }

    *newCount = fileList.entryCount;
    if (fileList.theList && sortFiles) {
        qsort(fileList.theList, fileList.entryCount, sizeof(char *), Compare);
    }
    return fileList.theList;
}
