added support for icons for the table widget (GTK)

Mon, 23 May 2016 12:28:32 +0200

author
Olaf Wintermann <olaf.wintermann@gmail.com>
date
Mon, 23 May 2016 12:28:32 +0200
changeset 124
80609f9675f1
parent 123
55adc92e7c09
child 125
3335268a8073

added support for icons for the table widget (GTK)

application/main.c file | annotate | diff | comparison | revisions
ucx/allocator.c file | annotate | diff | comparison | revisions
ucx/allocator.h file | annotate | diff | comparison | revisions
ucx/avl.c file | annotate | diff | comparison | revisions
ucx/avl.h file | annotate | diff | comparison | revisions
ucx/buffer.c file | annotate | diff | comparison | revisions
ucx/buffer.h file | annotate | diff | comparison | revisions
ucx/list.c file | annotate | diff | comparison | revisions
ucx/list.h file | annotate | diff | comparison | revisions
ucx/logging.c file | annotate | diff | comparison | revisions
ucx/logging.h file | annotate | diff | comparison | revisions
ucx/map.c file | annotate | diff | comparison | revisions
ucx/map.h file | annotate | diff | comparison | revisions
ucx/mempool.c file | annotate | diff | comparison | revisions
ucx/mempool.h file | annotate | diff | comparison | revisions
ucx/properties.c file | annotate | diff | comparison | revisions
ucx/properties.h file | annotate | diff | comparison | revisions
ucx/stack.c file | annotate | diff | comparison | revisions
ucx/stack.h file | annotate | diff | comparison | revisions
ucx/string.c file | annotate | diff | comparison | revisions
ucx/string.h file | annotate | diff | comparison | revisions
ucx/test.c file | annotate | diff | comparison | revisions
ucx/test.h file | annotate | diff | comparison | revisions
ucx/ucx.c file | annotate | diff | comparison | revisions
ucx/ucx.h file | annotate | diff | comparison | revisions
ucx/utils.c file | annotate | diff | comparison | revisions
ucx/utils.h file | annotate | diff | comparison | revisions
ui/gtk/model.c file | annotate | diff | comparison | revisions
ui/gtk/tree.c file | annotate | diff | comparison | revisions
ui/motif/container.c file | annotate | diff | comparison | revisions
ui/motif/container.h file | annotate | diff | comparison | revisions
ui/ui/tree.h file | annotate | diff | comparison | revisions
--- a/application/main.c	Tue Feb 16 17:39:33 2016 +0100
+++ b/application/main.c	Mon May 23 12:28:32 2016 +0200
@@ -97,6 +97,15 @@
     //*/
 }
 
