adds UCX + changes how the input file is read (uses an consecutive memory area now)

Tue, 23 Aug 2016 13:49:38 +0200

author
Mike Becker <universe@uap-core.de>
date
Tue, 23 Aug 2016 13:49:38 +0200
changeset 39
ac35daceb24c
parent 38
77c158821738
child 40
903b46fc4214

adds UCX + changes how the input file is read (uses an consecutive memory area now)

Makefile file | annotate | diff | comparison | revisions
clang.mk file | annotate | diff | comparison | revisions
gcc.mk file | annotate | diff | comparison | revisions
src/c2html.c file | annotate | diff | comparison | revisions
src/c2html.h file | annotate | diff | comparison | revisions
src/ccodegen.c file | annotate | diff | comparison | revisions
src/javacodegen.c file | annotate | diff | comparison | revisions
src/ucx/allocator.c file | annotate | diff | comparison | revisions
src/ucx/allocator.h file | annotate | diff | comparison | revisions
src/ucx/buffer.c file | annotate | diff | comparison | revisions
src/ucx/buffer.h file | annotate | diff | comparison | revisions
src/ucx/list.c file | annotate | diff | comparison | revisions
src/ucx/list.h file | annotate | diff | comparison | revisions
src/ucx/string.c file | annotate | diff | comparison | revisions
src/ucx/string.h file | annotate | diff | comparison | revisions
src/ucx/ucx.h file | annotate | diff | comparison | revisions
src/ucx/utils.c file | annotate | diff | comparison | revisions
src/ucx/utils.h file | annotate | diff | comparison | revisions
--- a/Makefile	Tue Aug 23 12:06:46 2016 +0200
+++ b/Makefile	Tue Aug 23 13:49:38 2016 +0200
@@ -36,10 +36,15 @@
 SRC += codegens.c
 SRC += ccodegen.c
 SRC += javacodegen.c
+SRC += ucx/allocator.c
+SRC += ucx/buffer.c
+SRC += ucx/list.c
+SRC += ucx/string.c
+SRC += ucx/utils.c
 
 OBJ = $(SRC:%.c=build/%$(OBJ_EXT))
 
-all: build build/$(BIN)
+all: build/ucx build/$(BIN)
 	
 build/$(BIN): $(OBJ)
 	$(LD) -o $@ $^ $(LDFLAGS)
@@ -47,8 +52,11 @@
 build/%$(OBJ_EXT): src/%.c
 	$(CC) -o $@ $(CFLAGS) -c $<
 	
+build/ucx: build
+	$(MKDIR) $@
+	
 build:
-	$(MKDIR) build
+	$(MKDIR) $@
 	
 test: build/$(BIN)
 	./build/$(BIN) test/ctestfile.c -o build/ctest.html \
--- a/clang.mk	Tue Aug 23 12:06:46 2016 +0200
+++ b/clang.mk	Tue Aug 23 13:49:38 2016 +0200
@@ -27,7 +27,7 @@
 #
 
 # system related
-MKDIR   = mkdir
+MKDIR   = mkdir -p
 RM      = rm
 RMFLAGS = -fR
 
--- a/gcc.mk	Tue Aug 23 12:06:46 2016 +0200
+++ b/gcc.mk	Tue Aug 23 13:49:38 2016 +0200
@@ -27,7 +27,7 @@
 #
 
 # system related
-MKDIR   = mkdir
+MKDIR   = mkdir -p
 RM      = rm
 RMFLAGS = -fR
 
--- a/src/c2html.c	Tue Aug 23 12:06:46 2016 +0200
+++ b/src/c2html.c	Tue Aug 23 13:49:38 2016 +0200
@@ -28,77 +28,9 @@
  */
 #include "c2html.h"
 
-inputfile_t *inputfilebuffer(size_t capacity) {
-    inputfile_t *inputfile = (inputfile_t*) malloc(sizeof(inputfile_t));
-    inputfile->lines = (char**) malloc(capacity * sizeof(char*));
-    inputfile->capacity = capacity;
-    inputfile->count = 0;
-    inputfile->maxlinewidth = 0;
-
-    return inputfile;
-}
-
-void addline(inputfile_t *inputfile, char* line, size_t width) {
-    char *l = (char*) malloc(width+1);
-    memcpy(l, line, width);
-    l[width] = 0;
-    if (inputfile->count >= inputfile->capacity) {
-        inputfile->capacity <<= 1;
-        inputfile->lines = realloc(inputfile->lines,
-            sizeof(char*)*inputfile->capacity);
-    }
-    inputfile->lines[inputfile->count] = l;
-    inputfile->maxlinewidth =
-        width > inputfile->maxlinewidth ? width : inputfile->maxlinewidth;
-    inputfile->count++;
-}
-
-void freeinputfilebuffer(inputfile_t *inputfile) {
-    for (int i = 0 ; i < inputfile->count ; i++) {
-        free(inputfile->lines[i]);
-    }
-    free(inputfile->lines);
-    free(inputfile);
-}
-
-inputfile_t *readinput(char *filename) {
-
-    int fd = open(filename, O_RDONLY);
-    if (fd == -1) return NULL;
-
-    inputfile_t *inputfile = inputfilebuffer(512);
-
-    char buf[INPUTBUF_SIZE];
-    ssize_t r;
-
-    size_t maxlinewidth = 256;
-    char *line = (char*) malloc(maxlinewidth);
-    size_t col = 0;
-
-    while ((r = read(fd, buf, INPUTBUF_SIZE)) > 0) {
-        for (size_t i = 0 ; i < r ; i++) {
-            if (col >= maxlinewidth-4) {
-                maxlinewidth <<= 1;
-                line = realloc(line, maxlinewidth);
-            }
-
-            if (buf[i] == '\n') {
-                line[col++] = '\n';
-                line[col] = 0;
-                addline(inputfile, line, col);
-                col = 0;
-            } else {
-                line[col++] = buf[i];
-            }
-        }
-    }
-
-    free(line);
-
-    close(fd);
-
-    return inputfile;
-}
+#include "ucx/buffer.h"
+#include "ucx/list.h"
+#include "ucx/utils.h"
 
 void printhelp() {
     printf("Formats source code using HTML.\n\nUsage:\n"
@@ -115,12 +47,6 @@
         "\n");
 }
 
-int lnint(size_t lnc) {
-    int w = 1, p = 1;
-    while ((p*=10) < lnc) w++;
-    return w;
-}
-
 int copyfile(char *filename, FILE *dest) {
     if (!filename) {
         return 0;
@@ -136,60 +62,71 @@
         fclose(src);
         return 0;
     } else {
-        return -1;
+        return 1;
     }
 }
 
 #define WRITECONST(stream, out, cstr) out(cstr, 1, sizeof(cstr)-1, stream)
 int formatfile(
         highlighter_t *highlighter,
-        inputfile_t *in,
-        fmt_write_func out,
+        UcxList *in,
+        write_func out,
         void *stream,
-        _Bool showln) {
-    // formats an input file and writes the result to out
+        int showlineno) {
+    size_t lines = ucx_list_size(in);
     
-    char *line = malloc(in->maxlinewidth*64);
+    UcxBuffer *line = ucx_buffer_new(NULL, 1024, UCX_BUFFER_AUTOEXTEND);
     if(!line) {
         return 1;
     }
     WRITECONST(stream, out, "<pre>\n");
     
-    int lnw = lnint(in->count);
-    for (int i = 0 ; i < in->count ; i++) {
+    int lnw;
+    {
+        lnw = 1;
+        int p = 1;
+        while ((p*=10) < lines) lnw++;
+    }
+
+    size_t lineno = 0;
+    UCX_FOREACH(sourceline, in) {
+        lineno++;
+        /* TODO: backwards compatibility: replace line->space in all occasions
+         * and use UcxBuffer functions
+         */
         if (highlighter) {
-            highlighter->parser(in->lines[i], line, highlighter);
+            highlighter->parser(sourceline->data, line->space, highlighter);
         } else {
-            char *c = in->lines[i];
+            char *c = sourceline->data;
             size_t dp = 0;
             while (*c) {
-                dp = writeescapedchar(line, dp, *c);
+                dp = writeescapedchar(line->space, dp, *c);
                 c++;
             }
-            line[dp] = '\0';
+            line->space[dp] = '\0';
         }
 
         // write line number
-        if (showln) {
+        if (showlineno) {
             WRITECONST(stream, out, "<span class=\"c2html-lineno\">");
             char lnbuf[128];
             int len;
             // line number link
             len = snprintf(lnbuf, 128, "<a name=\"l%d\" href=\"#l%d\">",
-                i+1, i+1);
+                lineno, lineno);
             out(lnbuf, 1, len, stream);
             // justified line number
-            len = snprintf(lnbuf, 128, "%*d ", lnw, i+1);
+            len = snprintf(lnbuf, 128, "%*d ", lnw, lineno);
             out(lnbuf, 1, len, stream);
             WRITECONST(stream, out, "</a></span> ");
         }
         
         // write formated (or plain) code line
-        out(line, 1, strlen(line), stream);
+        out(line->space, 1, strlen(line->space), stream);
     }
     
     WRITECONST(stream, out, "</pre>\n");
-    free(line);
+    ucx_buffer_free(line);
     return 0;
 }
 
@@ -209,15 +146,20 @@
     highlighter->parser = jparseline;
 }
 
