/* * * Singe 2 * Copyright (C) 2006-2024 Scott Duensing * * 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 #include #include #include #define ourMkdir(p,m) mkdir(p) static const int CONSOLE_LINES = 1000; #else #define ourMkdir mkdir #endif #include #include #include #include #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(©, 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); } }