MINGW = $(findstring MINGW32, $(UNAME))
CC ?= clang
-CFLAGS += -Wall -Wextra -I. -fno-strict-aliasing -fsigned-char -Wno-overlength-strings
+CFLAGS += -Wall -Wextra -Werror -I. -fno-strict-aliasing -fsigned-char
ifneq ($(shell git describe --always 2>/dev/null),)
CFLAGS += -DGMQCC_GITINFO="\"$(shell git describe --always)\""
endif
#turn on tons of warnings if clang is present
# but also turn off the STUPID ONES
ifeq ($(CC), clang)
- CFLAGS += \
+ CFLAGS += \
-Weverything \
-Wno-padded \
-Wno-format-nonliteral \
CFLAGS += -DNOTRACK
endif
-OBJ_D = util.o code.o ast.o ir.o conout.o ftepp.o opts.o file.o utf8.o correct.o
-OBJ_T = test.o util.o conout.o file.o
-OBJ_C = main.o lexer.o parser.o file.o
-OBJ_X = exec-standalone.o util.o conout.o file.o
+OBJ_D = util.o code.o ast.o ir.o conout.o ftepp.o opts.o fs.o utf8.o correct.o
+OBJ_P = util.o fs.o conout.o opts.o pak.o
+OBJ_T = test.o util.o conout.o fs.o
+OBJ_C = main.o lexer.o parser.o fs.o
+OBJ_X = exec-standalone.o util.o conout.o fs.o
ifneq ("$(CYGWIN)", "")
#nullify the common variables that
QCVM = qcvm.exe
GMQCC = gmqcc.exe
TESTSUITE = testsuite.exe
+ PAK = pak.exe
else
ifneq ("$(MINGW)", "")
#nullify the common variables that
QCVM = qcvm.exe
GMQCC = gmqcc.exe
TESTSUITE = testsuite.exe
+ PAK = pak.exe
else
#arm support for linux .. we need to allow unaligned accesses
#to memory otherwise we just segfault everywhere
QCVM = qcvm
GMQCC = gmqcc
TESTSUITE = testsuite
+ PAK = pak
endif
endif
$(TESTSUITE): $(OBJ_T)
$(CC) -o $@ $^ $(CFLAGS)
-all: $(GMQCC) $(QCVM) $(TESTSUITE)
+$(PAK): $(OBJ_P)
+ $(CC) -o $@ $^ $(CFLAGS)
+
+all: $(GMQCC) $(QCVM) $(TESTSUITE) $(PAK)
check: all
@ ./$(TESTSUITE)
-test: check
+test: all
@ ./$(TESTSUITE)
clean:
- rm -f *.o $(GMQCC) $(QCVM) $(TESTSUITE) *.dat
+ rm -f *.o $(GMQCC) $(QCVM) $(TESTSUITE) $(PAK) *.dat
splint:
@ splint $(SPLINTFLAGS) *.c *.h
$(subst .o,.c,$(OBJ_C))
@makedepend -a -Y -w 65536 2> /dev/null \
$(subst .o,.c,$(OBJ_X))
+ @makedepend -a -Y -w 65536 2> /dev/null \
+ $(subst .o,.c,$(OBJ_P))
#install rules
install: install-gmqcc install-qcvm install-doc
conout.o: gmqcc.h opts.def
ftepp.o: gmqcc.h opts.def lexer.h
opts.o: gmqcc.h opts.def
-file.o: gmqcc.h opts.def
+fs.o: gmqcc.h opts.def
utf8.o: gmqcc.h opts.def
correct.o: gmqcc.h opts.def
test.o: gmqcc.h opts.def
util.o: gmqcc.h opts.def
conout.o: gmqcc.h opts.def
-file.o: gmqcc.h opts.def
+fs.o: gmqcc.h opts.def
main.o: gmqcc.h opts.def lexer.h
lexer.o: gmqcc.h opts.def lexer.h
parser.o: gmqcc.h opts.def lexer.h ast.h ir.h
-file.o: gmqcc.h opts.def
+fs.o: gmqcc.h opts.def
util.o: gmqcc.h opts.def
conout.o: gmqcc.h opts.def
-file.o: gmqcc.h opts.def
+fs.o: gmqcc.h opts.def
ast_value *cp = ast_value_new(self->expression.node.context, self->name, self->expression.vtype);
if (self->expression.next) {
cp->expression.next = ast_type_copy(self->expression.node.context, self->expression.next);
- if (!cp->expression.next) {
- ast_value_delete(cp);
- return NULL;
- }
}
fromex = &self->expression;
selfex = &cp->expression;
selfex->flags = fromex->flags;
for (i = 0; i < vec_size(fromex->params); ++i) {
ast_value *v = ast_value_copy(fromex->params[i]);
- if (!v) {
- ast_value_delete(cp);
- return NULL;
- }
vec_push(selfex->params, v);
}
return cp;
}
-bool ast_type_adopt_impl(ast_expression *self, const ast_expression *other)
+void ast_type_adopt_impl(ast_expression *self, const ast_expression *other)
{
size_t i;
const ast_expression_common *fromex;
self->expression.vtype = other->expression.vtype;
if (other->expression.next) {
self->expression.next = (ast_expression*)ast_type_copy(ast_ctx(self), other->expression.next);
- if (!self->expression.next)
- return false;
}
fromex = &other->expression;
selfex = &self->expression;
selfex->flags = fromex->flags;
for (i = 0; i < vec_size(fromex->params); ++i) {
ast_value *v = ast_value_copy(fromex->params[i]);
- if (!v)
- return false;
vec_push(selfex->params, v);
}
- return true;
}
static ast_expression* ast_shallow_type(lex_ctx ctx, int vtype)
selfex->vtype = fromex->vtype;
if (fromex->next)
- {
selfex->next = ast_type_copy(ctx, fromex->next);
- if (!selfex->next) {
- ast_expression_delete_full(self);
- return NULL;
- }
- }
else
selfex->next = NULL;
selfex->flags = fromex->flags;
for (i = 0; i < vec_size(fromex->params); ++i) {
ast_value *v = ast_value_copy(fromex->params[i]);
- if (!v) {
- ast_expression_delete_full(self);
- return NULL;
- }
vec_push(selfex->params, v);
}
else
self->expression.vtype = left->expression.vtype;
+ /* references all */
+ self->refs = AST_REF_ALL;
+
return self;
}
void ast_binary_delete(ast_binary *self)
{
- ast_unref(self->left);
- ast_unref(self->right);
+ if (self->refs & AST_REF_LEFT) ast_unref(self->left);
+ if (self->refs & AST_REF_RIGHT) ast_unref(self->right);
+
ast_expression_delete((ast_expression*)self);
mem_d(self);
}
self->keep_dest = false;
- if (!ast_type_adopt(self, left)) {
- ast_delete(self);
- return NULL;
- }
-
+ ast_type_adopt(self, left);
return self;
}
ast_propagate_effects(self, entity);
ast_propagate_effects(self, field);
- if (!ast_type_adopt(self, outtype)) {
- ast_entfield_delete(self);
- return NULL;
- }
-
+ ast_type_adopt(self, outtype);
return self;
}
ast_propagate_effects(self, array);
ast_propagate_effects(self, index);
- if (!ast_type_adopt(self, outtype)) {
- ast_array_index_delete(self);
- return NULL;
- }
+ ast_type_adopt(self, outtype);
if (array->expression.vtype == TYPE_FIELD && outtype->expression.vtype == TYPE_ARRAY) {
if (self->expression.vtype != TYPE_ARRAY) {
compile_error(ast_ctx(self), "array_index node on type");
if (ontrue->expression.vtype == TYPE_NIL)
exprtype = onfalse;
- if (!ast_type_adopt(self, exprtype)) {
- ast_ternary_delete(self);
- return NULL;
- }
+ ast_type_adopt(self, exprtype);
return self;
}
self->dest = dest;
self->source = source;
- if (!ast_type_adopt(self, dest)) {
- ast_delete(self);
- return NULL;
- }
+ ast_type_adopt(self, dest);
return self;
}
ast_delete(self->expression.next);
self->expression.next = NULL;
}
- if (!ast_type_adopt(self, e)) {
- compile_error(ast_ctx(self), "internal error: failed to adopt type");
- return false;
- }
+ ast_type_adopt(self, e);
return true;
}
mem_d(self);
}
-bool ast_block_set_type(ast_block *self, ast_expression *from)
+void ast_block_set_type(ast_block *self, ast_expression *from)
{
if (self->expression.next)
ast_delete(self->expression.next);
- if (!ast_type_adopt(self, from))
- return false;
- return true;
+ ast_type_adopt(self, from);
}
ast_function* ast_function_new(lex_ctx ctx, const char *name, ast_value *vtype)
if (!lvalue && self->expression.outr) {
*out = self->expression.outr;
+ return true;
}
if (lvalue && self->expression.outl) {
*out = self->expression.outl;
+ return true;
}
if (!ast_istype(self->array, ast_value)) {
#define AST_FLAG_DEPRECATED (1<<4)
#define AST_FLAG_INCLUDE_DEF (1<<5)
#define AST_FLAG_IS_VARARG (1<<6)
+#define AST_FLAG_ALIAS (1<<7)
#define AST_FLAG_TYPE_MASK (AST_FLAG_VARIADIC | AST_FLAG_NORETURN)
/* Value
bool ast_compare_type(ast_expression *a, ast_expression *b);
ast_expression* ast_type_copy(lex_ctx ctx, const ast_expression *ex);
#define ast_type_adopt(a, b) ast_type_adopt_impl((ast_expression*)(a), (ast_expression*)(b))
-bool ast_type_adopt_impl(ast_expression *self, const ast_expression *other);
+void ast_type_adopt_impl(ast_expression *self, const ast_expression *other);
void ast_type_to_string(ast_expression *e, char *buf, size_t bufsize);
+typedef enum ast_binary_ref_s {
+ AST_REF_LEFT = 1 << 1,
+ AST_REF_RIGHT = 1 << 2,
+ AST_REF_ALL = (AST_REF_LEFT | AST_REF_RIGHT)
+} ast_binary_ref;
+
+
/* Binary
*
* A value-returning binary expression.
int op;
ast_expression *left;
ast_expression *right;
+ ast_binary_ref refs;
+
};
ast_binary* ast_binary_new(lex_ctx ctx,
int op,
};
ast_block* ast_block_new(lex_ctx ctx);
void ast_block_delete(ast_block*);
-bool ast_block_set_type(ast_block*, ast_expression *from);
+void ast_block_set_type(ast_block*, ast_expression *from);
bool ast_block_codegen(ast_block*, ast_function*, bool lvalue, ir_value**);
void ast_block_collect(ast_block*, ast_expression*);
if (lnofile) {
uint32_t version = 1;
- fp = file_open(lnofile, "wb");
+ fp = fs_file_open(lnofile, "wb");
if (!fp)
return false;
util_endianswap(code_linenums, vec_size(code_linenums), sizeof(code_linenums[0]));
- if (file_write("LNOF", 4, 1, fp) != 1 ||
- file_write(&version, sizeof(version), 1, fp) != 1 ||
- file_write(&code_header.defs.length, sizeof(code_header.defs.length), 1, fp) != 1 ||
- file_write(&code_header.globals.length, sizeof(code_header.globals.length), 1, fp) != 1 ||
- file_write(&code_header.fields.length, sizeof(code_header.fields.length), 1, fp) != 1 ||
- file_write(&code_header.statements.length, sizeof(code_header.statements.length), 1, fp) != 1 ||
- file_write(code_linenums, sizeof(code_linenums[0]), vec_size(code_linenums), fp) != vec_size(code_linenums))
+ if (fs_file_write("LNOF", 4, 1, fp) != 1 ||
+ fs_file_write(&version, sizeof(version), 1, fp) != 1 ||
+ fs_file_write(&code_header.defs.length, sizeof(code_header.defs.length), 1, fp) != 1 ||
+ fs_file_write(&code_header.globals.length, sizeof(code_header.globals.length), 1, fp) != 1 ||
+ fs_file_write(&code_header.fields.length, sizeof(code_header.fields.length), 1, fp) != 1 ||
+ fs_file_write(&code_header.statements.length, sizeof(code_header.statements.length), 1, fp) != 1 ||
+ fs_file_write(code_linenums, sizeof(code_linenums[0]), vec_size(code_linenums), fp) != vec_size(code_linenums))
{
con_err("failed to write lno file\n");
}
- file_close(fp);
+ fs_file_close(fp);
fp = NULL;
}
- fp = file_open(filename, "wb");
+ fp = fs_file_open(filename, "wb");
if (!fp)
return false;
- if (1 != file_write(&code_header, sizeof(prog_header) , 1 , fp) ||
- vec_size(code_statements) != file_write(code_statements, sizeof(prog_section_statement), vec_size(code_statements), fp) ||
- vec_size(code_defs) != file_write(code_defs, sizeof(prog_section_def) , vec_size(code_defs) , fp) ||
- vec_size(code_fields) != file_write(code_fields, sizeof(prog_section_field) , vec_size(code_fields) , fp) ||
- vec_size(code_functions) != file_write(code_functions, sizeof(prog_section_function) , vec_size(code_functions) , fp) ||
- vec_size(code_globals) != file_write(code_globals, sizeof(int32_t) , vec_size(code_globals) , fp) ||
- vec_size(code_chars) != file_write(code_chars, 1 , vec_size(code_chars) , fp))
+ if (1 != fs_file_write(&code_header, sizeof(prog_header) , 1 , fp) ||
+ vec_size(code_statements) != fs_file_write(code_statements, sizeof(prog_section_statement), vec_size(code_statements), fp) ||
+ vec_size(code_defs) != fs_file_write(code_defs, sizeof(prog_section_def) , vec_size(code_defs) , fp) ||
+ vec_size(code_fields) != fs_file_write(code_fields, sizeof(prog_section_field) , vec_size(code_fields) , fp) ||
+ vec_size(code_functions) != fs_file_write(code_functions, sizeof(prog_section_function) , vec_size(code_functions) , fp) ||
+ vec_size(code_globals) != fs_file_write(code_globals, sizeof(int32_t) , vec_size(code_globals) , fp) ||
+ vec_size(code_chars) != fs_file_write(code_chars, 1 , vec_size(code_chars) , fp))
{
- file_close(fp);
+ fs_file_close(fp);
return false;
}
vec_free(code_chars);
util_htdel(code_string_cache);
- file_close(fp);
+ fs_file_close(fp);
return true;
}
state = -1;
}
} else {
- file_putc(h, *str);
+ fs_file_putc(h, *str);
length ++;
}
str++;
char data[4096];
memset(data, 0, sizeof(data));
vsnprintf(data, sizeof(data), fmt, va);
- ln = (GMQCC_IS_DEFINE(handle)) ? win_fputs(handle, data) : file_puts(handle, data);
+ ln = (GMQCC_IS_DEFINE(handle)) ? win_fputs(handle, data) : fs_file_puts(handle, data);
}
#endif
return ln;
void con_close() {
if (!GMQCC_IS_DEFINE(console.handle_err))
- file_close(console.handle_err);
+ fs_file_close(console.handle_err);
if (!GMQCC_IS_DEFINE(console.handle_out))
- file_close(console.handle_out);
+ fs_file_close(console.handle_out);
}
void con_color(int state) {
if (GMQCC_IS_DEFINE(out)) {
console.handle_out = GMQCC_IS_STDOUT(out) ? stdout : stderr;
con_enablecolor();
- } else if (!(console.handle_out = file_open(out, "w"))) return 0;
+ } 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;
con_enablecolor();
- } else if (!(console.handle_err = file_open(err, "w"))) return 0;
+ } else if (!(console.handle_err = fs_file_open(err, "w"))) return 0;
/* no buffering */
setvbuf(console.handle_out, NULL, _IONBF, 0);
{
qc_program *prog;
prog_header header;
- FILE *file = file_open(filename, "rb");
+ FILE *file = fs_file_open(filename, "rb");
if (!file)
return NULL;
- if (file_read(&header, sizeof(header), 1, file) != 1) {
+ if (fs_file_read(&header, sizeof(header), 1, file) != 1) {
loaderror("failed to read header from '%s'", filename);
- file_close(file);
+ fs_file_close(file);
return NULL;
}
if (!skipversion && header.version != 6) {
loaderror("header says this is a version %i progs, we need version 6\n", header.version);
- file_close(file);
+ fs_file_close(file);
return NULL;
}
prog = (qc_program*)mem_a(sizeof(qc_program));
if (!prog) {
- file_close(file);
+ fs_file_close(file);
fprintf(stderr, "failed to allocate program data\n");
return NULL;
}
}
#define read_data(hdrvar, progvar, reserved) \
- if (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 (file_read ( \
+ if (fs_file_read ( \
vec_add(prog->progvar, header.hdrvar.length + reserved), \
sizeof(*prog->progvar), \
header.hdrvar.length, \
read_data1(strings);
read_data2(globals, 2); /* reserve more in case a RETURN using with the global at "the end" exists */
- file_close(file);
+ fs_file_close(file);
/* profile counters */
memset(vec_add(prog->profile, vec_size(prog->code)), 0, sizeof(prog->profile[0]) * vec_size(prog->code));
done:
if (len < (int)sizeof(spaces)-1) {
spaces[sizeof(spaces)-1-len] = 0;
- file_puts(stdout, spaces);
+ fs_file_puts(stdout, spaces);
spaces[sizeof(spaces)-1-len] = ' ';
}
}
+++ /dev/null
-/*
- * 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.
- */
-#include "gmqcc.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
-/* {{{ */
- /*
- * 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.
- */
-
- 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");
- abort();
- }
-
- static void file_init() {
- static bool init = false;
-
- if (init)
- return;
-
- _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 *file_open(const char *filename, const char *mode) {
- FILE *handle = NULL;
- file_init();
-
- return (fopen_s(&handle, filename, mode) != 0) ? NULL : handle;
- }
-
- size_t file_read(void *buffer, size_t size, size_t count, FILE *fp) {
- file_init();
- return fread_s(buffer, size*count, size, count, fp);
- }
-
- int 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 *file_open(const char *filename, const char *mode) {
- return fopen(filename, mode);
- }
-
- size_t file_read(void *buffer, size_t size, size_t count, FILE *fp) {
- return fread(buffer, size, count, fp);
- }
-
- int 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 file_close(FILE *fp) {
- /* Invokes file_exception on windows if fp is null */
- fclose (fp);
-}
-
-size_t file_write (
- const void *buffer,
- size_t size,
- size_t count,
- FILE *fp
-) {
- /* Invokes file_exception on windows if fp is null */
- return fwrite(buffer, size, count, fp);
-}
-
-int file_error(FILE *fp) {
- /* Invokes file_exception on windows if fp is null */
- return ferror(fp);
-}
-
-int file_getc(FILE *fp) {
- /* Invokes file_exception on windows if fp is null */
- return fgetc(fp);
-}
-
-int file_puts(FILE *fp, const char *str) {
- /* Invokes file_exception on windows if fp is null */
- return fputs(str, fp);
-}
-
-int file_seek(FILE *fp, long int off, int whence) {
- /* Invokes file_exception on windows if fp is null */
- return fseek(fp, off, whence);
-}
-
-int file_putc(FILE *fp, int ch) {
- /* Invokes file_exception on windows if fp is null */
- return fputc(ch, fp);
-}
-
-/*
- * Implements libc getline for systems that don't have it, which is
- * assmed all. This works the same as getline().
- */
-int file_getline(char **lineptr, size_t *n, FILE *stream) {
- int chr;
- int ret;
- char *pos;
-
- if (!lineptr || !n || !stream)
- return -1;
- if (!*lineptr) {
- if (!(*lineptr = (char*)mem_a((*n=64))))
- return -1;
- }
-
- chr = *n;
- pos = *lineptr;
-
- for (;;) {
- int c = file_getc(stream);
-
- if (chr < 2) {
- *n += (*n > 16) ? *n : 64;
- chr = *n + *lineptr - pos;
- if (!(*lineptr = (char*)mem_r(*lineptr,*n)))
- return -1;
- pos = *n - chr + *lineptr;
- }
-
- if (ferror(stream))
- return -1;
- if (c == EOF) {
- if (pos == *lineptr)
- return -1;
- else
- break;
- }
-
- *pos++ = c;
- chr--;
- if (c == '\n')
- break;
- }
- *pos = '\0';
- return (ret = pos - *lineptr);
-}
--- /dev/null
+/*
+ * 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.
+ */
+#include "gmqcc.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
+/* {{{ */
+ /*
+ * 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.
+ */
+
+ 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");
+ abort();
+ }
+
+ static void file_init() {
+ static bool init = false;
+
+ if (init)
+ return;
+
+ _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);
+}
+
+size_t fs_file_write (
+ const void *buffer,
+ size_t size,
+ size_t count,
+ FILE *fp
+) {
+ /* Invokes file_exception on windows if fp is null */
+ return fwrite(buffer, size, count, fp);
+}
+
+int fs_file_error(FILE *fp) {
+ /* Invokes file_exception on windows if fp is null */
+ return ferror(fp);
+}
+
+int fs_file_getc(FILE *fp) {
+ /* Invokes file_exception on windows if fp is null */
+ return fgetc(fp);
+}
+
+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_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_putc(FILE *fp, int ch) {
+ /* Invokes file_exception on windows if fp is null */
+ return fputc(ch, fp);
+}
+
+int fs_file_flush(FILE *fp) {
+ /* Invokes file_exception on windows if fp is null */
+ return fflush(fp);
+}
+
+long int fs_file_tell(FILE *fp) {
+ /* Invokes file_exception on windows if fp is null */
+ return ftell(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 chr;
+ int ret;
+ char *pos;
+
+ if (!lineptr || !n || !stream)
+ return -1;
+ if (!*lineptr) {
+ if (!(*lineptr = (char*)mem_a((*n=64))))
+ return -1;
+ }
+
+ chr = *n;
+ pos = *lineptr;
+
+ for (;;) {
+ int c = fs_file_getc(stream);
+
+ if (chr < 2) {
+ *n += (*n > 16) ? *n : 64;
+ chr = *n + *lineptr - pos;
+ if (!(*lineptr = (char*)mem_r(*lineptr,*n)))
+ return -1;
+ pos = *n - chr + *lineptr;
+ }
+
+ if (ferror(stream))
+ return -1;
+ if (c == EOF) {
+ if (pos == *lineptr)
+ return -1;
+ else
+ break;
+ }
+
+ *pos++ = c;
+ chr--;
+ if (c == '\n')
+ break;
+ }
+ *pos = '\0';
+ 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;
+
+ strcpy(dir->dd_name, 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 */)) {
+ strcpy(dirname, dir->dd_name);
+ strcpy(dirname + n, "\\*.*"); /* 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)))) {
+ 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);
+ }
+
+ /*
+ * Visual studio also lacks S_ISDIR for sys/stat.h, so we emulate this as well
+ * which is not hard at all.
+ */
+# undef S_ISDIR
+# define S_ISDIR(X) ((X)&_S_IFDIR)
+#else
+# if !defined(__MINGW32__)
+# include <sys/stat.h> /* mkdir */
+# include <unistd.h> /* chdir */
+
+ 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_close(DIR *dir) {
+ return closedir(dir);
+}
+
+struct dirent *fs_dir_read(DIR *dir) {
+ return readdir(dir);
+}
+
+int fs_dir_change(const char *path) {
+ return chdir(path);
+}
+
+#endif /*! defined(_WIN32) && !defined(__MINGW32__) */
char *ftepp_predef_file(lex_file *context) {
size_t length = strlen(context->name) + 3; /* two quotes and a terminator */
char *value = (char*)mem_a(length);
- memset (value, 0, length);
sprintf(value, "\"%s\"", context->name);
return value;
ftepp->token = old;
}
}
- else
- {
+ else if (macro->variadic && !strcmp(ftepp_tokval(ftepp), "__VA_COUNT__")) {
+ ftepp->token = TOKEN_VA_COUNT;
+ ptok = pptoken_make(ftepp);
+ vec_push(macro->output, ptok);
+ ftepp_next(ftepp);
+ } else {
ptok = pptoken_make(ftepp);
vec_push(macro->output, ptok);
ftepp_next(ftepp);
static bool ftepp_preprocess(ftepp_t *ftepp);
static bool ftepp_macro_expand(ftepp_t *ftepp, ppmacro *macro, macroparam *params, bool resetline)
{
+ char *buffer = NULL;
char *old_string = ftepp->output_string;
char *inner_string;
lex_file *old_lexer = ftepp->lex;
ftepp_param_out(ftepp, ¶ms[out->constval.i + vararg_start]);
break;
+ case TOKEN_VA_COUNT:
+ util_asprintf(&buffer, "%d", varargs);
+ ftepp_out(ftepp, buffer, false);
+ mem_d(buffer);
+ break;
+
case TOKEN_IDENT:
case TOKEN_TYPENAME:
case TOKEN_KEYWORD:
memcpy(vec_add(filename, len+1), file, len);
vec_last(filename) = 0;
- fp = file_open(filename, "rb");
+ fp = fs_file_open(filename, "rb");
if (fp) {
- file_close(fp);
+ fs_file_close(fp);
return filename;
}
vec_free(filename);
#ifdef _MSC_VER
# pragma warning(disable : 4244 ) /* conversion from 'int' to 'float', possible loss of data */
# pragma warning(disable : 4018 ) /* signed/unsigned mismatch */
-#endif
+#endif /*! _MSC_VER */
#define GMQCC_VERSION_MAJOR 0
#define GMQCC_VERSION_MINOR 3
# define GMQCC_DEV_VERSION_STRING "development build\n"
#else
# define GMQCC_DEV_VERSION_STRING
-#endif
+#endif /*! GMQCC_GITINGO */
#define GMQCC_STRINGIFY(x) #x
#define GMQCC_IND_STRING(x) GMQCC_STRINGIFY(x)
#ifndef __cplusplus
# ifdef false
# undef false
-# endif /* !false */
+# endif /*! false */
# ifdef true
# undef true
-# endif /* !true */
+# endif /*! true */
# define false (0)
# define true (1)
# ifdef __STDC_VERSION__
typedef int bool;
# else
typedef _Bool bool;
-# endif
+# endif /*! __STDC_VERSION__ < 199901L && __GNUC__ < 3 */
# else
typedef int bool;
-# endif /* !__STDC_VERSION__ */
-#endif /* !__cplusplus */
+# endif /*! __STDC_VERSION__ */
+#endif /*! __cplusplus */
/*
* Of some functions which are generated we want to make sure
#else
# define GMQCC_WARN
# define GMQCC_USED
-#endif
+#endif /*! defined(__GNUC__) || defined (__CLANG__) */
/*
* This is a hack to silent clang regarding empty
* body if statements.
# define GMQCC_INLINE
# else
# define GMQCC_INLINE __attribute__ ((always_inline))
-# endif
+# endif /*! __GNUC__ < 2 */
# else
# define GMQCC_INLINE
-# endif
+# endif /*! defined(__GNUC__) || defined (__CLANG__) */
# else
# define GMQCC_INLINE inline
-# endif
+# endif /*! __STDC_VERSION < 199901L */
/*
* Visual studio has __forcinline we can use. So lets use that
* I suspect it also has just __inline of some sort, but our use
# define GMQCC_INLINE __forceinline
#else
# define GMQCC_INLINE
-#endif /* !__STDC_VERSION__ */
+#endif /*! __STDC_VERSION__ */
/*
* noreturn is present in GCC and clang
# define GMQCC_NORETURN __attribute__ ((noreturn))
#else
# define GMQCC_NORETURN
-#endif
+#endif /*! (defined(__GNUC__) && __GNUC__ >= 2) || defined (__CLANG__) */
#ifndef _MSC_VER
# include <stdint.h>
typedef __int16 int16_t;
typedef __int32 int32_t;
typedef __int64 int64_t;
-#endif
+#endif /*! _MSC_VER */
/*
*windows makes these prefixed because they're C99
# define snprintf(X, Y, Z, ...) _snprintf(X, Y, Z, __VA_ARGS__)
/* strtof doesn't exist -> strtod does though :) */
# define strtof(X, Y) (float)(strtod(X, Y))
-#endif
+#endif /*! _MSC_VER */
/*
* Very roboust way at determining endianess at compile time: this handles
# define BIG_ENDIAN
# elif defined (__LITTLE_ENDIAN__) && !defined (LITTLE_ENDIAN)
# define LITTLE_ENDIAN
-# endif
+# endif /*! defined (__BIG_ENDIAN__) && !defined(BIG_ENDIAN) */
# elif !defined (__MINGW32__)
# include <endian.h>
# if !defined (__BEOS__)
# include <byteswap.h>
-# endif
-# endif
-#endif
+# endif /*! !definde (__BEOS__) */
+# endif /*! defined (__FreeBSD__) || defined (__OpenBSD__) */
+#endif /*! defined (__GNUC__) || defined (__GNU_LIBRARY__) */
#if !defined(PLATFORM_BYTE_ORDER)
# if defined (LITTLE_ENDIAN) || defined (BIG_ENDIAN)
# if defined (LITTLE_ENDIAN) && !defined(BIG_ENDIAN)
# define PLATFORM_BYTE_ORDER GMQCC_BYTE_ORDER_LITTLE
# elif defined (BYTE_ORDER) && (BYTE_ORDER == BIG_ENDIAN)
# define PLATFORM_BYTE_ORDER GMQCC_BYTE_ORDER_BIG
-# endif
+# endif /*! defined (LITTLE_ENDIAN) && !defined(BIG_ENDIAN) */
# elif defined (_LITTLE_ENDIAN) || defined (_BIG_ENDIAN)
# if defined (_LITTLE_ENDIAN) && !defined(_BIG_ENDIAN)
# define PLATFORM_BYTE_ORDER GMQCC_BYTE_ORDER_LITTLE
# define PLATFORM_BYTE_ORDER GMQCC_BYTE_ORDER_LITTLE
# elif defined (_BYTE_ORDER) && (_BYTE_ORDER == _BIG_ENDIAN)
# define PLATFORM_BYTE_ORDER GMQCC_BYTE_ORDER_BIG
-# endif
+# endif /*! defined (_LITTLE_ENDIAN) && !defined(_BIG_ENDIAN) */
# elif defined (__LITTLE_ENDIAN__) || defined (__BIG_ENDIAN__)
# if defined (__LITTLE_ENDIAN__) && !defined (__BIG_ENDIAN__)
# define PLATFORM_BYTE_ORDER GMQCC_BYTE_ORDER_LITTLE
# define PLATFORM_BYTE_ORDER GMQCC_BYTE_ORDER_LITTLE
# elif defined (__BYTE_ORDER__) && (__BYTE_ORDER__ == __BIG_ENDIAN__)
# define PLATFORM_BYTE_ORDER GMQCC_BYTE_ORDER_BIG
-# endif
-# endif
-#endif
+# endif /*! defined (__LITTLE_ENDIAN__) && !defined (__BIG_ENDIAN__) */
+# endif /*! defined(LITTLE_ENDIAN) || defined (BIG_ENDIAN) */
+#endif /*! !defined(PLATFORM_BYTE_ORDER) */
#if !defined (PLATFORM_BYTE_ORDER)
# if defined (__alpha__) || defined (__alpha) || defined (i386) || \
defined (__i386__) || defined (_M_I86) || defined (_M_IX86) || \
# else
# define PLATFORM_BYTE_ORDER -1
# endif
-#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;
+#else
+# include <dirent.h>
+#endif /*! _WIN32 && !defined(__MINGW32__) */
/*===================================================================*/
# define mem_a(x) util_memory_a((x), __LINE__, __FILE__)
# define mem_d(x) util_memory_d((void*)(x))
# define mem_r(x, n) util_memory_r((void*)(x), (n), __LINE__, __FILE__)
-#endif
+#endif /*! NOTRACK */
/*
* A flexible vector implementation: all vector pointers contain some
/*===================================================================*/
/*============================ file.c ===============================*/
/*===================================================================*/
-GMQCC_INLINE void file_close (FILE *);
-GMQCC_INLINE int file_error (FILE *);
-GMQCC_INLINE int file_getc (FILE *);
-GMQCC_INLINE int file_printf (FILE *, const char *, ...);
-GMQCC_INLINE int file_puts (FILE *, const char *);
-GMQCC_INLINE int file_putc (FILE *, int);
-GMQCC_INLINE int file_seek (FILE *, long int, int);
-
-GMQCC_INLINE size_t file_read (void *, size_t, size_t, FILE *);
-GMQCC_INLINE size_t file_write (const void *, size_t, size_t, FILE *);
-
-GMQCC_INLINE FILE *file_open (const char *, const char *);
-/*NOINLINE*/ int file_getline(char **, size_t *, FILE *);
+/* file handling */
+void fs_file_close (FILE *);
+int fs_file_error (FILE *);
+int fs_file_getc (FILE *);
+int fs_file_flush (FILE *);
+int fs_file_printf (FILE *, const char *, ...);
+int fs_file_puts (FILE *, const char *);
+int fs_file_putc (FILE *, int);
+int fs_file_seek (FILE *, long int, int);
+long int fs_file_tell (FILE *);
+
+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 *);
+
+/* directory handling */
+DIR *fs_dir_open (const char *);
+int fs_dir_close (DIR *);
+struct dirent *fs_dir_read (DIR *);
+int fs_dir_make (const char *);
+int fs_dir_change (const char *);
/*===================================================================*/
#define OPTS_OPTION_U32(X) (opts.options[X].U32)
#define OPTS_OPTION_STR(X) (opts.options[X].STR)
-#endif
+#endif /*! GMQCC_HDR */
lex_file* lex_open(const char *file)
{
lex_file *lex;
- FILE *in = file_open(file, "rb");
+ FILE *in = fs_file_open(file, "rb");
if (!in) {
lexerror(NULL, "open failed: '%s'\n", file);
lex = (lex_file*)mem_a(sizeof(*lex));
if (!lex) {
- file_close(in);
+ fs_file_close(in);
lexerror(NULL, "out of memory\n");
return NULL;
}
vec_free(lex->modelname);
if (lex->file)
- file_close(lex->file);
+ fs_file_close(lex->file);
#if 0
if (lex->tok)
token_delete(lex->tok);
if (ch == '+' || ch == '-' || /* ++, --, +=, -= and -> as well! */
ch == '>' || ch == '<' || /* <<, >>, <=, >= */
- ch == '=' || ch == '!' || /* ==, != */
+ ch == '=' || ch == '!' || /* <=>, ==, != */
ch == '&' || ch == '|' || /* &&, ||, &=, |= */
ch == '~' /* ~=, ~ */
) {
lex_tokench(lex, ch);
nextch = lex_getch(lex);
- if (nextch == '=' || (nextch == ch && ch != '!')) {
+ if ((nextch == '=' && ch != '<') || (nextch == ch && ch != '!')) {
lex_tokench(lex, nextch);
+ } else if (ch == '<' && nextch == '=') {
+ lex_tokench(lex, nextch);
+ if ((thirdch = lex_getch(lex)) == '>')
+ lex_tokench(lex, thirdch);
+ else
+ lex_ungetch(lex, thirdch);
+
} else if (ch == '-' && nextch == '>') {
lex_tokench(lex, nextch);
} else if (ch == '&' && nextch == '~') {
lex->tok.constval.f = -lex->tok.constval.f;
lex_endtoken(lex);
return lex->tok.ttype;
- } else
+ } else {
lex_ungetch(lex, nextch);
+ }
lex_endtoken(lex);
return (lex->tok.ttype = TOKEN_OPERATOR);
TOKEN_VA_ARGS, /* for the ftepp only */
TOKEN_VA_ARGS_ARRAY, /* for the ftepp only */
+ TOKEN_VA_COUNT, /* to get the count of vaargs */
TOKEN_STRINGCONST, /* not the typename but an actual "string" */
TOKEN_CHARCONST,
{ "<", 2, opid1('<'), ASSOC_LEFT, 10, 0 },
{ ">", 2, opid1('>'), ASSOC_LEFT, 10, 0 },
+ { "<=>", 2, opid3('<','=','>'), ASSOC_LEFT, 10, 0 },
{ "<=", 2, opid2('<','='), ASSOC_LEFT, 10, 0 },
{ ">=", 2, opid2('>','='), ASSOC_LEFT, 10, 0 },
char *end;
line = *out;
- len = file_getline(&line, alen, src);
+ len = fs_file_getline(&line, alen, src);
if (len == -1)
return false;
if (OPTS_OPTION_BOOL(OPTION_PP_ONLY)) {
if (opts_output_wasset) {
- outfile = file_open(OPTS_OPTION_STR(OPTION_OUTPUT), "wb");
+ outfile = fs_file_open(OPTS_OPTION_STR(OPTION_OUTPUT), "wb");
if (!outfile) {
con_err("failed to open `%s` for writing\n", OPTS_OPTION_STR(OPTION_OUTPUT));
retval = 1;
progs_src = true;
- src = file_open("progs.src", "rb");
+ src = fs_file_open("progs.src", "rb");
if (!src) {
con_err("failed to open `progs.src` for reading\n");
retval = 1;
}
srcdone:
- file_close(src);
+ fs_file_close(src);
mem_d(line);
}
}
out = ftepp_get();
if (out)
- file_printf(outfile, "%s", out);
+ fs_file_printf(outfile, "%s", out);
ftepp_flush();
}
else {
char *read_name;
char *read_value;
- while (file_getline(&line, &linesize, filehandle) != EOF) {
+ while (fs_file_getline(&line, &linesize, filehandle) != EOF) {
parse_beg = line;
/* handle BOM */
if (!file) {
/* try ini */
- if (!(ini = file_open((file = "gmqcc.ini"), "r")))
+ if (!(ini = fs_file_open((file = "gmqcc.ini"), "r")))
/* try cfg */
- if (!(ini = file_open((file = "gmqcc.cfg"), "r")))
+ if (!(ini = fs_file_open((file = "gmqcc.cfg"), "r")))
return;
- } else if (!(ini = file_open(file, "r")))
+ } else if (!(ini = fs_file_open(file, "r")))
return;
con_out("found ini file `%s`\n", file);
vec_free(error);
}
- file_close(ini);
+ fs_file_close(ini);
}
--- /dev/null
+/*
+ * Copyright (C) 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.
+ */
+#include "gmqcc.h"
+
+/*
+ * The PAK format uses a FOURCC concept for storing the magic ident within
+ * the header as a uint32_t.
+ */
+#define PAK_FOURCC ((uint32_t)(('P' | ('A' << 8) | ('C' << 16) | ('K' << 24))))
+
+typedef struct {
+ uint32_t magic; /* "PACK" */
+
+ /*
+ * Offset to first directory entry in PAK file. It's often
+ * best to store the directories at the end of the file opposed
+ * to the front, since it allows easy insertion without having
+ * to load the entire file into memory again.
+ */
+ uint32_t diroff;
+ uint32_t dirlen;
+} pak_header_t;
+
+/*
+ * A directory, is sort of a "file entry". The concept of
+ * a directory in Quake world is a "file entry/record". This
+ * describes a file (with directories/nested ones too in it's
+ * file name). Hence it can be a file, file with directory, or
+ * file with directories.
+ */
+typedef struct {
+ char name[56];
+ uint32_t pos;
+ uint32_t len;
+} pak_directory_t;
+
+/*
+ * Used to get the next token from a string, where the
+ * strings themselfs are seperated by chracters from
+ * `sep`. This is essentially strsep.
+ */
+static char *pak_tree_sep(char **str, const char *sep) {
+ char *beg = *str;
+ char *end;
+
+ if (!beg)
+ return NULL;
+
+ if (*(end = beg + strcspn(beg, sep)))
+ * end++ = '\0'; /* null terminate */
+ else
+ end = 0;
+
+ *str = end;
+ return beg;
+}
+
+/*
+ * When given a string like "a/b/c/d/e/file"
+ * this function will handle the creation of
+ * the directory structure, included nested
+ * directories.
+ */
+static void pak_tree_build(const char *entry) {
+ char *directory;
+ char *elements[28];
+ char *pathsplit;
+ char *token;
+
+ size_t itr;
+ size_t jtr;
+
+ pathsplit = (char *)mem_a(56);
+ directory = (char *)mem_a(56);
+
+ memset(pathsplit, 0, 56);
+
+ strncpy(directory, entry, 56);
+ for (itr = 0; (token = pak_tree_sep(&directory, "/")) != NULL; itr++) {
+ elements[itr] = token;
+ }
+
+ for (jtr = 0; jtr < itr - 1; jtr++) {
+ strcat(pathsplit, elements[jtr]);
+ strcat(pathsplit, "/");
+
+ if (fs_dir_make(pathsplit)) {
+ mem_d(pathsplit);
+ mem_d(directory);
+
+ /* TODO: undo on fail */
+
+ return;
+ }
+ }
+
+ mem_d(pathsplit);
+ mem_d(directory);
+}
+
+typedef struct {
+ pak_directory_t *directories;
+ pak_header_t header;
+ FILE *handle;
+ bool insert;
+} pak_file_t;
+
+static pak_file_t *pak_open_read(const char *file) {
+ pak_file_t *pak;
+ size_t itr;
+
+ if (!(pak = (pak_file_t*)mem_a(sizeof(pak_file_t))))
+ return NULL;
+
+ if (!(pak->handle = fs_file_open(file, "rb"))) {
+ mem_d(pak);
+ return NULL;
+ }
+
+ pak->directories = NULL;
+ pak->insert = false; /* read doesn't allow insert */
+
+ memset (&pak->header, 0, sizeof(pak_header_t));
+ fs_file_read (&pak->header, sizeof(pak_header_t), 1, pak->handle);
+ util_endianswap(&pak->header, 1, sizeof(pak_header_t));
+
+ /*
+ * Every PAK file has "PACK" stored as FOURCC data in the
+ * header. If this data cannot compare (as checked here), it's
+ * probably not a PAK file.
+ */
+ if (pak->header.magic != PAK_FOURCC) {
+ fs_file_close(pak->handle);
+ mem_d (pak);
+ return NULL;
+ }
+
+ /*
+ * 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);
+
+ /*
+ * Read in all directories from the PAK file. These are considered
+ * to be the "file entries".
+ */
+ for (itr = 0; itr < pak->header.dirlen / 64; itr++) {
+ pak_directory_t dir;
+ fs_file_read (&dir, sizeof(pak_directory_t), 1, pak->handle);
+ util_endianswap(&dir, 1, sizeof(pak_directory_t));
+
+ vec_push(pak->directories, dir);
+ }
+ return pak;
+}
+
+static pak_file_t *pak_open_write(const char *file) {
+ pak_file_t *pak;
+
+ if (!(pak = (pak_file_t*)mem_a(sizeof(pak_file_t))))
+ return NULL;
+
+ /*
+ * Generate the required directory structure / tree for
+ * writing this PAK file too.
+ */
+ pak_tree_build(file);
+
+ if (!(pak->handle = fs_file_open(file, "wb"))) {
+ /*
+ * The directory tree that was created, needs to be
+ * removed entierly if we failed to open a file.
+ */
+ /* TODO backup directory clean */
+
+ mem_d(pak);
+ return NULL;
+ }
+
+ memset(&(pak->header), 0, sizeof(pak_header_t));
+
+ /*
+ * We're in "insert" mode, we need to do things like header
+ * "patching" and writing the directories at the end of the
+ * file.
+ */
+ pak->insert = true;
+ pak->header.magic = PAK_FOURCC;
+
+ /* on BE systems we need to swap the byte order of the FOURCC */
+ util_endianswap(&pak->header.magic, 1, sizeof(uint32_t));
+
+ /*
+ * We need to write out the header since files will be wrote out to
+ * this even with directory entries, and that not wrote. The header
+ * will need to be patched in later with a file_seek, and overwrite,
+ * we could use offsets and other trickery. This is just easier.
+ */
+ fs_file_write(&(pak->header), sizeof(pak_header_t), 1, pak->handle);
+
+ return pak;
+}
+
+pak_file_t *pak_open(const char *file, const char *mode) {
+ if (!file || !mode)
+ return NULL;
+
+ switch (*mode) {
+ case 'r': return pak_open_read (file);
+ case 'w': return pak_open_write(file);
+ }
+
+ return NULL;
+}
+
+bool pak_exists(pak_file_t *pak, const char *file, pak_directory_t **dir) {
+ size_t itr;
+
+ if (!pak || !file)
+ return false;
+
+ for (itr = 0; itr < vec_size(pak->directories); itr++) {
+ if (!strcmp(pak->directories[itr].name, file)) {
+ /*
+ * Store back a pointer to the directory that matches
+ * the request if requested (NULL is not allowed).
+ */
+ if (dir) {
+ *dir = &(pak->directories[itr]);
+ }
+ return true;
+ }
+ }
+
+ return false;
+}
+
+/*
+ * Extraction abilities. These work as you expect them to.
+ */
+bool pak_extract_one(pak_file_t *pak, const char *file) {
+ pak_directory_t *dir = NULL;
+ unsigned char *dat = NULL;
+ FILE *out;
+
+ if (!pak_exists(pak, file, &dir)) {
+ return false;
+ }
+
+ if (!(dat = (unsigned char *)mem_a(dir->len))) {
+ return false;
+ }
+
+ /*
+ * Generate the directory structure / tree that will be required
+ * to store the extracted file.
+ */
+ pak_tree_build(file);
+
+ /*
+ * Now create the file, if this operation fails. Then abort
+ * It shouldn't fail though.
+ */
+ if (!(out = fs_file_open(file, "wb"))) {
+ mem_d(dat);
+ return false;
+ }
+
+
+ /* read */
+ fs_file_seek (pak->handle, dir->pos, SEEK_SET);
+ fs_file_read (dat, 1, dir->len, pak->handle);
+
+ /* write */
+ fs_file_write(dat, 1, dir->len, out);
+
+ /* close */
+ fs_file_close(out);
+
+ /* free */
+ mem_d(dat);
+
+ return true;
+}
+
+bool pak_extract_all(pak_file_t *pak, const char *dir) {
+ size_t itr;
+
+ if (!fs_dir_make(dir))
+ return false;
+
+ if (fs_dir_change(dir))
+ return false;
+
+ for (itr = 0; itr < vec_size(pak->directories); itr++) {
+ if (!pak_extract_one(pak, pak->directories[itr].name))
+ return false;
+ }
+
+ return true;
+}
+
+/*
+ * Insertion functions (the opposite of extraction). Yes for generating
+ * PAKs.
+ */
+bool pak_insert_one(pak_file_t *pak, const char *file) {
+ pak_directory_t dir;
+ unsigned char *dat;
+ FILE *fp;
+
+ /*
+ * We don't allow insertion on files that already exist within the
+ * pak file. Weird shit can happen if we allow that ;). We also
+ * don't allow insertion if the pak isn't opened in write mode.
+ */
+ if (!pak || !file || !pak->insert || pak_exists(pak, file, NULL))
+ return false;
+
+ if (!(fp = fs_file_open(file, "rb")))
+ return false;
+
+ /*
+ * Calculate the total file length, since it will be wrote to
+ * the directory entry, and the actual contents of the file
+ * to the PAK file itself.
+ */
+ fs_file_seek(fp, 0, SEEK_END);
+ dir.len = fs_file_tell(fp);
+ fs_file_seek(fp, 0, SEEK_SET);
+
+ dir.pos = fs_file_tell(pak->handle);
+
+ /*
+ * We're limited to 56 bytes for a file name string, that INCLUDES
+ * the directory and '/' seperators.
+ */
+ if (strlen(file) >= 56) {
+ fs_file_close(fp);
+ return false;
+ }
+
+ strcpy(dir.name, file);
+
+ /*
+ * Allocate some memory for loading in the data that will be
+ * redirected into the PAK file.
+ */
+ if (!(dat = (unsigned char *)mem_a(dir.len))) {
+ fs_file_close(fp);
+ return false;
+ }
+
+ fs_file_read (dat, dir.len, 1, fp);
+ fs_file_close(fp);
+ fs_file_write(dat, dir.len, 1, pak->handle);
+
+ /*
+ * Now add the directory to the directories vector, so pak_close
+ * can actually write it.
+ */
+ vec_push(pak->directories, dir);
+
+ return true;
+}
+
+/*
+ * Like pak_insert_one, except this collects files in all directories
+ * from a root directory, and inserts them all.
+ */
+bool pak_insert_all(pak_file_t *pak, const char *dir) {
+ DIR *dp;
+ struct dirent *dirp;
+
+ if (!(pak->insert))
+ return false;
+
+ if (!(dp = fs_dir_open(dir)))
+ return false;
+
+ while ((dirp = fs_dir_read(dp))) {
+ if (!(pak_insert_one(pak, dirp->d_name))) {
+ fs_dir_close(dp);
+ return false;
+ }
+ }
+
+ fs_dir_close(dp);
+ return true;
+}
+
+bool pak_close(pak_file_t *pak) {
+ size_t itr;
+
+ if (!pak)
+ return false;
+
+ /*
+ * In insert mode we need to patch the header, and write
+ * our directory entries at the end of the file.
+ */
+ if (pak->insert) {
+ pak->header.dirlen = vec_size(pak->directories) * 64;
+ pak->header.diroff = ftell(pak->handle);
+
+ /* patch header */
+ fs_file_seek (pak->handle, 0, SEEK_SET);
+ fs_file_write(&(pak->header), sizeof(pak_header_t), 1, pak->handle);
+
+ /* write directories */
+ fs_file_seek (pak->handle, pak->header.diroff, SEEK_SET);
+
+ 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);
+ fs_file_close(pak->handle);
+ mem_d (pak);
+
+ return true;
+}
+
+/*
+ * Fancy GCC-like LONG parsing allows things like --opt=param with
+ * assignment operator. This is used for redirecting stdout/stderr
+ * console to specific files of your choice.
+ */
+static bool parsecmd(const char *optname, int *argc_, char ***argv_, char **out, int ds, bool split) {
+ int argc = *argc_;
+ char **argv = *argv_;
+
+ size_t len = strlen(optname);
+
+ if (strncmp(argv[0]+ds, optname, len))
+ return false;
+
+ /* it's --optname, check how the parameter is supplied */
+ if (argv[0][ds+len] == '=') {
+ *out = argv[0]+ds+len+1;
+ return true;
+ }
+
+ if (!split || argc < ds) /* no parameter was provided, or only single-arg form accepted */
+ return false;
+
+ /* using --opt param */
+ *out = argv[1];
+ --*argc_;
+ ++*argv_;
+ return true;
+}
+
+int main(int argc, char **argv) {
+ bool extract = true;
+ char *redirout = (char*)stdout;
+ char *redirerr = (char*)stderr;
+ char *directory = NULL;
+ char *file = NULL;
+ char **files = NULL;
+ pak_file_t *pak = NULL;
+ size_t iter = 0;
+
+ con_init();
+
+ /*
+ * Command line option parsing commences now We only need to support
+ * a few things in the test suite.
+ */
+ while (argc > 1) {
+ ++argv;
+ --argc;
+
+ if (argv[0][0] == '-') {
+ if (parsecmd("redirout", &argc, &argv, &redirout, 1, false))
+ continue;
+ if (parsecmd("redirerr", &argc, &argv, &redirerr, 1, false))
+ continue;
+ if (parsecmd("directory", &argc, &argv, &directory, 1, false))
+ continue;
+ if (parsecmd("file", &argc, &argv, &file, 1, false))
+ continue;
+
+ con_change(redirout, redirerr);
+
+ switch (argv[0][1]) {
+ case 'e': extract = true; continue;
+ case 'c': extract = false; continue;
+ }
+
+ if (!strcmp(argv[0]+1, "debug")) {
+ OPTS_OPTION_BOOL(OPTION_DEBUG) = true;
+ continue;
+ }
+ if (!strcmp(argv[0]+1, "memchk")) {
+ OPTS_OPTION_BOOL(OPTION_MEMCHK) = true;
+ continue;
+ }
+ if (!strcmp(argv[0]+1, "nocolor")) {
+ con_color(0);
+ continue;
+ }
+ }
+
+ vec_push(files, argv[0]);
+ }
+ con_change(redirout, redirerr);
+
+
+ if (!file) {
+ con_err("-file must be specified for output/input PAK file\n");
+ vec_free(files);
+ return EXIT_FAILURE;
+ }
+
+ if (extract) {
+ if (!(pak = pak_open(file, "r"))) {
+ con_err("failed to open PAK file %s\n", file);
+ vec_free(files);
+ return EXIT_FAILURE;
+ }
+
+ if (!pak_extract_all(pak, (directory) ? directory : "./")) {
+ con_err("failed to extract PAK %s (files may be missing)\n", file);
+ pak_close(pak);
+ vec_free(files);
+ return EXIT_FAILURE;
+ }
+
+ /* not possible */
+ pak_close(pak);
+ vec_free(files);
+ util_meminfo();
+ return EXIT_SUCCESS;
+ }
+
+ if (!(pak = pak_open(file, "w"))) {
+ con_err("failed to open PAK %s for writing\n", file);
+ vec_free(files);
+ return EXIT_FAILURE;
+ }
+
+ if (directory && !fs_dir_change(directory)) {
+ con_err("failed to change directory %s\n", directory);
+ pak_close(pak);
+ vec_free(files);
+ return EXIT_FAILURE;
+ }
+
+ for (iter = 0; iter < vec_size(files); iter++) {
+ if (!(pak_insert_one(pak, files[iter]))) {
+ con_err("failed inserting %s for PAK %s\n", files[iter], file);
+ pak_close(pak);
+ vec_free(files);
+ return EXIT_FAILURE;
+ }
+ }
+
+ /* not possible */
+ pak_close(pak);
+ vec_free(files);
+
+
+ util_meminfo();
+ return EXIT_SUCCESS;
+}
size_t crc_fields;
ast_function *function;
+ ht aliases;
/* All the labels the function defined...
* Should they be in ast_function instead?
static ast_expression* parser_find_global(parser_t *parser, const char *name)
{
+ ast_expression *var = (ast_expression*)util_htget(parser->aliases, parser_tokval(parser));
+ if (var)
+ return var;
return (ast_expression*)util_htget(parser->htglobals, name);
}
return false;
}
}
- if (!ast_block_set_type(blocks[0], exprs[1]))
- return false;
+ ast_block_set_type(blocks[0], exprs[1]);
vec_push(sy->out, syblock(ctx, blocks[0]));
return true;
out = (ast_expression*)ast_ternary_new(ctx, exprs[0], exprs[1], exprs[2]);
break;
+ case opid3('<','=','>'): /* -1, 0, or 1 */
+ if (NotSameType(TYPE_FLOAT)) {
+ ast_type_to_string(exprs[0], ty1, sizeof(ty1));
+ ast_type_to_string(exprs[1], ty2, sizeof(ty2));
+ compile_error(ctx, "invalid types used in comparision: %s and %s",
+ ty1, ty2);
+
+ return false;
+ }
+
+ if (CanConstFold(exprs[0], exprs[1])) {
+ if (ConstF(0) < ConstF(1))
+ out = (ast_expression*)parser_const_float_neg1(parser);
+ else if (ConstF(0) == ConstF(1))
+ out = (ast_expression*)parser_const_float_0(parser);
+ else if (ConstF(0) > ConstF(1))
+ out = (ast_expression*)parser_const_float_1(parser);
+ } else {
+ ast_binary *eq = ast_binary_new(ctx, INSTR_EQ_F, exprs[0], exprs[1]);
+
+ eq->refs = (ast_binary_ref)false; /* references nothing */
+
+ /* if (lt) { */
+ out = (ast_expression*)ast_ternary_new(ctx,
+ (ast_expression*)ast_binary_new(ctx, INSTR_LT, exprs[0], exprs[1]),
+ /* out = -1 */
+ (ast_expression*)parser_const_float_neg1(parser),
+ /* } else { */
+ /* if (eq) { */
+ (ast_expression*)ast_ternary_new(ctx, (ast_expression*)eq,
+ /* out = 0 */
+ (ast_expression*)parser_const_float_0(parser),
+ /* } else { */
+ /* out = 1 */
+ (ast_expression*)parser_const_float_1(parser)
+ /* } */
+ )
+ /* } */
+ );
+
+ }
+ break;
+
case opid1('>'):
generated_op += 1; /* INSTR_GT */
case opid1('<'):
else
out = (ast_expression*)ast_binary_new(ctx, INSTR_SUB_F, (ast_expression*)parser_const_float_neg1(parser), exprs[0]);
break;
-
}
#undef NotSameType
if (!strcmp(parser_tokval(parser), "__builtin_debug_typestring")) {
var = (ast_expression*)intrinsic_debug_typestring;
}
- else
- {
+
+ if (!var) {
char *correct = NULL;
size_t i;
}
}
if (o == operator_count) {
- /* no operator found... must be the end of the statement */
- break;
+ compile_error(parser_ctx(parser), "unknown operator: %s", parser_tokval(parser));
+ goto onerr;
}
/* found an operator */
op = &operators[o];
return false;
}
}
+ else if (!strcmp(parser_tokval(parser), "alias") && !(flags & AST_FLAG_ALIAS)) {
+ flags |= AST_FLAG_ALIAS;
+ *message = NULL;
+
+ if (!parser_next(parser)) {
+ parseerror(parser, "parse error in attribute");
+ goto argerr;
+ }
+
+ if (parser->tok == '(') {
+ if (!parser_next(parser) || parser->tok != TOKEN_STRINGCONST) {
+ parseerror(parser, "`alias` attribute missing parameter");
+ goto argerr;
+ }
+
+ *message = util_strdup(parser_tokval(parser));
+
+ if (!parser_next(parser)) {
+ parseerror(parser, "parse error in attribute");
+ goto argerr;
+ }
+
+ if (parser->tok != ')') {
+ parseerror(parser, "`alias` attribute expected `)` after parameter");
+ goto argerr;
+ }
+ if (!parser_next(parser)) {
+ parseerror(parser, "parse error in attribute");
+ goto argerr;
+ }
+ }
+ if (parser->tok != TOKEN_ATTRIBUTE_CLOSE) {
+ parseerror(parser, "`alias` attribute expected `]]`");
+ goto argerr;
+ }
+ }
else if (!strcmp(parser_tokval(parser), "deprecated") && !(flags & AST_FLAG_DEPRECATED)) {
flags |= AST_FLAG_DEPRECATED;
*message = NULL;
has_frame_think = false;
old = parser->function;
+ if (var->expression.flags & AST_FLAG_ALIAS) {
+ parseerror(parser, "function aliases cannot have bodies");
+ return false;
+ }
+
if (vec_size(parser->gotos) || vec_size(parser->labels)) {
parseerror(parser, "gotos/labels leaking");
return false;
ast_expression *functype = fld_think->expression.next;
thinkfunc = ast_value_new(parser_ctx(parser), parser_tokval(parser), functype->expression.vtype);
- if (!thinkfunc || !ast_type_adopt(thinkfunc, functype)) {
+ if (!thinkfunc) { /* || !ast_type_adopt(thinkfunc, functype)*/
ast_unref(framenum);
parseerror(parser, "failed to create implicit prototype for `%s`", parser_tokval(parser));
return false;
}
+ ast_type_adopt(thinkfunc, functype);
if (!parser_next(parser)) {
ast_unref(framenum);
var->cvq = qualifier;
var->expression.flags |= qflags;
- if (var->expression.flags & AST_FLAG_DEPRECATED)
+
+ /*
+ * store the vstring back to var for alias and
+ * deprecation messages.
+ */
+ if (var->expression.flags & AST_FLAG_DEPRECATED ||
+ var->expression.flags & AST_FLAG_ALIAS)
var->desc = vstring;
/* Part 1:
}
}
else {
- parser_addglobal(parser, var->name, (ast_expression*)var);
- if (isvector) {
- for (i = 0; i < 3; ++i) {
- parser_addglobal(parser, me[i]->name, (ast_expression*)me[i]);
+ if (!(var->expression.flags & AST_FLAG_ALIAS)) {
+ parser_addglobal(parser, var->name, (ast_expression*)var);
+ if (isvector) {
+ for (i = 0; i < 3; ++i) {
+ parser_addglobal(parser, me[i]->name, (ast_expression*)me[i]);
+ }
+ }
+ } else {
+ ast_expression *find = parser_find_global(parser, var->desc);
+
+ if (!find) {
+ compile_error(parser_ctx(parser), "undeclared variable `%s` for alias `%s`", var->desc, var->name);
+ return false;
+ }
+
+ if (var->expression.vtype != find->expression.vtype) {
+ char ty1[1024];
+ char ty2[1024];
+
+ ast_type_to_string(find, ty1, sizeof(ty1));
+ ast_type_to_string((ast_expression*)var, ty2, sizeof(ty2));
+
+ compile_error(parser_ctx(parser), "incompatible types `%s` and `%s` for alias `%s`",
+ ty1, ty2, var->name
+ );
+ return false;
+ }
+
+ /*
+ * add alias to aliases table and to corrector
+ * so corrections can apply for aliases as well.
+ */
+ util_htset(parser->aliases, var->name, find);
+
+ /*
+ * add to corrector so corrections can work
+ * even for aliases too.
+ */
+ correct_add (
+ vec_last(parser->correct_variables),
+ &vec_last(parser->correct_variables_score),
+ var->name
+ );
+
+ /* generate aliases for vector components */
+ if (isvector) {
+ char *buffer[3];
+
+ util_asprintf(&buffer[0], "%s_x", var->desc);
+ util_asprintf(&buffer[1], "%s_y", var->desc);
+ util_asprintf(&buffer[2], "%s_z", var->desc);
+
+ util_htset(parser->aliases, me[0]->name, parser_find_global(parser, buffer[0]));
+ util_htset(parser->aliases, me[1]->name, parser_find_global(parser, buffer[1]));
+ util_htset(parser->aliases, me[2]->name, parser_find_global(parser, buffer[2]));
+
+ mem_d(buffer[0]);
+ mem_d(buffer[1]);
+ mem_d(buffer[2]);
+
+ /*
+ * add to corrector so corrections can work
+ * even for aliases too.
+ */
+ correct_add (
+ vec_last(parser->correct_variables),
+ &vec_last(parser->correct_variables_score),
+ me[0]->name
+ );
+ correct_add (
+ vec_last(parser->correct_variables),
+ &vec_last(parser->correct_variables_score),
+ me[1]->name
+ );
+ correct_add (
+ vec_last(parser->correct_variables),
+ &vec_last(parser->correct_variables_score),
+ me[2]->name
+ );
}
}
}
vec_push(parser->typedefs, util_htnew(TYPEDEF_HT_SIZE));
vec_push(parser->_blocktypedefs, 0);
+ parser->aliases = util_htnew(PARSER_HT_SIZE);
+
/* corrector */
vec_push(parser->correct_variables, correct_trie_new());
vec_push(parser->correct_variables_score, NULL);
ast_value_delete(parser->const_vec[1]);
ast_value_delete(parser->const_vec[2]);
+ util_htdel(parser->aliases);
+
mem_d(parser);
}
opts_cmd_t opts;
-char *task_bins[] = {
+const char *task_bins[] = {
"./gmqcc",
"./qcvm"
};
int errhandle [2];
int trypipe;
- popen_t *data = mem_a(sizeof(popen_t));
+ popen_t *data = (popen_t*)mem_a(sizeof(popen_t));
/*
* Parse the command now into a list for execv, this is a pain
return status;
}
#else
-# define _WIN32_LEAN_AND_MEAN
-# define popen _popen
-# define pclose _pclose
-# include <windows.h>
-# include <io.h>
-# include <fcntl.h>
/*
* Bidirectional piping implementation for windows using CreatePipe and DuplicateHandle +
* other hacks.
*/
-
typedef struct {
int __dummy;
/* TODO: implement */
(void)files;
return;
}
-
-# ifdef __MINGW32__
- /* mingw32 has dirent.h */
-# include <dirent.h>
-# elif defined (_WIN32)
- /*
- * visual studio lacks dirent.h it's a posix thing
- * so we emulate it with the WinAPI.
- */
-
- 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;
-
- DIR *opendir(const char *name) {
- DIR *dir = (DIR*)mem_a(sizeof(DIR) + strlen(name));
- if (!dir)
- return NULL;
-
- strcpy(dir->dd_name, name);
- return dir;
- }
-
- int closedir(DIR *dir) {
- FindClose((HANDLE)dir->dd_handle);
- mem_d ((void*)dir);
- return 0;
- }
-
- struct dirent *readdir(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 */)) {
- strcpy(dirname, dir->dd_name);
- strcpy(dirname + n, "\\*.*"); /* 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)))) {
- 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;
- }
-
- /*
- * Visual studio also lacks S_ISDIR for sys/stat.h, so we emulate this as well
- * which is not hard at all.
- */
-# undef S_ISDIR /* undef just incase */
-# define S_ISDIR(X) ((X)&_S_IFDIR)
-# endif
-#endif
+#endif /*! _WIN32 */
#define TASK_COMPILE 0
#define TASK_EXECUTE 1
-
/*
* Task template system:
* templates are rules for a specific test, used to create a "task" that
* This is very much like a compiler code generator :-). This generates
* a value from some data observed from the compiler.
*/
-bool task_template_generate(task_template_t *template, char tag, const char *file, size_t line, const char *value, size_t *pad) {
+bool task_template_generate(task_template_t *tmpl, char tag, const char *file, size_t line, char *value, size_t *pad) {
size_t desclen = 0;
char **destval = NULL;
- if (!template)
+ if (!tmpl)
return false;
switch(tag) {
- case 'D': destval = &template->description; break;
- case 'T': destval = &template->proceduretype; break;
- case 'C': destval = &template->compileflags; break;
- case 'E': destval = &template->executeflags; break;
- case 'I': destval = &template->sourcefile; break;
- case 'F': destval = &template->testflags; break;
+ case 'D': destval = &tmpl->description; break;
+ case 'T': destval = &tmpl->proceduretype; break;
+ case 'C': destval = &tmpl->compileflags; break;
+ case 'E': destval = &tmpl->executeflags; break;
+ case 'I': destval = &tmpl->sourcefile; break;
+ case 'F': destval = &tmpl->testflags; break;
default:
con_printmsg(LVL_ERROR, __FILE__, __LINE__, "internal error",
"invalid tag `%c:` during code generation\n",
*destval = util_strdup(value);
- if (*destval == template->description) {
+ if (*destval == tmpl->description) {
/*
* Create some padding for the description to align the
* printing of the rules file.
*/
- if ((desclen = strlen(template->description)) > pad[0])
+ if ((desclen = strlen(tmpl->description)) > pad[0])
pad[0] = desclen;
}
return true;
}
-bool task_template_parse(const char *file, task_template_t *template, FILE *fp, size_t *pad) {
+bool task_template_parse(const char *file, task_template_t *tmpl, FILE *fp, size_t *pad) {
char *data = NULL;
char *back = NULL;
size_t size = 0;
size_t line = 1;
- if (!template)
+ if (!tmpl)
return false;
/* top down parsing */
- while (file_getline(&back, &size, fp) != EOF) {
+ while (fs_file_getline(&back, &size, fp) != EOF) {
/* skip whitespace */
data = back;
if (*data && (*data == ' ' || *data == '\t'))
switch (*data) {
/*
- * Handle comments inside task template files. We're strict
+ * Handle comments inside task tmpl files. We're strict
* about the language for fun :-)
*/
case '/':
if (data[1] != '/') {
- con_printmsg(LVL_ERROR, file, line, "template parse error",
+ con_printmsg(LVL_ERROR, file, line, "tmpl parse error",
"invalid character `/`, perhaps you meant `//` ?");
mem_d(back);
case 'I':
case 'F':
if (data[1] != ':') {
- con_printmsg(LVL_ERROR, file, line, "template parse error",
+ con_printmsg(LVL_ERROR, file, line, "tmpl parse error",
"expected `:` after `%c`",
*data
);
goto failure;
}
- if (!task_template_generate(template, *data, file, line, &data[3], pad)) {
- con_printmsg(LVL_ERROR, file, line, "template compile error",
+ if (!task_template_generate(tmpl, *data, file, line, &data[3], pad)) {
+ con_printmsg(LVL_ERROR, file, line, "tmpl compile error",
"failed to generate for given task\n"
);
goto failure;
{
char *value = &data[3];
if (data[1] != ':') {
- con_printmsg(LVL_ERROR, file, line, "template parse error",
+ con_printmsg(LVL_ERROR, file, line, "tmpl parse error",
"expected `:` after `%c`",
*data
);
else /* cppcheck: possible null pointer dereference */
abort();
- vec_push(template->comparematch, util_strdup(value));
+ vec_push(tmpl->comparematch, util_strdup(value));
break;
}
default:
- con_printmsg(LVL_ERROR, file, line, "template parse error",
+ con_printmsg(LVL_ERROR, file, line, "tmpl parse error",
"invalid tag `%c`", *data
);
goto failure;
* Nullifies the template data: used during initialization of a new
* template and free.
*/
-void task_template_nullify(task_template_t *template) {
- if (!template)
+void task_template_nullify(task_template_t *tmpl) {
+ if (!tmpl)
return;
- template->description = NULL;
- template->proceduretype = NULL;
- template->compileflags = NULL;
- template->executeflags = NULL;
- template->comparematch = NULL;
- template->sourcefile = NULL;
- template->tempfilename = NULL;
- template->rulesfile = NULL;
- template->testflags = NULL;
+ tmpl->description = NULL;
+ tmpl->proceduretype = NULL;
+ tmpl->compileflags = NULL;
+ tmpl->executeflags = NULL;
+ tmpl->comparematch = NULL;
+ tmpl->sourcefile = NULL;
+ tmpl->tempfilename = NULL;
+ tmpl->rulesfile = NULL;
+ tmpl->testflags = NULL;
}
task_template_t *task_template_compile(const char *file, const char *dir, size_t *pad) {
char fullfile[4096];
size_t filepadd = 0;
FILE *tempfile = NULL;
- task_template_t *template = NULL;
+ task_template_t *tmpl = NULL;
- memset (fullfile, 0, sizeof(fullfile));
snprintf(fullfile, sizeof(fullfile), "%s/%s", dir, file);
- tempfile = file_open(fullfile, "r");
- template = mem_a(sizeof(task_template_t));
- task_template_nullify(template);
+ tempfile = fs_file_open(fullfile, "r");
+ tmpl = (task_template_t*)mem_a(sizeof(task_template_t));
+ task_template_nullify(tmpl);
/*
* Create some padding for the printing to align the
if ((filepadd = strlen(fullfile)) > pad[1])
pad[1] = filepadd;
- template->rulesfile = util_strdup(fullfile);
+ tmpl->rulesfile = util_strdup(fullfile);
/*
* Esnure the file even exists for the task, this is pretty useless
goto failure;
}
- if (!task_template_parse(file, template, tempfile, pad)) {
+ if (!task_template_parse(file, tmpl, tempfile, pad)) {
con_err("template parse error: error during parsing\n");
goto failure;
}
* C
* I
*/
- if (!template->description) {
+ if (!tmpl->description) {
con_err("template compile error: %s missing `D:` tag\n", file);
goto failure;
}
- if (!template->proceduretype) {
+ if (!tmpl->proceduretype) {
con_err("template compile error: %s missing `T:` tag\n", file);
goto failure;
}
- if (!template->compileflags) {
+ if (!tmpl->compileflags) {
con_err("template compile error: %s missing `C:` tag\n", file);
goto failure;
}
- if (!template->sourcefile) {
+ if (!tmpl->sourcefile) {
con_err("template compile error: %s missing `I:` tag\n", file);
goto failure;
}
* Now lets compile the template, compilation is really just
* the process of validating the input.
*/
- if (!strcmp(template->proceduretype, "-compile")) {
- if (template->executeflags)
+ if (!strcmp(tmpl->proceduretype, "-compile")) {
+ if (tmpl->executeflags)
con_err("template compile warning: %s erroneous tag `E:` when only compiling\n", file);
- if (template->comparematch)
+ if (tmpl->comparematch)
con_err("template compile warning: %s erroneous tag `M:` when only compiling\n", file);
goto success;
- } else if (!strcmp(template->proceduretype, "-execute")) {
- if (!template->executeflags) {
+ } else if (!strcmp(tmpl->proceduretype, "-execute")) {
+ if (!tmpl->executeflags) {
/* default to $null */
- template->executeflags = util_strdup("$null");
+ tmpl->executeflags = util_strdup("$null");
}
- if (!template->comparematch) {
+ if (!tmpl->comparematch) {
con_err("template compile error: %s missing `M:` tag (use `$null` for exclude)\n", file);
goto failure;
}
- } else if (!strcmp(template->proceduretype, "-fail")) {
- if (template->executeflags)
+ } else if (!strcmp(tmpl->proceduretype, "-fail")) {
+ if (tmpl->executeflags)
con_err("template compile warning: %s erroneous tag `E:` when only failing\n", file);
- if (template->comparematch)
+ if (tmpl->comparematch)
con_err("template compile warning: %s erroneous tag `M:` when only failing\n", file);
goto success;
} else {
- con_err("template compile error: %s invalid procedure type: %s\n", file, template->proceduretype);
+ con_err("template compile error: %s invalid procedure type: %s\n", file, tmpl->proceduretype);
goto failure;
}
success:
- file_close(tempfile);
- return template;
+ fs_file_close(tempfile);
+ return tmpl;
failure:
/*
* so the check to see if it's not null here is required.
*/
if (tempfile)
- file_close(tempfile);
- mem_d (template);
+ fs_file_close(tempfile);
+ mem_d (tmpl);
return NULL;
}
-void task_template_destroy(task_template_t **template) {
- if (!template)
+void task_template_destroy(task_template_t **tmpl) {
+ if (!tmpl)
return;
- if ((*template)->description) mem_d((*template)->description);
- if ((*template)->proceduretype) mem_d((*template)->proceduretype);
- if ((*template)->compileflags) mem_d((*template)->compileflags);
- if ((*template)->executeflags) mem_d((*template)->executeflags);
- if ((*template)->sourcefile) mem_d((*template)->sourcefile);
- if ((*template)->rulesfile) mem_d((*template)->rulesfile);
- if ((*template)->testflags) mem_d((*template)->testflags);
+ if ((*tmpl)->description) mem_d((*tmpl)->description);
+ if ((*tmpl)->proceduretype) mem_d((*tmpl)->proceduretype);
+ if ((*tmpl)->compileflags) mem_d((*tmpl)->compileflags);
+ if ((*tmpl)->executeflags) mem_d((*tmpl)->executeflags);
+ if ((*tmpl)->sourcefile) mem_d((*tmpl)->sourcefile);
+ if ((*tmpl)->rulesfile) mem_d((*tmpl)->rulesfile);
+ if ((*tmpl)->testflags) mem_d((*tmpl)->testflags);
/*
- * Delete all allocated string for task template then destroy the
+ * Delete all allocated string for task tmpl then destroy the
* main vector.
*/
{
size_t i = 0;
- for (; i < vec_size((*template)->comparematch); i++)
- mem_d((*template)->comparematch[i]);
+ for (; i < vec_size((*tmpl)->comparematch); i++)
+ mem_d((*tmpl)->comparematch[i]);
- vec_free((*template)->comparematch);
+ vec_free((*tmpl)->comparematch);
}
/*
* Nullify all the template members otherwise NULL comparision
- * checks will fail if template pointer is reused.
+ * checks will fail if tmpl pointer is reused.
*/
- mem_d(*template);
+ mem_d(*tmpl);
}
/*
* of a task list. This is the executor of the tasks essentially as well.
*/
typedef struct {
- task_template_t *template;
+ task_template_t *tmpl;
FILE **runhandles;
FILE *stderrlog;
FILE *stdoutlog;
dir = opendir(curdir);
while ((files = readdir(dir))) {
- memset (buffer, 0,sizeof(buffer));
snprintf(buffer, sizeof(buffer), "%s/%s", curdir, files->d_name);
if (stat(buffer, &directory) == -1) {
* actually a directory, so it must be a file :)
*/
if (strcmp(files->d_name + strlen(files->d_name) - 5, ".tmpl") == 0) {
- task_template_t *template = task_template_compile(files->d_name, curdir, pad);
+ task_template_t *tmpl = task_template_compile(files->d_name, curdir, pad);
char buf[4096]; /* one page should be enough */
char *qcflags = NULL;
task_t task;
util_debug("TEST", "compiling task template: %s/%s\n", curdir, files->d_name);
found ++;
- if (!template) {
+ if (!tmpl) {
con_err("error compiling task template: %s\n", files->d_name);
success = false;
continue;
* Generate a temportary file name for the output binary
* so we don't trample over an existing one.
*/
- template->tempfilename = tempnam(curdir, "TMPDAT");
+ tmpl->tempfilename = tempnam(curdir, "TMPDAT");
/*
* Additional QCFLAGS enviroment variable may be used
* which will be refered to with a handle in the task for
* reading the data from the pipe.
*/
- memset (buf,0,sizeof(buf));
if (qcflags) {
- if (template->testflags && !strcmp(template->testflags, "-no-defs")) {
+ if (tmpl->testflags && !strcmp(tmpl->testflags, "-no-defs")) {
snprintf(buf, sizeof(buf), "%s %s/%s %s %s -o %s",
task_bins[TASK_COMPILE],
curdir,
- template->sourcefile,
+ tmpl->sourcefile,
qcflags,
- template->compileflags,
- template->tempfilename
+ tmpl->compileflags,
+ tmpl->tempfilename
);
} else {
snprintf(buf, sizeof(buf), "%s %s/%s %s/%s %s %s -o %s",
curdir,
defs,
curdir,
- template->sourcefile,
+ tmpl->sourcefile,
qcflags,
- template->compileflags,
- template->tempfilename
+ tmpl->compileflags,
+ tmpl->tempfilename
);
}
} else {
- if (template->testflags && !strcmp(template->testflags, "-no-defs")) {
+ if (tmpl->testflags && !strcmp(tmpl->testflags, "-no-defs")) {
snprintf(buf, sizeof(buf), "%s %s/%s %s -o %s",
task_bins[TASK_COMPILE],
curdir,
- template->sourcefile,
- template->compileflags,
- template->tempfilename
+ tmpl->sourcefile,
+ tmpl->compileflags,
+ tmpl->tempfilename
);
} else {
snprintf(buf, sizeof(buf), "%s %s/%s %s/%s %s -o %s",
curdir,
defs,
curdir,
- template->sourcefile,
- template->compileflags,
- template->tempfilename
+ tmpl->sourcefile,
+ tmpl->compileflags,
+ tmpl->tempfilename
);
}
}
* The task template was compiled, now lets create a task from
* the template data which has now been propagated.
*/
- task.template = template;
+ task.tmpl = tmpl;
if (!(task.runhandles = task_popen(buf, "r"))) {
- con_err("error opening pipe to process for test: %s\n", template->description);
+ con_err("error opening pipe to process for test: %s\n", tmpl->description);
success = false;
continue;
}
- util_debug("TEST", "executing test: `%s` [%s]\n", template->description, buf);
+ util_debug("TEST", "executing test: `%s` [%s]\n", tmpl->description, buf);
/*
* Open up some file desciptors for logging the stdout/stderr
* to our own.
*/
- memset (buf,0,sizeof(buf));
- snprintf(buf, sizeof(buf), "%s.stdout", template->tempfilename);
+ snprintf(buf, sizeof(buf), "%s.stdout", tmpl->tempfilename);
task.stdoutlogfile = util_strdup(buf);
- if (!(task.stdoutlog = file_open(buf, "w"))) {
+ if (!(task.stdoutlog = fs_file_open(buf, "w"))) {
con_err("error opening %s for stdout\n", buf);
continue;
}
- memset (buf,0,sizeof(buf));
- snprintf(buf, sizeof(buf), "%s.stderr", template->tempfilename);
+ snprintf(buf, sizeof(buf), "%s.stderr", tmpl->tempfilename);
task.stderrlogfile = util_strdup(buf);
- if (!(task.stderrlog = file_open(buf, "w"))) {
+ if (!(task.stderrlog = fs_file_open(buf, "w"))) {
con_err("error opening %s for stderr\n", buf);
continue;
}
dir = opendir(curdir);
while ((files = readdir(dir))) {
- memset(buffer, 0, sizeof(buffer));
if (strstr(files->d_name, "TMP") ||
strstr(files->d_name, ".stdout") ||
strstr(files->d_name, ".stderr"))
* annoying to have to do all this cleanup work.
*/
if (task_tasks[i].runhandles) task_pclose(task_tasks[i].runhandles);
- if (task_tasks[i].stdoutlog) file_close (task_tasks[i].stdoutlog);
- if (task_tasks[i].stderrlog) file_close (task_tasks[i].stderrlog);
+ if (task_tasks[i].stdoutlog) fs_file_close (task_tasks[i].stdoutlog);
+ if (task_tasks[i].stderrlog) fs_file_close (task_tasks[i].stderrlog);
/*
* Only remove the log files if the test actually compiled otherwise
* forget about it (or if it didn't compile, and the procedure type
* was set to -fail (meaning it shouldn't compile) .. stil remove)
*/
- if (task_tasks[i].compiled || !strcmp(task_tasks[i].template->proceduretype, "-fail")) {
+ if (task_tasks[i].compiled || !strcmp(task_tasks[i].tmpl->proceduretype, "-fail")) {
if (remove(task_tasks[i].stdoutlogfile))
con_err("error removing stdout log file: %s\n", task_tasks[i].stdoutlogfile);
else
else
util_debug("TEST", "removed stderr log file: %s\n", task_tasks[i].stderrlogfile);
- remove(task_tasks[i].template->tempfilename);
+ remove(task_tasks[i].tmpl->tempfilename);
}
/* free util_strdup data for log files */
mem_d(task_tasks[i].stdoutlogfile);
mem_d(task_tasks[i].stderrlogfile);
- task_template_destroy(&task_tasks[i].template);
+ task_template_destroy(&task_tasks[i].tmpl);
}
vec_free(task_tasks);
}
* using the template passed into it for call-flags and user defined
* messages.
*/
-bool task_execute(task_template_t *template, char ***line) {
+bool task_execute(task_template_t *tmpl, char ***line) {
bool success = true;
FILE *execute;
char buffer[4096];
* Drop the execution flags for the QCVM if none where
* actually specified.
*/
- if (!strcmp(template->executeflags, "$null")) {
+ if (!strcmp(tmpl->executeflags, "$null")) {
snprintf(buffer, sizeof(buffer), "%s %s",
task_bins[TASK_EXECUTE],
- template->tempfilename
+ tmpl->tempfilename
);
} else {
snprintf(buffer, sizeof(buffer), "%s %s %s",
task_bins[TASK_EXECUTE],
- template->executeflags,
- template->tempfilename
+ tmpl->executeflags,
+ tmpl->tempfilename
);
}
util_debug("TEST", "executing qcvm: `%s` [%s]\n",
- template->description,
+ tmpl->description,
buffer
);
char *data = NULL;
size_t size = 0;
size_t compare = 0;
- while (file_getline(&data, &size, execute) != EOF) {
+ while (fs_file_getline(&data, &size, execute) != EOF) {
if (!strcmp(data, "No main function found\n")) {
con_err("test failure: `%s` (No main function found) [%s]\n",
- template->description,
- template->rulesfile
+ tmpl->description,
+ tmpl->rulesfile
);
pclose(execute);
return false;
if (strrchr(data, '\n'))
*strrchr(data, '\n') = '\0';
- if (vec_size(template->comparematch) > compare) {
- if (strcmp(data, template->comparematch[compare++]))
+ if (vec_size(tmpl->comparematch) > compare) {
+ if (strcmp(data, tmpl->comparematch[compare++]))
success = false;
} else {
success = false;
util_debug("TEST", "found %d tasks, preparing to execute\n", vec_size(task_tasks));
for (i = 0; i < vec_size(task_tasks); i++) {
- util_debug("TEST", "executing task: %d: %s\n", i, task_tasks[i].template->description);
+ util_debug("TEST", "executing task: %d: %s\n", i, task_tasks[i].tmpl->description);
/*
* Generate a task from thin air if it requires execution in
* the QCVM.
*/
- execute = !!(!strcmp(task_tasks[i].template->proceduretype, "-execute"));
+ execute = !!(!strcmp(task_tasks[i].tmpl->proceduretype, "-execute"));
/*
* We assume it compiled before we actually compiled :). On error
* Read data from stdout first and pipe that stuff into a log file
* then we do the same for stderr.
*/
- while (file_getline(&data, &size, task_tasks[i].runhandles[1]) != EOF) {
- file_puts(task_tasks[i].stdoutlog, data);
+ while (fs_file_getline(&data, &size, task_tasks[i].runhandles[1]) != EOF) {
+ fs_file_puts(task_tasks[i].stdoutlog, data);
if (strstr(data, "failed to open file")) {
task_tasks[i].compiled = false;
execute = false;
}
- fflush(task_tasks[i].stdoutlog);
+ fs_file_flush(task_tasks[i].stdoutlog);
}
- while (file_getline(&data, &size, task_tasks[i].runhandles[2]) != EOF) {
+ while (fs_file_getline(&data, &size, task_tasks[i].runhandles[2]) != EOF) {
/*
* If a string contains an error we just dissalow execution
* of it in the vm.
task_tasks[i].compiled = false;
}
- file_puts(task_tasks[i].stderrlog, data);
- fflush(task_tasks[i].stdoutlog);
+ fs_file_puts (task_tasks[i].stderrlog, data);
+ fs_file_flush(task_tasks[i].stdoutlog);
}
- if (!task_tasks[i].compiled && strcmp(task_tasks[i].template->proceduretype, "-fail")) {
+ if (!task_tasks[i].compiled && strcmp(task_tasks[i].tmpl->proceduretype, "-fail")) {
con_err("test failure: `%s` (failed to compile) see %s.stdout and %s.stderr [%s]\n",
- task_tasks[i].template->description,
- task_tasks[i].template->tempfilename,
- task_tasks[i].template->tempfilename,
- task_tasks[i].template->rulesfile
+ task_tasks[i].tmpl->description,
+ task_tasks[i].tmpl->tempfilename,
+ task_tasks[i].tmpl->tempfilename,
+ task_tasks[i].tmpl->rulesfile
);
continue;
}
if (!execute) {
con_out("test succeeded: `%s` %*s\n",
- task_tasks[i].template->description,
- (pad[0] + pad[1] - strlen(task_tasks[i].template->description)) +
- (strlen(task_tasks[i].template->rulesfile) - pad[1]),
- task_tasks[i].template->rulesfile
+ task_tasks[i].tmpl->description,
+ (pad[0] + pad[1] - strlen(task_tasks[i].tmpl->description)) +
+ (strlen(task_tasks[i].tmpl->rulesfile) - pad[1]),
+ task_tasks[i].tmpl->rulesfile
);
continue;
* If we made it here that concludes the task is to be executed
* in the virtual machine.
*/
- if (!task_execute(task_tasks[i].template, &match)) {
+ if (!task_execute(task_tasks[i].tmpl, &match)) {
size_t d = 0;
con_err("test failure: `%s` (invalid results from execution) [%s]\n",
- task_tasks[i].template->description,
- task_tasks[i].template->rulesfile
+ task_tasks[i].tmpl->description,
+ task_tasks[i].tmpl->rulesfile
);
/*
* what was actually returned from executing.
*/
con_err(" Expected From %u Matches: (got %u Matches)\n",
- vec_size(task_tasks[i].template->comparematch),
+ vec_size(task_tasks[i].tmpl->comparematch),
vec_size(match)
);
- for (; d < vec_size(task_tasks[i].template->comparematch); d++) {
- char *select = task_tasks[i].template->comparematch[d];
+ for (; d < vec_size(task_tasks[i].tmpl->comparematch); d++) {
+ char *select = task_tasks[i].tmpl->comparematch[d];
size_t length = 40 - strlen(select);
con_err(" Expected: \"%s\"", select);
* This will help track down bugs in template files that fail to match
* something.
*/
- if (vec_size(match) > vec_size(task_tasks[i].template->comparematch)) {
- for (d = 0; d < vec_size(match) - vec_size(task_tasks[i].template->comparematch); d++) {
+ if (vec_size(match) > vec_size(task_tasks[i].tmpl->comparematch)) {
+ for (d = 0; d < vec_size(match) - vec_size(task_tasks[i].tmpl->comparematch); d++) {
con_err(" Expected: Nothing | Got: \"%s\"\n",
- match[d + vec_size(task_tasks[i].template->comparematch)]
+ match[d + vec_size(task_tasks[i].tmpl->comparematch)]
);
}
}
vec_free(match);
con_out("test succeeded: `%s` %*s\n",
- task_tasks[i].template->description,
- (pad[0] + pad[1] - strlen(task_tasks[i].template->description)) +
- (strlen(task_tasks[i].template->rulesfile) - pad[1]),
- task_tasks[i].template->rulesfile
+ task_tasks[i].tmpl->description,
+ (pad[0] + pad[1] - strlen(task_tasks[i].tmpl->description)) +
+ (strlen(task_tasks[i].tmpl->rulesfile) - pad[1]),
+ task_tasks[i].tmpl->rulesfile
);
}
--- /dev/null
+float alias_1 = 3.14;
+void alias_2() {
+ print("alias_2\n");
+}
+
+[[alias("alias_2")]] void alias_2_aliased();
+[[alias("alias_1")]] float alias_1_aliased;
+
+
+// alias to an alias?
+vector alias_3;
+[[alias("alias_3")]] vector alias_3_aliased;
+
+// expected output
+// alias_2
+// 3.14
+void main() {
+ alias_2_aliased();
+
+ alias_3_aliased= '1 2 3';
+
+ print(
+ ftos(
+ alias_1_aliased
+ ),
+ "\n"
+ );
+
+ print(
+ "x ", ftos(alias_3_aliased_x), "\n",
+ "y ", ftos(alias_3_aliased_y), "\n",
+ "z ", ftos(alias_3_aliased_z), "\n"
+ );
+}
--- /dev/null
+I: aliases.qc
+D: test aliases
+T: -execute
+C: -std=gmqcc
+M: alias_2
+M: 3.14
+M: x 1
+M: y 2
+M: z 3
--- /dev/null
+void main() {
+ /* so far only one perl operator is implemented */
+ float x = 100;
+ float y = 200;
+ float z = 300;
+
+ /* to ensure runtime */
+ x += 1;
+ y += 1;
+ z += 1;
+
+ float test_x = (x <=> x + 1); // -1 less than
+ float test_y = (x <=> x); // 0 equal
+ float test_z = (x <=> x - 1); // 1 greater than
+
+ print(ftos(test_x), "\n");
+ print(ftos(test_y), "\n");
+ print(ftos(test_z), "\n");
+}
--- /dev/null
+I: pops.qc
+D: test perl operators
+T: -execute
+C: -std=gmqcc
+M: -1
+M: 0
+M: 1