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