HuntnGather – Rev 31

Subversion Repositories:
Rev:
///////////////////////////////////////////////////////////////////////////
//    Copyright (C) 2021 Wizardry and Steamworks - License: MIT          //
///////////////////////////////////////////////////////////////////////////

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#if !defined ___AmigaOS___
#include <dirent.h>
#include <sys/stat.h>
#include <signal.h>
#endif

#include <sys/types.h>
#include <sys/syslimits.h>

#if defined ___AmigaOS___
#include <proto/dos.h>
#include <proto/exec.h>
#include <proto/locale.h>
#endif

#if defined ___AsyncIO___
#include <asyncio.h>
#endif

#if !defined ___HAVE_GETOPT___
#include "getopt.h"
#endif

#include "StringStack.h"

// MemLib memory debugging.
#if defined MWDEBUG
#include "memwatch.h"
#endif

#define PROGRAM_VERSION "1.7.4"

#if defined ___AmigaOS___
/*************************************************************************/
/*        Version string used for querrying the program version.         */
/*************************************************************************/
TEXT version_string[] =
        "\0$VER: Gather " PROGRAM_VERSION " "__DATE__" by Wizardry and Steamworks";
#endif

#if !defined TRUE
#define TRUE 1;
#endif

#if !defined FALSE
#define FALSE 0;
#endif

#define ASYNC_BUF 8192
#define MAX_MEM 262144
#define LINE_BUF 256
#define DEFAULT_DATABASE_FILE "S:gather.db"

typedef struct {
        unsigned int dirs;
        unsigned int files;
} stats;

typedef struct {
        char *name;
        char *path;
} dbEntry;

typedef struct {
        char **database;
        unsigned int count;
} dbArray;

enum MODE {
        NONE,
        GATHER,
        REMOVE,
        CREATE
} operation;

unsigned int run = TRUE;
unsigned int verbose = TRUE;
unsigned int maxmem = MAX_MEM;
// Define global locale for string compare.
#if defined ___AmigaOS___
struct Locale *locale;
#endif

void SignalHandler(int sig) {
        // Toggle the run flag to stop execution.
        run = FALSE;
}

/*
        *
        * Used for sorting database lines.
        */
int QsortCompare(const void *a, const void *b) {
#if defined ___AmigaOS___
        return StrnCmp(
                locale,
                (STRPTR)(*(const char **)a),
                (STRPTR)*((const char **)b),
                -1,
                SC_ASCII
        );
#else
        return strcmp(*(const char **)a, *(const char **)b);
#endif
}

/*
        *
        * Gets the absolute path to file by name.
        */
char *PathToAbsolute(char *path) {
        char *abs;
#if defined ___AmigaOS___
        BPTR lock;
#endif

#if defined ___AmigaOS___
        if((abs = malloc(PATH_MAX * sizeof(*abs))) == NULL) {
                fprintf(stderr, "Memory allocation failure.\n");
                return NULL;
        }
        if((lock = Lock(path, SHARED_LOCK)) == 0) {
                fprintf(stderr, "Lock on %s failed.\n", path);
                return NULL;
        }
        if(NameFromLock(lock, abs, PATH_MAX) == FALSE) {
                fprintf(stderr, "Lock on %s failed.\n", path);
                UnLock(lock);
                return NULL;
        }
        UnLock(lock);
#else
        //abs = realpath(path, NULL);
        if((abs = malloc((strlen(path) + 1) * sizeof(*abs))) == NULL) {
                fprintf(stderr, "Memory allocation failure.\n");
                return NULL;
        }
        sprintf(abs, "%s", path);
#endif

        return abs;
}

/*
        *
        * Compares path parts for equality.
        */
