ast.o \
ir.o \
con.o \
- ftepp.o
+ ftepp.o \
+ opts.o
OBJ_T = test.o util.o con.o
OBJ_C = main.o lexer.o parser.o
}
}
- options_set(opts.warn, WARN_USED_UNINITIALIZED, false);
+ opts_set(opts.warn, WARN_USED_UNINITIALIZED, false);
if (self->setter) {
if (!ast_global_codegen (self->setter, ir, false) ||
!ast_function_codegen(self->setter->constval.vfunc, ir) ||
!ir_function_finalize(self->setter->constval.vfunc->ir_func))
{
compile_error(ast_ctx(self), "internal error: failed to generate setter for `%s`", self->name);
- options_set(opts.warn, WARN_USED_UNINITIALIZED, warn);
+ opts_set(opts.warn, WARN_USED_UNINITIALIZED, warn);
return false;
}
}
!ir_function_finalize(self->getter->constval.vfunc->ir_func))
{
compile_error(ast_ctx(self), "internal error: failed to generate getter for `%s`", self->name);
- options_set(opts.warn, WARN_USED_UNINITIALIZED, warn);
+ opts_set(opts.warn, WARN_USED_UNINITIALIZED, warn);
return false;
}
}
for (i = 0; i < self->expression.count; ++i) {
vec_free(self->ir_values[i]->life);
}
- options_set(opts.warn, WARN_USED_UNINITIALIZED, warn);
+ opts_set(opts.warn, WARN_USED_UNINITIALIZED, warn);
return true;
}
extern size_t compile_errors;
extern size_t compile_warnings;
-void /********/ compile_error (lex_ctx ctx, /*LVL_ERROR*/ const char *msg, ...);
-bool GMQCC_WARN compile_warning(lex_ctx ctx, int warntype, const char *fmt, ...);
-void /********/ vcompile_error (lex_ctx ctx, /*LVL_ERROR*/ const char *msg, va_list);
-bool GMQCC_WARN vcompile_warning(lex_ctx ctx, int warntype, const char *fmt, va_list);
+void /********/ compile_error (lex_ctx ctx, /*LVL_ERROR*/ const char *msg, ...);
+void /********/ vcompile_error (lex_ctx ctx, /*LVL_ERROR*/ const char *msg, va_list ap);
+bool GMQCC_WARN compile_warning (lex_ctx ctx, int warntype, const char *fmt, ...);
+bool GMQCC_WARN vcompile_warning(lex_ctx ctx, int warntype, const char *fmt, va_list ap);
/*===================================================================*/
/*========================= assembler.c =============================*/
#define LONGBIT(bit) (bit)
#endif
-/* Used to store the list of flags with names */
+/*===================================================================*/
+/*============================= opts.c ==============================*/
+/*===================================================================*/
typedef struct {
const char *name;
longbit bit;
} opts_flag_def;
-/*===================================================================*/
-/* list of -f flags, like -fdarkplaces-string-table-bug */
+bool opts_setflag (const char *, bool);
+bool opts_setwarn (const char *, bool);
+bool opts_setoptim(const char *, bool);
+
+void opts_init (const char *, int, size_t);
+void opts_set (uint32_t *, size_t, bool);
+void opts_setoptimlevel(unsigned int);
+
enum {
# define GMQCC_TYPE_FLAGS
# define GMQCC_DEFINE_FLAG(X) X,
# include "opts.def"
0
};
-extern unsigned int optimization_count[COUNT_OPTIMIZATIONS];
+extern unsigned int opts_optimizationcount[COUNT_OPTIMIZATIONS];
/* other options: */
-enum {
+typedef enum {
COMPILER_QCC, /* circa QuakeC */
COMPILER_FTEQCC, /* fteqcc QuakeC */
COMPILER_QCCX, /* qccx QuakeC */
COMPILER_GMQCC /* this QuakeC */
-};
+} opts_std_t;
typedef struct {
uint32_t O; /* -Ox */
const char *output; /* -o file */
bool g; /* -g */
- int standard; /* -std= */
+ opts_std_t standard; /* -std= */
bool debug; /* -debug */
bool memchk; /* -memchk */
bool dumpfin; /* -dumpfin */
uint32_t flags [1 + (COUNT_FLAGS / 32)];
uint32_t warn [1 + (COUNT_WARNINGS / 32)];
uint32_t optimization[1 + (COUNT_OPTIMIZATIONS / 32)];
-} cmd_options;
+} opts_cmd_t;
-extern cmd_options opts;
+extern opts_cmd_t opts;
/*===================================================================*/
#define OPTS_FLAG(i) (!! (opts.flags [(i)/32] & (1<< ((i)%32))))
#define OPTS_WARN(i) (!! (opts.warn [(i)/32] & (1<< ((i)%32))))
#define OPTS_OPTIMIZATION(i) (!! (opts.optimization[(i)/32] & (1<< ((i)%32))))
-void options_set(uint32_t *flags, size_t idx, bool on);
-
#endif
static bool irwarning(lex_ctx ctx, int warntype, const char *fmt, ...)
{
bool r;
- va_list ap;
- va_start(ap, fmt);
- r = vcompile_warning(ctx, warntype, fmt, ap);
- va_end(ap);
- return r;
+ va_list ap;
+ va_start(ap, fmt);
+ r = vcompile_warning(ctx, warntype, fmt, ap);
+ va_end(ap);
+ return r;
}
/***********************************************************************
if (store->_ops[1] != value)
continue;
- ++optimization_count[OPTIM_PEEPHOLE];
+ ++opts_optimizationcount[OPTIM_PEEPHOLE];
(void)!ir_instr_op(oper, 0, store->_ops[0], true);
vec_remove(block->instr, i, 1);
}
/* count */
- ++optimization_count[OPTIM_PEEPHOLE];
+ ++opts_optimizationcount[OPTIM_PEEPHOLE];
/* change operand */
(void)!ir_instr_op(inst, 0, inot->_ops[1], false);
/* remove NOT */
ret->_ops[0] == store->_ops[0] &&
store->_ops[1] == call->_ops[0])
{
- ++optimization_count[OPTIM_PEEPHOLE];
+ ++opts_optimizationcount[OPTIM_PEEPHOLE];
call->_ops[0] = store->_ops[0];
vec_remove(block->instr, vec_size(block->instr) - 2, 1);
ir_instr_delete(store);
if (ret->_ops[0] && call->_ops[0] != ret->_ops[0])
continue;
- ++optimization_count[OPTIM_TAIL_RECURSION];
+ ++opts_optimizationcount[OPTIM_TAIL_RECURSION];
vec_shrinkby(block->instr, 2);
block->final = false; /* open it back up */
if (stmt.o2.u1 == stmt.o1.u1 &&
OPTS_OPTIMIZATION(OPTIM_PEEPHOLE))
{
- ++optimization_count[OPTIM_PEEPHOLE];
+ ++opts_optimizationcount[OPTIM_PEEPHOLE];
continue;
}
}
#include "gmqcc.h"
#include "lexer.h"
-/* counter increased in ir.c */
-unsigned int optimization_count[COUNT_OPTIMIZATIONS];
-static bool opts_output_wasset = false;
-
-cmd_options opts;
+/* TODO: cleanup this whole file .. it's a fuckign mess */
/* set by the standard */
const oper_info *operators = NULL;
size_t operator_count = 0;
+static bool opts_output_wasset = false;
typedef struct { char *filename; int type; } argitem;
typedef struct { char *name; char *value; } ppitem;
return -1;
}
-static bool options_setflag_all(const char *name, bool on, uint32_t *flags, const opts_flag_def *list, size_t listsize) {
- size_t i;
-
- for (i = 0; i < listsize; ++i) {
- if (!strcmp(name, list[i].name)) {
- longbit lb = list[i].bit;
-#if 0
- if (on)
- flags[lb.idx] |= (1<<(lb.bit));
- else
- flags[lb.idx] &= ~(1<<(lb.bit));
-#else
- if (on)
- flags[0] |= (1<<lb);
- else
- flags[0] &= ~(1<<(lb));
-#endif
- return true;
- }
- }
- return false;
-}
-static bool options_setflag(const char *name, bool on) {
- return options_setflag_all(name, on, opts.flags, opts_flag_list, COUNT_FLAGS);
-}
-static bool options_setwarn(const char *name, bool on) {
- return options_setflag_all(name, on, opts.warn, opts_warn_list, COUNT_WARNINGS);
-}
-static bool options_setoptim(const char *name, bool on) {
- return options_setflag_all(name, on, opts.optimization, opts_opt_list, COUNT_OPTIMIZATIONS);
-}
-
+/* command line parsing */
static bool options_witharg(int *argc_, char ***argv_, char **out) {
int argc = *argc_;
char **argv = *argv_;
return options_long_witharg_all(optname, argc_, argv_, out, 1, false);
}
-void options_set(uint32_t *flags, size_t idx, bool on)
-{
- longbit lb = LONGBIT(idx);
-#if 0
- if (on)
- flags[lb.idx] |= (1<<(lb.bit));
- else
- flags[lb.idx] &= ~(1<<(lb.bit));
-#else
- if (on)
- flags[0] |= (1<<(lb));
- else
- flags[0] &= ~(1<<(lb));
-#endif
-}
-
-static void set_optimizations(unsigned int level)
-{
- size_t i;
- for (i = 0; i < COUNT_OPTIMIZATIONS; ++i)
- options_set(opts.optimization, i, level >= opts_opt_oflag[i]);
-}
-
static bool options_parse(int argc, char **argv) {
bool argend = false;
size_t itr;
--argc;
if (argv[0][0] == '-') {
- /* All gcc-type long options */
+ /* All gcc-type long options */
if (options_long_gcc("std", &argc, &argv, &argarg)) {
- if (!strcmp(argarg, "gmqcc") || !strcmp(argarg, "default")) {
- options_set(opts.flags, ADJUST_VECTOR_FIELDS, true);
+ if (!strcmp(argarg, "gmqcc") || !strcmp(argarg, "default")) {
+
+ opts_set(opts.flags, ADJUST_VECTOR_FIELDS, true);
opts.standard = COMPILER_GMQCC;
+
} else if (!strcmp(argarg, "qcc")) {
- options_set(opts.flags, ADJUST_VECTOR_FIELDS, false);
- options_set(opts.flags, ASSIGN_FUNCTION_TYPES, true);
+
+ opts_set(opts.flags, ADJUST_VECTOR_FIELDS, false);
+ opts_set(opts.flags, ASSIGN_FUNCTION_TYPES, true);
opts.standard = COMPILER_QCC;
+
} else if (!strcmp(argarg, "fte") || !strcmp(argarg, "fteqcc")) {
- options_set(opts.flags, FTEPP, true);
- options_set(opts.flags, TRANSLATABLE_STRINGS, true);
- options_set(opts.flags, ADJUST_VECTOR_FIELDS, false);
- options_set(opts.flags, ASSIGN_FUNCTION_TYPES, true);
- options_set(opts.warn, WARN_TERNARY_PRECEDENCE, true);
- options_set(opts.flags, CORRECT_TERNARY, false);
+
+ opts_set(opts.flags, FTEPP, true);
+ opts_set(opts.flags, TRANSLATABLE_STRINGS, true);
+ opts_set(opts.flags, ADJUST_VECTOR_FIELDS, false);
+ opts_set(opts.flags, ASSIGN_FUNCTION_TYPES, true);
+ opts_set(opts.warn, WARN_TERNARY_PRECEDENCE, true);
+ opts_set(opts.flags, CORRECT_TERNARY, false);
opts.standard = COMPILER_FTEQCC;
+
} else if (!strcmp(argarg, "qccx")) {
- options_set(opts.flags, ADJUST_VECTOR_FIELDS, false);
+
+ opts_set(opts.flags, ADJUST_VECTOR_FIELDS, false);
opts.standard = COMPILER_QCCX;
+
} else {
con_out("Unknown standard: %s\n", argarg);
return false;
continue;
}
if (options_long_gcc("force-crc", &argc, &argv, &argarg)) {
- opts.forcecrc = true;
+ opts.forcecrc = true;
opts.forced_crc = strtol(argarg, NULL, 0);
continue;
}
/* debug turns on -flno */
case 'g':
- options_setflag("LNO", true);
+ opts_setflag("LNO", true);
break;
case 'D':
exit(0);
}
else if (!strncmp(argv[0]+2, "NO_", 3)) {
- if (!options_setflag(argv[0]+5, false)) {
+ if (!opts_setflag(argv[0]+5, false)) {
con_out("unknown flag: %s\n", argv[0]+2);
return false;
}
}
- else if (!options_setflag(argv[0]+2, true)) {
+ else if (!opts_setflag(argv[0]+2, true)) {
con_out("unknown flag: %s\n", argv[0]+2);
return false;
}
break;
}
if (!strncmp(argv[0]+2, "NO_", 3)) {
- if (!options_setwarn(argv[0]+5, false)) {
+ if (!opts_setwarn(argv[0]+5, false)) {
con_out("unknown warning: %s\n", argv[0]+2);
return false;
}
}
- else if (!options_setwarn(argv[0]+2, true)) {
+ else if (!opts_setwarn(argv[0]+2, true)) {
con_out("unknown warning: %s\n", argv[0]+2);
return false;
}
}
if (isdigit(argarg[0])) {
opts.O = atoi(argarg);
- set_optimizations(opts.O);
+ opts_setoptimlevel(opts.O);
} else {
util_strtocmd(argarg, argarg, strlen(argarg)+1);
if (!strcmp(argarg, "HELP")) {
exit(0);
}
else if (!strcmp(argarg, "ALL"))
- set_optimizations(opts.O = 9999);
+ opts_setoptimlevel(opts.O = 9999);
else if (!strncmp(argarg, "NO_", 3)) {
- if (!options_setoptim(argarg+3, false)) {
+ if (!opts_setoptim(argarg+3, false)) {
con_out("unknown optimization: %s\n", argarg+3);
return false;
}
}
else {
- if (!options_setoptim(argarg, true)) {
+ if (!opts_setoptim(argarg, true)) {
con_out("unknown optimization: %s\n", argarg);
return false;
}
int main(int argc, char **argv) {
size_t itr;
- int retval = 0;
- bool opts_output_free = false;
- bool operators_free = false;
- bool progs_src = false;
- FILE *outfile = NULL;
-
- memset(&opts, 0, sizeof(opts));
- opts.output = "progs.dat";
- opts.standard = COMPILER_GMQCC;
- opts.max_array_size = (1024<<3);
+ int retval = 0;
+ bool opts_output_free = false;
+ bool operators_free = false;
+ bool progs_src = false;
+ FILE *outfile = NULL;
app_name = argv[0];
- con_init();
-
- /* default options / warn flags */
- options_set(opts.warn, WARN_UNKNOWN_CONTROL_SEQUENCE, true);
- options_set(opts.warn, WARN_EXTENSIONS, true);
- options_set(opts.warn, WARN_FIELD_REDECLARED, true);
- options_set(opts.warn, WARN_TOO_FEW_PARAMETERS, true);
- options_set(opts.warn, WARN_MISSING_RETURN_VALUES, true);
- options_set(opts.warn, WARN_USED_UNINITIALIZED, true);
- options_set(opts.warn, WARN_LOCAL_CONSTANTS, true);
- options_set(opts.warn, WARN_VOID_VARIABLES, true);
- options_set(opts.warn, WARN_IMPLICIT_FUNCTION_POINTER, true);
- options_set(opts.warn, WARN_VARIADIC_FUNCTION, true);
- options_set(opts.warn, WARN_FRAME_MACROS, true);
- options_set(opts.warn, WARN_UNUSED_VARIABLE, true);
- options_set(opts.warn, WARN_EFFECTLESS_STATEMENT, true);
- options_set(opts.warn, WARN_END_SYS_FIELDS, true);
- options_set(opts.warn, WARN_ASSIGN_FUNCTION_TYPES, true);
- options_set(opts.warn, WARN_PREPROCESSOR, true);
- options_set(opts.warn, WARN_MULTIFILE_IF, true);
- options_set(opts.warn, WARN_DOUBLE_DECLARATION, true);
- options_set(opts.warn, WARN_CONST_VAR, true);
- options_set(opts.warn, WARN_MULTIBYTE_CHARACTER, true);
- options_set(opts.warn, WARN_UNKNOWN_PRAGMAS, true);
-
- options_set(opts.flags, ADJUST_VECTOR_FIELDS, true);
- options_set(opts.flags, FTEPP, false);
- options_set(opts.flags, CORRECT_TERNARY, true);
+ con_init ();
+ opts_init("progs.dat", COMPILER_GMQCC, (1024 << 3));
if (!options_parse(argc, argv)) {
return usage();
/* the standard decides which set of operators to use */
if (opts.standard == COMPILER_GMQCC) {
- operators = c_operators;
+ operators = c_operators;
operator_count = c_operator_count;
} else if (opts.standard == COMPILER_FTEQCC) {
- operators = fte_operators;
+ operators = fte_operators;
operator_count = fte_operator_count;
} else {
- operators = qcc_operators;
+ operators = qcc_operators;
operator_count = qcc_operator_count;
}
}
if (opts.dump) {
- for (itr = 0; itr < COUNT_FLAGS; ++itr) {
- con_out("Flag %s = %i\n", opts_flag_list[itr].name, OPTS_FLAG(itr));
- }
- for (itr = 0; itr < COUNT_WARNINGS; ++itr) {
+ for (itr = 0; itr < COUNT_FLAGS; ++itr)
+ con_out("Flag %s = %i\n", opts_flag_list[itr].name, OPTS_FLAG(itr));
+ for (itr = 0; itr < COUNT_WARNINGS; ++itr)
con_out("Warning %s = %i\n", opts_warn_list[itr].name, OPTS_WARN(itr));
- }
- con_out("output = %s\n", opts.output);
- con_out("optimization level = %i\n", (int)opts.O);
- con_out("standard = %i\n", opts.standard);
+
+ con_out("output = %s\n", opts.output);
+ con_out("optimization level = %d\n", opts.O);
+ con_out("standard = %i\n", opts.standard);
}
if (opts.pp_only) {
}
/* stuff */
-
if (!opts.pp_only) {
for (itr = 0; itr < COUNT_OPTIMIZATIONS; ++itr) {
- if (optimization_count[itr]) {
- con_out("%s: %u\n", opts_opt_list[itr].name, (unsigned int)optimization_count[itr]);
+ if (opts_optimizationcount[itr]) {
+ con_out("%s: %u\n", opts_opt_list[itr].name, (unsigned int)opts_optimizationcount[itr]);
}
}
}
--- /dev/null
+/*
+ * Copyright (C) 2012
+ * Wolfgang Bumiller
+ * 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"
+unsigned int opts_optimizationcount[COUNT_OPTIMIZATIONS];
+opts_cmd_t opts; /* command lien options */
+
+static void opts_ini_init();
+static void opts_setdefault() {
+ memset(&opts, 0, sizeof(opts_cmd_t));
+
+ /* warnings */
+ opts_set(opts.warn, WARN_UNKNOWN_CONTROL_SEQUENCE, true);
+ opts_set(opts.warn, WARN_EXTENSIONS, true);
+ opts_set(opts.warn, WARN_FIELD_REDECLARED, true);
+ opts_set(opts.warn, WARN_TOO_FEW_PARAMETERS, true);
+ opts_set(opts.warn, WARN_MISSING_RETURN_VALUES, true);
+ opts_set(opts.warn, WARN_USED_UNINITIALIZED, true);
+ opts_set(opts.warn, WARN_LOCAL_CONSTANTS, true);
+ opts_set(opts.warn, WARN_VOID_VARIABLES, true);
+ opts_set(opts.warn, WARN_IMPLICIT_FUNCTION_POINTER, true);
+ opts_set(opts.warn, WARN_VARIADIC_FUNCTION, true);
+ opts_set(opts.warn, WARN_FRAME_MACROS, true);
+ opts_set(opts.warn, WARN_UNUSED_VARIABLE, true);
+ opts_set(opts.warn, WARN_EFFECTLESS_STATEMENT, true);
+ opts_set(opts.warn, WARN_END_SYS_FIELDS, true);
+ opts_set(opts.warn, WARN_ASSIGN_FUNCTION_TYPES, true);
+ opts_set(opts.warn, WARN_PREPROCESSOR, true);
+ opts_set(opts.warn, WARN_MULTIFILE_IF, true);
+ opts_set(opts.warn, WARN_DOUBLE_DECLARATION, true);
+ opts_set(opts.warn, WARN_CONST_VAR, true);
+ opts_set(opts.warn, WARN_MULTIBYTE_CHARACTER, true);
+ opts_set(opts.warn, WARN_UNKNOWN_PRAGMAS, true);
+ /* flags */
+ opts_set(opts.flags, ADJUST_VECTOR_FIELDS, true);
+ opts_set(opts.flags, FTEPP, false);
+ opts_set(opts.flags, CORRECT_TERNARY, true);
+}
+
+void opts_init(const char *output, int standard, size_t arraysize) {
+ opts_setdefault();
+
+ opts.output = output;
+ opts.standard = standard;
+ opts.max_array_size = arraysize;
+
+ opts_ini_init();
+}
+
+static bool opts_setflag_all(const char *name, bool on, uint32_t *flags, const opts_flag_def *list, size_t listsize) {
+ size_t i;
+
+ for (i = 0; i < listsize; ++i) {
+ if (!strcmp(name, list[i].name)) {
+ longbit lb = list[i].bit;
+#if 0
+ if (on)
+ flags[lb.idx] |= (1<<(lb.bit));
+ else
+ flags[lb.idx] &= ~(1<<(lb.bit));
+#else
+ if (on)
+ flags[0] |= (1<<lb);
+ else
+ flags[0] &= ~(1<<(lb));
+#endif
+ return true;
+ }
+ }
+ return false;
+}
+bool opts_setflag (const char *name, bool on) {
+ return opts_setflag_all(name, on, opts.flags, opts_flag_list, COUNT_FLAGS);
+}
+bool opts_setwarn (const char *name, bool on) {
+ return opts_setflag_all(name, on, opts.warn, opts_warn_list, COUNT_WARNINGS);
+}
+bool opts_setoptim(const char *name, bool on) {
+ return opts_setflag_all(name, on, opts.optimization, opts_opt_list, COUNT_OPTIMIZATIONS);
+}
+
+void opts_set(uint32_t *flags, size_t idx, bool on) {
+ longbit lb = LONGBIT(idx);
+#if 0
+ if (on)
+ flags[lb.idx] |= (1<<(lb.bit));
+ else
+ flags[lb.idx] &= ~(1<<(lb.bit));
+#else
+ if (on)
+ flags[0] |= (1<<(lb));
+ else
+ flags[0] &= ~(1<<(lb));
+#endif
+}
+
+void opts_setoptimlevel(unsigned int level) {
+ size_t i;
+ for (i = 0; i < COUNT_OPTIMIZATIONS; ++i)
+ opts_set(opts.optimization, i, level >= opts_opt_oflag[i]);
+}
+
+/*
+ * Standard configuration parser and subsystem. Yes, optionally you may
+ * create ini files or cfg (the driver accepts both) for a project opposed
+ * to supplying just a progs.src (since you also may need to supply command
+ * line arguments or set the options of the compiler) [which cannot be done
+ * from a progs.src.
+ */
+static char *opts_ini_rstrip(char *s) {
+ char *p = s + strlen(s);
+ while(p > s && isspace(*--p))
+ *p = '\0';
+ return s;
+}
+
+static char *opts_ini_lskip(const char *s) {
+ while (*s && isspace(*s))
+ s++;
+ return (char*)s;
+}
+
+static char *opts_ini_next(const char *s, char c) {
+ bool last = false;
+ while (*s && *s != c && !(last && *s == ';'))
+ last = !!isspace(*s), s++;
+
+ return (char*)s;
+}
+
+static size_t opts_ini_parse (
+ FILE *filehandle,
+ char *(*loadhandle)(const char *, const char *, const char *),
+ char **errorhandle
+) {
+ size_t linesize;
+ size_t lineno = 1;
+ size_t error = 0;
+ char *line = NULL;
+ char section_data[2048] = "";
+ char oldname_data[2048] = "";
+
+ /* parsing and reading variables */
+ char *parse_beg;
+ char *parse_end;
+ char *read_name;
+ char *read_value;
+
+ while (util_getline(&line, &linesize, filehandle) != EOF) {
+ parse_beg = line;
+
+ /* handle BOM */
+ if (lineno == 1 && (
+ (unsigned char)parse_beg[0] == 0xEF &&
+ (unsigned char)parse_beg[1] == 0xBB &&
+ (unsigned char)parse_beg[2] == 0xBF
+ )
+ ) {
+ parse_beg ++; /* 0xEF */
+ parse_beg ++; /* 0xBB */
+ parse_beg ++; /* 0xBF */
+ }
+
+ if (*(parse_beg = opts_ini_lskip(opts_ini_rstrip(parse_beg))) == ';' || *parse_beg == '#') {
+ /* ignore '#' is a perl extension */
+ } else if (*parse_beg == '[') {
+ /* section found */
+ if (*(parse_end = opts_ini_next(parse_beg + 1, ']')) == ']') {
+ * parse_end = '\0'; /* terminate bro */
+ strncpy(section_data, parse_beg + 1, sizeof(section_data));
+ section_data[sizeof(section_data) - 1] = '\0';
+ *oldname_data = '\0';
+ } else if (!error) {
+ /* otherwise set error to the current line number */
+ error = lineno;
+ }
+ } else if (*parse_beg && *parse_beg != ';') {
+ /* not a comment, must be a name value pair :) */
+ if (*(parse_end = opts_ini_next(parse_beg, '=')) != '=')
+ parse_end = opts_ini_next(parse_beg, ':');
+
+ if (*parse_end == '=' || *parse_end == ':') {
+ *parse_end = '\0'; /* terminate bro */
+ read_name = opts_ini_rstrip(parse_beg);
+ read_value = opts_ini_lskip(parse_end + 1);
+ if (*(parse_end = opts_ini_next(read_value, '\0')) == ';')
+ * parse_end = '\0';
+ opts_ini_rstrip(read_value);
+
+ /* valid name value pair, lets call down to handler */
+ strncpy(oldname_data, read_name, sizeof(oldname_data));
+ oldname_data[sizeof(oldname_data) - 1] ='\0';
+
+ if ((*errorhandle = loadhandle(section_data, read_name, read_value)) && !error)
+ error = lineno;
+ } else if (!error) {
+ /* otherwise set error to the current line number */
+ error = lineno;
+ }
+ }
+ lineno++;
+ }
+ mem_d(line);
+ return error;
+}
+
+/*
+ * returns true/false for a char that contains ("true" or "false" or numeric 0/1)
+ */
+static bool opts_ini_bool(const char *value) {
+ if (!strcmp(value, "true")) return true;
+ if (!strcmp(value, "false")) return false;
+ return !!atoi(value);
+}
+
+static char *opts_ini_load(const char *section, const char *name, const char *value) {
+ char *error = NULL;
+ bool found = false;
+
+ /*
+ * undef all of these because they may still be defined like in my
+ * case they where.
+ */
+ #undef GMQCC_TYPE_FLAGS
+ #undef GMQCC_TYPE_OPTIMIZATIONS
+ #undef GMQCC_TYPE_WARNS
+
+ /* flags */
+ #define GMQCC_TYPE_FLAGS
+ #define GMQCC_DEFINE_FLAG(X) \
+ if (!strcmp(section, "flags") && !strcmp(name, #X)) { \
+ opts_set(opts.flags, X, opts_ini_bool(value)); \
+ found = true; \
+ }
+ #include "opts.def"
+
+ /* warnings */
+ #define GMQCC_TYPE_WARNS
+ #define GMQCC_DEFINE_FLAG(X) \
+ if (!strcmp(section, "warnings") && !strcmp(name, #X)) { \
+ opts_set(opts.warn, WARN_##X, opts_ini_bool(value)); \
+ found = true; \
+ }
+ #include "opts.def"
+
+ /* optimizations */
+ #define GMQCC_TYPE_OPTIMIZATIONS
+ #define GMQCC_DEFINE_FLAG(X,Y) \
+ if (!strcmp(section, "optimizations") && !strcmp(name, #X)) { \
+ opts_set(opts.optimization, OPTIM_##X, opts_ini_bool(value)); \
+ found = true; \
+ }
+ #include "opts.def"
+
+ /* nothing was found ever! */
+ if (!found) {
+ if (strcmp(section, "flags") &&
+ strcmp(section, "warnings") &&
+ strcmp(section, "optimizations"))
+ {
+ vec_upload(error, "invalid section `", 17);
+ vec_upload(error, section, strlen(section));
+ vec_push (error, '`');
+ vec_push (error, '\0');
+ } else {
+ vec_upload(error, "invalid variable `", 18);
+ vec_upload(error, name, strlen(name));
+ vec_push (error, '`');
+ vec_upload(error, " in section: `", 14);
+ vec_upload(error, section, strlen(section));
+ vec_push (error, '`');
+ vec_push (error, '\0');
+ }
+ }
+ return error;
+}
+
+/*
+ * Actual loading subsystem, this finds the ini or cfg file, and properly
+ * loads it and executes it to set compiler options.
+ */
+static void opts_ini_init() {
+ /*
+ * Possible matches are:
+ * gmqcc.ini
+ * gmqcc.cfg
+ */
+
+ char *file;
+ char *error;
+ size_t line;
+ FILE *ini;
+
+ /* try ini */
+ if (!(ini = fopen((file = "gmqcc.ini"), "r")))
+ /* try cfg */
+ if (!(ini = fopen((file = "gmqcc.cfg"), "r")))
+ return;
+
+ con_out("found ini file `%s`\n", file);
+
+ if ((line = opts_ini_parse(ini, &opts_ini_load, &error)) != 0) {
+ /* there was a parse error with the ini file */
+ con_printmsg(LVL_ERROR, file, line, "error", error);
+ vec_free(error);
+ }
+
+ fclose(ini);
+}