singe/src/util.c

512 lines
10 KiB
C

/*
*
* Singe 2
* Copyright (C) 2006-2024 Scott Duensing <scott@kangaroopunch.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
#ifdef _WIN32
#include <windows.h>
#include <stdio.h>
#include <fcntl.h>
#include <io.h>
#define ourMkdir(p,m) mkdir(p)
static const int CONSOLE_LINES = 1000;
#else
#define ourMkdir mkdir
#endif
#include <stdlib.h>
#include <dirent.h>
#include <string.h>
#include <ctype.h>
#include "util.h"
static bool _consoleEnabled = true;
static bool _outputHappened = false;
static FILE *_utilTraceFile = NULL;
bool utilChMod(const char *path, const mode_t mode) {
bool result = true;
#ifdef _WIN32
(void)path;
(void)mode;
#else
result = (chmod(path, mode) >= 0);
#endif
return result;
}
char *utilCreateString(char *format, ...) {
va_list args;
char *string;
va_start(args, format);
string = utilCreateStringVArgs(format, args);
va_end(args);
return string;
}
__attribute__((__format__(__printf__, 1, 0)))
char *utilCreateStringVArgs(char *format, va_list args) {
va_list argsCopy;
int32_t size = 0;
char *buffer = NULL;
va_copy(argsCopy, args);
size = vsnprintf(NULL, 0, format, argsCopy) + 1;
va_end(argsCopy);
buffer = calloc(1, (size_t)size);
if (buffer) {
vsnprintf(buffer, (size_t)size, format, args);
}
return buffer;
}
__attribute__((__format__(__printf__, 1, 0)))
__attribute__((noreturn))
void utilDie(char *fmt, ...) {
va_list args;
if (_consoleEnabled) {
va_start(args, fmt);
vfprintf(stderr, fmt, args);
va_end(args);
printf("\n");
fflush(stderr);
#ifdef _WIN32
getchar();
#endif
}
exit(1);
}
void utilEnableConsole(bool enable) {
_consoleEnabled = enable;
}
bool utilFileExists(char *filename) {
FILE *file;
if ((file = fopen(filename, "r+"))) {
fclose(file);
return true;
}
return false;
}
void utilFixPathSeparators(char **path, bool slash) {
int32_t i = 0;
int32_t j = 0;
char *work = *path;
char *temp = NULL;
// Flip path separators to whatever our OS wants & remove repeated separators.
while (work[i] != 0) {
// Correct separator
if (work[i] == '\\' || work[i] == '/') {
// Was the prior character a seprator?
if (j == 0) {
work[j++] = utilGetPathSeparator();
} else {
if (work[j - 1] != utilGetPathSeparator()) {
// No, accept it.
work[j++] = utilGetPathSeparator();
}
}
} else {
work[j++] = work[i];
}
i++;
}
work[j] = 0;
if (slash) {
// Does this string end with a path separator?
if (work[strlen(work) - 1] != utilGetPathSeparator()) {
// No - append one.
temp = strdup(work);
free(work);
work = malloc(sizeof(char) * (strlen(temp) + 2));
strcpy(work, temp);
work[strlen(temp)] = utilGetPathSeparator();
work[strlen(temp) + 1] = 0;
free(temp);
}
}
*path = work;
}
bool utilGetConsoleEnabled(void) {
return _consoleEnabled && _outputHappened;
}
char *utilGetFileExtension(char *filename) {
char *start = filename + strlen(filename);
int32_t x;
// Scan through name and find the last '.'
for (x=0; x<(int32_t)strlen(filename); x++) {
if (filename[x] == '.') {
start = &filename[x + 1];
}
// Reset if we find a path separator
if (filename[x] == '\\' || filename[x] == '/') {
start = filename + strlen(filename);
}
}
return start;
}
char *utilGetLastPathComponent(char *pathname) {
static char *start;
int32_t x;
start = pathname;
// Scan through name and find the last path separator
for (x=0; x<(int32_t)strlen(pathname); x++) {
if (pathname[x] == '\\' || pathname[x] == '/') {
start = &pathname[x + 1];
}
}
return start;
}
char utilGetPathSeparator(void) {
#ifdef _WIN32
return '\\';
#else
return '/';
#endif
}
char *utilGetUpToLastPathComponent(char *pathname) {
static char *copy = NULL;
bool dumb = false; // Using (copy == NULL) below didn't work after optimizations, so enter the dummy.
int32_t x;
x = (int32_t)(strlen(pathname) - strlen(utilGetLastPathComponent(pathname))) - 1;
if (x < 0) x = 0;
if (dumb) {
free(copy);
copy = NULL;
} else {
dumb = true;
}
copy = strdup(pathname);
copy[x] = 0;
utilFixPathSeparators(&copy, true);
return copy;
}
bool utilMkDirP(const char *dir, const mode_t mode) {
char tmp[UTIL_PATH_MAX];
char *p = NULL;
struct stat sb;
size_t len;
// Make copy of dir.
len = strnlen(dir, UTIL_PATH_MAX);
if (len == 0 || len == UTIL_PATH_MAX) {
return -1;
}
memcpy(tmp, dir, len);
tmp[len] = '\0';
// Remove trailing slash.
if (tmp[len - 1] == utilGetPathSeparator()) {
tmp[len - 1] = '\0';
}
// Does it already exist?
if (stat(tmp, &sb) == 0) {
if (S_ISDIR (sb.st_mode)) {
return true;
}
}
// Recursive mkdir.
for (p = tmp + 1; *p; p++) {
if (*p == utilGetPathSeparator()) {
*p = 0;
if (stat(tmp, &sb) != 0) {
// Does not exist - create it.
if (ourMkdir(tmp, mode) < 0) {
return false;
}
} else {
if (!S_ISDIR(sb.st_mode)) {
// Not a directory
return false;
}
}
*p = utilGetPathSeparator();
}
}
// Check path
if (stat(tmp, &sb) != 0) {
// Does not exist - create it.
if (ourMkdir(tmp, mode) < 0) {
return false;
}
} else {
if (!S_ISDIR(sb.st_mode)) {
// Not a directory
return false;
}
}
return true;
}
bool utilPathExists(char *pathname) {
DIR *dir = opendir(pathname);
if (dir) {
closedir(dir);
return true;
}
return false;
}
char *utilReadFile(char *filename, size_t *bytes) {
char *data = NULL;
FILE *in = fopen(filename, "rb");
size_t read = 0;
(void)read;
*bytes = 0;
if (in) {
fseek(in, 0, SEEK_END);
*bytes = ftell(in);
fseek(in, 0, SEEK_SET);
data = malloc(sizeof(char) * (*bytes));
read = fread(data, sizeof(char), *bytes, in);
fclose(in);
}
return data;
}
char *utilReadLine(char *haystack, size_t length, char **offset) {
size_t bytes = 0;
char *temp = *offset;
char *tail = temp;
char *result = NULL;
// They didn't know where to start
if (temp == NULL) {
temp = haystack;
tail = temp;
}
// Is there still data to read?
while ((size_t)(tail - haystack) < length) {
// Is this the end of a line?
if ((tail[0] == 10) || (tail[0] == 13)) {
// Yep!
bytes = tail - temp + 1;
result = malloc(sizeof(char) * bytes);
memcpy(result, temp, bytes - 1);
result[bytes - 1] = 0;
// Read past any additional CR/LFs
while ((tail[0] == 10) || (tail[0] == 13)) {
tail++;
}
// Return where we left off
temp = tail;
*offset = temp;
return result;
}
// Next character
tail++;
}
// Was there data at the end of the block with no CR/LF?
if (tail > temp) {
// Yep. Treat it as a line.
bytes = tail - temp + 1;
result = malloc(sizeof(char) * bytes);
memcpy(result, temp, bytes - 1);
result[bytes - 1] = 0;
temp = tail;
*offset = temp;
}
// Didn't find anything
return result;
}
void utilRedirectConsole(void) {
#ifdef _WIN32
// http://dslweb.nwnexus.com/~ast/dload/guicon.htm
int hConHandle; // Not changing this int.
intptr_t lStdHandle;
CONSOLE_SCREEN_BUFFER_INFO coninfo;
FILE *fp;
static bool consoleOpen = false;
if (!consoleOpen) {
consoleOpen = true;
// allocate a console for this app
AllocConsole();
// set the screen buffer to be big enough to let us scroll text
GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &coninfo);
coninfo.dwSize.Y = CONSOLE_LINES;
SetConsoleScreenBufferSize(GetStdHandle(STD_OUTPUT_HANDLE), coninfo.dwSize);
// redirect unbuffered STDOUT to the console
lStdHandle = (intptr_t)GetStdHandle(STD_OUTPUT_HANDLE);
hConHandle = _open_osfhandle(lStdHandle, _O_TEXT);
fp = _fdopen(hConHandle, "w");
*stdout = *fp;
setvbuf(stdout, NULL, _IONBF, 0);
// redirect unbuffered STDIN to the console
lStdHandle = (intptr_t)GetStdHandle(STD_INPUT_HANDLE);
hConHandle = _open_osfhandle(lStdHandle, _O_TEXT);
fp = _fdopen(hConHandle, "r");
*stdin = *fp;
setvbuf(stdin, NULL, _IONBF, 0);
// redirect unbuffered STDERR to the console
lStdHandle = (intptr_t)GetStdHandle(STD_ERROR_HANDLE);
hConHandle = _open_osfhandle(lStdHandle, _O_TEXT);
fp = _fdopen(hConHandle, "w");
*stderr = *fp;
setvbuf(stderr, NULL, _IONBF, 0);
}
#endif
}
__attribute__((__format__(__printf__, 1, 0)))
void utilSay(char *fmt, ...) {
va_list args;
if (_consoleEnabled) {
va_start(args, fmt);
vfprintf(stdout, fmt, args);
va_end(args);
printf("\n");
fflush(stdout);
_outputHappened = true;
}
}
bool utilStartsWith(char *string, char *start) {
return strncmp(start, string, strlen(start)) == 0;
}
int utilStricmp(char *a, char *b) {
for (;; a++, b++) {
int d = tolower((unsigned char)*a) - tolower((unsigned char)*b);
if (d != 0 || !*a) {
return d;
}
}
}
// Windows does not have strndup() so we implement our own.
char *utilStrndup( const char *s1, size_t n) {
char *copy = (char *)malloc(n + 1);
memcpy(copy, s1, n);
copy[n] = 0;
return copy;
}
void utilTrace(char *fmt, ...) {
va_list args;
if (_utilTraceFile) {
va_start(args, fmt);
utilTraceVArgs(fmt, args);
va_end(args);
}
}
void utilTraceEnd(void) {
if (_utilTraceFile) fclose(_utilTraceFile);
}
FILE *utilTraceGetFile(void) {
return _utilTraceFile;
}
void utilTraceStart(char *filename) {
_utilTraceFile = fopen(filename, "wt");
if (!_utilTraceFile) utilDie("Unable to create trace file: %s", filename);
}
__attribute__((__format__(__printf__, 1, 0)))
void utilTraceVArgs(char *fmt, va_list args) {
#if defined(va_copy)
va_list args2;
#endif
if (_utilTraceFile) {
#if defined(va_copy)
va_copy(args2, args);
vprintf(fmt, args2);
printf("\n");
va_end(args2);
#endif
vfprintf(_utilTraceFile, fmt, args);
fprintf(_utilTraceFile, "\n");
fflush(_utilTraceFile);
}
}