+enum source_type {
+    SOURCE_C,
+    SOURCE_JAVA,
+    SOURCE_PLAIN
+};
+
 int main(int argc, char** argv) {
     int retcode = EXIT_SUCCESS;
     
-    settings_t settings;
+    Settings settings;
     memset(&settings, 0, sizeof(settings));
-    settings.highlight = 1;
     settings.showlinenumbers = 1;
     
-    int lang = C2HTML_C;
+    enum source_type sourcetype = SOURCE_C;
 
     char optc;
     while ((optc = getopt(argc, argv, "hljo:pH:F:vV")) != -1) {
@@ -234,10 +176,10 @@
                 settings.headerfile = optarg;
                 break;
             case 'j':
-                lang = C2HTML_JAVA;
+                sourcetype = SOURCE_JAVA;
                 break;
             case 'p':
-                settings.highlight = 0;
+                sourcetype = SOURCE_PLAIN;
                 break;
             case 'l':
                 settings.showlinenumbers = 0;
@@ -282,30 +224,53 @@
         
         highlighter_t highlighter;
         highlighter_t *hptr = &highlighter;
-        switch (lang) {
-            case C2HTML_C:
+        switch (sourcetype) {
+            case SOURCE_C:
                 init_c_highlighter(&highlighter);
                 break;
-            case C2HTML_JAVA:
+            case SOURCE_JAVA:
                 init_java_highlighter(&highlighter);
                 break;
-            default:
+            case SOURCE_PLAIN:
                 hptr = NULL;
                 break;
-        }
-        if (!settings.highlight) {
-            hptr = NULL;
+            default: /* should be unreachable */
+                fprintf(stderr, "error in enum source_type\n");
+                retcode = EXIT_FAILURE;
+                goto prog_end;
         }
 
-        inputfile_t *inputfile = readinput(settings.infilename);
+        FILE *inputfile = fopen(settings.infilename, "r");
         if (inputfile) {
+            UcxBuffer *filebuf = ucx_buffer_new(NULL,
+                    2048, UCX_BUFFER_AUTOEXTEND);
+            {
+                const size_t tmpbufsize = 512;
+                char *tmpbuf = malloc(tmpbufsize);
+                ucx_stream_copy(inputfile, filebuf, (read_func) fread,
+                        (write_func) ucx_buffer_write,
+                        tmpbuf, tmpbufsize, (size_t)-1);
+                free(tmpbuf);
+            }
+            fclose(inputfile);
+            
+            UcxList *inputlines = ucx_list_append(NULL, filebuf->space);
+            for (size_t i = 1 ; i < filebuf->size ; i++) {
+                if (filebuf->space[i] == '\r') {
+                    filebuf->space[i] = '\n'; i++;
+                }
+                if (filebuf->space[i] == '\n' && i+1 < filebuf->size) {
+                    ucx_list_append(inputlines, filebuf->space+i+1);
+                }
+            }
+            
             formatfile(
                     hptr,
-                    inputfile,
-                    (fmt_write_func)fwrite,
+                    inputlines,
+                    (write_func) fwrite,
                     fout,
                     settings.showlinenumbers);
-            freeinputfilebuffer(inputfile);
+            ucx_buffer_free(filebuf);
         } else {
             perror("Error opening input file");
             retcode = EXIT_FAILURE;
--- a/src/c2html.h	Tue Aug 23 12:06:46 2016 +0200
+++ b/src/c2html.h	Tue Aug 23 13:49:38 2016 +0200
@@ -46,31 +46,16 @@
     
 #define VERSION_MAJOR   2
 #define VERSION_MINOR   0
-#define VERSION_DEVELOP 1 // set this to zero for release version
-
+#define VERSION_DEVELOP 1 /* set this to zero for release version */
 
-#define INPUTBUF_SIZE 2048
-
-#define C2HTML_C    0
-#define C2HTML_JAVA 1
-    
 typedef struct {
     char* outfilename;
     char* headerfile;
     char* footerfile;
     char* infilename;
-    int highlight;
     int showlinenumbers;
-} settings_t;
+} Settings;
 
-typedef struct {
-    size_t count;
-    size_t capacity;
-    size_t maxlinewidth;
-    char** lines;
-} inputfile_t;
-
-typedef size_t(*fmt_write_func)(const void*, size_t, size_t, void*);
 
 #ifdef	__cplusplus
 }
--- a/src/ccodegen.c	Tue Aug 23 12:06:46 2016 +0200
+++ b/src/ccodegen.c	Tue Aug 23 13:49:38 2016 +0200
@@ -53,7 +53,7 @@
 void cparseline(char *src, char *dest, highlighter_t *hltr) {
 
     memset(hltr->word, 0, WORDBUF_SIZE);
-    size_t wp = 0, ifp = 0, sp = 0, dp = 0;
+    size_t wp = 0, ifp = 0, sp = (size_t)-1, dp = 0;
     int isstring = 0, iscomment = 0, isinclude = 0, parseinclude = 0;
     char quote = '\0';
     int isescaping = 0;
@@ -64,7 +64,11 @@
         memcpy_const(dest, dp, "<span class=\"c2html-comment\">");
     }
 
-    for (char c = src[sp] ; c ; c=src[++sp]) {
+    char c;
+    do {
+        c = src[++sp];
+        if (!c) break;
+        
         /* comments */
         if (!isstring && c == '/') {
             if (hltr->iscommentml && sp > 0 && src[sp-1] == '*') {
@@ -185,6 +189,6 @@
 
             isescaping = !isescaping & (c == '\\');
         }
-    }
+    } while (c != '\n');
     dest[dp] = 0;
 }
--- a/src/javacodegen.c	Tue Aug 23 12:06:46 2016 +0200
+++ b/src/javacodegen.c	Tue Aug 23 13:49:38 2016 +0200
@@ -54,7 +54,7 @@
 
 void jparseline(char *src, char *dest, highlighter_t *hltr) {
     memset(hltr->word, 0, WORDBUF_SIZE);
-    size_t wp = 0, sp = 0, dp = 0;
+    size_t wp = 0, sp = (size_t)-1, dp = 0;
     int isstring = 0, iscomment = 0, isimport = 0;
     char quote = '\0';
     int isescaping = 0;
@@ -64,7 +64,11 @@
         memcpy_const(dest, dp, "<span class=\"c2html-comment\">");
     }
 
-    for (char c = src[sp] ; c ; c=src[++sp]) {
+    char c;
+    do {
+        c = src[++sp];
+        if (!c) break;
+        
         /* comments */
         if (!isstring && c == '/') {
             if (hltr->iscommentml && sp > 0 && src[sp-1] == '*') {
@@ -155,6 +159,6 @@
 
             isescaping = !isescaping & (c == '\\');
         }
-    }
+    } while (c != '\n');
     dest[dp] = 0;
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ucx/allocator.c	Tue Aug 23 13:49:38 2016 +0200
@@ -0,0 +1,59 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2015 Olaf Wintermann. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *   1. Redistributions of source code must retain the above copyright
+ *      notice, this list of conditions and the following disclaimer.
+ *
+ *   2. Redistributions in binary form must reproduce the above copyright
+ *      notice, this list of conditions and the following disclaimer in the
+ *      documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdlib.h>
+#include "allocator.h"
+
+UcxAllocator default_allocator = {
+    NULL,
+    ucx_default_malloc,
+    ucx_default_calloc,
+    ucx_default_realloc,
+    ucx_default_free
+};
+
+UcxAllocator *ucx_default_allocator() {
+    UcxAllocator *allocator = &default_allocator;
+    return allocator;
+}
+
+void *ucx_default_malloc(void *ignore, size_t n) {
+    return malloc(n);
+}
+
+void *ucx_default_calloc(void *ignore, size_t n, size_t size) {
+    return calloc(n, size);
+}
+
+void *ucx_default_realloc(void *ignore, void *data, size_t n) {
+    return realloc(data, n);
+}
+
+void ucx_default_free(void *ignore, void *data) {
+    free(data);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ucx/allocator.h	Tue Aug 23 13:49:38 2016 +0200
@@ -0,0 +1,206 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2015 Olaf Wintermann. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *   1. Redistributions of source code must retain the above copyright
+ *      notice, this list of conditions and the following disclaimer.
+ *
+ *   2. Redistributions in binary form must reproduce the above copyright
+ *      notice, this list of conditions and the following disclaimer in the
+ *      documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+/**
+ * Allocator for custom memory management.
+ * 
+ * An UCX allocator consists of a pointer to the memory area / pool and four
+ * function pointers to memory management functions operating on this memory
+ * area / pool. These functions shall behave equivalent to the standard libc
+ * functions <code>malloc(), calloc(), realloc()</code> and <code>free()</code>.
+ * 
+ * The signature of the memory management functions is based on the signature
+ * of the respective libc function but each of them takes the pointer to the
+ * memory area / pool as first argument.
+ * 
+ * As the pointer to the memory area / pool can be arbitrarily chosen, any data
+ * can be provided to the memory management functions. An UcxMempool is just
+ * one example.
+ * 
+ * @see mempool.h
+ * @see UcxMap
+ * 
+ * @file   allocator.h
+ * @author Mike Becker
+ * @author Olaf Wintermann
+ */
+
+#ifndef UCX_ALLOCATOR_H
+#define	UCX_ALLOCATOR_H
+
+#include "ucx.h"
+
+#ifdef	__cplusplus
+extern "C" {
+#endif
+
+/**
+ * A function pointer to the allocators <code>malloc()</code> function.
+ * @see UcxAllocator
+ */
+typedef void*(*ucx_allocator_malloc)(void *pool, size_t n);
+
+/**
+ * A function pointer to the allocators <code>calloc()</code> function.
+ * @see UcxAllocator
+ */
+typedef void*(*ucx_allocator_calloc)(void *pool, size_t n, size_t size);
+
+/**
+ * A function pointer to the allocators <code>realloc()</code> function.
+ * @see UcxAllocator
+ */
+typedef void*(*ucx_allocator_realloc)(void *pool, void *data, size_t n);
+
+/**
+ * A function pointer to the allocators <code>free()</code> function.
+ * @see UcxAllocator
+ */
+typedef void(*ucx_allocator_free)(void *pool, void *data);
+
+/**
+ * UCX allocator data structure containing memory management functions.
+ */
+typedef struct {
+    /** Pointer to an area of memory or a complex memory pool.
+     * This pointer will be passed to any memory management function as first
+     * argument.
+     */
+    void *pool;
+    /**
+     * The <code>malloc()</code> function for this allocator.
+     */
+    ucx_allocator_malloc  malloc;
+    /**
+     * The <code>calloc()</code> function for this allocator.
+     */
+    ucx_allocator_calloc  calloc;
+    /**
+     * The <code>realloc()</code> function for this allocator.
+     */
+    ucx_allocator_realloc realloc;
+    /**
+     * The <code>free()</code> function for this allocator.
+     */
+    ucx_allocator_free    free;
+} UcxAllocator;
+
+/**
+ * Returns a pointer to the default allocator.
+ * 
+ * The default allocator contains wrappers to the standard libc memory
+ * management functions. Use this function to get a pointer to a globally
+ * available allocator. You may also define an own UcxAllocator by assigning
+ * #UCX_ALLOCATOR_DEFAULT to a variable and pass the address of this variable
+ * to any function that takes an UcxAllocator as argument. Note that using
+ * this function is the recommended way of passing a default allocator, thus
+ * it never runs out of scope.
+ * 
+ * @return a pointer to the default allocator
+ * 
+ * @see UCX_ALLOCATOR_DEFAULT
+ */
+UcxAllocator *ucx_default_allocator();
+
+/**
+ * A wrapper for the standard libc <code>malloc()</code> function.
+ * @param ignore ignored (may be used by allocators for pooled memory)
+ * @param n argument passed to <code>malloc()</code>
+ * @return return value of <code>malloc()</code>
+ */
+void *ucx_default_malloc(void *ignore, size_t n);
+/**
+ * A wrapper for the standard libc <code>calloc()</code> function.
+ * @param ignore ignored (may be used by allocators for pooled memory)
+ * @param n argument passed to <code>calloc()</code>
+ * @param size  argument passed to <code>calloc()</code>
+ * @return return value of <code>calloc()</code>
+ */
+void *ucx_default_calloc(void *ignore, size_t n, size_t size);
+/**
+ * A wrapper for the standard libc <code>realloc()</code> function.
+ * @param ignore ignored (may be used by allocators for pooled memory)
+ * @param data argumend passed to <code>realloc()</code>
+ * @param n argument passed to <code>realloc()</code>
+ * @return return value of <code>realloc()</code>
+ */
+void *ucx_default_realloc(void *ignore, void *data, size_t n);
+/**
+ * A wrapper for the standard libc <code>free()</code> function.
+ * @param ignore ignored (may be used by allocators for pooled memory)
+ * @param data argument passed to <code>free()</code>
+ */
+void ucx_default_free(void *ignore, void *data);
+
+/**
+ * Shorthand for calling an allocators malloc function.
+ * @param allocator the allocator to use
+ * @param n size of space to allocate
+ * @return a pointer to the allocated memory area
+ */
+#define almalloc(allocator, n) ((allocator)->malloc((allocator)->pool, n))
+
+/**
+ * Shorthand for calling an allocators calloc function.
+ * @param allocator the allocator to use
+ * @param n the count of elements the space should be allocated for
+ * @param size the size of each element
+ * @return a pointer to the allocated memory area
+ */
+#define alcalloc(allocator, n, size) \
+        ((allocator)->calloc((allocator)->pool, n, size))
+
+/**
+ * Shorthand for calling an allocators realloc function.
+ * @param allocator the allocator to use
+ * @param ptr the pointer to the memory area that shall be reallocated
+ * @param n the new size of the allocated memory area
+ * @return a pointer to the reallocated memory area
+ */
+#define alrealloc(allocator, ptr, n) \
+        ((allocator)->realloc((allocator)->pool, ptr, n))
+
+/**
+ * Shorthand for calling an allocators free function.
+ * @param allocator the allocator to use
+ * @param ptr the pointer to the memory area that shall be freed
+ */
+#define alfree(allocator, ptr) ((allocator)->free((allocator)->pool, ptr))
+
+/**
+ * Convenient macro for a default allocator <code>struct</code> definition.
+ */
+#define UCX_ALLOCATOR_DEFAULT {NULL, \
+        ucx_default_malloc, ucx_default_calloc, ucx_default_realloc, \
+        ucx_default_free }
+
+#ifdef	__cplusplus
+}
+#endif
+
+#endif	/* UCX_ALLOCATOR_H */
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ucx/buffer.c	Tue Aug 23 13:49:38 2016 +0200
@@ -0,0 +1,234 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2015 Olaf Wintermann. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *   1. Redistributions of source code must retain the above copyright
+ *      notice, this list of conditions and the following disclaimer.
+ *
+ *   2. Redistributions in binary form must reproduce the above copyright
+ *      notice, this list of conditions and the following disclaimer in the
+ *      documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "buffer.h"
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+
+UcxBuffer *ucx_buffer_new(void *space, size_t capacity, int flags) {
+    UcxBuffer *buffer = (UcxBuffer*) malloc(sizeof(UcxBuffer));
+    if (buffer) {
+        buffer->flags = flags;
+        if (!space) {
+            buffer->space = (char*)malloc(capacity);
+            if (!buffer->space) {
+                free(buffer);
+                return NULL;
+            }
+            memset(buffer->space, 0, capacity);
+            buffer->flags |= UCX_BUFFER_AUTOFREE;
+        } else {
+            buffer->space = (char*)space;
+        }
+        buffer->capacity = capacity;
+        buffer->size = 0;
+
+        buffer->pos = 0;
+    }
+
+    return buffer;
+}
+
+void ucx_buffer_free(UcxBuffer *buffer) {
+    if ((buffer->flags & UCX_BUFFER_AUTOFREE) == UCX_BUFFER_AUTOFREE) {
+        free(buffer->space);
+    }
+    free(buffer);
+}
+
+UcxBuffer* ucx_buffer_extract(
+        UcxBuffer *src, size_t start, size_t length, int flags) {
+    
+    if (src->size == 0 || length == 0 || start+length > src->capacity) {
+        return NULL;
+    }
+
+    UcxBuffer *dst = (UcxBuffer*) malloc(sizeof(UcxBuffer));
+    if (dst) {
+        dst->space = (char*)malloc(length);
+        if (!dst->space) {
+            free(dst);
+            return NULL;
+        }
+        dst->capacity = length;
+        dst->size = length;
+        dst->flags = flags | UCX_BUFFER_AUTOFREE;
+        dst->pos = 0;
+        memcpy(dst->space, src->space+start, length);
+    }
+    return dst;
+}
+
+int ucx_buffer_seek(UcxBuffer *buffer, off_t offset, int whence) {
+    size_t npos;
+    switch (whence) {
+    case SEEK_CUR:
+        npos = buffer->pos;
+        break;
+    case SEEK_END:
+        npos = buffer->size;
+        break;
+    case SEEK_SET:
+        npos = 0;
+        break;
+    default:
+        return -1;
+    }
+
+    size_t opos = npos;
+    npos += offset;
+    
+    if ((offset > 0 && npos < opos) || (offset < 0 && npos > opos)) {
+        return -1;
+    }
+    
+    if (npos >= buffer->size) {
+        return -1;
+    } else {
+        buffer->pos = npos;
+        return 0;
+    }
+
+}
+
+int ucx_buffer_eof(UcxBuffer *buffer) {
+    return buffer->pos >= buffer->size;
+}
+
+int ucx_buffer_extend(UcxBuffer *buffer, size_t len) {
+    size_t newcap = buffer->capacity;
+    
+    if (buffer->capacity + len < buffer->capacity) {
+        return -1;
+    }
+    
+    while (buffer->capacity + len > newcap) {
+        newcap <<= 1;
+        if (newcap < buffer->capacity) {
+            return -1;
+        }
+    }
+    
+    char *newspace = (char*)realloc(buffer->space, newcap);
+    if (newspace) {
+        memset(newspace+buffer->size, 0, newcap-buffer->size);
+        buffer->space = newspace;
+        buffer->capacity = newcap;
+    } else {
+        return -1;
+    }
+    
+    return 0;
+}
+
+size_t ucx_buffer_write(const void *ptr, size_t size, size_t nitems,
+        UcxBuffer *buffer) {
+    size_t len = size * nitems;
+    size_t required = buffer->pos + len;
+    if (buffer->pos > required) {
+        return 0;
+    }
+    
+    if (required > buffer->capacity) {
+        if ((buffer->flags & UCX_BUFFER_AUTOEXTEND) == UCX_BUFFER_AUTOEXTEND) {
+            if (ucx_buffer_extend(buffer, required - buffer->capacity)) {
+                return 0;
+            }
+        } else {
+            len = buffer->capacity - buffer->pos;
+            if (size > 1) {
+                len -= len%size;
+            }
+        }
+    }
+    
+    if (len == 0) {
+        return len;
+    }
+    
+    memcpy(buffer->space + buffer->pos, ptr, len);
+    buffer->pos += len;
+    if(buffer->pos > buffer->size) {
+        buffer->size = buffer->pos;
+    }
+    
+    return len / size;
+}
+
+size_t ucx_buffer_read(void *ptr, size_t size, size_t nitems,
+        UcxBuffer *buffer) {
+    size_t len = size * nitems;
+    if (buffer->pos + len > buffer->size) {
+        len = buffer->size - buffer->pos;
+        if (size > 1) len -= len%size;
+    }
+    
+    if (len <= 0) {
+        return len;
+    }
+    
+    memcpy(ptr, buffer->space + buffer->pos, len);
+    buffer->pos += len;
+    
+    return len / size;
+}
+
+int ucx_buffer_putc(UcxBuffer *buffer, int c) {
+    if(buffer->pos >= buffer->capacity) {
+        if ((buffer->flags & UCX_BUFFER_AUTOEXTEND) == UCX_BUFFER_AUTOEXTEND) {
+            if(ucx_buffer_extend(buffer, 1)) {
+                return EOF;
+            }
+        } else {
+            return EOF;
+        }
+    }
+    
+    c &= 0xFF;
+    buffer->space[buffer->pos] = (char) c;
+    buffer->pos++;
+    if(buffer->pos > buffer->size) {
+        buffer->size = buffer->pos;
+    }
+    return c;
+}
+
+int ucx_buffer_getc(UcxBuffer *buffer) {
+    if (ucx_buffer_eof(buffer)) {
+        return EOF;
+    } else {
+        int c = buffer->space[buffer->pos];
+        buffer->pos++;
+        return c;
+    }
+}
+
+size_t ucx_buffer_puts(UcxBuffer *buffer, char *str) {
+    return ucx_buffer_write((const void*)str, 1, strlen(str), buffer);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ucx/buffer.h	Tue Aug 23 13:49:38 2016 +0200
@@ -0,0 +1,270 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2015 Olaf Wintermann. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *   1. Redistributions of source code must retain the above copyright
+ *      notice, this list of conditions and the following disclaimer.
+ *
+ *   2. Redistributions in binary form must reproduce the above copyright
+ *      notice, this list of conditions and the following disclaimer in the
+ *      documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * @file buffer.h
+ * 
+ * Advanced buffer implementation.
+ * 
+ * Instances of UcxBuffer can be used to read from or to write to like one
+ * would do with a stream. This allows the use of ucx_stream_copy() to copy
+ * contents from one buffer to another.
+ * 
+ * Some features for convenient use of the buffer
+ * can be enabled. See the documentation of the macro constants for more
+ * information.
+ * 
+ * @author Mike Becker
+ * @author Olaf Wintermann
+ */
+
+#ifndef UCX_BUFFER_H
+#define	UCX_BUFFER_H
+
+#include "ucx.h"
+#include <sys/types.h>
+#include <stdio.h>
+
+#ifdef	__cplusplus
+extern "C" {
+#endif
+
+/**
+ * No buffer features enabled (all flags cleared).
+ */
+#define UCX_BUFFER_DEFAULT      0x00
+
+/**
+ * If this flag is enabled, the buffer will automatically free its contents.
+ */
+#define UCX_BUFFER_AUTOFREE     0x01
+
+/**
+ * If this flag is enabled, the buffer will automatically extends its capacity.
+ */
+#define UCX_BUFFER_AUTOEXTEND   0x02
+
+/** UCX Buffer. */
+typedef struct {
+    /** A pointer to the buffer contents. */
+    char *space;
+    /** Current position of the buffer. */
+    size_t pos;
+    /** Current capacity (i.e. maximum size) of the buffer. */
+    size_t capacity;
+    /** Current size of the buffer content. */
+    size_t size;
+    /**
+     * Flag register for buffer features.
+     * @see #UCX_BUFFER_DEFAULT
+     * @see #UCX_BUFFER_AUTOFREE
+     * @see #UCX_BUFFER_AUTOEXTEND
+     */
+    int flags;
+} UcxBuffer;
+
+/**
+ * Creates a new buffer.
+ * 
+ * <b>Note:</b> you may provide <code>NULL</code> as argument for
+ * <code>space</code>. Then this function will allocate the space and enforce
+ * the #UCX_BUFFER_AUTOFREE flag.
+ * 
+ * @param space pointer to the memory area, or <code>NULL</code> to allocate
+ * new memory
+ * @param capacity the capacity of the buffer
+ * @param flags buffer features (see UcxBuffer.flags)
+ * @return the new buffer
+ */
+UcxBuffer *ucx_buffer_new(void *space, size_t capacity, int flags);
+
+/**
+ * Destroys a buffer.
+ * 
+ * If the #UCX_BUFFER_AUTOFREE feature is enabled, the contents of the buffer
+ * are also freed.
+ * 
+ * @param buffer the buffer to destroy
+ */
+void ucx_buffer_free(UcxBuffer* buffer);
+
+/**
+ * Creates a new buffer and fills it with extracted content from another buffer.
+ * 
+ * <b>Note:</b> the #UCX_BUFFER_AUTOFREE feature is enforced for the new buffer.
+ * 
+ * @param src the source buffer
+ * @param start the start position of extraction
+ * @param length the count of bytes to extract (must not be zero)
+ * @param flags feature mask for the new buffer
+ * @return a new buffer containing the extraction
+ */
+UcxBuffer* ucx_buffer_extract(UcxBuffer *src,
+        size_t start, size_t length, int flags);
+
+/**
+ * A shorthand macro for the full extraction of the buffer.
+ * 
+ * @param src the source buffer
+ * @param flags feature mask for the new buffer
+ * @return a new buffer with the extracted content
+ */
+#define ucx_buffer_clone(src,flags) \
+    ucx_buffer_extract(src, 0, (src)->capacity, flags)
+
+/**
+ * Moves the position of the buffer.
+ * 
+ * The new position is relative to the <code>whence</code> argument.
+ *
+ * SEEK_SET marks the start of the buffer.
+ * SEEK_CUR marks the current position.
+ * SEEK_END marks the end of the buffer.
+ * 
+ * With an offset of zero, this function sets the buffer position to zero
+ * (SEEK_SET), the buffer size (SEEK_END) or leaves the buffer position
+ * unchanged (SEEK_CUR).
+ * 
+ * @param buffer
+ * @param offset position offset relative to <code>whence</code>
+ * @param whence one of SEEK_SET, SEEK_CUR or SEEK_END
+ * @return 0 on success, non-zero if the position is invalid
+ *
+ */
+int ucx_buffer_seek(UcxBuffer *buffer, off_t offset, int whence);
+
+/**
+ * Clears the buffer by resetting the position and deleting the data.
+ * 
+ * The data is deleted by a zeroing it with call to <code>memset()</code>.
+ * 
+ * @param buffer the buffer to be cleared
+ */
+#define ucx_buffer_clear(buffer) memset(buffer->space, 0, buffer->size); \
+        buffer->size = 0; buffer->pos = 0;
+
+/**
+ * Tests, if the buffer position has exceeded the buffer capacity.
+ * 
+ * @param buffer the buffer to test
+ * @return non-zero, if the current buffer position has exceeded the last
+ * available byte of the buffer.
+ */
+int ucx_buffer_eof(UcxBuffer *buffer);
+
+
+/**
+ * Extends the capacity of the buffer.
+ * 
+ * <b>Note:</b> The buffer capacity increased by a power of two. I.e.
+ * the buffer capacity is doubled, as long as it would not hold the current
+ * content plus the additional required bytes.
+ * 
+ * <b>Attention:</b> the argument provided is the number of <i>additional</i>
+ * bytes the buffer shall hold. It is <b>NOT</b> the total number of bytes the
+ * buffer shall hold.
+ * 
+ * @param buffer the buffer to extend
+ * @param additional_bytes the number of additional bytes the buffer shall
+ * <i>at least</i> hold
+ * @return 0 on success or a non-zero value on failure
+ */
+int ucx_buffer_extend(UcxBuffer *buffer, size_t additional_bytes);
+
+/**
+ * Writes data to an UcxBuffer.
+ * 
+ * The position of the buffer is increased by the number of bytes written.
+ * 
+ * @param ptr a pointer to the memory area containing the bytes to be written
+ * @param size the length of one element
+ * @param nitems the element count
+ * @param buffer the UcxBuffer to write to
+ * @return the total count of bytes written
+ */
+size_t ucx_buffer_write(const void *ptr, size_t size, size_t nitems,
+        UcxBuffer *buffer);
+
+/**
+ * Reads data from an UcxBuffer.
+ * 
+ * The position of the buffer is increased by the number of bytes read.
+ * 
+ * @param ptr a pointer to the memory area where to store the read data
+ * @param size the length of one element
+ * @param nitems the element count
+ * @param buffer the UcxBuffer to read from
+ * @return the total number of elements read
+ */
+size_t ucx_buffer_read(void *ptr, size_t size, size_t nitems,
+        UcxBuffer *buffer);
+
+/**
+ * Writes a character to a buffer.
+ * 
+ * The least significant byte of the argument is written to the buffer. If the
+ * end of the buffer is reached and #UCX_BUFFER_AUTOEXTEND feature is enabled,
+ * the buffer capacity is extended by ucx_buffer_extend(). If the feature is
+ * disabled or buffer extension fails, <code>EOF</code> is returned.
+ * 
+ * On successful write the position of the buffer is increased.
+ * 
+ * @param buffer the buffer to write to
+ * @param c the character to write as <code>int</code> value
+ * @return the byte that has bean written as <code>int</code> value or
+ * <code>EOF</code> when the end of the stream is reached and automatic
+ * extension is not enabled or not possible
+ */
+int ucx_buffer_putc(UcxBuffer *buffer, int c);
+
+/**
+ * Gets a character from a buffer.
+ * 
+ * The current position of the buffer is increased after a successful read.
+ * 
+ * @param buffer the buffer to read from
+ * @return the character as <code>int</code> value or <code>EOF</code>, if the
+ * end of the buffer is reached
+ */
+int ucx_buffer_getc(UcxBuffer *buffer);
+
+/**
+ * Writes a string to a buffer.
+ * 
+ * @param buffer the buffer
+ * @param str the string
+ * @return the number of bytes written
+ */
+size_t ucx_buffer_puts(UcxBuffer *buffer, char *str);
+
+#ifdef	__cplusplus
+}
+#endif
+
+#endif	/* UCX_BUFFER_H */
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ucx/list.c	Tue Aug 23 13:49:38 2016 +0200
@@ -0,0 +1,335 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2015 Olaf Wintermann. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *   1. Redistributions of source code must retain the above copyright
+ *      notice, this list of conditions and the following disclaimer.
+ *
+ *   2. Redistributions in binary form must reproduce the above copyright
+ *      notice, this list of conditions and the following disclaimer in the
+ *      documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "list.h"
+
+UcxList *ucx_list_clone(UcxList *l, copy_func fnc, void *data) {
+    return ucx_list_clone_a(ucx_default_allocator(), l, fnc, data);
+}
+
+UcxList *ucx_list_clone_a(UcxAllocator *alloc, UcxList *l,
+        copy_func fnc, void *data) {
+    UcxList *ret = NULL;
+    while (l) {
+        if (fnc) {
+            ret = ucx_list_append_a(alloc, ret, fnc(l->data, data));
+        } else {
+            ret = ucx_list_append_a(alloc, ret, l->data);
+        }
+        l = l->next;
+    }
+    return ret;
+}
+
+int ucx_list_equals(const UcxList *l1, const UcxList *l2,
+        cmp_func fnc, void* data) {
+    if (l1 == l2) return 1;
+    
+    while (l1 != NULL && l2 != NULL) {
+        if (fnc == NULL) {
+            if (l1->data != l2->data) return 0;
+        } else {
+            if (fnc(l1->data, l2->data, data) != 0) return 0;
+        }
+        l1 = l1->next;
+        l2 = l2->next;
+    }
+    
+    return (l1 == NULL && l2 == NULL);
+}
+
+void ucx_list_free(UcxList *l) {
+    ucx_list_free_a(ucx_default_allocator(), l);
+}
+
+void ucx_list_free_a(UcxAllocator *alloc, UcxList *l) {
+    UcxList *e = l, *f;
+    while (e != NULL) {
+        f = e;
+        e = e->next;
+        alfree(alloc, f);
+    }
+}
+
+void ucx_list_free_content(UcxList* list, ucx_destructor destr) {
+    while (list != NULL) {
+        destr(list->data);
+        list = list->next;
+    }
+}
+
+UcxList *ucx_list_append(UcxList *l, void *data)  {
+    return ucx_list_append_a(ucx_default_allocator(), l, data);
+}
+
+UcxList *ucx_list_append_a(UcxAllocator *alloc, UcxList *l, void *data)  {
+    UcxList *nl = (UcxList*) almalloc(alloc, sizeof(UcxList));
+    if (!nl) {
+        return NULL;
+    }
+    
+    nl->data = data;
+    nl->next = NULL;
+    if (l) {
+        UcxList *t = ucx_list_last(l);
+        t->next = nl;
+        nl->prev = t;
+        return l;
+    } else {
+        nl->prev = NULL;
+        return nl;
+    }
+}
+
+UcxList *ucx_list_prepend(UcxList *l, void *data) {
+    return ucx_list_prepend_a(ucx_default_allocator(), l, data);
+}
+
+UcxList *ucx_list_prepend_a(UcxAllocator *alloc, UcxList *l, void *data) {
+    UcxList *nl = ucx_list_append_a(alloc, NULL, data);
+    if (!nl) {
+        return NULL;
+    }
+    l = ucx_list_first(l);
+    
+    if (l) {
+        nl->next = l;
+        l->prev = nl;
+    }
+    return nl;
+}
+
+UcxList *ucx_list_concat(UcxList *l1, UcxList *l2) {
+    if (l1) {
+        UcxList *last = ucx_list_last(l1);
+        last->next = l2;
+        if (l2) {
+            l2->prev = last;
+        }
+        return l1;
+    } else {
+        return l2;
+    }
+}
+
+UcxList *ucx_list_last(const UcxList *l) {
+    if (l == NULL) return NULL;
+    
+    const UcxList *e = l;
+    while (e->next != NULL) {
+        e = e->next;
+    }
+    return (UcxList*)e;
+}
+
+ssize_t ucx_list_indexof(const UcxList *list, const UcxList *elem) {
+    ssize_t index = 0;
+    while (list) {
+        if (list == elem) {
+            return index;
+        }
+        list = list->next;
+        index++;
+    }
+    return -1;
+}
+
+UcxList *ucx_list_get(const UcxList *l, size_t index) {
+    if (l == NULL) return NULL;
+
+    const UcxList *e = l;
+    while (e->next && index > 0) {
+        e = e->next;
+        index--;
+    }
+    
+    return (UcxList*)(index == 0 ? e : NULL);
+}
+
+ssize_t ucx_list_find(UcxList *l, void *elem, cmp_func fnc, void *cmpdata) {
+    ssize_t index = 0;
+    UCX_FOREACH(e, l) {
+        if (fnc) {
+            if (fnc(elem, e->data, cmpdata) == 0) {
+                return index;
+            }
+        } else {
+            if (elem == e->data) {
+                return index;
+            }
+        }
+        index++;
+    }
+    return -1;
+}
+
+int ucx_list_contains(UcxList *l, void *elem, cmp_func fnc, void *cmpdata) {
+    return ucx_list_find(l, elem, fnc, cmpdata) > -1;
+}
+
+size_t ucx_list_size(const UcxList *l) {
+    if (l == NULL) return 0;
+    
+    const UcxList *e = l;
+    size_t s = 1;
+    while (e->next != NULL) {
+        e = e->next;
+        s++;
+    }
+
+    return s;
+}
+
+static UcxList *ucx_list_sort_merge(int length,
+        UcxList* restrict ls, UcxList* restrict le, UcxList* restrict re,
+        cmp_func fnc, void* data) {
+
+    UcxList** sorted = (UcxList**) malloc(sizeof(UcxList*)*length);
+    UcxList *rc, *lc;
+
+    lc = ls; rc = le;
+    int n = 0;
+    while (lc && lc != le && rc != re) {
+        if (fnc(lc->data, rc->data, data) <= 0) {
+            sorted[n] = lc;
+            lc = lc->next;
+        } else {
+            sorted[n] = rc;
+            rc = rc->next;
+        }
+        n++;
+    }
+    while (lc && lc != le) {
+        sorted[n] = lc;
+        lc = lc->next;
+        n++;
+    }
+    while (rc && rc != re) {
+        sorted[n] = rc;
+        rc = rc->next;
+        n++;
+    }
+
+    // Update pointer
+    sorted[0]->prev = NULL;
+    for (int i = 0 ; i < length-1 ; i++) {
+        sorted[i]->next = sorted[i+1];
+        sorted[i+1]->prev = sorted[i];
+    }
+    sorted[length-1]->next = NULL;
+
+    UcxList *ret = sorted[0];
+    free(sorted);
+    return ret;
+}
+
+UcxList *ucx_list_sort(UcxList *l, cmp_func fnc, void *data) {
+    if (l == NULL) {
+        return NULL;
+    }
+
+    UcxList *lc;
+    int ln = 1;
+
+    UcxList *restrict ls = l, *restrict le, *restrict re;
+    
+    // check how many elements are already sorted
+    lc = ls;
+    while (lc->next != NULL && fnc(lc->next->data, lc->data, data) > 0) {
+        lc = lc->next;
+        ln++;
+    }
+    le = lc->next;
+
+    if (le == NULL) {
+        return l; // this list is already sorted :)
+    } else {
+        UcxList *rc;
+        int rn = 1;
+        rc = le;
+        // skip already sorted elements
+        while (rc->next != NULL && fnc(rc->next->data, rc->data, data) > 0) {
+            rc = rc->next;
+            rn++;
+        }
+        re = rc->next;
+
+        // {ls,...,le->prev} and {rs,...,re->prev} are sorted - merge them
+        UcxList *sorted = ucx_list_sort_merge(ln+rn,
+                ls, le, re,
+                fnc, data);
+        
+        // Something left? Sort it!
+        size_t remainder_length = ucx_list_size(re);
+        if (remainder_length > 0) {
+            UcxList *remainder = ucx_list_sort(re, fnc, data);
+
+            // merge sorted list with (also sorted) remainder
+            l = ucx_list_sort_merge(ln+rn+remainder_length,
+                    sorted, remainder, NULL, fnc, data);
+        } else {
+            // no remainder - we've got our sorted list
+            l = sorted;
+        }
+
+        return l;
+    }
+}
+
+UcxList *ucx_list_first(const UcxList *l) {
+    if (!l) {
+        return NULL;
+    }
+    
+    const UcxList *e = l;
+    while (e->prev) {
+        e = e->prev;
+    }
+    return (UcxList *)e;
+}
+
+UcxList *ucx_list_remove(UcxList *l, UcxList *e) {
+    return ucx_list_remove_a(ucx_default_allocator(), l, e);
+}
+    
+UcxList *ucx_list_remove_a(UcxAllocator *alloc, UcxList *l, UcxList *e) {
+    if (l == e) {
+        l = e->next;
+    }
+    
+    if (e->next) {
+        e->next->prev = e->prev;
+    }
+    
+    if (e->prev) {
+        e->prev->next = e->next;
+    }
+    
+    alfree(alloc, e);
+    return l;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ucx/list.h	Tue Aug 23 13:49:38 2016 +0200
@@ -0,0 +1,395 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2015 Olaf Wintermann. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *   1. Redistributions of source code must retain the above copyright
+ *      notice, this list of conditions and the following disclaimer.
+ *
+ *   2. Redistributions in binary form must reproduce the above copyright
+ *      notice, this list of conditions and the following disclaimer in the
+ *      documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+/**
+ * Doubly linked list implementation.
+ * 
+ * @file   list.h
+ * @author Mike Becker
+ * @author Olaf Wintermann
+ */
+
+#ifndef UCX_LIST_H
+#define	UCX_LIST_H
+
+#include "ucx.h"
+#include "allocator.h"
+
+#ifdef	__cplusplus
+extern "C" {
+#endif
+
+/**
+ * Loop statement for UCX lists.
+ * 
+ * The first argument is the name of the iteration variable. The scope of
+ * this variable is limited to the <code>UCX_FOREACH</code> statement.
+ * 
+ * The second argument is a pointer to the list. In most cases this will be the
+ * pointer to the first element of the list, but it may also be an arbitrary
+ * element of the list. The iteration will then start with that element.
+ * 
+ * 
+ * @param list The first element of the list
+ * @param elem The variable name of the element
+ */
+#define UCX_FOREACH(elem,list) \
+        for (UcxList* elem = list ; elem != NULL ; elem = elem->next)
+
+/**
+ * UCX list type.
+ * @see UcxList
+ */
+typedef struct UcxList UcxList;
+
+/**
+ * UCX list structure.
+ */
+struct UcxList {
+    /**
+     * List element payload.
+     */
+    void    *data;
+    /**
+     * Pointer to the next list element or <code>NULL</code>, if this is the
+     * last element.
+     */
+    UcxList *next;
+    /**
+     * Pointer to the previous list element or <code>NULL</code>, if this is
+     * the first element.
+     */
+    UcxList *prev;
+};
+
+/**
+ * Creates an element-wise copy of a list.
+ * 
+ * This function clones the specified list by creating new list elements and
+ * copying the data with the specified copy_func(). If no copy_func() is
+ * specified, a shallow copy is created and the new list will reference the
+ * same data as the source list.
+ * 
+ * @param list the list to copy
+ * @param cpyfnc a pointer to the function that shall copy an element (may be
+ * <code>NULL</code>)
+ * @param data additional data for the copy_func()
+ * @return a pointer to the copy
+ */
+UcxList *ucx_list_clone(UcxList *list, copy_func cpyfnc, void* data);
+
+/**
+ * Creates an element-wise copy of a list using an UcxAllocator.
+ * 
+ * See ucx_list_clone() for details.
+ * 
+ * Keep in mind, that you might want to pass the allocator as an (part of the)
+ * argument for the <code>data</code> parameter, if you want the copy_func() to
+ * make use of the allocator.
+ * 
+ * @param allocator the allocator to use
+ * @param list the list to copy
+ * @param cpyfnc a pointer to the function that shall copy an element (may be
+ * <code>NULL</code>)
+ * @param data additional data for the copy_func()
+ * @return a pointer to the copy
+ * @see ucx_list_clone()
+ */
+UcxList *ucx_list_clone_a(UcxAllocator *allocator, UcxList *list,
+        copy_func cpyfnc, void* data);
+
+/**
+ * Compares two UCX lists element-wise by using a compare function.
+ * 
+ * Each element of the two specified lists are compared by using the specified
+ * compare function and the additional data. The type and content of this
+ * additional data depends on the cmp_func() used.
+ * 
+ * If the list pointers denote elements within a list, the lists are compared
+ * starting with the denoted elements. Thus any previous elements are not taken
+ * into account. This might be useful to check, if certain list tails match
+ * each other.
+ * 
+ * @param list1 the first list
+ * @param list2 the second list
+ * @param cmpfnc the compare function
+ * @param data additional data for the compare function
+ * @return 1, if and only if the two lists equal element-wise, 0 otherwise
+ */
+int ucx_list_equals(const UcxList *list1, const UcxList *list2,
+        cmp_func cmpfnc, void* data);
+
+/**
+ * Destroys the entire list.
+ * 
+ * The members of the list are not automatically freed, so ensure they are
+ * otherwise referenced or destroyed by ucx_list_free_contents().
+ * Otherwise, a memory leak is likely to occur.
+ * 
+ * <b>Caution:</b> the argument <b>MUST</b> denote an entire list (i.e. a call
+ * to ucx_list_first() on the argument must return the argument itself)
+ * 
+ * @param list the list to free
+ * @see ucx_list_free_contents()
+ */
+void ucx_list_free(UcxList *list);
+
+/**
+ * Destroys the entire list using an UcxAllocator.
+ * 
+ * See ucx_list_free() for details.
+ * 
+ * @param allocator the allocator to use
+ * @param list the list to free
+ * @see ucx_list_free()
+ */
+void ucx_list_free_a(UcxAllocator *allocator, UcxList *list);
+
+/**
+ * Destroys the contents of the specified list by calling the specified
+ * destructor on each of them.
+ * 
+ * Note, that the contents are not usable afterwards and the list should be
+ * destroyed with ucx_list_free().
+ * 
+ * @param list the list for which the contents shall be freed
+ * @param destr the destructor function (e.g. stdlib free())
+ * @see ucx_list_free()
+ */
+void ucx_list_free_content(UcxList* list, ucx_destructor destr);
+
+
+/**
+ * Inserts an element at the end of the list.
+ * 
+ * This is generally an O(n) operation, as the end of the list is retrieved with
+ * ucx_list_last().
+ * 
+ * @param list the list where to append the data, or <code>NULL</code> to
+ * create a new list
+ * @param data the data to insert
+ * @return <code>list</code>, if it is not <code>NULL</code> or a pointer to
+ * the newly created list otherwise
+ */
+UcxList *ucx_list_append(UcxList *list, void *data);
+
+/**
+ * Inserts an element at the end of the list using an UcxAllocator.
+ * 
+ * See ucx_list_append() for details.
+ * 
+ * @param allocator the allocator to use
+ * @param list the list where to append the data, or <code>NULL</code> to
+ * create a new list
+ * @param data the data to insert
+ * @return <code>list</code>, if it is not <code>NULL</code> or a pointer to
+ * the newly created list otherwise
+ * @see ucx_list_append()
+ */
+UcxList *ucx_list_append_a(UcxAllocator *allocator, UcxList *list, void *data);
+
+/**
+ * Inserts an element at the beginning of the list.
+ * 
+ * You <i>should</i> overwrite the old list pointer by calling
+ * <code>mylist = ucx_list_prepend(mylist, mydata);</code>. However, you may
+ * also perform successive calls of ucx_list_prepend() on the same list pointer,
+ * as this function always searchs for the head of the list with
+ * ucx_list_first().
+ * 
+ * @param list the list where to insert the data or <code>NULL</code> to create
+ * a new list
+ * @param data the data to insert
+ * @return a pointer to the new list head
+ */
+UcxList *ucx_list_prepend(UcxList *list, void *data);
+
+/**
+ * Inserts an element at the beginning of the list using an UcxAllocator.
+ * 
+ * See ucx_list_prepend() for details.
+ * 
+ * @param allocator the allocator to use
+ * @param list the list where to insert the data or <code>NULL</code> to create
+ * a new list
+ * @param data the data to insert
+ * @return a pointer to the new list head
+ * @see ucx_list_prepend()
+ */
+UcxList *ucx_list_prepend_a(UcxAllocator *allocator, UcxList *list, void *data);
+
+/**
+ * Concatenates two lists.
+ * 
+ * Either of the two arguments may be <code>NULL</code>.
+ * 
+ * This function modifies the references to the next/previous element of
+ * the last/first element of <code>list1</code>/<code>
+ * list2</code>.
+ * 
+ * @param list1 first list
+ * @param list2 second list
+ * @return if <code>list1</code> is <code>NULL</code>, <code>list2</code> is
+ * returned, otherwise <code>list1</code> is returned
+ */
+UcxList *ucx_list_concat(UcxList *list1, UcxList *list2);
+
+/**
+ * Returns the first element of a list.
+ * 
+ * If the argument is the list pointer, it is directly returned. Otherwise
+ * this function traverses to the first element of the list and returns the
+ * list pointer.
+ * 
+ * @param elem one element of the list
+ * @return the first element of the list, the specified element is a member of
+ */
+UcxList *ucx_list_first(const UcxList *elem);
+
+/**
+ * Returns the last element of a list.
+ * 
+ * If the argument has no successor, it is the last element and therefore
+ * directly returned. Otherwise this function traverses to the last element of
+ * the list and returns it.
+ * 
+ * @param elem one element of the list
+ * @return the last element of the list, the specified element is a member of
+ */
+UcxList *ucx_list_last(const UcxList *elem);
+
+/**
+ * Returns the list element at the specified index.
+ * 
+ * @param list the list to retrieve the element from
+ * @param index index of the element to return
+ * @return the element at the specified index or <code>NULL</code>, if the
+ * index is greater than the list size
+ */
+UcxList *ucx_list_get(const UcxList *list, size_t index);
+
+/**
+ * Returns the index of an element.
+ * 
+ * @param list the list where to search for the element
+ * @param elem the element to find
+ * @return the index of the element or -1 if the list does not contain the
+ * element
+ */
+ssize_t ucx_list_indexof(const UcxList *list, const UcxList *elem);
+
+/**
+ * Returns the element count of the list.
+ * 
+ * @param list the list whose elements are counted
+ * @return the element count
+ */
+size_t ucx_list_size(const UcxList *list);
+
+/**
+ * Returns the index of an element containing the specified data.
+ *
+ * This function uses a cmp_func() to compare the data of each list element
+ * with the specified data. If no cmp_func is provided, the pointers are
+ * compared.
+ * 
+ * If the list contains the data more than once, the index of the first
+ * occurrence is returned.
+ *  
+ * @param list the list where to search for the data
+ * @param elem the element data
+ * @param cmpfnc the compare function
+ * @param data additional data for the compare function
+ * @return the index of the element containing the specified data or -1 if the
+ * data is not found in this list
+ */
+ssize_t ucx_list_find(UcxList *list, void *elem, cmp_func cmpfnc, void *data);
+
+/**
+ * Checks, if a list contains a specific element.
+ * 
+ * An element is found, if ucx_list_find() returns a value greater than -1.
+ * 
+ * @param list the list where to search for the data
+ * @param elem the element data
+ * @param cmpfnc the compare function
+ * @param data additional data for the compare function
+ * @return 1, if and only if the list contains the specified element data
+ * @see ucx_list_find()
+ */
+int ucx_list_contains(UcxList *list, void *elem, cmp_func cmpfnc, void *data);
+
+/**
+ * Sorts an UcxList with natural merge sort.
+ * 
+ * This function uses O(n) additional temporary memory for merge operations
+ * that is automatically freed after each merge.
+ * 
+ * As the head of the list might change, you <b>MUST</b> call this function
+ * as follows: <code>mylist = ucx_list_sort(mylist, mycmpfnc, mydata);</code>.
+ * 
+ * @param list the list to sort
+ * @param cmpfnc the function that shall be used to compare the element data
+ * @param data additional data for the cmp_func()
+ * @return the sorted list
+ */
+UcxList *ucx_list_sort(UcxList *list, cmp_func cmpfnc, void *data);
+
+/**
+ * Removes an element from the list.
+ * 
+ * If the first element is removed, the list pointer changes. So it is
+ * <i>highly recommended</i> to <i>always</i> update the pointer by calling
+ * <code>mylist = ucx_list_remove(mylist, myelem);</code>.
+ * 
+ * @param list the list from which the element shall be removed
+ * @param element the element to remove
+ * @return returns the updated list pointer or <code>NULL</code>, if the list
+ * is now empty
+ */
+UcxList *ucx_list_remove(UcxList *list, UcxList *element);
+
+/**
+ * Removes an element from the list using an UcxAllocator.
+ * 
+ * See ucx_list_remove() for details.
+ * 
+ * @param allocator the allocator to use
+ * @param list the list from which the element shall be removed
+ * @param element the element to remove
+ * @return returns the updated list pointer or <code>NULL</code>, if the list
+ * @see ucx_list_remove()
+ */
+UcxList *ucx_list_remove_a(UcxAllocator *allocator, UcxList *list,
+        UcxList *element);
+
+#ifdef	__cplusplus
+}
+#endif
+
+#endif	/* UCX_LIST_H */
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ucx/string.c	Tue Aug 23 13:49:38 2016 +0200
@@ -0,0 +1,381 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2015 Olaf Wintermann. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *   1. Redistributions of source code must retain the above copyright
+ *      notice, this list of conditions and the following disclaimer.
+ *
+ *   2. Redistributions in binary form must reproduce the above copyright
+ *      notice, this list of conditions and the following disclaimer in the
+ *      documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+#include <ctype.h>
+
+#include "string.h"
+#include "allocator.h"
+
+sstr_t sstr(char *cstring) {
+    sstr_t string;
+    string.ptr = cstring;
+    string.length = strlen(cstring);
+    return string;
+}
+
+sstr_t sstrn(char *cstring, size_t length) {
+    sstr_t string;
+    string.ptr = cstring;
+    string.length = length;
+    return string;
+}
+
+size_t sstrnlen(size_t n, sstr_t s, ...) {
+    va_list ap;
+    size_t size = s.length;
+    va_start(ap, s);
+
+    for (size_t i = 1 ; i < n ; i++) {
+        sstr_t str = va_arg(ap, sstr_t);
+        size += str.length;
+    }
+    va_end(ap);
+
+    return size;
+}
+
+static sstr_t sstrvcat_a(
+        UcxAllocator *a,
+        size_t count,
+        sstr_t s1,
+        sstr_t s2,
+        va_list ap) {
+    sstr_t str;
+    str.ptr = NULL;
+    str.length = 0;
+    if(count < 2) {
+        return str;
+    }
+    
+    sstr_t *strings = (sstr_t*) calloc(count, sizeof(sstr_t));
+    if(!strings) {
+        return str;
+    }
+    
+    // get all args and overall length
+    strings[0] = s1;
+    strings[1] = s2;
+    size_t strlen = s1.length + s2.length;
+    for (size_t i=2;i<count;i++) {
+        sstr_t s = va_arg (ap, sstr_t);
+        strings[i] = s;
+        strlen += s.length;
+    }
+    
+    // create new string
+    str.ptr = (char*) almalloc(a, strlen + 1);
+    str.length = strlen;
+    if(!str.ptr) {
+        free(strings);
+        str.length = 0;
+        return str;
+    }
+    
+    // concatenate strings
+    size_t pos = 0;
+    for (size_t i=0;i<count;i++) {
+        sstr_t s = strings[i];
+        memcpy(str.ptr + pos, s.ptr, s.length);
+        pos += s.length;
+    }
+    
+    str.ptr[str.length] = '\0';
+    
+    free(strings);
+    
+    return str;
+}
+
+sstr_t sstrcat(size_t count, sstr_t s1, sstr_t s2, ...) {
+    va_list ap;
+    va_start(ap, s2);
+    sstr_t s = sstrvcat_a(ucx_default_allocator(), count, s1, s2, ap);
+    va_end(ap);
+    return s;
+}
+
+sstr_t sstrcat_a(UcxAllocator *a, size_t count, sstr_t s1, sstr_t s2, ...) {
+    va_list ap;
+    va_start(ap, s2);
+    sstr_t s = sstrvcat_a(a, count, s1, s2, ap);
+    va_end(ap);
+    return s;
+}
+
+sstr_t sstrsubs(sstr_t s, size_t start) {
+    return sstrsubsl (s, start, s.length-start);
+}
+
+sstr_t sstrsubsl(sstr_t s, size_t start, size_t length) {
+    sstr_t new_sstr;
+    if (start >= s.length) {
+        new_sstr.ptr = NULL;
+        new_sstr.length = 0;
+    } else {
+        if (length > s.length-start) {
+            length = s.length-start;
+        }
+        new_sstr.ptr = &s.ptr[start];
+        new_sstr.length = length;
+    }
+    return new_sstr;
+}
+
+sstr_t sstrchr(sstr_t s, int c) {
+    for(size_t i=0;i<s.length;i++) {
+        if(s.ptr[i] == c) {
+            return sstrsubs(s, i);
+        }
+    }
+    sstr_t n;
+    n.ptr = NULL;
+    n.length = 0;
+    return n;
+}
+
+sstr_t sstrrchr(sstr_t s, int c) {
+    if (s.length > 0) {
+        for(size_t i=s.length;i>0;i--) {
+            if(s.ptr[i-1] == c) {
+                return sstrsubs(s, i-1);
+            }
+        }
+    }
+    sstr_t n;
+    n.ptr = NULL;
+    n.length = 0;
+    return n;
+}
+
+sstr_t sstrstr(sstr_t string, sstr_t match) {
+    if (match.length == 0) {
+        return string;
+    }
+    
+    for (size_t i = 0 ; i < string.length ; i++) {
+        sstr_t substr = sstrsubs(string, i);
+        if (sstrprefix(substr, match)) {
+            return substr;
+        }
+    }
+    
+    sstr_t emptystr;
+    emptystr.length = 0;
+    emptystr.ptr = NULL;
+    return emptystr;
+}
+
+sstr_t* sstrsplit(sstr_t s, sstr_t d, ssize_t *n) {
+    return sstrsplit_a(ucx_default_allocator(), s, d, n);
+}
+
+sstr_t* sstrsplit_a(UcxAllocator *allocator, sstr_t s, sstr_t d, ssize_t *n) {
+    if (s.length == 0 || d.length == 0) {
+        *n = -1;
+        return NULL;
+    }
+
+    sstr_t* result;
+    ssize_t nmax = *n;
+    *n = 1;
+
+    /* special case: exact match - no processing needed */
+    if (sstrcmp(s, d) == 0) {
+        *n = 0;
+        return NULL;
+    }
+    sstr_t sv = sstrdup(s);
+    if (sv.length == 0) {
+        *n = -2;
+        return NULL;
+    }
+
+    for (size_t i = 0 ; i < s.length ; i++) {
+        sstr_t substr = sstrsubs(sv, i);
+        if (sstrprefix(substr, d)) {
+            (*n)++;
+            for (size_t j = 0 ; j < d.length ; j++) {
+                sv.ptr[i+j] = 0;
+            }
+            i += d.length - 1; // -1, because the loop will do a i++
+        }
+        if ((*n) == nmax) break;
+    }
+    result = (sstr_t*) almalloc(allocator, sizeof(sstr_t)*(*n));
+
+    if (result) {
+        char *pptr = sv.ptr;
+        for (ssize_t i = 0 ; i < *n ; i++) {
+            size_t l = strlen(pptr);
+            char* ptr = (char*) almalloc(allocator, l + 1);
+            if (ptr) {
+                memcpy(ptr, pptr, l);
+                ptr[l] = 0;
+
+                result[i] = sstrn(ptr, l);
+                pptr += l + d.length;
+            } else {
+                for (ssize_t j = i-1 ; j >= 0 ; j--) {
+                    alfree(allocator, result[j].ptr);
+                }
+                alfree(allocator, result);
+                *n = -2;
+                break;
+            }
+        }
+    } else {
+        *n = -2;
+    }
+    
+    free(sv.ptr);
+
+    return result;
+}
+
+int sstrcmp(sstr_t s1, sstr_t s2) {
+    if (s1.length == s2.length) {
+        return memcmp(s1.ptr, s2.ptr, s1.length);
+    } else if (s1.length > s2.length) {
+        return 1;
+    } else {
+        return -1;
+    }
+}
+
+int sstrcasecmp(sstr_t s1, sstr_t s2) {
+    if (s1.length == s2.length) {
+#ifdef _WIN32
+        return _strnicmp(s1.ptr, s2.ptr, s1.length);
+#else
+        return strncasecmp(s1.ptr, s2.ptr, s1.length);
+#endif
+    } else if (s1.length > s2.length) {
+        return 1;
+    } else {
+        return -1;
+    }
+}
+
+sstr_t sstrdup(sstr_t s) {
+    return sstrdup_a(ucx_default_allocator(), s);
+}
+
+sstr_t sstrdup_a(UcxAllocator *allocator, sstr_t s) {
+    sstr_t newstring;
+    newstring.ptr = (char*)almalloc(allocator, s.length + 1);
+    if (newstring.ptr) {
+        newstring.length = s.length;
+        newstring.ptr[newstring.length] = 0;
+        
+        memcpy(newstring.ptr, s.ptr, s.length);
+    } else {
+        newstring.length = 0;
+    }
+    
+    return newstring;
+}
+
+sstr_t sstrtrim(sstr_t string) {
+    sstr_t newstr = string;
+    
+    while (newstr.length > 0 && isspace(*newstr.ptr)) {
+        newstr.ptr++;
+        newstr.length--;
+    }
+    while (newstr.length > 0 && isspace(newstr.ptr[newstr.length-1])) {
+        newstr.length--;
+    }
+    
+    return newstr;
+}
+
+int sstrprefix(sstr_t string, sstr_t prefix) {
+    if (string.length == 0) {
+        return prefix.length == 0;
+    }
+    if (prefix.length == 0) {
+        return 1;
+    }
+    
+    if (prefix.length > string.length) {
+        return 0;
+    } else {
+        return memcmp(string.ptr, prefix.ptr, prefix.length) == 0;
+    }
+}
+
+int sstrsuffix(sstr_t string, sstr_t suffix) {
+    if (string.length == 0) {
+        return suffix.length == 0;
+    }
+    if (suffix.length == 0) {
+        return 1;
+    }
+    
+    if (suffix.length > string.length) {
+        return 0;
+    } else {
+        return memcmp(string.ptr+string.length-suffix.length,
+            suffix.ptr, suffix.length) == 0;
+    }
+}
+
+sstr_t sstrlower(sstr_t string) {
+    sstr_t ret = sstrdup(string);
+    for (size_t i = 0; i < ret.length ; i++) {
+        ret.ptr[i] = tolower(ret.ptr[i]);
+    }
+    return ret;
+}
+
+sstr_t sstrlower_a(UcxAllocator *allocator, sstr_t string) {
+    sstr_t ret = sstrdup_a(allocator, string);
+    for (size_t i = 0; i < ret.length ; i++) {
+        ret.ptr[i] = tolower(ret.ptr[i]);
+    }
+    return ret;
+}
+
+sstr_t sstrupper(sstr_t string) {
+    sstr_t ret = sstrdup(string);
+    for (size_t i = 0; i < ret.length ; i++) {
+        ret.ptr[i] = toupper(ret.ptr[i]);
+    }
+    return ret;
+}
+
+sstr_t sstrupper_a(UcxAllocator *allocator, sstr_t string) {
+    sstr_t ret = sstrdup_a(allocator, string);
+    for (size_t i = 0; i < ret.length ; i++) {
+        ret.ptr[i] = toupper(ret.ptr[i]);
+    }
+    return ret;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ucx/string.h	Tue Aug 23 13:49:38 2016 +0200
@@ -0,0 +1,457 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2015 Olaf Wintermann. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *   1. Redistributions of source code must retain the above copyright
+ *      notice, this list of conditions and the following disclaimer.
+ *
+ *   2. Redistributions in binary form must reproduce the above copyright
+ *      notice, this list of conditions and the following disclaimer in the
+ *      documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+/**
+ * Bounded string implementation.
+ * 
+ * The UCX strings (<code>sstr_t</code>) provide an alternative to C strings.
+ * The main difference to C strings is, that <code>sstr_t</code> does <b>not
+ * need to be <code>NULL</code>-terminated</b>. Instead the length is stored
+ * within the structure.
+ * 
+ * When using <code>sstr_t</code>, developers must be full aware of what type
+ * of string (<code>NULL</code>-terminated) or not) they are using, when 
+ * accessing the <code>char* ptr</code> directly.
+ * 
+ * The UCX string module provides some common string functions, known from
+ * standard libc, working with <code>sstr_t</code>.
+ * 
+ * @file   string.h
+ * @author Mike Becker
+ * @author Olaf Wintermann
+ */
+
+#ifndef UCX_STRING_H
+#define	UCX_STRING_H
+
+#include "ucx.h"
+#include "allocator.h"
+#include <stddef.h>
+
+/** Shortcut for a <code>sstr_t struct</code> literal. */
+#define ST(s) { (char*)s, sizeof(s)-1 }
+
+/** Shortcut for the conversion of a C string to a <code>sstr_t</code>. */
+#define S(s) sstrn((char*)s, sizeof(s)-1)
+
+#ifdef	__cplusplus
+extern "C" {
+#endif
+
+/**
+ * The UCX string structure.
+ */
+typedef struct {
+   /** A reference to the string (<b>not necessarily  <code>NULL</code>
+    * -terminated</b>) */
+    char   *ptr;
+    /** The length of the string */
+    size_t length;
+} sstr_t;
+
+/**
+ * Creates a new sstr_t based on a C string.
+ * 
+ * The length is implicitly inferred by using a call to <code>strlen()</code>.
+ *
+ * <b>Note:</b> the sstr_t will hold a <i>reference</i> to the C string. If you
+ * do want a copy, use sstrdup() on the return value of this function.
+ * 
+ * @param cstring the C string to wrap
+ * @return a new sstr_t containing the C string
+ * 
+ * @see sstrn()
+ */
+sstr_t sstr(char *cstring);
+
+/**
+ * Creates a new sstr_t of the specified length based on a C string.
+ *
+ * <b>Note:</b> the sstr_t will hold a <i>reference</i> to the C string. If you
+ * do want a copy, use sstrdup() on the return value of this function.
+ * 
+ * @param cstring  the C string to wrap
+ * @param length   the length of the string
+ * @return a new sstr_t containing the C string
+ * 
+ * @see sstr()
+ * @see S()
+ */
+sstr_t sstrn(char *cstring, size_t length);
+
+
+/**
+ * Returns the cumulated length of all specified strings.
+ *
+ * At least one string must be specified.
+ * 
+ * <b>Attention:</b> if the count argument does not match the count of the
+ * specified strings, the behavior is undefined.
+ *
+ * @param count    the total number of specified strings (so at least 1)
+ * @param string   the first string
+ * @param ...      all other strings
+ * @return the cumulated length of all strings
+ */
+size_t sstrnlen(size_t count, sstr_t string, ...);
+
+/**
+ * Concatenates two or more strings.
+ * 
+ * The resulting string will be allocated by standard <code>malloc()</code>. 
+ * So developers <b>MUST</b> pass the sstr_t.ptr to <code>free()</code>.
+ * 
+ * The sstr_t.ptr of the return value will <i>always</i> be <code>NULL</code>-
+ * terminated.
+ *
+ * @param count   the total number of strings to concatenate
+ * @param s1      first string
+ * @param s2      second string
+ * @param ...     all remaining strings
+ * @return the concatenated string
+ */
+sstr_t sstrcat(size_t count, sstr_t s1, sstr_t s2, ...);
+
+/**
+ * Concatenates two or more strings using an UcxAllocator.
+ * 
+ * See sstrcat() for details.
+ *
+ * @param a       the allocator to use
+ * @param count   the total number of strings to concatenate
+ * @param s1      first string
+ * @param s2      second string
+ * @param ...     all remaining strings
+ * @return the concatenated string
+ */
+sstr_t sstrcat_a(UcxAllocator *a, size_t count, sstr_t s1, sstr_t s2, ...);
+
+
+/**
+ * Returns a substring starting at the specified location.
+ * 
+ * <b>Attention:</b> the new string references the same memory area as the
+ * input string and will <b>NOT</b> be <code>NULL</code>-terminated.
+ * Use sstrdup() to get a copy.
+ * 
+ * @param string input string
+ * @param start  start location of the substring
+ * @return a substring of <code>string</code> starting at <code>start</code>
+ * 
+ * @see sstrsubsl()
+ * @see sstrchr()
+ */
+sstr_t sstrsubs(sstr_t string, size_t start);
+
+/**
+ * Returns a substring with a maximum length starting at the specified location.
+ * 
+ * <b>Attention:</b> the new string references the same memory area as the
+ * input string and will <b>NOT</b> be <code>NULL</code>-terminated.
+ * Use sstrdup() to get a copy.
+ * 
+ * @param string input string
+ * @param start  start location of the substring
+ * @param length the maximum length of the substring
+ * @return a substring of <code>string</code> starting at <code>start</code>
+ * with a maximum length of <code>length</code>
+ * 
+ * @see sstrsubs()
+ * @see sstrchr()
+ */
+sstr_t sstrsubsl(sstr_t string, size_t start, size_t length);
+
+/**
+ * Returns a substring starting at the location of the first occurrence of the
+ * specified character.
+ * 
+ * If the string does not contain the character, an empty string is returned.
+ * 
+ * @param string the string where to locate the character
+ * @param chr    the character to locate
+ * @return       a substring starting at the first location of <code>chr</code>
+ * 
+ * @see sstrsubs()
+ */
+sstr_t sstrchr(sstr_t string, int chr);
+
+/**
+ * Returns a substring starting at the location of the last occurrence of the
+ * specified character.
+ * 
+ * If the string does not contain the character, an empty string is returned.
+ * 
+ * @param string the string where to locate the character
+ * @param chr    the character to locate
+ * @return       a substring starting at the last location of <code>chr</code>
+ * 
+ * @see sstrsubs()
+ */
+sstr_t sstrrchr(sstr_t string, int chr);
+
+/**
+ * Returns a substring starting at the location of the first occurrence of the
+ * specified string.
+ * 
+ * If the string does not contain the other string, an empty string is returned.
+ * 
+ * If <code>match</code> is an empty string, the complete <code>string</code> is
+ * returned.
+ * 
+ * @param string the string to be scanned
+ * @param match  string containing the sequence of characters to match
+ * @return       a substring starting at the first occurrence of
+ *               <code>match</code>, or an empty string, if the sequence is not
+ *               present in <code>string</code>
+ */
+sstr_t sstrstr(sstr_t string, sstr_t match);
+
+/**
+ * Splits a string into parts by using a delimiter string.
+ * 
+ * This function will return <code>NULL</code>, if one of the following happens:
+ * <ul>
+ *   <li>the string length is zero</li>
+ *   <li>the delimeter length is zero</li>
+ *   <li>the string equals the delimeter</li>
+ *   <li>memory allocation fails</li>
+ * </ul>
+ * 
+ * The integer referenced by <code>count</code> is used as input and determines
+ * the maximum size of the resulting array, i.e. the maximum count of splits to
+ * perform + 1.
+ * 
+ * The integer referenced by <code>count</code> is also used as output and is
+ * set to
+ * <ul>
+ *   <li>-2, on memory allocation errors</li>
+ *   <li>-1, if either the string or the delimiter is an empty string</li>
+ *   <li>0, if the string equals the delimiter</li>
+ *   <li>1, if the string does not contain the delimiter</li>
+ *   <li>the count of array items, otherwise</li>
+ * </ul>
+ * 
+ * If the string starts with the delimiter, the first item of the resulting
+ * array will be an empty string.
+ * 
+ * If the string ends with the delimiter and the maximum list size is not
+ * exceeded, the last array item will be an empty string.
+ * 
+ * <b>Attention:</b> The array pointer <b>AND</b> all sstr_t.ptr of the array
+ * items must be manually passed to <code>free()</code>. Use sstrsplit_a() with
+ * an allocator to managed memory, to avoid this.
+ *
+ * @param string the string to split
+ * @param delim  the delimiter string
+ * @param count  IN: the maximum size of the resulting array (0 = no limit),
+ *               OUT: the actual size of the array
+ * @return a sstr_t array containing the split strings or
+ *         <code>NULL</code> on error
+ * 
+ * @see sstrsplit_a()
+ */
+sstr_t* sstrsplit(sstr_t string, sstr_t delim, ssize_t *count);
+
+/**
+ * Performing sstrsplit() using an UcxAllocator.
+ * 
+ * <i>Read the description of sstrsplit() for details.</i>
+ * 
+ * The memory for the sstr_t.ptr pointers of the array items and the memory for
+ * the sstr_t array itself are allocated by using the UcxAllocator.malloc()
+ * function.
+ * 
+ * <b>Note:</b> the allocator is not used for memory that is freed within the
+ * same call of this function (locally scoped variables).
+ * 
+ * @param allocator the UcxAllocator used for allocating memory
+ * @param string the string to split
+ * @param delim  the delimiter string
+ * @param count  IN: the maximum size of the resulting array (0 = no limit),
+ *               OUT: the actual size of the array
+ * @return a sstr_t array containing the split strings or
+ *         <code>NULL</code> on error
+ * 
+ * @see sstrsplit()
+ */
+sstr_t* sstrsplit_a(UcxAllocator *allocator, sstr_t string, sstr_t delim,
+        ssize_t *count);
+
+/**
+ * Compares two UCX strings with standard <code>memcmp()</code>.
+ * 
+ * At first it compares the sstr_t.length attribute of the two strings. The
+ * <code>memcmp()</code> function is called, if and only if the lengths match.
+ * 
+ * @param s1 the first string
+ * @param s2 the second string
+ * @return -1, if the length of s1 is less than the length of s2 or 1, if the 
+ * length of s1 is greater than the length of s2 or the result of
+ * <code>memcmp()</code> otherwise (i.e. 0 if the strings match)
+ */
+int sstrcmp(sstr_t s1, sstr_t s2);
+
+/**
+ * Compares two UCX strings ignoring the case.
+ * 
+ * At first it compares the sstr_t.length attribute of the two strings. If and
+ * only if the lengths match, both strings are compared char by char ignoring
+ * the case.
+ * 
+ * @param s1 the first string
+ * @param s2 the second string
+ * @return -1, if the length of s1 is less than the length of s2 or 1, if the 
+ * length of s1 is greater than the length of s2 or the difference between the
+ * first two differing characters otherwise (i.e. 0 if the strings match and
+ * no characters differ)
+ */
+int sstrcasecmp(sstr_t s1, sstr_t s2);
+
+/**
+ * Creates a duplicate of the specified string.
+ * 
+ * The new sstr_t will contain a copy allocated by standard
+ * <code>malloc()</code>. So developers <b>MUST</b> pass the sstr_t.ptr to
+ * <code>free()</code>.
+ * 
+ * The sstr_t.ptr of the return value will <i>always</i> be <code>NULL</code>-
+ * terminated.
+ * 
+ * @param string the string to duplicate
+ * @return a duplicate of the string
+ * @see sstrdup_a()
+ */
+sstr_t sstrdup(sstr_t string);
+
+/**
+ * Creates a duplicate of the specified string using an UcxAllocator.
+ * 
+ * The new sstr_t will contain a copy allocated by the allocators
+ * ucx_allocator_malloc function. So it is implementation depended, whether the
+ * returned sstr_t.ptr pointer must be passed to the allocators
+ * ucx_allocator_free function manually.
+ * 
+ * The sstr_t.ptr of the return value will <i>always</i> be <code>NULL</code>-
+ * terminated.
+ * 
+ * @param allocator a valid instance of an UcxAllocator
+ * @param string the string to duplicate
+ * @return a duplicate of the string
+ * @see sstrdup()
+ */
+sstr_t sstrdup_a(UcxAllocator *allocator, sstr_t string);
+
+/**
+ * Omits leading and trailing spaces.
+ * 
+ * This function returns a new sstr_t containing a trimmed version of the
+ * specified string.
+ * 
+ * <b>Note:</b> the new sstr_t references the same memory, thus you
+ * <b>MUST NOT</b> pass the sstr_t.ptr of the return value to
+ * <code>free()</code>. It is also highly recommended to avoid assignments like
+ * <code>mystr = sstrtrim(mystr);</code> as you lose the reference to the
+ * source string. Assignments of this type are only permitted, if the
+ * sstr_t.ptr of the source string does not need to be freed or if another
+ * reference to the source string exists.
+ * 
+ * @param string the string that shall be trimmed
+ * @return a new sstr_t containing the trimmed string
+ */
+sstr_t sstrtrim(sstr_t string);
+
+/**
+ * Checks, if a string has a specific prefix.
+ * @param string the string to check
+ * @param prefix the prefix the string should have
+ * @return 1, if and only if the string has the specified prefix, 0 otherwise
+ */
+int sstrprefix(sstr_t string, sstr_t prefix);
+
+/**
+ * Checks, if a string has a specific suffix.
+ * @param string the string to check
+ * @param suffix the suffix the string should have
+ * @return 1, if and only if the string has the specified suffix, 0 otherwise
+ */
+int sstrsuffix(sstr_t string, sstr_t suffix);
+
+/**
+ * Returns a lower case version of a string.
+ * 
+ * This function creates a duplicate of the input string, first. See the
+ * documentation of sstrdup() for the implications.
+ * 
+ * @param string the input string
+ * @return the resulting lower case string
+ * @see sstrdup()
+ */
+sstr_t sstrlower(sstr_t string);
+
+/**
+ * Returns a lower case version of a string.
+ * 
+ * This function creates a duplicate of the input string, first. See the
+ * documentation of sstrdup_a() for the implications.
+ * 
+ * @param allocator the allocator used for duplicating the string
+ * @param string the input string
+ * @return the resulting lower case string
+ * @see sstrdup_a()
+ */
+sstr_t sstrlower_a(UcxAllocator *allocator, sstr_t string);
+
+/**
+ * Returns a upper case version of a string.
+ * 
+ * This function creates a duplicate of the input string, first. See the
+ * documentation of sstrdup() for the implications.
+ * 
+ * @param string the input string
+ * @return the resulting upper case string
+ * @see sstrdup()
+ */
+sstr_t sstrupper(sstr_t string);
+
+/**
+ * Returns a upper case version of a string.
+ * 
+ * This function creates a duplicate of the input string, first. See the
+ * documentation of sstrdup_a() for the implications.
+ * 
+ * @param allocator the allocator used for duplicating the string
+ * @param string the input string
+ * @return the resulting upper case string
+ * @see sstrdup_a()
+ */
+sstr_t sstrupper_a(UcxAllocator *allocator, sstr_t string);
+
+#ifdef	__cplusplus
+}
+#endif
+
+#endif	/* UCX_STRING_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ucx/ucx.h	Tue Aug 23 13:49:38 2016 +0200
@@ -0,0 +1,138 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2015 Olaf Wintermann. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *   1. Redistributions of source code must retain the above copyright
+ *      notice, this list of conditions and the following disclaimer.
+ *
+ *   2. Redistributions in binary form must reproduce the above copyright
+ *      notice, this list of conditions and the following disclaimer in the
+ *      documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+/**
+ * Main UCX Header providing most common definitions.
+ * 
+ * @file   ucx.h
+ * @author Mike Becker
+ * @author Olaf Wintermann
+ */
+
+#ifndef UCX_H
+#define	UCX_H
+
+/** Major UCX version as integer constant. */
+#define UCX_VERSION_MAJOR   0
+
+/** Minor UCX version as integer constant. */
+#define UCX_VERSION_MINOR   9
+
+/** The UCX version in format [major].[minor] */
+#define UCX_VERSION UCX_VERSION_MAJOR.UCX_VERSION_MINOR
+
+#include <stdlib.h>
+
+#ifdef _WIN32
+#if !(defined __ssize_t_defined || defined _SSIZE_T_)
+#include <BaseTsd.h>
+typedef SSIZE_T ssize_t;
+#define __ssize_t_defined
+#define _SSIZE_T_
+#endif /* __ssize_t_defined and _SSIZE_T */
+#else /* !_WIN32 */
+#include <sys/types.h>
+#endif /* _WIN32 */
+
+#ifdef	__cplusplus
+#ifndef _Bool
+#define _Bool bool
+#define restrict
+#endif
+/** Use C naming even when compiling with C++.  */
+#define UCX_EXTERN extern "C"
+extern "C" {
+#else
+/** Pointless in C. */
+#define UCX_EXTERN
+#endif
+    
+
+/**
+ * A function pointer to a destructor function.
+ * @see ucx_mempool_setdestr()
+ * @see ucx_mempool_regdestr()
+ */
+typedef void(*ucx_destructor)(void*);
+
+/**
+ * Function pointer to a compare function.
+ * 
+ * The compare function shall take three arguments: the two values that shall be
+ * compared and optional additional data.
+ * The function shall then return -1 if the first argument is less than the
+ * second argument, 1 if the first argument is greater than the second argument
+ * and 0 if both arguments are equal. If the third argument is
+ * <code>NULL</code>, it shall be ignored.
+ */
+typedef int(*cmp_func)(void*,void*,void*);
+
+/**
+ * Function pointer to a copy function.
+ * 
+ * The copy function shall create a copy of the first argument and may use
+ * additional data provided by the second argument. If the second argument is
+ * <code>NULL</code>, it shall be ignored.
+
+ * <b>Attention:</b> if pointers returned by functions of this type may be
+ * passed to <code>free()</code> depends on the implementation of the
+ * respective <code>copy_func</code>.
+ */
+typedef void*(*copy_func)(void*,void*);
+
+/**
+ * Function pointer to a write function.
+ * 
+ * The signature of the write function shall be compatible to the signature
+ * of standard <code>fwrite</code>, though it may use arbitrary data types for
+ * source and destination.
+ * 
+ * The arguments shall contain (in ascending order): a pointer to the source,
+ * the length of one element, the element count and a pointer to the
+ * destination.
+ */
+typedef size_t(*write_func)(const void*, size_t, size_t, void*);
+
+/**
+ * Function pointer to a read function.
+ * 
+ * The signature of the read function shall be compatible to the signature
+ * of standard <code>fread</code>, though it may use arbitrary data types for
+ * source and destination.
+ * 
+ * The arguments shall contain (in ascending order): a pointer to the
+ * destination, the length of one element, the element count and a pointer to
+ * the source.
+ */
+typedef size_t(*read_func)(void*, size_t, size_t, void*);
+
+#ifdef	__cplusplus
+}
+#endif
+
+#endif	/* UCX_H */
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ucx/utils.c	Tue Aug 23 13:49:38 2016 +0200
@@ -0,0 +1,259 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2015 Olaf Wintermann. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *   1. Redistributions of source code must retain the above copyright
+ *      notice, this list of conditions and the following disclaimer.
+ *
+ *   2. Redistributions in binary form must reproduce the above copyright
+ *      notice, this list of conditions and the following disclaimer in the
+ *      documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "utils.h"
+#include <math.h>
+#include <stdio.h>
+#include <limits.h>
+#include <errno.h>
+
+/* COPY FUCNTIONS */
+void* ucx_strcpy(void* s, void* data) {
+    char *str = (char*) s;
+    size_t n = 1+strlen(str);
+    char *cpy = (char*) malloc(n);
+    memcpy(cpy, str, n);
+    return cpy;
+}
+
+void* ucx_memcpy(void* m, void* n) {
+    size_t k = *((size_t*)n);
+    void *cpy = malloc(k);
+    memcpy(cpy, m, k);
+    return cpy;
+}
+
+size_t ucx_stream_copy(void *src, void *dest, read_func readfnc,
+        write_func writefnc, char* buf, size_t bufsize, size_t n) {
+    if(n == 0 || bufsize == 0) {
+        return 0;
+    }
+    
+    char *lbuf;    
+    size_t ncp = 0;
+    
+    if(buf) {
+        lbuf = buf;
+    } else {
+        lbuf = (char*)malloc(bufsize);
+        if(lbuf == NULL) {
+            return 0;
+        }
+    }
+    
+    size_t r;
+    size_t rn = bufsize > n ? n : bufsize;
+    while((r = readfnc(lbuf, 1, rn, src)) != 0) {
+        r = writefnc(lbuf, 1, r, dest);
+        ncp += r;
+        n -= r;
+        rn = bufsize > n ? n : bufsize;
+        if(r == 0 || n == 0) {
+            break;
+        }
+    }
+    
+    if (lbuf != buf) {
+        free(lbuf);
+    }
+    
+    return ncp;
+}
+
+/* COMPARE FUNCTIONS */
+
+int ucx_strcmp(void *s1, void *s2, void *data) {
+    return strcmp((char*)s1, (char*)s2);
+}
+
+int ucx_strncmp(void *s1, void *s2, void *n) {
+    return strncmp((char*)s1, (char*)s2, *((size_t*) n));
+}
+
+int ucx_intcmp(void *i1, void *i2, void *data) {
+   int a = *((int*) i1);
+   int b = *((int*) i2);
+   if (a == b) {
+       return 0;
+   } else {
+       return a < b ? -1 : 1;
+   }
+}
+
+int ucx_floatcmp(void *f1, void *f2, void *epsilon) {
+   float a = *((float*) f1);
+   float b = *((float*) f2);
+   float e = !epsilon ? 1e-6f : *((float*)epsilon);
+   if (fabsf(a - b) < e) {
+       return 0;
+   } else {
+       return a < b ? -1 : 1;
+   }
+}
+
+int ucx_doublecmp(void *d1, void *d2, void *epsilon) {
+   double a = *((float*) d1);
+   double b = *((float*) d2);
+   double e = !epsilon ? 1e-14 : *((double*)epsilon);
+   if (fabs(a - b) < e) {
+       return 0;
+   } else {
+       return a < b ? -1 : 1;
+   }
+}
+
+int ucx_ptrcmp(void *ptr1, void *ptr2, void *data) {
+    intptr_t p1 = (intptr_t) ptr1;
+    intptr_t p2 = (intptr_t) ptr2;
+    if (p1 == p2) {
+        return 0;
+    } else {
+        return p1  < p2 ? -1 : 1;
+    }
+}
+
+int ucx_memcmp(void *ptr1, void *ptr2, void *n) {
+    return memcmp(ptr1, ptr2, *((size_t*)n));
+}
+
+/* PRINTF FUNCTIONS */
+
+#ifdef va_copy
+#define UCX_PRINTF_BUFSIZE 256
+#else
+#pragma message("WARNING: C99 va_copy macro not supported by this platform" \
+                " - limiting ucx_*printf to 2 KiB")
+#define UCX_PRINTF_BUFSIZE 0x800
+#endif
+
+int ucx_fprintf(void *stream, write_func wfc, const char *fmt, ...) {
+    int ret;
+    va_list ap;
+    va_start(ap, fmt);
+    ret = ucx_vfprintf(stream, wfc, fmt, ap);
+    va_end(ap);
+    return ret;
+}
+
+int ucx_vfprintf(void *stream, write_func wfc, const char *fmt, va_list ap) {
+    char buf[UCX_PRINTF_BUFSIZE];
+#ifdef va_copy
+    va_list ap2;
+    va_copy(ap2, ap);
+    int ret = vsnprintf(buf, UCX_PRINTF_BUFSIZE, fmt, ap);
+    if (ret < 0) {
+        return ret;
+    } else if (ret < UCX_PRINTF_BUFSIZE) {
+        return (int)wfc(buf, 1, ret, stream);
+    } else {
+        if (ret == INT_MAX) {
+            errno = ENOMEM;
+            return -1;
+        }
+        
+        int len = ret + 1;
+        char *newbuf = (char*)malloc(len);
+        if (!newbuf) {
+            return -1;
+        }
+        
+        ret = vsnprintf(newbuf, len, fmt, ap2);
+        if (ret > 0) {
+            ret = (int)wfc(newbuf, 1, ret, stream);
+        }
+        free(newbuf);
+    }
+    return ret;
+#else
+    int ret = vsnprintf(buf, UCX_PRINTF_BUFSIZE, fmt, ap);
+    if (ret < 0) {
+        return ret;
+    } else if (ret < UCX_PRINTF_BUFSIZE) {
+        return (int)wfc(buf, 1, ret, stream);
+    } else {
+        errno = ENOMEM;
+        return -1;
+    }
+#endif
+}
+
+sstr_t ucx_asprintf(UcxAllocator *allocator, const char *fmt, ...) {
+    va_list ap;
+    sstr_t ret;
+    va_start(ap, fmt);
+    ret = ucx_vasprintf(allocator, fmt, ap);
+    va_end(ap);
+    return ret;
+}
+
+sstr_t ucx_vasprintf(UcxAllocator *a, const char *fmt, va_list ap) {
+    sstr_t s;
+    s.ptr = NULL;
+    s.length = 0;
+    char buf[UCX_PRINTF_BUFSIZE];
+#ifdef va_copy
+    va_list ap2;
+    va_copy(ap2, ap);
+    int ret = vsnprintf(buf, UCX_PRINTF_BUFSIZE, fmt, ap);
+    if (ret > 0 && ret < UCX_PRINTF_BUFSIZE) {
+        s.ptr = (char*)almalloc(a, ret + 1);
+        if (s.ptr) {
+            s.length = (size_t)ret;
+            memcpy(s.ptr, buf, ret);
+            s.ptr[s.length] = '\0';
+        }
+    } else if (ret == INT_MAX) {
+        errno = ENOMEM;
+    } else  {
+        int len = ret + 1;
+        s.ptr = (char*)almalloc(a, len);
+        if (s.ptr) {
+            ret = vsnprintf(s.ptr, len, fmt, ap2);
+            if (ret < 0) {
+                free(s.ptr);
+                s.ptr = NULL;
+            } else {
+                s.length = (size_t)ret;
+            }
+        }
+    }
+#else
+    int ret = vsnprintf(buf, UCX_PRINTF_BUFSIZE, fmt, ap);
+    if (ret > 0 && ret < UCX_PRINTF_BUFSIZE) {
+        s.ptr = (char*)almalloc(a, ret + 1);
+        if (s.ptr) {
+            s.length = (size_t)ret;
+            memcpy(s.ptr, buf, ret);
+            s.ptr[s.length] = '\0';
+        }
+    } else {
+        errno = ENOMEM;
+    }
+#endif
+    return s;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ucx/utils.h	Tue Aug 23 13:49:38 2016 +0200
@@ -0,0 +1,254 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2015 Olaf Wintermann. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *   1. Redistributions of source code must retain the above copyright
+ *      notice, this list of conditions and the following disclaimer.
+ *
+ *   2. Redistributions in binary form must reproduce the above copyright
+ *      notice, this list of conditions and the following disclaimer in the
+ *      documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * @file utils.h
+ * 
+ * Compare, copy and printf functions.
+ * 
+ * @author Mike Becker
+ * @author Olaf Wintermann
+ */
+
+#ifndef UCX_UTILS_H
+#define UCX_UTILS_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "ucx.h"
+#include "string.h"
+#include "allocator.h"
+#include <inttypes.h>
+#include <string.h>
+#include <stdarg.h>
+
+/**
+ * Copies a string.
+ * @param s the string to copy
+ * @param data omitted
+ * @return a pointer to a copy of s1 that can be passed to free(void*)
+ */
+void *ucx_strcpy(void *s, void *data);
+
+/**
+ * Copies a memory area.
+ * @param m a pointer to the memory area
+ * @param n a pointer to the size_t containing the size of the memory area
+ * @return a pointer to a copy of the specified memory area that can
+ * be passed to free(void*)
+ */
+void *ucx_memcpy(void *m, void *n);
+
+
+/**
+ * Reads data from a stream and writes it to another stream.
+ * 
+ * @param src the source stream
+ * @param dest the destination stream
+ * @param rfnc the read function
+ * @param wfnc the write function
+ * @param buf a pointer to the copy buffer or <code>NULL</code> if a buffer
+ * shall be implicitly created on the heap
+ * @param bufsize the size of the copy buffer - if <code>NULL</code> was
+ * provided for <code>buf</code>, this is the size of the buffer that shall be
+ * implicitly created
+ * @param n the maximum number of bytes that shall be copied
+ * @return the total number of bytes copied
+  */
+size_t ucx_stream_copy(void *src, void *dest, read_func rfnc, write_func wfnc,
+        char* buf, size_t bufsize, size_t n);
+
+/**
+ * Shorthand for ucx_stream_copy using the default copy buffer.
+ * 
+ * @param src the source stream
+ * @param dest the destination stream
+ * @param rfnc the read function
+ * @param wfnc the write function
+ * @return total number of bytes copied
+ */
+#define ucx_stream_hcopy(src,dest,rfnc,wfnc) ucx_stream_copy(\
+        src, dest, (read_func)rfnc, (write_func)wfnc, NULL, 0x100, (size_t)-1)
+
+/**
+ * Shorthand for ucx_stream_copy using the default copy buffer and a copy limit.
+ * 
+ * @param src the source stream
+ * @param dest the destination stream
+ * @param rfnc the read function
+ * @param wfnc the write function
+ * @param n maximum number of bytes that shall be copied
+ * @return total number of bytes copied
+ */
+#define ucx_stream_ncopy(src,dest,rfnc,wfnc, n) ucx_stream_copy(\
+        src, dest, (read_func)rfnc, (write_func)wfnc, NULL, 0x100, n)
+
+/**
+ * Wraps the strcmp function.
+ * @param s1 string one
+ * @param s2 string two
+ * @param data omitted
+ * @return the result of strcmp(s1, s2)
+ */
+int ucx_strcmp(void *s1, void *s2, void *data);
+
+/**
+ * Wraps the strncmp function.
+ * @param s1 string one
+ * @param s2 string two
+ * @param n a pointer to the size_t containing the third strncmp parameter
+ * @return the result of strncmp(s1, s2, *n)
+ */
+int ucx_strncmp(void *s1, void *s2, void *n);
+
+/**
+ * Compares two integers of type int.
+ * @param i1 pointer to integer one
+ * @param i2 pointer to integer two
+ * @param data omitted
+ * @return -1, if *i1 is less than *i2, 0 if both are equal,
+ * 1 if *i1 is greater than *i2
+ */
+int ucx_intcmp(void *i1, void *i2, void *data);
+
+/**
+ * Compares two real numbers of type float.
+ * @param f1 pointer to float one
+ * @param f2 pointer to float two
+ * @param data if provided: a pointer to precision (default: 1e-6f)
+ * @return -1, if *f1 is less than *f2, 0 if both are equal,
+ * 1 if *f1 is greater than *f2
+ */
+
+int ucx_floatcmp(void *f1, void *f2, void *data);
+
+/**
+ * Compares two real numbers of type double.
+ * @param d1 pointer to double one
+ * @param d2 pointer to double two
+ * @param data if provided: a pointer to precision (default: 1e-14)
+ * @return -1, if *d1 is less than *d2, 0 if both are equal,
+ * 1 if *d1 is greater than *d2
+ */
+int ucx_doublecmp(void *d1, void *d2, void *data);
+
+/**
+ * Compares two pointers.
+ * @param ptr1 pointer one
+ * @param ptr2 pointer two
+ * @param data omitted
+ * @return -1 if ptr1 is less than ptr2, 0 if both are equal,
+ * 1 if ptr1 is greater than ptr2
+ */
+int ucx_ptrcmp(void *ptr1, void *ptr2, void *data);
+
+/**
+ * Compares two memory areas.
+ * @param ptr1 pointer one
+ * @param ptr2 pointer two
+ * @param n a pointer to the size_t containing the third parameter for memcmp
+ * @return the result of memcmp(ptr1, ptr2, *n)
+ */
+int ucx_memcmp(void *ptr1, void *ptr2, void *n);
+
+/**
+ * A <code>printf()</code> like function which writes the output to a stream by
+ * using a write_func().
+ * @param stream the stream the data is written to
+ * @param wfc the write function
+ * @param fmt format string
+ * @param ... additional arguments
+ * @return the total number of bytes written
+ */
+int ucx_fprintf(void *stream, write_func wfc, const char *fmt, ...);
+
+/**
+ * <code>va_list</code> version of ucx_fprintf().
+ * @param stream the stream the data is written to
+ * @param wfc the write function
+ * @param fmt format string
+ * @param ap argument list
+ * @return the total number of bytes written
+ * @see ucx_fprintf()
+ */
+int ucx_vfprintf(void *stream, write_func wfc, const char *fmt, va_list ap);
+
+/**
+ * A <code>printf()</code> like function which allocates space for a sstr_t
+ * the result is written to.
+ * 
+ * <b>Attention</b>: The sstr_t data is allocated with the allocators
+ * ucx_allocator_malloc() function. So it is implementation dependent, if
+ * the returned sstr_t.ptr pointer must be passed to the allocators
+ * ucx_allocator_free() function manually.
+ * 
+ * <b>Note</b>: The sstr_t.ptr of the return value will <i>always</i> be
+ * <code>NULL</code>-terminated.
+ * 
+ * @param allocator the UcxAllocator used for allocating the result sstr_t
+ * @param fmt format string
+ * @param ... additional arguments
+ * @return a sstr_t containing the formatted string
+ */
+sstr_t ucx_asprintf(UcxAllocator *allocator, const char *fmt, ...);
+
+/** Shortcut for ucx_asprintf() with default allocator. */
+#define ucx_sprintf(fmt, ...) \
+    ucx_asprintf(ucx_default_allocator(), fmt, __VA_ARGS__)
+
+/**
+ * <code>va_list</code> version of ucx_asprintf().
+ * 
+ * @param allocator the UcxAllocator used for allocating the result sstr_t
+ * @param fmt format string
+ * @param ap argument list
+ * @return a sstr_t containing the formatted string
+ * @see ucx_asprintf()
+ */
+sstr_t ucx_vasprintf(UcxAllocator *allocator, const char *fmt, va_list ap);
+
+/**
+ * A <code>printf()</code> like function which writes the output to an
+ * UcxBuffer.
+ * 
+ * @param buffer the buffer the data is written to
+ * @param ... format string and additional arguments
+ * @return the total number of bytes written
+ * @see ucx_fprintf()
+ */
+#define ucx_bprintf(buffer, ...) ucx_fprintf((UcxBuffer*)buffer, \
+        (write_func)ucx_buffer_write, __VA_ARGS__)
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* UCX_UTILS_H */
+

mercurial