HuntnGather – Rev 38

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 <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>
#include <clib/utility_protos.h>
#endif

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

#include "/shared/utilities.h"

/*
        *
        * 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 = strlen(b);
#endif
        return p > q ? p : q;
}

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

/*
        *
        * Converts a string to case.
        */
void StrUpr(char *s) {
        while(*s != '\0') {
#if defined ___AmigaOS___
                *s = ToUpper(*s);
#else
                *s = toupper((unsigned char)*s);
#endif
                ++s;
        }
}

/*
        *
        * Gets the filesystem type of a file or directory.
        */
FS_TYPE GetFsType(char *path) {
#if defined ___AmigaOS___
        struct FileInfoBlock *FIB;
        BPTR lock;
#else
        struct stat dirStat;
#endif

#if defined ___AmigaOS___
        if((lock = Lock(path, ACCESS_READ)) == NULL) {
                fprintf(stderr, "Lock failed for path '%s'.\n", path);
                return UNKNOWN;
        }

        if((FIB = AllocDosObject(DOS_FIB, NULL)) == NULL) {
                fprintf(stderr, "Could not allocated file information block for path '%s'.\n", path);
                UnLock(lock);
                return UNKNOWN;
        }

        if(Examine(lock, FIB) == FALSE) {
                fprintf(stderr, "Path '%s' could not be examined.\n", path);
                UnLock(lock);
                FreeDosObject(DOS_FIB, FIB);
                FIB = NULL;
                return UNKNOWN;
        }

        if(FIB->fib_DirEntryType < 0) {
                UnLock(lock);
                FreeDosObject(DOS_FIB, FIB);
                FIB = NULL;
                return REGULAR;
#else
        stat(path, &dirStat);
        if(!S_ISDIR(dirStat.st_mode)) {
                return REGULAR;
#endif
        }

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

        return DIRECTORY;
}

/*
        *
        * 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, "Could not open file '%s' for reading.\n", dbFile);
                return -1;
        }

        lines = 0;
        if(PROGRAM_VERBOSE) {
                fprintf(stdout, "Counting lines in '%s'...\r", dbFile);
        }

#if defined ___AsyncIO___
        while(PROGRAM_RUN && (c = ReadCharAsync(fp)) != -1) {
#else
        while(PROGRAM_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) {
                        PROGRAM_RUN = FALSE;
                        continue;
                }
#endif
                switch(c) {
                        case '\n':
                                ++lines;

                                if(PROGRAM_VERBOSE) {
                                        fprintf(stdout, "Counting lines in '%s': %d.\r", dbFile, lines);
                                }
                                break;
                }
        }

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

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

        return lines;
}

/*
        *
        * 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 failed for path '%s'.\n", path);
                return NULL;
        }
        if(NameFromLock(lock, abs, PATH_MAX) == FALSE) {
                fprintf(stderr, "Full path for '%s' could not be retrieved.\n", path);
                UnLock(lock);
                return NULL;
        }
        UnLock(lock);
#else
        /*
         * On POSIX this should be:
         * 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';
}

/*
        *
        * 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.
        */
VECTOR *CreateTemporaryFiles(int files) {
        VECTOR *tmpNames;

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

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

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

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

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

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

        return tmpNames;
}

/*
        *
        * Skips a line in a file.
        */
#if defined ___AsyncIO___
void SkipLine(struct AsyncFile *fp) {
        LONG c;
        while(PROGRAM_RUN && (c = ReadCharAsync(fp)) != -1) {
#else
void SkipLine(FILE *fp) {
        char c;
        while(PROGRAM_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) {
                        PROGRAM_RUN = FALSE;
                        continue;
                }
#endif
                switch(c) {
                        case '\n':
                                return;
                }
        }
}

/*
        *
        * Peeks for the next line in a file.
        */
#if defined ___AsyncIO___
dbLine *PeekLine(struct AsyncFile *fp) {
#else
dbLine *PeekLine(FILE *fp) {
#endif
        dbLine *line;

        // Read the next line.
        if((line = ReadLine(fp)) == NULL) {
                return NULL;
        }

        // Rewind the file by the number of read characters.
#if defined ___AsyncIO___
        if(SeekAsync(fp, -(line->length + 1), MODE_CURRENT) == -1) {
                fprintf(stderr, "Could not seek in file.\n");
                free(line->string);
                free(line);
                line = NULL;
                return NULL;
        }
#else
        if(fseek(fp, -(line->length + 1), SEEK_CUR) != 0) {
                fprintf(stderr, "Could not seek in file.\n");
                free(line->string);
                free(line);
                line = NULL;
                return NULL;
        }
#endif

        return line;
}

/*
        *
        * Reads the next line in a file.
        */
#if defined ___AsyncIO___
dbLine *ReadLine(struct AsyncFile *fp) {
        LONG c;
#else
dbLine *ReadLine(FILE *fp) {
        char c;
#endif
        int i;
        dbLine *line;

        i = 0;
#if defined ___AsyncIO___
        while(PROGRAM_RUN && (c = ReadCharAsync(fp)) != -1) {
#else
        while(PROGRAM_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) {
                        PROGRAM_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");
                                        fprintf(stderr, "index at: %d\n", i);
                                        return NULL;
                                }
#else
                                if(fseek(fp, -(i + 1), SEEK_CUR) != 0) {
                                        fprintf(stderr, "Could not seek in file.\n");
                                        return NULL;
                                }
#endif
                                goto LINE;
                }

                ++i;
        }

LINE:

        if(i == 0) {
                return NULL;
        }

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

        if((line->string = malloc((i + 1) * sizeof(*line->string))) == NULL) {
                fprintf(stderr, "Memory allocation error.\n");
                free(line);
                line = NULL;
                return NULL;
        }

#if defined ___AsyncIO___
        if(ReadAsync(fp, line->string, i) != i) {
#else
        if(fread(line->string, sizeof(char), i, fp) != i) {
#endif
                fprintf(stderr, "Unable to read line.\n");
                free(line->string);
                free(line);
                line = NULL;
                return NULL;
        }

// Clear newline.
#if defined ___AsyncIO___
        ReadCharAsync(fp);
#else
        fgetc(fp);
#endif

        line->length = i;

        return line;
}

/*
  *
  * 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) {
        int i;
        for(i = 0; i < count; ++i) {
                if(RemoveFile(names[i]) == FALSE) {
                        fprintf(stderr, "Could not remove file '%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, "Could not open file '%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, "Could not open file '%s' for writing.\n", b);

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

                return;
        }

#if defined ___AsyncIO___
        while(PROGRAM_RUN && (c = ReadCharAsync(ap)) != -1) {
#else
        while(PROGRAM_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) {
                        PROGRAM_RUN = FALSE;
                        continue;
                }
#endif
#if defined ___AsyncIO___
                if(WriteCharAsync(bp, (UBYTE)c) != 1) {
#else
                if(fprintf(bp, "%c", c) != 1) {
#endif
                        fprintf(stderr, "Could not write to file '%s'.\n", b);
                        break;
                }
        }

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

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

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

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

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

        for(ptr = line->string, side = 0, i = 0, j = 0; PROGRAM_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) {
                        PROGRAM_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;
}

/*
        * Compare two strings.
        */
#if defined ___AmigaOS___
BOOL StringMatch(char *a, char *b) {
#else
int StringMatch(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);
                pattern = NULL;
        }

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

        success = FALSE;

#if defined ___NOCASE_FS___
        StrUpr(e);
        StrUpr(n);
#endif

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

        return success;
#endif
}