+void* model_getval(void *obj, int col) {
+    if(col == 0) {
+        return "folder";
+    } else if(col == 2) {
+        return "test@localhost";
+    }
+    return obj;
+}
+
 UIMENU ctxmenu;
 void click(UiEvent *event, void *data) {
     UiMouseEvent *me = event->eventdata;
@@ -141,22 +150,9 @@
     ui_context_closefunc(obj->ctx, window_close, NULL);
     
 ///*
-    ui_vbox_sp(obj, 8, 4);
-    
-    ui_textfield(obj, NULL);
-    ui_passwordfield(obj, NULL);
-    ui_frameless_textfield(obj, NULL);
-    
-    ui_layout_fill(obj, FALSE);
-    ui_hbox_sp(obj, 0, 10);
-    ui_textfield_w(obj, 5, NULL);
-    ui_textfield_w(obj, 10, NULL);
-    ui_textfield(obj, NULL);
-    ui_end(obj);
-    
-    ui_textarea(obj, NULL);
-    
-    ui_end(obj);
+    UiModelInfo *modeldesc = ui_model_info(obj->ctx, UI_ICON_TEXT, "Name", UI_STRING, "Mail", -1);
+    modeldesc->getvalue = model_getval;
+    ui_table(obj, list, modeldesc);
 //*/
     
     ui_show(obj);
--- a/ucx/allocator.c	Tue Feb 16 17:39:33 2016 +0100
+++ b/ucx/allocator.c	Mon May 23 12:28:32 2016 +0200
@@ -1,7 +1,7 @@
 /*
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
  *
- * Copyright 2013 Olaf Wintermann. All rights reserved.
+ * 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:
--- a/ucx/allocator.h	Tue Feb 16 17:39:33 2016 +0100
+++ b/ucx/allocator.h	Mon May 23 12:28:32 2016 +0200
@@ -1,7 +1,7 @@
 /*
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
  *
- * Copyright 2013 Olaf Wintermann. All rights reserved.
+ * 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:
@@ -157,6 +157,41 @@
 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, \
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ucx/avl.c	Mon May 23 12:28:32 2016 +0200
@@ -0,0 +1,272 @@
+/*
+ * 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 "avl.h"
+
+#define ptrcast(ptr) ((void*)(ptr))
+
+static void ucx_avl_connect(UcxAVLTree *tree,
+        UcxAVLNode *node, UcxAVLNode *child, intptr_t nullkey) {
+    if (child) {
+        child->parent = node;
+    }
+    // if child is NULL, nullkey decides if left or right pointer is cleared
+    if (tree->cmpfunc(
+        ptrcast(child ? child->key : nullkey),
+        ptrcast(node->key), tree->userdata) > 0) {
+      node->right = child;
+    } else {
+      node->left = child;
+    }
+    size_t lh = node->left ? node->left->height : 0;
+    size_t rh = node->right ? node->right->height : 0;
+    node->height = 1 + (lh > rh ? lh : rh);
+}
+
+#define avlheight(node) ((node) ? (node)->height : 0)
+
+static UcxAVLNode* avl_rotright(UcxAVLTree *tree, UcxAVLNode *l0) {
+    UcxAVLNode *p = l0->parent;
+    UcxAVLNode *l1 = l0->left;
+    if (p) {
+        ucx_avl_connect(tree, p, l1, 0);
+    } else {
+        l1->parent = NULL;
+    }
+    ucx_avl_connect(tree, l0, l1->right, l1->key);
+    ucx_avl_connect(tree, l1, l0, 0);
+    return l1;
+}
+
+static UcxAVLNode* avl_rotleft(UcxAVLTree *tree, UcxAVLNode *l0) {
+    UcxAVLNode *p = l0->parent;
+    UcxAVLNode *l1 = l0->right;
+    if (p) {
+        ucx_avl_connect(tree, p, l1, 0);
+    } else {
+        l1->parent = NULL;
+    }
+    ucx_avl_connect(tree, l0, l1->left, l1->key);
+    ucx_avl_connect(tree, l1, l0, 0);
+    return l1;
+}
+
+static void ucx_avl_balance(UcxAVLTree *tree, UcxAVLNode *n) {
+    int lh = avlheight(n->left);
+    int rh = avlheight(n->right);
+    n->height = 1 + (lh > rh ? lh : rh);
+    
+    if (lh - rh == 2) {
+      UcxAVLNode *c = n->left;
+      if (avlheight(c->right) - avlheight(c->left) == 1) {
+        avl_rotleft(tree, c);
+      }
+      n = avl_rotright(tree, n);
+    } else if (rh - lh == 2) {  
+      UcxAVLNode *c = n->right;
+      if (avlheight(c->left) - avlheight(c->right) == 1) {
+        avl_rotright(tree, c);
+      }
+      n = avl_rotleft(tree, n);
+    }
+
+    if (n->parent) {
+      ucx_avl_balance(tree, n->parent);
+    } else {
+      tree->root = n;
+    }
+}
+
+UcxAVLTree *ucx_avl_new(cmp_func cmpfunc) {
+    return ucx_avl_new_a(cmpfunc, ucx_default_allocator());
+}
+
+UcxAVLTree *ucx_avl_new_a(cmp_func cmpfunc, UcxAllocator *allocator) {
+    UcxAVLTree *tree = almalloc(allocator, sizeof(UcxAVLTree));
+    if (tree) {
+        tree->allocator = allocator;
+        tree->cmpfunc = cmpfunc;
+        tree->root = NULL;
+        tree->userdata = NULL;
+    }
+    
+    return tree;
+}
+
+static void ucx_avl_free_node(UcxAllocator *al, UcxAVLNode *node) {
+    if (node) {
+        ucx_avl_free_node(al, node->left);
+        ucx_avl_free_node(al, node->right);
+        alfree(al, node);
+    }
+}
+
+void ucx_avl_free(UcxAVLTree *tree) {
+    UcxAllocator *al = tree->allocator;
+    ucx_avl_free_node(al, tree->root);
+    alfree(al, tree);
+}
+
+UcxAVLNode *ucx_avl_get_node(UcxAVLTree *tree, intptr_t key) {
+    UcxAVLNode *n = tree->root;
+    int cmpresult;
+    while (n && (cmpresult = tree->cmpfunc(
+            ptrcast(key), ptrcast(n->key), tree->userdata))) {
+        n = cmpresult > 0 ? n->right : n->left;
+    }
+    return n;
+}
+
+void *ucx_avl_get(UcxAVLTree *tree, intptr_t key) {
+    UcxAVLNode *n = ucx_avl_get_node(tree, key);
+    return n ? n->value : NULL;
+}
+
+int ucx_avl_put(UcxAVLTree *tree, intptr_t key, void *value) {
+    return ucx_avl_put_s(tree, key, value, NULL);
+}
+
+int ucx_avl_put_s(UcxAVLTree *tree, intptr_t key, void *value,
+        void **oldvalue) {
+    if (tree->root) {
+        UcxAVLNode *n = tree->root;
+        int cmpresult;
+        while ((cmpresult = tree->cmpfunc(
+                ptrcast(key), ptrcast(n->key), tree->userdata))) {
+            UcxAVLNode *m = cmpresult > 0 ? n->right : n->left;
+            if (m) {
+                n = m;
+            } else {
+                break;
+            }
+        }
+
+        if (cmpresult) {
+            UcxAVLNode *e = almalloc(tree->allocator, sizeof(UcxAVLNode));
+            if (e) {
+                e->key = key; e->value = value; e->height = 1;
+                e->parent = e->left = e->right = NULL;
+                ucx_avl_connect(tree, n, e, 0);
+                ucx_avl_balance(tree, n);
+                return 0;
+            } else {
+                return 1;
+            }
+        } else {
+            if (oldvalue) {
+                *oldvalue = n->value;
+            }
+            n->value = value;
+            return 0;
+        }
+    } else {
+        tree->root = almalloc(tree->allocator, sizeof(UcxAVLNode));
+        if (tree->root) {
+            tree->root->key = key; tree->root->value = value;
+            tree->root->height = 1;
+            tree->root->parent = tree->root->left = tree->root->right = NULL;
+            
+            if (oldvalue) {
+                *oldvalue = NULL;
+            }
+            
+            return 0;
+        } else {
+            return 1;
+        }
+    }
+}
+
+int ucx_avl_remove(UcxAVLTree *tree, intptr_t key) {
+    return ucx_avl_remove_s(tree, key, NULL, NULL);
+}
+    
+int ucx_avl_remove_node(UcxAVLTree *tree, UcxAVLNode *node) {
+    return ucx_avl_remove_s(tree, node->key, NULL, NULL);
+}
+
+int ucx_avl_remove_s(UcxAVLTree *tree, intptr_t key,
+        intptr_t *oldkey, void **oldvalue) {
+    
+    UcxAVLNode *n = tree->root;
+    int cmpresult;
+    while (n && (cmpresult = tree->cmpfunc(
+            ptrcast(key), ptrcast(n->key), tree->userdata))) {
+        n = cmpresult > 0 ? n->right : n->left;
+    }
+    if (n) {
+        if (oldkey) {
+            *oldkey = n->key;
+        }
+        if (oldvalue) {
+            *oldvalue = n->value;
+        }
+        
+        UcxAVLNode *p = n->parent;
+        if (n->left && n->right) {
+            UcxAVLNode *s = n->right;
+            while (s->left) {
+                s = s->left;
+            }
+            ucx_avl_connect(tree, s->parent, s->right, s->key);
+            n->key = s->key; n->value = s->value;
+            p = s->parent;
+            alfree(tree->allocator, s);
+        } else {
+            if (p) {
+                ucx_avl_connect(tree, p, n->right ? n->right:n->left, n->key);
+            } else {
+                tree->root = n->right ? n->right : n->left;
+                if (tree->root) {
+                    tree->root->parent = NULL;
+                }
+            }
+            alfree(tree->allocator, n);
+        }
+
+        if (p) {
+            ucx_avl_balance(tree, p);
+        }
+        
+        return 0;
+    } else {
+        return 1;
+    }
+}
+
+static size_t ucx_avl_countn(UcxAVLNode *node) {
+    if (node) {
+        return 1 + ucx_avl_countn(node->left) + ucx_avl_countn(node->right);
+    } else {
+        return 0;
+    }
+}
+
+size_t ucx_avl_count(UcxAVLTree *tree) {
+    return ucx_avl_countn(tree->root);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ucx/avl.h	Mon May 23 12:28:32 2016 +0200
@@ -0,0 +1,249 @@
+/*
+ * 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 avl.h
+ * 
+ * AVL tree implementation.
+ * 
+ * This binary search tree implementation allows average O(1) insertion and
+ * removal of elements (excluding binary search time).
+ * 
+ * @author Mike Becker
+ * @author Olaf Wintermann
+ */
+
+#ifndef UCX_AVL_H
+#define UCX_AVL_H
+
+#include "ucx.h"
+#include "allocator.h"
+#include <stdint.h>
+
+#ifdef	__cplusplus
+extern "C" {
+#endif
+
+/**
+ * UCX AVL Node type.
+ * 
+ * @see UcxAVLNode
+ */
+typedef struct UcxAVLNode UcxAVLNode;
+
+/**
+ * UCX AVL Node.
+ */
+struct UcxAVLNode {
+    /**
+     * The key for this node.
+     */
+    intptr_t key;
+    /**
+     * Data contained by this node.
+     */
+    void *value;
+    /**
+     * The height of this (sub)-tree.
+     */
+    size_t height;
+    /**
+     * Parent node.
+     */
+    UcxAVLNode *parent;
+    /**
+     * Root node of left subtree.
+     */
+    UcxAVLNode *left;
+    /**
+     * Root node of right subtree.
+     */
+    UcxAVLNode *right;
+};
+
+/**
+ * UCX AVL Tree.
+ */
+typedef struct {
+    /**
+     * The UcxAllocator that shall be used to manage the memory for node data.
+     */
+    UcxAllocator *allocator;
+    /**
+     * Root node of the tree.
+     */
+    UcxAVLNode *root;
+    /**
+     * Compare function that shall be used to compare the UcxAVLNode keys.
+     * @see UcxAVLNode.key
+     */
+    cmp_func cmpfunc;
+    /**
+     * Custom user data.
+     * This data will also be provided to the cmpfunc.
+     */
+    void *userdata;
+} UcxAVLTree;
+
+/**
+ * Initializes a new UcxAVLTree with a default allocator.
+ * 
+ * @param cmpfunc the compare function that shall be used
+ * @return a new UcxAVLTree object
+ * @see ucx_avl_new_a()
+ */
+UcxAVLTree *ucx_avl_new(cmp_func cmpfunc);
+
+/**
+ * Initializes a new UcxAVLTree with the specified allocator.
+ * 
+ * The cmpfunc should be capable of comparing two keys within this AVL tree.
+ * So if you want to use null terminated strings as keys, you could use the
+ * ucx_strcmp() function here.
+ * 
+ * @param cmpfunc the compare function that shall be used
+ * @param allocator the UcxAllocator that shall be used
+ * @return a new UcxAVLTree object
+ */
+UcxAVLTree *ucx_avl_new_a(cmp_func cmpfunc, UcxAllocator *allocator);
+
+/**
+ * Destroys an UcxAVLTree.
+ * @param tree the tree to destroy
+ */
+void ucx_avl_free(UcxAVLTree *tree);
+
+/**
+ * Macro for initializing a new UcxAVLTree with the default allocator and a
+ * ucx_ptrcmp() compare function.
+ * 
+ * @return a new default UcxAVLTree object
+ */
+#define ucx_avl_default_new() ucx_avl_new_a(ucx_ptrcmp, ucx_default_allocator())
+
+/**
+ * Gets the node from the tree, that is associated with the specified key.
+ * @param tree the UcxAVLTree
+ * @param key the key
+ * @return the node (or <code>NULL</code>, if the key is not present)
+ */
+UcxAVLNode *ucx_avl_get_node(UcxAVLTree *tree, intptr_t key);
+
+/**
+ * Gets the value from the tree, that is associated with the specified key.
+ * @param tree the UcxAVLTree
+ * @param key the key
+ * @return the value (or <code>NULL</code>, if the key is not present)
+ */
+void *ucx_avl_get(UcxAVLTree *tree, intptr_t key);
+
+/**
+ * Puts a key/value pair into the tree.
+ * 
+ * Attention: use this function only, if a possible old value does not need
+ * to be preserved.
+ * 
+ * @param tree the UcxAVLTree
+ * @param key the key
+ * @param value the new value
+ * @return zero, if and only if the operation succeeded
+ */
+int ucx_avl_put(UcxAVLTree *tree, intptr_t key, void *value);
+
+/**
+ * Puts a key/value pair into the tree.
+ * 
+ * This is a secure function which saves the old value to the variable pointed
+ * at by oldvalue.
+ * 
+ * @param tree the UcxAVLTree
+ * @param key the key
+ * @param value the new value
+ * @param oldvalue optional: a pointer to the location where a possible old
+ * value shall be stored
+ * @return zero, if and only if the operation succeeded
+ */
+int ucx_avl_put_s(UcxAVLTree *tree, intptr_t key, void *value, void **oldvalue);
+
+/**
+ * Removes a node from the AVL tree.
+ * 
+ * Note: the specified node is logically removed. The tree implementation
+ * decides which memory area is freed. In most cases the here provided node
+ * is freed, so it's further use is generally undefined.
+ * 
+ * @param tree the UcxAVLTree
+ * @param node the node to remove
+ * @return zero, if and only if an element has been removed
+ */
+int ucx_avl_remove_node(UcxAVLTree *tree, UcxAVLNode *node);
+
+/**
+ * Removes an element from the AVL tree.
+ * 
+ * @param tree the UcxAVLTree
+ * @param key the key
+ * @return zero, if and only if an element has been removed
+ */
+int ucx_avl_remove(UcxAVLTree *tree, intptr_t key);
+
+/**
+ * Removes an element from the AVL tree.
+ * 
+ * This is a secure function which saves the old key and value data from node
+ * to the variables at the location of oldkey and oldvalue (if specified), so
+ * they can be freed afterwards (if necessary).
+ * 
+ * Note: the returned key in oldkey is possibly not the same as the provided
+ * key for the lookup (in terms of memory location).
+ * 
+ * @param tree the UcxAVLTree
+ * @param key the key of the element to remove
+ * @param oldkey optional: a pointer to the location where the old key shall be
+ * stored
+ * @param oldvalue optional: a pointer to the location where the old value
+ * shall be stored
+ * @return zero, if and only if an element has been removed
+ */
+int ucx_avl_remove_s(UcxAVLTree *tree, intptr_t key,
+        intptr_t *oldkey, void **oldvalue);
+
+/**
+ * Counts the nodes in the specified UcxAVLTree.
+ * @param tree the AVL tree
+ * @return the node count
+ */
+size_t ucx_avl_count(UcxAVLTree *tree);
+
+#ifdef	__cplusplus
+}
+#endif
+
+#endif	/* UCX_AVL_H */
+
--- a/ucx/buffer.c	Tue Feb 16 17:39:33 2016 +0100
+++ b/ucx/buffer.c	Mon May 23 12:28:32 2016 +0200
@@ -1,7 +1,7 @@
 /*
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
  *
- * Copyright 2013 Olaf Wintermann. All rights reserved.
+ * 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:
@@ -31,22 +31,22 @@
 #include <stdlib.h>
 #include <string.h>
 
-UcxBuffer *ucx_buffer_new(void *space, size_t size, int flags) {
+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(size);
+            buffer->space = (char*)malloc(capacity);
             if (!buffer->space) {
                 free(buffer);
                 return NULL;
             }
-            memset(buffer->space, 0, size);
+            memset(buffer->space, 0, capacity);
             buffer->flags |= UCX_BUFFER_AUTOFREE;
         } else {
             buffer->space = (char*)space;
         }
-        buffer->capacity = size;
+        buffer->capacity = capacity;
         buffer->size = 0;
 
         buffer->pos = 0;
@@ -64,13 +64,8 @@
 
 UcxBuffer* ucx_buffer_extract(
         UcxBuffer *src, size_t start, size_t length, int flags) {
-    if(src->size == 0) {
-        return NULL;
-    }
-    if (length == 0) {
-        length = src->size - start;
-    }
-    if (start+length > src->size) {
+    
+    if (src->size == 0 || length == 0 || start+length > src->capacity) {
         return NULL;
     }
 
@@ -99,13 +94,21 @@
     case SEEK_END:
         npos = buffer->size;
         break;
+    case SEEK_SET:
+        npos = 0;
+        break;
     default:
-        npos = 0;
+        return -1;
     }
 
+    size_t opos = npos;
     npos += offset;
     
-    if (npos > buffer->size) {
+    if ((offset > 0 && npos < opos) || (offset < 0 && npos > opos)) {
+        return -1;
+    }
+    
+    if (npos >= buffer->size) {
         return -1;
     } else {
         buffer->pos = npos;
@@ -120,7 +123,17 @@
 
 int ucx_buffer_extend(UcxBuffer *buffer, size_t len) {
     size_t newcap = buffer->capacity;
-    while (buffer->pos + len > newcap) newcap <<= 1;
+    
+    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) {
@@ -137,18 +150,25 @@
 size_t ucx_buffer_write(const void *ptr, size_t size, size_t nitems,
         UcxBuffer *buffer) {
     size_t len = size * nitems;
-    if (buffer->pos + len > buffer->capacity) {
+    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, len)) {
-                return -1;
+            if (ucx_buffer_extend(buffer, required - buffer->capacity)) {
+                return 0;
             }
         } else {
             len = buffer->capacity - buffer->pos;
-            if (size > 1) len -= len%size;
+            if (size > 1) {
+                len -= len%size;
+            }
         }
     }
     
-    if (len <= 0) {
+    if (len == 0) {
         return len;
     }
     
--- a/ucx/buffer.h	Tue Feb 16 17:39:33 2016 +0100
+++ b/ucx/buffer.h	Mon May 23 12:28:32 2016 +0200
@@ -1,7 +1,7 @@
 /*
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
  *
- * Copyright 2013 Olaf Wintermann. All rights reserved.
+ * 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:
@@ -97,11 +97,11 @@
  * 
  * @param space pointer to the memory area, or <code>NULL</code> to allocate
  * new memory
- * @param size the size of the buffer
+ * @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 size, int flags);
+UcxBuffer *ucx_buffer_new(void *space, size_t capacity, int flags);
 
 /**
  * Destroys a buffer.
@@ -120,10 +120,9 @@
  * 
  * @param src the source buffer
  * @param start the start position of extraction
- * @param length the count of bytes to extract or 0 if all of the remaining
- * bytes shall be extracted
+ * @param length the count of bytes to extract (must not be zero)
  * @param flags feature mask for the new buffer
- * @return 
+ * @return a new buffer containing the extraction
  */
 UcxBuffer* ucx_buffer_extract(UcxBuffer *src,
         size_t start, size_t length, int flags);
@@ -136,7 +135,7 @@
  * @return a new buffer with the extracted content
  */
 #define ucx_buffer_clone(src,flags) \
-    ucx_buffer_extract(src, 0, 0, flags)
+    ucx_buffer_extract(src, 0, (src)->capacity, flags)
 
 /**
  * Moves the position of the buffer.
@@ -145,7 +144,11 @@
  *
  * SEEK_SET marks the start of the buffer.
  * SEEK_CUR marks the current position.
- * SEEK_END marks the first 0-byte in the buffer.
+ * 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>
@@ -182,12 +185,12 @@
  * 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 count of <i>additional</i>
- * bytes the buffer shall hold. It is <b>NOT</b> the total count of bytes the
+ * <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 count of additional bytes the buffer shall
+ * @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
  */
@@ -216,7 +219,7 @@
  * @param size the length of one element
  * @param nitems the element count
  * @param buffer the UcxBuffer to read from
- * @return the total count of bytes read
+ * @return the total number of elements read
  */
 size_t ucx_buffer_read(void *ptr, size_t size, size_t nitems,
         UcxBuffer *buffer);
--- a/ucx/list.c	Tue Feb 16 17:39:33 2016 +0100
+++ b/ucx/list.c	Mon May 23 12:28:32 2016 +0200
@@ -1,7 +1,7 @@
 /*
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
  *
- * Copyright 2013 Olaf Wintermann. All rights reserved.
+ * 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:
@@ -72,7 +72,7 @@
     while (e != NULL) {
         f = e;
         e = e->next;
-        alloc->free(alloc->pool, f);
+        alfree(alloc, f);
     }
 }
 
@@ -81,7 +81,7 @@
 }
 
 UcxList *ucx_list_append_a(UcxAllocator *alloc, UcxList *l, void *data)  {
-    UcxList *nl = (UcxList*) alloc->malloc(alloc->pool, sizeof(UcxList));
+    UcxList *nl = (UcxList*) almalloc(alloc, sizeof(UcxList));
     if (!nl) {
         return NULL;
     }
@@ -121,7 +121,9 @@
     if (l1) {
         UcxList *last = ucx_list_last(l1);
         last->next = l2;
-        l2->prev = last;
+        if (l2) {
+            l2->prev = last;
+        }
         return l1;
     } else {
         return l2;
@@ -150,7 +152,7 @@
     return -1;
 }
 
-UcxList *ucx_list_get(const UcxList *l, int index) {
+UcxList *ucx_list_get(const UcxList *l, size_t index) {
     if (l == NULL) return NULL;
 
     const UcxList *e = l;
@@ -196,7 +198,7 @@
     return s;
 }
 
-UcxList *ucx_list_sort_merge(int length,
+static UcxList *ucx_list_sort_merge(int length,
         UcxList* restrict ls, UcxList* restrict le, UcxList* restrict re,
         cmp_func fnc, void* data) {
 
@@ -248,6 +250,8 @@
     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;
@@ -261,27 +265,30 @@
         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;
 
-        // Something left? Sort it!
-        UcxList *remainder = re;
-        size_t remainder_length = ucx_list_size(remainder);
-        if (remainder != NULL) {
-            remainder = ucx_list_sort(remainder, fnc, data);
-        }
-
         // {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);
+            // 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;
     }
@@ -316,6 +323,6 @@
         e->prev->next = e->next;
     }
     
-    alloc->free(alloc->pool, e);
+    alfree(alloc, e);
     return l;
 }
--- a/ucx/list.h	Tue Feb 16 17:39:33 2016 +0100
+++ b/ucx/list.h	Mon May 23 12:28:32 2016 +0200
@@ -1,7 +1,7 @@
 /*
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
  *
- * Copyright 2013 Olaf Wintermann. All rights reserved.
+ * 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:
@@ -169,7 +169,7 @@
 /**
  * Inserts an element at the end of the list.
  * 
- * This is generally an O(n) operation, as the end of the list is seeked with
+ * 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
@@ -273,7 +273,7 @@
  * @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, int index);
+UcxList *ucx_list_get(const UcxList *list, size_t index);
 
 /**
  * Returns the index of an element.
@@ -350,7 +350,7 @@
  * <code>mylist = ucx_list_remove(mylist, myelem);</code>.
  * 
  * @param list the list from which the element shall be removed
- * @param element the element to removed
+ * @param element the element to remove
  * @return returns the updated list pointer or <code>NULL</code>, if the list
  * is now empty
  */
@@ -363,7 +363,7 @@
  * 
  * @param allocator the allocator to use
  * @param list the list from which the element shall be removed
- * @param element the element to removed
+ * @param element the element to remove
  * @return returns the updated list pointer or <code>NULL</code>, if the list
  * @see ucx_list_remove()
  */
--- a/ucx/logging.c	Tue Feb 16 17:39:33 2016 +0100
+++ b/ucx/logging.c	Mon May 23 12:28:32 2016 +0200
@@ -1,7 +1,7 @@
 /*
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
  *
- * Copyright 2013 Olaf Wintermann. All rights reserved.
+ * 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:
@@ -75,6 +75,7 @@
         if ((logger->mask & UCX_LOGGER_LEVEL) > 0) {
             text = (char*) ucx_map_int_get(logger->levels, level);
             n = strlen(text);
+            n = n > 256 ? 256 : n;
             memcpy(msg+k, text, n);
             k += n;
             msg[k++] = ' ';
@@ -87,11 +88,7 @@
             n = strlen(file);
             memcpy(msg+k, file, n);
             k += n;
-#ifdef _WIN32
-            k += _snprintf(msg+k, UCX_LOGGER_MSGMAX-k, ":%d ", line);
-#else
-            k += snprintf(msg+k, UCX_LOGGER_MSGMAX-k, ":%d ", line);
-#endif /* _WIN32 */
+            k += sprintf(msg+k, ":%u ", line);
         }
         
         msg[k++] = '-'; msg[k++] = ' ';
