]> de.git.xonotic.org Git - xonotic/gmqcc.git/commitdiff
Cleanups of compiler option configuration. Added ini/cfg parser system as well.
authorDale Weiler <killfieldengine@gmail.com>
Tue, 18 Dec 2012 04:57:17 +0000 (04:57 +0000)
committerDale Weiler <killfieldengine@gmail.com>
Tue, 18 Dec 2012 04:57:17 +0000 (04:57 +0000)
Makefile
ast.c
gmqcc.h
ir.c
main.c
opts.c [new file with mode: 0644]

index 482f13259bbe680707736a88b146e96553df5bdb..6ebf7b47a961fc417222b41773f787deb4b0f90b 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -27,7 +27,8 @@ OBJ     =             \
           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
diff --git a/ast.c b/ast.c
index d997baee476621711943abd892db646800d65198..7c5d0b15f031546e7ce4a0bd78cda1e1c789fc9e 100644 (file)
--- a/ast.c
+++ b/ast.c
@@ -1417,14 +1417,14 @@ bool ast_generate_accessors(ast_value *self, ir_builder *ir)
         }
     }
 
-    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;
         }
     }
@@ -1434,14 +1434,14 @@ bool ast_generate_accessors(ast_value *self, ir_builder *ir)
             !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;
 }
 
diff --git a/gmqcc.h b/gmqcc.h
index 02a8c891f57d5ee4638b66cbdfd8fd1ed7631cb4..5f39634528e375f1b01ddcbc07c643bfb007281e 100644 (file)
--- a/gmqcc.h
+++ b/gmqcc.h
@@ -623,10 +623,10 @@ int  con_out   (const char *, ...);
 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 =============================*/
@@ -867,14 +867,22 @@ typedef uint32_t longbit;
 #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,
@@ -919,21 +927,21 @@ static const unsigned int opts_opt_oflag[] = {
 #  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      */
@@ -947,15 +955,13 @@ typedef struct {
     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
diff --git a/ir.c b/ir.c
index 3467d321057045264f4358ae15fa40ecd79fa1fe..2cffb9d6f8238cd1971ce90142db5c4b5e61f62e 100644 (file)
--- a/ir.c
+++ b/ir.c
@@ -209,11 +209,11 @@ static void irerror(lex_ctx ctx, const char *msg, ...)
 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;
 }
 
 /***********************************************************************
@@ -575,7 +575,7 @@ bool ir_function_pass_peephole(ir_function *self)
                 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);
@@ -610,7 +610,7 @@ bool ir_function_pass_peephole(ir_function *self)
                     }
 
                     /* count */
-                    ++optimization_count[OPTIM_PEEPHOLE];
+                    ++opts_optimizationcount[OPTIM_PEEPHOLE];
                     /* change operand */
                     (void)!ir_instr_op(inst, 0, inot->_ops[1], false);
                     /* remove NOT */
@@ -678,7 +678,7 @@ bool ir_function_pass_tailcall(ir_function *self)
                 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);
@@ -700,7 +700,7 @@ bool ir_function_pass_tailcall(ir_function *self)
         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 */
@@ -2897,7 +2897,7 @@ tailcall:
             if (stmt.o2.u1 == stmt.o1.u1 &&
                 OPTS_OPTIMIZATION(OPTIM_PEEPHOLE))
             {
-                ++optimization_count[OPTIM_PEEPHOLE];
+                ++opts_optimizationcount[OPTIM_PEEPHOLE];
                 continue;
             }
         }
diff --git a/main.c b/main.c
index 40322644b098115cf60b754204daec647110df6f..6ae009145479850cca3c0f3dc5aaaf3db6eab007 100644 (file)
--- a/main.c
+++ b/main.c
 #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;
@@ -84,38 +81,7 @@ static int usage() {
     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_;
@@ -166,29 +132,6 @@ static bool options_long_gcc(const char *optname, int *argc_, char ***argv_, cha
     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;
@@ -205,26 +148,34 @@ static bool options_parse(int argc, char **argv) {
         --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;
@@ -232,7 +183,7 @@ static bool options_parse(int argc, char **argv) {
                 continue;
             }
             if (options_long_gcc("force-crc", &argc, &argv, &argarg)) {
-                opts.forcecrc = true;
+                opts.forcecrc   = true;
                 opts.forced_crc = strtol(argarg, NULL, 0);
                 continue;
             }
@@ -308,7 +259,7 @@ static bool options_parse(int argc, char **argv) {
 
                 /* debug turns on -flno */
                 case 'g':
-                    options_setflag("LNO", true);
+                    opts_setflag("LNO", true);
                     break;
 
                 case 'D':
@@ -340,12 +291,12 @@ static bool options_parse(int argc, char **argv) {
                         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;
                     }
@@ -379,12 +330,12 @@ static bool options_parse(int argc, char **argv) {
                         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;
                     }
@@ -397,7 +348,7 @@ static bool options_parse(int argc, char **argv) {
                     }
                     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")) {
@@ -409,15 +360,15 @@ static bool options_parse(int argc, char **argv) {
                             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;
                             }
@@ -517,46 +468,15 @@ static bool progs_nextline(char **out, size_t *alen,FILE *src) {
 
 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();
@@ -564,13 +484,13 @@ int main(int argc, char **argv) {
 
     /* 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;
     }
 
@@ -595,15 +515,14 @@ int main(int argc, char **argv) {
     }
 
     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) {
@@ -758,11 +677,10 @@ srcdone:
     }
 
     /* 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]);
             }
         }
     }
diff --git a/opts.c b/opts.c
new file mode 100644 (file)
index 0000000..93433b6
--- /dev/null
+++ b/opts.c
@@ -0,0 +1,329 @@
+/*
+ * 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);
+}