HuntnGather – Rev 29

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

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

#include <sys/types.h>

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

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

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

#define PROGRAM_VERSION "1.7.4"

#if defined ___AmigaOS___
/*************************************************************************/
/*        Version string used for querrying the program version.         */
/*************************************************************************/
TEXT version_string[] =
        "\0$VER: Hunt " 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 NAME_BUF 32
#define PATH_BUF 128
#define DEFAULT_DATABASE_FILE "S:gather.db"

int run = TRUE;

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

/*
        *
        * Returns largest of strings.
        */
#if defined ___AmigaOS___
LONG StringLenMax(char *a, char *b) {
        LONG p = strlen(a);
        LONG q = strlen(b);
#else
int StrlenMax(char *a, char *b) {
        int q = strlen(a);
        int p = strien(b);
#endif
        return p > q ? p : q;
}

/*
        * Compare two strings.
        */
#if defined ___AmigaOS___
BOOL compare(char *a, char *b) {
#else
int compare(char *a, char *b) {
#endif
#if defined ___AmigaOS___
        ULONG size;
        BOOL success;
        UBYTE *pattern;

        // "must be at least 2 times as large plus 2 bytes"
        size = strlen(b) * 2 + 2;

        success = FALSE;

        if(pattern = AllocVec(size, MEMF_ANY|MEMF_CLEAR)) {
                switch(ParsePatternNoCase(b, pattern, (LONG)size)) {
                        case 1: // the pattern contains wildcards
                                success = MatchPatternNoCase(pattern, a);

                                break;
                        case 0: // no wildcards so fall back to exact name match
#if defined ___NOCASE_FS___
                                success = (Strnicmp(a, b, StringLenMax(a, b)) == 0);
#else
                                success = (StrnCmp(a, b, StringLenMax(a, b)) == 0);
#endif

                                break;
                }

                FreeVec(pattern);
        }

        return success;
#else
        int success;
        char *e = a;
        char *n = b;

        success = FALSE;

#if defined ___NOCASE_FS___
        e = strupr(e);
        n = strupr(n);
#endif

        // search for substring
        success = strstr(e, n) != NULL;

        return success;
#endif
}


/*
        *
        * Search the database for a matching string.
        */
void SearchDatabase(char *dbFile, char* needle) {
#if defined ___AsyncIO___
        struct AsyncFile *fp;
        LONG c;
#else
        FILE *fp;
        char c;
#endif
        char *name;
        int name_size;
        char *path;
        int path_size;
        int i;
        int side;
        int match;
        int total;

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

        name_size = NAME_BUF;
        name = malloc(name_size * sizeof(*name));
        path_size = PATH_BUF;
        path = malloc(path_size * sizeof(*path));

        i = 0;
        side = 0;
        match = FALSE;
        total = 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':
                                ++total;
                                if(match) {
                                        fprintf(stdout, "%s\n", path);
                                        match = FALSE;
                                }
                                if(name != NULL) {
                                        free(name);
                                        name_size = NAME_BUF;
                                        name = malloc(name_size * sizeof(*name));
                                }
                                --side;
                                i = 0;
                                break;
                        case '\t':
                                // Case insensitive match.
                                if(compare(name, needle)) {
                                        match = TRUE;
                                }
                                if(path != NULL) {
                                        free(path);
                                        path_size = PATH_BUF;
                                        path = malloc(path_size * sizeof(*name));
                                }
                                ++side;
                                i = 0;
                                break;
                        default:
                                switch(side) {
                                        case 0:
                                                if(strlen(name) == name_size) {
                                                        name_size = 1.5 * name_size;
                                                        name = realloc(name, name_size * sizeof(*name));
                                                }
                                                name[i] = c;
                                                name[i + 1] = '\0';
                                                break;
                                        case 1:
                                                if(strlen(path) == path_size) {
                                                        path_size = 1.5 * path_size;
                                                        path = realloc(path, path_size * sizeof(*path));
                                                }
                                                path[i] = c;
                                                path[i + 1] = '\0';
                                                break;
                                        default:
                                                fprintf(stderr, "Database corrupted.\n");
                                                break;
                                }
                                ++i;
                                break;
                }
        }

        free(name);
        free(path);

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

/*
        *
        * Search the database for the matching string.
        */
void Hunt(char *dbFile, char *needle) {
        // Search the database for the matching string.
        SearchDatabase(dbFile, needle);
}

void usage(char *name) {
        fprintf(stdout, "Hunt & Gather - %s, a file index search tool.            \n", name);
        fprintf(stdout, "Version: %s                                              \n", PROGRAM_VERSION);
   fprintf(stdout, "                                                         \n");
        fprintf(stdout, "SYNTAX: %s [-d DATABASE] PATTERN                         \n", name);
   fprintf(stdout, "                                                         \n");
        fprintf(stdout, "    -d DATABASE    A path to a database generated by the \n");
        fprintf(stdout, "                   Gather tool that should be searched.  \n");
   fprintf(stdout, "                                                         \n");
        fprintf(stdout, "PATTERN is an AmigaOS DOS pattern to match file names.   \n");
        fprintf(stdout, "                                                         \n");
        fprintf(stdout, "(c) 2021 Wizardry and Steamworks, MIT.                   \n");
}

int main(int argc, char **argv) {
#if defined ___AmigaOS___
        struct FileInfoBlock *FIB;
        BPTR lock;
#else
        struct stat dirStat;
#endif
        int option;
        char *dbFile;

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

        dbFile = DEFAULT_DATABASE_FILE;
        while((option = getopt(argc, argv, "hd:")) != -1) {
                switch(option) {
                        case 'd':
                                dbFile = optarg;
                                break;
                        case 'h':
                                usage(argv[0]);
                                return 0;
                        case '?':
                                fprintf(stderr, "Invalid option %c.\n", optopt);;
                                return 1;
                }
        }

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

#if defined ___AmigaOS___
        if((lock = Lock(dbFile, ACCESS_READ)) == NULL) {
                fprintf(stderr, "Path '%s' is not accessible.\n", dbFile);
                return 1;
        }

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

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

        if(FIB->fib_DirEntryType > 0) {
#else
        stat(dbFile, &dirStat);
        if(!S_ISREG(dirStat.st_mode)) {
#endif
                fprintf(stderr, "'%s' is not a file.\n", dbFile);
                return 1;
        }

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

        Hunt(dbFile, argv[optind]);

   return 0;
}