Table of Contents

About

This is a small program that will print out any files or directories starting with a path supplied by the command-line arguments. The program makes partial use of a Wizardry and Steamworks string-stack implementation for AmigaOS.

In order to compile using SAS/C, issue:

sc link depthTraversal.c

After that, you can issue for example:

depthTraversal RAM:

which will result in output similar to:

Specified path: RAM:
Dirs: RAM:T
Dirs: RAM:Clipboards
File: RAM:Disk.info
Dirs: RAM:T/UCC
Dirs: RAM:T/FMS
File: RAM:T/AmiTCP.log
File: RAM:T/FMS/Unit4
File: RAM:T/FMS/Unit3
File: RAM:T/FMS/Unit2
File: RAM:T/FMS/Unit1
File: RAM:T/FMS/Unit0

Related

Code

depthTraversal.c
/*************************************************************************/
/*    Copyright (C) 2015 Wizardry and Steamworks - License: GNU GPLv3    */
/*************************************************************************/
/*                                                                       */
/*  depthTraversal                                                       */
/*                                                                       */
/*  Traverses a directory path starting from the current directory.      */
/*                                                                       */
/*  This program uses a stack implementation for Amiga OS found at:      */
/*  http://grimore.org/amiga/development/data_structures/stacks/strings  */
/*                                                                       */
/*  Amiga OS using SAS/C and issuing: sc link depthTraversal.c           */
/*                                                                       */
/*************************************************************************/
 
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <setjmp.h>
 
#include <proto/exec.h>
#include <proto/dos.h>
 
/*************************************************************************/
/*          Wizardry and Steamworks' string stack implementation.        */
/*************************************************************************/
 
/* The stringStack structure with top being the index of the next element 
 * to be inserted in stack (the top-most element to be found at top - 1).
 */
typedef struct {
   ULONG size;
   UBYTE **store;
   ULONG top;
} stringStack;
 
/*
 * Creates a new stringStack with a given size.
 */
stringStack* stringStackCreate(ULONG size) {
   stringStack *s = (stringStack*)AllocMem(
       sizeof(stringStack), 
       MEMF_ANY|MEMF_CLEAR
   );
   if ((s->store = (UBYTE**)AllocMem(
       size * sizeof(UBYTE **), 
       MEMF_ANY|MEMF_CLEAR
       )) == NULL) return NULL;
   s->size = size;
   s->top = 0;
   return s;
}
 
/*
 * Takes as parameter a stringStack and returns 1 if the stack is empty
 * or 0 if the stack is not empty.
 */
BOOL stringStackIsEmpty(stringStack *s) {
    return (BOOL)(s->top == 0);
}
 
/*
 * Pushes an element onto the stringStack.
 */
void stringStackPush(stringStack *s, UBYTE *e) {
    UBYTE **sStore;
    if (s->top > s->size - 1) { // realloc for AmigaOS
        sStore = (UBYTE **)AllocMem(
            (s->size + 1) * sizeof(UBYTE **), 
            MEMF_ANY|MEMF_CLEAR
        );
        CopyMem(s->store, sStore, s->size * sizeof(UBYTE **));
        FreeMem(s->store, sizeof(s->store));
        s->store = sStore;
        ++s->size;
    }
    s->store[s->top] = (UBYTE *)AllocMem(
            strlen(e) * sizeof(UBYTE *) + 1, 
            MEMF_ANY|MEMF_CLEAR
    );
    CopyMem(e, s->store[s->top], strlen(e) * sizeof(UBYTE *) + 1);
    ++s->top;
}
 
/*
 * Pops an element off the stringStack or returns NULL in case the
 * stack is empty.
 */
UBYTE *stringStackPop(stringStack *s) {
   UBYTE *e;
   if (stringStackIsEmpty(s))
       return NULL;
   --s->top;
   e = (UBYTE *)AllocMem(
       strlen(s->store[s->top]) * sizeof(UBYTE *) + 1, 
       MEMF_ANY|MEMF_CLEAR
   );
   CopyMem(
       s->store[s->top], 
       e, 
       strlen(s->store[s->top]) * sizeof(UBYTE *) + 1
   );
   return e;
}
 
/*
 * Prints out the elements of the stringStack.
 */
void stringStackPrint(stringStack *s) {
    LONG i;
    if (stringStackIsEmpty(s)) {
        Printf("Stack is empty.\n");
        return;
    }
    Printf("Elements in the stack: ");
    i = s->top - 1;
    do {
        Printf("%s ", s->store[i]);
    } while (--i > -1);
    Printf("\n");
}
 
/*************************************************************************/
/*        Version string used for querrying the program version.         */
/*************************************************************************/
GLOBAL TEXT version_string[] = 
    "\0$VER: depthTraversal 1.0 (29 Jul 2015) by Wizardry and Steamworks";
 
/*************************************************************************/
/*              Amiga OS command-line arguments parameters.              */
/*************************************************************************/
STATIC CONST TEXT template[] = "PATH/A";
enum {
    PATH,
    NUM_ARGS
};
 