--- a/ucx/logging.h	Tue Feb 16 17:39:33 2016 +0100
+++ b/ucx/logging.h	Mon May 23 12:28:32 2016 +0200
@@ -1,7 +1,7 @@
 /*
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
  *
- * Copyright 2013 Olaf Wintermann. All rights reserved.
+ * 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:
@@ -97,7 +97,7 @@
     write_func writer;
 
     /**
-     * The date format for timestamp outputs
+     * The date format for timestamp outputs including the delimiter
      * (default: <code>"%F %T %z "</code>).
      * @see UCX_LOGGER_TIMESTAMP
      */
@@ -162,7 +162,8 @@
  * <code>[LEVEL] [TIMESTAMP] [SOURCEFILE]:[LINENO] message</code>
  * 
  * <b>Attention:</b> the message (including automatically generated information)
- * <b>MUST NOT</b> exceed the size of 4 KB.
+ * is limited to 4096 characters. The level description is limited to
+ * 256 characters and the timestamp string is limited to 128 characters.
  * 
  * @param logger the logger to use
  * @param level the level to log on
--- a/ucx/map.c	Tue Feb 16 17:39:33 2016 +0100
+++ b/ucx/map.c	Mon May 23 12:28:32 2016 +0200
@@ -1,7 +1,7 @@
 /*
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
  *
- * Copyright 2013 Olaf Wintermann. All rights reserved.
+ * 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:
@@ -44,18 +44,16 @@
         allocator = ucx_default_allocator();
     }
     
-    UcxMap *map = (UcxMap*)allocator->malloc(allocator->pool, sizeof(UcxMap));
+    UcxMap *map = (UcxMap*)almalloc(allocator, sizeof(UcxMap));
     if (!map) {
         return NULL;
     }
     
     map->allocator = allocator;
-    map->map = (UcxMapElement**)allocator->calloc(
-            allocator->pool,
-            size,
-            sizeof(UcxMapElement*));
+    map->map = (UcxMapElement**)alcalloc(
+            allocator, size, sizeof(UcxMapElement*));
     if(map->map == NULL) {
-        allocator->free(allocator->pool, map);
+        alfree(allocator, map);
         return NULL;
     }
     map->size = size;
@@ -64,24 +62,41 @@
     return map;
 }
 
-static void ucx_map_free_elmlist(UcxMap *map) {
+static void ucx_map_free_elmlist_contents(UcxMap *map) {
     for (size_t n = 0 ; n < map->size ; n++) {
         UcxMapElement *elem = map->map[n];
         if (elem != NULL) {
             do {
                 UcxMapElement *next = elem->next;
-                map->allocator->free(map->allocator->pool, elem->key.data);
-                map->allocator->free(map->allocator->pool, elem);
+                alfree(map->allocator, elem->key.data);
+                alfree(map->allocator, elem);
                 elem = next;
             } while (elem != NULL);
         }
     }
-    map->allocator->free(map->allocator->pool, map->map);
 }
 
 void ucx_map_free(UcxMap *map) {
-    ucx_map_free_elmlist(map);
-    map->allocator->free(map->allocator->pool, map);
+    ucx_map_free_elmlist_contents(map);
+    alfree(map->allocator, map->map);
+    alfree(map->allocator, map);
+}
+
+void ucx_map_free_content(UcxMap *map, ucx_destructor destr) {
+    UcxMapIterator iter = ucx_map_iterator(map);
+    void *val;
+    UCX_MAP_FOREACH(key, val, iter) {
+        destr(val);
+    }
+}
+
+void ucx_map_clear(UcxMap *map) {
+    if (map->count == 0) {
+        return; // nothing to do
+    }
+    ucx_map_free_elmlist_contents(map);
+    memset(map->map, 0, map->size*sizeof(UcxMapElement*));
+    map->count = 0;
 }
 
 int ucx_map_copy(UcxMap *restrict from, UcxMap *restrict to,
@@ -116,10 +131,8 @@
         oldmap.allocator = map->allocator;
         
         map->size = (map->count * 5) >> 1;
-        map->map = (UcxMapElement**)map->allocator->calloc(
-                map->allocator->pool,
-                map->size,
-                sizeof(UcxMapElement*));
+        map->map = (UcxMapElement**)alcalloc(
+                map->allocator, map->size, sizeof(UcxMapElement*));
         if (!map->map) {
             *map = oldmap;
             return 1;
@@ -128,7 +141,8 @@
         ucx_map_copy(&oldmap, map, NULL, NULL);
         
         /* free the UcxMapElement list of oldmap */
