diff --git a/Makefile b/Makefile index a89d8c2..bfc68c7 100644 --- a/Makefile +++ b/Makefile @@ -148,21 +148,22 @@ install-doc: # DO NOT DELETE -util.o: gmqcc.h opts.def -fs.o: gmqcc.h opts.def -conout.o: gmqcc.h opts.def -opts.o: gmqcc.h opts.def -pak.o: gmqcc.h opts.def +pak.o: gmqcc.h opts.def platform.h +ansi.o: platform.h gmqcc.h opts.def +util.o: gmqcc.h opts.def platform.h stat.o: gmqcc.h opts.def -test.o: gmqcc.h opts.def +fs.o: gmqcc.h opts.def platform.h +conout.o: gmqcc.h opts.def platform.h +opts.o: gmqcc.h opts.def platform.h +test.o: gmqcc.h opts.def platform.h main.o: gmqcc.h opts.def lexer.h -lexer.o: gmqcc.h opts.def lexer.h -parser.o: parser.h gmqcc.h opts.def lexer.h ast.h ir.h +lexer.o: gmqcc.h opts.def lexer.h platform.h +parser.o: parser.h gmqcc.h opts.def lexer.h ast.h ir.h platform.h code.o: gmqcc.h opts.def -ast.o: gmqcc.h opts.def ast.h ir.h parser.h lexer.h -ir.o: gmqcc.h opts.def ir.h -ftepp.o: gmqcc.h opts.def lexer.h +ast.o: gmqcc.h opts.def ast.h ir.h parser.h lexer.h platform.h +ir.o: gmqcc.h opts.def ir.h platform.h +ftepp.o: gmqcc.h opts.def lexer.h platform.h utf8.o: gmqcc.h opts.def correct.o: gmqcc.h opts.def -fold.o: ast.h ir.h gmqcc.h opts.def parser.h lexer.h -intrin.o: parser.h gmqcc.h opts.def lexer.h ast.h ir.h +fold.o: ast.h ir.h gmqcc.h opts.def parser.h lexer.h platform.h +intrin.o: parser.h gmqcc.h opts.def lexer.h ast.h ir.h platform.h diff --git a/PORTING b/PORTING new file mode 100644 index 0000000..bbdb274 --- /dev/null +++ b/PORTING @@ -0,0 +1,4 @@ +Porting gmqcc to a new platform is farily trivial, in most cases ansi.c +will be sufficent enough to get it to run on your favorite platform. If +however it isn't you can duplicate ansi.c and change it accordingly. +Changes to platform.h may also be required. diff --git a/ansi.c b/ansi.c new file mode 100644 index 0000000..412e9a8 --- /dev/null +++ b/ansi.c @@ -0,0 +1,169 @@ +/* + * Copyright (C) 2012, 2013 + * Dale Weiler + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#define GMQCC_PLATFORM_HEADER +#include +#include +#include + +#include "platform.h" +#include "gmqcc.h" + +int platform_vsnprintf(char *buffer, size_t bytes, const char *format, va_list arg) { + return vsnprintf(buffer, bytes, format, arg); +} + +int platform_vsscanf(const char *str, const char *format, va_list arg) { + return vsscanf(str, format, arg); +} + +const struct tm *platform_localtime(const time_t *timer) { + return localtime(timer); +} + +const char *platform_ctime(const time_t *timer) { + return ctime(timer); +} + +char *platform_strncat(char *dest, const char *src, size_t num) { + return strncat(dest, src, num); +} + +const char *platform_tmpnam(char *str) { + return tmpnam(str); +} + +const char *platform_getenv(char *var) { + return getenv(var); +} + +int platform_vasprintf(char **dat, const char *fmt, va_list args) { + int ret; + int len; + char *tmp = NULL; + char buf[128]; + va_list cpy; + + va_copy(cpy, args); + len = vsnprintf(buf, sizeof(buf), fmt, cpy); + va_end (cpy); + + if (len < (int)sizeof(buf)) { + *dat = util_strdup(buf); + return len; + } + + tmp = (char*)mem_a(len + 1); + if ((ret = vsnprintf(tmp, len + 1, fmt, args)) != len) { + mem_d(tmp); + *dat = NULL; + return -1; + } + + *dat = tmp; + return len; +} + +char *platform_strcat(char *dest, const char *src) { + return strcat(dest, src); +} + +char *platform_strncpy(char *dest, const char *src, size_t num) { + return strncpy(dest, src, num); +} + +const char *platform_strerror(int err) { + return strerror(err); +} + +FILE *platform_fopen(const char *filename, const char *mode) { + return fopen(filename, mode); +} + +size_t platform_fread(void *ptr, size_t size, size_t count, FILE *stream) { + return fread(ptr, size, count, stream); +} + +size_t platform_fwrite(const void *ptr, size_t size, size_t count, FILE *stream) { + return fwrite(ptr, size, count, stream); +} + +int platform_fflush(FILE *stream) { + return fflush(stream); +} + +int platform_vfprintf(FILE *stream, const char *format, va_list arg) { + return vfprintf(stream, format, arg); +} + +int platform_fclose(FILE *stream) { + return fclose(stream); +} + +int platform_ferror(FILE *stream) { + return ferror(stream); +} + +int platform_fgetc(FILE *stream) { + return fgetc(stream); +} + +int platform_fputs(const char *str, FILE *stream) { + return fputs(str, stream); +} + +int platform_fseek(FILE *stream, long offset, int origin) { + return fseek(stream, offset, origin); +} + +long platform_ftell(FILE *stream) { + return ftell(stream); +} + +int platform_mkdir(const char *path, int mode) { + /* + * For some reason mingw32 just doesn't have a correct mkdir impl + * so we handle that here. + */ +# ifdef _WIN32 + (void)mode; + return mkdir(path); +# else + return mkdir(path, mode); +# endif /*!_WIN32*/ +} + +DIR *platform_opendir(const char *path) { + return opendir(path); +} + +int platform_closedir(DIR *dir) { + return closedir(dir); +} + +struct dirent *platform_readdir(DIR *dir) { + return readdir(dir); +} + +int platform_isatty(int fd) { + return isatty(fd); +} diff --git a/code.c b/code.c index 5703e50..05ffe70 100644 --- a/code.c +++ b/code.c @@ -401,7 +401,7 @@ static bool code_write_memory(code_t *code, uint8_t **datmem, size_t *sizedat, u bool code_write(code_t *code, const char *filename, const char *lnofile) { prog_header_t code_header; - FILE *fp = NULL; + fs_file_t *fp = NULL; code_create_header(code, &code_header, filename, lnofile); diff --git a/conout.c b/conout.c index 63391d6..c22758b 100644 --- a/conout.c +++ b/conout.c @@ -20,168 +20,20 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ +#include #include "gmqcc.h" -/* - * isatty/STDERR_FILENO/STDOUT_FILNO - * + some other things likewise. - */ -#ifndef _WIN32 -# include -#else -# include - /* - * Windows and it's posix underscore bullshit. We simply fix this - * with yay, another macro :P - */ -# define isatty _isatty -#endif - -#define GMQCC_IS_STDOUT(X) ((FILE*)((void*)X) == stdout) -#define GMQCC_IS_STDERR(X) ((FILE*)((void*)X) == stderr) +#define GMQCC_IS_STDOUT(X) ((fs_file_t*)((void*)X) == (fs_file_t*)stdout) +#define GMQCC_IS_STDERR(X) ((fs_file_t*)((void*)X) == (fs_file_t*)stderr) #define GMQCC_IS_DEFINE(X) (GMQCC_IS_STDERR(X) || GMQCC_IS_STDOUT(X)) typedef struct { - FILE *handle_err; - FILE *handle_out; - - int color_err; - int color_out; + fs_file_t *handle_err; + fs_file_t *handle_out; + int color_err; + int color_out; } con_t; -/* - * Doing colored output on windows is fucking stupid. The linux way is - * the real way. So we emulate it on windows :) - */ -#ifdef _WIN32 -#define WIN32_LEAN_AND_MEAN -#include - -/* - * Windows doesn't have constants for FILENO, sadly but the docs tell - * use the constant values. - */ -#undef STDERR_FILENO -#undef STDOUT_FILENO -#define STDERR_FILENO 2 -#define STDOUT_FILENO 1 - -enum { - RESET = 0, - BOLD = 1, - BLACK = 30, - RED, - GREEN, - YELLOW, - BLUE, - MAGENTA, - CYAN, - GRAY, - WHITE = GRAY -}; - -enum { - WBLACK, - WBLUE, - WGREEN = 2, - WRED = 4, - WINTENSE = 8, - WCYAN = WBLUE | WGREEN, - WMAGENTA = WBLUE | WRED, - WYELLOW = WGREEN | WRED, - WWHITE = WBLUE | WGREEN | WRED -}; - -static const int ansi2win[] = { - WBLACK, - WRED, - WGREEN, - WYELLOW, - WBLUE, - WMAGENTA, - WCYAN, - WWHITE -}; - -static int win_fputs(FILE *h, const char *str) { - /* state for translate */ - int acolor = 0; - int wcolor = 0; - int icolor = 0; - int state = 0; - - /* attributes */ - int intense = -1; - int colors[] = {-1, -1 }; - int colorpos = 1; - int length = 0; - CONSOLE_SCREEN_BUFFER_INFO cinfo; - GetConsoleScreenBufferInfo ( - (GMQCC_IS_STDOUT(h)) ? - GetStdHandle(STD_OUTPUT_HANDLE) : - GetStdHandle(STD_ERROR_HANDLE), &cinfo - ); - icolor = cinfo.wAttributes; - - while (*str) { - if (*str == '\x1B') - state = '\x1B'; - else if (state == '\x1B' && *str == '[') - state = '['; - else if (state == '[') { - if (*str != 'm') { - colors[colorpos] = *str; - colorpos--; - } else { - int find; - int mult; - for (find = colorpos + 1, acolor = 0, mult = 1; find < 2; find++) { - acolor += (colors[find] - 48) * mult; - mult *= 10; - } - - /* convert to windows color */ - if (acolor == BOLD) - intense = WINTENSE; - else if (acolor == RESET) { - intense = WBLACK; - wcolor = icolor; - } - else if (BLACK <= acolor && acolor <= WHITE) - wcolor = ansi2win[acolor - 30]; - else if (acolor == 90) { - /* special gray really white man */ - wcolor = WWHITE; - intense = WBLACK; - } - - SetConsoleTextAttribute ( - (GMQCC_IS_STDOUT(h)) ? - GetStdHandle(STD_OUTPUT_HANDLE) : - GetStdHandle(STD_ERROR_HANDLE), - - wcolor | intense | (icolor & 0xF0) - ); - colorpos = 1; - state = -1; - } - } else { - fs_file_write(str, 1, 1, stdout); - length ++; - } - str++; - } - /* restore */ - SetConsoleTextAttribute( - (GMQCC_IS_STDOUT(h)) ? - GetStdHandle(STD_OUTPUT_HANDLE) : - GetStdHandle(STD_ERROR_HANDLE), - icolor - ); - return length; -} -#endif - /* * We use standard files as default. These can be changed at any time * with con_change(F, F) @@ -197,10 +49,8 @@ static con_t console; * checks. */ static void con_enablecolor(void) { - if (console.handle_err == stderr || console.handle_err == stdout) - console.color_err = !!(isatty(STDERR_FILENO)); - if (console.handle_out == stderr || console.handle_out == stdout) - console.color_out = !!(isatty(STDOUT_FILENO)); + console.color_err = util_isatty(console.handle_err); + console.color_out = util_isatty(console.handle_out); } /* @@ -208,23 +58,8 @@ static void con_enablecolor(void) { * arguments. This colorizes for windows as well via translate * step. */ -static int con_write(FILE *handle, const char *fmt, va_list va) { - int ln; - #ifndef _WIN32 - ln = vfprintf(handle, fmt, va); - #else - { - char data[4096]; - memset(data, 0, sizeof(data)); -#ifdef _MSC_VER - vsnprintf_s(data, sizeof(data), sizeof(data), fmt, va); -#else - vsnprintf(data, sizeof(data), fmt, va); -#endif - ln = (GMQCC_IS_DEFINE(handle)) ? win_fputs(handle, data) : fs_file_puts(handle, data); - } - #endif - return ln; +static int con_write(fs_file_t *handle, const char *fmt, va_list va) { + return vfprintf((FILE*)handle, fmt, va); } /********************************************************************** @@ -248,8 +83,8 @@ void con_color(int state) { } void con_init() { - console.handle_err = stderr; - console.handle_out = stdout; + console.handle_err = (fs_file_t*)stderr; + console.handle_out = (fs_file_t*)stdout; con_enablecolor(); } @@ -271,16 +106,16 @@ void con_reset() { int con_change(const char *out, const char *err) { con_close(); - if (!out) out = (const char *)((!console.handle_out) ? stdout : console.handle_out); - if (!err) err = (const char *)((!console.handle_err) ? stderr : console.handle_err); + if (!out) out = (const char *)((!console.handle_out) ? (fs_file_t*)stdout : console.handle_out); + if (!err) err = (const char *)((!console.handle_err) ? (fs_file_t*)stderr : console.handle_err); if (GMQCC_IS_DEFINE(out)) { - console.handle_out = GMQCC_IS_STDOUT(out) ? stdout : stderr; + console.handle_out = (fs_file_t*)(GMQCC_IS_STDOUT(out) ? stdout : stderr); con_enablecolor(); } else if (!(console.handle_out = fs_file_open(out, "w"))) return 0; if (GMQCC_IS_DEFINE(err)) { - console.handle_err = GMQCC_IS_STDOUT(err) ? stdout : stderr; + console.handle_err = (fs_file_t*)(GMQCC_IS_STDOUT(err) ? stdout : stderr); con_enablecolor(); } else if (!(console.handle_err = fs_file_open(err, "w"))) return 0; @@ -291,11 +126,11 @@ int con_change(const char *out, const char *err) { * Defaultizer because stdio.h shouldn't be used anywhere except here * and inside file.c To prevent mis-match of wrapper-interfaces. */ -FILE *con_default_out() { - return (console.handle_out = stdout); +fs_file_t *con_default_out() { + return (fs_file_t*)(console.handle_out = (fs_file_t*)stdout); } -FILE *con_default_err() { - return (console.handle_err = stderr); +fs_file_t *con_default_err() { + return (fs_file_t*)(console.handle_err = (fs_file_t*)stderr); } int con_verr(const char *fmt, va_list va) { @@ -339,10 +174,10 @@ static void con_vprintmsg_c(int level, const char *name, size_t line, size_t col CON_RED }; - int err = !!(level == LVL_ERROR); - int color = (err) ? console.color_err : console.color_out; - int (*print) (const char *, ...) = (err) ? &con_err : &con_out; - int (*vprint)(const char *, va_list) = (err) ? &con_verr : &con_vout; + int err = !!(level == LVL_ERROR); + int color = (err) ? console.color_err : console.color_out; + int (*print) (const char *, ...) = (err) ? &con_err : &con_out; + int (*vprint)(const char *, va_list) = (err) ? &con_verr : &con_vout; if (color) print("\033[0;%dm%s:%d:%d: \033[0;%dm%s: \033[0m", CON_CYAN, name, (int)line, (int)column, sel[level], msgtype); diff --git a/exec.c b/exec.c index c4de8ad..57b4731 100644 --- a/exec.c +++ b/exec.c @@ -23,10 +23,9 @@ */ #ifndef QCVM_LOOP #include -#include -#include -#include #include +#include +#include #include "gmqcc.h" @@ -54,9 +53,9 @@ static void qcvmerror(qc_program_t *prog, const char *fmt, ...) qc_program_t* prog_load(const char *filename, bool skipversion) { - qc_program_t *prog; prog_header_t header; - FILE *file = fs_file_open(filename, "rb"); + qc_program_t *prog; + fs_file_t *file = fs_file_open(filename, "rb"); if (!file) return NULL; @@ -91,11 +90,11 @@ qc_program_t* prog_load(const char *filename, bool skipversion) } #define read_data(hdrvar, progvar, reserved) \ - if (fs_file_seek(file, header.hdrvar.offset, SEEK_SET) != 0) { \ + if (fs_file_seek(file, header.hdrvar.offset, SEEK_SET) != 0) { \ loaderror("seek failed"); \ goto error; \ } \ - if (fs_file_read ( \ + if (fs_file_read ( \ vec_add(prog->progvar, header.hdrvar.length + reserved), \ sizeof(*prog->progvar), \ header.hdrvar.length, \ @@ -352,7 +351,7 @@ static void trace_print_global(qc_program_t *prog, unsigned int glob, int vtype) done: if (len < (int)sizeof(spaces)-1) { spaces[sizeof(spaces)-1-len] = 0; - fs_file_puts(stdout, spaces); + fs_file_puts((fs_file_t*)stdout, spaces); spaces[sizeof(spaces)-1-len] = ' '; } } @@ -877,17 +876,10 @@ static void prog_main_setparams(qc_program_t *prog) { arg->vector[2] = 0; switch (main_params[i].vtype) { case TYPE_VECTOR: -#ifdef _MSC_VER - (void)sscanf_s(main_params[i].value, " %f %f %f ", - &arg->vector[0], - &arg->vector[1], - &arg->vector[2]); -#else - (void)sscanf(main_params[i].value, " %f %f %f ", - &arg->vector[0], - &arg->vector[1], - &arg->vector[2]); -#endif + (void)util_sscanf(main_params[i].value, " %f %f %f ", + &arg->vector[0], + &arg->vector[1], + &arg->vector[2]); break; case TYPE_FLOAT: arg->_float = atof(main_params[i].value); diff --git a/fs.c b/fs.c index 501041f..55e4edc 100644 --- a/fs.c +++ b/fs.c @@ -20,162 +20,76 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ +#define GMQCC_PLATFORM_HEADER #include "gmqcc.h" +#include "platform.h" -/* - * This is essentially a "wrapper" interface around standard C's IO - * library. There is two reason we implement this, 1) visual studio - * hearts for "secure" varations, as part of it's "Security Enhancements - * in the CRT" (http://msdn.microsoft.com/en-us/library/8ef0s5kh.aspx). - * 2) But one of the greater reasons is for the possibility of large file - * support in the future. I don't expect to reach the 2GB limit any - * time soon (mainly because that would be insane). But when it comes - * to adding support for some other larger IO tasks (in the test-suite, - * or even the QCVM we'll need it). There is also a third possibility of - * building .dat files directly from zip files (which would be very cool - * at least I think so). - */ -#ifdef _MSC_VER -#include /* _CrtSetReportMode, _CRT_ASSERT */ -/* {{{ */ - /* - * Visual Studio has security CRT features which I actually want to support - * if we ever port to Windows 8, and want GMQCC to be API safe. - * - * We handle them here, for all file-operations. - */ +fs_file_t *fs_file_open(const char *filename, const char *mode) { + return (fs_file_t*)platform_fopen(filename, mode); +} - static void file_exception ( - const wchar_t *expression, - const wchar_t *function, - const wchar_t *file, - unsigned int line, - uintptr_t reserved - ) { - wprintf(L"Invalid parameter dectected %s:%d %s [%s]\n", file, line, function, expression); - wprintf(L"Aborting ...\n"); - exit(EXIT_FAILURE); - } +size_t fs_file_read(void *buffer, size_t size, size_t count, fs_file_t *fp) { + return platform_fread(buffer, size, count, (FILE*)fp); +} - static void file_init() { - static bool init = false; +int fs_file_printf(fs_file_t *fp, const char *format, ...) { + int rt; + va_list va; + va_start(va, format); + rt = platform_vfprintf((FILE*)fp, format, va); + va_end (va); - if (init) - return; + return rt; +} - _set_invalid_parameter_handler(&file_exception); - - /* - * Turnoff the message box for CRT asserations otherwise - * we don't get the error reported to the console as we should - * otherwise get. - */ - _CrtSetReportMode(_CRT_ASSERT, 0); - init = !init; - } - - - FILE *fs_file_open(const char *filename, const char *mode) { - FILE *handle = NULL; - file_init(); - - return (fopen_s(&handle, filename, mode) != 0) ? NULL : handle; - } - - size_t fs_file_read(void *buffer, size_t size, size_t count, FILE *fp) { - file_init(); - return fread_s(buffer, size*count, size, count, fp); - } - - int fs_file_printf(FILE *fp, const char *format, ...) { - int rt; - va_list va; - va_start(va, format); - - file_init(); - rt = vfprintf_s(fp, format, va); - va_end (va); - - return rt; - } - -/* }}} */ -#else -/* {{{ */ - /* - * All other compilers/platforms that don't restrict insane policies on - * IO for no aparent reason. - */ - FILE *fs_file_open(const char *filename, const char *mode) { - return fopen(filename, mode); - } - - size_t fs_file_read(void *buffer, size_t size, size_t count, FILE *fp) { - return fread(buffer, size, count, fp); - } - - int fs_file_printf(FILE *fp, const char *format, ...) { - int rt; - va_list va; - va_start(va, format); - rt = vfprintf(fp, format, va); - va_end (va); - - return rt; - } - -/* }}} */ -#endif - -/* - * These are implemented as just generic wrappers to keep consistency in - * the API. Not as macros though - */ -void fs_file_close(FILE *fp) { - /* Invokes file_exception on windows if fp is null */ - fclose (fp); +void fs_file_close(fs_file_t *fp) { + platform_fclose((FILE*)fp); } size_t fs_file_write ( const void *buffer, size_t size, size_t count, - FILE *fp + fs_file_t *fp ) { - /* Invokes file_exception on windows if fp is null */ - return fwrite(buffer, size, count, fp); + return platform_fwrite(buffer, size, count, (FILE*)fp); } -int fs_file_error(FILE *fp) { - /* Invokes file_exception on windows if fp is null */ - return ferror(fp); +int fs_file_error(fs_file_t *fp) { + return platform_ferror((FILE*)fp); } -int fs_file_getc(FILE *fp) { - /* Invokes file_exception on windows if fp is null */ - return fgetc(fp); +int fs_file_getc(fs_file_t *fp) { + int get = platform_fgetc((FILE*)fp); + return (get == EOF) ? FS_FILE_EOF : get; } -int fs_file_puts(FILE *fp, const char *str) { - /* Invokes file_exception on windows if fp is null */ - return fputs(str, fp); +int fs_file_puts(fs_file_t *fp, const char *str) { + return platform_fputs(str, (FILE*)fp); } -int fs_file_seek(FILE *fp, long int off, int whence) { - /* Invokes file_exception on windows if fp is null */ - return fseek(fp, off, whence); +int fs_file_seek(fs_file_t *fp, long int off, int whence) { + switch(whence) { + case FS_FILE_SEEK_CUR: whence = SEEK_CUR; break; + case FS_FILE_SEEK_SET: whence = SEEK_SET; break; + case FS_FILE_SEEK_END: whence = SEEK_END; break; + } + return platform_fseek((FILE*)fp, off, whence); } -long int fs_file_tell(FILE *fp) { - /* Invokes file_exception on windows if fp is null */ - return ftell(fp); +long int fs_file_tell(fs_file_t *fp) { + return platform_ftell((FILE*)fp); +} + +int fs_file_flush(fs_file_t *fp) { + return platform_fflush((FILE*)fp); } /* * Implements libc getline for systems that don't have it, which is * assmed all. This works the same as getline(). */ -int fs_file_getline(char **lineptr, size_t *n, FILE *stream) { +int fs_file_getline(char **lineptr, size_t *n, fs_file_t *stream) { int chr; int ret; char *pos; @@ -201,7 +115,7 @@ int fs_file_getline(char **lineptr, size_t *n, FILE *stream) { pos = *n - chr + *lineptr; } - if (ferror(stream)) + if (fs_file_error(stream)) return -1; if (c == EOF) { if (pos == *lineptr) @@ -219,94 +133,18 @@ int fs_file_getline(char **lineptr, size_t *n, FILE *stream) { return (ret = pos - *lineptr); } -/* - * Now we implement some directory functionality. Windows lacks dirent.h - * this is such a pisss off, we implement it here. - */ -#if defined(_WIN32) && !defined(__MINGW32__) - DIR *fs_dir_open(const char *name) { - DIR *dir = (DIR*)mem_a(sizeof(DIR) + strlen(name)); - if (!dir) - return NULL; - - util_strncpy(dir->dd_name, name, strlen(name)); - return dir; - } - - int fs_dir_close(DIR *dir) { - FindClose((HANDLE)dir->dd_handle); - mem_d ((void*)dir); - return 0; - } - - struct dirent *fs_dir_read(DIR *dir) { - WIN32_FIND_DATA info; - struct dirent *data; - int rets; - - if (!dir->dd_handle) { - char *dirname; - if (*dir->dd_name) { - size_t n = strlen(dir->dd_name); - if ((dirname = (char*)mem_a(n + 5) /* 4 + 1 */)) { - util_strncpy(dirname, dir->dd_name, n); - util_strncpy(dirname + n, "\\*.*", 4); /* 4 + 1 */ - } - } else { - if (!(dirname = util_strdup("\\*.*"))) - return NULL; - } - - dir->dd_handle = (long)FindFirstFile(dirname, &info); - mem_d(dirname); - rets = !(!dir->dd_handle); - } else if (dir->dd_handle != -11) { - rets = FindNextFile ((HANDLE)dir->dd_handle, &info); - } else { - rets = 0; - } - - if (!rets) - return NULL; - - if ((data = (struct dirent*)mem_a(sizeof(struct dirent)))) { - util_strncpy(data->d_name, info.cFileName, FILENAME_MAX - 1); - data->d_name[FILENAME_MAX - 1] = '\0'; /* terminate */ - data->d_namlen = strlen(data->d_name); - } - return data; - } - - int fs_dir_change(const char *path) { - return !SetCurrentDirectory(path); - } - - int fs_dir_make(const char *path) { - return !CreateDirectory(path, NULL); - } -#else -# if !defined(__MINGW32__) -# include /* mkdir */ - - int fs_dir_make(const char *path) { - return mkdir(path, 0700); - } -# else - int fs_dir_make(const char *path) { - return mkdir(path); - } -# endif /*! !defined(__MINGW32__) */ - -DIR *fs_dir_open(const char *name) { - return opendir(name); +int fs_dir_make(const char *path) { + return platform_mkdir(path, 0700); } -int fs_dir_close(DIR *dir) { - return closedir(dir); +fs_dir_t *fs_dir_open(const char *name) { + return (fs_dir_t*)platform_opendir(name); } -struct dirent *fs_dir_read(DIR *dir) { - return readdir(dir); +int fs_dir_close(fs_dir_t *dir) { + return platform_closedir((DIR*)dir); } -#endif /*! defined(_WIN32) && !defined(__MINGW32__) */ +fs_dirent_t *fs_dir_read(fs_dir_t *dir) { + return (fs_dirent_t*)platform_readdir((DIR*)dir); +} diff --git a/ftepp.c b/ftepp.c index 050d6d8..36bb1a7 100644 --- a/ftepp.c +++ b/ftepp.c @@ -21,15 +21,16 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ -#include #include #include +#include #include #include "gmqcc.h" #include "lexer.h" #define HT_MACROS 1024 + typedef struct { bool on; bool was_on; @@ -85,22 +86,14 @@ static uint32_t ftepp_predef_randval = 0; /* __DATE__ */ static char *ftepp_predef_date(lex_file *context) { - struct tm *itime = NULL; - time_t rtime; - char *value = (char*)mem_a(82); - /* 82 is enough for strftime but we also have " " in our string */ + const struct tm *itime = NULL; + char *value = (char*)mem_a(82); + time_t rtime; (void)context; - /* get time */ time (&rtime); - -#ifdef _MSC_VER - localtime_s(itime, &rtime); -#else - itime = localtime(&rtime); -#endif - + itime = util_localtime(&rtime); strftime(value, 82, "\"%b %d %Y\"", itime); return value; @@ -108,22 +101,14 @@ static char *ftepp_predef_date(lex_file *context) { /* __TIME__ */ static char *ftepp_predef_time(lex_file *context) { - struct tm *itime = NULL; - time_t rtime; - char *value = (char*)mem_a(82); - /* 82 is enough for strftime but we also have " " in our string */ + const struct tm *itime = NULL; + char *value = (char*)mem_a(82); + time_t rtime; (void)context; - /* get time */ time (&rtime); - -#ifdef _MSC_VER - localtime_s(itime, &rtime); -#else - itime = localtime(&rtime); -#endif - + itime = util_localtime(&rtime); strftime(value, 82, "\"%X\"", itime); return value; @@ -180,27 +165,14 @@ static char *ftepp_predef_randomlast(lex_file *context) { /* __TIMESTAMP__ */ static char *ftepp_predef_timestamp(lex_file *context) { struct stat finfo; - char *find; + const char *find; char *value; size_t size; -#ifdef _MSC_VER - char buffer[64]; -#endif + if (stat(context->name, &finfo)) return util_strdup("\"\""); - /* - * ctime and its fucking annoying newline char, no worries, we're - * professionals here. - */ - -#ifndef _MSC_VER - find = ctime(&finfo.st_mtime); -#else - ctime_s(buffer, sizeof(buffer), &finfo.st_mtime); - find = buffer; -#endif - + find = util_ctime(&finfo.st_mtime); value = (char*)mem_a(strlen(find) + 1); memcpy(&value[1], find, (size = strlen(find)) - 1); @@ -1303,7 +1275,7 @@ static void unescape(const char *str, char *out) { static char *ftepp_include_find_path(const char *file, const char *pathfile) { - FILE *fp; + fs_file_t *fp; char *filename = NULL; const char *last_slash; size_t len; diff --git a/gmqcc.h b/gmqcc.h index cb70d66..f396b28 100644 --- a/gmqcc.h +++ b/gmqcc.h @@ -24,7 +24,8 @@ #ifndef GMQCC_HDR #define GMQCC_HDR #include -#include /* TODO: remove this */ +#include +#include /* TODO: remove?*/ /* * Disable some over protective warnings in visual studio because fixing them is a waste @@ -252,46 +253,7 @@ GMQCC_IND_STRING(GMQCC_VERSION_PATCH) \ # endif #endif /*! !defined (PLATFORM_BYTE_ORDER) */ -/* - * On windows systems where we're not compiling with MING32 we need a - * little extra help on dependinces for implementing our own dirent.h - * in fs.c. - */ -#if defined(_WIN32) && !defined(__MINGW32__) -# define _WIN32_LEAN_AND_MEAN -# include -# include -# include - - struct dirent { - long d_ino; - unsigned short d_reclen; - unsigned short d_namlen; - char d_name[FILENAME_MAX]; - }; - - typedef struct { - struct _finddata_t dd_dta; - struct dirent dd_dir; - long dd_handle; - int dd_stat; - char dd_name[1]; - } DIR; - /* - * Visual studio also lacks S_ISDIR for sys/stat.h, so we emulate this as well - * which is not hard at all. - */ -# ifdef S_ISDIR -# undef S_ISDIR -# endif /*! S_ISDIR */ -# define S_ISDIR(X) ((X)&_S_IFDIR) -#else -# include -#endif /*! _WIN32 && !defined(__MINGW32__) */ - -/*===================================================================*/ -/*=========================== stat.c ================================*/ -/*===================================================================*/ +/* stat.c */ void stat_info (void); char *stat_mem_strdup (const char *, size_t, const char *, bool); void *stat_mem_reallocate(void *, size_t, size_t, const char *); @@ -307,9 +269,7 @@ void *stat_mem_allocate (size_t, size_t, const char *); #define util_strdup(SRC) stat_mem_strdup((char*)(SRC), __LINE__, __FILE__, false) #define util_strdupe(SRC) stat_mem_strdup((char*)(SRC), __LINE__, __FILE__, true) -/*===================================================================*/ -/*=========================== util.c ================================*/ -/*===================================================================*/ +/* util.c */ /* * Microsoft implements against the spec versions of ctype.h. Which @@ -329,10 +289,9 @@ void *stat_mem_allocate (size_t, size_t, const char *); #define util_isprint(a) (((unsigned)(a)-0x20) < 0x5F) #define util_isspace(a) (((a) >= 9 && (a) <= 13) || (a) == ' ') -bool util_filexists (const char *); bool util_strupper (const char *); bool util_strdigit (const char *); -void util_endianswap (void *, size_t, unsigned int); +void util_endianswap (void *, size_t, unsigned int); size_t util_strtocmd (const char *, char *, size_t); size_t util_strtononcmd (const char *, char *, size_t); @@ -343,18 +302,18 @@ uint16_t util_crc16(uint16_t crc, const char *data, size_t len); void util_seed(uint32_t); uint32_t util_rand(void); -/* - * String functions (formatting, copying, concatenating, errors). These are wrapped - * to use the MSVC _safe_ versions when using MSVC, plus some implementations of - * these are non-conformant or don't exist such as asprintf and snprintf, which are - * not supported in C90, but do exist in C99. - */ -int util_vasprintf(char **ret, const char *fmt, va_list); -int util_asprintf (char **ret, const char *fmt, ...); -int util_snprintf (char *src, size_t bytes, const char *format, ...); -char *util_strcat (char *dest, const char *src); -char *util_strncpy (char *dest, const char *src, size_t num); -const char *util_strerror (int num); +int util_asprintf (char **ret, const char *fmt, ...); +int util_sscanf (const char *str, const char *format, ...); +char *util_strncpy (char *dest, const char *src, size_t n); +char *util_strncat (char *dest, const char *src, size_t n); +char *util_strcat (char *dest, const char *src); +const char *util_strerror(int err); + +const struct tm *util_localtime(const time_t *timer); +const char *util_ctime (const time_t *timer); + +typedef struct fs_file_s fs_file_t; +int util_isatty (fs_file_t *); /* * A flexible vector implementation: all vector pointers contain some @@ -391,13 +350,6 @@ void _util_vec_grow(void **a, size_t i, size_t s); #define vec_append(A,N,S) ((void)(memcpy(vec_add((A), (N)), (S), (N) * sizeof(*(S))))) #define vec_remove(A,I,N) ((void)(memmove((A)+(I),(A)+((I)+(N)),sizeof(*(A))*(vec_meta(A)->used-(I)-(N))),vec_meta(A)->used-=(N))) -typedef struct correct_trie_s { - void *value; - struct correct_trie_s *entries; -} correct_trie_t; - -correct_trie_t* correct_trie_new(void); - typedef struct hash_table_s { size_t size; struct hash_node_t **table; @@ -445,34 +397,48 @@ void util_htrm (hash_table_t *ht, const char *key, void (*cb)(void*)); void *util_htget (hash_table_t *ht, const char *key); void *util_htgeth(hash_table_t *ht, const char *key, size_t hash); -/*===================================================================*/ -/*============================ file.c ===============================*/ -/*===================================================================*/ -/* file handling */ -void fs_file_close (FILE *); -int fs_file_error (FILE *); -int fs_file_getc (FILE *); -int fs_file_printf (FILE *, const char *, ...); -int fs_file_puts (FILE *, const char *); -int fs_file_seek (FILE *, long int, int); -long int fs_file_tell (FILE *); +int util_snprintf(char *str, size_t, const char *fmt, ...); -size_t fs_file_read (void *, size_t, size_t, FILE *); -size_t fs_file_write (const void *, size_t, size_t, FILE *); -FILE *fs_file_open (const char *, const char *); -int fs_file_getline(char **, size_t *, FILE *); +/* fs.c */ +#define FS_FILE_SEEK_SET 0 +#define FS_FILE_SEEK_CUR 1 +#define FS_FILE_SEEK_END 2 +#define FS_FILE_EOF -1 + +typedef struct fs_dir_s fs_dir_t; +/*typedef struct fs_file_s fs_file_t;*/ +typedef struct dirent fs_dirent_t; + +void fs_file_close (fs_file_t *); +int fs_file_error (fs_file_t *); +int fs_file_getc (fs_file_t *); +int fs_file_printf (fs_file_t *, const char *, ...); +int fs_file_puts (fs_file_t *, const char *); +int fs_file_seek (fs_file_t *, long int, int); +long fs_file_tell (fs_file_t *); +int fs_file_flush (fs_file_t *); + +size_t fs_file_read (void *, size_t, size_t, fs_file_t *); +size_t fs_file_write (const void *, size_t, size_t, fs_file_t *); + +fs_file_t *fs_file_open (const char *, const char *); +int fs_file_getline(char **, size_t *, fs_file_t *); -/* directory handling */ int fs_dir_make (const char *); -DIR *fs_dir_open (const char *); -int fs_dir_close (DIR *); -struct dirent *fs_dir_read (DIR *); +fs_dir_t *fs_dir_open (const char *); +int fs_dir_close (fs_dir_t *); +fs_dirent_t *fs_dir_read (fs_dir_t *); -/*===================================================================*/ -/*=========================== correct.c =============================*/ -/*===================================================================*/ +/* correct.c */ +typedef struct correct_trie_s { + void *value; + struct correct_trie_s *entries; +} correct_trie_t; + +correct_trie_t* correct_trie_new(void); + typedef struct { char ***edits; size_t **lens; @@ -484,9 +450,8 @@ char *correct_str (correction_t *, correct_trie_t*, const char *); void correct_init(correction_t *); void correct_free(correction_t *); -/*===================================================================*/ -/*=========================== code.c ================================*/ -/*===================================================================*/ + +/* code.c */ /* Note: if you change the order, fix type_sizeof in ir.c */ enum { @@ -783,9 +748,7 @@ void code_push_statement(code_t *, prog_section_statement_t *stmt, lex_ctx_ void code_pop_statement (code_t *); -/*===================================================================*/ -/*============================ con.c ================================*/ -/*===================================================================*/ +/* conout.c */ enum { CON_BLACK = 30, CON_RED, @@ -804,8 +767,8 @@ enum { LVL_ERROR }; -FILE *con_default_out(void); -FILE *con_default_err(void); +fs_file_t *con_default_out(void); +fs_file_t *con_default_err(void); void con_vprintmsg (int level, const char *name, size_t line, size_t column, const char *msgtype, const char *msg, va_list ap); void con_printmsg (int level, const char *name, size_t line, size_t column, const char *msgtype, const char *msg, ...); @@ -833,10 +796,8 @@ bool GMQCC_WARN compile_warning (lex_ctx_t ctx, int warntype, const char *fmt, . bool GMQCC_WARN vcompile_warning(lex_ctx_t ctx, int warntype, const char *fmt, va_list ap); void compile_show_werrors(void); -/*===================================================================*/ -/*============================= ir.c ================================*/ -/*===================================================================*/ - +/* ir.c */ +/* TODO: cleanup */ enum store_types { store_global, store_local, /* local, assignable for now, should get promoted later */ @@ -849,9 +810,7 @@ typedef struct { qcfloat_t x, y, z; } vec3_t; -/*===================================================================*/ -/*============================= exec.c ==============================*/ -/*===================================================================*/ +/* exec.c */ /* TODO: cleanup */ /* @@ -944,9 +903,7 @@ qcany_t* prog_getedict (qc_program_t *prog, qcint_t e); qcint_t prog_tempstring(qc_program_t *prog, const char *_str); -/*===================================================================*/ -/*===================== parser.c commandline ========================*/ -/*===================================================================*/ +/* parser.c */ struct parser_s; struct parser_s *parser_create (void); bool parser_compile_file (struct parser_s *parser, const char *); @@ -954,9 +911,7 @@ bool parser_compile_string(struct parser_s *parser, const char *, co bool parser_finish (struct parser_s *parser, const char *); void parser_cleanup (struct parser_s *parser); -/*===================================================================*/ -/*====================== ftepp.c commandline ========================*/ -/*===================================================================*/ +/* ftepp.c */ struct ftepp_s; struct ftepp_s *ftepp_create (void); bool ftepp_preprocess_file (struct ftepp_s *ftepp, const char *filename); @@ -967,9 +922,7 @@ void ftepp_flush (struct ftepp_s *ftepp); void ftepp_add_define (struct ftepp_s *ftepp, const char *source, const char *name); void ftepp_add_macro (struct ftepp_s *ftepp, const char *name, const char *value); -/*===================================================================*/ -/*======================= main.c commandline ========================*/ -/*===================================================================*/ +/* main.c */ #if 1 /* Helpers to allow for a whole lot of flags. Otherwise we'd limit @@ -987,16 +940,12 @@ typedef uint32_t longbit; #define LONGBIT_SET(B, I) ((B) = (I)) #endif -/*===================================================================*/ -/*============================= utf8.c ==============================*/ -/*===================================================================*/ +/* utf.8 */ typedef long utf8ch_t; int utf8_from(char *, utf8ch_t); int utf8_to(utf8ch_t *, const unsigned char *, size_t); -/*===================================================================*/ -/*============================= opts.c ==============================*/ -/*===================================================================*/ +/* opts.c */ typedef struct { const char *name; longbit bit; diff --git a/include.mk b/include.mk index 20ffdd4..f95ff7c 100644 --- a/include.mk +++ b/include.mk @@ -13,11 +13,14 @@ CC ?= clang LDFLAGS += LIBS += -lm +#common objects +COMMON = ansi.o util.o stat.o fs.o opts.o conout.o + #objects -OBJ_C = main.o lexer.o parser.o fs.o stat.o util.o code.o ast.o ir.o conout.o ftepp.o opts.o utf8.o correct.o fold.o intrin.o -OBJ_P = util.o fs.o conout.o opts.o pak.o stat.o -OBJ_T = test.o util.o opts.o conout.o fs.o stat.o -OBJ_X = exec-standalone.o util.o opts.o conout.o fs.o stat.o +OBJ_C = $(COMMON) main.o lexer.o parser.o code.o ast.o ir.o ftepp.o utf8.o correct.o fold.o intrin.o +OBJ_P = $(COMMON) pak.o +OBJ_T = $(COMMON) test.o +OBJ_X = $(COMMON) exec-standalone.o #gource flags GOURCEFLAGS = \ diff --git a/ir.c b/ir.c index f0274e1..9967d7d 100644 --- a/ir.c +++ b/ir.c @@ -3977,10 +3977,6 @@ bool ir_builder_generate(ir_builder *self, const char *filename) #define IND_BUFSZ 1024 -#ifdef _MSC_VER -# define strncat(dst, src, sz) strncat_s(dst, sz, src, _TRUNCATE) -#endif - static const char *qc_opname(int op) { if (op < 0) return ""; @@ -4039,7 +4035,7 @@ void ir_function_dump(ir_function *f, char *ind, return; } oprintf("%sfunction %s\n", ind, f->name); - strncat(ind, "\t", IND_BUFSZ-1); + util_strncat(ind, "\t", IND_BUFSZ-1); if (vec_size(f->locals)) { oprintf("%s%i locals:\n", ind, (int)vec_size(f->locals)); @@ -4135,7 +4131,7 @@ void ir_block_dump(ir_block* b, char *ind, { size_t i; oprintf("%s:%s\n", ind, b->label); - strncat(ind, "\t", IND_BUFSZ-1); + util_strncat(ind, "\t", IND_BUFSZ-1); if (b->instr && b->instr[0]) oprintf("%s (%i) [entry]\n", ind, (int)(b->instr[0]->eid-1)); @@ -4169,7 +4165,7 @@ void ir_instr_dump(ir_instr *in, char *ind, return; } - strncat(ind, "\t", IND_BUFSZ-1); + util_strncat(ind, "\t", IND_BUFSZ-1); if (in->_ops[0] && (in->_ops[1] || in->_ops[2])) { ir_value_dump(in->_ops[0], oprintf); diff --git a/lexer.c b/lexer.c index 5882bdb..6c8ccd9 100644 --- a/lexer.c +++ b/lexer.c @@ -25,6 +25,7 @@ #include "gmqcc.h" #include "lexer.h" + /* * List of Keywords */ @@ -179,8 +180,8 @@ static void lex_token_new(lex_file *lex) lex_file* lex_open(const char *file) { - lex_file *lex; - FILE *in = fs_file_open(file, "rb"); + lex_file *lex; + fs_file_t *in = fs_file_open(file, "rb"); if (!in) { lexerror(NULL, "open failed: '%s'\n", file); @@ -273,11 +274,11 @@ static int lex_fgetc(lex_file *lex) } if (lex->open_string) { if (lex->open_string_pos >= lex->open_string_length) - return EOF; + return FS_FILE_EOF; lex->column++; return lex->open_string[lex->open_string_pos++]; } - return EOF; + return FS_FILE_EOF; } /* Get or put-back data @@ -493,7 +494,7 @@ static bool lex_try_pragma(lex_file *lex) goto unroll; lex->line = line; - while (ch != '\n' && ch != EOF) + while (ch != '\n' && ch != FS_FILE_EOF) ch = lex_getch(lex); vec_free(command); vec_free(param); @@ -573,7 +574,7 @@ static int lex_skipwhite(lex_file *lex, bool hadwhite) do { ch = lex_getch(lex); - while (ch != EOF && util_isspace(ch)) { + while (ch != FS_FILE_EOF && util_isspace(ch)) { if (ch == '\n') { if (lex_try_pragma(lex)) continue; @@ -613,7 +614,7 @@ static int lex_skipwhite(lex_file *lex, bool hadwhite) lex_tokench(lex, ' '); } - while (ch != EOF && ch != '\n') { + while (ch != FS_FILE_EOF && ch != '\n') { if (lex->flags.preprocessing) lex_tokench(lex, ' '); /* ch); */ ch = lex_getch(lex); @@ -638,7 +639,7 @@ static int lex_skipwhite(lex_file *lex, bool hadwhite) lex_tokench(lex, ' '); } - while (ch != EOF) + while (ch != FS_FILE_EOF) { ch = lex_getch(lex); if (ch == '*') { @@ -671,7 +672,7 @@ static int lex_skipwhite(lex_file *lex, bool hadwhite) ch = '/'; break; } - } while (ch != EOF && util_isspace(ch)); + } while (ch != FS_FILE_EOF && util_isspace(ch)); if (haswhite) { lex_endtoken(lex); @@ -687,7 +688,7 @@ static bool GMQCC_WARN lex_finish_ident(lex_file *lex) int ch; ch = lex_getch(lex); - while (ch != EOF && isident(ch)) + while (ch != FS_FILE_EOF && isident(ch)) { lex_tokench(lex, ch); ch = lex_getch(lex); @@ -707,7 +708,7 @@ static int lex_parse_frame(lex_file *lex) lex_token_new(lex); ch = lex_getch(lex); - while (ch != EOF && ch != '\n' && util_isspace(ch)) + while (ch != FS_FILE_EOF && ch != '\n' && util_isspace(ch)) ch = lex_getch(lex); if (ch == '\n') @@ -768,7 +769,7 @@ static int GMQCC_WARN lex_finish_string(lex_file *lex, int quote) char u8buf[8]; /* way more than enough */ int u8len, uc; - while (ch != EOF) + while (ch != FS_FILE_EOF) { ch = lex_getch(lex); if (ch == quote) @@ -777,18 +778,18 @@ static int GMQCC_WARN lex_finish_string(lex_file *lex, int quote) if (lex->flags.preprocessing && ch == '\\') { lex_tokench(lex, ch); ch = lex_getch(lex); - if (ch == EOF) { + if (ch == FS_FILE_EOF) { lexerror(lex, "unexpected end of file"); - lex_ungetch(lex, EOF); /* next token to be TOKEN_EOF */ + lex_ungetch(lex, FS_FILE_EOF); /* next token to be TOKEN_EOF */ return (lex->tok.ttype = TOKEN_ERROR); } lex_tokench(lex, ch); } else if (ch == '\\') { ch = lex_getch(lex); - if (ch == EOF) { + if (ch == FS_FILE_EOF) { lexerror(lex, "unexpected end of file"); - lex_ungetch(lex, EOF); /* next token to be TOKEN_EOF */ + lex_ungetch(lex, FS_FILE_EOF); /* next token to be TOKEN_EOF */ return (lex->tok.ttype = TOKEN_ERROR); } @@ -911,7 +912,7 @@ static int GMQCC_WARN lex_finish_string(lex_file *lex, int quote) lex_tokench(lex, ch); } lexerror(lex, "unexpected end of file within string constant"); - lex_ungetch(lex, EOF); /* next token to be TOKEN_EOF */ + lex_ungetch(lex, FS_FILE_EOF); /* next token to be TOKEN_EOF */ return (lex->tok.ttype = TOKEN_ERROR); } @@ -1032,7 +1033,7 @@ int lex_do(lex_file *lex) if (lex->eof) return (lex->tok.ttype = TOKEN_FATAL); - if (ch == EOF) { + if (ch == FS_FILE_EOF) { lex->eof = true; return (lex->tok.ttype = TOKEN_EOF); } @@ -1069,7 +1070,7 @@ int lex_do(lex_file *lex) if (!strcmp(v, "framevalue")) { ch = lex_getch(lex); - while (ch != EOF && util_isspace(ch) && ch != '\n') + while (ch != FS_FILE_EOF && util_isspace(ch) && ch != '\n') ch = lex_getch(lex); if (!util_isdigit(ch)) { @@ -1149,7 +1150,7 @@ int lex_do(lex_file *lex) vec_free(lex->frames); /* skip line (fteqcc does it too) */ ch = lex_getch(lex); - while (ch != EOF && ch != '\n') + while (ch != FS_FILE_EOF && ch != '\n') ch = lex_getch(lex); return lex_do(lex); } @@ -1163,7 +1164,7 @@ int lex_do(lex_file *lex) { /* skip line */ ch = lex_getch(lex); - while (ch != EOF && ch != '\n') + while (ch != FS_FILE_EOF && ch != '\n') ch = lex_getch(lex); return lex_do(lex); } @@ -1473,14 +1474,10 @@ int lex_do(lex_file *lex) lex_endtoken(lex); lex->tok.ttype = TOKEN_CHARCONST; - /* It's a vector if we can successfully scan 3 floats */ -#ifdef _MSC_VER - if (sscanf_s(lex->tok.value, " %f %f %f ", + + /* It's a vector if we can successfully scan 3 floats */ + if (util_sscanf(lex->tok.value, " %f %f %f ", &lex->tok.constval.v.x, &lex->tok.constval.v.y, &lex->tok.constval.v.z) == 3) -#else - if (sscanf(lex->tok.value, " %f %f %f ", - &lex->tok.constval.v.x, &lex->tok.constval.v.y, &lex->tok.constval.v.z) == 3) -#endif { lex->tok.ttype = TOKEN_VECTORCONST; diff --git a/lexer.h b/lexer.h index 24e29dd..3184fcc 100644 --- a/lexer.h +++ b/lexer.h @@ -105,7 +105,7 @@ typedef struct { } frame_macro; typedef struct lex_file_s { - FILE *file; + fs_file_t *file; const char *open_string; size_t open_string_length; size_t open_string_pos; diff --git a/main.c b/main.c index 9a76706..74d927d 100644 --- a/main.c +++ b/main.c @@ -531,7 +531,7 @@ static bool options_parse(int argc, char **argv) { } /* returns the line number, or -1 on error */ -static bool progs_nextline(char **out, size_t *alen,FILE *src) { +static bool progs_nextline(char **out, size_t *alen, fs_file_t *src) { int len; char *line; char *start; @@ -562,7 +562,7 @@ int main(int argc, char **argv) { bool opts_output_free = false; bool operators_free = false; bool progs_src = false; - FILE *outfile = NULL; + fs_file_t *outfile = NULL; struct parser_s *parser = NULL; struct ftepp_s *ftepp = NULL; @@ -667,10 +667,10 @@ int main(int argc, char **argv) { } if (!vec_size(items)) { - FILE *src; - char *line = NULL; - size_t linelen = 0; - bool hasline = false; + fs_file_t *src; + char *line = NULL; + size_t linelen = 0; + bool hasline = false; progs_src = true; diff --git a/msvc.c b/msvc.c new file mode 100644 index 0000000..d1273c6 --- /dev/null +++ b/msvc.c @@ -0,0 +1,235 @@ +/* + * Copyright (C) 2012, 2013 + * Dale Weiler + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#define GMQCC_PLATFORM_HEADER +#include +#include + +#include "platform.h" + +#define CTIME_BUFFER 64 +#define GETENV_BUFFER 4096 +#define STRERROR_BUFFER 128 + +static void **platform_mem_pool = NULL; +static void platform_mem_atexit() { + size_t i; + for (i = 0; i < vec_size(platform_mem_pool); i++) + mem_d(platform_mem_pool[i]); + vec_free(platform_mem_pool); +} + +static void *platform_mem_allocate(size_t bytes) { + void *mem = NULL; + if (!platform_mem_pool) { + atexit(&platform_mem_atexit); + vec_push(platform_mem_pool, NULL); + } + + mem = mem_a(bytes); + vec_push(platform_mem_pool, mem); + + return mem; +} + +int platform_vsnprintf(char *buffer, size_t bytes, const char *format, va_list arg) { + vsnprintf_s(buffer, bytes, bytes, format, arg); +} + +int platform_vsscanf(const char *str, const char *format, va_list va) { + return vsscanf_s(str, format, va); +} + +const struct tm *platform_localtime(const time_t *timer) { + struct tm *t; + t = (struct tm*)platform_mem_allocate(sizeof(struct tm)); + localtime_s(&t, timer); + return t; +} + +const char *platform_ctime(const time_t *timer) { + char *buffer = (char *)platform_mem_allocate(CTIME_BUFFER); + ctime_s(buffer, CTIME_BUFFER, timer); + return buffer; +} + +char *platform_strncat(char *dest, const char *src, size_t num) { + return strncat_s(dest, num, src, _TRUNCATE); +} + +const char *platform_tmpnam(char *str) { + return tmpnam_s(str, L_tmpnam); +} + +const char *platform_getenv(char *var) { + char *buffer = (char *)platform_mem_allocate(GETENV_BUFFER); + size_t size; + getenv_s(&size, buffer, GETENV_BUFFER, var); + return buffer; +} + +/* + * TODO: this isn't exactly 'accurate' for MSVC but it seems to work, + * at least to some extent. + */ +int platform_vasprintf(char **dat, const char *fmt, va_list args) { + int ret; + int len; + char *tmp = NULL; + + if ((len = _vscprintf(fmt, args)) < 0) { + *dat = NULL; + return -1; + } + + tmp = (char*)mem_a(len + 1); + if ((ret = _vsnprintf_s(tmp, len+1, len+1, fmt, args)) != len) { + mem_d(tmp); + *dat = NULL; + return -1; + } + *dat = tmp; + return len; +} + +char *platform_strcat(char *dest, const char *src) { + strcat_s(dest, strlen(src), src); + return dest; +} + +char *platform_strncpy(char *dest, const char *src, size_t num) { + strncpy_s(dest, num, src, num); + return dest; +} + +const char *platform_strerror(int err) { + char *buffer = (char*)platform_mem_allocate(STRERROR_BUFFER); + strerror_s(buffer, STRERROR_BUFFER, err); + return buffer; +} + +FILE *platform_fopen(const char *filename, const char *mode) { + FILE *handle; + return (fopen_s(&handle, filename, mode) != 0) ? NULL : handle; +} + +size_t platform_fread(void *ptr, size_t size, size_t count, FILE *stream) { + return fread_s(ptr, size, size, count, stream); +} + +size_t platform_fwrite(const void *ptr, size_t size, size_t count, FILE *stream) { + return fwrite(ptr, size, count, stream); +} + +int platform_fflush(FILE *stream) { + return fflush(stream); +} + +int platform_vfprintf(FILE *stream, const char *format, va_list arg) { + return vfprintf_s(stream, format, arg); +} + +int platform_fclose(FILE *stream) { + return fclose(stream); +} + +int platform_ferror(FILE *stream) { + return ferror(stream); +} + +int platform_fgetc(FILE *stream) { + return fgetc(stream); +} + +int platform_fputs(const char *str, FILE *stream) { + return fputs(str, stream); +} + +int platform_fseek(FILE *stream, long offset, int origin) { + return fseek(stream, offset, origin); +} + +long platform_ftell(FILE *stream) { + return ftell(stream); +} + +int platform_mkdir(const char *path, int mode) { + return mkdir(path, mode); +} + +DIR *platform_opendir(const char *path) { + DIR *dir = (DIR*)mem_a(sizeof(DIR) + strlen(path)); + if (!dir) + return NULL; + + platform_strncpy(dir->dd_name, path, strlen(path)); + return dir; +} + +int platform_closedir(DIR *dir) { + FindClose((HANDLE)dir->dd_handle); + mem_d((void*)dir); + return 0; +} + +struct dirent *platform_readdir(DIR *dir) { + WIN32_FIND_DATA info; + struct dirent *data; + int ret; + + if (!dir->dd_handle) { + char *dirname; + if (*dir->dd_name) { + size_t n = strlen(dir->dd_name); + if ((dir = (char*)mem_a(n+5))) { + platform_strncpy(dirname, dir->dd_name, n); + platform_strncpy(dirname + n, "\\*.*", 4); + } + } else { + if (!(dirname = util_strdup("\\*.*"))) + return NULL; + } + + dir->dd_handle = (long)FindFirstFile(dirname, &info); + mem_d(dirname); + ret = !(!dir->dd_handle); + } else if (dir->dd_handle != -11) { + ret = FindNextFile((HANDLE)dir->dd_handle, &info); + } else { + ret = 0; + } + + if (!ret) + return NULL; + + if ((data = (struct dirent*)mem_a(sizeof(struct dirent)))) { + platform_strncpy(data->d_name, info.cFileName, FILENAME_MAX - 1); + data->d_name[FILENAME_MAX - 1] = '\0'; + data->d_namelen = strlen(data->d_name); + } + + return data; +} + +int platform_istty(int fd) { + return _istty(fd); +} diff --git a/opts.c b/opts.c index 27d6a56..1d8fd73 100644 --- a/opts.c +++ b/opts.c @@ -213,7 +213,7 @@ static char *opts_ini_next(const char *s, char c) { } static size_t opts_ini_parse ( - FILE *filehandle, + fs_file_t *filehandle, char *(*loadhandle)(const char *, const char *, const char *), char **errorhandle ) { @@ -230,7 +230,7 @@ static size_t opts_ini_parse ( char *read_name; char *read_value; - while (fs_file_getline(&line, &linesize, filehandle) != EOF) { + while (fs_file_getline(&line, &linesize, filehandle) != FS_FILE_EOF) { parse_beg = line; /* handle BOM */ @@ -380,7 +380,7 @@ void opts_ini_init(const char *file) { */ char *error = NULL; size_t line; - FILE *ini; + fs_file_t *ini; if (!file) { /* try ini */ diff --git a/pak.c b/pak.c index 0b3f221..6fdd88b 100644 --- a/pak.c +++ b/pak.c @@ -124,7 +124,7 @@ static void pak_tree_build(const char *entry) { typedef struct { pak_directory_t *directories; pak_header_t header; - FILE *handle; + fs_file_t *handle; bool insert; } pak_file_t; @@ -162,7 +162,7 @@ static pak_file_t *pak_open_read(const char *file) { * Time to read in the directory handles and prepare the directories * vector. We're going to be reading some the file inwards soon. */ - fs_file_seek(pak->handle, pak->header.diroff, SEEK_SET); + fs_file_seek(pak->handle, pak->header.diroff, FS_FILE_SEEK_SET); /* * Read in all directories from the PAK file. These are considered @@ -266,7 +266,7 @@ static bool pak_extract_one(pak_file_t *pak, const char *file, const char *outdi pak_directory_t *dir = NULL; unsigned char *dat = NULL; char *local = NULL; - FILE *out = NULL; + fs_file_t *out = NULL; if (!pak_exists(pak, file, &dir)) { return false; @@ -295,7 +295,7 @@ static bool pak_extract_one(pak_file_t *pak, const char *file, const char *outdi mem_d(local); /* read */ - if (fs_file_seek (pak->handle, dir->pos, SEEK_SET) != 0) + if (fs_file_seek (pak->handle, dir->pos, FS_FILE_SEEK_SET) != 0) goto err; fs_file_read (dat, 1, dir->len, pak->handle); @@ -333,7 +333,7 @@ static bool pak_insert_one(pak_file_t *pak, const char *file) { pak_directory_t dir; unsigned char *dat; long len; - FILE *fp; + fs_file_t *fp; /* * We don't allow insertion on files that already exist within the @@ -351,9 +351,9 @@ static bool pak_insert_one(pak_file_t *pak, const char *file) { * the directory entry, and the actual contents of the file * to the PAK file itself. */ - if (fs_file_seek(fp, 0, SEEK_END) != 0 || ((len = fs_file_tell(fp)) < 0)) + if (fs_file_seek(fp, 0, FS_FILE_SEEK_END) != 0 || ((len = fs_file_tell(fp)) < 0)) goto err; - if (fs_file_seek(fp, 0, SEEK_SET) != 0) + if (fs_file_seek(fp, 0, FS_FILE_SEEK_SET) != 0) goto err; dir.len = len; @@ -438,18 +438,17 @@ static bool pak_close(pak_file_t *pak) { pak->header.diroff = tell; /* patch header */ - if (fs_file_seek (pak->handle, 0, SEEK_SET) != 0) + if (fs_file_seek (pak->handle, 0, FS_FILE_SEEK_SET) != 0) goto err; fs_file_write(&(pak->header), sizeof(pak_header_t), 1, pak->handle); /* write directories */ - if (fs_file_seek (pak->handle, pak->header.diroff, SEEK_SET) != 0) + if (fs_file_seek (pak->handle, pak->header.diroff, FS_FILE_SEEK_SET) != 0) goto err; - for (itr = 0; itr < vec_size(pak->directories); itr++) { + for (itr = 0; itr < vec_size(pak->directories); itr++) fs_file_write(&(pak->directories[itr]), sizeof(pak_directory_t), 1, pak->handle); - } } vec_free (pak->directories); @@ -496,6 +495,7 @@ static bool parsecmd(const char *optname, int *argc_, char ***argv_, char **out, return true; } +#include int main(int argc, char **argv) { bool extract = true; char *redirout = (char*)stdout; diff --git a/parser.c b/parser.c index 15bb921..2585f69 100644 --- a/parser.c +++ b/parser.c @@ -23,6 +23,7 @@ */ #include #include + #include "parser.h" #define PARSER_HT_LOCALS 2 @@ -5905,7 +5906,7 @@ parser_t *parser_create() } } if (!parser->assign_op) { - printf("internal error: initializing parser: failed to find assign operator\n"); + con_err("internal error: initializing parser: failed to find assign operator\n"); mem_d(parser); return NULL; } diff --git a/platform.h b/platform.h new file mode 100644 index 0000000..27abc64 --- /dev/null +++ b/platform.h @@ -0,0 +1,573 @@ +/* + * Copyright (C) 2012, 2013 + * Dale Weiler + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef GMQCC_PLATFORM_HDR +#define GMQCC_PLATFORM_HDR + +#ifndef GMQCC_PLATFORM_HEADER +# error "This header shouldn't be included!" +#endif + +#undef GMQCC_PLATFORM_HEADER +#include +#include +#include + +#ifdef _WIN32 +# ifndef STDERR_FILENO +# define STDERR_FILENO 2 +# endif +# ifndef STDOUT_FILENO +# define STDOUT_FILENO 1 +# endif +# ifndef __MINGW32__ +# define _WIN32_LEAN_AND_MEAN +# include +# include +# include + + struct dirent { + long d_ino; + unsigned short d_reclen; + unsigned short d_namlen; + char d_name[FILENAME_MAX]; + }; + + typedef struct { + struct _finddata_t dd_dta; + struct dirent dd_dir; + long dd_handle; + int dd_stat; + char dd_name[1]; + } DIR; + +# ifdef S_ISDIR +# undef S_ISDIR +# endif /*! S_ISDIR */ +# define S_ISDIR(X) ((X)&_S_IFDIR) +# else +# include +# endif /*!__MINGW32__*/ +#else +# include +# include +# include +# include +#endif /*!_WIN32*/ + +/* + * Function: platform_vsnprintf + * Write formatted output using a pointer to a lis of arguments. + * + * Parameters: + * buffer - Storage location for output. + * bytes - Maximum number of characters to write. + * format - Format specification. + * arg - Variable argument list. + * + * Returns: + * The number of characters written if the number of characters to write + * is less than or equal to `bytes`; if the number of characters to write + * is greater than `bytes`, this function returns -1 indicating that the + * output has been truncated. The return value does not include the + * terminating null, if one is written. + * + * Remarks: + * Function takes pointer to an argument list, then formats the data, + * and writes up to `bytes` characters to the memory pointed to by + * `buffer`. If there is room at the end (that is, if the number of + * character to write is less than `bytes`), the buffer will be null-terminated. + */ +int platform_vsnprintf(char *buffer, size_t bytes, const char *format, va_list arg); + +/* + * Function: platform_vsscanf + * Reads formatted data from a string. + * + * Parameters: + * buffer - Stored data to read. + * format - Format specification. + * arg - Variable argument list. + * + * Returns: + * The number of fields that are successfully converted and assigned; + * the return value does not include fields that were read but not + * assigned. A return vlaue of 0 indicated that no fields were assigned. + * The return value if EOF for error or if the end of the string is + * reached before the first conversion. + * + * Remarks: + * Reads data from `buffer` into the locations that are given by each + * argument in the `arg` argument list. Every argument in the list must + * be a pointer to a variable that has a type that corresponds to a + * type specifier in `format`. The `format` argument controls th + * interpretation of the input fields and has the same form and function + * as the `format` argument for the *scanf* function. If copying takes + * place between strings that overlap, the behaviour is undefined. + */ +int platform_vsscanf(const char *buffer, const char *format, va_list arg); + +/* + * Function: platform_localtime + * Convert a time value and correct for the local time zone. + * + * Parameters + * timer - Pointer to stored time. + * + * Returns: + * A pointer to a structure result, or NULL if the date passed to + * the function is before midnight, January 1, 1970. + */ +const struct tm *platform_localtime(const time_t *timer); + +/* + * Function: platform_ctime + * Convert a time value to a string and adjust for local time zone + * settings. + * + * Parameters: + * timer - Pointer to stored time. + * + * Returns: + * Pointer to the character string result. NULL will be returned if time + * represents a date before midnight, January 1, 1970, UTC. + * + * Remarks: + * Converts a time value stored as a `time_t` value into a chracter string. + * The `timer` value is usually obtained from a call to *time*, which returns + * the number of seconds since midnight, January 1, 1970 UTC. The return + * value of the string contains exactly 26 characters. A 24-hour clock is used. + * All fields have constant width. The newline chracter and the null character + * occupy the last two positions of the string. The converted character string + * is also adjusted according to the local time zone settings. + */ +const char *platform_ctime(const time_t *timer); + +/* + * Function: platform_strncat + * Append characters of a string. + * + * Parameters: + * dest - Null terminated destination string + * src - Source string + * num - Number of characters to append + * + * Returns: + * Pointer to the destination string. No return value is used to indicate + * an error. + * + * Remarks: + * Function appends, at mode, the first `num` characters of `src` to + * `dest`. The initial character of `src` overwrites the terminating + * null chracter of `dest`. If a null character appears in `src` before + * `num` chracters are appended, `platform_strncat` appends all chracters + * from `src`, up to the null chracter. If `num` is greater than the + * length of `src`, the length of `src` is used in place of `num`. + */ +char *platform_strncat(char *dest, const char *src, size_t num); + +/* + * Function: platform_tmpnam + * Generates names you can use to create temporary files. + * + * Parameters: + * str - Pointer that will hold the generated name and will be identical + * to the name returned by the function. This is a convenient way + * to save the generated name. + * + * Returns: + * Pointer to the name generate or *NULL* if there is a failure. Failure + * can occur if you attempt more than TMP_MAX calls. + * + * Remarks: + * Returns a name unique in the current workign directory. + */ +const char *platform_tmpnam(char *str); + +/* + * Function: platform_getenv + * Get a value from the current enviroment. + * + * Parameters: + * var - Enviroment variable name + * + * Returns: + * A pointer to the enviroment table entry containing `var. It's not + * safe to modify the value of the enviroment variable using the returned + * pointer. The return value is *NULL* if `var` is not found in the + * enviroment table. + */ +const char *platform_getenv(char *var); + +/* + * Function: platform_vasprintf + * Print to allocated string + * + * Parameters: + * dat - Pointer to pointer to store allocated data. + * fmt - Format specification. + * args - Variable argument list. + * + * Returns: + * Number of character written, -1 is used to indicate an error. + * + * Remarks: + * Allocate a string large enough to hold the output including + * the terminating null character than write formatted output + * to it using format specification. + */ +int platform_vasprintf(char **dat, const char *fmt, va_list args); + +/* + * Function: platform_vfprintf + * Write formatted output using a pointer to a list of arguments. + * + * Parameters: + * stream - Pointer to stream. + * format - Format specification. + * atrg - Variable argument list. + * + * Returns: + * Number of characters written, not including the terminating null + * character, or a negitive value if an output error occurs. -1 is + * also used to indicate an error. + * + * Remarks: + * Takes a pointer to an argument list, then formats and writes the + * given data to `stream`. + */ +int platform_vfprintf(FILE *stream, const char *format, va_list arg); + +/* + * Function: platform_strcat + * Append characters of a string. + * + * Parameters: + * dest - Null terminated destination string + * src - Source string + * + * Returns: + * Pointer to the destination string. No return value is used to indicate + * an error. + * + * Remarks: + * Appens `src` to `dest` and terminates with resulting null character. + * The initial character of `src` overwrites the terminating null + * character of `dest`. The behaviour of platform_strcat is undefined + * if the source and destination string overlap. + */ +char *platform_strcat(char *dest, const char *src); + +/* + * Function: platform_strncpy + * Copys characters of one string to another. + * + * Parameters: + * dest - Destination string. + * src - Source string. + * num - Number of characters to be copied. + * + * Returns: + * `dest`. No return value is reserved to indicate an error. + * + * Remarks: + * Copies the initial characters of `src` to `dest` and returns `dest`. + * If `num` is less than or equal to the length of `src1 a null character + * is not appended automatically to the copied string. If `num` is greater + * than the length of `src`, the destination string is padded with null + * characters up to length `num`. The behaviour of this function is undefined + * if the source and destination strings overlap. + */ +char *platform_strncpy(char *dest, const char *src, size_t num); + +/* + * Function: platform_strerror + * Get a system error message + * + * Parameters: + * err - Error number. + * + * Returns: + * A pointer to the error message + */ +const char *platform_strerror(int err); + +/* + * Function: platform_fopen + * Opens a file + * + * Parameters: + * filename - File name. + * mode - Kind of access that's enabled. + * + * Returns: + * A pointer to the open file. A null pointer value indicates an error. + */ +FILE *platform_fopen(const char *filename, const char *mode); + +/* + * Function: platform_fread + * Reads data from a stream + * + * Parameters: + * ptr - Storage location for data. + * size - Item size in bytes. + * count - Maximum number of items to be read. + * stream - Pointer to stream + * + * Returns: + * The number of full items actually read, which may be less than `count` + * if an error occurs or if the end of the file is encountered before + * reaching `count`. If `size` or `count` is 0, `platform_fread` + * returns 0 and the buffer contents are unchanged. + */ +size_t platform_fread(void *ptr, size_t size, size_t count, FILE *stream); + +/* + * Function: platform_fwrite + * Writes data to a stream + * + * Parameters: + * ptr - Pointer to data to be written. + * size - Item size in bytes. + * count - Maximum number of items to be read. + * stream - Pointer to stream + * + * Returns: + * The number of full items actually written, which may be less than + * `count` if an error occurs. Also, if an error occurs, the + * file-position indicator cannot be determined. + * + * Remarks: + * Writes up to `count` items, of `size` length each, from `ptr` to the + * output stream. The file pointer associated with stream (if there is one) + * is incremented by the number of bytes actually written. + */ +size_t platform_fwrite(const void *ptr, size_t size, size_t count, FILE *stream); + +/* + * Function: platform_fflush + * Flushes a stream + * + * Parameters: + * stream - Pointer to stream + * + * Returns: + * 0 value if the buffer was succesffuly flushed. The value 0 is also + * returned in cases in which the specified stream has no buffer or is + * open for reading only. A return value of *EOF* indicates an error. + * + * Remarks: + * Flushes a stream. If the file associated with stream is open for output, + * platform_fflush writes to that file the contents of the buffer + * associated with the stream. If the stream is open for input, + * platform_fflush clears the contents of the buffer. platform_fflush + * negates the effect of any prior call to ungetc against stream. Also, + * platform_fflush(NULL) flushes all streams opened for output. + * The stream remains open after the call. platform_fflush has no effect + * on an unbuffered stream. + */ +int platform_fflush(FILE *stream); + +/* + * Function: platform_fclose + * Closes a stream. + * + * Parameters: + * stream - Pointer to stream. + * + * Returns: + * 0 value. *EOF* is used to indicate an error. + * + * Remarks: + * Closes a stream. + */ +int platform_fclose(FILE *stream); + +/* + * Function: platform_ferror + * Tests for an error on a stream. + * + * Parameters: + * stream - Pointer to stream. + * + * Returns: + * If not error has occured on `stream`, 0 value is returned, otherwise + * it returns a nonzero value. + * + * Remarks: + * Tests for a reading or writing error on the file associated with `stream`. + * If an error has occured, the error indicator for the stream remains set + * until the stream is closed or rewound. + */ +int platform_ferror(FILE *stream); + +/* + * Function: platform_fgetc + * Read a character from a stream. + * + * Parameters: + * stream - Pointer to a stream. + * + * Returns: + * The chracter read as an int or EOF to indicate an error or end-of-file. + * + * Remarks: + * Reads a single chracter from the current position of the file associated + * with `stream`. Than increments that position. If the steam is at the end + * of the file, the end-of-file indicator for the stream is set. + */ +int platform_fgetc(FILE *stream); + +/* + * Function: platform_fputs + * Write a string to a stream + * + * Parameters: + * str - Output string. + * stream - Pointer to stream. + * + * Returns: + * Non-negative value if successful. EOF is used to indicate an error. + * + * Remarks: + * Copies `str` to the output stream at the current position. + */ +int platform_fputs(const char *str, FILE *stream); + +/* + * Function: platform_fseek + * Moves the file pointer to a specified location. + * + * Parameters: + * stream - Pointer to stream. + * offset - Number of bytes from origin to offset. + * origin - Initital position. + * + * Returns: + * 0 value, nonzero values are used to indicate an error. + * + * Remarks: + * Moves the file pointer (if any) associated with stream to a new + * location that is offset bytes from origin. + * The next operation on the stream takes place at the new location. + * On a stream open for update, the next operation can be either a + * read or a write. + */ +int platform_fseek(FILE *stream, long offset, int origin); + +/* + * Function: platform_ftell + * Gets the current position of a file pointer + * + * Parameters: + * stream - Pointer to stream + * + * Returns: + * Current file position. May not reflect physical byte offset, e.g + * systems where read-mode does carriage return-linefeed translation. + * -1 value is used to indivate an error. + */ +long platform_ftell(FILE *stream); + +/* + * Function: platform_mkdir + * Make a directory + * + * Parameters: + * path - Path to create + * mode - The mode of the directory (implementation defined) + * + * Returns: + * 0 value. -1 value is used to indicate an error. On error no + * directory shall be created. + * + * Remarks: + * Shall create a new empty directory with with the name path specified + * by argument `path. + */ +int platform_mkdir(const char *path, int mode); + +/* + * Function: platform_opendir + * Open a directory + * + * Parameters: + * path - Path to the directory to open + * + * Returns: + * Pointer to an object of type *DIR*. A null pointer value indicates + * an error. + * + * Remarks: + * Shall open a directory stream corresponding to the directory named by + * the `path` argument. The directory stream is positioned at the first entry. + */ +DIR *platform_opendir(const char *path); + +/* + * Function: platform_closedir + * Close a directory stream + * + * Parameters: + * dir - Pointer to directory stream + * + * Returns: + * 0 value. A -1 value indicated an error. + * + * Remarks: + * Shall close the directory stream referred to by the argument + * `dir`. Upon return, the value of `dir` may no longer point to + * an accessible object of the type *DIR*. + */ +int platform_closedir(DIR *dir); + +/* + * Function: platform_readdir + * Read directory + * + * Parameters: + * dir - Pointer to directory stream + * + * Returns: + * Pointer to an object of type `struct dirent`. A null pointer value + * indicates an error. + * + * Remarks: + * When the end of the directory is encountered, a null pointer is + * returned. + */ +struct dirent *platform_readdir(DIR *dir); + +/* + * Function: platform_isatty + * Determines whether a file descriptor is associated with a character + * device. + * + * Returns: + * A nonzero value if the descriptor is associated with a character + * device. Otherwise `platform_isatty` returns 0. + */ +int platform_isatty(int fd); + +#endif diff --git a/test.c b/test.c index fdf3777..2672cb6 100644 --- a/test.c +++ b/test.c @@ -20,12 +20,12 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ +#define GMQCC_PLATFORM_HEADER /* TODO: eliminate! */ #include #include -#include -#include #include "gmqcc.h" +#include "platform.h" static const char *task_bins[] = { "./gmqcc", @@ -53,15 +53,15 @@ static const char *task_bins[] = { #include #include typedef struct { - FILE *handles[3]; - int pipes [3]; + fs_file_t *handles[3]; + int pipes [3]; int stderr_fd; int stdout_fd; int pid; } popen_t; -static FILE ** task_popen(const char *command, const char *mode) { +static fs_file_t **task_popen(const char *command, const char *mode) { int inhandle [2]; int outhandle [2]; int errhandle [2]; @@ -103,9 +103,9 @@ static FILE ** task_popen(const char *command, const char *mode) { data->pipes [1] = outhandle[0]; data->pipes [2] = errhandle[0]; - data->handles[0] = fdopen(inhandle [1], "w"); - data->handles[1] = fdopen(outhandle[0], mode); - data->handles[2] = fdopen(errhandle[0], mode); + data->handles[0] = (fs_file_t*)fdopen(inhandle [1], "w"); + data->handles[1] = (fs_file_t*)fdopen(outhandle[0], mode); + data->handles[2] = (fs_file_t*)fdopen(errhandle[0], mode); /* sigh */ vec_free(argv); @@ -137,7 +137,7 @@ task_popen_error_0: return NULL; } -static int task_pclose(FILE **handles) { +static int task_pclose(fs_file_t **handles) { popen_t *data = (popen_t*)handles; int status = 0; @@ -153,22 +153,17 @@ static int task_pclose(FILE **handles) { } #else typedef struct { - FILE *handles[3]; - char name_err[L_tmpnam]; - char name_out[L_tmpnam]; + fs_file_t *handles[3]; + char name_err[L_tmpnam]; + char name_out[L_tmpnam]; } popen_t; - static FILE **task_popen(const char *command, const char *mode) { + static fs_file_t **task_popen(const char *command, const char *mode) { char *cmd = NULL; popen_t *open = (popen_t*)mem_a(sizeof(popen_t)); -#ifndef _MSC_VER - tmpnam(open->name_err); - tmpnam(open->name_out); -#else - tmpnam_s(open->name_err, L_tmpnam); - tmpnam_s(open->name_out, L_tmpnam); -#endif + util_tmpnam(open->name_err); + util_tmpnam(open->name_out); (void)mode; /* excluded */ @@ -184,10 +179,12 @@ static int task_pclose(FILE **handles) { return open->handles; } - static int task_pclose(FILE **files) { + static int task_pclose(fs_file_t **files) { popen_t *open = ((popen_t*)files); + fs_file_close(files[1]); fs_file_close(files[2]); + remove(open->name_err); remove(open->name_out); @@ -363,7 +360,7 @@ static bool task_template_generate(task_template_t *tmpl, char tag, const char * return true; } -static bool task_template_parse(const char *file, task_template_t *tmpl, FILE *fp, size_t *pad) { +static bool task_template_parse(const char *file, task_template_t *tmpl, fs_file_t *fp, size_t *pad) { char *data = NULL; char *back = NULL; size_t size = 0; @@ -373,7 +370,7 @@ static bool task_template_parse(const char *file, task_template_t *tmpl, FILE *f return false; /* top down parsing */ - while (fs_file_getline(&back, &size, fp) != EOF) { + while (fs_file_getline(&back, &size, fp) != FS_FILE_EOF) { /* skip whitespace */ data = back; if (*data && (*data == ' ' || *data == '\t')) @@ -504,7 +501,7 @@ static task_template_t *task_template_compile(const char *file, const char *dir, /* a page should be enough */ char fullfile[4096]; size_t filepadd = 0; - FILE *tempfile = NULL; + fs_file_t *tempfile = NULL; task_template_t *tmpl = NULL; util_snprintf(fullfile, sizeof(fullfile), "%s/%s", dir, file); @@ -659,9 +656,9 @@ static void task_template_destroy(task_template_t *tmpl) { */ typedef struct { task_template_t *tmpl; - FILE **runhandles; - FILE *stderrlog; - FILE *stdoutlog; + fs_file_t **runhandles; + fs_file_t *stderrlog; + fs_file_t *stdoutlog; char *stdoutlogfile; char *stderrlogfile; bool compiled; @@ -675,8 +672,8 @@ static task_t *task_tasks = NULL; */ static bool task_propagate(const char *curdir, size_t *pad, const char *defs) { bool success = true; - DIR *dir; - struct dirent *files; + fs_dir_t *dir; + fs_dirent_t *files; struct stat directory; char buffer[4096]; size_t found = 0; @@ -733,7 +730,7 @@ static bool task_propagate(const char *curdir, size_t *pad, const char *defs) { if (strcmp(files->d_name + strlen(files->d_name) - 5, ".tmpl") == 0) { task_template_t *tmpl = task_template_compile(files->d_name, directories[i], pad); char buf[4096]; /* one page should be enough */ - char *qcflags = NULL; + const char *qcflags = NULL; task_t task; found ++; @@ -754,16 +751,7 @@ static bool task_propagate(const char *curdir, size_t *pad, const char *defs) { * to test compile flags for all tests. This needs to be * BEFORE other flags (so that the .tmpl can override them) */ - #ifdef _MSC_VER - { - char buffer[4096]; - size_t size; - getenv_s(&size, buffer, sizeof(buffer), "QCFLAGS"); - qcflags = buffer; - } - #else - qcflags = getenv("QCFLAGS"); - #endif + qcflags = platform_getenv("QCFLAGS"); /* * Generate the command required to open a pipe to a process @@ -881,9 +869,9 @@ static bool task_propagate(const char *curdir, size_t *pad, const char *defs) { * left behind from a previous invoke of the test-suite. */ static void task_precleanup(const char *curdir) { - DIR *dir; - struct dirent *files; - char buffer[4096]; + fs_dir_t *dir; + fs_dirent_t *files; + char buffer[4096]; dir = fs_dir_open(curdir); @@ -949,7 +937,7 @@ static bool task_trymatch(size_t i, char ***line) { bool success = true; bool process = true; int retval = EXIT_SUCCESS; - FILE *execute; + fs_file_t *execute; char buffer[4096]; task_template_t *tmpl = task_tasks[i].tmpl; @@ -973,7 +961,7 @@ static bool task_trymatch(size_t i, char ***line) { ); } - execute = popen(buffer, "r"); + execute = (fs_file_t*)popen(buffer, "r"); if (!execute) return false; } else if (!strcmp(tmpl->proceduretype, "-pp")) { @@ -1006,7 +994,7 @@ static bool task_trymatch(size_t i, char ***line) { size_t size = 0; size_t compare = 0; - while (fs_file_getline(&data, &size, execute) != EOF) { + while (fs_file_getline(&data, &size, execute) != FS_FILE_EOF) { if (!strcmp(data, "No main function found\n")) { con_err("test failure: `%s` (No main function found) [%s]\n", tmpl->description, @@ -1015,7 +1003,7 @@ static bool task_trymatch(size_t i, char ***line) { if (!process) fs_file_close(execute); else - pclose(execute); + pclose((FILE*)execute); return false; } @@ -1075,7 +1063,7 @@ static bool task_trymatch(size_t i, char ***line) { } if (process) - retval = pclose(execute); + retval = pclose((FILE*)execute); else fs_file_close(execute); @@ -1139,7 +1127,7 @@ static size_t task_schedualize(size_t *pad) { * Read data from stdout first and pipe that stuff into a log file * then we do the same for stderr. */ - while (fs_file_getline(&data, &size, task_tasks[i].runhandles[1]) != EOF) { + while (fs_file_getline(&data, &size, task_tasks[i].runhandles[1]) != FS_FILE_EOF) { fs_file_puts(task_tasks[i].stdoutlog, data); if (strstr(data, "failed to open file")) { @@ -1147,7 +1135,7 @@ static size_t task_schedualize(size_t *pad) { execute = false; } } - while (fs_file_getline(&data, &size, task_tasks[i].runhandles[2]) != EOF) { + while (fs_file_getline(&data, &size, task_tasks[i].runhandles[2]) != FS_FILE_EOF) { /* * If a string contains an error we just dissalow execution * of it in the vm. @@ -1162,7 +1150,7 @@ static size_t task_schedualize(size_t *pad) { } fs_file_puts (task_tasks[i].stderrlog, data); - fflush(task_tasks[i].stderrlog); /* fast flush for read */ + fs_file_flush(task_tasks[i].stderrlog); /* fast flush for read */ } if (!task_tasks[i].compiled && strcmp(task_tasks[i].tmpl->proceduretype, "-fail")) { diff --git a/util.c b/util.c index 9b32e48..baff4a4 100644 --- a/util.c +++ b/util.c @@ -21,10 +21,13 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ +#define GMQCC_PLATFORM_HEADER #include #include +#include #include "gmqcc.h" +#include "platform.h" /* * Initially this was handled with a table in the gmqcc.h header, but @@ -224,163 +227,64 @@ size_t util_optimizationtostr(const char *in, char *out, size_t outsz) { return util_strtransform(in, out, outsz, "_ ", 'a'-'A'); } -/* - * Portable implementation of vasprintf/asprintf. Assumes vsnprintf - * exists, otherwise compiler error. - * - * TODO: fix for MSVC .... - */ -int util_vasprintf(char **dat, const char *fmt, va_list args) { - int ret; - int len; - char *tmp = NULL; +int util_snprintf(char *str, size_t size, const char *fmt, ...) { + va_list arg; + int ret; - /* - * For visual studio _vsnprintf doesn't tell you the length of a - * formatted string if it overflows. However there is a MSVC - * intrinsic (which is documented wrong) called _vcsprintf which - * will return the required amount to allocate. - */ - #ifdef _MSC_VER - if ((len = _vscprintf(fmt, args)) < 0) { - *dat = NULL; - return -1; - } + va_start(arg, fmt); + ret = platform_vsnprintf(str, size, fmt, arg); + va_end(arg); - tmp = (char*)mem_a(len + 1); - if ((ret = _vsnprintf_s(tmp, len+1, len+1, fmt, args)) != len) { - mem_d(tmp); - *dat = NULL; - return -1; - } - *dat = tmp; - return len; - #else - /* - * For everything else we have a decent conforming vsnprintf that - * returns the number of bytes needed. We give it a try though on - * a short buffer, since efficiently speaking, it could be nice to - * above a second vsnprintf call. - */ - char buf[128]; - va_list cpy; - va_copy(cpy, args); - len = vsnprintf(buf, sizeof(buf), fmt, cpy); - va_end (cpy); - - if (len < (int)sizeof(buf)) { - *dat = util_strdup(buf); - return len; - } - - /* not large enough ... */ - tmp = (char*)mem_a(len + 1); - if ((ret = vsnprintf(tmp, len + 1, fmt, args)) != len) { - mem_d(tmp); - *dat = NULL; - return -1; - } - - *dat = tmp; - return len; - #endif + return ret; } + int util_asprintf(char **ret, const char *fmt, ...) { va_list args; int read; + va_start(args, fmt); - read = util_vasprintf(ret, fmt, args); + read = platform_vasprintf(ret, fmt, args); va_end (args); return read; } -/* - * These are various re-implementations (wrapping the real ones) of - * string functions that MSVC considers unsafe. We wrap these up and - * use the safe variations on MSVC. - */ -#ifdef _MSC_VER - static char **util_strerror_allocated() { - static char **data = NULL; - return data; - } +int util_sscanf(const char *str, const char *format, ...) { + va_list args; + int read; - static void util_strerror_cleanup(void) { - size_t i; - char **data = util_strerror_allocated(); - for (i = 0; i < vec_size(data); i++) - mem_d(data[i]); - vec_free(data); - } + va_start(args, format); + read = platform_vsscanf(str, format, args); + va_end(args); - const char *util_strerror(int num) { - char *allocated = NULL; - static bool install = false; - static size_t tries = 0; - char **vector = util_strerror_allocated(); + return read; +} - /* try installing cleanup handler */ - while (!install) { - if (tries == 32) - return "(unknown)"; +char *util_strncpy(char *dest, const char *src, size_t n) { + return platform_strncpy(dest, src, n); +} +char *util_strncat(char *dest, const char *src, size_t n) { + return platform_strncat(dest, src, n); +} +char *util_strcat(char *dest, const char *src) { + return platform_strcat(dest, src); +} +const char *util_strerror(int err) { + return platform_strerror(err); +} - install = !atexit(&util_strerror_cleanup); - tries ++; - } - - allocated = (char*)mem_a(4096); /* A page must be enough */ - strerror_s(allocated, 4096, num); - - vec_push(vector, allocated); - return (const char *)allocated; - } - - int util_snprintf(char *src, size_t bytes, const char *format, ...) { - int rt; - va_list va; - va_start(va, format); - - rt = vsprintf_s(src, bytes, format, va); - va_end (va); - - return rt; - } - - char *util_strcat(char *dest, const char *src) { - strcat_s(dest, strlen(src), src); - return dest; - } - - char *util_strncpy(char *dest, const char *src, size_t num) { - strncpy_s(dest, num, src, num); - return dest; - } -#else - const char *util_strerror(int num) { - return strerror(num); - } - - int util_snprintf(char *src, size_t bytes, const char *format, ...) { - int rt; - va_list va; - va_start(va, format); - rt = vsnprintf(src, bytes, format, va); - va_end (va); - - return rt; - } - - char *util_strcat(char *dest, const char *src) { - return strcat(dest, src); - } - - char *util_strncpy(char *dest, const char *src, size_t num) { - return strncpy(dest, src, num); - } - -#endif /*! _MSC_VER */ +const struct tm *util_localtime(const time_t *timer) { + return platform_localtime(timer); +} +const char *util_ctime(const time_t *timer) { + return platform_ctime(timer); +} +bool util_isatty(fs_file_t *file) { + if (file == (fs_file_t*)stdout) return !!platform_isatty(STDOUT_FILENO); + if (file == (fs_file_t*)stderr) return !!platform_isatty(STDERR_FILENO); + return false; +} void util_seed(uint32_t value) { srand((int)value);