#if defined ___AmigaOS___
BOOL PathCompare(char *path, char *look) {
#else
int PathCompare(char *path, char *look) {
#endif
        char *a;
        char *b;

        for(a = path, b = look; *a != '\0' && *b != '\0'; ++a, ++b) {
                if(*b != '\0' && *a != *b) {
                        return FALSE;
                }
        }

        return *b == '\0';
}

/*
        *
        * Gets the size of a file by name.
        */
int GetFileSize(char *dbFile) {
#if defined ___AsyncIO___
        struct AsyncFile *fp;
        LONG size;
#else
        FILE *fp;
        int size;
#endif

#if defined ___AsyncIO___
        if((fp = OpenAsync(dbFile, MODE_READ, ASYNC_BUF)) == NULL) {
#else
        if((fp = fopen(dbFile, "r")) == NULL) {
#endif
                fprintf(stderr, "Unable to open '%s' for reading.\n", dbFile);
                return -1;
        }

#if defined ___AsyncIO___
        if(SeekAsync(fp, 0, MODE_END) == -1) {
#else
        if(fseek(fp, 0L, SEEK_END) != 0) {
#endif
                fprintf(stderr, "Seek in file %s failed.\n", dbFile);
#if defined ___AsyncIO___
        CloseAsync(fp);
#else
                fclose(fp);
#endif
                return -1;
        }
#if defined ___AsyncIO___
        if((size = SeekAsync(fp, 0, MODE_CURRENT)) == -1) {
                fprintf(stderr, "Seek in file %s failed.\n", dbFile);
                CloseAsync(fp);
                return -1;
        }
#else
        size = ftell(fp);
#endif

#if defined ___AsyncIO___
   CloseAsync(fp);
#else
        fclose(fp);
#endif

        return size;
}

/*
        *
        * Counts the lines of a file.
        */
int CountFileLines(char *dbFile) {
#if defined ___AsyncIO___
        struct AsyncFile *fp;
        LONG c;
#else
        FILE *fp;
        char c;
#endif
        int lines;

#if defined ___AsyncIO___
        if((fp = OpenAsync(dbFile, MODE_READ, ASYNC_BUF)) == NULL) {
#else
        if((fp = fopen(dbFile, "r")) == NULL) {
#endif
                fprintf(stderr, "Unable to open '%s' for reading.\n", dbFile);
                return -1;
        }

        lines = 0;
        if(verbose) {
                fprintf(stdout, "Lines in '%s' so far: %d\r", dbFile, lines);
        }

#if defined ___AsyncIO___
        while(run && (c = ReadCharAsync(fp)) != -1) {
#else
        while(run && fscanf(fp, "%c", &c) == 1) {
#endif
#if defined ___AmigaOS___
                // Check if CTRL+C was pressed and abort the program.
                if(SetSignal(0L, SIGBREAKF_CTRL_C) & SIGBREAKF_CTRL_C) {
                        run = FALSE;
                        continue;
                }
#endif
                switch(c) {
                        case '\n':
                                ++lines;

                                if(verbose) {
                                        fprintf(stdout, "Lines in '%s' so far: %d\r", dbFile, lines);
                                }
                                break;
                }
        }

#if defined ___AsyncIO___
        CloseAsync(fp);
#else
        fclose(fp);
#endif

        if(verbose) {
                fprintf(stdout, "\n");
        }

        return lines;
}

/*
        *
        * Creates a temporary file and returns its name.
        */
char *CreateTemporaryFile(void) {
        char *name;

        name = tmpnam(NULL);

        return name;
}

/*
        *
        * Create multiple temporary files and return their names.
        */
char **CreateTemporaryFiles(int files) {
        char **tmpNames;
        int count;

        if((tmpNames = malloc(files * sizeof(*tmpNames))) == NULL) {
                fprintf(stderr, "Memory allocation failure.\n");
                return NULL;
        }

        if(verbose) {
                fprintf(stdout, "Creating temporary files...\r");
        }

        count = files;
        while(run && --count > -1) {
#if defined ___AmigaOS___
                // Check if CTRL+C was pressed and abort the program.
                if(SetSignal(0L, SIGBREAKF_CTRL_C) & SIGBREAKF_CTRL_C) {
                        run = FALSE;
                        continue;
                }
#endif
                tmpNames[count] = CreateTemporaryFile();

                if(verbose) {
                        fprintf(stdout, "Creating temporary files: %d%%\r", 100 - (int)(((float)count / files) * 100.0));
                }
        }

        if(verbose) {
                fprintf(stdout, "\n");
        }

        return tmpNames;
}


/*
        *
        * Skips a line in a file.
        */
#if defined ___AsyncIO___
void SkipLine(struct AsyncFile *fp) {
        LONG c;
        while(run && (c = ReadCharAsync(fp)) != -1) {
#else
void SkipLine(FILE *fp) {
        char c;
        while(run && fscanf(fp, "%c", &c) == 1) {
#endif
#if defined ___AmigaOS___
                // Check if CTRL+C was pressed and abort the program.
                if(SetSignal(0L, SIGBREAKF_CTRL_C) & SIGBREAKF_CTRL_C) {
                        run = FALSE;
                        continue;
                }
#endif
                switch(c) {
                        case '\n':
                                return;
                }
        }
}

/*
        *
        * Peeks at a line from a file.
        */
#if defined ___AsyncIO___
char *PeekLine(struct AsyncFile *fp) {
        LONG c;
#else
char *PeekLine(FILE *fp) {
        char c;
#endif
        char *line = NULL;
        char *real = NULL;
        unsigned int size;
        int i;

        size = LINE_BUF;
        if((line = malloc(size * sizeof(*line))) == NULL) {
                fprintf(stderr, "Memory allocation failure.\n");
                return NULL;
        }

        i = 0;
#if defined ___AsyncIO___
        while(run && (c = ReadCharAsync(fp)) != -1) {
#else
        while(run && fscanf(fp, "%c", &c) == 1) {
#endif
#if defined ___AmigaOS___
                // Check if CTRL+C was pressed and abort the program.
                if(SetSignal(0L, SIGBREAKF_CTRL_C) & SIGBREAKF_CTRL_C) {
                        run = FALSE;
                        continue;
                }
#endif
                switch(c) {
                        case '\n':
                                // Rewind the file by the number of read characters.
#if defined ___AsyncIO___
                                if(SeekAsync(fp, -(i + 1), MODE_CURRENT) == -1) {
                                        fprintf(stderr, "Could not seek in file.\n");
                                        free(line);
                                        return NULL;
                                }
#else
                                if(fseek(fp, -(i + 1), SEEK_CUR) != 0) {
                                        fprintf(stderr, "Could not seek in file.\n");
                                        free(line);
                                        return NULL;
                                }
#endif
                                return line;
                        default:
                                if(strlen(line) == size) {
                                        size = size * 1.5;
                                        real = realloc(line, size * sizeof(*line));
                                        if(real == NULL) {
                                                fprintf(stderr, "Memory reallocation failure.\n");
                                                free(line);
                                                return NULL;
                                        }
                                        line = real;
                                }
                                line[i] = c;
                                line[i + 1] = '\0';
                                break;
                }
                ++i;
        }

        if(line != NULL) {
                free(line);
        }

        return NULL;
}

/*
        *
        * Read a line from a file.
        */
#if defined ___AsyncIO___
char *ReadLine(struct AsyncFile *fp) {
        LONG c;
#else
char *ReadLine(FILE *fp) {
        char c;
#endif
        char *line = NULL;
        char *real = NULL;
        unsigned int size;
        unsigned int i;

        size = LINE_BUF;
        if((line = malloc(size * sizeof(*line))) == NULL) {
                fprintf(stderr, "Memory allication failure.\n");
                return NULL;
        }

        i = 0;
#if defined ___AsyncIO___
        while(run && (c = ReadCharAsync(fp)) != -1) {
#else
        while(run && fscanf(fp, "%c", &c) == 1) {
#endif
#if defined ___AmigaOS___
                // Check if CTRL+C was pressed and abort the program.
                if(SetSignal(0L, SIGBREAKF_CTRL_C) & SIGBREAKF_CTRL_C) {
                        run = FALSE;
                        continue;
                }
#endif
                switch(c) {
                        case '\n':
                                return line;
                        default:
                                if(strlen(line) == size) {
                                        size = size * 1.5;
                                        real = realloc(line, size * sizeof(*line));
                                        if(real == NULL) {
                                                fprintf(stderr, "Memory reallocation failure.\n");
                                                free(line);
                                                return NULL;
                                        }
                                        line = real;
                                }
                                line[i] = c;
                                line[i + 1] = '\0';
                                break;
                }
                ++i;
        }

        if(line != NULL) {
                free(line);
        }

        return NULL;
}

/*
  *
  * Delete a file.
  */
#if defined ___AmigaOS___
BOOL RemoveFile(char *name) {
        return DeleteFile(name);
#else
int RemoveFile(char *name) {
        return remove(name) == 0;
#endif
}

/*
        *
        * Deletes files.
        */
void RemoveFiles(char **names, int count) {
        unsigned int i;
        for(i = 0; i < count; ++i) {
                if(RemoveFile(names[i]) == FALSE) {
                        fprintf(stderr, "Unable to remove %s...\n", names[i]);
                        continue;
                }
                fprintf(stderr, "Removing file: %s\n", names[i]);
        }
}

/*
        *
        * Copies a file to another file by name.
        */
void CopyFile(char *a, char *b) {
#if defined ___AsyncIO___
        struct AsyncFile *ap;
        struct AsyncFile *bp;
        LONG c;
#else
        FILE *ap;
        FILE *bp;
        char c;
#endif

        // Open database file for writing.
#if defined ___AsyncIO___
        if((ap = OpenAsync(a, MODE_READ, ASYNC_BUF)) == NULL) {
#else
        if((ap = fopen(a, "r")) == NULL) {
#endif
                fprintf(stderr, "Unable to open '%s' for reading.\n", a);
                return;
        }

        // Open temporary file for reading.
#if defined ___AsyncIO___
        if((bp = OpenAsync(b, MODE_WRITE, ASYNC_BUF)) == NULL) {
#else
        if((bp = fopen(b, "w")) == NULL) {
#endif
                fprintf(stderr, "Unable to open file '%s' for writing.\n", b);

                // Close database file.
#if defined ___AsyncIO___
                CloseAsync(ap);
#else
                fclose(ap);
#endif

                return;
        }

#if defined ___AsyncIO___
        while(run && (c = ReadCharAsync(ap)) != -1) {
#else
        while(run && fscanf(ap, "%c", &c) == 1) {
#endif
#if defined ___AmigaOS___
                // Check if CTRL+C was pressed and abort the program.
                if(SetSignal(0L, SIGBREAKF_CTRL_C) & SIGBREAKF_CTRL_C) {
                        run = FALSE;
                        continue;
                }
#endif
#if defined ___AsyncIO___
                if(WriteCharAsync(bp, (UBYTE)c) != 1) {
#else
                if(fprintf(bp, "%c", c) != 1) {
#endif
                        fprintf(stderr, "Unable to write to '%s'.\n", b);
                        break;
                }
        }

#if defined ___AsyncIO___
        CloseAsync(ap);
        CloseAsync(bp);
#else
        fclose(ap);
        fclose(bp);
#endif
}

/*
        *
        * Write lines to a file.
        */
void WriteLinesToFile(char *dbFile, char **lines, unsigned int count) {
#if defined ___AsyncIO___
        struct AsyncFile *fp;
#else
        FILE *fp;
#endif
        unsigned int i;
        char *rem;

        // Write the database lines back to the database.
#if defined ___AsyncIO___
        if((fp = OpenAsync(dbFile, MODE_WRITE, ASYNC_BUF)) == NULL) {
#else
        if((fp = fopen(dbFile, "w")) == NULL) {
#endif
                fprintf(stderr, "Unable to open '%s' for writing.\n", dbFile);
                return;
        }

        rem = NULL;
        for(i = 0; i < count; ++i) {
#if defined ___AmigaOS___
                // Check if CTRL+C was pressed and abort the program.
                if(SetSignal(0L, SIGBREAKF_CTRL_C) & SIGBREAKF_CTRL_C) {
                        run = FALSE;
                        continue;
                }
#endif

                if(rem != NULL) {
#if defined ___AmigaOS___
                        if(StrnCmp(locale, lines[i], rem, -1, SC_ASCII) == 0) {
#else
                        if(strcmp(lines[i], rem) == 0) {
#endif
                                continue;
                        }
                }

#if defined ___AsyncIO___
                WriteAsync(fp, lines[i], (LONG)strlen(lines[i]));
                WriteAsync(fp, "\n", 1);
#else
                fprintf(fp, "%s\n", lines[i]);
#endif

                if(rem != NULL) {
                        free(rem);
                }

                rem = malloc(strlen(lines[i]) + 1);
                sprintf(rem, "%s", lines[i]);
        }

        if(rem != NULL) {
                free(rem);
        }

#if defined ___AsyncIO___
        CloseAsync(fp);
#else
        fclose(fp);
#endif
}

/*
        *
        * Create a database entry from a line of text.
        */
dbEntry* CreateDatabaseEntry(char *line) {
        dbEntry *entry;
        char *ptr;
        unsigned int side;
        unsigned int i;
        unsigned int j;

        if((entry = malloc(1 * sizeof(*entry))) == NULL) {
                fprintf(stderr, "Memory allocation failure.\n");
                return NULL;
        }

        if((entry->name = malloc((strlen(line) + 1) * sizeof(*entry->name))) == NULL) {
                fprintf(stderr, "Memory allocation failure.\n");
                return NULL;
        }

        if((entry->path = malloc((strlen(line) + 1) * sizeof(*entry->path))) == NULL) {
                fprintf(stderr, "Memory allocation failure.\n");
                return NULL;
        }

        for(ptr = line, side = 0, i = 0, j = 0; run && *ptr != '\0'; ++ptr) {
#if defined ___AmigaOS___
                // Check if CTRL+C was pressed and abort the program.
                if(SetSignal(0L, SIGBREAKF_CTRL_C) & SIGBREAKF_CTRL_C) {
                        run = FALSE;
                        continue;
                }
#endif
                switch(*ptr) {
                        case '\t':
                                entry->name[i] = '\0';
                                ++side;
                                break;
                        case '\n':
                                entry->path[j] = '\0';
                                return entry;
                        default:
                                switch(side) {
                                        case 0:
                                                entry->name[i++] = *ptr;
                                                break;
                                        case 1:
                                                entry->path[j++] = *ptr;
                                                break;
                                }
                                break;
                }
        }

        return entry;
}

/*
        *
        *
        */
dbArray *GetDatabaseArray(char *dbFile) {
#if defined ___AsyncIO___
        struct AsyncFile *fp;
#else
        FILE *fp;
#endif
        dbArray *array;
        dbEntry *entry;
        char *line = NULL;
        char *real = NULL;
        unsigned int count;

        // Open database file for reading.
#if defined ___AsyncIO___
        if((fp = OpenAsync(dbFile, MODE_READ, ASYNC_BUF)) == NULL) {
#else
        if((fp = fopen(dbFile, "r")) == NULL) {
#endif
                fprintf(stderr, "Unable to open '%s' for reading.\n", dbFile);
                return NULL;
        }

        if((array = malloc(1 * sizeof(*array))) == NULL) {
                fprintf(stderr, "Memory allocation failure.\n");
#if defined ___AsyncIO___
                CloseAsync(fp);
#else
                fclose(fp);
#endif
                return NULL;
        }

        count = 0;
        while(run && (line = ReadLine(fp)) != NULL) {
#if defined ___AmigaOS___
                // Check if CTRL+C was pressed and abort the program.
        if(SetSignal(0L, SIGBREAKF_CTRL_C) & SIGBREAKF_CTRL_C) {
                        run = FALSE;
                        continue;
                }
#endif
                if((entry = CreateDatabaseEntry(line)) == NULL) {
                        fprintf(stderr, "Unable to create database entry.\n");
                        free(line);
#if defined ___AsyncIO___
                        CloseAsync(fp);
#else
                        fclose(fp);
#endif
                        return NULL;
                }

                // Load up the name and path into the database variable.
                real = realloc(array->database, (count + 1) * sizeof(*array->database));
                if(real == NULL) {
                        fprintf(stderr, "Memory reallocation failure.\n");
                        free(entry->name);
                        free(entry->path);
                        free(entry);
                        free(line);
#if defined ___AsyncIO___
                        CloseAsync(fp);
#else
                        fclose(fp);
#endif
                        return NULL;
                }
                array->database = real;

                if((array->database[count] = malloc((strlen(entry->name) + strlen(entry->path) + 1 + 1) * sizeof(*array->database[count]))) == NULL) {
                        fprintf(stderr, "Memory allocation failure.\n");
                        free(entry->name);
                        free(entry->path);
                        free(entry);
                        free(line);
#if defined ___AsyncIO___
                        CloseAsync(fp);
#else
                        fclose(fp);
#endif
                        return NULL;
                }
                sprintf(array->database[count], "%s\t%s", entry->name, entry->path);
                ++count;

                // Free the database entry.
                free(entry->name);
                free(entry->path);
                free(entry);

                free(line);
        }

        if(line != NULL) {
                free(line);
        }

#if defined ___AsyncIO___
        CloseAsync(fp);
#else
        fclose(fp);
#endif

        array->count = count;
        return array;
}

/*
        *
        * Sorts a database file lexicographically.
        */
void SortDatabase(char *dbFile) {
        dbArray *array;
        int i;

        if(verbose) {
                fprintf(stdout, "Sorting '%s'...\n", dbFile);
        }

        // Retrieve the database as an array.
        if((array = GetDatabaseArray(dbFile)) == NULL) {
                fprintf(stderr, "Unable to read '%s' as a database file.\n", dbFile);
                return;
        }

        // Sort the database.
        qsort(array->database, array->count, sizeof(char *), QsortCompare);

        // Write back the database to the database file.
        WriteLinesToFile(dbFile, array->database, array->count);

    // Deallocate all the lines.
    for(i = 0; i < array->count; ++i) {
       free(array->database[i]);
    }

        free(array);
}

/*
        *
        * Updates a database file "dbFile".
        */
stats *CollectFiles(char *dbFile, char **paths, unsigned int count) {
#if defined ___AsyncIO___
        struct AsyncFile *fp;
#else
        FILE *fp;
#endif
#if defined ___AmigaOS___
        struct FileInfoBlock *FIBp, *FIBq;
        BPTR lockp, lockq;
#else
        DIR *dir;
        struct dirent *entry;
        struct stat dirStat;
#endif
        stringStack *stack;
        stats *stats = NULL;
        int i;
        char *path;
        char *sub;

#if defined ___AsyncIO___
        if((fp = OpenAsync(dbFile, MODE_APPEND, ASYNC_BUF)) == NULL) {
#else
        if((fp = fopen(dbFile, "a")) == NULL) {
#endif
                fprintf(stderr, "Unable to open '%s' for writing.\n", dbFile);
                return NULL;
        }

        if(verbose) {
                fprintf(stdout, "Collecting files...\r");
        }

        // Initialize metrics.
        if((stats = malloc(sizeof(*stats))) == NULL) {
                fprintf(stderr, "Memory allocation failure.\n");
#if defined ___AsyncIO___
                CloseAsync(fp);
#else
                fclose(fp);
#endif
                return NULL;
        }

        stats->dirs = 0;
        stats->files = 0;

        // Push the first path onto the stack.
        stack = stringStackCreate(count);
        for(i = 0; run && i < count; ++i) {
                stringStackPush(stack, paths[i]);
        }

        while(run && !stringStackIsEmpty(stack)) {
#if defined ___AmigaOS___
                // Check if CTRL+C was pressed and abort the program.
                if(SetSignal(0L, SIGBREAKF_CTRL_C) & SIGBREAKF_CTRL_C) {
                        run = FALSE;
                        continue;
                }
#endif
                if((path = stringStackPop(stack)) == NULL) {
                        break;
                }

#if defined ___AmigaOS___
                if((lockp = Lock(path, ACCESS_READ)) == NULL) {
                        fprintf(stderr, "Could not lock path '%s' for reading.\n", path);
                        free(path);
                        continue;
                }

                if((FIBp = AllocDosObject(DOS_FIB, NULL)) == NULL) {
                        fprintf(stderr, "Path '%s' info block allocation failure.\n", path);
                        UnLock(lockp);
                        free(path);
                        continue;
                }

                if(Examine(lockp, FIBp) == FALSE) {
                        fprintf(stderr, "Path '%s' could not be examined.\n", path);
                        FreeDosObject(DOS_FIB, FIBp);
                        UnLock(lockp);
                        free(path);
                        continue;
                }

                while(run && ExNext(lockp, FIBp)) {
                        // Check if CTRL+C was pressed and abort the program.
                        if(SetSignal(0L, SIGBREAKF_CTRL_C) & SIGBREAKF_CTRL_C) {
                                run = FALSE;
                                continue;
                        }
#else

                if((dir = opendir(path)) == NULL) {
                        fprintf(stderr, "Unable to open '%s' for reading.\n", path);
                        free(path);
                        continue;
                }

                while(run && (entry = readdir(dir)) != NULL) {
#endif
                        switch(path[strlen(path) - 1]) {
                                case '/':
                                case ':': // This is a drive path.
#if defined ___AmigaOS___
                                        if((sub = malloc(strlen(path) + strlen(FIBp->fib_FileName) + 1)) == NULL) {
#else
                                        if((sub = malloc(strlen(path) + strlen(entry->d_name) + 1)) == NULL) {
#endif
                                                fprintf(stderr, "Memory allocation failure.\n");
#if defined ___AmigaOS___
                                                FreeDosObject(DOS_FIB, FIBp);
                                                UnLock(lockp);
#else
                                                closedir(dir);
#endif
                                                free(path);
                                                stringStackDestroy(stack);
#if defined ___AsyncIO___
                                                CloseAsync(fp);
#else
                                                fclose(fp);
#endif
                                                return NULL;
                                        }
#if defined ___AmigaOS___
                                        sprintf(sub, "%s%s", path, FIBp->fib_FileName);
#else
                                        sprintf(sub, "%s%s", path, entry->d_name);
#endif
                                        break;
                                default:
#if defined ___AmigaOS___
                                        if((sub = malloc(strlen(path) + strlen(FIBp->fib_FileName) + 1 + 1)) == NULL) {
#else
                                        if((sub = malloc(strlen(path) + strlen(entry->d_name) + 1 + 1)) == NULL) {
#endif
                                                fprintf(stderr, "Memory allocation failure.\n");
#if defined ___AmigaOS___
                                                FreeDosObject(DOS_FIB, FIBp);
                                                UnLock(lockp);
#else
                                                closedir(dir);
#endif
                                                free(path);
                                                stringStackDestroy(stack);
#if defined ___AsyncIO___
                                                CloseAsync(fp);
#else
                                                fclose(fp);
#endif
                                                return NULL;
                                        }
#if defined ___AmigaOS___
                                        sprintf(sub, "%s/%s", path, FIBp->fib_FileName);
#else
                                        sprintf(sub, "%s/%s", path, entry->d_name);
#endif
                                        break;
                        }


#if defined ___AmigaOS___
                        if((lockq = Lock(sub, ACCESS_READ)) == NULL) {
                                fprintf(stderr, "Could not lock path '%s' for reading.\n", sub);
                                free(sub);
                                continue;
                        }

                        if((FIBq = AllocDosObject(DOS_FIB, NULL)) == NULL) {
                                fprintf(stderr, "Path '%s' info block allocation failure.\n", sub);
                                UnLock(lockq);
                                free(sub);
                                continue;
                        }

                        if(Examine(lockq, FIBq) == FALSE) {
                                fprintf(stderr, "Path '%s' could not be examined.\n", sub);
                                FreeDosObject(DOS_FIB, FIBq);
                                UnLock(lockq);
                                free(sub);
                                continue;
                        }

                        if(FIBq->fib_DirEntryType > 0) {
#else
                        stat(sub, &dirStat);
                        if(S_ISDIR(dirStat.st_mode)) {
#endif
                                stringStackPush(stack, sub);

                                ++stats->dirs;

                                if(verbose) {
                                        fprintf(stdout,
                                                "Gathered %d directories and %d files.\r",
                                                stats->dirs,
                                                stats->files);
                                }

#if defined ___AmigaOS___
                                FreeDosObject(DOS_FIB, FIBq);
                                UnLock(lockq);
#endif
                                free(sub);
                                continue;
                        }

#if defined ___AmigaOS___
                        FreeDosObject(DOS_FIB, FIBq);
                        UnLock(lockq);
#endif

                        ++stats->files;

                        if(verbose) {
                                fprintf(stdout,
                                        "Gathered %d directories and %d files.\r",
                                        stats->dirs,
                                        stats->files);
                        }

#if defined ___NOCASE_FS___
#if defined ___AmigaOS___
                        strupr(FIBp->fib_FileName);
#else
                        strupr(entry->d_name);
#endif
#endif


                        // Write to database file.
#if defined ___AsyncIO___
#if defined ___AmigaOS___
                        WriteAsync(fp, FIBp->fib_FileName, (LONG)strlen(FIBp->fib_FileName));
#else
                        WriteAsync(fp, entry->d_name, (LONG)strlen(entry->d_name));
#endif
                        WriteAsync(fp, "\t", 1);
                        WriteAsync(fp, sub, (LONG)strlen(sub));
                        WriteAsync(fp, "\n", 1);
#else
#if defined ___AmigaOS___
                        fprintf(fp, "%s\t%s\n", FIBp->fib_FileName, sub);
#else
                        fprintf(fp, "%s\t%s\n", entry->d_name, sub);
#endif
#endif
                        free(sub);
                }

#if defined ___AmigaOS___
                FreeDosObject(DOS_FIB, FIBp);
                UnLock(lockp);
#else
                closedir(dir);
#endif
                free(path);
        }

        if(verbose) {
                fprintf(stdout, "\n");
        }

        stringStackDestroy(stack);

#if defined ___AsyncIO___
        CloseAsync(fp);
#else
        fclose(fp);
#endif

        return stats;

}

/*
        *
        * Writes lines from the database "dbFile" to temporary filenames "tmpNames".
        */
void WriteTemporaryFiles(char *dbFile, char **tmpNames, int tmpFiles, int tmpLines, int total) {
#if defined ___AsyncIO___
        struct AsyncFile *fp, *tp;
        LONG c;
#else
        FILE *fp, *tp;
        char c;
#endif
        int lines;
        int write;

#if defined ___AsyncIO___
        if((fp = OpenAsync(dbFile, MODE_READ, ASYNC_BUF)) == NULL) {
#else
        if((fp = fopen(dbFile, "r")) == NULL) {
#endif
                fprintf(stderr, "Unable to open '%s' for reading.\n", dbFile);
                return;
        }

#if defined ___AsyncIO___
        if((tp = OpenAsync(tmpNames[--tmpFiles], MODE_WRITE, ASYNC_BUF)) == NULL) {
#else
        if((tp = fopen(tmpNames[--tmpFiles], "w")) == NULL) {
#endif
                fprintf(stderr, "Unable to open '%s' for writing.\n", tmpNames[tmpFiles]);
#if defined ___AsyncIO___
                CloseAsync(fp);
#else
                fclose(fp);
#endif
                return;
        }

        if(verbose) {
                fprintf(stdout, "Writing to temporary files...\r");
        }

        write = 0;
        lines = 0;
#if defined ___AsyncIO___
        while(run && (c = ReadCharAsync(fp)) != -1) {
#else
        while(run && fscanf(fp, "%c", &c) == 1) {
#endif
#if defined ___AmigaOS___
                // Check if CTRL+C was pressed and abort the program.
                if(SetSignal(0L, SIGBREAKF_CTRL_C) & SIGBREAKF_CTRL_C) {
                        run = FALSE;
                        continue;
                }
#endif
                switch(c) {
                        case '\n':
                                // Increment the total written lines.
                                ++write;

                                if(verbose) {
                                        fprintf(stdout, "Writing to temporary files: %d%%.\r", (int)(((float)write / total) * 100.0));
                                }

                                // Write the newline character back.
#if defined ___AsyncIO___
                                if(WriteCharAsync(tp, (UBYTE)c) != 1) {
#else
                                if(fprintf(tp, "%c", c) != 1) {
#endif
                                        fprintf(stderr, "Unable to write to '%s'.\n", tmpNames[tmpFiles]);
#if defined ___AsyncIO___
                                        CloseAsync(tp);
                                        CloseAsync(fp);
#else
                                        fclose(tp);
                                        fclose(fp);
#endif
                                        return;
                                }
                                // Switch to the next temporary file.
                                if(++lines >= tmpLines) {
                                        // If there are no temporary files left then run till the end.
                                        if(tmpFiles - 1 < 0) {
                                                break;
                                        }

                                        // Close the previous temporary file and write to the next temporary file.
#if defined ___AsyncIO___
                                        CloseAsync(tp);
                                        if((tp = OpenAsync(tmpNames[--tmpFiles], MODE_WRITE, ASYNC_BUF)) == NULL) {
#else
                                        fclose(tp);
                                        if((tp = fopen(tmpNames[--tmpFiles], "w")) == NULL) {
#endif
                                                fprintf(stderr, "Unable to open '%s' for writing.\n", tmpNames[tmpFiles]);
#if defined ___AsyncIO___
                                                CloseAsync(fp);
#else
                                                fclose(fp);
#endif
                                                return;
                                        }
                                        lines = 0;
                                        break;
                                }
                                break;
                        default:
#if defined ___AsyncIO___
                                if(WriteCharAsync(tp, (UBYTE)c) != 1) {
#else
                                if(fprintf(tp, "%c", c) != 1) {
#endif
                                        fprintf(stderr, "Unable to write to '%s'.\n", tmpNames[tmpFiles]);
#if defined ___AsyncIO___
                                        CloseAsync(tp);
                                        CloseAsync(fp);
#else
                                        fclose(tp);
                                        fclose(fp);
#endif
                                        return;
                                }
                                break;
                }
        }

        if(verbose) {
                fprintf(stdout, "\n");
        }

#if defined ___AsyncIO___
        CloseAsync(tp);
        CloseAsync(fp);
#else
        fclose(tp);
        fclose(fp);
#endif
}

/*
        *
        * Merges temporary files "tmpNames" into a database "dbFile".
        */
void MergeTemporaryFiles(char *dbFile, char **tmpNames, int files, int lines) {
#if defined ___AsyncIO___
        struct AsyncFile *fp;
        struct AsyncFile **tp;
#else
        FILE *fp;
        FILE **tp;
#endif
        unsigned int i;
        unsigned int j;
        char *tmp;
        char *rem;
        char *min;
        int count;

#if defined ___AsyncIO___
        if((fp = OpenAsync(dbFile, MODE_WRITE, ASYNC_BUF)) == NULL) {
#else
        if((fp = fopen(dbFile, "w")) == NULL) {
#endif
                fprintf(stderr, "Unable to open '%s' for writing.\n", dbFile);
                return;
        }

        // Allocate as many file pointers as temporary files.
        if((tp = malloc(files * sizeof(*tp))) == NULL) {
                fprintf(stderr, "Memory allocation failure.\n");
#if defined ___AsyncIO___
                CloseAsync(fp);
#else
                fclose(fp);
#endif
                return;
        }

        // Open all temporary files for reading.
        for(i = 0; i < files; ++i) {
#if defined ___AsyncIO___
                if((tp[i] = OpenAsync(tmpNames[i], MODE_READ, ASYNC_BUF)) == NULL) {
#else
                if((tp[i] = fopen(tmpNames[i], "r")) == NULL) {
#endif
                        fprintf(stderr, "Unable to open '%s' for reading.\n", tmpNames[i]);
                        // Close all temporary files.
                        while(--i > -1) {
#if defined ___AsyncIO___
                                CloseAsync(tp[i]);
#else
                                fclose(tp[i]);
#endif
                        }
#if defined ___AsyncIO___
                        CloseAsync(fp);
#else
                        fclose(fp);
#endif
                        return;
                }
        }

        if(verbose) {
                fprintf(stdout, "Merging all files...\r");
        }

        rem = NULL;
        count = lines;
        j = 0;
        while(run && --count > -1) {
#if defined ___AmigaOS___
                // Check if CTRL+C was pressed and abort the program.
                if(SetSignal(0L, SIGBREAKF_CTRL_C) & SIGBREAKF_CTRL_C) {
                        run = FALSE;
                        continue;
                }
#endif
                // Find the smallest line in all temporary files.
                if(verbose) {
                        fprintf(stdout, "Merging all files: %d%%.\r", 100 - (int)(((float)count / lines) * 100.0));
                }

                min = NULL;
                for(i = 0; i < files; ++i) {
                        tmp = PeekLine(tp[i]);
                        if(tmp == NULL) {
                                continue;
                        }
#if defined ___AmigaOS___
                        if(min == NULL || StrnCmp(locale, tmp, min, -1, SC_ASCII) < 0) {
#else
                        if(min == NULL || strcmp(tmp, min) < 0) {
#endif
                                if(min != NULL) {
                                        // Free previous instance.
                                        free(min);
                                }
                                if((min = malloc((strlen(tmp) + 1) * sizeof(*min))) == NULL) {
                                        fprintf(stderr, "Memory allication failure.\n");
                                        free(tmp);
                                        if(min != NULL) {
                                                free(min);
                                        }
                                        if(rem != NULL) {
                                                free(rem);
                                        }
#if defined ___AsyncIO___
                                        CloseAsync(fp);
#else
                                        fclose(fp);
#endif
                                        return;
                                }
                                sprintf(min, "%s", tmp);
                                // Remember the index of the file where the smallest entry has been found.
                                j = i;
                        }
                        free(tmp);
                }

                // Forward the file where the smallest line was found.
                SkipLine(tp[j]);

                // Write the smallest line.
                if(min != NULL) {
                        // If current minimum line is identical to previous minimum line then skip to remove duplicates.
                        if(rem != NULL) {
#if defined ___AmigaOS___
                                if(StrnCmp(locale, min, rem, -1, SC_ASCII) == 0) {
#else
                                if(strcmp(min, rem) == 0) {
#endif
                                        free(min);
                                        continue;
                                }
                        }

#if defined ___AsyncIO___
                        WriteAsync(fp, min, (LONG)strlen(min));
                        WriteAsync(fp, "\n", 1);
#else
                        fprintf(fp, "%s\n", min);
#endif

                        if(rem != NULL) {
                                free(rem);
                        }

                        if((rem = malloc((strlen(min) + 1) * sizeof(*rem))) == NULL) {
                                fprintf(stderr, "Memory allocation failure.\n");
                                free(min);
#if defined ___AsyncIO___
                                CloseAsync(fp);
#else
                                fclose(fp);
#endif
                                return;
                        }

                        sprintf(rem, "%s", min);
                        free(min);
                }
        }

        if(rem != NULL) {
                free(rem);
        }

        // Write out any remaining contents from the temporary files.
        for(i = 0; run && i < files; ++i) {
#if defined ___AmigaOS___
                // Check if CTRL+C was pressed and abort the program.
                if(SetSignal(0L, SIGBREAKF_CTRL_C) & SIGBREAKF_CTRL_C) {
                        run = FALSE;
                        continue;
                }
#endif
                tmp = ReadLine(tp[i]);
                if(tmp == NULL) {
                        continue;
                }
#if defined ___AsyncIO___
                WriteAsync(fp, tmp, (LONG)strlen(tmp));
                WriteAsync(fp, "\n", 1);
#else
                fprintf(fp, "%s\n", tmp);
#endif
                free(tmp);
        }

        // Close all temporary files.
        for(i = 0; i < files; ++i) {
#if defined ___AsyncIO___
                CloseAsync(tp[i]);
#else
                fclose(tp[i]);
#endif
        }

        if(verbose) {
                fprintf(stdout, "\n");
        }

#if defined ___AsyncIO___
        CloseAsync(fp);
#else
        fclose(fp);
#endif
}

/*
  *
  * Filter the paths inside the database with provided paths.
  */
void FilterDatabasePaths(char *dbFile, char *tmpName, char **paths, unsigned int count) {
#if defined ___AsyncIO___
        struct AsyncFile *fp;
        struct AsyncFile *tp;
#else
        FILE *fp;
        FILE *tp;
#endif
        char *line;
        unsigned int lines;
        int i;
        dbEntry *entry;

        // Open database file for reading.
#if defined ___AsyncIO___
        if((fp = OpenAsync(dbFile, MODE_READ, ASYNC_BUF)) == NULL) {
#else
        if((fp = fopen(dbFile, "r")) == NULL) {
#endif
                fprintf(stderr, "Unable to open '%s' for reading.\n", dbFile);
                return;
        }

        // Open temporary file for writing.
#if defined ___AsyncIO___
        if((tp = OpenAsync(tmpName, MODE_WRITE, ASYNC_BUF)) == NULL) {
#else
        if((tp = fopen(tmpName, "w")) == NULL) {
#endif
                fprintf(stderr, "Unable to open '%s' for writing.\n", tmpName);

                // Close database file.
#if defined ___AsyncIO___
                CloseAsync(fp);
#else
                fclose(fp);
#endif

                return;
        }

        if(verbose) {
                fprintf(stdout, "Removing lines...\r");
        }

        lines = 0;
        while(run && (line = ReadLine(fp)) != NULL) {
#if defined ___AmigaOS___
                // Check if CTRL+C was pressed and abort the program.
        if(SetSignal(0L, SIGBREAKF_CTRL_C) & SIGBREAKF_CTRL_C) {
                        run = FALSE;
                        continue;
                }
#endif
                if((entry = CreateDatabaseEntry(line)) == NULL) {
                        fprintf(stderr, "Unable to create database entry.\n");
                        free(line);
                        continue;
                }

                for(i = 0; i < count; ++i) {
                        if(PathCompare(entry->path, paths[i]) == TRUE) {
                                ++lines;
                                if(verbose) {
                                        fprintf(stdout, "Removing lines: %d\r", lines);
                                }
                                continue;
                        }
#if defined ___AsyncIO___
                        WriteAsync(tp, line, (LONG)strlen(line));
                        WriteAsync(tp, "\n", 1);
#else
                        fprintf(tp, "%s\n", line);
#endif
                        break;
                }


                free(entry->name);
                free(entry->path);
                free(entry);

                free(line);
        }

        if(verbose) {
                fprintf(stdout, "\n");
        }


#if defined ___AsyncIO___
        CloseAsync(fp);
        CloseAsync(tp);
#else
        fclose(fp);
        fclose(tp);
#endif
}

/*
        *
        * Indexes a "path" by creating a database "dbFile".
        */
void GatherDatabaseFiles(char *dbFile, char **paths, unsigned int count) {
        stats *stats;
        char **tmpNames;
        int dbSize;
        int dbLines;
        int tmpFiles;
        int tmpLines;
        int i;

        // Generate the database file from the supplied paths.
        if((stats = CollectFiles(dbFile, paths, count)) == NULL) {
                fprintf(stderr, "Collecting files failed.\n");
                return;
        }
        free(stats);

        // Compute the amount of temporary files needed.
        dbSize = GetFileSize(dbFile);
        if(dbSize == -1) {
                fprintf(stderr, "File size for '%s' failed.\n", dbFile);
                return;
        }
        tmpFiles = dbSize / maxmem;

        /* In case no temporary files are required,
         * just sort the database and terminate.
         */
        if(tmpFiles <= 1) {
                SortDatabase(dbFile);
                return;
        }

        // Get the database metrics.
        dbLines = CountFileLines(dbFile);
        if(dbLines == -1) {
                fprintf(stderr, "Counting lines of '%s' failed.\n", dbFile);
        }
        tmpLines = dbLines / tmpFiles;

        // Create temporary files.
        if((tmpNames = CreateTemporaryFiles(tmpFiles)) == NULL) {
                fprintf(stderr, "Unable to create temporary files.\n");
                return;
        }

        // Write "tmpLines" to temporary files in "tmpNames" from "dbFile".
        WriteTemporaryFiles(dbFile, tmpNames, tmpFiles, tmpLines, dbLines);

        // Sort the temporary files.
        for(i = 0; i < tmpFiles; ++i) {
                SortDatabase(tmpNames[i]);
        }

        // Merge all the temporary files to the database file.
        MergeTemporaryFiles(dbFile, tmpNames, tmpFiles, dbLines);

        // Remove all temporary files.
        RemoveFiles(tmpNames, tmpFiles);

        // Free temporary file names.
        free(tmpNames);
}

void RemoveDatabaseFiles(char *dbFile, char **paths, unsigned int count) {
        char *tmpName;

        // Create a temporary file to hold the changes.
        if((tmpName = CreateTemporaryFile()) == NULL) {
                fprintf(stderr, "Unable to create temporary file.\n");
                return;
        }

        // Filter the database of the provided paths.
        FilterDatabasePaths(dbFile, tmpName, paths, count);

        // Overwrite the database file with the filtered paths.
        CopyFile(tmpName, dbFile);

        // Remove temporary file.
        if(RemoveFile(tmpName) == FALSE) {
                fprintf(stderr, "Temporary file could not be removed.\n");
                return;
        }
}

void usage(char *name) {
        fprintf(stdout, "Hunt & Gather - %s, a file index generating tool.        \n", name);
        fprintf(stdout, "Version: %s                                              \n", PROGRAM_VERSION);
        fprintf(stdout, "                                                         \n");
        fprintf(stdout, "SYNTAX: %s [-q] <-a|-r|-c> <PATH PATH PATH...>           \n", name);
        fprintf(stdout, "                                                         \n");
        fprintf(stdout, "Required:                                                \n");
        fprintf(stdout, "    -a [PATH...]   Add files.                            \n");
        fprintf(stdout, "    -c [PATH...]   Create from scratch.                  \n");
        fprintf(stdout, "    -r [PATH...]   Remove files.                         \n");
        fprintf(stdout, "                                                         \n");
        fprintf(stdout, "Optional:                                                \n");
        fprintf(stdout, "    -d [FIILE]     Where to store the database.          \n");
        fprintf(stdout, "    -m BYTES       Memory to use (default: %d).          \n", maxmem);
        fprintf(stdout, "    -q             Do not print out any messages.        \n");
        fprintf(stdout, "                                                         \n");
        fprintf(stdout, "DATABASE is a path to where the indexed results will be  \n");
        fprintf(stdout, "stored for searching with the Hunt tool.                 \n");
        fprintf(stdout, "                                                         \n");
        fprintf(stdout, "(c) 2021 Wizardry and Steamworks, MIT.                   \n");
}

/*
        *
        * Main entry point.
        */
int main(int argc, char **argv) {
#if defined ___AmigaOS___
        struct FileInfoBlock *FIB;
        BPTR lock;
#else
        struct stat dirStat;
#endif
        int option;
        unsigned int i;
        unsigned int count;
        char *dbFile;
        char *path;
        char **paths;

        // Bind handler to SIGINT.
#if !defined ___AmigaOS___
        signal(SIGINT, SignalHandler);
#endif

        dbFile = DEFAULT_DATABASE_FILE;
        while((option = getopt(argc, argv, "hqdm:arc")) != -1) {
                switch(option) {
                        case 'a':
                                operation = GATHER;
                                break;
                        case 'r':
                                operation = REMOVE;
                                break;
                        case 'c':
                                operation = CREATE;
                                break;
                        case 'm':
                                maxmem = strtoul(optarg, NULL, 10);
                                break;
                        case 'd':
                                dbFile = optarg;
                                break;
                        case 'q':
                                verbose = FALSE;
                                break;
                        case 'h':
                                usage(argv[0]);
                                return 0;
                        case '?':
                                fprintf(stderr, "Invalid option %ct.\n", optopt);
                                return 1;
                }
        }

        if(operation == NONE) {
                usage(argv[0]);
                return 1;
        }

        if(optind >= argc) {
                usage(argv[0]);
                return 1;
        }

        // Go through all supplied arguments and add paths to search.
        if((paths = malloc((argc - optind) * sizeof(*paths))) == NULL) {
                fprintf(stderr, "Memory allocation failure.\n");
                return 1;
        }

        count = 0;
        for(i = optind, count = 0; i < argc; ++i) {
                if((path = PathToAbsolute(argv[i])) == NULL) {
                        fprintf(stderr, "Absolute path for '%s' failed to resolve.\n", argv[optind]);
                        continue;
                }

                // Check that the path is a directory.
#if defined ___AmigaOS___
                if((lock = Lock(path, ACCESS_READ)) == NULL) {
                        fprintf(stderr, "Path '%s' is not accessible.\n", path);
                        free(path);
                        continue;
                }

                if((FIB = AllocDosObject(DOS_FIB, NULL)) == NULL) {
                        fprintf(stderr, "Path '%s' file information block not accessible.\n", path);
                        UnLock(lock);
                        free(path);
                        continue;
                }

                if(Examine(lock, FIB) == FALSE) {
                        fprintf(stderr, "Path '%s' information unexaminable.\n", path);
                        UnLock(lock);
                        FreeDosObject(DOS_FIB, FIB);
                        free(path);
                        continue;
                }

                if(FIB->fib_DirEntryType < 0) {
#else
                stat(path, &dirStat);
                if(!S_ISDIR(dirStat.st_mode)) {
#endif
                        fprintf(stderr, "Path '%s' is not a directory.\n", argv[optind]);
#if defined ___AmigaOS___
                        UnLock(lock);
                        FreeDosObject(DOS_FIB, FIB);
#endif
                        free(path);
                        return 1;
                }

                if(verbose) {
                        fprintf(stdout, "Will process path: '%s'\n", path);
                }

                // Add the path to the array of paths.
                if((paths[count] = malloc((strlen(path) + 1) * sizeof(*paths[count]))) == NULL) {
                        fprintf(stderr, "Memory allocation failure.");
                        return 1;
                }

                sprintf(paths[count], "%s", path);
                ++count;

#if defined ___AmigaOS___
                UnLock(lock);
                FreeDosObject(DOS_FIB, FIB);
#endif
                free(path);

        }

        if(count == 0) {
                fprintf(stderr, "No valid paths are available.\n");
                free(paths);
                return 1;
        }

        if(verbose) {
                fprintf(stdout, "Gathering to: '%s'\n", dbFile);
        }

#if defined ___AmigaOS___
        locale = OpenLocale(NULL);
#endif

        switch(operation) {
                case CREATE:
                        if(verbose) {
                                fprintf(stdout, "Removing '%s' and creating a new database.\n", dbFile);
                        }
                        if(RemoveFile(dbFile) == FALSE) {
                                fprintf(stderr, "File '%s' could not be removed.\n", dbFile);
                                break;
                        }
                case GATHER:
                        if(verbose) {
                                fprintf(stdout, "Gathering files to database...\n");
                        }
                        GatherDatabaseFiles(dbFile, paths, count);
                        break;
                case REMOVE:
                        if(verbose) {
                                fprintf(stdout, "Removing files from database...\n");
                        }
                        RemoveDatabaseFiles(dbFile, paths, count);
                        break;
                default:
                        fprintf(stderr, "Unknown operation.\n");
                        break;
        }

#if defined ___AmigaOS___
        CloseLocale(locale);
#endif

        free(paths);

#if defined MWDEBUG
        /* Generate a memory usage report */
        MWReport("At end of main()", MWR_FULL);
#endif

        return 0;
}