-        ucx_map_free_elmlist(&oldmap);
+        ucx_map_free_elmlist_contents(&oldmap);
+        alfree(map->allocator, oldmap.map);
     }
     return 0;
 }
@@ -150,9 +164,8 @@
     }
     
     if (!elm || elm->key.hash != key.hash) {
-        UcxMapElement *e = (UcxMapElement*)allocator->malloc(
-                allocator->pool,
-                sizeof(UcxMapElement));
+        UcxMapElement *e = (UcxMapElement*)almalloc(
+                allocator, sizeof(UcxMapElement));
         if (!e) {
             return -1;
         }
@@ -167,7 +180,7 @@
     }
     
     if (!elm->key.data) {
-        void *kd = allocator->malloc(allocator->pool, key.len);
+        void *kd = almalloc(allocator, key.len);
         if (!kd) {
             return -1;
         }
@@ -200,8 +213,8 @@
                     } else {
                         map->map[slot] = elm->next;
                     }
-                    map->allocator->free(map->allocator->pool, elm->key.data);
-                    map->allocator->free(map->allocator->pool, elm);
+                    alfree(map->allocator, elm->key.data);
+                    alfree(map->allocator, elm);
                     map->count--;
                 }
 
--- a/ucx/map.h	Tue Feb 16 17:39:33 2016 +0100
+++ b/ucx/map.h	Mon May 23 12:28:32 2016 +0200
@@ -1,7 +1,7 @@
 /*
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
  *
- * Copyright 2013 Olaf Wintermann. All rights reserved.
+ * 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:
@@ -146,14 +146,45 @@
 /**
  * Frees a hash map.
  * 
- * <b>Note:</b> the contents are <b>not</b> freed, use an UcxMempool for that
- * purpose.
+ * <b>Note:</b> the contents are <b>not</b> freed, use ucx_map_free_content()
+ * before calling this function to achieve that.
  * 
  * @param map the map to be freed
+ * @see ucx_map_free_content()
  */
 void ucx_map_free(UcxMap *map);
 
 /**
+ * Frees the contents of a hash map.
+ * 
+ * This is a convenience function that iterates over the map and passes all
+ * values to the specified destructor function (e.g. stdlib free()).
+ * 
+ * You must ensure, that it is valid to pass each value in the map to the same
+ * destructor function.
+ * 
+ * You should free or clear the map afterwards, as the contents will be invalid.
+ * 
+ * @param map for which the contents shall be freed
+ * @param destr pointer to the destructor function
+ * @see ucx_map_free()
+ * @see ucx_map_clear()
+ */
+void ucx_map_free_content(UcxMap *map, ucx_destructor destr);
+
+/**
+ * Clears a hash map.
+ * 
+ * <b>Note:</b> the contents are <b>not</b> freed, use ucx_map_free_content()
+ * before calling this function to achieve that.
+ * 
+ * @param map the map to be cleared
+ * @see ucx_map_free_content()
+ */
+void ucx_map_clear(UcxMap *map);
+
+
+/**
  * Copies contents from a map to another map using a copy function.
  * 
  * <b>Note:</b> The destination map does not need to be empty. However, if it
--- a/ucx/mempool.c	Tue Feb 16 17:39:33 2016 +0100
+++ b/ucx/mempool.c	Mon May 23 12:28:32 2016 +0200
@@ -1,7 +1,7 @@
 /*
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
  *
- * Copyright 2013 Olaf Wintermann. All rights reserved.
+ * 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:
--- a/ucx/mempool.h	Tue Feb 16 17:39:33 2016 +0100
+++ b/ucx/mempool.h	Mon May 23 12:28:32 2016 +0200
@@ -1,7 +1,7 @@
 /*
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
  *
- * Copyright 2013 Olaf Wintermann. All rights reserved.
+ * 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:
@@ -47,13 +47,6 @@
 #endif
 
 /**
- * A function pointer to a destructor function.
- * @see ucx_mempool_setdestr()
- * @see ucx_mempool_regdestr()
- */
-typedef void(*ucx_destructor)(void*);
-
-/**
  * UCX mempool structure.
  */
 typedef struct {
@@ -121,7 +114,7 @@
 /**
  * Allocates a pooled memory array.
  * 
- * The contents of the allocated memory is set to zero.
+ * The content of the allocated memory is set to zero.
  * 
  * @param pool the memory pool
  * @param nelem amount of elements to allocate
@@ -142,7 +135,7 @@
  * @param pool the memory pool
  * @param ptr a pointer to the memory that shall be reallocated
  * @param n the new size of the memory
- * @return a pointer to the the location of the memory
+ * @return a pointer to the new location of the memory
  * @see ucx_allocator_realloc()
  */
 void *ucx_mempool_realloc(UcxMempool *pool, void *ptr, size_t n);
--- a/ucx/properties.c	Tue Feb 16 17:39:33 2016 +0100
+++ b/ucx/properties.c	Mon May 23 12:28:32 2016 +0200
@@ -1,7 +1,7 @@
 /*
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
  *
- * Copyright 2013 Olaf Wintermann. All rights reserved.
+ * 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:
@@ -206,7 +206,7 @@
             return 1;
         }
         if(ucx_map_sstr_put(map, name, value.ptr)) {
-            map->allocator->free(map->allocator->pool, value.ptr);
+            alfree(map->allocator, value.ptr);
             return 1;
         }
     }
--- a/ucx/properties.h	Tue Feb 16 17:39:33 2016 +0100
+++ b/ucx/properties.h	Mon May 23 12:28:32 2016 +0200
@@ -1,7 +1,7 @@
 /*
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
  *
- * Copyright 2013 Olaf Wintermann. All rights reserved.
+ * 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:
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ucx/stack.c	Mon May 23 12:28:32 2016 +0200
@@ -0,0 +1,143 @@
+/*
+ * 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 "stack.h"
+#include <string.h>
+
+static size_t ucx_stack_align(size_t n) {
+    int align = n % sizeof(void*);
+    if (align) {
+        n += sizeof(void*) - align;
+    }
+    return n;
+}
+
+void ucx_stack_init(UcxStack *stack, char* space, size_t size) {
+    stack->size = size - size % sizeof(void*);
+    stack->space = space;
+    stack->top = NULL;
+    
+    stack->allocator.pool = stack;
+    stack->allocator.malloc = (ucx_allocator_malloc) ucx_stack_malloc;
+    stack->allocator.calloc = (ucx_allocator_calloc) ucx_stack_calloc;
+    stack->allocator.realloc = (ucx_allocator_realloc) ucx_stack_realloc;
+    stack->allocator.free = (ucx_allocator_free) ucx_stack_free;
+}
+
+void *ucx_stack_malloc(UcxStack *stack, size_t n) {
+
+    if (ucx_stack_avail(stack) < ucx_stack_align(n)) {
+        return NULL;
+    } else {
+        char *prev = stack->top;
+        if (stack->top) {
+            stack->top += ucx_stack_align(ucx_stack_topsize(stack));
+        } else {
+            stack->top = stack->space;
+        }
+        
+        ((struct ucx_stack_metadata*)stack->top)->prev = prev;
+        ((struct ucx_stack_metadata*)stack->top)->size = n;
+        stack->top += sizeof(struct ucx_stack_metadata);
+        
+        return stack->top;
+    }
+}
+
+void *ucx_stack_calloc(UcxStack *stack, size_t nelem, size_t elsize) {
+    void *mem = ucx_stack_malloc(stack, nelem*elsize);
+    memset(mem, 0, nelem*elsize);
+    return mem;
+}
+
+void *ucx_stack_realloc(UcxStack *stack, void *ptr, size_t n) {
+    if (ptr == stack->top) {
+        if (stack->size - (stack->top - stack->space) < ucx_stack_align(n)) {
+            return NULL;
+        } else {
+            ((struct ucx_stack_metadata*)stack->top - 1)->size = n;
+            return ptr;
+        }
+    } else {
+        if (ucx_stack_align(((struct ucx_stack_metadata*)ptr - 1)->size) <
+                ucx_stack_align(n)) {
+            void *nptr = ucx_stack_malloc(stack, n);
+            if (nptr) {
+                memcpy(nptr, ptr, n);
+                ucx_stack_free(stack, ptr);
+                
+                return nptr;
+            } else {
+                return NULL;
+            }
+        } else {
+            ((struct ucx_stack_metadata*)ptr - 1)->size = n;
+            return ptr;
+        }
+    }
+}
+
+void ucx_stack_free(UcxStack *stack, void *ptr) {
+    if (ptr == stack->top) {
+        stack->top = ((struct ucx_stack_metadata*) stack->top - 1)->prev;
+    } else {
+        struct ucx_stack_metadata *next = (struct ucx_stack_metadata*)(
+            (char*)ptr +
+            ucx_stack_align(((struct ucx_stack_metadata*) ptr - 1)->size)
+        );
+        next->prev = ((struct ucx_stack_metadata*) ptr - 1)->prev;
+    }
+}
+
+void ucx_stack_popn(UcxStack *stack, void *dest, size_t n) {
+    if (ucx_stack_empty(stack)) {
+        return;
+    }
+    
+    size_t len = ucx_stack_topsize(stack);
+    if (len > n) {
+        len = n;
+    }
+    
+    memcpy(dest, stack->top, len);
+    
+    ucx_stack_free(stack, stack->top);
+}
+
+size_t ucx_stack_avail(UcxStack *stack) {
+    size_t avail = ((stack->top ? (stack->size
+                    - (stack->top - stack->space)
+                    - ucx_stack_align(ucx_stack_topsize(stack)))
+                    : stack->size));
+    
+    if (avail > sizeof(struct ucx_stack_metadata)) {
+        return avail - sizeof(struct ucx_stack_metadata);
+    } else {
+        return 0;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ucx/stack.h	Mon May 23 12:28:32 2016 +0200
@@ -0,0 +1,233 @@
+/*
+ * 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 stack.h
+ * 
+ * Default stack memory allocation implementation.
+ * 
+ * @author Mike Becker
+ * @author Olaf Wintermann
+ */
+
+#ifndef UCX_STACK_H
+#define	UCX_STACK_H
+
+#include "ucx.h"
+#include <stdint.h>
+#include "allocator.h"
+
+#ifdef	__cplusplus
+extern "C" {
+#endif
+
+
+/**
+ * UCX stack structure.
+ */
+typedef struct {
+    /** UcxAllocator based on this stack */
+    UcxAllocator allocator;
+    
+    /** Stack size. */
+    size_t size;
+    
+    /** Pointer to the bottom of the stack */
+    char *space;
+    
+    /** Pointer to the top of the stack */
+    char *top;
+} UcxStack;
+
+/**
+ * Metadata for each UCX stack element.
+ */
+struct ucx_stack_metadata {
+    /**
+     * Location of the previous element (<code>NULL</code> if this is the first)
+     */
+    char *prev;
+    
+    /** Size of this element */
+    size_t size;
+};
+
+/**
+ * Initializes UcxStack structure with memory.
+ * 
+ * @param stack a pointer to an uninitialized stack structure
+ * @param space the memory area that shall be managed
+ * @param size size of the memory area
+ * @return a new UcxStack structure
+ */
+void ucx_stack_init(UcxStack *stack, char* space, size_t size);
+
+/**
+ * Allocates stack memory.
+ * 
+ * @param stack a pointer to the stack
+ * @param n amount of memory to allocate
+ * @return a pointer to the allocated memory
+ * @see ucx_allocator_malloc()
+ */
+void *ucx_stack_malloc(UcxStack *stack, size_t n);
+
+/**
+ * Alias for #ucx_stack_malloc().
+ * @param stack a pointer to the stack
+ * @param n amount of memory to allocate
+ * @return a pointer to the allocated memory
+ * @see ucx_stack_malloc
+ */
+#define ucx_stack_push(stack, n) ucx_stack_malloc(stack, n)
+
+/**
+ * Allocates an array of stack memory
+ * 
+ * The content of the allocated memory is set to zero.
+ * 
+ * @param stack a pointer to the stack
+ * @param nelem amount of elements to allocate
+ * @param elsize amount of memory per element
+ * @return a pointer to the allocated memory
+ * @see ucx_allocator_calloc()
+ */
+void *ucx_stack_calloc(UcxStack *stack, size_t nelem, size_t elsize);
+
+/**
+ * Alias for #ucx_stack_calloc().
+ * 
+ * @param stack a pointer to the stack
+ * @param n amount of elements to allocate
+ * @param elsize amount of memory per element
+ * @return a pointer to the allocated memory
+ * @see ucx_stack_calloc
+ */
+#define ucx_stack_pusharr(stack,n,elsize) ucx_stack_calloc(stack,n,elssize)
+
+/**
+ * Reallocates memory on the stack.
+ * 
+ * Shrinking memory is always safe. Extending memory can be very expensive. 
+ * 
+ * @param stack the stack
+ * @param ptr a pointer to the memory that shall be reallocated
+ * @param n the new size of the memory
+ * @return a pointer to the new location of the memory
+ * @see ucx_allocator_realloc()
+ */
+void *ucx_stack_realloc(UcxStack *stack, void *ptr, size_t n);
+
+/**
+ * Frees memory on the stack.
+ * 
+ * Freeing stack memory behaves in a special way.
+ * 
+ * If the element, that should be freed, is the top most element of the stack,
+ * it is removed from the stack. Otherwise it is marked as freed. Marked
+ * elements are removed, when they become the top most elements of the stack.
+ * 
+ * @param stack a pointer to the stack
+ * @param ptr a pointer to the memory that shall be freed
+ */
+void ucx_stack_free(UcxStack *stack, void *ptr);
+
+
+/**
+ * Returns the size of the top most element.
+ * @param stack a pointer to the stack
+ * @return the size of the top most element
+ */
+#define ucx_stack_topsize(stack) ((stack)->top ? ((struct ucx_stack_metadata*)\
+                                  (stack)->top - 1)->size : 0)
+
+/**
+ * Removes the top most element from the stack and copies the content to <code>
+ * dest</code>, if specified.
+ * 
+ * Use #ucx_stack_topsize()# to get the amount of memory that must be available
+ * at the location of <code>dest</code>.
+ * 
+ * @param stack a pointer to the stack
+ * @param dest the location where the contents shall be written to, or <code>
+ * NULL</code>, if the element shall only be removed.
+ * @see ucx_stack_free
+ * @see ucx_stack_popn
+ */
+#define ucx_stack_pop(stack, dest) ucx_stack_popn(stack, dest, SIZE_MAX)
+
+/**
+ * Removes the top most element from the stack and copies the content to <code>
+ * dest</code>.
+ * 
+ * In contrast to #ucx_stack_pop() the <code>dest</code> pointer <code>MUST
+ * NOT</code> be <code>NULL</code>.
+ * 
+ * @param stack a pointer to the stack
+ * @param dest the location where the contents shall be written to
+ * @param n copies at most n elements to <code>dest</code>
+ * @see ucx_stack_pop
+ */
+void ucx_stack_popn(UcxStack *stack, void *dest, size_t n);
+
+/**
+ * Returns the remaining available memory on the specified stack.
+ * 
+ * @param stack a pointer to the stack
+ * @return the remaining available memory
+ */
+size_t ucx_stack_avail(UcxStack *stack);
+
+/**
+ * Checks, if the stack is empty.
+ * 
+ * @param stack a pointer to the stack
+ * @return nonzero, if the stack is empty, zero otherwise
+ */
+#define ucx_stack_empty(stack) (!(stack)->top)
+
+/**
+ * Computes a recommended size for the stack memory area. Note, that
+ * reallocations have not been taken into account, so you might need to reserve
+ * twice as much memory to allow many reallocations.
+ * 
+ * @param size the approximate payload
+ * @param elems the approximate count of element allocations
+ * @return a recommended size for the stack space based on the information
+ * provided
+ */
+#define ucx_stack_dim(size, elems) (size+sizeof(struct ucx_stack_metadata) * \
+                                    (elems + 1))
+
+
+#ifdef	__cplusplus
+}
+#endif
+
+#endif	/* UCX_STACK_H */
+
--- a/ucx/string.c	Tue Feb 16 17:39:33 2016 +0100
+++ b/ucx/string.c	Mon May 23 12:28:32 2016 +0200
@@ -1,7 +1,7 @@
 /*
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
  *
- * Copyright 2013 Olaf Wintermann. All rights reserved.
+ * 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:
@@ -29,6 +29,7 @@
 #include <stdlib.h>
 #include <string.h>
 #include <stdarg.h>
+#include <ctype.h>
 
 #include "string.h"
 #include "allocator.h"
@@ -61,32 +62,71 @@
     return size;
 }
 
-sstr_t sstrncat(sstr_t s, size_t n, sstr_t c1, ...) {
-    va_list ap;
-    va_start(ap, c1);
-    s.ptr[0] = 0;
+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;
+    }
     
-    size_t len = s.length;
-    size_t cplen = c1.length > len ? len : c1.length;
-    char   *ptr = s.ptr;
+    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;
+    }
     
-    memcpy(ptr, c1.ptr, cplen);
-    len -= cplen;
-    ptr += cplen;
-    for (size_t i = 1 ; i < n ; i++) {
-        sstr_t str = va_arg (ap, sstr_t);
-        cplen = str.length > len ? len : str.length;
-        if(cplen <= 0) {
-            va_end(ap);
-            return s;
-        }
-        memcpy(ptr, str.ptr, cplen);
-        len -= cplen;
-        ptr += cplen;
+    // 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);
-    s.length = ptr - s.ptr;
+    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;
 }
 
@@ -97,13 +137,15 @@
 sstr_t sstrsubsl(sstr_t s, size_t start, size_t length) {
     sstr_t new_sstr;
     if (start >= s.length) {
-        return s;
+        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;
     }
-    if (length > s.length-start) {
-        length = s.length-start;
-    }
-    new_sstr.ptr = &s.ptr[start];
-    new_sstr.length = length;
     return new_sstr;
 }
 
@@ -133,18 +175,18 @@
     return n;
 }
 
-sstr_t* sstrsplit(sstr_t s, sstr_t d, size_t *n) {
+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, size_t *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;
-    size_t nmax = *n;
+    ssize_t nmax = *n;
     *n = 1;
 
     /* special case: exact match - no processing needed */