/*************************************************************************/
/*    Traverses a stack of paths and prints out files and directories.   */
/*************************************************************************/
LONG TraversePath(stringStack *dirStack) {
    struct FileInfoBlock *FIB = NULL;
    UBYTE *path;
    BPTR lock = NULL;
    UBYTE *nextPath;
    ULONG nextSize;
    jmp_buf env;
    LONG error;
 
    /* jump: Free any resources before returning an error. */
    error = setjmp(env);
    if(error != 0) {
        /* Free the file information block. */
        if(FIB != NULL) FreeDosObject(DOS_FIB, FIB);
        /* Release the lock. */
        if(lock != NULL) UnLock(lock);
        /* And return the error. */
        return error;
    }
 
    /* The directory stack is empty so return. */
    if(stringStackIsEmpty(dirStack))
        return 0;
 
    /* Pop the top element in the stack. */
    path = stringStackPop(dirStack);
 
    /* Lock the path. */
    if((lock = Lock(path, ACCESS_READ)) == NULL) {
        Printf("Unable to acquire a read lock to the specified path.\n");
        longjmp(env, RETURN_ERROR);
    }
 
    /* Create a new file information block to scan the path. */
    if((FIB = (struct FileInfoBlock *)
        AllocDosObject(DOS_FIB, NULL)) == NULL) {
        Printf("Unable to allocate file information block.\n");
        longjmp(env, RETURN_ERROR);
    }
 
    if(Examine(lock, FIB)) {
        /* Examine the rest of the entries in the directory. */
        while(ExNext(lock, FIB)) {
            /* check for Ctrl-C */
            if(CheckSignal(SIGBREAKF_CTRL_C) != FALSE)
                longjmp(env, RETURN_WARN);
            nextSize = strlen(path) + 
                strlen(FIB->fib_FileName) * 
                sizeof(UBYTE *) + 1;
            if((nextPath = (UBYTE*)
                AllocMem(nextSize, MEMF_ANY|MEMF_CLEAR)) == NULL) {
                Printf("Unable to allocate memory for path.\n");
                longjmp(env, RETURN_ERROR);
            }
            CopyMem(path, nextPath, nextSize);
            if(AddPart(nextPath, FIB->fib_FileName, nextSize) == FALSE) {
                Printf("Unable to add the file to the path.\n");
                longjmp(env, RETURN_ERROR);
            }
            /* Check whether the path is a file or a directory. */
            switch(FIB->fib_DirEntryType > 0) {
                case TRUE: // We have a directory.
                    Printf("Dirs: %s\n", nextPath);
 
                    // So we push it onto the stack.
                    stringStackPush(dirStack, nextPath);
 
                    break;
                default: // We have a file.
                    Printf("File: %s\n", nextPath);
                    break;
            }
        }
    }
 
    /* Free the file information block. */
    if(FIB != NULL) FreeDosObject(DOS_FIB, FIB);
 
    /* Release the lock. */
    if(lock != NULL) UnLock(lock);
 
    /* DEBUG: print the stack. */
    /* stringStackPrint(dirStack); */
 
    /* Recurse over the stack till it is empty. */
    return TraversePath(dirStack);
}
 
/*************************************************************************/
/*                          Program entry.                               */
/*************************************************************************/
int main(int argc, char **argv) {
    stringStack *dirStack = NULL;
    struct FileInfoBlock *FIB = NULL;
    BPTR lock = NULL;
    LONG size;
    jmp_buf env;
    LONG error;
 
    /* Setup command-line arguments to retrieve path.*/
    LONG *arg_array;
    struct RDArgs *rdargs;
    UBYTE *path;
 
    /* jump: Free any resources before returning an error. */
    error = setjmp(env);
    if(error != 0) {
        /* Free the file information block. */
        if(FIB != NULL) FreeDosObject(DOS_FIB, FIB);
        /* Release the lock. */
        if(lock != NULL) UnLock(lock);
        /* And return the error. */
        return error;
    }
 
    /* Allocate memory for arg array. */
    if((arg_array = AllocMem(NUM_ARGS, MEMF_ANY|MEMF_CLEAR)) == NULL) {
        Printf("Unable to allocate memory for command-line arguments.\n");
        longjmp(env, RETURN_ERROR);
    }
 
    /* Read command-line arguments */
    if((rdargs = ReadArgs(template, arg_array, NULL)) == NULL) {
        FreeArgs(rdargs);
        longjmp(env, RETURN_ERROR);
    }
    /* Allocate memory for the path name as the product between the lengh 
     * of the path name and the size of UBYTE plus a termination character. 
     */
    size = strlen((STRPTR)arg_array[PATH]) * sizeof(UBYTE) + 1;
    /* Allocate memory for path name. */
    if((path = AllocMem(size, MEMF_ANY|MEMF_CLEAR)) == NULL) {
        Printf("Unable to allocate memory for path name.\n");
        longjmp(env, RETURN_ERROR);
    }
    /* Copy path name to be able to free the command-line arguments. */
    CopyMem((STRPTR)arg_array[PATH], path, size);
    FreeArgs(rdargs);
 
    Printf("Specified path: %s\n", path);
 
    /* Lock the path. */
    if((lock = Lock(path, ACCESS_READ)) == NULL) {
        Printf("Unable to acquire a read lock to the specified path.\n");
        longjmp(env, RETURN_ERROR);
    }
 
    /* Create a new file information block to scan the path. */
    if((FIB = (struct FileInfoBlock *)
        AllocDosObject(DOS_FIB, NULL)) == NULL) {
        Printf("Unable to allocate file information block.\n");
        longjmp(env, RETURN_ERROR);
    }
 
    /* Attempt to examine the path. */
    if(!Examine(lock, FIB)) {
        Printf("Unable to examine %s\n", path);
        longjmp(env, RETURN_ERROR);
    }
 
    /* If the specified path is a file bail out. */
    if(FIB->fib_DirEntryType < 0) {
        Printf("Specified path is a file.\n");
        longjmp(env, RETURN_ERROR);
    }
 
    /* Free the file information block. */
    if(FIB != NULL) FreeDosObject(DOS_FIB, FIB);
 
    /* Release the lock. */
    if(lock != NULL) UnLock(lock);
 
    /* Allocate a new stack. */
    dirStack = stringStackCreate(1);
    /* Push the specified path onto the stack. */
    stringStackPush(dirStack, path);
 
    /* Now traverse the path and return any errors. */
    return TraversePath(dirStack);
}