fixes use after free when a GtkTreeView was destroyed

Sun, 21 Jan 2018 12:13:09 +0100

author
Olaf Wintermann <olaf.wintermann@gmail.com>
date
Sun, 21 Jan 2018 12:13:09 +0100
changeset 152
62921b370c60
parent 151
11f3bb408051
child 153
ee49d1852a5f

fixes use after free when a GtkTreeView was destroyed

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/common/types.c file | annotate | diff | comparison | revisions
ui/gtk/model.c file | annotate | diff | comparison | revisions
ui/gtk/model.h file | annotate | diff | comparison | revisions
ui/gtk/tree.c file | annotate | diff | comparison | revisions
ui/gtk/tree.h file | annotate | diff | comparison | revisions
ui/ui/toolkit.h file | annotate | diff | comparison | revisions
--- a/application/main.c	Wed Nov 22 12:59:13 2017 +0100
+++ b/application/main.c	Sun Jan 21 12:13:09 2018 +0100
@@ -149,7 +149,7 @@
 }
 
 void application_startup(UiEvent *event, void *data) {
-    UiIcon *icon = ui_icon("gnome-folder", 16);
+    UiIcon *icon = ui_icon("folder", 16);
     img = ui_icon_image(icon);
     if(!img) {
         fprintf(stderr, "Cannot load folder icon\n");
--- a/ucx/allocator.c	Wed Nov 22 12:59:13 2017 +0100
+++ b/ucx/allocator.c	Sun Jan 21 12:13:09 2018 +0100
@@ -1,7 +1,7 @@
 /*
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
  *
- * Copyright 2015 Olaf Wintermann. All rights reserved.
+ * Copyright 2016 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	Wed Nov 22 12:59:13 2017 +0100
+++ b/ucx/allocator.h	Sun Jan 21 12:13:09 2018 +0100
@@ -1,7 +1,7 @@
 /*
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
  *
- * Copyright 2015 Olaf Wintermann. All rights reserved.
+ * Copyright 2016 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:
@@ -28,7 +28,7 @@
 /**
  * Allocator for custom memory management.
  * 
- * An UCX allocator consists of a pointer to the memory area / pool and four
+ * A 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>.
@@ -38,7 +38,7 @@
  * 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
+ * can be provided to the memory management functions. A UcxMempool is just
  * one example.
  * 
  * @see mempool.h
@@ -116,7 +116,7 @@
  * 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
+ * to any function that takes a UcxAllocator as argument. Note that using
  * this function is the recommended way of passing a default allocator, thus
  * it never runs out of scope.
  * 
--- a/ucx/avl.c	Wed Nov 22 12:59:13 2017 +0100
+++ b/ucx/avl.c	Sun Jan 21 12:13:09 2018 +0100
@@ -1,7 +1,7 @@
 /*
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
  *
- * Copyright 2015 Olaf Wintermann. All rights reserved.
+ * Copyright 2016 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:
@@ -26,9 +26,13 @@
  * POSSIBILITY OF SUCH DAMAGE.
  */
 
+#include <limits.h>
+
 #include "avl.h"
 
 #define ptrcast(ptr) ((void*)(ptr))
+#define alloc_tree(al) (UcxAVLTree*) almalloc((al), sizeof(UcxAVLTree))
+#define alloc_node(al) (UcxAVLNode*) almalloc((al), sizeof(UcxAVLNode))
 
 static void ucx_avl_connect(UcxAVLTree *tree,
         UcxAVLNode *node, UcxAVLNode *child, intptr_t nullkey) {
@@ -107,7 +111,7 @@
 }
 
 UcxAVLTree *ucx_avl_new_a(cmp_func cmpfunc, UcxAllocator *allocator) {
-    UcxAVLTree *tree = almalloc(allocator, sizeof(UcxAVLTree));
+    UcxAVLTree* tree = alloc_tree(allocator);
     if (tree) {
         tree->allocator = allocator;
         tree->cmpfunc = cmpfunc;
@@ -147,6 +151,46 @@
     return n ? n->value : NULL;
 }
 
+UcxAVLNode *ucx_avl_find_node(UcxAVLTree *tree, intptr_t key,
+        distance_func dfnc, int mode) {
+    UcxAVLNode *n = tree->root;
+    UcxAVLNode *closest = NULL;
+
+    intmax_t cmpresult;
+    intmax_t closest_dist;
+    closest_dist = mode == UCX_AVL_FIND_LOWER_BOUNDED ? INTMAX_MIN : INTMAX_MAX;
+    
+    while (n && (cmpresult = dfnc(
+            ptrcast(key), ptrcast(n->key), tree->userdata))) {
+        if (mode == UCX_AVL_FIND_CLOSEST) {
+            intmax_t dist = cmpresult;
+            if (dist < 0) dist *= -1;
+            if (dist < closest_dist) {
+                closest_dist = dist;
+                closest = n;
+            }
+        } else if (mode == UCX_AVL_FIND_LOWER_BOUNDED && cmpresult <= 0) {
+            if (cmpresult > closest_dist) {
+                closest_dist = cmpresult;
+                closest = n;
+            }
+        } else if (mode == UCX_AVL_FIND_UPPER_BOUNDED && cmpresult >= 0) {
+            if (cmpresult < closest_dist) {
+                closest_dist = cmpresult;
+                closest = n;
+            }
+        }
+        n = cmpresult > 0 ? n->right : n->left;
+    }
+    return n ? n : closest;
+}
+
+void *ucx_avl_find(UcxAVLTree *tree, intptr_t key,
+        distance_func dfnc, int mode) {
+    UcxAVLNode *n = ucx_avl_find_node(tree, key, dfnc, mode);
+    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);
 }
@@ -167,7 +211,7 @@
         }
 
         if (cmpresult) {
-            UcxAVLNode *e = almalloc(tree->allocator, sizeof(UcxAVLNode));
+            UcxAVLNode* e = alloc_node(tree->allocator);
             if (e) {
                 e->key = key; e->value = value; e->height = 1;
                 e->parent = e->left = e->right = NULL;
@@ -185,7 +229,7 @@
             return 0;
         }
     } else {
-        tree->root = almalloc(tree->allocator, sizeof(UcxAVLNode));
+        tree->root = alloc_node(tree->allocator);
         if (tree->root) {
             tree->root->key = key; tree->root->value = value;
             tree->root->height = 1;
@@ -270,3 +314,43 @@
 size_t ucx_avl_count(UcxAVLTree *tree) {
     return ucx_avl_countn(tree->root);
 }
+
+UcxAVLNode* ucx_avl_pred(UcxAVLNode* node) {
+    if (node->left) {
+        UcxAVLNode* n = node->left;
+        while (n->right) {
+            n = n->right;
+        }
+        return n;
+    } else {
+        UcxAVLNode* n = node;
+        while (n->parent) {
+            if (n->parent->right == n) {
+                return n->parent;
+            } else {
+                n = n->parent;
+            }
+        }
+        return NULL;
+    }
+}
+
+UcxAVLNode* ucx_avl_succ(UcxAVLNode* node) {
+    if (node->right) {
+        UcxAVLNode* n = node->right;
+        while (n->left) {
+            n = n->left;
+        }
+        return n;
+    } else {
+        UcxAVLNode* n = node;
+        while (n->parent) {
+            if (n->parent->left == n) {
+                return n->parent;
+            } else {
+                n = n->parent;
+            }
+        }
+        return NULL;
+    }
+}
--- a/ucx/avl.h	Wed Nov 22 12:59:13 2017 +0100
+++ b/ucx/avl.h	Sun Jan 21 12:13:09 2018 +0100
@@ -1,7 +1,7 @@
 /*
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
  *
- * Copyright 2015 Olaf Wintermann. All rights reserved.
+ * Copyright 2016 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,7 +44,7 @@
 
 #include "ucx.h"
 #include "allocator.h"
-#include <stdint.h>
+#include <inttypes.h>
 
 #ifdef	__cplusplus
 extern "C" {
@@ -134,7 +134,7 @@
 UcxAVLTree *ucx_avl_new_a(cmp_func cmpfunc, UcxAllocator *allocator);
 
 /**
- * Destroys an UcxAVLTree.
+ * Destroys a UcxAVLTree.
  * @param tree the tree to destroy
  */
 void ucx_avl_free(UcxAVLTree *tree);
@@ -164,6 +164,68 @@
 void *ucx_avl_get(UcxAVLTree *tree, intptr_t key);
 
 /**
+ * A mode for #ucx_avl_find_node() with the same behavior as
+ * #ucx_avl_get_node().
+ */
+#define UCX_AVL_FIND_EXACT         0
+/**
+ * A mode for #ucx_avl_find_node() finding the node whose key is at least
+ * as large as the specified key.
+ */
+#define UCX_AVL_FIND_LOWER_BOUNDED 1
+/**
+ * A mode for #ucx_avl_find_node() finding the node whose key is at most
+ * as large as the specified key.
+ */
+#define UCX_AVL_FIND_UPPER_BOUNDED 2
+/**
+ * A mode for #ucx_avl_find_node() finding the node with a key that is as close
+ * to the specified key as possible. If the key is present, the behavior is
+ * like #ucx_avl_get_node(). This mode only returns <code>NULL</code> on
+ * empty trees.
+ */
+#define UCX_AVL_FIND_CLOSEST       3
+
+/**
+ * Finds a node within the tree. The following modes are supported:
+ * <ul>
+ * <li>#UCX_AVL_FIND_EXACT: the same behavior as #ucx_avl_get_node()</li>
+ * <li>#UCX_AVL_FIND_LOWER_BOUNDED: finds the node whose key is at least
+ * as large as the specified key</li>
+ * <li>#UCX_AVL_FIND_UPPER_BOUNDED: finds the node whose key is at most
+ * as large as the specified key</li>
+ * <li>#UCX_AVL_FIND_CLOSEST: finds the node with a key that is as close to
+ * the specified key as possible. If the key is present, the behavior is
+ * like #ucx_avl_get_node(). This mode only returns <code>NULL</code> on
+ * empty trees.</li> 
+ * </ul>
+ * 
+ * The distance function provided MUST agree with the compare function of
+ * the AVL tree.
+ * 
+ * @param tree the UcxAVLTree
+ * @param key the key
+ * @param dfnc the distance function
+ * @param mode the find mode
+ * @return the node (or <code>NULL</code>, if no node can be found)
+ */
+UcxAVLNode *ucx_avl_find_node(UcxAVLTree *tree, intptr_t key,
+        distance_func dfnc, int mode);
+
+/**
+ * Finds a value within the tree.
+ * See #ucx_avl_find_node() for details.
+ * 
+ * @param tree the UcxAVLTree
+ * @param key the key
+ * @param dfnc the distance function
+ * @param mode the find mode
+ * @return the value (or <code>NULL</code>, if no value can be found)
+ */
+void *ucx_avl_find(UcxAVLTree *tree, intptr_t key,
+        distance_func dfnc, int mode);
+
+/**
  * Puts a key/value pair into the tree.
  * 
  * Attention: use this function only, if a possible old value does not need
@@ -196,7 +258,7 @@
  * 
  * 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.
+ * is freed, so its further use is generally undefined.
  * 
  * @param tree the UcxAVLTree
  * @param node the node to remove
@@ -241,6 +303,22 @@
  */
 size_t ucx_avl_count(UcxAVLTree *tree);
 
+/**
+ * Finds the in-order predecessor of the given node.
+ * @param node an AVL node
+ * @return the in-order predecessor of the given node, or <code>NULL</code> if
+ * the given node is the in-order minimum
+ */
+UcxAVLNode* ucx_avl_pred(UcxAVLNode* node);
+
+/**
+ * Finds the in-order successor of the given node.
+ * @param node an AVL node
+ * @return the in-order successor of the given node, or <code>NULL</code> if
+ * the given node is the in-order maximum
+ */
+UcxAVLNode* ucx_avl_succ(UcxAVLNode* node);
+
 #ifdef	__cplusplus
 }
 #endif
--- a/ucx/buffer.c	Wed Nov 22 12:59:13 2017 +0100
+++ b/ucx/buffer.c	Sun Jan 21 12:13:09 2018 +0100
@@ -1,7 +1,7 @@
 /*
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
  *
- * Copyright 2015 Olaf Wintermann. All rights reserved.
+ * Copyright 2016 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:
@@ -150,6 +150,7 @@
 size_t ucx_buffer_write(const void *ptr, size_t size, size_t nitems,
         UcxBuffer *buffer) {
     size_t len = size * nitems;
+    const char *string = ptr;
     size_t required = buffer->pos + len;
     if (buffer->pos > required) {
         return 0;
--- a/ucx/buffer.h	Wed Nov 22 12:59:13 2017 +0100
+++ b/ucx/buffer.h	Sun Jan 21 12:13:09 2018 +0100
@@ -1,7 +1,7 @@
 /*
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
  *
- * Copyright 2015 Olaf Wintermann. All rights reserved.
+ * Copyright 2016 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:
@@ -197,9 +197,9 @@
 int ucx_buffer_extend(UcxBuffer *buffer, size_t additional_bytes);
 
 /**
- * Writes data to an UcxBuffer.
+ * Writes data to a UcxBuffer.
  * 
- * The position of the buffer is increased by the number of bytes read.
+ * 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
@@ -211,7 +211,7 @@
         UcxBuffer *buffer);
 
 /**
- * Reads data from an UcxBuffer.
+ * Reads data from a UcxBuffer.
  * 
  * The position of the buffer is increased by the number of bytes read.
  * 
--- a/ucx/list.c	Wed Nov 22 12:59:13 2017 +0100
+++ b/ucx/list.c	Sun Jan 21 12:13:09 2018 +0100
@@ -1,7 +1,7 @@
 /*
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
  *
- * Copyright 2015 Olaf Wintermann. All rights reserved.
+ * Copyright 2016 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:
@@ -76,6 +76,13 @@
     }
 }
 
+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);
 }
@@ -99,6 +106,41 @@
     }
 }
 
+UcxList *ucx_list_append_once(UcxList *l, void *data,
+        cmp_func cmpfnc, void *cmpdata) {
+    return ucx_list_append_once_a(ucx_default_allocator(), l,
+            data, cmpfnc, cmpdata);
+}
+
+UcxList *ucx_list_append_once_a(UcxAllocator *alloc, UcxList *l, void *data,
+        cmp_func cmpfnc, void *cmpdata) {
+
+    UcxList *last = NULL;
+    {
+        UcxList *e = l;
+        while (e) {
+            if (cmpfnc(e->data, data, cmpdata) == 0) {
+                return l;
+            }
+            last = e;
+            e = e->next;
+        }
+    }
+    
+    UcxList *nl = ucx_list_append_a(alloc, NULL, data);
+    if (!nl) {
+        return NULL;
+    }
+
+    if (last == NULL) {
+        return nl;
+    } else {
+        nl->prev = last;
+        last->next = nl;
+        return l;
+    }
+}
+
 UcxList *ucx_list_prepend(UcxList *l, void *data) {
     return ucx_list_prepend_a(ucx_default_allocator(), l, data);
 }
--- a/ucx/list.h	Wed Nov 22 12:59:13 2017 +0100
+++ b/ucx/list.h	Sun Jan 21 12:13:09 2018 +0100
@@ -1,7 +1,7 @@
 /*
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
  *
- * Copyright 2015 Olaf Wintermann. All rights reserved.
+ * Copyright 2016 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:
@@ -46,13 +46,13 @@
 /**
  * Loop statement for UCX lists.
  * 
- * The first argument is a pointer to the list. In most cases this will be the
+ * 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.
  * 
- * The second argument is the name of the iteration variable. The scope of
- * this variable is limited to the <code>UCX_FOREACH</code> statement.
- * 
  * @param list The first element of the list
  * @param elem The variable name of the element
  */
@@ -102,13 +102,12 @@
 UcxList *ucx_list_clone(UcxList *list, copy_func cpyfnc, void* data);
 
 /**
- * Creates an element-wise copy of a list using an UcxAllocator.
+ * Creates an element-wise copy of a list using a 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.
+ * You might want to pass the allocator via the <code>data</code> parameter,
+ * to access it within the copy function for making deep copies.
  * 
  * @param allocator the allocator to use
  * @param list the list to copy
@@ -146,17 +145,19 @@
  * Destroys the entire list.
  * 
  * The members of the list are not automatically freed, so ensure they are
- * otherwise referenced or a memory leak will occur.
+ * 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.
+ * Destroys the entire list using a UcxAllocator.
  * 
  * See ucx_list_free() for details.
  * 
@@ -167,6 +168,20 @@
 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
@@ -181,7 +196,7 @@
 UcxList *ucx_list_append(UcxList *list, void *data);
 
 /**
- * Inserts an element at the end of the list using an UcxAllocator.
+ * Inserts an element at the end of the list using a UcxAllocator.
  * 
  * See ucx_list_append() for details.
  * 
@@ -196,6 +211,41 @@
 UcxList *ucx_list_append_a(UcxAllocator *allocator, UcxList *list, void *data);
 
 /**
+ * Inserts an element at the end of the list, if it is not present in the list.
+ * 
+ * 
+ * @param list the list where to append the data, or <code>NULL</code> to
+ * create a new list
+ * @param data the data to insert
+ * @param cmpfnc the compare function
+ * @param cmpdata additional data for the compare function
+ * @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_once(UcxList *list, void *data,
+        cmp_func cmpfnc, void *cmpdata);
+
+/**
+ * Inserts an element at the end of the list, if it is not present in the list,
+ * using a 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
+ * @param cmpfnc the compare function
+ * @param cmpdata additional data for the compare function
+ * @return <code>list</code>, if it is not <code>NULL</code> or a pointer to
+ * the newly created list otherwise
+ * @see ucx_list_append_a()
+ */
+UcxList *ucx_list_append_once_a(UcxAllocator *allocator,
+        UcxList *list, void *data, cmp_func cmpfnc, void *cmpdata);
+
+/**
  * Inserts an element at the beginning of the list.
  * 
  * You <i>should</i> overwrite the old list pointer by calling
@@ -212,7 +262,7 @@
 UcxList *ucx_list_prepend(UcxList *list, void *data);
 
 /**
- * Inserts an element at the beginning of the list using an UcxAllocator.
+ * Inserts an element at the beginning of the list using a UcxAllocator.
  * 
  * See ucx_list_prepend() for details.
  * 
@@ -327,7 +377,7 @@
 int ucx_list_contains(UcxList *list, void *elem, cmp_func cmpfnc, void *data);
 
 /**
- * Sorts an UcxList with natural merge sort.
+ * Sorts a UcxList with natural merge sort.
  * 
  * This function uses O(n) additional temporary memory for merge operations
  * that is automatically freed after each merge.
@@ -357,7 +407,7 @@
 UcxList *ucx_list_remove(UcxList *list, UcxList *element);
 
 /**
- * Removes an element from the list using an UcxAllocator.
+ * Removes an element from the list using a UcxAllocator.
  * 
  * See ucx_list_remove() for details.
  * 
--- a/ucx/logging.c	Wed Nov 22 12:59:13 2017 +0100
+++ b/ucx/logging.c	Sun Jan 21 12:13:09 2018 +0100
@@ -1,7 +1,7 @@
 /*
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
  *
- * Copyright 2015 Olaf Wintermann. All rights reserved.
+ * Copyright 2016 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/logging.h	Wed Nov 22 12:59:13 2017 +0100
+++ b/ucx/logging.h	Sun Jan 21 12:13:09 2018 +0100
@@ -1,7 +1,7 @@
 /*
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
  *
- * Copyright 2015 Olaf Wintermann. All rights reserved.
+ * Copyright 2016 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/map.c	Wed Nov 22 12:59:13 2017 +0100
+++ b/ucx/map.c	Sun Jan 21 12:13:09 2018 +0100
@@ -1,7 +1,7 @@
 /*
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
  *
- * Copyright 2015 Olaf Wintermann. All rights reserved.
+ * Copyright 2016 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/map.h	Wed Nov 22 12:59:13 2017 +0100
+++ b/ucx/map.h	Sun Jan 21 12:13:09 2018 +0100
@@ -1,7 +1,7 @@
 /*
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
  *
- * Copyright 2015 Olaf Wintermann. All rights reserved.
+ * Copyright 2016 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:
@@ -59,7 +59,7 @@
  * 
  * @param key the variable name for the key
  * @param value the variable name for the value
- * @param iter an UcxMapIterator
+ * @param iter a UcxMapIterator
  * @see ucx_map_iterator()
  */
 #define UCX_MAP_FOREACH(key,value,iter) \
@@ -68,13 +68,13 @@
 /** Type for the UCX map. @see UcxMap */
 typedef struct UcxMap          UcxMap;
 
-/** Type for a key of an UcxMap. @see UcxKey */
+/** Type for a key of a UcxMap. @see UcxKey */
 typedef struct UcxKey          UcxKey;
 
-/** Type for an element of an UcxMap. @see UcxMapElement */
+/** Type for an element of a UcxMap. @see UcxMapElement */
 typedef struct UcxMapElement   UcxMapElement;
 
-/** Type for an iterator over an UcxMap. @see UcxMapIterator */
+/** Type for an iterator over a UcxMap. @see UcxMapIterator */
 typedef struct UcxMapIterator  UcxMapIterator;
 
 /** Structure for the UCX map. */
@@ -89,7 +89,7 @@
     size_t        count;
 };
 
-/** Structure for a key of an UcxMap. */
+/** Structure for a key of a UcxMap. */
 struct UcxKey {
     /** The key data. */
     void   *data;
@@ -99,7 +99,7 @@
     int    hash;
 };
 
-/** Structure for an element of an UcxMap. */
+/** Structure for an element of a UcxMap. */
 struct UcxMapElement {
     /** The value data. */
     void          *data;
@@ -111,7 +111,7 @@
     UcxKey        key;
 };
 
-/** Structure for an iterator over an UcxMap. */
+/** Structure for an iterator over a UcxMap. */
 struct UcxMapIterator {
     /** The map to iterate over. */
     UcxMap        *map;
@@ -136,7 +136,7 @@
 UcxMap *ucx_map_new(size_t size);
 
 /**
- * Creates a new hash map with the specified size using an UcxAllocator.
+ * Creates a new hash map with the specified size using a UcxAllocator.
  * @param allocator the allocator to use
  * @param size the size of the hash map
  * @return a pointer to the new hash map
@@ -357,13 +357,13 @@
     ucx_map_remove(map, ucx_key((void*)&key, sizeof(key)))
 
 /**
- * Creates an UcxKey based on the given data.
+ * Creates a UcxKey based on the given data.
  * 
  * This function implicitly computes the hash.
  * 
  * @param data the data for the key
  * @param len the length of the data
- * @return an UcxKey with implicitly computed hash
+ * @return a UcxKey with implicitly computed hash
  * @see ucx_hash()
  */
 UcxKey ucx_key(void *data, size_t len);
@@ -380,14 +380,14 @@
 /**
  * Creates an iterator for a map.
  * 
- * <b>Note:</b> An UcxMapIterator iterates over all elements in all element
+ * <b>Note:</b> A UcxMapIterator iterates over all elements in all element
  * lists successively. Therefore the order highly depends on the key hashes and
  * may vary under different map sizes. So generally you may <b>NOT</b> rely on
  * the iteration order.
  * 
  * <b>Note:</b> The iterator is <b>NOT</b> initialized. You need to call
  * ucx_map_iter_next() at least once before accessing any information. However,
- * it is not recommended to access the fields of an UcxMapIterator directly.
+ * it is not recommended to access the fields of a UcxMapIterator directly.
  * 
  * @param map the map to create the iterator for
  * @return an iterator initialized on the first element of the
--- a/ucx/mempool.c	Wed Nov 22 12:59:13 2017 +0100
+++ b/ucx/mempool.c	Sun Jan 21 12:13:09 2018 +0100
@@ -1,7 +1,7 @@
 /*
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
  *
- * Copyright 2015 Olaf Wintermann. All rights reserved.
+ * Copyright 2016 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:
@@ -93,25 +93,30 @@
 }
 
 int ucx_mempool_chcap(UcxMempool *pool, size_t newcap) {
+    if (newcap < pool->ndata) {
+        return 1;
+    }
+    
     void **data = (void**) realloc(pool->data, newcap*sizeof(void*));
     if (data) {
         pool->data = data; 
         pool->size = newcap;
-        return EXIT_SUCCESS;
+        return 0;
     } else {
-        return EXIT_FAILURE;
+        return 1;
     }
 }
 
 void *ucx_mempool_malloc(UcxMempool *pool, size_t n) {
     if (pool->ndata >= pool->size) {
-        // The hard coded 16 is documented for this function and ucx_mempool_new
-        if (ucx_mempool_chcap(pool, pool->size + 16) == EXIT_FAILURE) {
+        size_t newcap = pool->size*2;
+        if (newcap < pool->size || ucx_mempool_chcap(pool, newcap)) {
             return NULL;
         }
     }
 
-    ucx_memchunk *mem = (ucx_memchunk*)malloc(sizeof(ucx_destructor) + n);
+    void *p = malloc(sizeof(ucx_destructor) + n);
+    ucx_memchunk *mem = (ucx_memchunk*)p;
     if (!mem) {
         return NULL;
     }
@@ -147,7 +152,7 @@
         }
         fprintf(stderr, "FATAL: 0x%08" PRIxPTR" not in mpool 0x%08" PRIxPTR"\n",
           (intptr_t)ptr, (intptr_t)pool);
-        exit(EXIT_FAILURE);
+        abort();
     } else {
         return newm + sizeof(ucx_destructor);
     }
@@ -172,7 +177,7 @@
     }
     fprintf(stderr, "FATAL: 0x%08" PRIxPTR" not in mpool 0x%08" PRIxPTR"\n",
             (intptr_t)ptr, (intptr_t)pool);
-    exit(EXIT_FAILURE);
+    abort();
 }
 
 void ucx_mempool_destroy(UcxMempool *pool) {
--- a/ucx/mempool.h	Wed Nov 22 12:59:13 2017 +0100
+++ b/ucx/mempool.h	Sun Jan 21 12:13:09 2018 +0100
@@ -1,7 +1,7 @@
 /*
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
  *
- * Copyright 2015 Olaf Wintermann. All rights reserved.
+ * Copyright 2016 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:
@@ -70,39 +70,29 @@
 /**
  * Creates a memory pool with the specified initial size.
  * 
- * As the created memory pool automatically grows in size by 16 elements, when
+ * As the created memory pool automatically grows in size by factor two when
  * trying to allocate memory on a full pool, it is recommended that you use
- * a multiple of 16 for the initial size.
+ * a power of two for the initial size.
  * 
- * @param n initial pool size (should be a multiple of 16)
+ * @param n initial pool size (should be a power of two, e.g. 16)
  * @return a pointer to the new memory pool
+ * @see ucx_mempool_new_default()
  */
 UcxMempool *ucx_mempool_new(size_t n);
 
 /**
  * Resizes a memory pool.
  * 
+ * This function will fail if the new capacity is not sufficient for the
+ * present data.
+ * 
  * @param pool the pool to resize
  * @param newcap the new capacity
- * @return <code>EXIT_SUCCESS</code> on success or
- * <code>EXIT_FAILURE</code> on failure
+ * @return zero on success or non-zero on failure
  */
 int ucx_mempool_chcap(UcxMempool *pool, size_t newcap);
 
 /**
- * Changes the pool size to the next smallest multiple of 16.
- * 
- * You may use this macro, to reduce the pool size after freeing
- * many pooled memory items.
- * 
- * @param pool the pool to clamp
- * @return <code>EXIT_SUCCESS</code> on success or
- * <code>EXIT_FAILURE</code> on failure
- */
-#define ucx_mempool_clamp(pool) ucx_mempool_chcap(pool, \
-        (pool->ndata & ~0xF)+0x10)
-
-/**
  * Allocates pooled memory.
  * 
  * @param pool the memory pool
@@ -127,10 +117,8 @@
 /**
  * Reallocates pooled memory.
  * 
- * If the memory to be reallocated is not contained by the specified pool, this
- * function will possibly fail. In case the memory had to be moved to another
- * location, this function will print out a message to <code>stderr</code>
- * and exit the program with error code <code>EXIT_FAILURE</code>.
+ * If the memory to be reallocated is not contained by the specified pool, the
+ * behavior is undefined.
  * 
  * @param pool the memory pool
  * @param ptr a pointer to the memory that shall be reallocated
@@ -146,10 +134,8 @@
  * Before freeing the memory, the specified destructor function (if any)
  * is called.
  * 
- * If you specify memory, that is not pooled by the specified memory pool, this
- * is considered as a severe error and an error message is written to
- * <code>stderr</code> and the application is shut down with code
- * <code>EXIT_FAILURE</code>.
+ * If you specify memory, that is not pooled by the specified memory pool, the
+ * program will terminate with a call to <code>abort()</code>.
  * 
  * @param pool the memory pool
  * @param ptr a pointer to the memory that shall be freed
@@ -180,7 +166,7 @@
  * pool is destroyed.
  * 
  * The only requirement for the specified memory is, that it <b>MUST</b> be
- * pooled memory by an UcxMempool or an element-compatible mempool. The pointer
+ * pooled memory by a UcxMempool or an element-compatible mempool. The pointer
  * to the destructor function is saved in a reserved area before the actual
  * memory.
  * 
--- a/ucx/properties.c	Wed Nov 22 12:59:13 2017 +0100
+++ b/ucx/properties.c	Sun Jan 21 12:13:09 2018 +0100
@@ -1,7 +1,7 @@
 /*
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
  *
- * Copyright 2015 Olaf Wintermann. All rights reserved.
+ * Copyright 2016 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/properties.h	Wed Nov 22 12:59:13 2017 +0100
+++ b/ucx/properties.h	Sun Jan 21 12:13:09 2018 +0100
@@ -1,7 +1,7 @@
 /*
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
  *
- * Copyright 2015 Olaf Wintermann. All rights reserved.
+ * Copyright 2016 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:
@@ -127,7 +127,7 @@
 UcxProperties *ucx_properties_new();
 
 /**
- * Destroys an UcxProperties object.
+ * Destroys a UcxProperties object.
  * @param prop the UcxProperties object to destroy
  */
 void ucx_properties_free(UcxProperties *prop);
@@ -137,8 +137,8 @@
  * 
  * After calling this function, you may parse the data by calling
  * ucx_properties_next() until it returns 0. The function ucx_properties2map()
- * is a convenience function that performs these successive calls of
- * ucx_properties_next() within a while loop and puts the properties to a map.
+ * is a convenience function that reads as much data as possible by using this
+ * function.
  * 
  * 
  * @param prop the UcxProperties object
@@ -170,7 +170,7 @@
 int ucx_properties_next(UcxProperties *prop, sstr_t *name, sstr_t *value);
 
 /**
- * Retrieves all available key/value-pairs and puts them into an UcxMap.
+ * Retrieves all available key/value-pairs and puts them into a UcxMap.
  * 
  * This is done by successive calls to ucx_properties_next() until no more
  * key/value-pairs can be retrieved. 
@@ -183,13 +183,11 @@
 int ucx_properties2map(UcxProperties *prop, UcxMap *map);
 
 /**
- * Loads a properties file to an UcxMap.
+ * Loads a properties file to a UcxMap.
  * 
- * This is a convenience function that reads chunks of 1 KB from an input
+ * This is a convenience function that reads data from an input
  * stream until the end of the stream is reached.
  * 
- * An UcxProperties object is implicitly created and destroyed.
- * 
  * @param map the map object to write the key/value-pairs to
  * @param file the <code>FILE*</code> stream to read from
  * @return 0 on success, or a non-zero value on error
@@ -200,7 +198,7 @@
 int ucx_properties_load(UcxMap *map, FILE *file);
 
 /**
- * Stores an UcxMap to a file.
+ * Stores a UcxMap to a file.
  * 
  * The key/value-pairs are written by using the following format:
  * 
--- a/ucx/stack.c	Wed Nov 22 12:59:13 2017 +0100
+++ b/ucx/stack.c	Sun Jan 21 12:13:09 2018 +0100
@@ -1,7 +1,7 @@
 /*
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
  *
- * Copyright 2015 Olaf Wintermann. All rights reserved.
+ * Copyright 2016 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/stack.h	Wed Nov 22 12:59:13 2017 +0100
+++ b/ucx/stack.h	Sun Jan 21 12:13:09 2018 +0100
@@ -1,7 +1,7 @@
 /*
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
  *
- * Copyright 2015 Olaf Wintermann. All rights reserved.
+ * Copyright 2016 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:
@@ -39,7 +39,6 @@
 #define	UCX_STACK_H
 
 #include "ucx.h"
-#include <stdint.h>
 #include "allocator.h"
 
 #ifdef	__cplusplus
@@ -179,7 +178,7 @@
  * @see ucx_stack_free
  * @see ucx_stack_popn
  */
-#define ucx_stack_pop(stack, dest) ucx_stack_popn(stack, dest, SIZE_MAX)
+#define ucx_stack_pop(stack, dest) ucx_stack_popn(stack, dest, (size_t)-1)
 
 /**
  * Removes the top most element from the stack and copies the content to <code>
--- a/ucx/string.c	Wed Nov 22 12:59:13 2017 +0100
+++ b/ucx/string.c	Sun Jan 21 12:13:09 2018 +0100
@@ -1,7 +1,7 @@
 /*
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
  *
- * Copyright 2015 Olaf Wintermann. All rights reserved.
+ * Copyright 2016 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 <stdint.h>
 #include <ctype.h>
 
 #include "string.h"
@@ -175,6 +176,79 @@
     return n;
 }
 
+#define ptable_r(dest, useheap, ptable, index) (dest = useheap ? \
+    ((size_t*)ptable)[index] : (size_t) ((uint8_t*)ptable)[index])
+
+#define ptable_w(useheap, ptable, index, src) do {\
+    if (!useheap) ((uint8_t*)ptable)[index] = (uint8_t) src;\
+    else ((size_t*)ptable)[index] = src;\
+    } while (0);
+
+sstr_t sstrstr(sstr_t string, sstr_t match) {
+    if (match.length == 0) {
+        return string;
+    }
+    
+    /* prepare default return value in case of no match */
+    sstr_t result = sstrn(NULL, 0);
+    
+    /*
+     * IMPORTANT:
+     * our prefix table contains the prefix length PLUS ONE
+     * this is our decision, because we want to use the full range of size_t
+     * the original algorithm needs a (-1) at one single place
+     * and we want to avoid that
+     */
+    
+    /* static prefix table */
+    static uint8_t s_prefix_table[256];
+    
+    /* check pattern length and use appropriate prefix table */
+    /* if the pattern exceeds static prefix table, allocate on the heap */
+    register int useheap = match.length > 255;
+    register void* ptable = useheap ?
+        calloc(match.length+1, sizeof(size_t)): s_prefix_table;
+    
+    /* keep counter in registers */
+    register size_t i, j;
+    
+    /* fill prefix table */
+    i = 0; j = 0;
+    ptable_w(useheap, ptable, i, j);
+    while (i < match.length) {
+        while (j >= 1 && match.ptr[j-1] != match.ptr[i]) {
+            ptable_r(j, useheap, ptable, j-1);
+        }
+        i++; j++;
+        ptable_w(useheap, ptable, i, j);
+    }
+
+    /* search */
+    i = 0; j = 1;
+    while (i < string.length) {
+        while (j >= 1 && string.ptr[i] != match.ptr[j-1]) {
+            ptable_r(j, useheap, ptable, j-1);
+        }
+        i++; j++;
+        if (j-1 == match.length) {
+            size_t start = i - match.length;
+            result.ptr = string.ptr + start;
+            result.length = string.length - start;
+            break;
+        }
+    }
+
+    /* if prefix table was allocated on the heap, free it */
+    if (ptable != s_prefix_table) {
+        free(ptable);
+    }
+    
+    return result;
+}
+
+#undef ptable_r
+#undef ptable_w
+
 sstr_t* sstrsplit(sstr_t s, sstr_t d, ssize_t *n) {
     return sstrsplit_a(ucx_default_allocator(), s, d, n);
 }