@@ -179,18 +221,27 @@
         }
         if ((*n) == nmax) break;
     }
-    result = (sstr_t*) allocator->malloc(allocator->pool, sizeof(sstr_t)*(*n));
+    result = (sstr_t*) almalloc(allocator, sizeof(sstr_t)*(*n));
 
     if (result) {
         char *pptr = sv.ptr;
-        for (size_t i = 0 ; i < *n ; i++) {
+        for (ssize_t i = 0 ; i < *n ; i++) {
             size_t l = strlen(pptr);
-            char* ptr = (char*) allocator->malloc(allocator->pool, l + 1);
-            memcpy(ptr, pptr, l);
-            ptr[l] = 0;
+            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;
+                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;
@@ -231,7 +282,7 @@
 
 sstr_t sstrdup_a(UcxAllocator *allocator, sstr_t s) {
     sstr_t newstring;
-    newstring.ptr = (char*)allocator->malloc(allocator->pool, s.length + 1);
+    newstring.ptr = (char*)almalloc(allocator, s.length + 1);
     if (newstring.ptr) {
         newstring.length = s.length;
         newstring.ptr[newstring.length] = 0;
@@ -246,38 +297,15 @@
 
 sstr_t sstrtrim(sstr_t string) {
     sstr_t newstr = string;
-    if (string.length == 0) {
-        return newstr;
-    }
     
-    size_t i;
-    for(i=0;i<string.length;i++) {
-        char c = string.ptr[i];
-        if(c > 32) {
-            break;
-        }
+    while (newstr.length > 0 && isspace(*newstr.ptr)) {
+        newstr.ptr++;
+        newstr.length--;
     }
-    newstr.ptr = &string.ptr[i];
-    newstr.length = string.length - i;
-    
-    if(newstr.length == 0) {
-        return newstr;
+    while (newstr.length > 0 && isspace(newstr.ptr[newstr.length-1])) {
+        newstr.length--;
     }
     
-    i = newstr.length - 1;
-    for(;;) {
-        char c = newstr.ptr[i];
-        if(c > 32) {
-            break;
-        }
-        if(i > 0) {
-            i--;
-        } else {
-            break;
-        }
-    }
-    newstr.length = i + 1;
-    
     return newstr;
 }
 
@@ -311,3 +339,35 @@
             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;
+}
--- a/ucx/string.h	Tue Feb 16 17:39:33 2016 +0100
+++ b/ucx/string.h	Mon May 23 12:28:32 2016 +0200
@@ -1,7 +1,7 @@
 /*
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
  *
- * Copyright 2013 Olaf Wintermann. All rights reserved.
+ * 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:
@@ -119,38 +119,36 @@
  */
 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 strings.
- * 
- * At least one string must be specified and there must be enough memory
- * available referenced by the destination sstr_t.ptr for this function to
- * successfully concatenate all specified strings.
- * 
- * The sstr_t.length of the destination string specifies the capacity and
- * should match the total memory available referenced by the destination
- * sstr_t.ptr. This function <i>never</i> copies data beyond the capacity and
- * does not modify any of the source strings.
+ * Concatenates two or more strings using an UcxAllocator.
  * 
- * <b>Attention:</b>
- * <ul>
- *   <li>Any content in the destination string will be overwritten</li>
- *   <li>The destination sstr_t.ptr is <b>NOT</b>
- *       <code>NULL</code>-terminated</li>
- *   <li>The destination sstr_t.length is set to the total length of the
- *       concatenated strings</li>
- *   <li><i>Hint:</i> get a <code>NULL</code>-terminated string by performing
- *       <code>mystring.ptr[mystring.length]='\0'</code> after calling this
- *       function</li>
- * </ul>
+ * See sstrcat() for details.
  *
- * @param dest    new sstr_t with capacity information and allocated memory
+ * @param a       the allocator to use
  * @param count   the total number of strings to concatenate
- * @param src     the first string
- * @param ...     all other strings
- * @return the argument for <code>dest</code> is returned
+ * @param s1      first string
+ * @param s2      second string
+ * @param ...     all remaining strings
+ * @return the concatenated string
  */
-sstr_t sstrncat(sstr_t dest, size_t count, sstr_t src, ...);
+sstr_t sstrcat_a(UcxAllocator *a, size_t count, sstr_t s1, sstr_t s2, ...);
 
 
 /**
@@ -227,7 +225,7 @@
  * </ul>
  * 
  * The integer referenced by <code>count</code> is used as input and determines
- * the maximum size of the resulting list, i.e. the maximum count of splits to
+ * 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
@@ -237,36 +235,36 @@
  *   <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 list items, otherwise</li>
+ *   <li>the count of array items, otherwise</li>
  * </ul>
  * 
  * If the string starts with the delimiter, the first item of the resulting
- * list will be an empty string.
+ * array will be an empty string.
  * 
  * If the string ends with the delimiter and the maximum list size is not
- * exceeded, the last list item will be an empty string.
+ * exceeded, the last array item will be an empty string.
  * 
- * <b>Attention:</b> All list items <b>AND</b> all sstr_t.ptr of the list
+ * <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 list (0 for an
- *               unbounded list), OUT: the actual size of the list
- * @return a list of the split strings as sstr_t array or
+ * @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, size_t *count);
+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 list items and the memory for
+ * 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.
  * 
@@ -276,15 +274,15 @@
  * @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 list (0 for an
- *               unbounded list), OUT: the actual size of the list
- * @return a list of the split strings as sstr_t array or
+ * @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,
-        size_t *count);
+        ssize_t *count);
 
 /**
  * Compares two UCX strings with standard <code>memcmp()</code>.
@@ -385,6 +383,56 @@
  */
 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
--- a/ucx/test.c	Tue Feb 16 17:39:33 2016 +0100
+++ b/ucx/test.c	Mon May 23 12:28:32 2016 +0200
@@ -1,7 +1,7 @@
 /*
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
  *
- * Copyright 2013 Olaf Wintermann. All rights reserved.
+ * 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:
--- a/ucx/test.h	Tue Feb 16 17:39:33 2016 +0100
+++ b/ucx/test.h	Mon May 23 12:28:32 2016 +0200
@@ -1,7 +1,7 @@
 /*
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
  *
- * Copyright 2013 Olaf Wintermann. All rights reserved.
+ * 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:
@@ -84,8 +84,8 @@
 
 /**
  * Alias for the <code>__func__</code> preprocessor macro.
- * Some compilers use <code>__func__</code> and others use __FUNC__.
- * We use __FUNC__ so we define it for those compilers which use
+ * Some compilers use <code>__func__</code> and others use __FUNCTION__.
+ * We use __FUNCTION__ so we define it for those compilers which use
  * <code>__func__</code>.
  */
 #define __FUNCTION__ __func__
--- a/ucx/ucx.c	Tue Feb 16 17:39:33 2016 +0100
+++ b/ucx/ucx.c	Mon May 23 12:28:32 2016 +0200
@@ -9,7 +9,7 @@
  * 
  * <h2>LICENCE</h2>
  * 
- * Copyright 2013 Olaf Wintermann. All rights reserved.
+ * 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:
--- a/ucx/ucx.h	Tue Feb 16 17:39:33 2016 +0100
+++ b/ucx/ucx.h	Mon May 23 12:28:32 2016 +0200
@@ -1,7 +1,7 @@
 /*
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
  *
- * Copyright 2013 Olaf Wintermann. All rights reserved.
+ * 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:
@@ -37,10 +37,10 @@
 #define	UCX_H
 
 /** Major UCX version as integer constant. */
-#define UCX_VERSION_MAJOR   1
+#define UCX_VERSION_MAJOR   0
 
 /** Minor UCX version as integer constant. */
-#define UCX_VERSION_MINOR   0
+#define UCX_VERSION_MINOR   9
 
 /** The UCX version in format [major].[minor] */
 #define UCX_VERSION UCX_VERSION_MAJOR.UCX_VERSION_MINOR
@@ -70,6 +70,14 @@
 /** 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.
--- a/ucx/utils.c	Tue Feb 16 17:39:33 2016 +0100
+++ b/ucx/utils.c	Mon May 23 12:28:32 2016 +0200
@@ -1,7 +1,7 @@
 /*
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
  *
- * Copyright 2013 Olaf Wintermann. All rights reserved.
+ * 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:
@@ -54,18 +54,22 @@
         return 0;
     }
     
+    char *lbuf;    
     size_t ncp = 0;
-    if (!buf) {
-        buf = (char*)malloc(bufsize);
-        if(buf == NULL) {
+    
+    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(buf, 1, rn, src)) != 0) {
-        r = writefnc(buf, 1, r, dest);
+    while((r = readfnc(lbuf, 1, rn, src)) != 0) {
+        r = writefnc(lbuf, 1, r, dest);
         ncp += r;
         n -= r;
         rn = bufsize > n ? n : bufsize;
@@ -74,7 +78,10 @@
         }
     }
     
-    free(buf);
+    if (lbuf != buf) {
+        free(lbuf);
+    }
+    
     return ncp;
 }
 
@@ -121,10 +128,12 @@
 }
 
 int ucx_ptrcmp(void *ptr1, void *ptr2, void *data) {
-    if (ptr1 == ptr2) {
+    intptr_t p1 = (intptr_t) ptr1;
+    intptr_t p2 = (intptr_t) ptr2;
+    if (p1 == p2) {
         return 0;
     } else {
-        return ptr1 < ptr2 ? -1 : 1;
+        return p1  < p2 ? -1 : 1;
     }
 }
 
@@ -212,30 +221,36 @@
     va_copy(ap2, ap);
     int ret = vsnprintf(buf, UCX_PRINTF_BUFSIZE, fmt, ap);
     if (ret > 0 && ret < UCX_PRINTF_BUFSIZE) {
-        s.ptr = (char*)a->malloc(a->pool, ret + 1);
-        s.length = (size_t)ret;
-        memcpy(s.ptr, buf, ret);
-        s.ptr[s.length] = '\0';
+        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*)a->malloc(a->pool, len);
-        ret = vsnprintf(s.ptr, len, fmt, ap2);
-        if (ret < 0) {
-            free(s.ptr);
-            s.ptr = NULL;
-        } else {
-            s.length = (size_t)ret;
+        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*)a->malloc(a->pool, ret + 1);
-        s.length = (size_t)ret;
-        memcpy(s.ptr, buf, ret);
-        s.ptr[s.length] = '\0';
+        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;
     }
--- a/ucx/utils.h	Tue Feb 16 17:39:33 2016 +0100
+++ b/ucx/utils.h	Mon May 23 12:28:32 2016 +0200
@@ -1,7 +1,7 @@
 /*
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
  *
- * Copyright 2013 Olaf Wintermann. All rights reserved.
+ * 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:
@@ -219,6 +219,10 @@
  */
 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().
  * 
--- a/ui/gtk/model.c	Tue Feb 16 17:39:33 2016 +0100
+++ b/ui/gtk/model.c	Mon May 23 12:28:32 2016 +0200
@@ -105,14 +105,14 @@
     return G_TYPE_INVALID;
 }
 
-static void ui_model_set_value(UiModelType type, void *data, GValue *value) {
+static void ui_model_set_value(GType type, void *data, GValue *value) {
     switch(type) {
-        case UI_STRING: {
+        case G_TYPE_STRING: {
             value->g_type = G_TYPE_STRING;
             g_value_set_string(value, data);
             return;
         }
-        case UI_INTEGER: {
+        case G_TYPE_INT: {
             value->g_type = G_TYPE_INT;
             int *i = data;
             g_value_set_int(value, *i);
@@ -126,11 +126,20 @@
     UiListModel *model = g_object_new(list_model_type, NULL);
     model->info = info;
     model->list = list;
-    model->columntypes = malloc(sizeof(GType));
-    model->numcolumns = info->columns;
+    model->columntypes = calloc(sizeof(GType), 2 * info->columns);
+    int ncol = 0;
     for(int i=0;i<info->columns;i++) {
-        model->columntypes[i] = ui_gtk_type(info->types[i]);
+        UiModelType type = info->types[i];
+        if(type == UI_ICON_TEXT) {
+            model->columntypes[ncol] = G_TYPE_STRING;
+            ncol++;
+            model->columntypes[ncol] = G_TYPE_STRING;
+        } else {
+            model->columntypes[ncol] = ui_gtk_type(info->types[i]);
+        }
+        ncol++;
     }
+    model->numcolumns = ncol;
     return model;
 }
 
@@ -237,7 +246,7 @@
     //list->current = iter->user_data3;
     if(model->info->getvalue) {
         void *data = model->info->getvalue(iter->user_data3, column);
-        ui_model_set_value(model->info->types[column], data, value);
+        ui_model_set_value(model->columntypes[column], data, value);
     } else {
         value->g_type = G_TYPE_INVALID;
     }
--- a/ui/gtk/tree.c	Tue Feb 16 17:39:33 2016 +0100
+++ b/ui/gtk/tree.c	Mon May 23 12:28:32 2016 +0200
@@ -134,14 +134,33 @@
 UIWIDGET ui_table_var(UiObject *obj, UiListPtr *list, UiModelInfo *modelinfo) {
     // create treeview
     GtkWidget *view = gtk_tree_view_new();
+    int addi = 0;
     for(int i=0;i<modelinfo->columns;i++) {
-        GtkCellRenderer *renderer = gtk_cell_renderer_text_new();
-        GtkTreeViewColumn *column = gtk_tree_view_column_new_with_attributes(
+        GtkTreeViewColumn *column = NULL;
+        if(modelinfo->types[i] == UI_ICON_TEXT) {
+            column = gtk_tree_view_column_new();
+            gtk_tree_view_column_set_title(column, modelinfo->titles[i]);
+            
+            GtkCellRenderer *iconrenderer = gtk_cell_renderer_pixbuf_new();
+            GtkCellRenderer *textrenderer = gtk_cell_renderer_text_new();
+            
+            gtk_tree_view_column_pack_end(column, textrenderer, TRUE);
+            gtk_tree_view_column_pack_start(column, iconrenderer, FALSE);
+            
+            
+            gtk_tree_view_column_add_attribute(column, iconrenderer, "icon-name", i);
+            gtk_tree_view_column_add_attribute(column, textrenderer, "text", i+1);
+            
+            addi++;
+        } else {
+            GtkCellRenderer *renderer = gtk_cell_renderer_text_new();
+            column = gtk_tree_view_column_new_with_attributes(
                 modelinfo->titles[i],
                 renderer,
                 "text",
-                i,
+                i + addi,
                 NULL);
+        }
         gtk_tree_view_column_set_resizable(column, TRUE);
         gtk_tree_view_append_column(GTK_TREE_VIEW(view), column);
     }
--- a/ui/motif/container.c	Tue Feb 16 17:39:33 2016 +0100
+++ b/ui/motif/container.c	Mon May 23 12:28:32 2016 +0200
@@ -320,6 +320,27 @@
     ucx_list_free(rowdim);
 }
 
+UiContainer* ui_scrolledwindow_container(UiObject *obj, Widget scrolledwindow) {
+    UiContainer *ct = ucx_mempool_calloc(
+            obj->ctx->mempool,
+            1,
+            sizeof(UiContainer));
+    ct->widget = scrolledwindow;
+    ct->prepare = ui_scrolledwindow_container_prepare;
+    ct->add = ui_scrolledwindow_container_add;
+    return ct;
+}
+
+Widget ui_scrolledwindow_container_prepare(UiContainer *ct, Arg *args, int *n, UiBool fill) {
+    return ct->widget;
+}
+
+void ui_scrolledwindow_container_add(UiContainer *ct, Widget widget) {
+    ui_reset_layout(ct->layout);
+    ct->current = widget;
+}
+
+
 UiContainer* ui_tabview_container(UiObject *obj, Widget frame) {
     UiTabViewContainer *ct = ucx_mempool_calloc(
             obj->ctx->mempool,
@@ -413,6 +434,25 @@
     return grid;
 }
 
+UIWIDGET ui_scrolledwindow(UiObject *obj) {
+    UiContainer *ct = uic_get_current_container(obj);
+    
+    Arg args[16];
+    int n = 0;
+    XtSetArg(args[n], XmNscrollingPolicy, XmAUTOMATIC); // TODO: dosn't work, use XmAPPLICATION_DEFINED
+    n++;
+    Widget parent = ct->prepare(ct, args, &n, TRUE);
+    Widget scrolledwindow = XmCreateScrolledWindow(parent, "scrolledwindow", args, n);
+    ct->add(ct, scrolledwindow);
+    XtManageChild(scrolledwindow);
+    
+    UiObject *newobj = uic_object_new(obj, scrolledwindow);
+    newobj->container = ui_scrolledwindow_container(obj, scrolledwindow);
+    uic_obj_add(obj, newobj);
+    
+    return scrolledwindow;
+}
+
 UIWIDGET ui_sidebar(UiObject *obj) {
     UiContainer *ct = uic_get_current_container(obj);
     
--- a/ui/motif/container.h	Tue Feb 16 17:39:33 2016 +0100
+++ b/ui/motif/container.h	Mon May 23 12:28:32 2016 +0200
@@ -136,6 +136,10 @@
 Widget ui_grid_container_prepare(UiContainer *ct, Arg *args, int *n, UiBool fill);
 void ui_grid_container_add(UiContainer *ct, Widget widget);
 
+UiContainer* ui_scrolledwindow_container(UiObject *obj, Widget scrolledwindow);
+Widget ui_scrolledwindow_container_prepare(UiContainer *ct, Arg *args, int *n, UiBool fill);
+void ui_scrolledwindow_container_add(UiContainer *ct, Widget widget);
+
 UiContainer* ui_tabview_container(UiObject *obj, Widget rowcolumn);
 Widget ui_tabview_container_prepare(UiContainer *ct, Arg *args, int *n, UiBool fill);
 void ui_tabview_container_add(UiContainer *ct, Widget widget);
--- a/ui/ui/tree.h	Tue Feb 16 17:39:33 2016 +0100
+++ b/ui/ui/tree.h	Mon May 23 12:28:32 2016 +0200
@@ -41,7 +41,9 @@
 
 typedef enum UiModelType {
     UI_STRING = 0,
-    UI_INTEGER
+    UI_INTEGER,
+    UI_ICON,
+    UI_ICON_TEXT,
 } UiModelType;
 
 struct UiModelInfo {

mercurial