Merge branch 'cooking' of git://github.com/graphitemaster/gmqcc into cooking

This commit is contained in:
Wolfgang Bumiller 2013-10-11 14:10:57 +02:00
commit 82afdb1e2c
21 changed files with 1304 additions and 847 deletions

View file

@ -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

4
PORTING Normal file
View file

@ -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.

169
ansi.c Normal file
View file

@ -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 <string.h>
#include <stdlib.h>
#include <unistd.h>
#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);
}

2
code.c
View file

@ -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);

215
conout.c
View file

@ -20,168 +20,20 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include <stdio.h>
#include "gmqcc.h"
/*
* isatty/STDERR_FILENO/STDOUT_FILNO
* + some other things likewise.
*/
#ifndef _WIN32
# include <unistd.h>
#else
# include <io.h>
/*
* 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.h>
/*
* 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);

30
exec.c
View file

@ -23,10 +23,9 @@
*/
#ifndef QCVM_LOOP
#include <errno.h>
#include <stdio.h>
#include <string.h>
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#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);

264
fs.c
View file

@ -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 <crtdbg.h> /* _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 <sys/stat.h> /* 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);
}

56
ftepp.c
View file

@ -21,15 +21,16 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include <time.h>
#include <string.h>
#include <stdlib.h>
#include <time.h>
#include <sys/stat.h>
#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("\"<failed to determine timestamp>\"");
/*
* 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;

183
gmqcc.h
View file

@ -24,7 +24,8 @@
#ifndef GMQCC_HDR
#define GMQCC_HDR
#include <stdarg.h>
#include <stdio.h> /* TODO: remove this */
#include <stddef.h>
#include <time.h> /* 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 <windows.h>
# include <io.h>
# include <fcntl.h>
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 <dirent.h>
#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;
bool 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;

View file

@ -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 = \

10
ir.c
View file

@ -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 "<INVALID>";
@ -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);

53
lexer.c
View file

@ -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;

View file

@ -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;

12
main.c
View file

@ -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;

235
msvc.c Normal file
View file

@ -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 <string.h>
#include <stdlib.h>
#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 _isatty(fd);
}

6
opts.c
View file

@ -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 */

22
pak.c
View file

@ -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 <stdio.h>
int main(int argc, char **argv) {
bool extract = true;
char *redirout = (char*)stdout;

View file

@ -23,6 +23,7 @@
*/
#include <string.h>
#include <math.h>
#include "parser.h"
#define PARSER_HT_LOCALS 2
@ -5912,7 +5913,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;
}

573
platform.h Normal file
View file

@ -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 <stdarg.h>
#include <time.h>
#include <stdio.h>
#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 <windows.h>
# include <io.h>
# include <fcntl.h>
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 <dirent.h>
# endif /*!__MINGW32__*/
#else
# include <sys/types.h>
# include <sys/stat.h>
# include <unistd.h>
# include <dirent.h>
#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

90
test.c
View file

@ -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 <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include "gmqcc.h"
#include "platform.h"
static const char *task_bins[] = {
"./gmqcc",
@ -53,15 +53,15 @@ static const char *task_bins[] = {
#include <dirent.h>
#include <unistd.h>
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")) {

184
util.c
View file

@ -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 <string.h>
#include <stdlib.h>
#include <stdarg.h>
#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);