]> de.git.xonotic.org Git - xonotic/gmqcc.git/commitdiff
Merge remote-tracking branch 'upstream/master'
authormatthiaskrgr <matthias.krueger@famsik.de>
Fri, 28 Dec 2012 22:33:43 +0000 (23:33 +0100)
committermatthiaskrgr <matthias.krueger@famsik.de>
Fri, 28 Dec 2012 22:33:43 +0000 (23:33 +0100)
22 files changed:
Makefile
ast.c
ast.h
code.c
doc/gmqcc.1
exec.c
file.c
ftepp.c
gmqcc.h
gmqcc.ini.example
ir.c
ir.h
lexer.c
main.c
opts.c
opts.def
parser.c
splint.sh [new file with mode: 0755]
test.c
tests/noref.tmpl
utf8.c
util.c

index d35726d89526aa37a5a1c7ded0973a6cc1efeb6c..0e23a92a3b9b204ec4af272c6bbe20e1cc8f119d 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -9,7 +9,8 @@ CYGWIN  = $(findstring CYGWIN,  $(UNAME))
 MINGW   = $(findstring MINGW32, $(UNAME))
 
 CC     ?= clang
-CFLAGS += -Wall -Wextra -I. -pedantic-errors -fno-strict-aliasing
+CFLAGS += -Wall -Wextra -I. -fno-strict-aliasing -fsigned-char
+CFLAGS += -DGMQCC_GITINFO="`git describe`"
 #turn on tons of warnings if clang is present
 # but also turn off the STUPID ONES
 ifeq ($(CC), clang)
@@ -22,7 +23,16 @@ ifeq ($(CC), clang)
                -Wno-missing-prototypes       \
                -Wno-float-equal              \
                -Wno-cast-align
+else
+       #Tiny C Compiler doesn't know what -pedantic-errors is
+       # and instead of ignoring .. just errors.
+       ifneq ($(CC), tcc)
+               CFLAGS +=-pedantic-errors
+       else
+               CFLAGS += -Wno-pointer-sign -fno-common
+       endif
 endif
+
 ifeq ($(track), no)
     CFLAGS += -DNOTRACK
 endif
@@ -54,6 +64,12 @@ ifneq ("$(MINGW)", "")
        GMQCC     = gmqcc.exe
        TESTSUITE = testsuite.exe
 else
+       #arm support for linux .. we need to allow unaligned accesses
+       #to memory otherwise we just segfault everywhere
+       ifneq (, $(findstring arm, $(shell uname -m)))
+               CFLAGS += -munaligned-access
+       endif
+
        QCVM      = qcvm
        GMQCC     = gmqcc
        TESTSUITE = testsuite
diff --git a/ast.c b/ast.c
index b6a8a786c7d76b55a1fb7af884e82958848255af..89d738cd5d40cc060e9236f41f168275b7ae167a 100644 (file)
--- a/ast.c
+++ b/ast.c
@@ -764,12 +764,13 @@ void ast_loop_delete(ast_loop *self)
     mem_d(self);
 }
 
-ast_breakcont* ast_breakcont_new(lex_ctx ctx, bool iscont)
+ast_breakcont* ast_breakcont_new(lex_ctx ctx, bool iscont, unsigned int levels)
 {
     ast_instantiate(ast_breakcont, ctx, ast_breakcont_delete);
     ast_expression_init((ast_expression*)self, (ast_expression_codegen*)&ast_breakcont_codegen);
 
     self->is_continue = iscont;
+    self->levels      = levels;
 
     return self;
 }
@@ -1025,8 +1026,8 @@ ast_function* ast_function_new(lex_ctx ctx, const char *name, ast_value *vtype)
     self->ir_func = NULL;
     self->curblock = NULL;
 
-    self->breakblock    = NULL;
-    self->continueblock = NULL;
+    self->breakblocks    = NULL;
+    self->continueblocks = NULL;
 
     vtype->hasvalue = true;
     vtype->constval.vfunc = self;
@@ -1051,6 +1052,8 @@ void ast_function_delete(ast_function *self)
     for (i = 0; i < vec_size(self->blocks); ++i)
         ast_delete(self->blocks[i]);
     vec_free(self->blocks);
+    vec_free(self->breakblocks);
+    vec_free(self->continueblocks);
     mem_d(self);
 }
 
