]> git.xonotic.org Git - xonotic/gmqcc.git/commitdiff
Merge branch 'cooking'
authorDale Weiler <killfieldengine@gmail.com>
Sun, 21 Apr 2013 10:09:08 +0000 (10:09 +0000)
committerDale Weiler <killfieldengine@gmail.com>
Sun, 21 Apr 2013 10:09:08 +0000 (10:09 +0000)
49 files changed:
.gitignore
CHANGES
Makefile
README
ast.c
conout.c
distro/Makefile [new file with mode: 0644]
distro/archbsd/git/PKGBUILD
distro/archbsd/release/PKGBUILD
distro/archlinux/git/PKGBUILD
distro/archlinux/release/PKGBUILD
distro/archlinux/this/Makefile
distro/deb/Makefile
distro/deb/control [deleted file]
exec.c
fs.c
ftepp.c
gmqcc.h
intrin.h [new file with mode: 0644]
ir.c
lexer.c
lexer.h
main.c
opts.c
opts.def
pak.c
parser.c
syntax/README [new file with mode: 0644]
syntax/geany/README [new file with mode: 0644]
syntax/geany/filetypes.qc [new file with mode: 0644]
syntax/gtksourceview/README [new file with mode: 0644]
syntax/gtksourceview/qc.lang [new file with mode: 0644]
syntax/jedit/README [new file with mode: 0644]
syntax/jedit/qc.xml [new file with mode: 0644]
syntax/kate/README [new file with mode: 0644]
syntax/kate/qc.xml [new file with mode: 0644]
syntax/nano/README [new file with mode: 0644]
syntax/nano/qc.nanorc [new file with mode: 0644]
test.c
tests/defs.qh
tests/enum.qc
tests/enum.tmpl
tests/exponentiation.qc [new file with mode: 0644]
tests/exponentiation.tmpl [new file with mode: 0644]
tests/ppcat.qc [new file with mode: 0644]
tests/ppcat.tmpl [new file with mode: 0644]
tests/uninit2.tmpl
utf8.c
util.c

index 596972e8cb5100df4a55eda4523fa440b1a2681d..342039cb75acefa4cecf1becd535a07c01f49b30 100644 (file)
@@ -7,10 +7,12 @@
 testsuite
 qcvm
 gmqcc
+pak
 
-distro/arch/*
+distro/archlinux/*
+distro/archbsd/*
 !distro/archlinux/git/PKGBUILD
 !distro/archlinux/release/PKGBUILD
 !distro/archbsd/release/PKGBUILD
 !distro/archbsd/git/PKGBUILD
-!distro/arch/this/Makefile
+!distro/archlinux/this/Makefile
diff --git a/CHANGES b/CHANGES
index 7a0013b82223057a3e28e091d0f8a25c4ab8ca1f..0b88fd9daf871fd556bc0937bfd08e8effb38032 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -1,10 +1,12 @@
-Release v0.2.4
+Release v0.2.9
     * Preprocessor:
         - __VA_ARGS__ support
+        _ __VA_ARGS__ indexing
         - Predefined macros like __DATE__, __TIME__, ...
           (check the manpage for a full list)
         - Signed numbers as single token in the
         - Fixes some issues with #if operations on macros.
+        - Speed improvements
     * Language:
         - Untyped `nil` keyword.
         - Removed the `noreturn` keyword.
@@ -21,6 +23,8 @@ Release v0.2.4
         - Type restricted variadict parameters:
           ie: void print(string...);
         - Accessing varargs from QC via: ...(index, type)
+        - New operators: ** (exponentiation), % (modulo), etc
+        - Enumeration attributes: flag, reverse
     * Compilation:
         - Various optimizations and progs-size reductions.
         - A new spell-checking algorithm tries to hint you at existing
@@ -29,16 +33,35 @@ Release v0.2.4
           have been solved in both DP and our own executor. A new
           compatbility option (enabled by default) has been added for
           now: -flegacy-vector-maths
-    * qcvm:
+        - Compiler intrinsics: __builtin_floor, __builtin_mod,
+        __builtin_exp, __builtin_isnan
+        - Improved memory tracing
+        - Speed improvements
+    * QCVM:
         - Improved commandline argument handling.
         - More builtins: sqrt(), normalize()
     * Commandline:
+        - Nicer memory dumps
         - Support for making individual warnings an error
         - via -Werror-<warning>
         - added --add-info
     * Testsuite:
         - Support for QCFLAGS to run tests with several additional
           flags.
+        - Added support for preprocessor tests
+        - Added preprocessor tests
+        - Added defs.qh (auto included) for qcvm definitions
+    * Syntax Highlighting:
+        - Added various syntax highlighting description files for
+        various text editors / integrated development envirorments,
+        including support for: geany, kate, kwrite, kdevelop, QtCreator,
+        gtksourceview, gedit, sany, nano, jedit
+    * Build:
+        - Build scripts for building debian, archlinux and archbsd
+        packages for x86, and x86_64.
+        - Makefile targets for gource visualization, and render of
+          gource visualization.
+
 
 2012-12-27 Hotfix v0.2.2
     * Liferanges
index 410a97d19c4c6d98c5c35662aa506581b0f06856..20152c5bf3f6cfa361066c45a33cf097d3917a73 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -1,4 +1,5 @@
 DESTDIR :=
+OPTIONAL:=
 PREFIX  := /usr/local
 BINDIR  := $(PREFIX)/bin
 DATADIR := $(PREFIX)/share
@@ -9,7 +10,7 @@ CYGWIN  = $(findstring CYGWIN,  $(UNAME))
 MINGW   = $(findstring MINGW32, $(UNAME))
 
 CC     ?= clang
-CFLAGS += -Wall -Wextra -Werror -I. -fno-strict-aliasing -fsigned-char
+CFLAGS += -Wall -Wextra -Werror -I. -fno-strict-aliasing -fsigned-char $(OPTIONAL)
 ifneq ($(shell git describe --always 2>/dev/null),)
     CFLAGS += -DGMQCC_GITINFO="\"$(shell git describe --always)\""
 endif
@@ -17,28 +18,28 @@ endif
 # but also turn off the STUPID ONES
 ifeq ($(CC), clang)
        CFLAGS +=                              \
-               -Weverything                       \
-               -Wno-padded                        \
-               -Wno-format-nonliteral             \
-               -Wno-disabled-macro-expansion      \
-               -Wno-conversion                    \
-               -Wno-missing-prototypes            \
-               -Wno-float-equal                   \
-               -Wno-cast-align                    \
-               -Wno-missing-variable-declarations \
-               -Wno-unknown-warning-option
+           -Weverything                       \
+           -Wno-padded                        \
+           -Wno-format-nonliteral             \
+           -Wno-disabled-macro-expansion      \
+           -Wno-conversion                    \
+           -Wno-missing-prototypes            \
+           -Wno-float-equal                   \
+           -Wno-cast-align                    \
+           -Wno-missing-variable-declarations \
+           -Wno-unknown-warning-option
 else
        #Tiny C Compiler doesn't know what -pedantic-errors is
        # and instead of ignoring .. just errors.
        ifneq ($(CC), tcc)
-               CFLAGS +=-pedantic-errors
+               CFLAGS +=-pedantic-errors -ffunction-sections -fdata-sections -Wl,-gc-sections
        else
                CFLAGS += -Wno-pointer-sign -fno-common
        endif
 endif
 
 ifeq ($(track), no)
-    CFLAGS += -DNOTRACK
+       CFLAGS += -DNOTRACK
 endif
 
 OBJ_D = util.o code.o ast.o ir.o conout.o ftepp.o opts.o fs.o utf8.o correct.o
@@ -84,6 +85,36 @@ else
 endif
 endif
 
+#gource flags
+GOURCEFLAGS=                  \
+    --date-format "%d %B, %Y" \
+    --seconds-per-day 0.01    \
+    --auto-skip-seconds 1     \
+    --title "GMQCC"           \
+    --key                     \
+    --camera-mode overview    \
+    --highlight-all-users     \
+    --file-idle-time 0        \
+    --hide progress,mouse     \
+    --stop-at-end             \
+    --max-files 99999999999   \
+    --max-file-lag 0.000001   \
+    --bloom-multiplier 1.3    \
+    -1280x720
+
+#ffmpeg flags for gource
+FFMPEGFLAGS=                  \
+    -y                        \
+    -r 60                     \
+    -f image2pipe             \
+    -vcodec ppm               \
+    -i -                      \
+    -vcodec libx264           \
+    -preset ultrafast         \
+    -crf 1                    \
+    -threads 0                \
+    -bf 0
+
 #splint flags
 SPLINTFLAGS =            \
     -redef               \
@@ -119,7 +150,6 @@ SPLINTFLAGS =            \
     -kepttrans           \
     -unqualifiedtrans    \
     +matchanyintegral    \
-    -bufferoverflowhigh  \
     +voidabstract        \
     -nullassign          \
     -unrecog             \
@@ -147,7 +177,7 @@ $(QCVM): $(OBJ_X)
        $(CC) -o $@ $^ $(CFLAGS) -lm
 
 $(GMQCC): $(OBJ_C) $(OBJ_D)
-       $(CC) -o $@ $^ $(CFLAGS)
+       $(CC) -o $@ $^ $(CFLAGS) -lm
 
 $(TESTSUITE): $(OBJ_T)
        $(CC) -o $@ $^ $(CFLAGS) -lm
@@ -163,11 +193,17 @@ test: all
        @ ./$(TESTSUITE)
 
 clean:
-       rm -f *.o $(GMQCC) $(QCVM) $(TESTSUITE) $(PAK) *.dat
+       rm -f *.o $(GMQCC) $(QCVM) $(TESTSUITE) $(PAK) *.dat gource.mp4
 
 splint:
        @  splint $(SPLINTFLAGS) *.c *.h
 
+gource:
+       @ gource $(GOURCEFLAGS)
+
+gource-record:
+       @ gource $(GOURCEFLAGS) -o - | ffmpeg $(FFMPEGFLAGS) gource.mp4
+
 depend:
        @makedepend    -Y -w 65536 2> /dev/null \
                $(subst .o,.c,$(OBJ_D))
@@ -219,9 +255,15 @@ fs.o: gmqcc.h opts.def
 
 main.o: gmqcc.h opts.def lexer.h
 lexer.o: gmqcc.h opts.def lexer.h
-parser.o: gmqcc.h opts.def lexer.h ast.h ir.h
+parser.o: gmqcc.h opts.def lexer.h ast.h ir.h intrin.h
 fs.o: gmqcc.h opts.def
 
 util.o: gmqcc.h opts.def
 conout.o: gmqcc.h opts.def
 fs.o: gmqcc.h opts.def
+
+util.o: gmqcc.h opts.def
+fs.o: gmqcc.h opts.def
+conout.o: gmqcc.h opts.def
+opts.o: gmqcc.h opts.def
+pak.o: gmqcc.h opts.def
diff --git a/README b/README
index 252986434bc83d685d61b7d87d96cd42fe38fb39..bd71be0477c21f17051b784b57159e48c3bde94f 100644 (file)
--- a/README
+++ b/README
@@ -1,10 +1,14 @@
 GMQCC: An improved Quake C compiler
 
-For licensing:          see the LICENSE file.
-For installation notes: see the INSTALL file.
-For a list of authors:  see the AUTHORS file.
-For a list of changes:  see the CHANGES file.
+For licensing:           see the LICENSE file.
+For installation notes:  see the INSTALL file.
+For a list of authors:   see the AUTHORS file.
+For a list of changes:   see the CHANGES file.
 
 For documentation:
     See the manpages, or visit the documentation online at
     http://graphitemaster.github.com/gmqcc/doc.html
+
+For syntax highlighting description files, or information
+regarding how to install them:
+    See the README in syntax directory
diff --git a/ast.c b/ast.c
index c0c92fbe3ab122f7c15fa0b0a588b54f7f549c0a..22c4defd76c694d3b696a6509c8408dc2c27934b 100644 (file)
--- a/ast.c
+++ b/ast.c
@@ -42,7 +42,7 @@ static GMQCC_NORETURN void _ast_node_destroy(ast_node *self)
 {
     (void)self;
     con_err("ast node missing destroy()\n");
-    abort();
+    exit(EXIT_FAILURE);
 }
 
 /* Initialize main ast node aprts */
@@ -87,6 +87,8 @@ static void ast_expression_delete(ast_expression *self)
         ast_delete(self->expression.params[i]);
     }
     vec_free(self->expression.params);
+    if (self->expression.varparam)
+        ast_delete(self->expression.varparam);
 }
 
 static void ast_expression_delete_full(ast_expression *self)
@@ -218,7 +220,7 @@ static size_t ast_type_to_string_impl(ast_expression *e, char *buf, size_t bufsi
     if (!e) {
         if (pos + 6 >= bufsize)
             goto full;
-        strcpy(buf + pos, "(null)");
+        strncpy(buf + pos, "(null)", 6);
         return pos + 6;
     }
 
@@ -227,7 +229,7 @@ static size_t ast_type_to_string_impl(ast_expression *e, char *buf, size_t bufsi
 
     switch (e->expression.vtype) {
         case TYPE_VARIANT:
-            strcpy(buf + pos, "(variant)");
+            strncpy(buf + pos, "(variant)", 9);
             return pos + 9;
 
         case TYPE_FIELD:
@@ -284,7 +286,7 @@ static size_t ast_type_to_string_impl(ast_expression *e, char *buf, size_t bufsi
             typelen = strlen(typestr);
             if (pos + typelen >= bufsize)
                 goto full;
-            strcpy(buf + pos, typestr);
+            strncpy(buf + pos, typestr, typelen);
             return pos + typelen;
     }
 
@@ -584,6 +586,7 @@ void ast_member_delete(ast_member *self)
      * purpose that is not garbage-collected.
     */
     ast_expression_delete((ast_expression*)self);
+    mem_d(self->name);
     mem_d(self);
 }
 
@@ -1216,7 +1219,7 @@ bool ast_global_codegen(ast_value *self, ir_builder *ir, bool isfield)
 
             namelen = strlen(self->name);
             name    = (char*)mem_a(namelen + 16);
-            strcpy(name, self->name);
+            strncpy(name, self->name, namelen);
 
             array->ir_values = (ir_value**)mem_a(sizeof(array->ir_values[0]) * array->expression.count);
             array->ir_values[0] = v;
@@ -1274,7 +1277,7 @@ bool ast_global_codegen(ast_value *self, ir_builder *ir, bool isfield)
 
         namelen = strlen(self->name);
         name    = (char*)mem_a(namelen + 16);
-        strcpy(name, self->name);
+        strncpy(name, self->name, namelen);
 
         self->ir_values = (ir_value**)mem_a(sizeof(self->ir_values[0]) * self->expression.count);
         self->ir_values[0] = v;
@@ -1407,7 +1410,7 @@ bool ast_local_codegen(ast_value *self, ir_function *func, bool param)
 
         v = ir_function_create_local(func, self->name, vtype, param);
         if (!v) {
-            compile_error(ast_ctx(self), "ir_function_create_local failed");
+            compile_error(ast_ctx(self), "internal error: ir_function_create_local failed");
             return false;
         }
         v->context = ast_ctx(self);
@@ -1416,20 +1419,21 @@ bool ast_local_codegen(ast_value *self, ir_function *func, bool param)
 
         namelen = strlen(self->name);
         name    = (char*)mem_a(namelen + 16);
-        strcpy(name, self->name);
+        strncpy(name, self->name, namelen);
 
         self->ir_values[0] = v;
         for (ai = 1; ai < self->expression.count; ++ai) {
             snprintf(name + namelen, 16, "[%u]", (unsigned int)ai);
             self->ir_values[ai] = ir_function_create_local(func, name, vtype, param);
             if (!self->ir_values[ai]) {
-                compile_error(ast_ctx(self), "ir_builder_create_global failed on `%s`", name);
+                compile_error(ast_ctx(self), "internal_error: ir_builder_create_global failed on `%s`", name);
                 return false;
             }
             self->ir_values[ai]->context = ast_ctx(self);
             self->ir_values[ai]->unique_life = true;
             self->ir_values[ai]->locked      = true;
         }
+        mem_d(name);
     }
     else
     {
@@ -1538,7 +1542,7 @@ bool ast_function_codegen(ast_function *self, ir_builder *ir)
 
     irf = self->ir_func;
     if (!irf) {
-        compile_error(ast_ctx(self), "ast_function's related ast_value was not generated yet");
+        compile_error(ast_ctx(self), "internal error: ast_function's related ast_value was not generated yet");
         return false;
     }
 
index 8613ece127e502c01123dfb00e6a96ca525b6537..00b81580760e90989ab4c46d9255c976d57e75f1 100644 (file)
--- a/conout.c
+++ b/conout.c
@@ -168,7 +168,7 @@ static int win_fputs(FILE *h, const char *str) {
                 state    = -1;
             }
         } else {
-            fs_file_putc(h, *str);
+            fs_file_write(str, 1, 1, stdout);
             length ++;
         }
         str++;
@@ -282,10 +282,6 @@ int con_change(const char *out, const char *err) {
         con_enablecolor();
     } else if (!(console.handle_err = fs_file_open(err, "w"))) return 0;
 
-    /* no buffering */
-    setvbuf(console.handle_out, NULL, _IONBF, 0);
-    setvbuf(console.handle_err, NULL, _IONBF, 0);
-
     return 1;
 }
 
