HuntnGather – Rev 49
?pathlinks?
///////////////////////////////////////////////////////////////////////////
// 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.
*/
#if defined ___AmigaOS___
void StrUpr(UBYTE *s) {
#else
void StrUpr(char *s) {
#endif
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;
}
/*
*
* Get the size of a file.
*/
int GetFileSize(char *dbFile) {
#if defined ___AsyncIO___
struct AsyncFile *fp;
#else
FILE *fp;
#endif
int size;
#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;
}
#if defined ___AsyncIO___
SeekAsync(fp, 0, MODE_END);
size = SeekAsync(fp, 0, MODE_CURRENT);
#else
fseek(fp, 0L, SEEK_END);
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;
#else
FILE *fp;
#endif
int lines;
dbLine *line = NULL;
#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;
}
if(PROGRAM_VERBOSE) {
fprintf(stdout, "Counting lines in '%s'...\r", dbFile);
}
lines = 0;
while(PROGRAM_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) {
PROGRAM_RUN = FALSE;
continue;
}
#endif
++lines;
if(PROGRAM_VERBOSE) {
fprintf(stdout, "Counting lines in '%s': %d.\r", dbFile, lines);
}
free(line->string);
free(line);
line = NULL;
}
if(line != NULL) {
free(line->string);
free(line);
line = NULL;
}
#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;
int total;
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;
total = files;
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->array[files] = CreateTemporaryFile();
++tmpNames->length;
if(PROGRAM_VERBOSE) {
fprintf(stdout, "Creating temporary files: %d%%.\r", (int)(((float)tmpNames->length / total) * 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), (unsigned int)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(VECTOR *names) {
int i;
for(i = 0; i < names->length; ++i) {
if(RemoveFile(names->array[i]) == FALSE) {
fprintf(stderr, "Could not remove file '%s'.\n", (char *)names->array[i]);
continue;
}
fprintf(stderr, "Removing file '%s'\n", (char *)names->array[i]);
}
}
/*
*
* Copies a file to another file by name.
*/
void CopyLines(char *a, char *b) {
#if defined ___AsyncIO___
struct AsyncFile *ap;
struct AsyncFile *bp;
#else
FILE *ap;
FILE *bp;
#endif
dbLine *line = NULL;
// 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;
}
while(PROGRAM_RUN && (line = ReadLine(ap)) != NULL) {
#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___
WriteAsync(bp, line->string, line->length);
WriteAsync(bp, "\n", 1);
#else
fprintf(bp, "%s\n", line->string);
#endif
free(line->string);
free(line);
line = NULL;
}
if(line != NULL) {
free(line->string);
free(line);
line = NULL;
}
#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;
}