@@ -1098,6 +1101,10 @@ bool ast_value_codegen(ast_value *self, ast_function *func, bool lvalue, ir_valu
 {
     (void)func;
     (void)lvalue;
+    if (self->expression.vtype == TYPE_NIL) {
+        *out = func->ir_func->owner->nil;
+        return true;
+    }
     /* NOTE: This is the codegen for a variable used in an expression.
      * It is not the codegen to generate the value. For this purpose,
      * ast_local_codegen and ast_global_codegen are to be used before this
@@ -1119,6 +1126,11 @@ bool ast_global_codegen(ast_value *self, ir_builder *ir, bool isfield)
 {
     ir_value *v = NULL;
 
+    if (self->expression.vtype == TYPE_NIL) {
+        compile_error(ast_ctx(self), "internal error: trying to generate a variable of TYPE_NIL");
+        return false;
+    }
+
     if (self->hasvalue && self->expression.vtype == TYPE_FUNCTION)
     {
         ir_function *func = ir_builder_create_function(ir, self->name, self->expression.next->expression.vtype);
@@ -1313,6 +1325,12 @@ error: /* clean up */
 bool ast_local_codegen(ast_value *self, ir_function *func, bool param)
 {
     ir_value *v = NULL;
+
+    if (self->expression.vtype == TYPE_NIL) {
+        compile_error(ast_ctx(self), "internal error: trying to generate a variable of TYPE_NIL");
+        return false;
+    }
+
     if (self->hasvalue && self->expression.vtype == TYPE_FUNCTION)
     {
         /* Do we allow local functions? I think not...
@@ -2406,9 +2424,6 @@ bool ast_loop_codegen(ast_loop *self, ast_function *func, bool lvalue, ir_value
     ir_block *bcontinue     = NULL;
     ir_block *bbreak        = NULL;
 
-    ir_block *old_bcontinue = NULL;
-    ir_block *old_bbreak    = NULL;
-
     ir_block *tmpblock      = NULL;
 
     (void)lvalue;
@@ -2501,12 +2516,11 @@ bool ast_loop_codegen(ast_loop *self, ast_function *func, bool lvalue, ir_value
         /* enter */
         func->curblock = bbody;
 
-        old_bbreak          = func->breakblock;
-        old_bcontinue       = func->continueblock;
-        func->breakblock    = bbreak;
-        func->continueblock = bcontinue;
-        if (!func->continueblock)
-            func->continueblock = bbody;
+        vec_push(func->breakblocks,    bbreak);
+        if (bcontinue)
+            vec_push(func->continueblocks, bcontinue);
+        else
+            vec_push(func->continueblocks, bbody);
 
         /* generate */
         if (self->body) {
@@ -2516,8 +2530,8 @@ bool ast_loop_codegen(ast_loop *self, ast_function *func, bool lvalue, ir_value
         }
 
         end_bbody = func->curblock;
-        func->breakblock    = old_bbreak;
-        func->continueblock = old_bcontinue;
+        vec_pop(func->breakblocks);
+        vec_pop(func->continueblocks);
     }
 
     /* post-loop-condition */
@@ -2643,9 +2657,9 @@ bool ast_breakcont_codegen(ast_breakcont *self, ast_function *func, bool lvalue,
     self->expression.outr = (ir_value*)1;
 
     if (self->is_continue)
-        target = func->continueblock;
+        target = func->continueblocks[vec_size(func->continueblocks)-1-self->levels];
     else
-        target = func->breakblock;
+        target = func->breakblocks[vec_size(func->breakblocks)-1-self->levels];
 
     if (!target) {
         compile_error(ast_ctx(self), "%s is lacking a target block", (self->is_continue ? "continue" : "break"));
@@ -2668,7 +2682,6 @@ bool ast_switch_codegen(ast_switch *self, ast_function *func, bool lvalue, ir_va
 
     ir_value *dummy     = NULL;
     ir_value *irop      = NULL;
-    ir_block *old_break = NULL;
     ir_block *bout      = NULL;
     ir_block *bfall     = NULL;
     size_t    bout_id;
@@ -2711,8 +2724,7 @@ bool ast_switch_codegen(ast_switch *self, ast_function *func, bool lvalue, ir_va
         return false;
 
     /* setup the break block */
-    old_break        = func->breakblock;
-    func->breakblock = bout;
+    vec_push(func->breakblocks, bout);
 
     /* Now create all cases */
     for (c = 0; c < vec_size(self->cases); ++c) {
@@ -2817,7 +2829,7 @@ bool ast_switch_codegen(ast_switch *self, ast_function *func, bool lvalue, ir_va
     func->curblock = bout;
 
     /* restore the break block */
-    func->breakblock = old_break;
+    vec_pop(func->breakblocks);
 
     /* Move 'bout' to the end, it's nicer */
     vec_remove(func->ir_func->blocks, bout_id, 1);
diff --git a/ast.h b/ast.h
index bc37eea72888fb3800028997f1f9b4559567224c..b9b2ab875a6df7dcf1f6fd1c1e8ade2bfd93d695 100644 (file)
--- a/ast.h
+++ b/ast.h
@@ -95,7 +95,7 @@ typedef struct
     bool             side_effects;
 } ast_node_common;
 
-#define ast_delete(x) ( ( (ast_node*)(x) ) -> node.destroy )((ast_node*)(x))
+#define ast_delete(x) (*( ((ast_node*)(x))->node.destroy ))((ast_node*)(x))
 #define ast_unref(x) do                     \
 {                                           \
     if (! (((ast_node*)(x))->node.keep) ) { \
@@ -142,6 +142,7 @@ typedef struct
 } ast_expression_common;
 #define AST_FLAG_VARIADIC     (1<<0)
 #define AST_FLAG_NORETURN     (1<<1)
+#define AST_FLAG_INLINE       (1<<2)
 
 /* Value
  *
@@ -474,9 +475,10 @@ bool ast_loop_codegen(ast_loop*, ast_function*, bool lvalue, ir_value**);
 struct ast_breakcont_s
 {
     ast_expression_common expression;
-    bool is_continue;
+    bool         is_continue;
+    unsigned int levels;
 };
-ast_breakcont* ast_breakcont_new(lex_ctx ctx, bool iscont);
+ast_breakcont* ast_breakcont_new(lex_ctx ctx, bool iscont, unsigned int levels);
 void ast_breakcont_delete(ast_breakcont*);
 
 bool ast_breakcont_codegen(ast_breakcont*, ast_function*, bool lvalue, ir_value**);
@@ -607,8 +609,8 @@ struct ast_function_s
 
     ir_function *ir_func;
     ir_block    *curblock;
-    ir_block    *breakblock;
-    ir_block    *continueblock;
+    ir_block    **breakblocks;
+    ir_block    **continueblocks;
 
 #if 0
     /* In order for early-out logic not to go over
diff --git a/code.c b/code.c
index e6cd6a313cd2f6812a69d1482423185894bd1a21..092555170035850f4c441246977f1309b247eecb 100644 (file)
--- a/code.c
+++ b/code.c
@@ -53,7 +53,7 @@ void code_pop_statement()
 }
 
 void code_init() {
-    prog_section_function  empty_function  = {0,0,0,0,0,0,0,{0}};
+    prog_section_function  empty_function  = {0,0,0,0,0,0,0,{0,0,0,0,0,0,0,0}};
     prog_section_statement empty_statement = {0,{0},{0},{0}};
     prog_section_def       empty_def       = {0, 0, 0};
     int                    i               = 0;
@@ -272,6 +272,8 @@ bool code_write(const char *filename, const char *lnofile) {
     vec_free(code_functions);
     vec_free(code_globals);
     vec_free(code_chars);
+    util_htdel(code_string_cache);
+
     file_close(fp);
     return true;
 }
index b1ae949e303fad4448cc3c0cb6c74d1d3f883738..697a3fc44cf1b326c6fba3605a44ef9087ed6c54 100644 (file)
@@ -258,6 +258,18 @@ You won't need this.
 Warn on an unknown attribute. The warning will inlclude only the first
 token inside the enclosing attribute-brackets. This may change when
 the actual attribute syntax is better defined.
+.TP
+.B -Wreserved-names
+Warn when using reserved names such as 'nil'.
+.TP
+.B -Wuninitialized-constant
+Warn about global constants (using the 'const' keyword) with no
+assigned value.
+.TP
+.B -Wuninitialized-global
+Warn about global variables with no initializing value. This is off by
+default, and is added mostly to help find null-values which are
+supposed to be replaced by the untyped 'nil' constant.
 .SH COMPILE FLAGS
 .TP
 .B -fdarkplaces-string-table-bug
@@ -373,6 +385,36 @@ compilation to stop. When disabling this flag by using
 \-fno-bail-on-werror, compilation will continue until the end, but no
 output is generated. Instead the first such error message's context is
 shown.
+.TP
+.B -floop-labels
+Allow loops to be labeled, and allow 'break' and 'continue' to take an
+optional label to decide which loop to actually jump out of or
+continue.
+.sp
+.in +4
+.nf
+for :outer (i = 0; i < n; ++i) {
+    while (inner) {
+        ...;
+        if (something)
+            continue outer;
+    }
+}
+.fi
+.in
+.TP
+.B -funtyped-nil
+Adds a global named 'nil' which is of no type and can be assigned to
+anything. No typechecking will be performed on assignments. Assigning
+to it is forbidden, using it in any other kind of expression is also
+not allowed.
+.TP
+.B -fpermissive
+Various effects, usually to weaken some conditions.
+.RS
+.IP "with -funtyped-nil"
+Allow local variables named 'nil'. (This will not allow declaring a
+global of that name.)
 .SH OPTIMIZATIONS
 .TP
 .B -Opeephole
diff --git a/exec.c b/exec.c
index 7931486e411ab65b382755020c70a0e88eff1f3a..2957d4c5bf315431353a5d6d7d9c8aaa7528adbc 100644 (file)
--- a/exec.c
+++ b/exec.c
@@ -359,7 +359,7 @@ static void trace_print_global(qc_program *prog, unsigned int glob, int vtype)
 done:
     if (len < (int)sizeof(spaces)-1) {
         spaces[sizeof(spaces)-1-len] = 0;
-        printf(spaces);
+        file_puts(stdout, spaces);
         spaces[sizeof(spaces)-1-len] = ' ';
     }
 }
diff --git a/file.c b/file.c
index 51ebc955b53855da07fd653c626bfced13f81711..b551b149b6fba4dc0c14e2c6b2357d158f169f49 100644 (file)
--- a/file.c
+++ b/file.c
@@ -78,7 +78,7 @@
         FILE *handle = NULL;
         file_init();
 
-        return ((fopen_s(&handle, filename, mode) != 0) ? NULL : handle;
+        return (fopen_s(&handle, filename, mode) != 0) ? NULL : handle;
     }
 
     size_t file_read(void *buffer, size_t size, size_t count, FILE *fp) {
diff --git a/ftepp.c b/ftepp.c
index ab51bd916756c4e8ef40508a6bec67ea21a6ceb5..6d5026479807434c98e0af53ec52ea8c3428bad7 100644 (file)
--- a/ftepp.c
+++ b/ftepp.c
@@ -82,8 +82,8 @@ static uint32_t ftepp_predef_randval  = 0;
 
 /* __LINE__ */
 char *ftepp_predef_line(lex_file *context) {
-    char   *value = (char*)mem_a(128);
-    sprintf(value, "%d", (int)context->line);
+    char   *value;
+    util_asprintf(&value, "%d", (int)context->line);
     return value;
 }
 /* __FILE__ */
@@ -97,34 +97,34 @@ char *ftepp_predef_file(lex_file *context) {
 }
 /* __COUNTER_LAST__ */
 char *ftepp_predef_counterlast(lex_file *context) {
-    char   *value = (char*)mem_a(128);
-    sprintf(value, "%u", ftepp_predef_countval);
+    char   *value;
+    util_asprintf(&value, "%u", ftepp_predef_countval);
 
     (void)context;
     return value;
 }
 /* __COUNTER__ */
 char *ftepp_predef_counter(lex_file *context) {
-    char   *value = (char*)mem_a(128);
+    char   *value;
     ftepp_predef_countval ++;
-    sprintf(value, "%u", ftepp_predef_countval);
+    util_asprintf(&value, "%u", ftepp_predef_countval);
     (void)context;
 
     return value;
 }
 /* __RANDOM__ */
 char *ftepp_predef_random(lex_file *context) {
-    char  *value = (char*)mem_a(128);
+    char  *value;
     ftepp_predef_randval = (util_rand() % 0xFF) + 1;
-    sprintf(value, "%u", ftepp_predef_randval);
+    util_asprintf(&value, "%u", ftepp_predef_randval);
 
     (void)context;
     return value;
 }
 /* __RANDOM_LAST__ */
 char *ftepp_predef_randomlast(lex_file *context) {
-    char   *value = (char*)mem_a(128);
-    sprintf(value, "%u", ftepp_predef_randval);
+    char   *value;
+    util_asprintf(&value, "%u", ftepp_predef_randval);
 
     (void)context;
     return value;
diff --git a/gmqcc.h b/gmqcc.h
index 9dc184657ccbb2a810dd7185bb0710d65a68ebe9..fe9e8a04d4da5e029745a0654ad9a4539ae3710f 100644 (file)
--- a/gmqcc.h
+++ b/gmqcc.h
 #define GMQCC_VERSION \
     GMQCC_VERSION_BUILD(GMQCC_VERSION_MAJOR, GMQCC_VERSION_MINOR, GMQCC_VERSION_PATCH)
 
+#ifndef GMQCC_GITINFO
+# define GMQCC_GITINFO "(no git info)"
+#endif
+
 /*
- * We cannoy rely on C99 at all, since compilers like MSVC
+ * We cannot rely on C99 at all, since compilers like MSVC
  * simply don't support it.  We define our own boolean type
  * as a result (since we cannot include <stdbool.h>). For
  * compilers that are in 1999 mode (C99 compliant) we can use
@@ -74,8 +78,6 @@
 #   endif /* !__STDC_VERSION__ */
 #endif    /* !__cplusplus      */
 
-
-
 /*
  * Of some functions which are generated we want to make sure
  * that the result isn't ignored. To find such function calls,
@@ -263,6 +265,10 @@ uint16_t util_crc16(uint16_t crc, const char *data, size_t len);
 void     util_seed(uint32_t);
 uint32_t util_rand();
 
+int util_vasprintf(char **ret, const char *fmt, va_list);
+int util_asprintf (char **ret, const char *fmt, ...);
+
+
 #ifdef NOTRACK
 #    define mem_a(x)    malloc (x)
 #    define mem_d(x)    free   ((void*)x)
@@ -390,6 +396,8 @@ enum {
     TYPE_UNION    ,
     TYPE_ARRAY    ,
 
+    TYPE_NIL      , /* it's its own type / untyped */
+
     TYPE_COUNT
 };
 
index d1e291425d14030fa342e343ac0c4bab42e921ba..3332231e4dbbd013d1c13c92686c54058028d0cb 100644 (file)
     # Enabling this corrects ternary percedence bugs present in fteqcc.
     CORRECT_TERNARY              = true
 
+    # Prevent the creation of _x, _y and _z progdefs for vectors
+    SINGLE_VECTOR_DEFS           = false
+
+    # Cast vectors to real booleans when used in logic expressions.
+    # This is achieved by using NOT_V.
+    CORRECT_LOGIC                = false
+
+    # Always treat empty strings as true. Usuall !"" yields true, because
+    # the string-NOT instruction considers empty strings to be false, while
+    # an empty string as condition for 'if' will be considered true, since
+    # only the numerical value of the global is looked at.
+    TRUE_EMPTY_STRINGS           = false
+
+    # Opposite of the above, empty strings are always false. Similar to
+    # CORRECT_LOGIC this will always use NOT_S to cast a string to a real
+    # boolean value.
+    FALSE_EMPTY_STRINGS          = false
+
+    # Recognize utf-8 characters in character constants, and encode
+    # codepoint escape sequences in strings as utf-8. This essentially allows
+    # \{1234} escape sequences to be higher than 255.
+    UTF8
+
+    # When a warning is printed and it is set to be treated as error via
+    # a -Werror switch, compilation will be stopped, unless this is false.
+    # When this is false, the rest of the code will be compiled, and at the end
+    # the file and line of the first warning will be shown.
+    BAIL_ON_WERROR               = true
+
+    # Allow loops and switches to be labeled and break and continue to take an
+    # optional label to target a specific loop/switch.
+    LOOP_LABELS                  = false
+
+    # Enable the 'nil' null constant, which has no type. It can be used as the
+    # right hand of any assignment regardless of the required type.
+    UNTYPED_NIL                  = false
+
+    # Be "permissive". For instance, when -funtyped-nil is used, this allows local
+    # variables with the name 'nil' to be declared.
+    PREMISSIVE                   = false
+
 # These are all the warnings, usually present via the -W prefix from
 # the command line.
 [warnings]
     # Enables preprocessor "#warnings"
     CPP                          = true
 
+    # With the [[attribute]] syntax enabled, warn when an unknown
+    # attribute is encountered. Its first token will be included in the
+    # message.
+    UNKNOWN_ATTRIBUTE            = true
+
+    # Warn when declaring variables or fields with a reserved name like 'nil'
+    RESERVED_NAMES               = true
+
+    # Warn about 'const'-qualified global variables with no initializing value.
+    UNINITIALIZED_CONSTANT       = true
+
+    # Warn about non-constant global variables with no initializing value.
+    UNINITIALIZED_GLOBAL         = true
+
 # Finally these are all the optimizations, usually present via the -O
 # prefix from the command line.
 [optimizations]
     # Enables tail recrusion optimizationd.
     TAIL_RECURSION               = true
 
-    # Enables tail-call optimizations.
+    # Enables tail-call optimizations. (Not implemented)
     TAIL_CALLS                   = true
+
+    # Every function where it is safe to do so will share its local data section
+    # with the others. The criteria are currently that the function must not have
+    # any possibly uninitialized locals, or local arrays regardless of how they
+    # are initialized.
+    OVERLAP_LOCALS               = false
+
+    # Strip out the names of constants to save some space in the progs.dat
+    STRIP_CONSTANT_NAMES         = true
+
+    # Aggressivly reuse strings in the string-section
+    OVERLAP_STRINGS              = true
+
+    # Have expressions which are used as function parameters evaluate directly
+    # into the parameter-globals if possible.
+    # This avoids a whole lot of copying.
+    CALL_STORES                  = true
+
+    # Do not create a RETURN instruction at the end functions of return-type void.
+    VOID_RETURN                  = true
diff --git a/ir.c b/ir.c
index 6081b5545bc049ef78b105a0fdd1275d1dab8ad1..87ad615a4b77ba8d4238706a26a42b27aba79743 100644 (file)
--- a/ir.c
+++ b/ir.c
@@ -42,7 +42,9 @@ const char *type_name[TYPE_COUNT] = {
     "variant",
     "struct",
     "union",
-    "array"
+    "array",
+
+    "nil"
 };
 
 size_t type_sizeof_[TYPE_COUNT] = {
@@ -59,6 +61,7 @@ size_t type_sizeof_[TYPE_COUNT] = {
     0, /* TYPE_STRUCT   */
     0, /* TYPE_UNION    */
     0, /* TYPE_ARRAY    */
+    0, /* TYPE_NIL      */
 };
 
 uint16_t type_store_instr[TYPE_COUNT] = {
@@ -81,6 +84,7 @@ uint16_t type_store_instr[TYPE_COUNT] = {
     AINSTR_END, /* struct */
     AINSTR_END, /* union  */
     AINSTR_END, /* array  */
+    AINSTR_END, /* nil    */
 };
 
 uint16_t field_store_instr[TYPE_COUNT] = {
@@ -103,6 +107,7 @@ uint16_t field_store_instr[TYPE_COUNT] = {
     AINSTR_END, /* struct */
     AINSTR_END, /* union  */
     AINSTR_END, /* array  */
+    AINSTR_END, /* nil    */
 };
 
 uint16_t type_storep_instr[TYPE_COUNT] = {
@@ -125,6 +130,7 @@ uint16_t type_storep_instr[TYPE_COUNT] = {
     AINSTR_END, /* struct */
     AINSTR_END, /* union  */
     AINSTR_END, /* array  */
+    AINSTR_END, /* nil    */
 };
 
 uint16_t type_eq_instr[TYPE_COUNT] = {
@@ -147,6 +153,7 @@ uint16_t type_eq_instr[TYPE_COUNT] = {
     AINSTR_END, /* struct */
     AINSTR_END, /* union  */
     AINSTR_END, /* array  */
+    AINSTR_END, /* nil    */
 };
 
 uint16_t type_ne_instr[TYPE_COUNT] = {
@@ -169,6 +176,7 @@ uint16_t type_ne_instr[TYPE_COUNT] = {
     AINSTR_END, /* struct */
     AINSTR_END, /* union  */
     AINSTR_END, /* array  */
+    AINSTR_END, /* nil    */
 };
 
 uint16_t type_not_instr[TYPE_COUNT] = {
@@ -191,6 +199,7 @@ uint16_t type_not_instr[TYPE_COUNT] = {
     AINSTR_END, /* struct */
     AINSTR_END, /* union  */
     AINSTR_END, /* array  */
+    AINSTR_END, /* nil    */
 };
 
 /* protos */
@@ -300,6 +309,9 @@ ir_builder* ir_builder_new(const char *modulename)
         return NULL;
     }
 
+    self->nil = ir_value_var("nil", store_value, TYPE_NIL);
+    self->nil->cvq = CV_CONST;
+
     return self;
 }
 
@@ -325,6 +337,7 @@ void ir_builder_delete(ir_builder* self)
     for (i = 0; i != vec_size(self->fields); ++i) {
         ir_value_delete(self->fields[i]);
     }
+    ir_value_delete(self->nil);
     vec_free(self->fields);
     vec_free(self->filenames);
     vec_free(self->filestrings);
@@ -2481,7 +2494,9 @@ static bool ir_block_life_propagate(ir_block *self, ir_block *prev, bool *change
     size_t i, o, p, mem;
     /* bitmasks which operands are read from or written to */
     size_t read, write;
-    char dbg_ind[16] = { '#', '0' };
+    char dbg_ind[16];
+    dbg_ind[0] = '#';
+    dbg_ind[1] = '0';
     (void)dbg_ind;
 
     if (prev)
diff --git a/ir.h b/ir.h
index 05b7447d2e2daa391553b4400bae360f8a3a20ec..4a1778e9d11d0b9e011745546802560e1feb10a2 100644 (file)
--- a/ir.h
+++ b/ir.h
@@ -22,7 +22,7 @@
  */
 #ifndef GMQCC_IR_HDR
 #define GMQCC_IR_HDR
-
+#include "gmqcc.h"
 /* ir_value */
 
 typedef struct
@@ -322,7 +322,9 @@ typedef struct ir_builder_s
     const char **filenames;
     qcint       *filestrings;
     /* we cache the #IMMEDIATE string here */
-    qcint str_immediate;
+    qcint        str_immediate;
+    /* there should just be this one nil */
+    ir_value    *nil;
 } ir_builder;
 
 ir_builder* ir_builder_new(const char *modulename);
diff --git a/lexer.c b/lexer.c
index a2e69d5f17f1c5bce8fb555cfebfeaa77539f928..43182c335ae93ef94bf262ce82c065375a9b1423 100644 (file)
--- a/lexer.c
+++ b/lexer.c
@@ -736,6 +736,8 @@ static bool lex_finish_frames(lex_file *lex)
         vec_shrinkto(lex->tok.value, 0);
         vec_push(lex->frames, m);
     } while (true);
+
+    return false;
 }
 
 static int GMQCC_WARN lex_finish_string(lex_file *lex, int quote)
diff --git a/main.c b/main.c
index af4a34cb1f094f02b56d7c8a3546abacd0594645..194679582a8ab4592417677c31c07b329f59d7e0 100644 (file)
--- a/main.c
+++ b/main.c
@@ -158,7 +158,11 @@ static bool options_parse(int argc, char **argv) {
             if (options_long_gcc("std", &argc, &argv, &argarg)) {
                 if (!strcmp(argarg, "gmqcc") || !strcmp(argarg, "default")) {
 
-                    opts_set(opts.flags, ADJUST_VECTOR_FIELDS,  true);
+                    opts_set(opts.flags, ADJUST_VECTOR_FIELDS, true);
+                    opts_set(opts.flags, CORRECT_LOGIC,        true);
+                    opts_set(opts.flags, FALSE_EMPTY_STRINGS,  false);
+                    opts_set(opts.flags, TRUE_EMPTY_STRINGS,   true);
+                    opts_set(opts.flags, LOOP_LABELS,          true);
                     opts.standard = COMPILER_GMQCC;
 
                 } else if (!strcmp(argarg, "qcc")) {
@@ -523,7 +527,7 @@ int main(int argc, char **argv) {
 
     if (OPTS_FLAG(TRUE_EMPTY_STRINGS) && OPTS_FLAG(FALSE_EMPTY_STRINGS)) {
         con_err("-ftrue-empty-strings and -ffalse-empty-strings are mutually exclusive");
-        exit(1);
+        exit(EXIT_FAILURE);
     }
 
     /* the standard decides which set of operators to use */
@@ -546,7 +550,7 @@ int main(int argc, char **argv) {
                 operators[operator_count-1].id != opid2(':','?'))
             {
                 con_err("internal error: operator precedence table wasn't updated correctly!\n");
-                exit(1);
+                exit(EXIT_FAILURE);
             }
             operators_free = true;
             newops = (oper_info*)mem_a(sizeof(operators[0]) * operator_count);
diff --git a/opts.c b/opts.c
index 334186b8cf9a034764d38c53734026d350631f74..390e28413c957aa4568c2da28d882cb6bd5c1c09 100644 (file)
--- a/opts.c
+++ b/opts.c
@@ -54,6 +54,8 @@ static void opts_setdefault() {
     opts_set(opts.warn,  WARN_UNREACHABLE_CODE,          true);
     opts_set(opts.warn,  WARN_CPP,                       true);
     opts_set(opts.warn,  WARN_UNKNOWN_ATTRIBUTE,         true);
+    opts_set(opts.warn,  WARN_RESERVED_NAMES,            true);
+    opts_set(opts.warn,  WARN_UNINITIALIZED_CONSTANT,    true);
     /* flags */
     opts_set(opts.flags, ADJUST_VECTOR_FIELDS,           true);
     opts_set(opts.flags, FTEPP,                          false);
index 26994c428bdbeb1743109880d716bfdb30f9a25e..3c60deb7307f9759ebbdb080a0da3f6e5f86a7f9 100644 (file)
--- a/opts.def
+++ b/opts.def
@@ -27,7 +27,6 @@
 
 /* codegen flags */
 #ifdef GMQCC_TYPE_FLAGS
-    GMQCC_DEFINE_FLAG(OVERLAP_LOCALS)
     GMQCC_DEFINE_FLAG(DARKPLACES_STRING_TABLE_BUG)
     GMQCC_DEFINE_FLAG(ADJUST_VECTOR_FIELDS)
     GMQCC_DEFINE_FLAG(FTEPP)
@@ -46,6 +45,9 @@
     GMQCC_DEFINE_FLAG(FALSE_EMPTY_STRINGS)
     GMQCC_DEFINE_FLAG(UTF8)
     GMQCC_DEFINE_FLAG(BAIL_ON_WERROR)
+    GMQCC_DEFINE_FLAG(LOOP_LABELS)
+    GMQCC_DEFINE_FLAG(UNTYPED_NIL)
+    GMQCC_DEFINE_FLAG(PERMISSIVE)
 #endif
 
 /* warning flags */
@@ -77,6 +79,9 @@
     GMQCC_DEFINE_FLAG(UNREACHABLE_CODE)
     GMQCC_DEFINE_FLAG(CPP)
     GMQCC_DEFINE_FLAG(UNKNOWN_ATTRIBUTE)
+    GMQCC_DEFINE_FLAG(RESERVED_NAMES)
+    GMQCC_DEFINE_FLAG(UNINITIALIZED_CONSTANT)
+    GMQCC_DEFINE_FLAG(UNINITIALIZED_GLOBAL)
 #endif
 
 #ifdef GMQCC_TYPE_OPTIMIZATIONS
index 6a9157797c4517d12a3582dce6840f74e40bb1dc..757178c9946e8c8bdaf59b89bf04fbf036a0ecf5 100644 (file)
--- a/parser.c
+++ b/parser.c
@@ -51,6 +51,7 @@ typedef struct {
     ast_value *imm_float_zero;
     ast_value *imm_float_one;
     ast_value *imm_vector_zero;
+    ast_value *nil;
 
     size_t crc_globals;
     size_t crc_fields;
@@ -60,8 +61,10 @@ typedef struct {
     /* All the labels the function defined...
      * Should they be in ast_function instead?
      */
-    ast_label **labels;
-    ast_goto  **gotos;
+    ast_label  **labels;
+    ast_goto   **gotos;
+    const char **breaks;
+    const char **continues;
 
     /* A list of hashtables for each scope */
     ht *variables;
@@ -101,7 +104,7 @@ static void parser_enterblock(parser_t *parser);
 static bool parser_leaveblock(parser_t *parser);
 static void parser_addlocal(parser_t *parser, const char *name, ast_expression *e);
 static bool parse_typedef(parser_t *parser);
-static bool parse_variable(parser_t *parser, ast_block *localblock, bool nofields, int qualifier, ast_value *cached_typedef, bool noref, bool noreturn, bool is_static);
+static bool parse_variable(parser_t *parser, ast_block *localblock, bool nofields, int qualifier, ast_value *cached_typedef, bool noref, bool is_static, uint32_t qflags);
 static ast_block* parse_block(parser_t *parser);
 static bool parse_block_into(parser_t *parser, ast_block *block);
 static bool parse_statement_or_block(parser_t *parser, ast_expression **out);
@@ -757,10 +760,11 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
             break;
         case opid1('*'):
             if (exprs[0]->expression.vtype != exprs[1]->expression.vtype &&
-                exprs[0]->expression.vtype != TYPE_VECTOR &&
-                exprs[0]->expression.vtype != TYPE_FLOAT &&
-                exprs[1]->expression.vtype != TYPE_VECTOR &&
-                exprs[1]->expression.vtype != TYPE_FLOAT)
+                !(exprs[0]->expression.vtype == TYPE_VECTOR &&
+                  exprs[1]->expression.vtype == TYPE_FLOAT) &&
+                !(exprs[1]->expression.vtype == TYPE_VECTOR &&
+                  exprs[0]->expression.vtype == TYPE_FLOAT)
+                )
             {
                 parseerror(parser, "invalid types used in expression: cannot multiply types %s and %s",
                            type_name[exprs[1]->expression.vtype],
@@ -1011,7 +1015,9 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
                     ast_type_to_string(exprs[1], ty2, sizeof(ty2));
                     parseerror(parser, "invalid types in assignment: cannot assign %s to %s", ty2, ty1);
                 }
-                else if (!ast_compare_type(exprs[0], exprs[1])) {
+                else if (exprs[1]->expression.vtype != TYPE_NIL &&
+                         !ast_compare_type(exprs[0], exprs[1]))
+                {
                     ast_type_to_string(exprs[0], ty1, sizeof(ty1));
                     ast_type_to_string(exprs[1], ty2, sizeof(ty2));
                     if (OPTS_FLAG(ASSIGN_FUNCTION_TYPES) &&
@@ -1663,7 +1669,7 @@ static ast_expression* parse_expression_leave(parser_t *parser, bool stopatcomma
             const oper_info *olast = NULL;
             size_t o;
             for (o = 0; o < operator_count; ++o) {
-                if ((!(operators[o].flags & OP_PREFIX) == wantop) &&
+                if (((!(operators[o].flags & OP_PREFIX) == !!wantop)) &&
                     /* !(operators[o].flags & OP_SUFFIX) && / * remove this */
                     !strcmp(parser_tokval(parser), operators[o].op))
                 {
@@ -2018,7 +2024,61 @@ static bool parse_if(parser_t *parser, ast_block *block, ast_expression **out)
     return true;
 }
 
+static bool parse_while_go(parser_t *parser, ast_block *block, ast_expression **out);
 static bool parse_while(parser_t *parser, ast_block *block, ast_expression **out)
+{
+    bool rv;
+    char *label = NULL;
+
+    /* skip the 'while' and get the body */
+    if (!parser_next(parser)) {
+        if (OPTS_FLAG(LOOP_LABELS))
+            parseerror(parser, "expected loop label or 'while' condition in parenthesis");
+        else
+            parseerror(parser, "expected 'while' condition in parenthesis");
+        return false;
+    }
+
+    if (parser->tok == ':') {
+        if (!OPTS_FLAG(LOOP_LABELS))
+            parseerror(parser, "labeled loops not activated, try using -floop-labels");
+        if (!parser_next(parser) || parser->tok != TOKEN_IDENT) {
+            parseerror(parser, "expected loop label");
+            return false;
+        }
+        label = util_strdup(parser_tokval(parser));
+        if (!parser_next(parser)) {
+            mem_d(label);
+            parseerror(parser, "expected 'while' condition in parenthesis");
+            return false;
+        }
+    }
+
+    if (parser->tok != '(') {
+        parseerror(parser, "expected 'while' condition in parenthesis");
+        return false;
+    }
+
+    vec_push(parser->breaks, label);
+    vec_push(parser->continues, label);
+
+    rv = parse_while_go(parser, block, out);
+    if (label)
+        mem_d(label);
+    if (vec_last(parser->breaks) != label || vec_last(parser->continues) != label) {
+        parseerror(parser, "internal error: label stack corrupted");
+        rv = false;
+        ast_delete(*out);
+        *out = NULL;
+    }
+    else {
+        vec_pop(parser->breaks);
+        vec_pop(parser->continues);
+    }
+    return rv;
+}
+
+static bool parse_while_go(parser_t *parser, ast_block *block, ast_expression **out)
 {
     ast_loop *aloop;
     ast_expression *cond, *ontrue;
@@ -2029,11 +2089,6 @@ static bool parse_while(parser_t *parser, ast_block *block, ast_expression **out
 
     (void)block; /* not touching */
 
-    /* skip the 'while' and check for opening paren */
-    if (!parser_next(parser) || parser->tok != '(') {
-        parseerror(parser, "expected 'while' condition in parenthesis");
-        return false;
-    }
     /* parse into the expression */
     if (!parser_next(parser)) {
         parseerror(parser, "expected 'while' condition after opening paren");
@@ -2070,7 +2125,56 @@ static bool parse_while(parser_t *parser, ast_block *block, ast_expression **out
     return true;
 }
 
+static bool parse_dowhile_go(parser_t *parser, ast_block *block, ast_expression **out);
 static bool parse_dowhile(parser_t *parser, ast_block *block, ast_expression **out)
+{
+    bool rv;
+    char *label = NULL;
+
+    /* skip the 'do' and get the body */
+    if (!parser_next(parser)) {
+        if (OPTS_FLAG(LOOP_LABELS))
+            parseerror(parser, "expected loop label or body");
+        else
+            parseerror(parser, "expected loop body");
+        return false;
+    }
+
+    if (parser->tok == ':') {
+        if (!OPTS_FLAG(LOOP_LABELS))
+            parseerror(parser, "labeled loops not activated, try using -floop-labels");
+        if (!parser_next(parser) || parser->tok != TOKEN_IDENT) {
+            parseerror(parser, "expected loop label");
+            return false;
+        }
+        label = util_strdup(parser_tokval(parser));
+        if (!parser_next(parser)) {
+            mem_d(label);
+            parseerror(parser, "expected loop body");
+            return false;
+        }
+    }
+
+    vec_push(parser->breaks, label);
+    vec_push(parser->continues, label);
+
+    rv = parse_dowhile_go(parser, block, out);
+    if (label)
+        mem_d(label);
+    if (vec_last(parser->breaks) != label || vec_last(parser->continues) != label) {
+        parseerror(parser, "internal error: label stack corrupted");
+        rv = false;
+        ast_delete(*out);
+        *out = NULL;
+    }
+    else {
+        vec_pop(parser->breaks);
+        vec_pop(parser->continues);
+    }
+    return rv;
+}
+
+static bool parse_dowhile_go(parser_t *parser, ast_block *block, ast_expression **out)
 {
     ast_loop *aloop;
     ast_expression *cond, *ontrue;
@@ -2081,11 +2185,6 @@ static bool parse_dowhile(parser_t *parser, ast_block *block, ast_expression **o
 
     (void)block; /* not touching */
 
-    /* skip the 'do' and get the body */
-    if (!parser_next(parser)) {
-        parseerror(parser, "expected loop body");
-        return false;
-    }
     if (!parse_statement_or_block(parser, &ontrue))
         return false;
 
@@ -2146,7 +2245,60 @@ static bool parse_dowhile(parser_t *parser, ast_block *block, ast_expression **o
     return true;
 }
 
+static bool parse_for_go(parser_t *parser, ast_block *block, ast_expression **out);
 static bool parse_for(parser_t *parser, ast_block *block, ast_expression **out)
+{
+    bool rv;
+    char *label = NULL;
+
+    /* skip the 'for' and check for opening paren */
+    if (!parser_next(parser)) {
+        if (OPTS_FLAG(LOOP_LABELS))
+            parseerror(parser, "expected loop label or 'for' expressions in parenthesis");
+        else
+            parseerror(parser, "expected 'for' expressions in parenthesis");
+        return false;
+    }
+
+    if (parser->tok == ':') {
+        if (!OPTS_FLAG(LOOP_LABELS))
+            parseerror(parser, "labeled loops not activated, try using -floop-labels");
+        if (!parser_next(parser) || parser->tok != TOKEN_IDENT) {
+            parseerror(parser, "expected loop label");
+            return false;
+        }
+        label = util_strdup(parser_tokval(parser));
+        if (!parser_next(parser)) {
+            mem_d(label);
+            parseerror(parser, "expected 'for' expressions in parenthesis");
+            return false;
+        }
+    }
+
+    if (parser->tok != '(') {
+        parseerror(parser, "expected 'for' expressions in parenthesis");
+        return false;
+    }
+
+    vec_push(parser->breaks, label);
+    vec_push(parser->continues, label);
+
+    rv = parse_for_go(parser, block, out);
+    if (label)
+        mem_d(label);
+    if (vec_last(parser->breaks) != label || vec_last(parser->continues) != label) {
+        parseerror(parser, "internal error: label stack corrupted");
+        rv = false;
+        ast_delete(*out);
+        *out = NULL;
+    }
+    else {
+        vec_pop(parser->breaks);
+        vec_pop(parser->continues);
+    }
+    return rv;
+}
+static bool parse_for_go(parser_t *parser, ast_block *block, ast_expression **out)
 {
     ast_loop       *aloop;
     ast_expression *initexpr, *cond, *increment, *ontrue;
@@ -2164,11 +2316,6 @@ static bool parse_for(parser_t *parser, ast_block *block, ast_expression **out)
     increment = NULL;
     ontrue    = NULL;
 
-    /* skip the 'while' and check for opening paren */
-    if (!parser_next(parser) || parser->tok != '(') {
-        parseerror(parser, "expected 'for' expressions in parenthesis");
-        goto onerr;
-    }
     /* parse into the expression */
     if (!parser_next(parser)) {
         parseerror(parser, "expected 'for' initializer after opening paren");
@@ -2185,7 +2332,7 @@ static bool parse_for(parser_t *parser, ast_block *block, ast_expression **out)
                              "current standard does not allow variable declarations in for-loop initializers"))
                 goto onerr;
         }
-        if (!parse_variable(parser, block, true, CV_VAR, typevar, false, false, false))
+        if (!parse_variable(parser, block, true, CV_VAR, typevar, false, false, 0))
             goto onerr;
     }
     else if (parser->tok != ';')
@@ -2311,11 +2458,39 @@ static bool parse_return(parser_t *parser, ast_block *block, ast_expression **ou
 
 static bool parse_break_continue(parser_t *parser, ast_block *block, ast_expression **out, bool is_continue)
 {
-    lex_ctx ctx = parser_ctx(parser);
+    size_t       i;
+    unsigned int levels = 0;
+    lex_ctx      ctx = parser_ctx(parser);
+    const char **loops = (is_continue ? parser->continues : parser->breaks);
 
     (void)block; /* not touching */
+    if (!parser_next(parser)) {
+        parseerror(parser, "expected semicolon or loop label");
+        return false;
+    }
 
-    if (!parser_next(parser) || parser->tok != ';') {
+    if (parser->tok == TOKEN_IDENT) {
+        if (!OPTS_FLAG(LOOP_LABELS))
+            parseerror(parser, "labeled loops not activated, try using -floop-labels");
+        i = vec_size(loops);
+        while (i--) {
+            if (loops[i] && !strcmp(loops[i], parser_tokval(parser)))
+                break;
+            if (!i) {
+                parseerror(parser, "no such loop to %s: `%s`",
+                           (is_continue ? "continue" : "break out of"),
+                           parser_tokval(parser));
+                return false;
+            }
+            ++levels;
+        }
+        if (!parser_next(parser)) {
+            parseerror(parser, "expected semicolon");
+            return false;
+        }
+    }
+
+    if (parser->tok != ';') {
         parseerror(parser, "expected semicolon");
         return false;
     }
@@ -2323,21 +2498,21 @@ static bool parse_break_continue(parser_t *parser, ast_block *block, ast_express
     if (!parser_next(parser))
         parseerror(parser, "parse error");
 
-    *out = (ast_expression*)ast_breakcont_new(ctx, is_continue);
+    *out = (ast_expression*)ast_breakcont_new(ctx, is_continue, levels);
     return true;
 }
 
 /* returns true when it was a variable qualifier, false otherwise!
  * on error, cvq is set to CV_WRONG
  */
-static bool parse_var_qualifiers(parser_t *parser, bool with_local, int *cvq, bool *noref, bool *noreturn, bool *is_static)
+static bool parse_qualifiers(parser_t *parser, bool with_local, int *cvq, bool *noref, bool *is_static, uint32_t *_flags)
 {
     bool had_const    = false;
     bool had_var      = false;
     bool had_noref    = false;
-    bool had_noreturn = false;
     bool had_attrib   = false;
     bool had_static   = false;
+    uint32_t flags    = 0;
 
     *cvq = CV_NONE;
     for (;;) {
@@ -2350,7 +2525,7 @@ static bool parse_var_qualifiers(parser_t *parser, bool with_local, int *cvq, bo
                 return false;
             }
             if (!strcmp(parser_tokval(parser), "noreturn")) {
-                had_noreturn = true;
+                flags |= AST_FLAG_NORETURN;
                 if (!parser_next(parser) || parser->tok != TOKEN_ATTRIBUTE_CLOSE) {
                     parseerror(parser, "`noreturn` attribute has no parameters, expected `]]`");
                     *cvq = CV_WRONG;
@@ -2365,6 +2540,14 @@ static bool parse_var_qualifiers(parser_t *parser, bool with_local, int *cvq, bo
                     return false;
                 }
             }
+            else if (!strcmp(parser_tokval(parser), "inline")) {
+                flags |= AST_FLAG_INLINE;
+                if (!parser_next(parser) || parser->tok != TOKEN_ATTRIBUTE_CLOSE) {
+                    parseerror(parser, "`noref` attribute has no parameters, expected `]]`");
+                    *cvq = CV_WRONG;
+                    return false;
+                }
+            }
             else
             {
                 /* Skip tokens until we hit a ]] */
@@ -2388,7 +2571,7 @@ static bool parse_var_qualifiers(parser_t *parser, bool with_local, int *cvq, bo
             had_var = true;
         else if (!strcmp(parser_tokval(parser), "noref"))
             had_noref = true;
-        else if (!had_const && !had_var && !had_noref && !had_noreturn && !had_attrib && !had_static) {
+        else if (!had_const && !had_var && !had_noref && !had_attrib && !had_static && !flags) {
             return false;
         }
         else
@@ -2403,8 +2586,8 @@ static bool parse_var_qualifiers(parser_t *parser, bool with_local, int *cvq, bo
     else
         *cvq = CV_NONE;
     *noref     = had_noref;
-    *noreturn  = had_noreturn;
     *is_static = had_static;
+    *_flags    = flags;
     return true;
 onerr:
     parseerror(parser, "parse error after variable qualifier");
@@ -2412,7 +2595,59 @@ onerr:
     return true;
 }
 
+static bool parse_switch_go(parser_t *parser, ast_block *block, ast_expression **out);
 static bool parse_switch(parser_t *parser, ast_block *block, ast_expression **out)
+{
+    bool rv;
+    char *label = NULL;
+
+    /* skip the 'while' and get the body */
+    if (!parser_next(parser)) {
+        if (OPTS_FLAG(LOOP_LABELS))
+            parseerror(parser, "expected loop label or 'switch' operand in parenthesis");
+        else
+            parseerror(parser, "expected 'switch' operand in parenthesis");
+        return false;
+    }
+
+    if (parser->tok == ':') {
+        if (!OPTS_FLAG(LOOP_LABELS))
+            parseerror(parser, "labeled loops not activated, try using -floop-labels");
+        if (!parser_next(parser) || parser->tok != TOKEN_IDENT) {
+            parseerror(parser, "expected loop label");
+            return false;
+        }
+        label = util_strdup(parser_tokval(parser));
+        if (!parser_next(parser)) {
+            mem_d(label);
+            parseerror(parser, "expected 'switch' operand in parenthesis");
+            return false;
+        }
+    }
+
+    if (parser->tok != '(') {
+        parseerror(parser, "expected 'switch' operand in parenthesis");
+        return false;
+    }
+
+    vec_push(parser->breaks, label);
+
+    rv = parse_switch_go(parser, block, out);
+    if (label)
+        mem_d(label);
+    if (vec_last(parser->breaks) != label) {
+        parseerror(parser, "internal error: label stack corrupted");
+        rv = false;
+        ast_delete(*out);
+        *out = NULL;
+    }
+    else {
+        vec_pop(parser->breaks);
+    }
+    return rv;
+}
+
+static bool parse_switch_go(parser_t *parser, ast_block *block, ast_expression **out)
 {
     ast_expression *operand;
     ast_value      *opval;
@@ -2421,19 +2656,14 @@ static bool parse_switch(parser_t *parser, ast_block *block, ast_expression **ou
     ast_switch_case swcase;
 
     int  cvq;
-    bool noref, noreturn, is_static;
+    bool noref, is_static;
+    uint32_t qflags = 0;
 
     lex_ctx ctx = parser_ctx(parser);
 
     (void)block; /* not touching */
     (void)opval;
 
-    /* parse over the opening paren */
-    if (!parser_next(parser) || parser->tok != '(') {
-        parseerror(parser, "expected switch operand in parenthesis");
-        return false;
-    }
-
     /* parse into the expression */
     if (!parser_next(parser)) {
         parseerror(parser, "expected switch operand");
@@ -2473,19 +2703,19 @@ static bool parse_switch(parser_t *parser, ast_block *block, ast_expression **ou
         if (parser->tok == TOKEN_IDENT)
             typevar = parser_find_typedef(parser, parser_tokval(parser), 0);
         if (typevar || parser->tok == TOKEN_TYPENAME) {
-            if (!parse_variable(parser, block, false, CV_NONE, typevar, false, false, false)) {
+            if (!parse_variable(parser, block, false, CV_NONE, typevar, false, false, 0)) {
                 ast_delete(switchnode);
                 return false;
             }
             continue;
         }
-        if (parse_var_qualifiers(parser, true, &cvq, &noref, &noreturn, &is_static))
+        if (parse_qualifiers(parser, true, &cvq, &noref, &is_static, &qflags))
         {
             if (cvq == CV_WRONG) {
                 ast_delete(switchnode);
                 return false;
             }
-            if (!parse_variable(parser, block, false, cvq, NULL, noref, noreturn, is_static)) {
+            if (!parse_variable(parser, block, false, cvq, NULL, noref, is_static, qflags)) {
                 ast_delete(switchnode);
                 return false;
             }
@@ -2700,8 +2930,9 @@ static bool parse_pragma(parser_t *parser)
 
 static bool parse_statement(parser_t *parser, ast_block *block, ast_expression **out, bool allow_cases)
 {
-    bool       noref, noreturn, is_static;
+    bool       noref, is_static;
     int        cvq = CV_NONE;
+    uint32_t   qflags = 0;
     ast_value *typevar = NULL;
 
     *out = NULL;
@@ -2720,15 +2951,15 @@ static bool parse_statement(parser_t *parser, ast_block *block, ast_expression *
             if (parsewarning(parser, WARN_EXTENSIONS, "missing 'local' keyword when declaring a local variable"))
                 return false;
         }
-        if (!parse_variable(parser, block, false, CV_NONE, typevar, false, false, false))
+        if (!parse_variable(parser, block, false, CV_NONE, typevar, false, false, 0))
             return false;
         return true;
     }
-    else if (parse_var_qualifiers(parser, !!block, &cvq, &noref, &noreturn, &is_static))
+    else if (parse_qualifiers(parser, !!block, &cvq, &noref, &is_static, &qflags))
     {
         if (cvq == CV_WRONG)
             return false;
-        return parse_variable(parser, block, true, cvq, NULL, noref, noreturn, is_static);
+        return parse_variable(parser, block, true, cvq, NULL, noref, is_static, qflags);
     }
     else if (parser->tok == TOKEN_KEYWORD)
     {
@@ -3984,7 +4215,7 @@ static bool parse_typedef(parser_t *parser)
     return true;
 }
 
-static bool parse_variable(parser_t *parser, ast_block *localblock, bool nofields, int qualifier, ast_value *cached_typedef, bool noref, bool noreturn, bool is_static)
+static bool parse_variable(parser_t *parser, ast_block *localblock, bool nofields, int qualifier, ast_value *cached_typedef, bool noref, bool is_static, uint32_t qflags)
 {
     ast_value *var;
     ast_value *proto;
@@ -4054,14 +4285,20 @@ static bool parse_variable(parser_t *parser, ast_block *localblock, bool nofield
         /* in a noref section we simply bump the usecount */
         if (noref || parser->noref)
             var->uses++;
-        if (noreturn)
-            var->expression.flags |= AST_FLAG_NORETURN;
+        var->expression.flags |= qflags;
 
         /* Part 1:
          * check for validity: (end_sys_..., multiple-definitions, prototypes, ...)
          * Also: if there was a prototype, `var` will be deleted and set to `proto` which
          * is then filled with the previous definition and the parameter-names replaced.
          */
+        if (!strcmp(var->name, "nil")) {
+            if (OPTS_FLAG(UNTYPED_NIL)) {
+                if (!localblock || !OPTS_FLAG(PERMISSIVE))
+                    parseerror(parser, "name `nil` not allowed (try -fpermissive)");
+            } else
+                (void)!parsewarning(parser, WARN_RESERVED_NAMES, "variable name `nil` is reserved");
+        }
         if (!localblock) {
             /* Deal with end_sys_ vars */
             was_end = false;
@@ -4561,8 +4798,8 @@ static bool parser_global_statement(parser_t *parser)
 {
     int        cvq       = CV_WRONG;
     bool       noref     = false;
-    bool       noreturn  = false;
     bool       is_static = false;
+    uint32_t   qflags    = 0;
     ast_value *istype    = NULL;
 
     if (parser->tok == TOKEN_IDENT)
@@ -4570,13 +4807,13 @@ static bool parser_global_statement(parser_t *parser)
 
     if (istype || parser->tok == TOKEN_TYPENAME || parser->tok == '.')
     {
-        return parse_variable(parser, NULL, false, CV_NONE, istype, false, false, false);
+        return parse_variable(parser, NULL, false, CV_NONE, istype, false, false, 0);
     }
-    else if (parse_var_qualifiers(parser, false, &cvq, &noref, &noreturn, &is_static))
+    else if (parse_qualifiers(parser, false, &cvq, &noref, &is_static, &qflags))
     {
         if (cvq == CV_WRONG)
             return false;
-        return parse_variable(parser, NULL, true, cvq, NULL, noref, noreturn, is_static);
+        return parse_variable(parser, NULL, true, cvq, NULL, noref, is_static, qflags);
     }
     else if (parser->tok == TOKEN_KEYWORD)
     {
@@ -4689,6 +4926,7 @@ static parser_t *parser;
 
 bool parser_init()
 {
+    lex_ctx empty_ctx;
     size_t i;
 
     parser = (parser_t*)mem_a(sizeof(parser_t));
@@ -4713,6 +4951,12 @@ bool parser_init()
     vec_push(parser->variables, parser->htglobals = util_htnew(PARSER_HT_SIZE));
     vec_push(parser->typedefs, util_htnew(TYPEDEF_HT_SIZE));
     vec_push(parser->_blocktypedefs, 0);
+
+    empty_ctx.file = "<internal>";
+    empty_ctx.line = 0;
+    parser->nil = ast_value_new(empty_ctx, "nil", TYPE_NIL);
+    if (OPTS_FLAG(UNTYPED_NIL))
+        util_htset(parser->htglobals, "nil", (void*)parser->nil);
     return true;
 }
 
@@ -4820,6 +5064,8 @@ void parser_cleanup()
 
     vec_free(parser->labels);
     vec_free(parser->gotos);
+    vec_free(parser->breaks);
+    vec_free(parser->continues);
 
     mem_d(parser);
 }
@@ -4908,6 +5154,14 @@ bool parser_finish(const char *output)
         if (!ast_istype(parser->globals[i], ast_value))
             continue;
         asvalue = (ast_value*)(parser->globals[i]);
+        if (asvalue->cvq == CV_CONST && !asvalue->hasvalue)
+            (void)!compile_warning(ast_ctx(asvalue), WARN_UNINITIALIZED_CONSTANT,
+                                   "uninitialized constant: `%s`",
+                                   asvalue->name);
+        else if ((asvalue->cvq == CV_NONE || asvalue->cvq == CV_CONST) && !asvalue->hasvalue)
+            (void)!compile_warning(ast_ctx(asvalue), WARN_UNINITIALIZED_GLOBAL,
+                                   "uninitialized global: `%s`",
+                                   asvalue->name);
         if (!ast_generate_accessors(asvalue, ir)) {
             ir_builder_delete(ir);
             return false;
diff --git a/splint.sh b/splint.sh
new file mode 100755 (executable)
index 0000000..0aa2131
--- /dev/null
+++ b/splint.sh
@@ -0,0 +1,70 @@
+#!/bin/sh
+
+#these are stupid flags ... i.e to inhibit warnings that are just stupid
+FLAGS_STUPID="\
+    -redef               \
+    -noeffect            \
+    -nullderef           \
+    -usedef              \
+    -type                \
+    -mustfreeonly        \
+    -nullstate           \
+    -varuse              \
+    -mustfreefresh       \
+    -compdestroy         \
+    -compmempass         \
+    -nullpass            \
+    -onlytrans           \
+    -predboolint         \
+    -boolops             \
+    -exportlocal         \
+    -incondefs           \
+    -macroredef          \
+    -retvalint           \
+    -nullret             \
+    -predboolothers      \
+    -globstate           \
+    -dependenttrans      \
+    -branchstate         \
+    -compdef             \
+    -temptrans           \
+    -usereleased         \
+    -warnposix"
+
+#flags that have no place anywhere else
+#mostly stupid
+FLAGS_OTHERS="\
+    -shiftimplementation \
+    +charindex           \
+    -kepttrans           \
+    -unqualifiedtrans    \
+    +matchanyintegral    \
+    -bufferoverflowhigh  \
+    +voidabstract"
+
+#these are flags that MAYBE shouldn't be required
+# -nullassign should be surpressed in code with /*@null*/
+# (although that might be odd?)
+FLAGS_MAYBE="\
+    -nullassign          \
+    -unrecog             \
+    -casebreak           \
+    -retvalbool          \
+    -retvalother         \
+    -mayaliasunique      \
+    -realcompare         \
+    -observertrans       \
+    -shiftnegative       \
+    -freshtrans          \
+    -abstract            \
+    -statictrans"
+
+#these are flags that shouldn't be required. I.e tofix in code so that
+#these don't need to be here to onhibit the warning
+# remove one flag from here at a time while fixing the code so that
+FLAGS_TOFIX="\
+    -castfcnptr          \
+    -evalorder"
+
+
+splint $FLAGS_STUPID $FLAGS_MAYBE $FLAGS_TOFIX $FLAGS_OTHERS *.c *.h
diff --git a/test.c b/test.c
index 1111c370ed87394ea7fcb9cae729486662907aac..0762876735d4a30bd469fb567dbdc37c35b2900b 100644 (file)
--- a/test.c
+++ b/test.c
@@ -121,29 +121,12 @@ FILE ** task_popen(const char *command, const char *mode) {
         close(2), dup(errhandle[1]);
 
         execvp(*argv, argv);
-        exit(1);
+        exit(EXIT_FAILURE);
     } else {
         /* fork failed */
         goto task_popen_error_3;
     }
 
-    /*
-     * clang is stupid, it doesn't understand that yes, this code
-     * is actually reachable.
-     */
-#   ifdef __clang__
-#       pragma clang diagnostic push
-#       pragma clang diagnostic ignored "-Wunreachable-code"
-#   endif
-    if (argv)
-        vec_free(argv);
-
-#   ifdef __clang__
-#    pragma clang diagnostic pop
-#   endif
-
-    return data->handles;
-
 task_popen_error_3: close(errhandle[0]), close(errhandle[1]);
 task_popen_error_2: close(outhandle[0]), close(outhandle[1]);
 task_popen_error_1: close(inhandle [0]), close(inhandle [1]);
index 82f3d3d57bcdafd077725bd8f554cff3e859d6d0..27a537d9bbc72cb87e8ffa3921421debb4a5ce87 100644 (file)
@@ -1,4 +1,4 @@
 I: noref.qc
 D: noref keyword and pragma
 T: -compile
-C: -std=qcc -Wall -Werror
+C: -std=qcc -Wall -Werror -Wno-uninitialized-global
diff --git a/utf8.c b/utf8.c
index 68bd038ea400d8cbf11af7b70f0e8fe9f2f80aca..391fc0951d34ba89e275940ef4ef9000f5a09c28 100644 (file)
--- a/utf8.c
+++ b/utf8.c
 #include "gmqcc.h"
 
 static unsigned char utf8_lengths[256] = {
-       1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* ascii characters */
-       1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
-       1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
-       1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
-       1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
-       1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
-       1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
-       1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
-       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x80 - 0xBF are within multibyte sequences
-       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  * they could be interpreted as 2-byte starts but
-       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  * the codepoint would be < 127
-       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  *
-       0, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,  * C0 and C1 would also result in overlong encodings
-       2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,  */
-       3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
-       4, 4, 4, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
-       /* with F5 the codepoint is above 0x10FFFF,
-        * F8-FB would start 5-byte sequences
-        * FC-FD would start 6-byte sequences
-        * ...
-        */
+    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* ascii characters */
+    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x80 - 0xBF are within multibyte sequences        */
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* they could be interpreted as 2-byte starts but    */
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* the codepoint would be < 127                      */
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /*                                                   */
+    0, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, /* C0 and C1 would also result in overlong encodings */
+    2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, /*                                                   */
+    3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
+    4, 4, 4, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+    /* with F5 the codepoint is above 0x10FFFF,
+     * F8-FB would start 5-byte sequences
+     * FC-FD would start 6-byte sequences
+     * ...
+     */
 };
 
 static uchar_t utf8_range[5] = {
-       1,       /* invalid - let's not allow the creation of 0-bytes :P
-       1,        * ascii minimum
-       0x80,     * 2-byte minimum
-       0x800,    * 3-byte minimum
-       0x10000,  * 4-byte minimum */
+    1,       /* invalid - let's not allow the creation of 0-bytes :P */
+    1,       /* ascii minimum                                        */
+    0x80,    /* 2-byte minimum                                       */
+    0x800,   /* 3-byte minimum                                       */
+    0x10000, /* 4-byte minimum                                       */
 };
 
 /** Analyze the next character and return various information if requested.
@@ -64,220 +64,220 @@ static uchar_t utf8_range[5] = {
  */
 bool u8_analyze(const char *_s, size_t *_start, size_t *_len, uchar_t *_ch, size_t _maxlen)
 {
-       const unsigned char *s = (const unsigned char*)_s;
-       size_t i, j;
-       size_t bits = 0;
-       uchar_t ch;
+    const unsigned char *s = (const unsigned char*)_s;
+    size_t i, j;
+    size_t bits = 0;
+    uchar_t ch;
 
-       i = 0;
+    i = 0;
 /* findchar: */
-       while (i < _maxlen && s[i] && (bits = utf8_lengths[s[i]]) == 0)
-               ++i;
+    while (i < _maxlen && s[i] && (bits = utf8_lengths[s[i]]) == 0)
+        ++i;
 
-       if (i >= _maxlen || !s[i]) {
-               if (_start) *_start = i;
-               if (_len) *_len = 0;
-               return false;
-       }
+    if (i >= _maxlen || !s[i]) {
+        if (_start) *_start = i;
+        if (_len) *_len = 0;
+        return false;
+    }
 
-       if (bits == 1) { /* ascii */
-               if (_start) *_start = i;
-               if (_len) *_len = 1;
-               if (_ch) *_ch = (uchar_t)s[i];
-               return true;
-       }
+    if (bits == 1) { /* ascii */
+        if (_start) *_start = i;
+        if (_len) *_len = 1;
+        if (_ch) *_ch = (uchar_t)s[i];
+        return true;
+    }
 
-       ch = (s[i] & (0xFF >> bits));
-       for (j = 1; j < bits; ++j)
-       {
-               if ( (s[i+j] & 0xC0) != 0x80 )
-               {
-                       i += j;
-                       /* in gmqcc, invalid / overlong encodings are considered an error
-                        * goto findchar;
-                        */
-                       if (!s[i]) goto done;
-                       return false;
-               }
-               ch = (ch << 6) | (s[i+j] & 0x3F);
-       }
-       if (ch < utf8_range[bits] || ch >= 0x10FFFF)
-       {
-               /* same: error
-                * i += bits;
-                * goto findchar;
-                */
-               return false;
-       }
+    ch = (s[i] & (0xFF >> bits));
+    for (j = 1; j < bits; ++j)
+    {
+        if ( (s[i+j] & 0xC0) != 0x80 )
+        {
+            i += j;
+            /* in gmqcc, invalid / overlong encodings are considered an error
+             * goto findchar;
+             */
+            if (!s[i]) goto done;
+            return false;
+        }
+        ch = (ch << 6) | (s[i+j] & 0x3F);
+    }
+    if (ch < utf8_range[bits] || ch >= 0x10FFFF)
+    {
+        /* same: error
+         * i += bits;
+         * goto findchar;
+         */
+        return false;
+    }
 
 done:
-       if (_start)
-               *_start = i;
-       if (_len)
-               *_len = bits;
-       if (_ch)
-               *_ch = ch;
-       return true;
+    if (_start)
+        *_start = i;
+    if (_len)
+        *_len = bits;
+    if (_ch)
+        *_ch = ch;
+    return true;
 }
 
 /* might come in handy */
 size_t u8_strlen(const char *_s)
 {
-       size_t st, ln;
-       size_t len = 0;
-       const unsigned char *s = (const unsigned char*)_s;
+    size_t st, ln;
+    size_t len = 0;
+    const unsigned char *s = (const unsigned char*)_s;
 
-       while (*s)
-       {
-               /* ascii char, skip u8_analyze */
-               if (*s < 0x80)
-               {
-                       ++len;
-                       ++s;
-                       continue;
-               }
+    while (*s)
+    {
+        /* ascii char, skip u8_analyze */
+        if (*s < 0x80)
+        {
+            ++len;
+            ++s;
+            continue;
+        }
 
-               /* invalid, skip u8_analyze */
-               if (*s < 0xC2)
-               {
-                       ++s;
-                       continue;
-               }
+        /* invalid, skip u8_analyze */
+        if (*s < 0xC2)
+        {
+            ++s;
+            continue;
+        }
 
-               if (!u8_analyze((const char*)s, &st, &ln, NULL, 0x10))
-                       break;
-               /* valid character, skip after it */
-               s += st + ln;
-               ++len;
-       }
-       return len;
+        if (!u8_analyze((const char*)s, &st, &ln, NULL, 0x10))
+            break;
+        /* valid character, skip after it */
+        s += st + ln;
+        ++len;
+    }
+    return len;
 }
 
 size_t u8_strnlen(const char *_s, size_t n)
 {
-       size_t st, ln;
-       size_t len = 0;
-       const unsigned char *s = (const unsigned char*)_s;
+    size_t st, ln;
+    size_t len = 0;
+    const unsigned char *s = (const unsigned char*)_s;
 
-       while (*s && n)
-       {
-               /* ascii char, skip u8_analyze */
-               if (*s < 0x80)
-               {
-                       ++len;
-                       ++s;
-                       --n;
-                       continue;
-               }
+    while (*s && n)
+    {
+        /* ascii char, skip u8_analyze */
+        if (*s < 0x80)
+        {
+            ++len;
+            ++s;
+            --n;
+            continue;
+        }
 
-               /* invalid, skip u8_analyze */
-               if (*s < 0xC2)
-               {
-                       ++s;
-                       --n;
-                       continue;
-               }
+        /* invalid, skip u8_analyze */
+        if (*s < 0xC2)
+        {
+            ++s;
+            --n;
+            continue;
+        }
 
-               if (!u8_analyze((const char*)s, &st, &ln, NULL, n))
-                       break;
-               /* valid character, see if it's still inside the range specified by n: */
-               if (n < st + ln)
-                       return len;
-               ++len;
-               n -= st + ln;
-               s += st + ln;
-       }
-       return len;
+        if (!u8_analyze((const char*)s, &st, &ln, NULL, n))
+            break;
+        /* valid character, see if it's still inside the range specified by n: */
+        if (n < st + ln)
+            return len;
+        ++len;
+        n -= st + ln;
+        s += st + ln;
+    }
+    return len;
 }
 
 /* Required for character constants */
 uchar_t u8_getchar(const char *_s, const char **_end)
 {
-       size_t st, ln;
-       uchar_t ch;
+    size_t st, ln;
+    uchar_t ch;
 
-       if (!u8_analyze(_s, &st, &ln, &ch, 0x10))
-               ch = 0;
-       if (_end)
-               *_end = _s + st + ln;
-       return ch;
+    if (!u8_analyze(_s, &st, &ln, &ch, 0x10))
+        ch = 0;
+    if (_end)
+        *_end = _s + st + ln;
+    return ch;
 }
 
 uchar_t u8_getnchar(const char *_s, const char **_end, size_t _maxlen)
 {
-       size_t st, ln;
-       uchar_t ch;
+    size_t st, ln;
+    uchar_t ch;
 
-       if (!u8_analyze(_s, &st, &ln, &ch, _maxlen))
-               ch = 0;
-       if (_end)
-               *_end = _s + st + ln;
-       return ch;
+    if (!u8_analyze(_s, &st, &ln, &ch, _maxlen))
+        ch = 0;
+    if (_end)
+        *_end = _s + st + ln;
+    return ch;
 }
 
 /* required for \x{asdf}-like string escape sequences */
 int u8_fromchar(uchar_t w, char *to, size_t maxlen)
 {
-       if (maxlen < 1)
-               return 0;
+    if (maxlen < 1)
+        return 0;
 
-       if (!w)
-               return 0;
+    if (!w)
+        return 0;
 
 /* We may want an -f flag for this behaviour...
-       if (w >= 0xE000)
-               w -= 0xE000;
+    if (w >= 0xE000)
+        w -= 0xE000;
 */
 
-       if (w < 0x80)
-       {
-               to[0] = (char)w;
-               if (maxlen < 2)
-                       return -1;
-               to[1] = 0;
-               return 1;
-       }
-       /* for a little speedup */
-       if (w < 0x800)
-       {
-               if (maxlen < 3)
-               {
-                       to[0] = 0;
-                       return -1;
-               }
-               to[2] = 0;
-               to[1] = 0x80 | (w & 0x3F); w >>= 6;
-               to[0] = 0xC0 | w;
-               return 2;
-       }
-       if (w < 0x10000)
-       {
-               if (maxlen < 4)
-               {
-                       to[0] = 0;
-                       return -1;
-               }
-               to[3] = 0;
-               to[2] = 0x80 | (w & 0x3F); w >>= 6;
-               to[1] = 0x80 | (w & 0x3F); w >>= 6;
-               to[0] = 0xE0 | w;
-               return 3;
-       }
+    if (w < 0x80)
+    {
+        to[0] = (char)w;
+        if (maxlen < 2)
+            return -1;
+        to[1] = 0;
+        return 1;
+    }
+    /* for a little speedup */
+    if (w < 0x800)
+    {
+        if (maxlen < 3)
+        {
+            to[0] = 0;
+            return -1;
+        }
+        to[2] = 0;
+        to[1] = 0x80 | (w & 0x3F); w >>= 6;
+        to[0] = 0xC0 | w;
+        return 2;
+    }
+    if (w < 0x10000)
+    {
+        if (maxlen < 4)
+        {
+            to[0] = 0;
+            return -1;
+        }
+        to[3] = 0;
+        to[2] = 0x80 | (w & 0x3F); w >>= 6;
+        to[1] = 0x80 | (w & 0x3F); w >>= 6;
+        to[0] = 0xE0 | w;
+        return 3;
+    }
 
-       /* RFC 3629 */
-       if (w <= 0x10FFFF)
-       {
-               if (maxlen < 5)
-               {
-                       to[0] = 0;
-                       return -1;
-               }
-               to[4] = 0;
-               to[3] = 0x80 | (w & 0x3F); w >>= 6;
-               to[2] = 0x80 | (w & 0x3F); w >>= 6;
-               to[1] = 0x80 | (w & 0x3F); w >>= 6;
-               to[0] = 0xF0 | w;
-               return 4;
-       }
-       return 0;
+    /* RFC 3629 */
+    if (w <= 0x10FFFF)
+    {
+        if (maxlen < 5)
+        {
+            to[0] = 0;
+            return -1;
+        }
+        to[4] = 0;
+        to[3] = 0x80 | (w & 0x3F); w >>= 6;
+        to[2] = 0x80 | (w & 0x3F); w >>= 6;
+        to[1] = 0x80 | (w & 0x3F); w >>= 6;
+        to[0] = 0xF0 | w;
+        return 4;
+    }
+    return 0;
 }
diff --git a/util.c b/util.c
index b2b092d13828a7be9609fc08b68b380c46071f86..8671faa651a18fd9cb488893583afc380c1286fe 100644 (file)
--- a/util.c
+++ b/util.c
@@ -573,6 +573,38 @@ void util_htdel(hash_table_t *ht) {
     mem_d(ht);
 }
 
+/*
+ * Portable implementation of vasprintf/asprintf. Assumes vsnprintf
+ * exists, otherwise compiler error.
+ */
+int util_vasprintf(char **ret, const char *fmt, va_list args) {
+    int     read;
+    va_list copy;
+    va_copy(copy, args);
+
+    *ret = 0;
+    if ((read = vsnprintf(NULL, 0, fmt, args)) >= 0) {
+        char *buffer;
+        if  ((buffer = (char*)mem_a(read + 1))) {
+            if ((read = vsnprintf(buffer, read + 1, fmt, copy)) < 0)
+                mem_d(buffer);
+            else
+                *ret = buffer;
+        }
+    }
+    va_end(copy);
+    return read;
+}
+int util_asprintf(char **ret, const char *fmt, ...) {
+    va_list  args;
+    int      read;
+    va_start(args, fmt);
+    read = util_vasprintf(ret, fmt, args);
+    va_end  (args);
+
+    return read;
+}
+
 /*
  * Implementation of the Mersenne twister PRNG (pseudo random numer
  * generator).  Implementation of MT19937.  Has a period of 2^19937-1