@@ -184,70 +258,85 @@
         *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;
+    
+    /* special cases: delimiter is at least as large as the string */
+    if (d.length >= s.length) {
+        /* exact match */
+        if (sstrcmp(s, d) == 0) {
+            *n = 0;
+            return NULL;
+        } else /* no match possible */ {
+            *n = 1;
+            sstr_t *result = (sstr_t*) almalloc(allocator, sizeof(sstr_t));
+            *result = sstrdup_a(allocator, s);
+            return result;
+        }
     }
-    sstr_t sv = sstrdup(s);
-    if (sv.length == 0) {
-        *n = -2;
-        return NULL;
-    }
+    
+    ssize_t nmax = *n;
+    size_t arrlen = 16;
+    sstr_t* result = (sstr_t*) almalloc(allocator, arrlen*sizeof(sstr_t));
 
-    for (size_t i = 0 ; i < s.length ; i++) {
-        if (sv.ptr[i] == d.ptr[0]) {
-            _Bool match = 1;
-            for (size_t j = 1 ; j < d.length ; j++) {
-                if (j+i < s.length) {
-                    match &= (sv.ptr[i+j] == d.ptr[j]);
+    if (result) {
+        sstr_t curpos = s;
+        ssize_t j = 1;
+        while (1) {
+            sstr_t match;
+            /* optimize for one byte delimiters */
+            if (d.length == 1) {
+                match = curpos;
+                for (size_t i = 0 ; i < curpos.length ; i++) {
+                    if (curpos.ptr[i] == *(d.ptr)) {
+                        match.ptr = curpos.ptr + i;
+                        break;
+                    }
+                    match.length--;
+                }
+            } else {
+                match = sstrstr(curpos, d);
+            }
+            if (match.length > 0) {
+                /* is this our last try? */
+                if (nmax == 0 || j < nmax) {
+                    /* copy the current string to the array */
+                    sstr_t item = sstrn(curpos.ptr, match.ptr - curpos.ptr);
+                    result[j-1] = sstrdup_a(allocator, item);
+                    size_t processed = item.length + d.length;
+                    curpos.ptr += processed;
+                    curpos.length -= processed;
+
+                    /* allocate memory for the next string */
+                    j++;
+                    if (j > arrlen) {
+                        arrlen *= 2;
+                        sstr_t* reallocated = (sstr_t*) alrealloc(
+                                allocator, result, arrlen*sizeof(sstr_t));
+                        if (reallocated) {
+                            result = reallocated;
+                        } else {
+                            for (ssize_t i = 0 ; i < j-1 ; i++) {
+                                alfree(allocator, result[i].ptr);
+                            }
+                            alfree(allocator, result);
+                            *n = -2;
+                            return NULL;
+                        }
+                    }
                 } else {
-                    match = 0;
+                    /* nmax reached, copy the _full_ remaining string */
+                    result[j-1] = sstrdup_a(allocator, curpos);
                     break;
                 }
-            }
-            if (match) {
-                (*n)++;
-                for (size_t j = 0 ; j < d.length ; j++) {
-                    sv.ptr[i+j] = 0;
-                }
-                i += d.length;
-            }
-        }
-        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;
+                /* no more matches, copy last string */
+                result[j-1] = sstrdup_a(allocator, curpos);
                 break;
             }
         }
+        *n = j;
     } else {
         *n = -2;
     }
-    
-    free(sv.ptr);
 
     return result;
 }
--- a/ucx/string.h	Wed Nov 22 12:59:13 2017 +0100
+++ b/ucx/string.h	Sun Jan 21 12:13:09 2018 +0100
@@ -1,7 +1,7 @@
 /*
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
  *
- * Copyright 2015 Olaf Wintermann. All rights reserved.
+ * Copyright 2016 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:
@@ -137,7 +137,7 @@
 sstr_t sstrcat(size_t count, sstr_t s1, sstr_t s2, ...);
 
 /**
- * Concatenates two or more strings using an UcxAllocator.
+ * Concatenates two or more strings using a UcxAllocator.
  * 
  * See sstrcat() for details.
  *
@@ -214,6 +214,23 @@
 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:
@@ -243,6 +260,9 @@
  * 
  * If the string ends with the delimiter and the maximum list size is not
  * exceeded, the last array item will be an empty string.
+ * In case the list size would be exceeded, the last array item will be the
+ * remaining string after the last split, <i>including</i> the terminating
+ * delimiter.
  * 
  * <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
@@ -260,7 +280,7 @@
 sstr_t* sstrsplit(sstr_t string, sstr_t delim, ssize_t *count);
 
 /**
- * Performing sstrsplit() using an UcxAllocator.
+ * Performing sstrsplit() using a UcxAllocator.
  * 
  * <i>Read the description of sstrsplit() for details.</i>
  * 
@@ -331,7 +351,7 @@
 sstr_t sstrdup(sstr_t string);
 
 /**
- * Creates a duplicate of the specified string using an UcxAllocator.
+ * Creates a duplicate of the specified string using a UcxAllocator.
  * 
  * The new sstr_t will contain a copy allocated by the allocators
  * ucx_allocator_malloc function. So it is implementation depended, whether the
@@ -341,7 +361,7 @@
  * 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 allocator a valid instance of a UcxAllocator
  * @param string the string to duplicate
  * @return a duplicate of the string
  * @see sstrdup()
--- a/ucx/test.c	Wed Nov 22 12:59:13 2017 +0100
+++ b/ucx/test.c	Sun Jan 21 12:13:09 2018 +0100
@@ -1,7 +1,7 @@
 /*
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
  *
- * Copyright 2015 Olaf Wintermann. All rights reserved.
+ * Copyright 2016 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:
@@ -86,6 +86,6 @@
         elem->test(suite, output);
     }
     fwrite("\nAll test completed.\n", 1, 21, output);
-    fprintf(output, "  Total:   %d\n  Success: %d\n  Failure: %d\n",
+    fprintf(output, "  Total:   %u\n  Success: %u\n  Failure: %u\n",
             suite->success+suite->failure, suite->success, suite->failure);
 }
--- a/ucx/test.h	Wed Nov 22 12:59:13 2017 +0100
+++ b/ucx/test.h	Sun Jan 21 12:13:09 2018 +0100
@@ -1,7 +1,7 @@
 /*
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
  *
- * Copyright 2015 Olaf Wintermann. All rights reserved.
+ * Copyright 2016 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:
@@ -160,7 +160,7 @@
 /**
  * Macro for a #UcxTest function header.
  * 
- * Use this macro to declare and/or define an #UcxTest function.
+ * Use this macro to declare and/or define a #UcxTest function.
  * 
  * @param name the name of the test function
  */
@@ -196,7 +196,7 @@
 /**
  * Macro for a test subroutine function header.
  * 
- * Use this to declare and/or define an subroutine that can be called by using
+ * Use this to declare and/or define a subroutine that can be called by using
  * UCX_TEST_CALL_SUBROUTINE().
  * 
  * @param name the name of the subroutine
--- a/ucx/ucx.c	Wed Nov 22 12:59:13 2017 +0100
+++ b/ucx/ucx.c	Sun Jan 21 12:13:09 2018 +0100
@@ -9,7 +9,7 @@
  * 
  * <h2>LICENCE</h2>
  * 
- * Copyright 2015 Olaf Wintermann. All rights reserved.
+ * Copyright 2016 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	Wed Nov 22 12:59:13 2017 +0100
+++ b/ucx/ucx.h	Sun Jan 21 12:13:09 2018 +0100
@@ -1,7 +1,7 @@
 /*
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
  *
- * Copyright 2015 Olaf Wintermann. All rights reserved.
+ * Copyright 2016 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:
@@ -40,12 +40,10 @@
 #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
+#define UCX_VERSION_MINOR   12
 
 #include <stdlib.h>
+#include <stdint.h>
 
 #ifdef _WIN32
 #if !(defined __ssize_t_defined || defined _SSIZE_T_)
@@ -89,7 +87,16 @@
  * 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*);
+typedef int(*cmp_func)(const void*,const void*,void*);
+
+/**
+ * Function pointer to a distance function.
+ * 
+ * The distance function shall take three arguments: the two values for which
+ * the distance shall be computed and optional additional data.
+ * The function shall then return the signed distance as integer value.
+ */
+typedef intmax_t(*distance_func)(const void*,const void*,void*);
 
 /**
  * Function pointer to a copy function.
@@ -102,7 +109,7 @@
  * passed to <code>free()</code> depends on the implementation of the
  * respective <code>copy_func</code>.
  */
-typedef void*(*copy_func)(void*,void*);
+typedef void*(*copy_func)(const void*,void*);
 
 /**
  * Function pointer to a write function.
--- a/ucx/utils.c	Wed Nov 22 12:59:13 2017 +0100
+++ b/ucx/utils.c	Sun Jan 21 12:13:09 2018 +0100
@@ -1,7 +1,7 @@
 /*
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
  *
- * Copyright 2015 Olaf Wintermann. All rights reserved.
+ * Copyright 2016 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:
@@ -33,22 +33,22 @@
 #include <errno.h>
 
 /* COPY FUCNTIONS */
-void* ucx_strcpy(void* s, void* data) {
-    char *str = (char*) s;
+void* ucx_strcpy(const void* s, void* data) {
+    const char *str = (const 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) {
+void* ucx_memcpy(const 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,
+size_t ucx_stream_bncopy(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;
@@ -87,17 +87,17 @@
 
 /* COMPARE FUNCTIONS */
 
-int ucx_strcmp(void *s1, void *s2, void *data) {
-    return strcmp((char*)s1, (char*)s2);
+int ucx_strcmp(const void *s1, const void *s2, void *data) {
+    return strcmp((const char*)s1, (const char*)s2);
 }
 
-int ucx_strncmp(void *s1, void *s2, void *n) {
-    return strncmp((char*)s1, (char*)s2, *((size_t*) n));
+int ucx_strncmp(const void *s1, const void *s2, void *n) {
+    return strncmp((const char*)s1, (const char*)s2, *((size_t*) n));
 }
 
-int ucx_intcmp(void *i1, void *i2, void *data) {
-   int a = *((int*) i1);
-   int b = *((int*) i2);
+int ucx_intcmp(const void *i1, const void *i2, void *data) {
+   int a = *((const int*) i1);
+   int b = *((const int*) i2);
    if (a == b) {
        return 0;
    } else {
@@ -105,9 +105,9 @@
    }
 }
 
-int ucx_floatcmp(void *f1, void *f2, void *epsilon) {
-   float a = *((float*) f1);
-   float b = *((float*) f2);
+int ucx_floatcmp(const void *f1, const void *f2, void *epsilon) {
+   float a = *((const float*) f1);
+   float b = *((const float*) f2);
    float e = !epsilon ? 1e-6f : *((float*)epsilon);
    if (fabsf(a - b) < e) {
        return 0;
@@ -116,9 +116,9 @@
    }
 }
 
-int ucx_doublecmp(void *d1, void *d2, void *epsilon) {
-   double a = *((float*) d1);
-   double b = *((float*) d2);
+int ucx_doublecmp(const void *d1, const void *d2, void *epsilon) {
+   double a = *((const double*) d1);
+   double b = *((const double*) d2);
    double e = !epsilon ? 1e-14 : *((double*)epsilon);
    if (fabs(a - b) < e) {
        return 0;
@@ -127,9 +127,9 @@
    }
 }
 
-int ucx_ptrcmp(void *ptr1, void *ptr2, void *data) {
-    intptr_t p1 = (intptr_t) ptr1;
-    intptr_t p2 = (intptr_t) ptr2;
+int ucx_ptrcmp(const void *ptr1, const void *ptr2, void *data) {
+    const intptr_t p1 = (const intptr_t) ptr1;
+    const intptr_t p2 = (const intptr_t) ptr2;
     if (p1 == p2) {
         return 0;
     } else {
@@ -137,7 +137,7 @@
     }
 }
 
-int ucx_memcmp(void *ptr1, void *ptr2, void *n) {
+int ucx_memcmp(const void *ptr1, const void *ptr2, void *n) {
     return memcmp(ptr1, ptr2, *((size_t*)n));
 }
 
--- a/ucx/utils.h	Wed Nov 22 12:59:13 2017 +0100
+++ b/ucx/utils.h	Sun Jan 21 12:13:09 2018 +0100
@@ -1,7 +1,7 @@
 /*
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
  *
- * Copyright 2015 Olaf Wintermann. All rights reserved.
+ * Copyright 2016 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:
@@ -45,17 +45,22 @@
 #include "ucx.h"
 #include "string.h"
 #include "allocator.h"
-#include <stdint.h>
+#include <inttypes.h>
 #include <string.h>
 #include <stdarg.h>
 
 /**
+ * Default buffer size for ucx_stream_copy() and ucx_stream_ncopy().
+ */
+#define UCX_STREAM_COPY_BUFSIZE 4096
+
+/**
  * 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);
+void *ucx_strcpy(const void *s, void *data);
 
 /**
  * Copies a memory area.
@@ -64,7 +69,7 @@
  * @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);
+void *ucx_memcpy(const void *m, void *n);
 
 
 /**
@@ -82,23 +87,26 @@
  * @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,
+size_t ucx_stream_bncopy(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.
+ * Shorthand for an unbounded ucx_stream_bncopy call using a default 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
+ * 
+ * @see #UCX_STREAM_COPY_BUFSIZE
  */
-#define ucx_stream_hcopy(src,dest,rfnc,wfnc) ucx_stream_copy(\
-        src, dest, (read_func)rfnc, (write_func)wfnc, NULL, 0x100, SIZE_MAX)
+#define ucx_stream_copy(src,dest,rfnc,wfnc) ucx_stream_bncopy(\
+        src, dest, (read_func)rfnc, (write_func)wfnc, \
+        NULL, UCX_STREAM_COPY_BUFSIZE, (size_t)-1)
 
 /**
- * Shorthand for ucx_stream_copy using the default copy buffer and a copy limit.
+ * Shorthand for ucx_stream_bncopy using a default copy buffer.
  * 
  * @param src the source stream
  * @param dest the destination stream
@@ -107,8 +115,27 @@
  * @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)
+#define ucx_stream_ncopy(src,dest,rfnc,wfnc, n) ucx_stream_bncopy(\
+        src, dest, (read_func)rfnc, (write_func)wfnc, \
+        NULL, UCX_STREAM_COPY_BUFSIZE, n)
+
+/**
+ * Shorthand for an unbounded ucx_stream_bncopy call using the specified buffer.
+ * 
+ * @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
+ * @return total number of bytes copied
+ */
+#define ucx_stream_bcopy(src,dest,rfnc,wfnc, buf, bufsize) ucx_stream_bncopy(\
+        src, dest, (read_func)rfnc, (write_func)wfnc, \
+        buf, bufsize, (size_t)-1)
 
 /**
  * Wraps the strcmp function.
@@ -117,7 +144,7 @@
  * @param data omitted
  * @return the result of strcmp(s1, s2)
  */
-int ucx_strcmp(void *s1, void *s2, void *data);
+int ucx_strcmp(const void *s1, const void *s2, void *data);
 
 /**
  * Wraps the strncmp function.
@@ -126,7 +153,7 @@
  * @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);
+int ucx_strncmp(const void *s1, const void *s2, void *n);
 
 /**
  * Compares two integers of type int.
@@ -136,7 +163,7 @@
  * @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);
+int ucx_intcmp(const void *i1, const void *i2, void *data);
 
 /**
  * Compares two real numbers of type float.
@@ -147,7 +174,7 @@
  * 1 if *f1 is greater than *f2
  */
 
-int ucx_floatcmp(void *f1, void *f2, void *data);
+int ucx_floatcmp(const void *f1, const void *f2, void *data);
 
 /**
  * Compares two real numbers of type double.
@@ -157,7 +184,7 @@
  * @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);
+int ucx_doublecmp(const void *d1, const void *d2, void *data);
 
 /**
  * Compares two pointers.
@@ -167,7 +194,7 @@
  * @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);
+int ucx_ptrcmp(const void *ptr1, const void *ptr2, void *data);
 
 /**
  * Compares two memory areas.
@@ -176,7 +203,7 @@
  * @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);
+int ucx_memcmp(const void *ptr1, const void *ptr2, void *n);
 
 /**
  * A <code>printf()</code> like function which writes the output to a stream by
@@ -219,10 +246,6 @@
  */
 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().
  * 
@@ -234,8 +257,12 @@
  */
 sstr_t ucx_vasprintf(UcxAllocator *allocator, const char *fmt, va_list ap);
 
+/** Shortcut for ucx_asprintf() with default allocator. */
+#define ucx_sprintf(...) \
+    ucx_asprintf(ucx_default_allocator(), __VA_ARGS__)
+
 /**
- * A <code>printf()</code> like function which writes the output to an
+ * A <code>printf()</code> like function which writes the output to a
  * UcxBuffer.
  * 
  * @param buffer the buffer the data is written to
--- a/ui/common/types.c	Wed Nov 22 12:59:13 2017 +0100
+++ b/ui/common/types.c	Sun Jan 21 12:13:09 2018 +0100
@@ -157,6 +157,11 @@
     list->data = ucx_list_prepend(list->data, data);
 }
 
+void ui_list_clear(UiList *list) {
+    ucx_list_free(list->data);
+    list->data = NULL;
+}
+
 void ui_list_addobsv(UiList *list, ui_callback f, void *data) {
     list->observers = ui_add_observer(list->observers, f, data);
 }
--- a/ui/gtk/model.c	Wed Nov 22 12:59:13 2017 +0100
+++ b/ui/gtk/model.c	Sun Jan 21 12:13:09 2018 +0100
@@ -96,7 +96,8 @@
 }
 
 static void list_model_class_init(GObjectClass *cl, gpointer data) {
-    //cl->finalize = ...; // TODO
+    cl->dispose = ui_list_model_dispose;
+    cl->finalize = ui_list_model_finalize;
     
 }
 
@@ -185,6 +186,14 @@
     return model;
 }
 
+void ui_list_model_dispose(GObject *obj) {
+    
+}
+
+void ui_list_model_finalize(GObject *obj) {
+    
+}
+
 
 GtkTreeModelFlags ui_list_model_get_flags(GtkTreeModel *tree_model) {
     return (GTK_TREE_MODEL_LIST_ONLY | GTK_TREE_MODEL_ITERS_PERSIST);
--- a/ui/gtk/model.h	Wed Nov 22 12:59:13 2017 +0100
+++ b/ui/gtk/model.h	Sun Jan 21 12:13:09 2018 +0100
@@ -62,6 +62,9 @@
  */
 UiListModel* ui_list_model_new(UiObject *obj, UiVar *var, UiModel *info);
 
+void ui_list_model_dispose(GObject *obj);
+void ui_list_model_finalize(GObject *obj);
+
 
 // interface functions
 
--- a/ui/gtk/tree.c	Wed Nov 22 12:59:13 2017 +0100
+++ b/ui/gtk/tree.c	Sun Jan 21 12:13:09 2018 +0100
@@ -368,10 +368,19 @@
     UiListView *view = list->obj;
     UiListModel *model = ui_list_model_new(view->obj, view->var, view->model);
     gtk_tree_view_set_model(GTK_TREE_VIEW(view->widget), GTK_TREE_MODEL(model));
+    g_object_unref(G_OBJECT(model));
     // TODO: free old model
 }
 
 void ui_listview_destroy(GtkWidget *w, UiListView *v) {
+    gtk_tree_view_set_model(GTK_TREE_VIEW(w), NULL);
+    ui_destroy_boundvar(v->obj->ctx, v->var);
+    // TODO: destroy model?
+    free(v);
+}
+
+void ui_combobox_destroy(GtkWidget *w, UiListView *v) {
+    gtk_combo_box_set_model(GTK_COMBO_BOX(w), NULL);
     ui_destroy_boundvar(v->obj->ctx, v->var);
     // TODO: destroy model?
     free(v);
@@ -498,7 +507,7 @@
     g_signal_connect(
                 combobox,
                 "destroy",
-                G_CALLBACK(ui_listview_destroy),
+                G_CALLBACK(ui_combobox_destroy),
                 uicbox);
     
     // bind var
--- a/ui/gtk/tree.h	Wed Nov 22 12:59:13 2017 +0100
+++ b/ui/gtk/tree.h	Sun Jan 21 12:13:09 2018 +0100
@@ -59,6 +59,7 @@
 GtkWidget* ui_get_tree_widget(UIWIDGET widget);
 
 void ui_listview_update(UiList *list, int i);
+void ui_combobox_destroy(GtkWidget *w, UiListView *v);
 void ui_listview_destroy(GtkWidget *w, UiListView *v);
 
 void ui_listview_activate_event(
--- a/ui/ui/toolkit.h	Wed Nov 22 12:59:13 2017 +0100
+++ b/ui/ui/toolkit.h	Sun Jan 21 12:13:09 2018 +0100
@@ -343,6 +343,7 @@
 int   ui_list_count(UiList *list);
 void  ui_list_append(UiList *list, void *data);
 void  ui_list_prepend(UiList *list, void *data);
+void ui_list_clear(UiList *list);
 void  ui_list_addobsv(UiList *list, ui_callback f, void *data);
 void  ui_list_notify(UiList *list);
 

mercurial