diff --git a/distro/Makefile b/distro/Makefile
new file mode 100644 (file)
index 0000000..d29cff8
--- /dev/null
@@ -0,0 +1,34 @@
+DROPBOX := dropbox_uploader.sh
+UNAME   := $(shell uname -m)
+ifneq ($(shell uname -m), x86_64)
+    $(error Cannot build packages without an x86_64 capable CPU)
+endif
+
+.NOTPARALLEL: base
+.NOTPARALLEL: upload
+
+base:
+       $(MAKE) -C deb/
+       $(MAKE) -C deb/ CARCH=i686
+       $(MAKE) -C archlinux/this/
+       $(MAKE) -C archlinux/this/ CARCH=i686
+       @mv deb/*.deb ./
+       @mv archlinux/this/*pkg.tar.xz ./
+
+upload:
+       @echo "APPKEY:76vh3q42hnvmzm3" > dropbox_config
+       @echo "APPSECRET:tmeecht2cmh72xa" >> dropbox_config
+       @echo "ACCESS_LEVEL:sandbox" >> dropbox_config
+       @echo "OAUTH_ACCESS_TOKEN:w0bxzf0dft8edfq" >> dropbox_config
+       @echo "OAUTH_ACCESS_TOKEN_SECRET:9vosx7x8gy4kgjk" >> dropbox_config
+       @wget -q "http://raw.github.com/andreafabrizi/Dropbox-Uploader/master/dropbox_uploader.sh"
+       @chmod +x dropbox_uploader.sh
+       @sed -i -e "s/~\/.dropbox_uploader/.\/dropbox_config/g" $$(basename $(DROPBOX))
+       @find . -type f -regex ".*/.*\.\(xz\|deb\)" -exec ./$$(basename $(DROPBOX)) upload {} \;
+       @rm dropbox_config dropbox_uploader.sh
+
+clean:
+       @rm -f *.deb
+       @rm -f *.pkg.tar.xz
+
+all: base upload
index 2144110adb287b64e8aab4aeec1868cb50769c83..006c8d082aab3e8d86171cd9500e25c9adb6a70d 100644 (file)
@@ -47,9 +47,9 @@ check() {
 package() {
        cd "$srcdir"/"$_gitname"
        msg "Compiling and installing to pkgdir this time..."
-       gmake install DESTDIR=$pkgdir PREFIX=/usr/local MANDIR=/usr/local/man
+       gmake install DESTDIR=$pkgdir PREFIX=/usr
        msg "Compiling done."
 
-       install -dm755 ${pkgdir}/usr/local/share/licenses/gmqcc
-       install -m644 LICENSE ${pkgdir}/usr/local/share/licenses/gmqcc/LICENSE
+       install -dm755 ${pkgdir}/usr/share/licenses/gmqcc
+       install -m644 LICENSE ${pkgdir}/usr/share/licenses/gmqcc/LICENSE
 }
index 8c449c6a8a53969d791d5442892b931f9fc2b61a..246cf732c098593e3738405aee0e4f2ad6e9923d 100644 (file)
@@ -30,9 +30,9 @@ check() {
 package() {
        cd "$srcdir"/"$_gitname"
        msg "Compiling and installing to pkgdir this time..."
-       gmake install DESTDIR=$pkgdir PREFIX=/usr/local MANDIR=/usr/local/man
+       gmake install DESTDIR=$pkgdir PREFIX=/usr
        msg "Compiling done."
 
-       install -dm755 ${pkgdir}/usr/local/share/licenses/gmqcc
-       install -m644 LICENSE ${pkgdir}/usr/local/share/licenses/gmqcc/LICENSE
+       install -dm755 ${pkgdir}/usr/share/licenses/gmqcc
+       install -m644 LICENSE ${pkgdir}/usr/share/licenses/gmqcc/LICENSE
 }
index 4a9127cf37d532173b54cb2a13035c9570c19812..0ac2423a4828eef01a1b67ab0721df58ee3f912d 100644 (file)
@@ -1,7 +1,11 @@
 # Contributor: matthiaskrgr <matthiaskrgr _strange_curverd_character_ freedroid D0T org>
 
 pkgname=gmqcc-git
-pkgver=20130127
+pkgver=0.2.524.gc6bd5e6
+pkgver(){
+    cd gmqcc
+    git describe --tags | sed -e 's/^gmqcc\-//' -e 's/-/./g'
+}
 pkgrel=1
 pkgdesc="An Improved Quake C Compiler"
 arch=('i686' 'x86_64')
@@ -11,40 +15,25 @@ provides=('gmqcc=0.2.4')
 makedepends=('git')
 url="https://github.com/graphitemaster/gmqcc.git"
 license=('MIT')
+source=('gmqcc::git://github.com/graphitemaster/gmqcc.git')
+sha1sums=('SKIP')
 
-_gitroot="git://github.com/graphitemaster/gmqcc.git"
-_gitname="gmqcc"
 
 build() {
-       cd $srcdir
-       msg "Connecting to the GIT server..."
-       if [[ -d $srcdir/$_gitname ]] ; then
-               cd $_gitname
-               msg "Removing build files..."
-               git clean -dfx
-               msg "Updating..."
-               git pull --no-tags
-               msg "The local files are updated."
-       else
-               msg "Cloning..."
-               git clone $_gitroot $_gitname --depth 1
-               msg "Clone done."
-       fi
-
        msg "Starting compilation..."
-       cd "$srcdir"/"$_gitname"
+       cd "$srcdir"/"gmqcc"
 
        msg "Compiling..."
        make
 }
 
 check() {
-       cd "$srcdir"/"$_gitname"
+       cd "$srcdir"/"gmqcc"
        make check
 }
 
 package() {
-       cd "$srcdir"/"$_gitname"
+       cd "$srcdir"/"gmqcc"
        msg "Compiling and installing to pkgdir this time..."
        make install DESTDIR=$pkgdir PREFIX=/usr
        msg "Compiling done."
index 1f8f66e5b6e8bbbec1e24eabce8610f003d4cecf..ef7de8fea3c2b8a06e5b9ac5dddfaeba7b592ed6 100644 (file)
@@ -9,7 +9,7 @@ depends=('glibc')
 url="https://github.com/graphitemaster/gmqcc.git"
 license=('MIT')
 source=(gmqcc-$pkgver.zip::https://github.com/graphitemaster/gmqcc/zipball/$pkgver)
-sha1sums=('8cd91dc13f70cd9d3767602bf3eb47a1906d9353')
+sha1sums=('e0fe99af9a55d36cd9e0909a96d1b14f2db8b757')
 
 _gitname=graphitemaster-gmqcc-de24486/
 
index 6d3b557580f43230742274e032324834bb37fe40..bf5a3e86e7f9ca8842ee710e6078013d159fd685 100644 (file)
@@ -9,9 +9,17 @@ CARCH   := $(shell uname -m)
 PKGDIR  := gmqcc-$(MAJOR).$(MINOR).$(PATCH)-$(PKGREL)-$(CARCH)
 PKG     := $(PKGDIR).pkg.tar.xz
 PKGINFO := $(PKGDIR)/.PKGINFO
+DESTDIR := distro/archlinux/this/$(PKGDIR)
+CFLAGS  :=
 
-base: clean
-       $(MAKE) -C $(BASEDIR) DESTDIR=distro/archlinux/this/$(PKGDIR) PREFIX=$(PREFIX) install
+
+ifneq (, $(findstring i686, $(CARCH)))
+       CFLAGS := -m32
+endif
+
+base:
+       $(MAKE) -C $(BASEDIR) clean
+       $(MAKE) -C $(BASEDIR) OPTIONAL=$(CFLAGS) DESTDIR=$(DESTDIR) PREFIX=$(PREFIX) install
        @echo "pkgname = gmqcc" > $(PKGINFO)
        @echo "pkgver = $(MAJOR).$(MINOR).$(PATCH)-$(PKGREL)" >> $(PKGINFO)
        @echo "pkgdesc = An Improved Quake C Compiler" >> $(PKGINFO)
@@ -34,7 +42,7 @@ base: clean
        @rm -rf $(PKGDIR)
 
 clean:
-       @rm -f  $(PKG)
-
+       $(MAKE) -C $(BASEDIR) clean
+       @rm -f *.pkg.tar.xz
 
 all: base
index 535cf1cdabaa1b1ad511824fa1a1d53686da7a35..df23602b592b94a07c7ef47d0792b030dad86e0b 100644 (file)
@@ -5,19 +5,39 @@ MAJOR   := `sed -n -e '/GMQCC_VERSION_MAJOR/{s/.* .* //;p;q;}' $(HEADER)`
 MINOR   := `sed -n -e '/GMQCC_VERSION_MINOR/{s/.* .* //;p;q;}' $(HEADER)`
 PATCH   := `sed -n -e '/GMQCC_VERSION_PATCH/{s/.* .* //;p;q;}' $(HEADER)`
 DEBDIR  := gmqcc-$(MAJOR).$(MINOR).$(PATCH)
-DEB     := $(DEBDIR).deb
+CARCH   := $(shell uname -m)
+DEB     := $(DEBDIR)-$(CARCH).deb
+CONTROL := $(DEBDIR)/DEBIAN/control
+
+ifneq (, $(findstring i686, $(CARCH)))
+       CFLAGS := -m32
+endif
 
 base:
+       $(MAKE) -C $(BASEDIR) clean
        $(MAKE) -C $(BASEDIR) DESTDIR=distro/deb/$(DEBDIR) PREFIX=$(PREFIX) install
        @install -d -m755 $(DEBDIR)/DEBIAN
-       @cp       control $(DEBDIR)/DEBIAN/control
+       @echo "Package: gmqcc" > $(CONTROL)
+       @echo "Version: $(MAJOR).$(MINOR).$(PATCH)" >> $(CONTROL)
+       @echo "Section: user/hidden" >> $(CONTROL)
+       @echo "Priority: optional" >> $(CONTROL)
+       @echo "Architecture: $(CARCH)" >> $(CONTROL)
+       @echo "Installed-Size: `du -ks $($(DEBDIR)/usr) | cut -f 1`" >> $(CONTROL)
+       @echo "Maintainer: Dale Weiler <killfieldengine@gmail.com>" >> $(CONTROL)
+       @echo "Description: An improved Quake C Compiler" >> $(CONTROL)
+       @echo "   For an enduring period of time the options for a decent compiler for the Quake C programming language" >> $(CONTROL)
+       @echo "   were confined to a specific compiler known as QCC. Attempts were made to extend and improve upon the" >> $(CONTROL)
+       @echo "   design of QCC, but many foreseen the consequences of building on a broken foundation. The solution" >> $(CONTROL)
+       @echo "   was obvious, a new compiler; one born from the NIH realm of sarcastic wit. We welcome you. You won't" >> $(CONTROL)
+       @echo "   find a better Quake C compiler." >> $(CONTROL)
        @tar czf data.tar.gz -C $(DEBDIR)/ . --exclude=DEBIAN
        @tar czf control.tar.gz -C $(DEBDIR)/DEBIAN/ .
        @echo 2.0 > debian-binary
        @ar r $(DEB) debian-binary control.tar.gz data.tar.gz
        @rm -rf debian-binary control.tar.gz data.tar.gz $(DEBDIR)
-clean:
-       @rm -f $(DEB)
 
+clean:
+       $(MAKE) -C $(BASEDIR) clean
+       @rm -f *.deb
 
 all: base
diff --git a/distro/deb/control b/distro/deb/control
deleted file mode 100644 (file)
index 57cde3a..0000000
+++ /dev/null
@@ -1,13 +0,0 @@
-Package: gmqcc
-Version: 0.3.0
-Section: user/hidden
-Priority: optional
-Architecture: i386
-Installed-Size: `du -ks usr|cut -f 1`
-Maintainer: Dale Weiler <killfieldengine@gmail.com>
-Description: An improved Quake C Compiler
-   For an enduring period of time the options for a decent compiler for the Quake C programming language
-   were confined to a specific compiler known as QCC. Attempts were made to extend and improve upon the
-   design of QCC, but many foreseen the consequences of building on a broken foundation. The solution
-   was obvious, a new compiler; one born from the NIH realm of sarcastic wit. We welcome you. You won't
-   find a better Quake C compiler.
diff --git a/exec.c b/exec.c
index f44bd6295f8bd35f409f31519caf857041286619..8b0ee5e830722084c8e522840c05239cd918b431 100644 (file)
--- a/exec.c
+++ b/exec.c
@@ -455,7 +455,6 @@ static void prog_print_statement(qc_program *prog, prog_section_statement *st)
         else           printf("(none)");
         printf("\n");
     }
-    fflush(stdout);
 }
 
 static qcint prog_enterfunction(qc_program *prog, prog_section_function *func)
@@ -825,6 +824,16 @@ static int qc_strcmp(qc_program *prog)
     return 0;
 }
 
+static int qc_floor(qc_program *prog)
+{
+    qcany *num, out;
+    CheckArgs(1);
+    num = GetArg(0);
+    out._float = floor(num->_float);
+    Return(out);
+    return 0;
+}
+
 static prog_builtin qc_builtins[] = {
     NULL,
     &qc_print,       /*   1   */
@@ -839,7 +848,8 @@ static prog_builtin qc_builtins[] = {
     &qc_strcat,      /*   10  */
     &qc_strcmp,      /*   11  */
     &qc_normalize,   /*   12  */
-    &qc_sqrt         /*   13  */
+    &qc_sqrt,        /*   13  */
+    &qc_floor        /*   14  */
 };
 static size_t qc_builtins_count = sizeof(qc_builtins) / sizeof(qc_builtins[0]);
 
diff --git a/fs.c b/fs.c
index 8d73b1d0b8ae793aa8dfefad075c9013457298ec..997b368b13ce6bf801ff5637b9689ae464e853b0 100644 (file)
--- a/fs.c
+++ b/fs.c
@@ -53,7 +53,7 @@
     ) {
         wprintf(L"Invalid parameter dectected %s:%d %s [%s]\n", file, line, function, expression);
         wprintf(L"Aborting ...\n");
-        abort();
+        exit(EXIT_FAILURE);
     }
 
     static void file_init() {
@@ -165,16 +165,6 @@ int fs_file_seek(FILE *fp, long int off, int whence) {
     return fseek(fp, off, whence);
 }
 
-int fs_file_putc(FILE *fp, int ch) {
-    /* Invokes file_exception on windows if fp is null */
-    return fputc(ch, fp);
-}
-
-int fs_file_flush(FILE *fp) {
-    /* Invokes file_exception on windows if fp is null */
-    return fflush(fp);
-}
-
 long int fs_file_tell(FILE *fp) {
     /* Invokes file_exception on windows if fp is null */
     return ftell(fp);
@@ -238,7 +228,7 @@ int fs_file_getline(char **lineptr, size_t *n, FILE *stream) {
         if (!dir)
             return NULL;
 
-        strcpy(dir->dd_name, name);
+        strncpy(dir->dd_name, name, strlen(name));
         return dir;
     }
         
@@ -258,8 +248,8 @@ int fs_file_getline(char **lineptr, size_t *n, FILE *stream) {
             if (*dir->dd_name) {
                 size_t n = strlen(dir->dd_name);
                 if ((dirname  = (char*)mem_a(n + 5) /* 4 + 1 */)) {
-                    strcpy(dirname,     dir->dd_name);
-                    strcpy(dirname + n, "\\*.*");   /* 4 + 1 */
+                    strncpy(dirname, dir->dd_name, n);
+                    strncpy(dirname + n, "\\*.*", 4);   /* 4 + 1 */
                 }
             } else {
                 if (!(dirname = util_strdup("\\*.*")))
@@ -303,7 +293,6 @@ int fs_file_getline(char **lineptr, size_t *n, FILE *stream) {
 #else
 #   if !defined(__MINGW32__)
 #       include <sys/stat.h> /* mkdir */
-#       include <unistd.h>   /* chdir */
 
         int fs_dir_make(const char *path) {
             return mkdir(path, 0700);
@@ -326,8 +315,4 @@ struct dirent *fs_dir_read(DIR *dir) {
     return readdir(dir);
 }
 
-int fs_dir_change(const char *path) {
-    return chdir(path);
-}
-
 #endif /*! defined(_WIN32) && !defined(__MINGW32__) */
diff --git a/ftepp.c b/ftepp.c
index 68491ed6dd2af72e1c8e69035b6e48bce5384a35..803466598e83b883858ad563e6c7a7f177f92adf 100644 (file)
--- a/ftepp.c
+++ b/ftepp.c
@@ -25,6 +25,7 @@
 #include "gmqcc.h"
 #include "lexer.h"
 
+#define HT_MACROS 1024
 typedef struct {
     bool on;
     bool was_on;
@@ -55,14 +56,15 @@ typedef struct {
     pptoken **output;
 } ppmacro;
 
-typedef struct {
+typedef struct ftepp_s {
     lex_file    *lex;
     int          token;
     unsigned int errors;
 
     bool         output_on;
     ppcondition *conditions;
-    ppmacro    **macros;
+    /*ppmacro    **macros;*/
+    ht           macros; /* hashtable<string, ppmacro*> */
 
     char        *output_string;
 
@@ -124,7 +126,7 @@ char *ftepp_predef_line(lex_file *context) {
 char *ftepp_predef_file(lex_file *context) {
     size_t  length = strlen(context->name) + 3; /* two quotes and a terminator */
     char   *value  = (char*)mem_a(length);
-    sprintf(value, "\"%s\"", context->name);
+    snprintf(value, length, "\"%s\"", context->name);
 
     return value;
 }
@@ -225,7 +227,7 @@ static pptoken *pptoken_make(ftepp_t *ftepp)
     return token;
 }
 
-static void pptoken_delete(pptoken *self)
+static GMQCC_INLINE void pptoken_delete(pptoken *self)
 {
     mem_d(self->value);
     mem_d(self);
@@ -261,27 +263,27 @@ static ftepp_t* ftepp_new()
     ftepp = (ftepp_t*)mem_a(sizeof(*ftepp));
     memset(ftepp, 0, sizeof(*ftepp));
 
+    ftepp->macros    = util_htnew(HT_MACROS);
     ftepp->output_on = true;
 
     return ftepp;
 }
 
-static void ftepp_flush_do(ftepp_t *self)
+static GMQCC_INLINE void ftepp_flush_do(ftepp_t *self)
 {
     vec_free(self->output_string);
 }
 
 static void ftepp_delete(ftepp_t *self)
 {
-    size_t i;
     ftepp_flush_do(self);
     if (self->itemname)
         mem_d(self->itemname);
     if (self->includename)
         vec_free(self->includename);
-    for (i = 0; i < vec_size(self->macros); ++i)
-        ppmacro_delete(self->macros[i]);
-    vec_free(self->macros);
+
+    util_htrem(self->macros, (void (*)(void*))&ppmacro_delete);
+
     vec_free(self->conditions);
     if (self->lex)
         lex_close(self->lex);
@@ -300,7 +302,7 @@ static void ftepp_out(ftepp_t *ftepp, const char *str, bool ignore_cond)
     }
 }
 
-static void ftepp_update_output_condition(ftepp_t *ftepp)
+static GMQCC_INLINE void ftepp_update_output_condition(ftepp_t *ftepp)
 {
     size_t i;
     ftepp->output_on = true;
@@ -308,25 +310,14 @@ static void ftepp_update_output_condition(ftepp_t *ftepp)
         ftepp->output_on = ftepp->output_on && ftepp->conditions[i].on;
 }
 
-static ppmacro* ftepp_macro_find(ftepp_t *ftepp, const char *name)
+static GMQCC_INLINE ppmacro* ftepp_macro_find(ftepp_t *ftepp, const char *name)
 {
-    size_t i;
-    for (i = 0; i < vec_size(ftepp->macros); ++i) {
-        if (!strcmp(name, ftepp->macros[i]->name))
-            return ftepp->macros[i];
-    }
-    return NULL;
+    return util_htget(ftepp->macros, name);
 }
 
-static void ftepp_macro_delete(ftepp_t *ftepp, const char *name)
+static GMQCC_INLINE void ftepp_macro_delete(ftepp_t *ftepp, const char *name)
 {
-    size_t i;
-    for (i = 0; i < vec_size(ftepp->macros); ++i) {
-        if (!strcmp(name, ftepp->macros[i]->name)) {
-            vec_remove(ftepp->macros, i, 1);
-            return;
-        }
-    }
+    util_htrm(ftepp->macros, name, NULL);
 }
 
 static GMQCC_INLINE int ftepp_next(ftepp_t *ftepp)
@@ -394,6 +385,7 @@ static bool ftepp_define_params(ftepp_t *ftepp, ppmacro *macro)
             return false;
         }
     } while (ftepp->token == ',');
+
     if (ftepp->token != ')') {
         ftepp_error(ftepp, "expected closing paren after macro parameter list");
         return false;
@@ -422,7 +414,7 @@ static bool ftepp_define_body(ftepp_t *ftepp, ppmacro *macro)
                         return false;
                     }
 
-                    index = atoi(ftepp_tokval(ftepp));
+                    index = (int)strtol(ftepp_tokval(ftepp), NULL, 10);
 
                     if (ftepp_next(ftepp) != ']') {
                         ftepp_error(ftepp, "expected `]` in __VA_ARGS__ subscript");
@@ -471,7 +463,7 @@ static bool ftepp_define_body(ftepp_t *ftepp, ppmacro *macro)
 
 static bool ftepp_define(ftepp_t *ftepp)
 {
-    ppmacro *macro;
+    ppmacro *macro = NULL;
     size_t l = ftepp_ctx(ftepp).line;
 
     (void)ftepp_next(ftepp);
@@ -499,18 +491,28 @@ static bool ftepp_define(ftepp_t *ftepp)
 
     if (ftepp->token == '(') {
         macro->has_params = true;
-        if (!ftepp_define_params(ftepp, macro))
+        if (!ftepp_define_params(ftepp, macro)) {
+            ppmacro_delete(macro);
             return false;
+        }
     }
 
-    if (!ftepp_skipspace(ftepp))
+    if (!ftepp_skipspace(ftepp)) {
+        ppmacro_delete(macro);
         return false;
+    }
 
-    if (!ftepp_define_body(ftepp, macro))
+    if (!ftepp_define_body(ftepp, macro)) {
+        ppmacro_delete(macro);
         return false;
+    }
 
+#if 0
     if (ftepp->output_on)
         vec_push(ftepp->macros, macro);
+#endif
+    if (ftepp->output_on)
+        util_htset(ftepp->macros, macro->name, (void*)macro);
     else {
         ppmacro_delete(macro);
     }
@@ -830,7 +832,7 @@ static bool ftepp_macro_expand(ftepp_t *ftepp, ppmacro *macro, macroparam *param
 
     if (resetline && !ftepp->in_macro) {
         char lineno[128];
-        sprintf(lineno, "\n#pragma line(%lu)\n", (unsigned long)(old_lexer->sline));
+        snprintf(lineno, 128, "\n#pragma line(%lu)\n", (unsigned long)(old_lexer->sline));
         ftepp_out(ftepp, lineno, false);
     }
 
@@ -1706,9 +1708,7 @@ static bool ftepp_preprocess(ftepp_t *ftepp)
 /* Like in parser.c - files keep the previous state so we have one global
  * preprocessor. Except here we will want to warn about dangling #ifs.
  */
-static ftepp_t *ftepp;
-
-static bool ftepp_preprocess_done()
+static bool ftepp_preprocess_done(ftepp_t *ftepp)
 {
     bool retval = true;
     if (vec_size(ftepp->conditions)) {
@@ -1724,7 +1724,7 @@ static bool ftepp_preprocess_done()
     return retval;
 }
 
-bool ftepp_preprocess_file(const char *filename)
+bool ftepp_preprocess_file(ftepp_t *ftepp, const char *filename)
 {
     ftepp->lex = lex_open(filename);
     ftepp->itemname = util_strdup(filename);
@@ -1734,10 +1734,10 @@ bool ftepp_preprocess_file(const char *filename)
     }
     if (!ftepp_preprocess(ftepp))
         return false;
-    return ftepp_preprocess_done();
+    return ftepp_preprocess_done(ftepp);
 }
 
-bool ftepp_preprocess_string(const char *name, const char *str)
+bool ftepp_preprocess_string(ftepp_t *ftepp, const char *name, const char *str)
 {
     ftepp->lex = lex_open_string(str, strlen(str), name);
     ftepp->itemname = util_strdup(name);
@@ -1747,16 +1747,16 @@ bool ftepp_preprocess_string(const char *name, const char *str)
     }
     if (!ftepp_preprocess(ftepp))
         return false;
-    return ftepp_preprocess_done();
+    return ftepp_preprocess_done(ftepp);
 }
 
 
-void ftepp_add_macro(const char *name, const char *value) {
+void ftepp_add_macro(ftepp_t *ftepp, const char *name, const char *value) {
     char *create = NULL;
 
     /* use saner path for empty macros */
     if (!value) {
-        ftepp_add_define("__builtin__", name);
+        ftepp_add_define(ftepp, "__builtin__", name);
         return;
     }
 
@@ -1766,26 +1766,27 @@ void ftepp_add_macro(const char *name, const char *value) {
     vec_upload(create, value, strlen(value));
     vec_push  (create, 0);
 
-    ftepp_preprocess_string("__builtin__", create);
+    ftepp_preprocess_string(ftepp, "__builtin__", create);
     vec_free  (create);
 }
 
-bool ftepp_init()
+ftepp_t *ftepp_create()
 {
+    ftepp_t *ftepp;
     char minor[32];
     char major[32];
 
     ftepp = ftepp_new();
     if (!ftepp)
-        return false;
+        return NULL;
 
     memset(minor, 0, sizeof(minor));
     memset(major, 0, sizeof(major));
 
     /* set the right macro based on the selected standard */
-    ftepp_add_define(NULL, "GMQCC");
+    ftepp_add_define(ftepp, NULL, "GMQCC");
     if (OPTS_OPTION_U32(OPTION_STANDARD) == COMPILER_FTEQCC) {
-        ftepp_add_define(NULL, "__STD_FTEQCC__");
+        ftepp_add_define(ftepp, NULL, "__STD_FTEQCC__");
         /* 1.00 */
         major[0] = '"';
         major[1] = '1';
@@ -1795,15 +1796,15 @@ bool ftepp_init()
         minor[1] = '0';
         minor[2] = '"';
     } else if (OPTS_OPTION_U32(OPTION_STANDARD) == COMPILER_GMQCC) {
-        ftepp_add_define(NULL, "__STD_GMQCC__");
-        sprintf(major, "\"%d\"", GMQCC_VERSION_MAJOR);
-        sprintf(minor, "\"%d\"", GMQCC_VERSION_MINOR);
+        ftepp_add_define(ftepp, NULL, "__STD_GMQCC__");
+        snprintf(major, 32, "\"%d\"", GMQCC_VERSION_MAJOR);
+        snprintf(minor, 32, "\"%d\"", GMQCC_VERSION_MINOR);
     } else if (OPTS_OPTION_U32(OPTION_STANDARD) == COMPILER_QCCX) {
-        ftepp_add_define(NULL, "__STD_QCCX__");
-        sprintf(major, "\"%d\"", GMQCC_VERSION_MAJOR);
-        sprintf(minor, "\"%d\"", GMQCC_VERSION_MINOR);
+        ftepp_add_define(ftepp, NULL, "__STD_QCCX__");
+        snprintf(major, 32, "\"%d\"", GMQCC_VERSION_MAJOR);
+        snprintf(minor, 32, "\"%d\"", GMQCC_VERSION_MINOR);
     } else if (OPTS_OPTION_U32(OPTION_STANDARD) == COMPILER_QCC) {
-        ftepp_add_define(NULL, "__STD_QCC__");
+        ftepp_add_define(ftepp, NULL, "__STD_QCC__");
         /* 1.0 */
         major[0] = '"';
         major[1] = '1';
@@ -1814,35 +1815,35 @@ bool ftepp_init()
         minor[2] = '"';
     }
 
-    ftepp_add_macro("__STD_VERSION_MINOR__", minor);
-    ftepp_add_macro("__STD_VERSION_MAJOR__", major);
+    ftepp_add_macro(ftepp, "__STD_VERSION_MINOR__", minor);
+    ftepp_add_macro(ftepp, "__STD_VERSION_MAJOR__", major);
 
-    return true;
+    return ftepp;
 }
 
-void ftepp_add_define(const char *source, const char *name)
+void ftepp_add_define(ftepp_t *ftepp, const char *source, const char *name)
 {
     ppmacro *macro;
     lex_ctx ctx = { "__builtin__", 0 };
     ctx.file = source;
     macro = ppmacro_new(ctx, name);
-    vec_push(ftepp->macros, macro);
+    /*vec_push(ftepp->macros, macro);*/
+    util_htset(ftepp->macros, name, macro);
 }
 
-const char *ftepp_get()
+const char *ftepp_get(ftepp_t *ftepp)
 {
     return ftepp->output_string;
 }
 
-void ftepp_flush()
+void ftepp_flush(ftepp_t *ftepp)
 {
     ftepp_flush_do(ftepp);
 }
 
-void ftepp_finish()
+void ftepp_finish(ftepp_t *ftepp)
 {
     if (!ftepp)
         return;
     ftepp_delete(ftepp);
-    ftepp = NULL;
 }
diff --git a/gmqcc.h b/gmqcc.h
index 2cc0864126451a10d4d13b7fa6328ca654c2f0e6..32028c11c7643e6f6ac087ad12e1aa6aa9cef54d 100644 (file)
--- a/gmqcc.h
+++ b/gmqcc.h
@@ -300,7 +300,8 @@ void  util_meminfo       ();
 bool  util_filexists     (const char *);
 bool  util_strupper      (const char *);
 bool  util_strdigit      (const char *);
-char *util_strdup        (const char *);
+char *_util_Estrdup      (const char *, const char *, size_t);
+char *_util_Estrdup_empty(const char *, const char *, size_t);
 void  util_debug         (const char *, const char *, ...);
 void  util_endianswap    (void *,  size_t, unsigned int);
 
@@ -317,22 +318,27 @@ int util_asprintf (char **ret, const char *fmt, ...);
 
 
 #ifdef NOTRACK
-#    define mem_a(x)    malloc (x)
-#    define mem_d(x)    free   ((void*)x)
-#    define mem_r(x, n) realloc((void*)x, n)
+#    define mem_a(x)      malloc (x)
+#    define mem_d(x)      free   ((void*)x)
+#    define mem_r(x, n)   realloc((void*)x, n)
+#    define mem_af(x,f,l) malloc (x)
 #else
-#    define mem_a(x)    util_memory_a((x), __LINE__, __FILE__)
-#    define mem_d(x)    util_memory_d((void*)(x))
-#    define mem_r(x, n) util_memory_r((void*)(x), (n), __LINE__, __FILE__)
+#    define mem_a(x)      util_memory_a((x), __LINE__, __FILE__)
+#    define mem_d(x)      util_memory_d((void*)(x))
+#    define mem_r(x, n)   util_memory_r((void*)(x), (n), __LINE__, __FILE__)
+#    define mem_af(x,f,l) util_memory_a((x), __LINE__, __FILE__)
 #endif /*! NOTRACK */
 
+#define util_strdup(X)  _util_Estrdup((X), __FILE__, __LINE__)
+#define util_strdupe(X) _util_Estrdup_empty((X), __FILE__, __LINE__)
+
 /*
  * A flexible vector implementation: all vector pointers contain some
  * data about themselfs exactly - sizeof(vector_t) behind the pointer
  * this data is represented in the structure below.  Doing this allows
  * us to use the array [] to access individual elements from the vector
  * opposed to using set/get methods.
- */     
+ */
 typedef struct {
     size_t  allocated;
     size_t  used;
@@ -374,14 +380,6 @@ typedef struct hash_table_t {
     struct hash_node_t **table;
 } hash_table_t, *ht;
 
-typedef struct hash_set_t {
-    size_t  bits;
-    size_t  mask;
-    size_t  capacity;
-    size_t *items;
-    size_t  total;
-} hash_set_t, *hs;
-
 /*
  * hashtable implementation:
  *
@@ -413,51 +411,16 @@ typedef struct hash_set_t {
  * util_htdel(foo);
  */
 hash_table_t *util_htnew (size_t size);
+void          util_htrem (hash_table_t *ht, void (*callback)(void *data));
 void          util_htset (hash_table_t *ht, const char *key, void *value);
 void          util_htdel (hash_table_t *ht);
 size_t        util_hthash(hash_table_t *ht, const char *key);
 void          util_htseth(hash_table_t *ht, const char *key, size_t hash, void *value);
+void          util_htrmh (hash_table_t *ht, const char *key, size_t bin, void (*cb)(void*));
+void          util_htrm  (hash_table_t *ht, const char *key, void (*cb)(void*));
 
 void         *util_htget (hash_table_t *ht, const char *key);
 void         *util_htgeth(hash_table_t *ht, const char *key, size_t hash);
-
-/*
- * hashset implementation:
- *      This was designed for pointers:  you manage the life of the object yourself
- *      if you do use this for non-pointers please be warned that the object may not
- *      be valid if the duration of it exceeds (i.e on stack).  So you need to allocate
- *      yourself, or put those in global scope to ensure duration is for the whole
- *      runtime.
- *
- * util_hsnew()                             -- to make a new hashset
- * util_hsadd(set, key)                     -- to add something in the set
- * util_hshas(set, key)                     -- to check if something is in the set
- * util_hsrem(set, key)                     -- to remove something in the set
- * util_hsdel(set)                          -- to delete the set
- *
- * example of use:
- * 
- * hs    foo = util_hsnew();
- * char *bar = "hello blub\n";
- * char *baz = "hello dale\n";
- *
- * util_hsadd(foo, bar);
- * util_hsadd(foo, baz);
- * util_hsrem(foo, baz);
- *
- * printf("bar %d | baz %d\n",
- *     util_hshas(foo, bar),
- *     util_hshad(foo, baz)
- * );
- *
- * util_hsdel(foo);  
- */
-
-hash_set_t *util_hsnew(void);
-int         util_hsadd(hash_set_t *, void *);
-int         util_hshas(hash_set_t *, void *);
-int         util_hsrem(hash_set_t *, void *);
-void        util_hsdel(hash_set_t *);
  
 /*===================================================================*/
 /*============================ file.c ===============================*/
@@ -466,10 +429,8 @@ void        util_hsdel(hash_set_t *);
 void           fs_file_close  (FILE *);
 int            fs_file_error  (FILE *);
 int            fs_file_getc   (FILE *);
-int            fs_file_flush  (FILE *);
 int            fs_file_printf (FILE *, const char *, ...);
 int            fs_file_puts   (FILE *, const char *);
-int            fs_file_putc   (FILE *, int);
 int            fs_file_seek   (FILE *, long int, int);
 long int       fs_file_tell   (FILE *); 
 
@@ -480,11 +441,10 @@ FILE          *fs_file_open   (const char *, const char *);
 int            fs_file_getline(char  **, size_t *, FILE *);
 
 /* directory handling */
+int            fs_dir_make    (const char *);
 DIR           *fs_dir_open    (const char *);
 int            fs_dir_close   (DIR *);
 struct dirent *fs_dir_read    (DIR *);
-int            fs_dir_make    (const char *);
-int            fs_dir_change  (const char *);
 
 
 /*===================================================================*/
@@ -1025,17 +985,20 @@ qcint             prog_tempstring(qc_program *prog, const char *_str);
 /*===================================================================*/
 /*===================== parser.c commandline ========================*/
 /*===================================================================*/
+struct parser_s;
 
-bool parser_init          ();
-bool parser_compile_file  (const char *);
-bool parser_compile_string(const char *, const char *, size_t);
-bool parser_finish        (const char *);
-void parser_cleanup       ();
+struct parser_s *parser_create        ();
+bool             parser_compile_file  (struct parser_s *parser, const char *);
+bool             parser_compile_string(struct parser_s *parser, const char *, const char *, size_t);
+bool             parser_finish        (struct parser_s *parser, const char *);
+void             parser_cleanup       (struct parser_s *parser);
 
 /*===================================================================*/
 /*====================== ftepp.c commandline ========================*/
 /*===================================================================*/
 struct lex_file_s;
+struct ftepp_s;
+
 typedef struct {
     const char  *name;
     char      *(*func)(struct lex_file_s *);
@@ -1047,14 +1010,14 @@ typedef struct {
  */
 #define FTEPP_PREDEF_COUNT 8
 
-bool        ftepp_init             ();
-bool        ftepp_preprocess_file  (const char *filename);
-bool        ftepp_preprocess_string(const char *name, const char *str);
-void        ftepp_finish           ();
-const char *ftepp_get              ();
-void        ftepp_flush            ();
-void        ftepp_add_define       (const char *source, const char *name);
-void        ftepp_add_macro        (const char *name,   const char *value);
+struct ftepp_s *ftepp_create           ();
+bool            ftepp_preprocess_file  (struct ftepp_s *ftepp, const char *filename);
+bool            ftepp_preprocess_string(struct ftepp_s *ftepp, const char *name, const char *str);
+void            ftepp_finish           (struct ftepp_s *ftepp);
+const char     *ftepp_get              (struct ftepp_s *ftepp);
+void            ftepp_flush            (struct ftepp_s *ftepp);
+void            ftepp_add_define       (struct ftepp_s *ftepp, const char *source, const char *name);
+void            ftepp_add_macro        (struct ftepp_s *ftepp, const char *name,   const char *value);
 
 extern const ftepp_predef_t ftepp_predefs[FTEPP_PREDEF_COUNT];
 
diff --git a/intrin.h b/intrin.h
new file mode 100644 (file)
index 0000000..69490dc
--- /dev/null
+++ b/intrin.h
@@ -0,0 +1,418 @@
+/*
+ * Copyright (C) 2012, 2013
+ *     Dale Weiler
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of
+ * this software and associated documentation files (the "Software"), to deal in
+ * the Software without restriction, including without limitation the rights to
+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+/*
+ * Provides all the "intrinsics" / "builtins" for GMQCC. These can do
+ * a few things, they can provide fall back implementations for math
+ * functions if the definitions don't exist for some given engine. Or
+ * then can determine definitions for existing builtins, and simply
+ * wrap back to them instead.  This is like a "portable" intrface that
+ * is entered when -fintrin is used (causing all existing builtins to
+ * be ignored by the compiler and instead interface through here.
+ */
+typedef struct {
+    ast_expression *(*intrin)(parser_t *);
+    const char       *name;
+    const char       *alias;
+} intrin_t;
+
+ht intrin_intrinsics() {
+    static ht intrinsics = NULL;
+    if (!intrinsics)
+        intrinsics = util_htnew(PARSER_HT_SIZE);
+
+    return intrinsics;
+}
+
+#define INTRIN_VAL(VALUE, NAME, FUNC, STYPE, VTYPE)                   \
+    do {                                                              \
+        (VALUE) = ast_value_new (                                     \
+            parser_ctx(parser),                                       \
+            "__builtin_" NAME,                                        \
+            TYPE_FUNCTION                                             \
+        );                                                            \
+        (VALUE)->expression.next = (ast_expression*)ast_value_new (   \
+            parser_ctx(parser),                                       \
+            STYPE,                                                    \
+            VTYPE                                                     \
+        );                                                            \
+        (FUNC) = ast_function_new (                                   \
+            parser_ctx(parser),                                       \
+            "__builtin_" NAME,                                        \
+            (VALUE)                                                   \
+        );                                                            \
+    } while (0)
+
+#define INTRIN_REG(FUNC, VALUE)                                       \
+    do {                                                              \
+        vec_push(parser->functions, (FUNC));                          \
+        vec_push(parser->globals,   (ast_expression*)(VALUE));        \
+    } while (0)
+
+
+ast_expression *intrin_func (parser_t *parser, const char *name);
+
+#define QC_M_E 2.71828182845905
+
+ast_expression *intrin_pow(parser_t *parser) {
+    /*
+     * float pow(float x, float y) {
+     *   float local = 1.0f;
+     *   while (y > 0) {
+     *     while (!(y & 1)) {
+     *       y >>= 2;
+     *       x *=  x;
+     *     }
+     *     y--;
+     *     local *= x;
+     *   }
+     *   return local;
+     * } 
+     */
+    static ast_value *value = NULL;
+
+    if (!value) {
+        ast_value    *arg1  = ast_value_new(parser_ctx(parser), "x",     TYPE_FLOAT);
+        ast_value    *arg2  = ast_value_new(parser_ctx(parser), "y",     TYPE_FLOAT);
+        ast_value    *local = ast_value_new(parser_ctx(parser), "local", TYPE_FLOAT);
+        ast_block    *body  = ast_block_new(parser_ctx(parser));
+        ast_block    *l1b   = ast_block_new(parser_ctx(parser)); /* loop 1 body */
+        ast_block    *l2b   = ast_block_new(parser_ctx(parser)); /* looo 2 body */
+        ast_loop     *loop1 = NULL;
+        ast_loop     *loop2 = NULL;
+        ast_function *func  = NULL;
+
+        INTRIN_VAL(value, "pow", func, "<float>", TYPE_FLOAT);
+
+        /* arguments */
+        vec_push(value->expression.params, arg1);
+        vec_push(value->expression.params, arg2);
+
+        /* local */
+        vec_push(body->locals, local);
+
+        /* assignment to local of value 1.0f */
+        vec_push(body->exprs,
+            (ast_expression*)ast_store_new (
+                parser_ctx(parser),
+                INSTR_STORE_F,
+                (ast_expression*)local,
+                (ast_expression*)parser_const_float_1(parser)
+            )
+        );
+
+        /* y >>= 2 */
+        vec_push(l2b->exprs,
+            (ast_expression*)ast_binstore_new (
+                parser_ctx(parser),
+                INSTR_STORE_F,
+                INSTR_MUL_F,
+                (ast_expression*)arg2,
+                (ast_expression*)parser_const_float(parser, 0.25f)
+            )
+        );
+
+        /* x *= x */
+        vec_push(l2b->exprs,
+            (ast_expression*)ast_binstore_new (
+                parser_ctx(parser),
+                INSTR_STORE_F,
+                INSTR_MUL_F,
+                (ast_expression*)arg1,
+                (ast_expression*)arg1
+            )
+        );
+
+        /* while (!(y&1)) */
+        loop2 = ast_loop_new (
+            parser_ctx(parser),
+            NULL,
+            (ast_expression*)ast_binary_new (
+                parser_ctx(parser),
+                INSTR_AND,
+                (ast_expression*)arg2,
+                (ast_expression*)parser_const_float_1(parser)
+            ),
+            true, /* ! not */
+            NULL,
+            false,
+            NULL,
+            (ast_expression*)l2b
+        );
+
+        /* push nested loop into loop expressions */
+        vec_push(l1b->exprs, (ast_expression*)loop2);
+
+        /* y-- */
+        vec_push(l1b->exprs,
+            (ast_expression*)ast_binstore_new (
+                parser_ctx(parser),
+                INSTR_STORE_F,
+                INSTR_SUB_F,
+                (ast_expression*)arg2,
+                (ast_expression*)parser_const_float_1(parser)
+            )
+        );
+        /* local *= x */
+        vec_push(l1b->exprs,
+            (ast_expression*)ast_binstore_new (
+                parser_ctx(parser),
+                INSTR_STORE_F,
+                INSTR_MUL_F,
+                (ast_expression*)local,
+                (ast_expression*)arg1
+            )
+        );
+
+        /* while (y > 0) */
+        loop1 = ast_loop_new (
+            parser_ctx(parser),
+            NULL,
+            (ast_expression*)ast_binary_new (
+                parser_ctx(parser),
+                INSTR_GT,
+                (ast_expression*)arg2,
+                (ast_expression*)parser_const_float_0(parser)
+            ),
+            false,
+            NULL,
+            false,
+            NULL,
+            (ast_expression*)l1b
+        );
+
+        /* push the loop1 into the body for the function */
+        vec_push(body->exprs, (ast_expression*)loop1);
+
+        /* return local; */
+        vec_push(body->exprs,
+            (ast_expression*)ast_return_new (
+                parser_ctx(parser),
+                (ast_expression*)local
+            )
+        );
+
+        /* push block and register intrin for codegen */
+        vec_push(func->blocks, body);
+
+        INTRIN_REG(func, value);
+    }
+
+    return (ast_expression*)value;
+}
+
+ast_expression *intrin_mod(parser_t *parser) {
+    /*
+     * float mod(float x, float y) {
+     *   return x - y * floor(x / y);
+     * }
+     */    
+    static ast_value *value = NULL;
+
+    if (!value) {
+        ast_call     *call  = ast_call_new (parser_ctx(parser), intrin_func(parser, "floor"));
+        ast_value    *arg1  = ast_value_new(parser_ctx(parser), "x", TYPE_FLOAT);
+        ast_value    *arg2  = ast_value_new(parser_ctx(parser), "y", TYPE_FLOAT);
+        ast_block    *body  = ast_block_new(parser_ctx(parser));
+        ast_function *func  = NULL;
+
+        INTRIN_VAL(value, "mod", func, "<float>", TYPE_FLOAT);
+
+        /* floor(x/y) */
+        vec_push(call->params,
+            (ast_expression*)ast_binary_new (
+                parser_ctx(parser),
+                INSTR_DIV_F,
+                (ast_expression*)arg1,
+                (ast_expression*)arg2
+            )
+        );
+
+        vec_push(body->exprs,
+            (ast_expression*)ast_return_new(
+                parser_ctx(parser),
+                (ast_expression*)ast_binary_new(
+                    parser_ctx(parser),
+                    INSTR_SUB_F,
+                    (ast_expression*)arg1,
+                    (ast_expression*)ast_binary_new(
+                        parser_ctx(parser),
+                        INSTR_MUL_F,
+                        (ast_expression*)arg2,
+                        (ast_expression*)call
+                    )
+                )
+            )
+        );
+
+        vec_push(value->expression.params, arg1); /* float x (for param) */
+        vec_push(value->expression.params, arg2); /* float y (for param) */
+
+        vec_push(func->blocks,            body); /* {{{ body }}} */
+
+        INTRIN_REG(func, value);
+    }
+
+    return (ast_expression*)value;
+}
+
+ast_expression *intrin_exp(parser_t *parser) {
+    /*
+     * float exp(float x) {
+     *     return pow(QC_M_E, x);
+     * }
+     */
+    static ast_value *value = NULL;
+
+    if (!value) {
+        ast_call     *call = ast_call_new    (parser_ctx(parser), intrin_func(parser, "pow"));
+        ast_value    *arg1 = ast_value_new   (parser_ctx(parser), "x", TYPE_FLOAT);
+        ast_block    *body = ast_block_new   (parser_ctx(parser));
+        ast_function *func = NULL;
+
+        INTRIN_VAL(value, "exp", func, "<float>", TYPE_FLOAT);
+
+        /* push arguments for params to call */
+        vec_push(call->params, (ast_expression*)parser_const_float(parser, QC_M_E));
+        vec_push(call->params, (ast_expression*)arg1);
+
+        /* return pow(QC_M_E, x) */
+        vec_push(body->exprs,
+            (ast_expression*)ast_return_new(
+                parser_ctx(parser),
+                (ast_expression*)call
+            )
+        );
+
+        vec_push(value->expression.params, arg1); /* float x (for param) */
+
+        vec_push(func->blocks,             body); /* {{{ body }}} */
+
+        INTRIN_REG(func, value);
+    }
+
+    return (ast_expression*)value;
+}
+
+ast_expression *intrin_isnan(parser_t *parser) {
+    /*
+     * float isnan(float x) {
+     *   float local;
+     *   local = x;
+     *
+     *   return (x != local);
+     * } 
+     */      
+    static ast_value *value = NULL;
+
+    if (!value) {
+        ast_value    *arg1   = ast_value_new (parser_ctx(parser), "x", TYPE_FLOAT);
+        ast_value    *local  = ast_value_new (parser_ctx(parser), "local", TYPE_FLOAT);
+        ast_block    *body   = ast_block_new (parser_ctx(parser));
+        ast_function *func   = NULL;
+    
+        INTRIN_VAL(value, "isnan", func, "<float>", TYPE_FLOAT);
+
+        vec_push(body->locals, local);
+        vec_push(body->exprs,
+            (ast_expression*)ast_store_new(
+                parser_ctx(parser),
+                INSTR_STORE_F,
+                (ast_expression*)local,
+                (ast_expression*)arg1
+            )
+        );
+
+        vec_push(body->exprs,
+            (ast_expression*)ast_return_new(
+                parser_ctx(parser),
+                (ast_expression*)ast_binary_new(
+                    parser_ctx(parser),
+                    INSTR_NE_F,
+                    (ast_expression*)arg1,
+                    (ast_expression*)local
+                )
+            )
+        );
+
+        vec_push(value->expression.params, arg1);
+
+        vec_push(func->blocks, body);
+
+        INTRIN_REG(func, value);
+    }
+
+    return (ast_expression*)value;
+}
+
+static intrin_t intrinsics[] = {
+    {&intrin_exp,   "__builtin_exp",   "exp"},
+    {&intrin_mod,   "__builtin_mod",   "mod"},
+    {&intrin_pow,   "__builtin_pow",   "pow"},
+    {&intrin_isnan, "__builtin_isnan", "isnan"}
+};
+
+void intrin_intrinsics_destroy(parser_t *parser) {
+    /*size_t i;*/
+    (void)parser;
+    util_htdel(intrin_intrinsics());
+#if 0
+    for (i = 0; i < sizeof(intrinsics)/sizeof(intrin_t); i++)
+        ast_value_delete( (ast_value*) intrinsics[i].intrin(parser));
+#endif
+}
+
+
+ast_expression *intrin_func(parser_t *parser, const char *name) {
+    static bool  init = false;
+    size_t       i    = 0;
+    void        *find;
+
+    /* register the intrinsics in the hashtable for O(1) lookup */
+    if (!init) {
+        for (i = 0; i < sizeof(intrinsics)/sizeof(*intrinsics); i++)
+            util_htset(intrin_intrinsics(), intrinsics[i].alias, &intrinsics[i]);
+
+        init = true; /* only once */
+    }
+
+    /*
+     * jesus fucking christ, Blub design something less fucking
+     * impossible to use, like a ast_is_builtin(ast_expression *), also
+     * use a hashtable :P 
+     */  
+    if ((find = (void*)parser_find_global(parser, name)) && ((ast_value*)find)->expression.vtype == TYPE_FUNCTION)
+        for (i = 0; i < vec_size(parser->functions); ++i)
+            if (((ast_value*)find)->name && !strcmp(parser->functions[i]->name, ((ast_value*)find)->name) && parser->functions[i]->builtin < 0)
+                return find;
+
+    if ((find = util_htget(intrin_intrinsics(), name))) {
+        /* intrinsic is in table. This will "generate the function" so
+         * to speak (if it's not already generated).
+         */  
+        return ((intrin_t*)find)->intrin(parser);
+    }
+
+    parseerror(parser, "need function: `%s` compiler depends on it", name);
+    return NULL;
+}
diff --git a/ir.c b/ir.c
index a28b07e95f3452d5b68999a1005799fbd41480c9..c61459499bedc3810430b77b0387343dbee7b997 100644 (file)
--- a/ir.c
+++ b/ir.c
@@ -1206,22 +1206,11 @@ bool ir_value_set_field(ir_value *self, ir_value *fld)
     return true;
 }
 
-static char *ir_strdup(const char *str)
-{
-    if (str && !*str) {
-        /* actually dup empty strings */
-        char *out = (char*)mem_a(1);
-        *out = 0;
-        return out;
-    }
-    return util_strdup(str);
-}
-
 bool ir_value_set_string(ir_value *self, const char *str)
 {
     if (self->vtype != TYPE_STRING)
         return false;
-    self->constval.vstring = ir_strdup(str);
+    self->constval.vstring = util_strdupe(str);
     self->hasvalue = true;
     return true;
 }
@@ -1651,7 +1640,7 @@ void ir_phi_add(ir_instr* self, ir_block *b, ir_value *v)
          * is doing something wrong.
          */
         irerror(self->context, "Invalid entry block for PHI");
-        abort();
+        exit(EXIT_FAILURE);
     }
 
     pe.value = v;
@@ -3292,6 +3281,8 @@ static void gen_vector_defs(prog_section_def def, const char *name)
         def.offset++;
         component[len-1]++;
     }
+
+    mem_d(component);
 }
 
 static void gen_vector_fields(prog_section_field fld, const char *name)
@@ -3320,6 +3311,8 @@ static void gen_vector_fields(prog_section_field fld, const char *name)
         fld.offset++;
         component[len-1]++;
     }
+
+    mem_d(component);
 }
 
 static bool ir_builder_gen_global(ir_builder *self, ir_value *global, bool islocal)
diff --git a/lexer.c b/lexer.c
index 5f23952615ce6c1aa8d3f89607aa1c75821d7c11..7f5736dc21f0c5dceb758336cc44a0e9293bbf62 100644 (file)
--- a/lexer.c
+++ b/lexer.c
@@ -272,7 +272,7 @@ void lex_close(lex_file *lex)
 static int lex_fgetc(lex_file *lex)
 {
     if (lex->file)
-        return fgetc(lex->file);
+        return fs_file_getc(lex->file);
     if (lex->open_string) {
         if (lex->open_string_pos >= lex->open_string_length)
             return EOF;
@@ -483,6 +483,9 @@ static bool lex_try_pragma(lex_file *lex)
     lex->line = line;
     while (ch != '\n' && ch != EOF)
         ch = lex_getch(lex);
+    vec_free(command);
+    vec_free(param);
+    vec_free(pragma);
     return true;
 
 unroll:
@@ -495,13 +498,13 @@ unroll:
         vec_free(command);
         lex_ungetch(lex, ' ');
     }
-    if (command) {
-        vec_pop(command);
-        while (vec_size(command)) {
-            lex_ungetch(lex, (unsigned char)vec_last(command));
-            vec_pop(command);
+    if (param) {
+        vec_pop(param);
+        while (vec_size(param)) {
+            lex_ungetch(lex, (unsigned char)vec_last(param));
+            vec_pop(param);
         }
-        vec_free(command);
+        vec_free(param);
         lex_ungetch(lex, ' ');
     }
     if (pragma) {
@@ -1232,7 +1235,7 @@ int lex_do(lex_file *lex)
             /*
             case '+':
             case '-':
-            */
+            */ 
             case '*':
             case '/':
             case '<':
@@ -1352,7 +1355,7 @@ int lex_do(lex_file *lex)
         lex_tokench(lex, ch);
 
         nextch = lex_getch(lex);
-        if (nextch == '=') {
+        if (nextch == '=' || nextch == '*') {
             lex_tokench(lex, nextch);
         } else
             lex_ungetch(lex, nextch);
@@ -1361,6 +1364,12 @@ int lex_do(lex_file *lex)
         return (lex->tok.ttype = TOKEN_OPERATOR);
     }
 
+    if (ch == '%') {
+        lex_tokench(lex, ch);
+        lex_endtoken(lex);
+        return (lex->tok.ttype = TOKEN_OPERATOR);
+    }
+
     if (isident_start(ch))
     {
         const char *v;
diff --git a/lexer.h b/lexer.h
index 9724a7b90d97c386e1de013a97e08d654e05d280..9f080dcb139fbd7ba3f6fbbf1e7d7e8031dd702e 100644 (file)
--- a/lexer.h
+++ b/lexer.h
@@ -166,18 +166,21 @@ typedef struct {
 static const oper_info c_operators[] = {
     { "(",   0, opid1('('),         ASSOC_LEFT,  99, OP_PREFIX}, /* paren expression - non function call */
 
-    { "++",  1, opid3('S','+','+'), ASSOC_LEFT,  15, OP_SUFFIX},
-    { "--",  1, opid3('S','-','-'), ASSOC_LEFT,  15, OP_SUFFIX},
-    { ".",   2, opid1('.'),         ASSOC_LEFT,  15, 0 },
-    { "(",   0, opid1('('),         ASSOC_LEFT,  15, 0 }, /* function call */
-    { "[",   2, opid1('['),         ASSOC_LEFT,  15, 0 }, /* array subscript */
+    { "++",  1, opid3('S','+','+'), ASSOC_LEFT,  17, OP_SUFFIX},
+    { "--",  1, opid3('S','-','-'), ASSOC_LEFT,  17, OP_SUFFIX},
+    { ".",   2, opid1('.'),         ASSOC_LEFT,  17, 0 },
+    { "(",   0, opid1('('),         ASSOC_LEFT,  17, 0 }, /* function call */
+    { "[",   2, opid1('['),         ASSOC_LEFT,  17, 0 }, /* array subscript */
+
+    { "++",  1, opid3('+','+','P'), ASSOC_RIGHT, 16, OP_PREFIX },
+    { "--",  1, opid3('-','-','P'), ASSOC_RIGHT, 16, OP_PREFIX },
+
+    { "**",  2, opid2('*', '*'),    ASSOC_RIGHT, 15, 0 },
 
     { "!",   1, opid2('!', 'P'),    ASSOC_RIGHT, 14, OP_PREFIX },
     { "~",   1, opid2('~', 'P'),    ASSOC_RIGHT, 14, OP_PREFIX },
     { "+",   1, opid2('+','P'),     ASSOC_RIGHT, 14, OP_PREFIX },
     { "-",   1, opid2('-','P'),     ASSOC_RIGHT, 14, OP_PREFIX },
-    { "++",  1, opid3('+','+','P'), ASSOC_RIGHT, 14, OP_PREFIX },
-    { "--",  1, opid3('-','-','P'), ASSOC_RIGHT, 14, OP_PREFIX },
 /*  { "&",   1, opid2('&','P'),     ASSOC_RIGHT, 14, OP_PREFIX }, */
 
     { "*",   2, opid1('*'),         ASSOC_LEFT,  13, 0 },
diff --git a/main.c b/main.c
index e801aaff7ea7d840a658e504273c4b0b99ed2f81..0a22b1bada1716cdf55a6d389f71dd9294a27701 100644 (file)
--- a/main.c
+++ b/main.c
@@ -146,9 +146,10 @@ static bool options_parse(int argc, char **argv) {
     bool argend = false;
     size_t itr;
     char  buffer[1024];
-    char *redirout = NULL;
-    char *redirerr = NULL;
-    char *config   = NULL;
+    char *redirout    = NULL;
+    char *redirerr    = NULL;
+    char *config      = NULL;
+    char *memdumpcols = NULL;
 
     while (!argend && argc > 1) {
         char *argarg;
@@ -223,6 +224,10 @@ static bool options_parse(int argc, char **argv) {
                 config = argarg;
                 continue;
             }
+            if (options_long_gcc("memdumpcols", &argc, &argv, &memdumpcols)) {
+                OPTS_OPTION_U16(OPTION_MEMDUMPCOLS) = (uint16_t)strtol(memdumpcols, NULL, 10);
+                continue;
+            }
 
             /* show defaults (like pathscale) */
             if (!strcmp(argv[0]+1, "show-defaults")) {
@@ -403,7 +408,7 @@ static bool options_parse(int argc, char **argv) {
                         return false;
                     }
                     if (isdigit(argarg[0])) {
-                        uint32_t val = atoi(argarg);
+                        uint32_t val = (uint32_t)strtol(argarg, NULL, 10);
                         OPTS_OPTION_U32(OPTION_O) = val;
                         opts_setoptimlevel(val);
                     } else {
@@ -541,12 +546,14 @@ 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;
+    size_t          itr;
+    int             retval           = 0;
+    bool            opts_output_free = false;
+    bool            operators_free   = false;
+    bool            progs_src        = false;
+    FILE            *outfile         = NULL;
+    struct parser_s *parser          = NULL;
+    struct ftepp_s  *ftepp           = NULL;
 
     app_name = argv[0];
     con_init ();
@@ -621,7 +628,7 @@ int main(int argc, char **argv) {
     }
 
     if (!OPTS_OPTION_BOOL(OPTION_PP_ONLY)) {
-        if (!parser_init()) {
+        if (!(parser = parser_create())) {
             con_err("failed to initialize parser\n");
             retval = 1;
             goto cleanup;
@@ -629,7 +636,7 @@ int main(int argc, char **argv) {
     }
 
     if (OPTS_OPTION_BOOL(OPTION_PP_ONLY) || OPTS_FLAG(FTEPP)) {
-        if (!ftepp_init()) {
+        if (!(ftepp = ftepp_create())) {
             con_err("failed to initialize parser\n");
             retval = 1;
             goto cleanup;
@@ -644,7 +651,7 @@ int main(int argc, char **argv) {
     /* add macros */
     if (OPTS_OPTION_BOOL(OPTION_PP_ONLY) || OPTS_FLAG(FTEPP)) {
         for (itr = 0; itr < vec_size(ppems); itr++) {
-            ftepp_add_macro(ppems[itr].name, ppems[itr].value);
+            ftepp_add_macro(ftepp, ppems[itr].name, ppems[itr].value);
             mem_d(ppems[itr].name);
 
             /* can be null */
@@ -703,6 +710,7 @@ srcdone:
             con_out("Mode: %s\n", (progs_src ? "progs.src" : "manual"));
             con_out("There are %lu items to compile:\n", (unsigned long)vec_size(items));
         }
+
         for (itr = 0; itr < vec_size(items); ++itr) {
             if (!OPTS_OPTION_BOOL(OPTION_QUIET) &&
                 !OPTS_OPTION_BOOL(OPTION_PP_ONLY))
@@ -717,33 +725,33 @@ srcdone:
 
             if (OPTS_OPTION_BOOL(OPTION_PP_ONLY)) {
                 const char *out;
-                if (!ftepp_preprocess_file(items[itr].filename)) {
+                if (!ftepp_preprocess_file(ftepp, items[itr].filename)) {
                     retval = 1;
                     goto cleanup;
                 }
-                out = ftepp_get();
+                out = ftepp_get(ftepp);
                 if (out)
                     fs_file_printf(outfile, "%s", out);
-                ftepp_flush();
+                ftepp_flush(ftepp);
             }
             else {
                 if (OPTS_FLAG(FTEPP)) {
                     const char *data;
-                    if (!ftepp_preprocess_file(items[itr].filename)) {
+                    if (!ftepp_preprocess_file(ftepp, items[itr].filename)) {
                         retval = 1;
                         goto cleanup;
                     }
-                    data = ftepp_get();
+                    data = ftepp_get(ftepp);
                     if (vec_size(data)) {
-                        if (!parser_compile_string(items[itr].filename, data, vec_size(data))) {
+                        if (!parser_compile_string(parser, items[itr].filename, data, vec_size(data))) {
                             retval = 1;
                             goto cleanup;
                         }
                     }
-                    ftepp_flush();
+                    ftepp_flush(ftepp);
                 }
                 else {
-                    if (!parser_compile_file(items[itr].filename)) {
+                    if (!parser_compile_file(parser, items[itr].filename)) {
                         retval = 1;
                         goto cleanup;
                     }
@@ -756,9 +764,10 @@ srcdone:
             }
         }
 
-        ftepp_finish();
+        ftepp_finish(ftepp);
+        ftepp = NULL;
         if (!OPTS_OPTION_BOOL(OPTION_PP_ONLY)) {
-            if (!parser_finish(OPTS_OPTION_STR(OPTION_OUTPUT))) {
+            if (!parser_finish(parser, OPTS_OPTION_STR(OPTION_OUTPUT))) {
                 retval = 1;
                 goto cleanup;
             }
@@ -778,13 +787,14 @@ srcdone:
 
 cleanup:
     util_debug("COM", "cleaning ...\n");
-    ftepp_finish();
+    if (ftepp)
+        ftepp_finish(ftepp);
     con_close();
     vec_free(items);
     vec_free(ppems);
 
     if (!OPTS_OPTION_BOOL(OPTION_PP_ONLY))
-        parser_cleanup();
+        parser_cleanup(parser);
     if (opts_output_free)
         mem_d(OPTS_OPTION_STR(OPTION_OUTPUT));
     if (operators_free)
diff --git a/opts.c b/opts.c
index 4b765683099c56e58cfc5979189c61e0ec9db25b..e37ba7356d78ad6bdace480b77113c1d75863b25 100644 (file)
--- a/opts.c
+++ b/opts.c
@@ -64,6 +64,7 @@ static void opts_setdefault() {
     opts_set(opts.flags, BAIL_ON_WERROR,                 true);
     opts_set(opts.flags, LEGACY_VECTOR_MATHS,            true);
     opts_set(opts.flags, DARKPLACES_STRING_TABLE_BUG,    true);
+
 }
 
 void opts_backup_non_Wall() {
@@ -96,6 +97,7 @@ void opts_init(const char *output, int standard, size_t arraysize) {
     OPTS_OPTION_STR(OPTION_OUTPUT)         = (char*)output;
     OPTS_OPTION_U32(OPTION_STANDARD)       = standard;
     OPTS_OPTION_U32(OPTION_MAX_ARRAY_SIZE) = arraysize;
+    OPTS_OPTION_U16(OPTION_MEMDUMPCOLS)    = 16;
 }
 
 static bool opts_setflag_all(const char *name, bool on, uint32_t *flags, const opts_flag_def *list, size_t listsize) {
@@ -257,7 +259,7 @@ static size_t opts_ini_parse (
 static bool opts_ini_bool(const char *value) {
     if (!strcmp(value, "true"))  return true;
     if (!strcmp(value, "false")) return false;
-    return !!atoi(value);
+    return !!strtol(value, NULL, 10);
 }
 
 static char *opts_ini_load(const char *section, const char *name, const char *value) {
index 90e6fce7b6360cfeaf14e9dea6e103d7be9ac423..f7b5cf115ea90ee143bb713bd99d9920f1fefcab 100644 (file)
--- a/opts.def
+++ b/opts.def
     GMQCC_DEFINE_FLAG(G)
     GMQCC_DEFINE_FLAG(STANDARD)
     GMQCC_DEFINE_FLAG(DEBUG)
+    GMQCC_DEFINE_FLAG(MEMDUMPCOLS)
     GMQCC_DEFINE_FLAG(MEMCHK)
     GMQCC_DEFINE_FLAG(DUMPFIN)
     GMQCC_DEFINE_FLAG(DUMP)
diff --git a/pak.c b/pak.c
index 043ef8957a53ffed4817fda9bb5d3f9a8e28fc14..a9ef112869066920a06b46608926fc3cdf47f823 100644 (file)
--- a/pak.c
+++ b/pak.c
@@ -259,9 +259,10 @@ bool pak_exists(pak_file_t *pak, const char *file, pak_directory_t **dir) {
 /*
  * Extraction abilities.  These work as you expect them to.
  */ 
-bool pak_extract_one(pak_file_t *pak, const char *file) {
-    pak_directory_t *dir = NULL;
-    unsigned char   *dat = NULL;
+bool pak_extract_one(pak_file_t *pak, const char *file, const char *outdir) {
+    pak_directory_t *dir   = NULL;
+    unsigned char   *dat   = NULL;
+    char            *local = NULL;
     FILE            *out;
 
     if (!pak_exists(pak, file, &dir)) {
@@ -278,15 +279,20 @@ bool pak_extract_one(pak_file_t *pak, const char *file) {
      */   
     pak_tree_build(file);
 
+    /* TODO portable path seperators */
+    util_asprintf(&local, "%s/%s", outdir, file);
+
     /*
      * Now create the file, if this operation fails.  Then abort
      * It shouldn't fail though.
      */   
-    if (!(out = fs_file_open(file, "wb"))) {
+    if (!(out = fs_file_open(local, "wb"))) {
         mem_d(dat);
         return false;
     }
 
+    /* free memory for directory string */
+    mem_d(local);
 
     /* read */
     fs_file_seek (pak->handle, dir->pos, SEEK_SET);
@@ -310,11 +316,8 @@ bool pak_extract_all(pak_file_t *pak, const char *dir) {
     if (!fs_dir_make(dir))
         return false;
 
-    if (fs_dir_change(dir))
-        return false;
-
     for (itr = 0; itr < vec_size(pak->directories); itr++) {
-        if (!pak_extract_one(pak, pak->directories[itr].name))
+        if (!pak_extract_one(pak, pak->directories[itr].name, dir))
             return false;
     }
 
@@ -361,7 +364,7 @@ bool pak_insert_one(pak_file_t *pak, const char *file) {
         return false;
     }
 
-    strcpy(dir.name, file);
+    strncpy(dir.name, file, strlen(file));
 
     /*
      * Allocate some memory for loading in the data that will be
@@ -477,7 +480,6 @@ int main(int argc, char **argv) {
     bool          extract   = true;
     char         *redirout  = (char*)stdout;
     char         *redirerr  = (char*)stderr;
-    char         *directory = NULL;
     char         *file      = NULL;
     char        **files     = NULL;
     pak_file_t   *pak       = NULL;
@@ -498,8 +500,6 @@ int main(int argc, char **argv) {
                 continue;
             if (parsecmd("redirerr",  &argc, &argv, &redirerr,  1, false))
                 continue;
-            if (parsecmd("directory", &argc, &argv, &directory, 1, false))
-                continue;
             if (parsecmd("file",      &argc, &argv, &file,      1, false))
                 continue;
 
@@ -542,7 +542,7 @@ int main(int argc, char **argv) {
             return EXIT_FAILURE;
         }
 
-        if (!pak_extract_all(pak, (directory) ? directory : "./")) {
+        if (!pak_extract_all(pak, "./")) {
             con_err("failed to extract PAK %s (files may be missing)\n", file);
             pak_close(pak);
             vec_free(files);
@@ -562,13 +562,6 @@ int main(int argc, char **argv) {
         return EXIT_FAILURE;
     }
 
-    if (directory && !fs_dir_change(directory)) {
-        con_err("failed to change directory %s\n", directory);
-        pak_close(pak);
-        vec_free(files);
-        return EXIT_FAILURE;
-    }
-
     for (iter = 0; iter < vec_size(files); iter++) {
         if (!(pak_insert_one(pak, files[iter]))) {
             con_err("failed inserting %s for PAK %s\n", files[iter], file);
index 681207c192c01d55b74f565856ae537d78b6fdaa..c1d3fb1d786641ecd3611c3e2f66337b897d968c 100644 (file)
--- a/parser.c
+++ b/parser.c
@@ -23,6 +23,7 @@
  */
 #include <stdio.h>
 #include <stdarg.h>
+#include <math.h>
 
 #include "gmqcc.h"
 #include "lexer.h"
@@ -34,7 +35,7 @@
 #define PARSER_HT_SIZE    128
 #define TYPEDEF_HT_SIZE   16
 
-typedef struct {
+typedef struct parser_s {
     lex_file *lex;
     int      tok;
 
@@ -46,6 +47,8 @@ typedef struct {
     ast_value    **imm_vector;
     size_t         translated;
 
+    ht ht_imm_string;
+
     /* must be deleted first, they reference immediates and values */
     ast_value    **accessors;
 
@@ -252,12 +255,22 @@ static char *parser_strdup(const char *str)
 
 static ast_value* parser_const_string(parser_t *parser, const char *str, bool dotranslate)
 {
-    size_t i;
+    size_t hash = util_hthash(parser->ht_imm_string, str);
     ast_value *out;
+    if ( (out = util_htgeth(parser->ht_imm_string, str, hash)) ) {
+        if (dotranslate && out->name[0] == '#') {
+            char name[32];
+            snprintf(name, sizeof(name), "dotranslate_%lu", (unsigned long)(parser->translated++));
+            ast_value_set_name(out, name);
+        }
+        return out;
+    }
+    /*
     for (i = 0; i < vec_size(parser->imm_string); ++i) {
         if (!strcmp(parser->imm_string[i]->constval.vstring, str))
             return parser->imm_string[i];
     }
+    */
     if (dotranslate) {
         char name[32];
         snprintf(name, sizeof(name), "dotranslate_%lu", (unsigned long)(parser->translated++));
@@ -268,6 +281,7 @@ static ast_value* parser_const_string(parser_t *parser, const char *str, bool do
     out->hasvalue = true;
     out->constval.vstring = parser_strdup(str);
     vec_push(parser->imm_string, out);
+    util_htseth(parser->ht_imm_string, str, hash, out);
     return out;
 }
 
@@ -379,6 +393,9 @@ static ast_value* parser_find_typedef(parser_t *parser, const char *name, size_t
     return NULL;
 }
 
+/* include intrinsics */
+#include "intrin.h"
+
 typedef struct
 {
     size_t etype; /* 0 = expression, others are operators */
@@ -961,10 +978,35 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
                 return false;
             }
             break;
+
         case opid1('%'):
+            if (NotSameType(TYPE_FLOAT)) {
+                compile_error(ctx, "invalid types used in expression: cannot perform modulo operation between types %s and %s",
+                    type_name[exprs[0]->expression.vtype],
+                    type_name[exprs[1]->expression.vtype]);
+                return false;
+            }
+            if (CanConstFold(exprs[0], exprs[1])) {
+                out = (ast_expression*)parser_const_float(parser,
+                            (float)(((qcint)ConstF(0)) % ((qcint)ConstF(1))));
+            } else {
+                /* generate a call to __builtin_mod */
+                ast_expression *mod  = intrin_func(parser, "mod");
+                ast_call       *call = NULL;
+                if (!mod) return false; /* can return null for missing floor */
+
+                call = ast_call_new(parser_ctx(parser), mod);
+                vec_push(call->params, exprs[0]);
+                vec_push(call->params, exprs[1]);
+
+                out = (ast_expression*)call;
+            }
+            break;
+
         case opid2('%','='):
-            compile_error(ctx, "qc does not have a modulo operator");
+            compile_error(ctx, "%= is unimplemented");
             return false;
+
         case opid1('|'):
         case opid1('&'):
             if (NotSameType(TYPE_FLOAT)) {
@@ -1071,6 +1113,26 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
                 out = (ast_expression*)ast_ternary_new(ctx, exprs[0], exprs[1], exprs[2]);
             break;
 
+        case opid2('*', '*'):
+            if (NotSameType(TYPE_FLOAT)) {
+                ast_type_to_string(exprs[0], ty1, sizeof(ty1));
+                ast_type_to_string(exprs[1], ty2, sizeof(ty2));
+                compile_error(ctx, "invalid types used in exponentiation: %s and %s",
+                    ty1, ty2);
+
+                return false;
+            }
+
+            if (CanConstFold(exprs[0], exprs[1])) {
+                out = (ast_expression*)parser_const_float(parser, powf(ConstF(0), ConstF(1)));
+            } else {
+                ast_call *gencall = ast_call_new(parser_ctx(parser), intrin_func(parser, "pow"));
+                vec_push(gencall->params, exprs[0]);
+                vec_push(gencall->params, exprs[1]);
+                out = (ast_expression*)gencall;
+            }
+            break;
+
         case opid3('<','=','>'): /* -1, 0, or 1 */
             if (NotSameType(TYPE_FLOAT)) {
                 ast_type_to_string(exprs[0], ty1, sizeof(ty1));
@@ -1416,7 +1478,8 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
             if(CanConstFold1(exprs[0]))
                 out = (ast_expression*)parser_const_float(parser, ~(qcint)ConstF(0));
             else
-                out = (ast_expression*)ast_binary_new(ctx, INSTR_SUB_F, (ast_expression*)parser_const_float_neg1(parser), exprs[0]);
+                out = (ast_expression*)
+                    ast_binary_new(ctx, INSTR_SUB_F, (ast_expression*)parser_const_float_neg1(parser), exprs[0]);
             break;
     }
 #undef NotSameType
@@ -1830,6 +1893,15 @@ static bool parse_sya_operand(parser_t *parser, shunt *sy, bool with_labels)
             if (!strcmp(parser_tokval(parser), "__builtin_debug_typestring")) {
                 var = (ast_expression*)intrinsic_debug_typestring;
             }
+            /* now we try for the real intrinsic hashtable. If the string
+             * begins with __builtin, we simply skip past it, otherwise we
+             * use the identifier as is.
+             */
+            else if (!strncmp(parser_tokval(parser), "__builtin_", 10)) {
+                var = intrin_func(parser, parser_tokval(parser) + 10 /* skip __builtin */);
+            } else {
+                var = intrin_func(parser, parser_tokval(parser));
+            }
                 
             if (!var) {
                 char *correct = NULL;
@@ -3654,6 +3726,8 @@ static bool parse_statement(parser_t *parser, ast_block *block, ast_expression *
 
 static bool parse_enum(parser_t *parser)
 {
+    bool        flag = false;
+    bool        reverse = false;
     qcfloat     num = 0;
     ast_value **values = NULL;
     ast_value  *var = NULL;
@@ -3661,11 +3735,37 @@ static bool parse_enum(parser_t *parser)
 
     ast_expression *old;
 
-    if (!parser_next(parser) || parser->tok != '{') {
-        parseerror(parser, "expected `{` after `enum` keyword");
+    if (!parser_next(parser) || (parser->tok != '{' && parser->tok != ':')) {
+        parseerror(parser, "expected `{` or `:` after `enum` keyword");
         return false;
     }
 
+    /* enumeration attributes (can add more later) */
+    if (parser->tok == ':') {
+        if (!parser_next(parser) || parser->tok != TOKEN_IDENT){
+            parseerror(parser, "expected `flag` or `reverse` for enumeration attribute");
+            return false;
+        }
+
+        /* attributes? */
+        if (!strcmp(parser_tokval(parser), "flag")) {
+            num  = 1;
+            flag = true;
+        }
+        else if (!strcmp(parser_tokval(parser), "reverse")) {
+            reverse = true;
+        }
+        else {
+            parseerror(parser, "invalid attribute `%s` for enumeration", parser_tokval(parser));
+            return false;
+        }
+
+        if (!parser_next(parser) || parser->tok != '{') {
+            parseerror(parser, "expected `{` after enum attribute ");
+            return false;
+        }
+    }
+
     while (true) {
         if (!parser_next(parser) || parser->tok != TOKEN_IDENT) {
             if (parser->tok == '}') {
@@ -3689,8 +3789,9 @@ static bool parse_enum(parser_t *parser)
         vec_push(values, var);
         var->cvq             = CV_CONST;
         var->hasvalue        = true;
-        var->constval.vfloat = num++;
 
+        /* for flagged enumerations increment in POTs of TWO */
+        var->constval.vfloat = (flag) ? (num *= 2) : (num ++);
         parser_addglobal(parser, var->name, (ast_expression*)var);
 
         if (!parser_next(parser)) {
@@ -3729,6 +3830,13 @@ static bool parse_enum(parser_t *parser)
         }
     }
 
+    /* patch them all (for reversed attribute) */
+    if (reverse) {
+        size_t i;
+        for (i = 0; i < vec_size(values); i++)
+            values[i]->constval.vfloat = vec_size(values) - i - 1;
+    }
+
     if (parser->tok != '}') {
         parseerror(parser, "internal error: breaking without `}`");
         goto onerror;
@@ -4696,6 +4804,8 @@ static ast_value *parse_parameter_list(parser_t *parser, ast_value *var)
 on_error:
     if (argcounter)
         mem_d(argcounter);
+    if (varparam)
+        ast_delete(varparam);
     ast_delete(var);
     for (i = 0; i < vec_size(params); ++i)
         ast_delete(params[i]);
@@ -5814,16 +5924,15 @@ static void generate_checksum(parser_t *parser)
     code_crc = crc;
 }
 
-static parser_t *parser;
-
-bool parser_init()
+parser_t *parser_create()
 {
+    parser_t *parser;
     lex_ctx empty_ctx;
     size_t i;
 
     parser = (parser_t*)mem_a(sizeof(parser_t));
     if (!parser)
-        return false;
+        return NULL;
 
     memset(parser, 0, sizeof(*parser));
 
@@ -5836,7 +5945,7 @@ bool parser_init()
     if (!parser->assign_op) {
         printf("internal error: initializing parser: failed to find assign operator\n");
         mem_d(parser);
-        return false;
+        return NULL;
     }
 
     vec_push(parser->variables, parser->htfields  = util_htnew(PARSER_HT_SIZE));
@@ -5846,6 +5955,8 @@ bool parser_init()
 
     parser->aliases = util_htnew(PARSER_HT_SIZE);
 
+    parser->ht_imm_string = util_htnew(512);
+
     /* corrector */
     vec_push(parser->correct_variables, correct_trie_new());
     vec_push(parser->correct_variables_score, NULL);
@@ -5872,10 +5983,11 @@ bool parser_init()
     } else {
         parser->reserved_version = NULL;
     }
-    return true;
+
+    return parser;
 }
 
-bool parser_compile()
+bool parser_compile(parser_t *parser)
 {
     /* initial lexer/parser state */
     parser->lex->flags.noops = true;
@@ -5907,27 +6019,27 @@ bool parser_compile()
     return !compile_errors;
 }
 
-bool parser_compile_file(const char *filename)
+bool parser_compile_file(parser_t *parser, const char *filename)
 {
     parser->lex = lex_open(filename);
     if (!parser->lex) {
         con_err("failed to open file \"%s\"\n", filename);
         return false;
     }
-    return parser_compile();
+    return parser_compile(parser);
 }
 
-bool parser_compile_string(const char *name, const char *str, size_t len)
+bool parser_compile_string(parser_t *parser, const char *name, const char *str, size_t len)
 {
     parser->lex = lex_open_string(str, len, name);
     if (!parser->lex) {
         con_err("failed to create lexer for string \"%s\"\n", name);
         return false;
     }
-    return parser_compile();
+    return parser_compile(parser);
 }
 
-void parser_cleanup()
+void parser_cleanup(parser_t *parser)
 {
     size_t i;
     for (i = 0; i < vec_size(parser->accessors); ++i) {
@@ -5957,6 +6069,7 @@ void parser_cleanup()
     vec_free(parser->functions);
     vec_free(parser->imm_vector);
     vec_free(parser->imm_string);
+    util_htdel(parser->ht_imm_string);
     vec_free(parser->imm_float);
     vec_free(parser->globals);
     vec_free(parser->fields);
@@ -5998,10 +6111,12 @@ void parser_cleanup()
 
     util_htdel(parser->aliases);
 
+    intrin_intrinsics_destroy(parser);
+
     mem_d(parser);
 }
 
-bool parser_finish(const char *output)
+bool parser_finish(parser_t *parser, const char *output)
 {
     size_t i;
     ir_builder *ir;
diff --git a/syntax/README b/syntax/README
new file mode 100644 (file)
index 0000000..5a5c4b4
--- /dev/null
@@ -0,0 +1,24 @@
+Here exists some syntax highlighting configuration files for various
+text editors. Inside each directory exists some documentaiton on how
+you can install the configuration file correctly.
+
+Currently the supported text editors:
+    geany
+    kate
+    kwrite        - uses kate syntax highlighting
+    kdevelop      - uses kate syntax highlighting
+    QtCreator     - supports kate syntax highlighting
+    gtksourceview - main source viewer in GNOME
+    gedit         - uses gtksourceview
+    sandy         - uses gtksourceview
+    nano
+    jedit
+
+
+Other text editors we plan to provide syntax highlighting configuration
+files for (but never got around to figuring out)
+    vim
+    emacs
+
+If your text editor is not supported and you'd like to create syntax
+highlighting support for it, don't hesitate to share it with us.
diff --git a/syntax/geany/README b/syntax/geany/README
new file mode 100644 (file)
index 0000000..c527d3f
--- /dev/null
@@ -0,0 +1,8 @@
+To use the geany syntax highlighting install filetypes.qc to the syntax
+directory for geany.
+
+# Can be installed globally to
+/usr/share/geany/
+
+# Can be installed locally to
+~/.config/geany/filedefs/
diff --git a/syntax/geany/filetypes.qc b/syntax/geany/filetypes.qc
new file mode 100644 (file)
index 0000000..d84bb7e
--- /dev/null
@@ -0,0 +1,55 @@
+[styling]
+default=default
+comment=comment
+commentline=comment_line
+commentdoc=comment_doc
+preprocessorcomment=comment
+number=number_1
+word=keyword_1
+word2=keyword_2
+string=string_1
+stringraw=string_2
+character=character
+uuid=other
+preprocessor=preprocessor
+operator=operator
+identifier=identifier_1
+stringeol=string_eol
+verbatim=string_2
+regex=regex
+commentlinedoc=comment_line_doc
+commentdockeyword=comment_doc_keyword
+commentdockeyworderror=comment_doc_keyword_error
+globalclass=class
+tripleverbatim=string_2
+hashquotedstring=string_2
+
+[keywords]
+primary=break case const continue string default do else enum float for goto if return switch typedef void while false nil true
+secondary=
+docComment=
+
+[lexer_properties]
+styling.within.preprocessor=1
+lexer.cpp.track.preprocessor=0
+preprocessor.symbol.$(file.patterns.cpp)=#
+preprocessor.start.$(file.patterns.cpp)=if ifdef ifndef
+preprocessor.middle.$(file.patterns.cpp)=else elif
+preprocessor.end.$(file.patterns.cpp)=endif
+
+[settings]
+extension=qc
+comment_single=//
+comment_open=/*
+comment_close=*/
+comment_use_indent=true
+context_action_cmd=
+
+[indentation]
+width=4
+type=0
+
+[build_settings]
+compiler=gmqcc -Wall "%f" -o "%e"
+linker=
+run_cmd=qcvm "./%e"
diff --git a/syntax/gtksourceview/README b/syntax/gtksourceview/README
new file mode 100644 (file)
index 0000000..73a6072
--- /dev/null
@@ -0,0 +1,5 @@
+To use the gtksourceview syntax highlighting install qc.lang to the syntax
+directory for gtksourceview
+
+# Can be installed globally to
+/usr/share/gtksourceview-[version]/language-specs/
diff --git a/syntax/gtksourceview/qc.lang b/syntax/gtksourceview/qc.lang
new file mode 100644 (file)
index 0000000..827290f
--- /dev/null
@@ -0,0 +1,173 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<language id="qc" _name="QuakeC" version="1.0" _section="Sources">
+  <metadata>
+    <property name="globs">*.qc</property>
+    <property name="line-comment-start">//</property>
+    <property name="block-comment-start">/*</property>
+    <property name="block-comment-end">*/</property>
+  </metadata>
+
+  <styles>
+    <style id="comment"           _name="Comment"               map-to="def:comment"/>
+    <style id="string"            _name="String"                map-to="def:string"/>
+    <style id="preprocessor"      _name="Preprocessor"          map-to="def:preprocessor"/>
+    <style id="common-defines"    _name="Common Defines"        map-to="def:special-constant"/>
+    <style id="included-file"     _name="Included File"         map-to="def:string"/>
+    <style id="keyword"           _name="Keyword"               map-to="def:keyword"/>
+    <style id="type"              _name="Data Type"             map-to="def:type"/>
+    <style id="escaped-character" _name="Escaped Character"     map-to="def:special-char"/>
+    <style id="floating-point"    _name="Floating point number" map-to="def:floating-point"/>
+    <style id="decimal"           _name="Decimal number"        map-to="def:decimal"/>
+    <style id="hexadecimal"       _name="Hexadecimal number"    map-to="def:base-n-integer"/>
+    <style id="boolean"           _name="Boolean value"         map-to="def:boolean"/>
+  </styles>
+
+  <definitions>
+
+    <!--regexs-->
+    <define-regex id="preproc-start">^\s*#\s*</define-regex>
+    <define-regex id="escaped-character" extended="true">
+      \\(                   # leading backslash
+      [\\\"\'nrbtfav\?] |   # escaped character
+      [0-7]{1,3} |          # one, two, or three octal digits
+      x[0-9A-Fa-f]+         # 'x' followed by hex digits
+      )
+    </define-regex>
+
+    <!-- Preprocessor -->
+    <context id="if0-comment" style-ref="comment">
+      <start>\%{preproc-start}if\b\s*0\b</start>
+      <end>\%{preproc-start}(endif|else|elif)\b</end>
+      <include>
+        <context id="if-in-if0">
+          <start>\%{preproc-start}if(n?def)?\b</start>
+          <end>\%{preproc-start}endif\b</end>
+          <include>
+            <context ref="if-in-if0"/>
+            <context ref="def:in-comment"/>
+          </include>
+        </context>
+        <context ref="def:in-comment"/>
+      </include>
+    </context>
+    <context id="include" style-ref="preprocessor">
+      <match extended="true">
+        \%{preproc-start}
+        (include|import)\s*
+        (".*?"|&lt;.*&gt;)
+      </match>
+      <include>
+        <context id="included-file" sub-pattern="2" style-ref="included-file"/>
+      </include>
+    </context>
+    <context id="preprocessor" style-ref="preprocessor" end-at-line-end="true">
+      <start extended="true">
+        \%{preproc-start}
+        (define|undef|error|pragma|ident|if(n?def)?|else|elif|endif|line|warning)
+        \b
+      </start>
+      <include>
+        <context ref="def:line-continue" ignore-style="true"/>
+        <context ref="string" ignore-style="true"/>
+        <context ref="def:qc-like-comment"/>
+        <context ref="def:qc-like-comment-multiline"/>
+      </include>
+    </context>
+
+    <context id="float" style-ref="floating-point">
+      <match extended="true">
+        (?&lt;![\w\.])
+        ((\.[0-9]+ | [0-9]+\.[0-9]*) ([Ee][+-]?[0-9]*)? |
+         ([0-9]+[Ee][+-]?[0-9]*))
+        [fFlL]?
+        (?![\w\.])
+      </match>
+    </context>
+
+    <context id="hexadecimal" style-ref="hexadecimal">
+      <match extended="true">
+        (?&lt;![\w\.])
+        0[xX][a-fA-F0-9]+[uUlL]*
+        (?![\w\.])
+      </match>
+    </context>
+
+    <context id="invalid-hexadecimal" style-ref="error">
+      <match extended="true">
+        (?&lt;![\w\.])
+        0[xX][a-fA-F0-9]*[g-zG-Z][a-zA-Z0-9]*[uUlL]*
+        (?![\w\.])
+      </match>
+    </context>
+
+    <context id="decimal" style-ref="decimal">
+      <match extended="true">
+        (?&lt;![\w\.])
+        (0|[1-9][0-9]*)[uUlL]*
+        (?![\w\.])
+      </match>
+    </context>
+
+    <context id="keywords" style-ref="keyword">
+      <keyword>break</keyword>
+      <keyword>case</keyword>
+      <keyword>continue</keyword>
+      <keyword>default</keyword>
+      <keyword>do</keyword>
+      <keyword>else</keyword>
+      <keyword>enum</keyword>
+      <keyword>for</keyword>
+      <keyword>goto</keyword>
+      <keyword>if</keyword>
+      <keyword>return</keyword>
+      <keyword>switch</keyword>
+      <keyword>typedef</keyword>
+      <keyword>while</keyword>
+      <keyword>nil</keyword>
+    </context>
+
+    <context id="types" style-ref="type">
+      <keyword>bool</keyword>
+      <keyword>string</keyword>
+      <keyword>vector</keyword>
+      <keyword>float</keyword>
+      <keyword>void</keyword>
+    </context>
+
+    <context id="boolean" style-ref="boolean">
+      <keyword>true</keyword>
+      <keyword>false</keyword>
+    </context>
+
+    <context id="common-defines" style-ref="common-defines">
+      <keyword>__LINE__</keyword>
+      <keyword>__FILE__</keyword>
+      <keyword>__TIME__</keyword>
+      <keyword>__RANDOM__</keyword>
+      <keyword>__RANDOM_LAST__</keyword>
+      <keyword>__COUNTER__</keyword>
+      <keyword>__COUNTER_LAST__</keyword>
+      <keyword>__DATE__</keyword>
+    </context>
+
+    <context id="qc" class="no-spell-check">
+      <include>
+        <context ref="def:qc-like-comment"/>
+        <context ref="def:qc-like-comment-multiline"/>
+        <context ref="def:qc-like-close-comment-outside-comment"/>
+        <context ref="if0-comment"/>
+        <context ref="include"/>
+        <context ref="preprocessor"/>
+        <context ref="string"/>
+        <context ref="float"/>
+        <context ref="hexadecimal"/>
+        <context ref="invalid-hexadecimal"/>
+        <context ref="decimal"/>
+        <context ref="keywords"/>
+        <context ref="types"/>
+        <context ref="boolean"/>
+        <context ref="common-defines"/>
+      </include>
+    </context>
+  </definitions>
+</language>
diff --git a/syntax/jedit/README b/syntax/jedit/README
new file mode 100644 (file)
index 0000000..cfdf523
--- /dev/null
@@ -0,0 +1,26 @@
+To use the jedit syntax highlighting install qc.xml to the syntax
+directory for jedit
+
+# For Windows Users that directory is
+C:\Users\username\.jedit\modes
+
+# For Linux users that directory is
+/home/username/.jedit/modes
+
+# For Mac users that directory is
+/Users/username/Library/jEdit/modes
+
+After the file is installed, a mode line needs to be added to
+a file caled catalog in that same directory.
+
+Add the following line:
+<MODE NAME="QuakeC Code" FILE="qc.xml" FILE_NAME_GLOB="*.qc" />
+
+inside the <MODES> block before the end tag </MODES>. If the file
+does not exist, you can simply make one and use the following:
+
+<?xml version="1.0"?>
+<!DOCTYPE MODES SYSTEM "catalog.dtd">
+<MODES>
+    <MODE NAME="QuakeC Code" FILE="qc.xml" FILE_NAME_GLOB="*.qc" />
+</MODES>
diff --git a/syntax/jedit/qc.xml b/syntax/jedit/qc.xml
new file mode 100644 (file)
index 0000000..94b2d66
--- /dev/null
@@ -0,0 +1,271 @@
+<?xml version="1.0"?>
+
+<!DOCTYPE MODE SYSTEM "xmode.dtd">
+
+<MODE>
+    <PROPS>
+        <PROPERTY NAME="commentStart" VALUE="/*" />
+        <PROPERTY NAME="commentEnd" VALUE="*/" />
+        <PROPERTY NAME="lineComment" VALUE="//" />
+        <PROPERTY NAME="wordBreakChars" VALUE=",+-=&lt;&gt;/?^&amp;*" />
+
+        <!-- Auto indent -->
+        <PROPERTY NAME="indentOpenBrackets" VALUE="{" />
+        <PROPERTY NAME="indentCloseBrackets" VALUE="}" />
+        <PROPERTY NAME="unalignedOpenBrackets" VALUE="(" />
+        <PROPERTY NAME="unalignedCloseBrackets" VALUE=")" />
+        <PROPERTY NAME="indentNextLine"
+            VALUE="(?!^\s*(#|//)).*(\b(if|while|for)\s*\(.*\)|\b(else|do)\b)[^{;]*$" />
+        <PROPERTY NAME="unindentThisLine"
+            VALUE="^\s*((case\b.*|[\p{Alpha}_][\p{Alnum}_]*)\s*:(?!:)).*$" />
+        <PROPERTY NAME="electricKeys" VALUE=":" />
+    </PROPS>
+
+    <RULES
+        IGNORE_CASE="FALSE"
+        HIGHLIGHT_DIGITS="TRUE" DIGIT_RE="[0-9][0-9a-zA-Z]*">
+        <EOL_SPAN TYPE="KEYWORD2" AT_WHITESPACE_END="TRUE" DELEGATE="CPP">#</EOL_SPAN>
+
+        <IMPORT DELEGATE="LEX"/>
+        <IMPORT DELEGATE="CORE"/>
+    </RULES>
+
+    <RULES SET="LEX" IGNORE_CASE="FALSE">
+        <IMPORT DELEGATE="COMMENTS" />
+        <IMPORT DELEGATE="C_LEXER" />
+    </RULES>
+
+    <!-- Comments, Trigraph, Alternate-Tokens -->
+    <RULES SET="C_LEXER"
+        IGNORE_CASE="FALSE"
+        HIGHLIGHT_DIGITS="TRUE" DIGIT_RE="[0-9][0-9a-zA-Z]*">
+
+        <SPAN TYPE="LITERAL1" NO_LINE_BREAK="TRUE" ESCAPE="\">
+            <BEGIN>L"</BEGIN>
+            <END>"</END>
+        </SPAN>
+        <SPAN TYPE="LITERAL1" NO_LINE_BREAK="TRUE" ESCAPE="\">
+            <BEGIN>"</BEGIN>
+            <END>"</END>
+        </SPAN>
+        <SPAN TYPE="LITERAL1" NO_LINE_BREAK="TRUE" ESCAPE="\">
+            <BEGIN>L'</BEGIN>
+            <END>'</END>
+        </SPAN>
+        <SPAN TYPE="LITERAL1" NO_LINE_BREAK="TRUE" ESCAPE="\">
+            <BEGIN>'</BEGIN>
+            <END>'</END>
+        </SPAN>
+
+        <!-- Trigraphs -->
+        <SEQ TYPE="LITERAL4">??(</SEQ>
+        <SEQ TYPE="LITERAL4">??/</SEQ>
+        <SEQ TYPE="LITERAL4">??)</SEQ>
+        <SEQ TYPE="LITERAL4">??'</SEQ>
+        <SEQ TYPE="LITERAL4">??&lt;</SEQ>
+        <SEQ TYPE="LITERAL4">??!</SEQ>
+        <SEQ TYPE="LITERAL4">??&gt;</SEQ>
+        <SEQ TYPE="LITERAL4">??-</SEQ>
+        <SEQ TYPE="LITERAL4">??=</SEQ>
+
+        <!-- Alternate tokens -->
+        <SEQ TYPE="LITERAL4">&lt;:</SEQ>
+        <SEQ TYPE="LITERAL4">:&gt;</SEQ>
+        <SEQ TYPE="LITERAL4">&lt;%</SEQ>
+        <SEQ TYPE="LITERAL4">%&gt;</SEQ>
+        <SEQ TYPE="LITERAL4">%:</SEQ>
+
+        <!-- Labels.
+            This is a part of core language syntax, but must be here
+            because it can't work after SEQ for ':'. -->
+        <MARK_PREVIOUS AT_WHITESPACE_END="TRUE"
+            MATCH_TYPE="OPERATOR"
+            TYPE="LABEL">:</MARK_PREVIOUS>
+
+        <!-- Function-like macro or function calls.
+            This can't work after SEQ for '('. -->
+        <MARK_PREVIOUS
+            TYPE="FUNCTION"
+            MATCH_TYPE="OPERATOR">(</MARK_PREVIOUS>
+
+        <SEQ TYPE="OPERATOR">=</SEQ>
+        <SEQ TYPE="OPERATOR">!</SEQ>
+        <SEQ TYPE="OPERATOR">+</SEQ>
+        <SEQ TYPE="OPERATOR">-</SEQ>
+        <SEQ TYPE="OPERATOR">/</SEQ>
+        <SEQ TYPE="OPERATOR">*</SEQ>
+        <SEQ TYPE="OPERATOR">&gt;</SEQ>
+        <SEQ TYPE="OPERATOR">&lt;</SEQ>
+        <SEQ TYPE="OPERATOR">%</SEQ>
+        <SEQ TYPE="OPERATOR">&amp;</SEQ>
+        <SEQ TYPE="OPERATOR">|</SEQ>
+        <SEQ TYPE="OPERATOR">^</SEQ>
+        <SEQ TYPE="OPERATOR">~</SEQ>
+        <SEQ TYPE="OPERATOR">?</SEQ>
+        <SEQ TYPE="OPERATOR">:</SEQ>
+        <SEQ TYPE="OPERATOR">.</SEQ>
+        <SEQ TYPE="OPERATOR">,</SEQ>
+        <SEQ TYPE="OPERATOR">[</SEQ>
+        <SEQ TYPE="OPERATOR">]</SEQ>
+        <SEQ TYPE="OPERATOR">)</SEQ>
+        <SEQ TYPE="OPERATOR">}</SEQ>
+        <SEQ TYPE="OPERATOR">{</SEQ>
+        <SEQ TYPE="OPERATOR">;</SEQ>
+
+        <KEYWORDS>
+            <LITERAL2>__FILE__</LITERAL2>
+            <LITERAL2>__LINE__</LITERAL2>
+            <LITERAL2>__DATE__</LITERAL2>
+            <LITERAL2>__RANDOM__</LITERAL2>
+            <LITERAL2>__RANDOM_LAST</LITERAL2>
+            <LITERAL2>__COUNT__</LITERAL2>
+            <LITERAL2>__COUNT_LAST</LITERAL2>
+        </KEYWORDS>
+    </RULES>
+
+    <!-- Core language -->
+    <RULES SET="CORE"
+        IGNORE_CASE="FALSE"
+        HIGHLIGHT_DIGITS="TRUE" DIGIT_RE="[0-9][0-9a-zA-Z]*">
+        <KEYWORDS>
+            <!-- Types -->
+            <KEYWORD3>float</KEYWORD3>
+            <KEYWORD3>vector</KEYWORD3>
+            <KEYWORD3>string</KEYWORD3>
+            <KEYWORD3>entity</KEYWORD3>
+            <KEYWORD3>enum</KEYWORD3>
+            <KEYWORD3>.float</KEYWORD3>
+            <KEYWORD3>.int</KEYWORD3>
+            <KEYWORD3>.vector</KEYWORD3>
+            <KEYWORD3>.string</KEYWORD3>
+            <KEYWORD3>.entity</KEYWORD3>
+            <KEYWORD3>.void</KEYWORD3>
+            <KEYWORD3>typedef</KEYWORD3>
+
+            <KEYWORD1>break</KEYWORD1>
+            <KEYWORD1>case</KEYWORD1>
+            <KEYWORD1>continue</KEYWORD1>
+            <KEYWORD1>default</KEYWORD1>
+            <KEYWORD1>do</KEYWORD1>
+            <KEYWORD1>else</KEYWORD1>
+            <KEYWORD1>for</KEYWORD1>
+            <KEYWORD1>goto</KEYWORD1>
+            <KEYWORD1>if</KEYWORD1>
+            <KEYWORD1>return</KEYWORD1>
+            <KEYWORD1>switch</KEYWORD1>
+            <KEYWORD1>void</KEYWORD1>
+            <KEYWORD1>while</KEYWORD1>
+            <KEYWORD1>nil</KEYWORD1>
+
+            <LITERAL2>FALSE</LITERAL2>
+            <LITERAL2>TRUE</LITERAL2>
+            <LITERAL2>...</LITERAL2>
+        </KEYWORDS>
+    </RULES>
+
+    <!-- Different comment styles. -->
+    <RULES SET="COMMENTS">
+        <!-- Doxygen comment, Javadoc style -->
+        <SEQ TYPE="COMMENT1">/**/</SEQ>
+        <SPAN TYPE="COMMENT3" DELEGATE="doxygen::DOXYGEN">
+            <BEGIN>/**&lt;</BEGIN>
+            <END>*/</END>
+        </SPAN>
+        <SPAN TYPE="COMMENT3" DELEGATE="doxygen::DOXYGEN">
+            <BEGIN>/**</BEGIN>
+            <END>*/</END>
+        </SPAN>
+        <EOL_SPAN TYPE="COMMENT3" DELEGATE="doxygen::DOXYGEN">///&lt;</EOL_SPAN>
+        <EOL_SPAN TYPE="COMMENT3" DELEGATE="doxygen::DOXYGEN">///</EOL_SPAN>
+
+        <!-- Doxygen comment, Qt style -->
+        <SPAN TYPE="COMMENT3" DELEGATE="doxygen::DOXYGEN">
+            <BEGIN>/*!&lt;</BEGIN>
+            <END>*/</END>
+        </SPAN>
+        <SPAN TYPE="COMMENT3" DELEGATE="doxygen::DOXYGEN">
+            <BEGIN>/*!</BEGIN>
+            <END>*/</END>
+        </SPAN>
+        <EOL_SPAN TYPE="COMMENT3" DELEGATE="doxygen::DOXYGEN">//!&lt;</EOL_SPAN>
+        <EOL_SPAN TYPE="COMMENT3" DELEGATE="doxygen::DOXYGEN">//!</EOL_SPAN>
+
+        <!-- C style comment -->
+        <SPAN TYPE="COMMENT1">
+            <BEGIN>/*</BEGIN>
+            <END>*/</END>
+        </SPAN>
+        <EOL_SPAN TYPE="COMMENT1">//</EOL_SPAN>
+    </RULES>
+
+    <!-- Preprocessor specific rules -->
+    <RULES SET="CPP"
+        IGNORE_CASE="FALSE"
+        HIGHLIGHT_DIGITS="TRUE" DIGIT_RE="[0-9][0-9a-zA-Z]*">
+
+        <EOL_SPAN_REGEXP HASH_CHAR="include" TYPE="MARKUP" DELEGATE="INCLUDE">include\b</EOL_SPAN_REGEXP>
+        <EOL_SPAN_REGEXP HASH_CHAR="define" TYPE="MARKUP" DELEGATE="DEFINE">define\b</EOL_SPAN_REGEXP>
+        <EOL_SPAN_REGEXP HASH_CHAR="endif" TYPE="MARKUP" DELEGATE="LEX">endif\b</EOL_SPAN_REGEXP>
+        <EOL_SPAN_REGEXP HASH_CHAR="elif" TYPE="MARKUP" DELEGATE="CONDITION">elif\b</EOL_SPAN_REGEXP>
+        <EOL_SPAN_REGEXP HASH_CHAR="if" TYPE="MARKUP" DELEGATE="CONDITION">if\b</EOL_SPAN_REGEXP>
+
+        <IMPORT DELEGATE="LEX"/>
+
+        <!-- Directives -->
+        <KEYWORDS>
+            <MARKUP>undef</MARKUP>
+            <MARKUP>ifdef</MARKUP>
+            <MARKUP>ifndef</MARKUP>
+            <MARKUP>else</MARKUP>
+            <MARKUP>error</MARKUP>
+            <MARKUP>warning</MARKUP>
+            <MARKUP>pragma</MARKUP>
+            <MARKUP>$frame</MARKUP>
+            <MARKUP>$model</MARKUP>
+        </KEYWORDS>
+    </RULES>
+
+    <!-- After #include directive -->
+    <!-- "\"s are not escaped. -->
+    <RULES SET="INCLUDE"
+        IGNORE_CASE="FALSE"
+        HIGHLIGHT_DIGITS="TRUE" DIGIT_RE="[0-9][0-9a-zA-Z]*">
+        <SPAN TYPE="LITERAL1" NO_LINE_BREAK="TRUE">
+            <BEGIN>&lt;</BEGIN>
+            <END>&gt;</END>
+        </SPAN>
+        <SPAN TYPE="LITERAL1" NO_LINE_BREAK="TRUE">
+            <BEGIN>"</BEGIN>
+            <END>"</END>
+        </SPAN>
+        <IMPORT DELEGATE="LEX"/>
+    </RULES>
+
+    <!-- After #define directive -->
+    <!-- Almost same as the normal code,
+        except two additional operators # and ##. -->
+    <RULES SET="DEFINE"
+        IGNORE_CASE="FALSE"
+        HIGHLIGHT_DIGITS="TRUE" DIGIT_RE="[0-9][0-9a-zA-Z]*">
+        <SEQ TYPE="OPERATOR">#</SEQ>
+        <IMPORT DELEGATE="LEX"/>
+        <IMPORT DELEGATE="CORE"/>
+    </RULES>
+
+    <!-- After #if or #elif directive -->
+    <!-- All constant expressions and a special operator
+        'defined' is available. But the core language elements
+        (such as operator 'sizeof', type casting, etc...) are not. -->
+    <RULES SET="CONDITION"
+        IGNORE_CASE="FALSE"
+        HIGHLIGHT_DIGITS="TRUE" DIGIT_RE="[0-9][0-9a-zA-Z]*">
+        <IMPORT DELEGATE="LEX"/>
+        <KEYWORDS>
+            <KEYWORD2>defined</KEYWORD2>
+            <KEYWORD2>TRUE</KEYWORD2>
+            <KEYWORD2>FALSE</KEYWORD2>
+            <KEYWORD2>true</KEYWORD2>
+            <KEYWORD2>false</KEYWORD2>
+        </KEYWORDS>
+    </RULES>
+</MODE>
diff --git a/syntax/kate/README b/syntax/kate/README
new file mode 100644 (file)
index 0000000..888d6d2
--- /dev/null
@@ -0,0 +1,9 @@
+To use the Kate syntax highlighting install qc.xml to the syntax
+directory for kate.
+
+# Can be installed globally to
+$KDEDIR/share/apps/katepart/syntax
+
+if $KDEDIR is unset you can lookup the folder directory with
+kde4-config --prefix if that doesn't work chances are KDEDIR is
+/usr
diff --git a/syntax/kate/qc.xml b/syntax/kate/qc.xml
new file mode 100644 (file)
index 0000000..2f49dba
--- /dev/null
@@ -0,0 +1,155 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE language SYSTEM "language.dtd">
+<language name="QuakeC" section="Sources"
+          version="1.00" kateversion="2.4"
+          indenter="cstyle"
+          extensions="*.qc;*.QC;*.qh"
+          mimetype=""
+          priority="5"
+          author="Dale Weiler">
+  <highlighting>
+    <list name="keywords">
+      <item> break </item>
+      <item> case </item>
+      <item> continue </item>
+      <item> default </item>
+      <item> do </item>
+      <item> else </item>
+      <item> enum </item>
+      <item> for </item>
+      <item> goto </item>
+      <item> if </item>
+      <item> return </item>
+      <item> switch </item>
+      <item> typedef </item>
+      <item> while </item>
+      <item> nil </item>
+    </list>
+    <list name="types">
+      <item> const </item>
+      <item> vector </item>
+      <item> float </item>
+      <item> void </item>
+      <item> string </item>
+    </list>
+    <contexts>
+      <context attribute="Normal Text" lineEndContext="#stay" name="Normal">
+        <DetectSpaces />
+    <RegExpr attribute="Preprocessor" context="Outscoped" String="#\s*if\s+0\s*$" beginRegion="PP" firstNonSpace="true" />
+        <DetectChar context="AfterHash" char="#" firstNonSpace="true" lookAhead="true" />
+        <StringDetect attribute="Region Marker" context="Region Marker" String="//BEGIN" beginRegion="Region1" firstNonSpace="true" />
+        <StringDetect attribute="Region Marker" context="Region Marker" String="//END" endRegion="Region1" firstNonSpace="true" />
+        <keyword attribute="Keyword" context="#stay" String="keywords"/>
+        <keyword attribute="Data Type" context="#stay" String="types"/>
+        <DetectIdentifier />
+        <DetectChar attribute="Symbol" context="#stay" char="{" beginRegion="Brace1" />
+        <DetectChar attribute="Symbol" context="#stay" char="}" endRegion="Brace1" />
+        <Float attribute="Float" context="#stay">
+          <AnyChar String="fF" attribute="Float" context="#stay"/>
+        </Float>
+        <HlCHex attribute="Hex" context="#stay"/>
+        <Int attribute="Decimal" context="#stay" >
+          <StringDetect attribute="Decimal" context="#stay" String="ULL" insensitive="TRUE"/>
+          <StringDetect attribute="Decimal" context="#stay" String="LUL" insensitive="TRUE"/>
+          <StringDetect attribute="Decimal" context="#stay" String="LLU" insensitive="TRUE"/>
+          <StringDetect attribute="Decimal" context="#stay" String="UL" insensitive="TRUE"/>
+          <StringDetect attribute="Decimal" context="#stay" String="LU" insensitive="TRUE"/>
+          <StringDetect attribute="Decimal" context="#stay" String="LL" insensitive="TRUE"/>
+          <StringDetect attribute="Decimal" context="#stay" String="U" insensitive="TRUE"/>
+          <StringDetect attribute="Decimal" context="#stay" String="L" insensitive="TRUE"/>
+        </Int>
+        <HlCChar attribute="Char" context="#stay"/>
+        <DetectChar attribute="String" context="String" char="&quot;"/>
+        <Detect2Chars attribute="Comment" context="Commentar 1" char="/" char1="/"/>
+        <Detect2Chars attribute="Comment" context="Commentar 2" char="/" char1="*" beginRegion="Comment"/>
+        <AnyChar attribute="Symbol" context="#stay" String=":!%&amp;()+,-/.*&lt;=&gt;?[]|~^&#59;"/>
+      </context>
+
+      <context attribute="String" lineEndContext="#pop" name="String">
+        <LineContinue attribute="String" context="#stay"/>
+        <HlCStringChar attribute="String Char" context="#stay"/>
+        <DetectChar attribute="String" context="#pop" char="&quot;"/>
+      </context>
+
+      <context attribute="Region Marker" lineEndContext="#pop" name="Region Marker">
+      </context>
+
+      <context attribute="Comment" lineEndContext="#pop" name="Commentar 1">
+        <LineContinue attribute="Comment" context="#stay"/>
+        <IncludeRules context="##Alerts" />
+      </context>
+
+      <context attribute="Comment" lineEndContext="#stay" name="Commentar 2">
+        <Detect2Chars attribute="Comment" context="#pop" char="*" char1="/" endRegion="Comment"/>
+        <IncludeRules context="##Alerts" />
+      </context>
+
+      <context attribute="Error" lineEndContext="#pop" name="AfterHash">
+        <!-- define, elif, else, endif, error, if, ifdef, ifndef, include, include_next, line, pragma, undef, warning -->
+        <RegExpr attribute="Preprocessor" context="Preprocessor" String="#\s*if(?:def|ndef)?(?=\s+\S)" insensitive="true" beginRegion="PP" firstNonSpace="true" />
+        <RegExpr attribute="Preprocessor" context="Preprocessor" String="#\s*endif" insensitive="true" endRegion="PP" firstNonSpace="true" />
+        <RegExpr attribute="Preprocessor" context="Define" String="#\s*define.*((?=\\))" insensitive="true" firstNonSpace="true" />
+        <RegExpr attribute="Preprocessor" context="Preprocessor" String="#\s*(?:el(?:se|if)|include(?:_next)?|define|undef|line|error|warning|pragma)" insensitive="true" firstNonSpace="true" />
+        <RegExpr attribute="Preprocessor" context="Preprocessor" String="#\s+[0-9]+" insensitive="true" firstNonSpace="true" />
+      </context>
+
+      <context attribute="Preprocessor" lineEndContext="#pop" name="Preprocessor">
+        <LineContinue attribute="Preprocessor" context="#stay"/>
+        <RangeDetect attribute="Prep. Lib" context="#stay" char="&quot;" char1="&quot;"/>
+        <RangeDetect attribute="Prep. Lib" context="#stay" char="&lt;" char1="&gt;"/>
+        <IncludeRules context="##Doxygen" />
+        <Detect2Chars attribute="Comment" context="Commentar/Preprocessor" char="/" char1="*" beginRegion="Comment2" />
+        <Detect2Chars attribute="Comment" context="Commentar 1" char="/" char1="/" />
+      </context>
+
+      <context attribute="Preprocessor" lineEndContext="#pop" name="Define">
+        <LineContinue attribute="Preprocessor" context="#stay"/>
+      </context>
+
+      <context attribute="Comment" lineEndContext="#stay" name="Commentar/Preprocessor">
+        <Detect2Chars attribute="Comment" context="#pop" char="*" char1="/" endRegion="Comment2" />
+      </context>
+
+      <context attribute="Comment" lineEndContext="#stay" name="Outscoped" >
+        <DetectSpaces />
+        <DetectIdentifier />
+        <DetectChar attribute="String" context="String" char="&quot;"/>
+        <Detect2Chars attribute="Comment" context="Commentar 1" char="/" char1="/"/>
+        <Detect2Chars attribute="Comment" context="Commentar 2" char="/" char1="*" beginRegion="Comment"/>
+        <RegExpr attribute="Comment" context="Outscoped intern" String="#\s*if" beginRegion="PP" firstNonSpace="true" />
+        <RegExpr attribute="Preprocessor" context="#pop" String="#\s*el(?:se|if)" firstNonSpace="true" />
+        <RegExpr attribute="Preprocessor" context="#pop" String="#\s*endif" endRegion="PP" firstNonSpace="true" />
+      </context>
+
+      <context attribute="Comment" lineEndContext="#stay" name="Outscoped intern">
+        <DetectSpaces />
+        <DetectIdentifier />
+        <DetectChar attribute="String" context="String" char="&quot;"/>
+        <Detect2Chars attribute="Comment" context="Commentar 1" char="/" char1="/"/>
+        <Detect2Chars attribute="Comment" context="Commentar 2" char="/" char1="*" beginRegion="Comment"/>
+        <RegExpr attribute="Comment" context="Outscoped intern" String="#\s*if" beginRegion="PP" firstNonSpace="true" />
+        <RegExpr attribute="Comment" context="#pop" String="#\s*endif" endRegion="PP" firstNonSpace="true" />
+      </context>
+    </contexts>
+    <itemDatas>
+      <itemData name="Normal Text"  defStyleNum="dsNormal" spellChecking="false"/>
+      <itemData name="Keyword"      defStyleNum="dsKeyword" spellChecking="false"/>
+      <itemData name="Data Type"    defStyleNum="dsDataType" spellChecking="false"/>
+      <itemData name="Decimal"      defStyleNum="dsDecVal" spellChecking="false"/>
+      <itemData name="Hex"          defStyleNum="dsBaseN" spellChecking="false"/>
+      <itemData name="Float"        defStyleNum="dsFloat" spellChecking="false"/>
+      <itemData name="String"       defStyleNum="dsString"/>
+      <itemData name="String Char"  defStyleNum="dsChar"/>
+      <itemData name="Comment"      defStyleNum="dsComment"/>
+      <itemData name="Symbol"       defStyleNum="dsNormal" spellChecking="false"/>
+      <itemData name="Preprocessor" defStyleNum="dsOthers" spellChecking="false"/>
+    </itemDatas>
+  </highlighting>
+  <general>
+    <comments>
+      <comment name="singleLine" start="//" />
+      <comment name="multiLine" start="/*" end="*/" />
+    </comments>
+    <keywords casesensitive="1" additionalDeliminator="'&quot;" />
+  </general>
+</language>
diff --git a/syntax/nano/README b/syntax/nano/README
new file mode 100644 (file)
index 0000000..edd9d3f
--- /dev/null
@@ -0,0 +1,12 @@
+To use the nano syntax highlighting install qc.nanorc somewhere and
+add:
+
+include /directory/qc.nanorc
+
+to your nanorc file located at ~/.nanorc. If the file doesn't exist
+create it.
+
+Optionally you can install it globally by installing qc.nanorc to
+/usr/share/nano
+
+However you still need to provide the include to your ~/.nanorc
diff --git a/syntax/nano/qc.nanorc b/syntax/nano/qc.nanorc
new file mode 100644 (file)
index 0000000..5222485
--- /dev/null
@@ -0,0 +1,22 @@
+# Language:   QuakeC
+# Maintainer: Dale Weiler
+
+syntax "qc" "\.(qc|QC)$" "\.(qh|QH)$"
+color brightred "\<[A-Z_][0-9A-Z_]+\>" 
+color green "\<(float|string|enum|void|const|typedef|nil)\>"
+color brightyellow "\<(for|if|while|do|else|case|default|switch)\>"
+color magenta "\<(goto|continue|break|return)\>"
+color brightcyan "^[[:space:]]*#[[:space:]]*(define|include|(un|ifn?)def|endif|el(if|se)|if|warning|error|pragma)"
+color brightmagenta "'([^'\]|(\\["'abfnrtv\\]))'" "'\\(([0-3]?[0-7]{1,2}))'" "'\\x[0-9A-Fa-f]{1,2}'"
+
+color brightyellow "<[^=       ]*>" ""(\\.|[^"])*""
+
+## This string is VERY resource intensive!
+color brightyellow start=""(\\.|[^"])*\\[[:space:]]*$" end="^(\\.|[^"])*""
+
+## Comment highlighting
+color brightblue "//.*"
+color brightblue start="/\*" end="\*/"
+
+## Trailing whitespace
+color ,green "[[:space:]]+$"
diff --git a/test.c b/test.c
index 97ac09f9a6d8f8cc6e42a807370a5ef1789a4456..107140e5cc7697f4a7e5e346e7334f12dd7a53cb 100644 (file)
--- a/test.c
+++ b/test.c
@@ -176,8 +176,8 @@ int task_pclose(FILE **handles) {
     }
 #endif /*! _WIN32 */
 
-#define TASK_COMPILE 0
-#define TASK_EXECUTE 1
+#define TASK_COMPILE    0
+#define TASK_EXECUTE    1
 /*
  * Task template system:
  *  templates are rules for a specific test, used to create a "task" that
@@ -269,6 +269,7 @@ typedef struct {
  */
 bool task_template_generate(task_template_t *tmpl, char tag, const char *file, size_t line, char *value, size_t *pad) {
     size_t desclen = 0;
+    size_t filelen = 0;
     char **destval = NULL;
 
     if (!tmpl)
@@ -315,7 +316,7 @@ bool task_template_generate(task_template_t *tmpl, char tag, const char *file, s
     if (strchr(value, '\n'))
         *strrchr(value, '\n')='\0';
     else /* cppcheck: possible nullpointer dereference */
-        abort();
+        exit(EXIT_FAILURE);
 
     /*
      * Now allocate and set the actual value for the specific tag. Which
@@ -333,6 +334,9 @@ bool task_template_generate(task_template_t *tmpl, char tag, const char *file, s
             pad[0] = desclen;
     }
 
+    if ((filelen = strlen(file)) > pad[2])
+        pad[2] = filelen;
+
     return true;
 }
 
@@ -428,7 +432,7 @@ bool task_template_parse(const char *file, task_template_t *tmpl, FILE *fp, size
                 if (strrchr(value, '\n'))
                     *strrchr(value, '\n')='\0';
                 else /* cppcheck: possible null pointer dereference */
-                    abort();
+                    exit(EXIT_FAILURE);
 
                 vec_push(tmpl->comparematch, util_strdup(value));
 
@@ -563,7 +567,13 @@ task_template_t *task_template_compile(const char *file, const char *dir, size_t
             con_err("template compile warning: %s erroneous tag `E:` when only failing\n", file);
         if (tmpl->comparematch)
             con_err("template compile warning: %s erroneous tag `M:` when only failing\n", file);
-        goto success;
+    } else if (!strcmp(tmpl->proceduretype, "-pp")) {
+        if (tmpl->executeflags)
+            con_err("template compile warning: %s erroneous tag `E:` when only preprocessing\n", file);
+        if (!tmpl->comparematch) {
+            con_err("template compile error: %s missing `M:` tag (use `$null` for exclude)\n", file);
+            goto failure;
+        }
     } else {
         con_err("template compile error: %s invalid procedure type: %s\n", file, tmpl->proceduretype);
         goto failure;
@@ -644,10 +654,10 @@ bool task_propagate(const char *curdir, size_t *pad, const char *defs) {
     char             buffer[4096];
     size_t           found = 0;
 
-    dir = opendir(curdir);
+    dir = fs_dir_open(curdir);
 
-    while ((files = readdir(dir))) {
-        snprintf(buffer,   sizeof(buffer), "%s/%s", curdir, files->d_name);
+    while ((files = fs_dir_read(dir))) {
+        snprintf(buffer, sizeof(buffer), "%s/%s", curdir, files->d_name);
 
         if (stat(buffer, &directory) == -1) {
             con_err("internal error: stat failed, aborting\n");
@@ -679,7 +689,8 @@ bool task_propagate(const char *curdir, size_t *pad, const char *defs) {
              * Generate a temportary file name for the output binary
              * so we don't trample over an existing one.
              */
-            tmpl->tempfilename = tempnam(curdir, "TMPDAT");
+            tmpl->tempfilename = NULL;
+            util_asprintf(&tmpl->tempfilename, "%s/TMPDAT.%s", curdir, files->d_name);
 
             /*
              * Additional QCFLAGS enviroment variable may be used
@@ -693,45 +704,66 @@ bool task_propagate(const char *curdir, size_t *pad, const char *defs) {
              * which will be refered to with a handle in the task for
              * reading the data from the pipe.
              */
-            if (qcflags) {
-                if (tmpl->testflags && !strcmp(tmpl->testflags, "-no-defs")) {
-                    snprintf(buf, sizeof(buf), "%s %s/%s %s %s -o %s",
-                        task_bins[TASK_COMPILE],
-                        curdir,
-                        tmpl->sourcefile,
-                        qcflags,
-                        tmpl->compileflags,
-                        tmpl->tempfilename
-                    );
+            if (strcmp(tmpl->proceduretype, "-pp")) {
+                if (qcflags) {
+                    if (tmpl->testflags && !strcmp(tmpl->testflags, "-no-defs")) {
+                        snprintf(buf, sizeof(buf), "%s %s/%s %s %s -o %s",
+                            task_bins[TASK_COMPILE],
+                            curdir,
+                            tmpl->sourcefile,
+                            qcflags,
+                            tmpl->compileflags,
+                            tmpl->tempfilename
+                        );
+                    } else {
+                        snprintf(buf, sizeof(buf), "%s %s/%s %s/%s %s %s -o %s",
+                            task_bins[TASK_COMPILE],
+                            curdir,
+                            defs,
+                            curdir,
+                            tmpl->sourcefile,
+                            qcflags,
+                            tmpl->compileflags,
+                            tmpl->tempfilename
+                        );
+                    }
                 } else {
-                    snprintf(buf, sizeof(buf), "%s %s/%s %s/%s %s %s -o %s",
-                        task_bins[TASK_COMPILE],
-                        curdir,
-                        defs,
-                        curdir,
-                        tmpl->sourcefile,
-                        qcflags,
-                        tmpl->compileflags,
-                        tmpl->tempfilename
-                    );
+                    if (tmpl->testflags && !strcmp(tmpl->testflags, "-no-defs")) {
+                        snprintf(buf, sizeof(buf), "%s %s/%s %s -o %s",
+                            task_bins[TASK_COMPILE],
+                            curdir,
+                            tmpl->sourcefile,
+                            tmpl->compileflags,
+                            tmpl->tempfilename
+                        );
+                    } else {
+                        snprintf(buf, sizeof(buf), "%s %s/%s %s/%s %s -o %s",
+                            task_bins[TASK_COMPILE],
+                            curdir,
+                            defs,
+                            curdir,
+                            tmpl->sourcefile,
+                            tmpl->compileflags,
+                            tmpl->tempfilename
+                        );
+                    }
                 }
             } else {
+                /* Preprocessing (qcflags mean shit all here we don't allow them) */
                 if (tmpl->testflags && !strcmp(tmpl->testflags, "-no-defs")) {
-                    snprintf(buf, sizeof(buf), "%s %s/%s %s -o %s",
+                    snprintf(buf, sizeof(buf), "%s -E %s/%s -o %s",
                         task_bins[TASK_COMPILE],
                         curdir,
                         tmpl->sourcefile,
-                        tmpl->compileflags,
                         tmpl->tempfilename
                     );
                 } else {
-                    snprintf(buf, sizeof(buf), "%s %s/%s %s/%s %s -o %s",
+                    snprintf(buf, sizeof(buf), "%s -E %s/%s %s/%s -o %s",
                         task_bins[TASK_COMPILE],
                         curdir,
                         defs,
                         curdir,
                         tmpl->sourcefile,
-                        tmpl->compileflags,
                         tmpl->tempfilename
                     );
                 }
@@ -777,7 +809,7 @@ bool task_propagate(const char *curdir, size_t *pad, const char *defs) {
         found
     );
 
-    closedir(dir);
+    fs_dir_close(dir);
     return success;
 }
 
@@ -790,9 +822,9 @@ void task_precleanup(const char *curdir) {
     struct dirent   *files;
     char             buffer[4096];
 
-    dir = opendir(curdir);
+    dir = fs_dir_open(curdir);
 
-    while ((files = readdir(dir))) {
+    while ((files = fs_dir_read(dir))) {
         if (strstr(files->d_name, "TMP")     ||
             strstr(files->d_name, ".stdout") ||
             strstr(files->d_name, ".stderr"))
@@ -805,7 +837,7 @@ void task_precleanup(const char *curdir) {
         }
     }
 
-    closedir(dir);
+    fs_dir_close(dir);
 }
 
 void task_destroy(void) {
@@ -854,40 +886,50 @@ void task_destroy(void) {
 /*
  * This executes the QCVM task for a specificly compiled progs.dat
  * using the template passed into it for call-flags and user defined
- * messages.
+ * messages IF the procedure type is -execute, otherwise it matches
+ * the preprocessor output. 
  */
-bool task_execute(task_template_t *tmpl, char ***line) {
+bool task_trymatch(task_template_t *tmpl, char ***line) {
     bool     success = true;
     FILE    *execute;
     char     buffer[4096];
     memset  (buffer,0,sizeof(buffer));
 
-    /*
-     * Drop the execution flags for the QCVM if none where
-     * actually specified.
-     */
-    if (!strcmp(tmpl->executeflags, "$null")) {
-        snprintf(buffer,  sizeof(buffer), "%s %s",
-            task_bins[TASK_EXECUTE],
-            tmpl->tempfilename
+    if (strcmp(tmpl->proceduretype, "-pp")) {
+        /*
+         * Drop the execution flags for the QCVM if none where
+         * actually specified.
+         */
+        if (!strcmp(tmpl->executeflags, "$null")) {
+            snprintf(buffer,  sizeof(buffer), "%s %s",
+                task_bins[TASK_EXECUTE],
+                tmpl->tempfilename
+            );
+        } else {
+            snprintf(buffer,  sizeof(buffer), "%s %s %s",
+                task_bins[TASK_EXECUTE],
+                tmpl->executeflags,
+                tmpl->tempfilename
+            );
+        }
+
+        util_debug("TEST", "executing qcvm: `%s` [%s]\n",
+            tmpl->description,
+            buffer
         );
+
+        execute = popen(buffer, "r");
+        if (!execute)
+            return false;
     } else {
-        snprintf(buffer,  sizeof(buffer), "%s %s %s",
-            task_bins[TASK_EXECUTE],
-            tmpl->executeflags,
-            tmpl->tempfilename
-        );
+        /*
+         * we're preprocessing, which means we need to read int
+         * the produced file and do some really weird shit.
+         */
+        if (!(execute = fs_file_open(tmpl->tempfilename, "r")))
+            return false;
     }
 
-    util_debug("TEST", "executing qcvm: `%s` [%s]\n",
-        tmpl->description,
-        buffer
-    );
-
-    execute = popen(buffer, "r");
-    if (!execute)
-        return false;
-
     /*
      * Now lets read the lines and compare them to the matches we expect
      * and handle accordingly.
@@ -913,6 +955,13 @@ bool task_execute(task_template_t *tmpl, char ***line) {
             if  (strrchr(data, '\n'))
                 *strrchr(data, '\n') = '\0';
 
+            /*
+             * If data is just null now, that means the line was an empty
+             * one and for that, we just ignore it.
+             */
+            if (!*data)
+                continue;
+
             if (vec_size(tmpl->comparematch) > compare) {
                 if (strcmp(data, tmpl->comparematch[compare++]))
                     success = false;
@@ -933,10 +982,25 @@ bool task_execute(task_template_t *tmpl, char ***line) {
         mem_d(data);
         data = NULL;
     }
-    pclose(execute);
+
+    if (strcmp(tmpl->proceduretype, "-pp"))
+        pclose(execute);
+    else
+        fs_file_close(execute);
+
     return success;
 }
 
+const char *task_type(task_template_t *tmpl) {
+    if (!strcmp(tmpl->proceduretype, "-pp"))
+        return "type: preprocessor";
+    if (!strcmp(tmpl->proceduretype, "-execute"))
+        return "type: execution";
+    if (!strcmp(tmpl->proceduretype, "-compile"))
+        return "type: compile";
+    return "type: fail";
+}
+
 /*
  * This schedualizes all tasks and actually runs them individually
  * this is generally easy for just -compile variants.  For compile and
@@ -945,6 +1009,7 @@ bool task_execute(task_template_t *tmpl, char ***line) {
  */
 #include <math.h>
 void task_schedualize(size_t *pad) {
+    char   space[2][64];
     bool   execute  = false;
     char  *data     = NULL;
     char **match    = NULL;
@@ -952,15 +1017,21 @@ void task_schedualize(size_t *pad) {
     size_t i        = 0;
     size_t j        = 0;
 
+    snprintf(space[0], sizeof(space[0]), "%d", (int)vec_size(task_tasks));
+
     for (; i < vec_size(task_tasks); i++) {
-        con_out("test #%u %*s", i + 1, (int)log10(vec_size(task_tasks)) - (int)(log10(i + 1)), "");
+        memset(space[1], 0, sizeof(space[1]));
+        snprintf(space[1], sizeof(space[1]), "%d", (int)(i + 1));
+        
+        con_out("test #%u %*s", i + 1, strlen(space[0]) - strlen(space[1]), "");
 
         util_debug("TEST", "executing task: %d: %s\n", i, task_tasks[i].tmpl->description);
         /*
          * Generate a task from thin air if it requires execution in
          * the QCVM.
          */
-        execute = !!(!strcmp(task_tasks[i].tmpl->proceduretype, "-execute"));
+        execute = !! (!strcmp(task_tasks[i].tmpl->proceduretype, "-execute")) ||
+                     (!strcmp(task_tasks[i].tmpl->proceduretype, "-pp"));
 
         /*
          * We assume it compiled before we actually compiled :).  On error
@@ -979,8 +1050,6 @@ void task_schedualize(size_t *pad) {
                 task_tasks[i].compiled = false;
                 execute                = false;
             }
-
-            fs_file_flush(task_tasks[i].stdoutlog);
         }
         while (fs_file_getline(&data, &size, task_tasks[i].runhandles[2]) != EOF) {
             /*
@@ -997,25 +1066,26 @@ void task_schedualize(size_t *pad) {
             }
 
             fs_file_puts (task_tasks[i].stderrlog, data);
-            fs_file_flush(task_tasks[i].stdoutlog);
         }
 
         if (!task_tasks[i].compiled && strcmp(task_tasks[i].tmpl->proceduretype, "-fail")) {
-            con_err("failure: `%s` (failed to compile) see %s.stdout and %s.stderr [%s]\n",
+            con_out("failure:   `%s` %*s %*s\n",
                 task_tasks[i].tmpl->description,
-                task_tasks[i].tmpl->tempfilename,
-                task_tasks[i].tmpl->tempfilename,
-                task_tasks[i].tmpl->rulesfile
+                (pad[0] + pad[1] - strlen(task_tasks[i].tmpl->description)) + (strlen(task_tasks[i].tmpl->rulesfile) - pad[1]),
+                task_tasks[i].tmpl->rulesfile,
+                (pad[1] + pad[2] - strlen(task_tasks[i].tmpl->rulesfile)) + (strlen("(failed to compile)") - pad[2]),
+                "(failed to compile)"
             );
             continue;
         }
 
         if (!execute) {
-            con_out("succeeded: `%s` %*s\n",
+            con_out("succeeded: `%s` %*s %*s\n",
                 task_tasks[i].tmpl->description,
-                (pad[0] + pad[1] - strlen(task_tasks[i].tmpl->description)) +
-                (strlen(task_tasks[i].tmpl->rulesfile) - pad[1]),
-                task_tasks[i].tmpl->rulesfile
+                (pad[0] + pad[1] - strlen(task_tasks[i].tmpl->description)) + (strlen(task_tasks[i].tmpl->rulesfile) - pad[1]),
+                task_tasks[i].tmpl->rulesfile,
+                (pad[1] + pad[2] - strlen(task_tasks[i].tmpl->rulesfile)) + (strlen(task_type(task_tasks[i].tmpl)) - pad[2]),
+                task_type(task_tasks[i].tmpl)
                 
             );
             continue;
@@ -1023,14 +1093,24 @@ void task_schedualize(size_t *pad) {
 
         /*
          * If we made it here that concludes the task is to be executed
-         * in the virtual machine.
+         * in the virtual machine (or the preprocessor output needs to
+         * be matched). 
          */
-        if (!task_execute(task_tasks[i].tmpl, &match)) {
+        if (!task_trymatch(task_tasks[i].tmpl, &match)) {
             size_t d = 0;
 
-            con_err("failure: `%s` (invalid results from execution) [%s]\n",
+            con_out("failure:   `%s` %*s %*s\n",
                 task_tasks[i].tmpl->description,
-                task_tasks[i].tmpl->rulesfile
+                (pad[0] + pad[1] - strlen(task_tasks[i].tmpl->description)) + (strlen(task_tasks[i].tmpl->rulesfile) - pad[1]),
+                task_tasks[i].tmpl->rulesfile,
+                (pad[1] + pad[2] - strlen(task_tasks[i].tmpl->rulesfile)) + (strlen(
+                    (strcmp(task_tasks[i].tmpl->proceduretype, "-pp"))
+                        ? "(invalid results from execution)"
+                        : "(invalid results from preprocessing)"
+                ) - pad[2]),
+                (strcmp(task_tasks[i].tmpl->proceduretype, "-pp"))
+                    ? "(invalid results from execution)"
+                    : "(invalid results from preprocessing)"
             );
 
             /*
@@ -1038,7 +1118,7 @@ void task_schedualize(size_t *pad) {
              * handler for the all the given matches in the template file and
              * what was actually returned from executing.
              */
-            con_err("    Expected From %u Matches: (got %u Matches)\n",
+            con_out("    Expected From %u Matches: (got %u Matches)\n",
                 vec_size(task_tasks[i].tmpl->comparematch),
                 vec_size(match)
             );
@@ -1046,10 +1126,10 @@ void task_schedualize(size_t *pad) {
                 char  *select = task_tasks[i].tmpl->comparematch[d];
                 size_t length = 40 - strlen(select);
 
-                con_err("        Expected: \"%s\"", select);
+                con_out("        Expected: \"%s\"", select);
                 while (length --)
-                    con_err(" ");
-                con_err("| Got: \"%s\"\n", (d >= vec_size(match)) ? "<<nothing else to compare>>" : match[d]);
+                    con_out(" ");
+                con_out("| Got: \"%s\"\n", (d >= vec_size(match)) ? "<<nothing else to compare>>" : match[d]);
             }
 
             /*
@@ -1059,7 +1139,7 @@ void task_schedualize(size_t *pad) {
              */  
             if (vec_size(match) > vec_size(task_tasks[i].tmpl->comparematch)) {
                 for (d = 0; d < vec_size(match) - vec_size(task_tasks[i].tmpl->comparematch); d++) {
-                    con_err("        Expected: Nothing                                   | Got: \"%s\"\n",
+                    con_out("        Expected: Nothing                                   | Got: \"%s\"\n",
                         match[d + vec_size(task_tasks[i].tmpl->comparematch)]
                     );
                 }
@@ -1075,11 +1155,12 @@ void task_schedualize(size_t *pad) {
             mem_d(match[j]);
         vec_free(match);
 
-        con_out("succeeded: `%s` %*s\n",
+        con_out("succeeded: `%s` %*s %*s\n",
             task_tasks[i].tmpl->description,
-            (pad[0] + pad[1] - strlen(task_tasks[i].tmpl->description)) +
-            (strlen(task_tasks[i].tmpl->rulesfile) - pad[1]),
-            task_tasks[i].tmpl->rulesfile
+            (pad[0] + pad[1] - strlen(task_tasks[i].tmpl->description)) + (strlen(task_tasks[i].tmpl->rulesfile) - pad[1]),
+            task_tasks[i].tmpl->rulesfile,
+            (pad[1] + pad[2] - strlen(task_tasks[i].tmpl->rulesfile)) + (strlen(task_type(task_tasks[i].tmpl))- pad[2]),
+            task_type(task_tasks[i].tmpl)
             
         );
     }
@@ -1104,7 +1185,8 @@ GMQCC_WARN bool test_perform(const char *curdir, const char *defs) {
     static const char *default_defs = "defs.qh";
 
     size_t pad[] = {
-        0, 0
+        /* test ### [succeed/fail]: `description`      [tests/template.tmpl]     [type] */
+                    0,                                 0,                        0
     };
 
     /*
index 830692c3ea3f9cb59653cf10c816c68ec33d463a..772797a13f43a2e2a5330f3d1c1920740e74d582 100644 (file)
@@ -16,3 +16,4 @@ string (...)            strcat    = #10;
 float  (string, string) strcmp    = #11;
 vector (vector)         normalize = #12;
 float  (float)          sqrt      = #13;
+float  (float)          floor     = #14;
index da08ceee128aa2a0da962a5f9c1f852ed89b51a5..42853d85ffc3886218c3d24319484c8daea3d890 100644 (file)
@@ -1,4 +1,4 @@
-void(string, ...)   print  = #1;enum {
+enum {
     // this behaviour is confusing, but I like that
     // we support it.
     __ = (__ - 1),
@@ -27,6 +27,20 @@ enum {
     N
 };
 
+enum : flag {
+    F1, /* = 1 << 1 */
+    F2, /* = 1 << 2 */
+    F3  /* = 1 << 3 */
+};
+
+/* reversed enumeration */
+enum : reverse {
+    R1, // 3
+    R2, // 2
+    R3, // 1
+    R4  // 0
+};
+
 void main() {
     print(ftos(A), "\n");
     print(ftos(B), "\n");
@@ -42,4 +56,13 @@ void main() {
     print(ftos(L), "\n");
     print(ftos(M), "\n");
     print(ftos(N), "\n");
+
+    print(ftos(F1), "\n");
+    print(ftos(F2), "\n");
+    print(ftos(F3), "\n");
+
+    print(ftos(R1), "\n");
+    print(ftos(R2), "\n");
+    print(ftos(R3), "\n");
+    print(ftos(R4), "\n");
 };
index 28cbd57495046a31567a165854a0afba808eb9ae..7647683173c815075a86b6c63469d518e14ce6c0 100644 (file)
@@ -16,3 +16,10 @@ M: 10
 M: 11
 M: 12
 M: 13
+M: 2
+M: 4
+M: 8
+M: 3
+M: 2
+M: 1
+M: 0
diff --git a/tests/exponentiation.qc b/tests/exponentiation.qc
new file mode 100644 (file)
index 0000000..5b8f24e
--- /dev/null
@@ -0,0 +1,14 @@
+float pow(float x, float y) {
+    return __builtin_pow(x, y);
+}
+
+void main() {
+    float hundy = pow(10, 2); // 10^2 == 100
+    print(ftos(hundy), "\n");      // prints: 100
+
+    hundy -= 90; // 100-90 = 10
+    print(ftos(hundy ** 2), "\n"); // prints: 100
+
+    hundy = 10.0f;
+    print(ftos(__builtin_exp(hundy)), "\n"); // prints: 22026.5
+}
diff --git a/tests/exponentiation.tmpl b/tests/exponentiation.tmpl
new file mode 100644 (file)
index 0000000..0aa7f85
--- /dev/null
@@ -0,0 +1,9 @@
+# used to test the builtins
+I: exponentiation.qc
+D: test exponentiation operator and __builtin_pow
+T: -execute
+C: -std=gmqcc
+E: $null
+M: 100
+M: 100
+M: 22026.5
diff --git a/tests/ppcat.qc b/tests/ppcat.qc
new file mode 100644 (file)
index 0000000..ca92060
--- /dev/null
@@ -0,0 +1,13 @@
+#define CAT(X, Y) X##Y
+CAT(hello, world)
+
+#define REDIR(X, Y) CAT(X, Y)
+REDIR(CAT(hello, world), CAT(world, hello))
+
+#define SCONS(X, ...) REDIR(X, __VA_ARGS__)
+SCONS(hello, world)
+
+#define FOO(X) X##X
+#define BAR(X) FOO(X)##FOO(X)
+
+REDIR(BAR(hello), BAR(world))
diff --git a/tests/ppcat.tmpl b/tests/ppcat.tmpl
new file mode 100644 (file)
index 0000000..0844a8d
--- /dev/null
@@ -0,0 +1,10 @@
+I: ppcat.qc
+D: test preprocessor concatenation
+T: -pp
+C: -std=gmqcc
+F: -no-defs
+M: helloworld
+M: helloworldworldhello
+M: helloworld
+M: hellohellohellohelloworldworldworldworld
+
index 2c4be19093915f9d53db4eb5293e6ebf2ecd566c..4397431687dc5b24dbb6cecfefeac9966f6c1160 100644 (file)
@@ -1,4 +1,4 @@
 I: uninit.qc
-D: catch another case of unused vector accesses - should fail
+D: catch another case of unused vector accesses
 T: -fail
 C: -std=fteqcc -Wall -Werror -DUNINIT
diff --git a/utf8.c b/utf8.c
index 209317b03c38760a94cbbe9443803d48dd45c828..a10a11aaa8cab039b6efda1a2b4ed457f0c733eb 100644 (file)
--- a/utf8.c
+++ b/utf8.c
@@ -198,7 +198,7 @@ uchar_t u8_getchar(const char *_s, const char **_end)
 
     if (!u8_analyze(_s, &st, &ln, &ch, 0x10))
         ch = 0;
-    if (_end)
+    else if (_end)
         *_end = _s + st + ln;
     return ch;
 }
@@ -210,7 +210,7 @@ uchar_t u8_getnchar(const char *_s, const char **_end, size_t _maxlen)
 
     if (!u8_analyze(_s, &st, &ln, &ch, _maxlen))
         ch = 0;
-    if (_end)
+    else if (_end)
         *_end = _s + st + ln;
     return ch;
 }
diff --git a/util.c b/util.c
index 004b69b0d5daef6215a2e85d16a408607d8ea07c..73c5335e08e49a3d87929a75351bdfd5d3315ad5 100644 (file)
--- a/util.c
+++ b/util.c
@@ -30,6 +30,8 @@ uint64_t mem_ab = 0;
 uint64_t mem_db = 0;
 uint64_t mem_at = 0;
 uint64_t mem_dt = 0;
+uint64_t mem_pk = 0;
+uint64_t mem_hw = 0;
 
 struct memblock_t {
     const char  *file;
@@ -39,6 +41,12 @@ struct memblock_t {
     struct memblock_t *prev;
 };
 
+#define PEAK_MEM             \
+    do {                     \
+        if (mem_hw > mem_pk) \
+            mem_pk = mem_hw; \
+    } while (0)
+
 static struct memblock_t *mem_start = NULL;
 
 void *util_memory_a(size_t byte, unsigned int line, const char *file) {
@@ -56,6 +64,9 @@ void *util_memory_a(size_t byte, unsigned int line, const char *file) {
 
     mem_at++;
     mem_ab += info->byte;
+    mem_hw += info->byte;
+
+    PEAK_MEM;
 
     return data;
 }
@@ -67,6 +78,7 @@ void util_memory_d(void *ptrn) {
     info = ((struct memblock_t*)ptrn - 1);
 
     mem_db += info->byte;
+    mem_hw -= info->byte;
     mem_dt++;
 
     if (info->prev)
@@ -122,51 +134,111 @@ void *util_memory_r(void *ptrn, size_t byte, unsigned int line, const char *file
     mem_start = newinfo;
 
     mem_ab -= oldinfo->byte;
+    mem_hw -= oldinfo->byte;
     mem_ab += newinfo->byte;
+    mem_hw += newinfo->byte;
+
+    PEAK_MEM;
 
     free(oldinfo);
 
     return newinfo+1;
 }
 
+static void util_dumpmem(struct memblock_t *memory, uint16_t cols) {
+    uint32_t i, j;
+    for (i = 0; i < memory->byte + ((memory->byte % cols) ? (cols - memory->byte % cols) : 0); i++) {
+        if (i % cols == 0)    con_out("    0x%06X: ", i);
+        if (i < memory->byte) con_out("%02X "   , 0xFF & ((char*)(memory + 1))[i]);
+        else                  con_out("    ");
+
+        if ((uint16_t)(i % cols) == (cols - 1)) {
+            for (j = i - (cols - 1); j <= i; j++) {
+                con_out("%c",
+                    (j >= memory->byte)
+                        ? ' '
+                        : (isprint(((char*)(memory + 1))[j]))
+                            ? 0xFF & ((char*)(memory + 1)) [j]
+                            : '.'
+                );
+            }
+            con_out("\n");
+        }
+    }
+}
+
 void util_meminfo() {
     struct memblock_t *info;
 
-    if (!OPTS_OPTION_BOOL(OPTION_MEMCHK))
-        return;
 
-    for (info = mem_start; info; info = info->next) {
-        util_debug("MEM", "lost:       % 8u (bytes) at %s:%u\n",
-            info->byte,
-            info->file,
-            info->line);
+    if (OPTS_OPTION_BOOL(OPTION_DEBUG)) {
+        for (info = mem_start; info; info = info->next) {
+            con_out("lost: %u (bytes) at %s:%u\n",
+                info->byte,
+                info->file,
+                info->line);
+
+            util_dumpmem(info, OPTS_OPTION_U16(OPTION_MEMDUMPCOLS));
+        }
     }
 
-    util_debug("MEM", "Memory information:\n\
-        Total allocations:   %llu\n\
-        Total deallocations: %llu\n\
-        Total allocated:     %llu (bytes)\n\
-        Total deallocated:   %llu (bytes)\n\
-        Leaks found:         lost %llu (bytes) in %d allocations\n",
-            mem_at,   mem_dt,
-            mem_ab,   mem_db,
-           (mem_ab -  mem_db),
-           (mem_at -  mem_dt)
-    );
+    if (OPTS_OPTION_BOOL(OPTION_DEBUG) ||
+        OPTS_OPTION_BOOL(OPTION_MEMCHK)) {
+        con_out("Memory information:\n\
+            Total allocations:   %llu\n\
+            Total deallocations: %llu\n\
+            Total allocated:     %f (MB)\n\
+            Total deallocated:   %f (MB)\n\
+            Total peak memory:   %f (MB)\n\
+            Total leaked memory: %f (MB) in %llu allocations\n",
+                mem_at,
+                mem_dt,
+                (float)(mem_ab)           / 1048576.0f,
+                (float)(mem_db)           / 1048576.0f,
+                (float)(mem_pk)           / 1048576.0f,
+                (float)(mem_ab -  mem_db) / 1048576.0f,
+
+                /* could be more clever */
+                (mem_at -  mem_dt)
+        );
+    }
 }
 
 /*
  * Some string utility functions, because strdup uses malloc, and we want
  * to track all memory (without replacing malloc).
  */
-char *util_strdup(const char *s) {
+char *_util_Estrdup(const char *s, const char *file, size_t line) {
     size_t  len = 0;
     char   *ptr = NULL;
 
+    /* in case of -DNOTRACK */
+    (void)file;
+    (void)line;
+
     if (!s)
         return NULL;
 
-    if ((len = strlen(s)) && (ptr = (char*)mem_a(len+1))) {
+    if ((len = strlen(s)) && (ptr = (char*)mem_af(len+1, line, file))) {
+        memcpy(ptr, s, len);
+        ptr[len] = '\0';
+    }
+    return ptr;
+}
+
+char *_util_Estrdup_empty(const char *s, const char *file, size_t line) {
+    size_t  len = 0;
+    char   *ptr = NULL;
+
+    /* in case of -DNOTRACK */
+    (void)file;
+    (void)line;
+
+    if (!s)
+        return NULL;
+
+    len = strlen(s);
+    if ((ptr = (char*)mem_af(len+1, line, file))) {
         memcpy(ptr, s, len);
         ptr[len] = '\0';
     }
@@ -253,7 +325,7 @@ void util_endianswap(void *_data, size_t length, unsigned int typesize) {
                 util_swap64((uint32_t*)_data, length>>3);
                 return;
 
-            default: abort(); /* please blow the fuck up! */
+            default: exit(EXIT_FAILURE); /* please blow the fuck up! */
         }
 #   endif
 #endif
@@ -425,7 +497,7 @@ hash_node_t *_util_htnewpair(const char *key, void *value) {
     if (!(node = (hash_node_t*)mem_a(sizeof(hash_node_t))))
         return NULL;
 
-    if (!(node->key = util_strdup(key))) {
+    if (!(node->key = util_strdupe(key))) {
         mem_d(node);
         return NULL;
     }
@@ -548,7 +620,7 @@ void *code_util_str_htgeth(hash_table_t *ht, const char *key, size_t bin) {
  * Free all allocated data in a hashtable, this is quite the amount
  * of work.
  */
-void util_htdel(hash_table_t *ht) {
+void util_htrem(hash_table_t *ht, void (*callback)(void *data)) {
     size_t i = 0;
     for (; i < ht->size; i++) {
         hash_node_t *n = ht->table[i];
@@ -558,6 +630,8 @@ void util_htdel(hash_table_t *ht) {
         while (n) {
             if (n->key)
                 mem_d(n->key);
+            if (callback)
+                callback(n->value);
             p = n;
             n = n->next;
             mem_d(p);
@@ -569,145 +643,32 @@ void util_htdel(hash_table_t *ht) {
     mem_d(ht);
 }
 
-/*
- * A basic implementation of a hash-set.  Unlike a hashtable, a hash
- * set doesn't maintain key-value pairs.  It simply maintains a key
- * that can be set, removed, and checked for.
- *
- * See EXPOSED interface comment below 
- */
-#define GMQCC_HASHSET_PRIME0 0x0049
-#define GMQCC_HASHSET_PRIME1 0x1391
-
-static int util_hsput(hash_set_t *set, void *item) {
-    size_t hash = (size_t)item; /* shouldn't drop the bits */
-    size_t iter;
-
-    /* a == 0 || a == 1 */
-    if (hash >> 1)
-        return -1;
-
-    iter = set->mask & (GMQCC_HASHSET_PRIME0 * hash);
-
-    /* while (set->items[iter] != 0 && set->items[iter] != 1) */
-    while  (!(set->items[iter] >> 1)) {
-        if (set->items[iter] == hash)
-            return 0;
+void util_htrmh(hash_table_t *ht, const char *key, size_t bin, void (*cb)(void*)) {
+    hash_node_t **pair = &ht->table[bin];
+    hash_node_t *tmp;
 
-        iter = set->mask & (iter + GMQCC_HASHSET_PRIME1);
-    }
-
-    set->total ++;
-    set->items[iter] = hash;
-
-    return 1;
-}
+    while (*pair && (*pair)->key && strcmp(key, (*pair)->key) > 0)
+        pair = &(*pair)->next;
 
-static void util_hsupdate(hash_set_t *set) {
-    size_t *old;
-    size_t  end;
-    size_t  itr;
-
-    /* time to rehash? */
-    if ((float)set->total >= (size_t)((double)set->capacity * 0.85)) {
-        old = set->items;
-        end = set->capacity;
-
-        set->bits ++;
-        set->capacity = (size_t)(1 << set->bits);
-        set->mask     = set->capacity - 1;
-        set->items    = (size_t*)mem_a(set->capacity * sizeof(size_t));
-        set->total    = 0;
-
-        /*assert(set->items);*/
-
-        /*
-         * this shouldn't be slow?  if so unroll it a little perhaps
-         * (shouldn't be though)
-         */
-        for (itr = 0; itr < end; itr++)
-            util_hsput(set, (void*)old[itr]);
-
-        mem_d(old);
-    }
-}
-
-/*
- * EXPOSED interface: all of these functions are exposed to the outside
- * for use. The stuff above is static because it's the "internal" mechanics
- * for syncronizing the set for updating, and putting data into the set.
- */   
-int util_hsadd(hash_set_t *set, void *item) {
-    int run = util_hsput(set, item); /* inlined */
-    util_hsupdate(set);
-
-    return run;
-}
-
-/* remove item in set */
-int util_hsrem(hash_set_t *set, void *item) {
-    size_t hash = (size_t)item;
-    size_t iter = set->mask & (GMQCC_HASHSET_PRIME0 * hash);
-
-    while  (set->items[iter]) {
-        if (set->items[iter] == hash) {
-            set->items[iter] =  1;
-            set->total       --;
-
-            return 1;
-        }
-        iter = set->mask & (iter + GMQCC_HASHSET_PRIME1);
-    }
-
-    return 0;
-}
-
-/* check if item is set */
-int util_hshas(hash_set_t *set, void *item) {
-    size_t hash = (size_t)item;
-    size_t iter = set->mask & (GMQCC_HASHSET_PRIME0 * hash);
-
-    while  (set->items[iter]) {
-        if (set->items[iter] == hash)
-            return 1;
+    tmp = *pair;
+    if (!tmp || !tmp->key || strcmp(key, tmp->key) != 0)
+        return;
 
-        iter = set->mask & (iter + GMQCC_HASHSET_PRIME1);
-    }
+    if (cb)
+        (*cb)(tmp->value);
 
-    return 0;
+    *pair = tmp->next;
+    mem_d(tmp->key);
+    mem_d(tmp);
 }
 
-hash_set_t *util_hsnew(void) {
-    hash_set_t *set;
-
-    if (!(set = (hash_set_t*)mem_a(sizeof(hash_set_t))))
-        return NULL;
-
-    set->bits     = 3;
-    set->total    = 0;
-    set->capacity = (size_t)(1 << set->bits);
-    set->mask     = set->capacity - 1;
-    set->items    = (size_t*)mem_a(set->capacity * sizeof(size_t));
-
-    if (!set->items) {
-        util_hsdel(set);
-        return NULL;
-    }
-
-    return set;
+void util_htrm(hash_table_t *ht, const char *key, void (*cb)(void*)) {
+    util_htrmh(ht, key, util_hthash(ht, key), cb);
 }
 
-void util_hsdel(hash_set_t *set) {
-    if (!set) return;
-
-    if (set->items)
-        mem_d(set->items);
-
-    mem_d(set);
+void util_htdel(hash_table_t *ht) {
+    util_htrem(ht, NULL);
 }
-#undef GMQCC_HASHSET_PRIME0
-#undef GMQCC_HASHSET_PRIME1
-
 
 /*
  * Portable implementation of vasprintf/asprintf. Assumes vsnprintf