diff --git a/core/dap_common.h b/core/dap_common.h
index 7027890e299fa28bc77033cea870782d0b9f2aac..147da6af33af192c851099021066334a0c2b74fe 100644
--- a/core/dap_common.h
+++ b/core/dap_common.h
@@ -32,6 +32,7 @@
 #define DAP_NEW_SIZE(a,b)    ( (a*) malloc(b))
 #define DAP_NEW_Z(a) ( (a*) calloc(1,sizeof(a)))
 #define DAP_NEW_Z_SIZE(a,b) ( (a*) calloc(1,b))
+#define DAP_REALLOC(a,b) (realloc(a,b))
 
 #define DAP_DELETE(a)   free(a)
 #define DAP_DUP(a) (__typeof(a) ret = memcpy(ret,a,sizeof(*a)) )
diff --git a/core/dap_config.c b/core/dap_config.c
index 4a06883c3fdbe1341f71e957cc16330b155bc91f..7e788a86de986654749f9d2a4c1e1df254e12b47 100755
--- a/core/dap_config.c
+++ b/core/dap_config.c
@@ -2,8 +2,8 @@
 #include <string.h>
 #include <errno.h>
 #include <ctype.h>
+#include "dap_file_utils.h"
 #include "uthash.h"
-#include "file_utils.h"
 #include "dap_common.h"
 #include "dap_config.h"
 
@@ -52,12 +52,12 @@ int dap_config_init(const char * a_configs_path)
     if( a_configs_path ) {
 #ifdef _WIN32
         // Check up under Windows, in Linux is not required
-        if(!valid_ascii_symbols(a_configs_path)) {
+        if(!dap_valid_ascii_symbols(a_configs_path)) {
             log_it(L_ERROR, "Supported only ASCII symbols for directory path");
             return -1;
         }
 #endif
-        if(dir_test(a_configs_path) || !mkdir_with_parents(a_configs_path)) {
+        if(dap_dir_test(a_configs_path) || !dap_mkdir_with_parents(a_configs_path)) {
             strncpy(s_configs_path, a_configs_path,sizeof(s_configs_path));
             return 0;
         }
diff --git a/core/dap_file_utils.c b/core/dap_file_utils.c
new file mode 100644
index 0000000000000000000000000000000000000000..dac0786d40427bc6918e4ef67dc62a764170dc53
--- /dev/null
+++ b/core/dap_file_utils.c
@@ -0,0 +1,346 @@
+/*
+ * Authors:
+ * Aleksandr Lysikov <alexander.lysikov@demlabs.net>
+ * DeM Labs Inc.   https://demlabs.net
+ * Kelvin Project https://github.com/kelvinblockchain
+ * Copyright  (c) 2017-2018
+ * All rights reserved.
+
+ This file is part of DAP (Deus Applications Prototypes) the open source project
+
+ DAP (Deus Applicaions Prototypes) is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ DAP is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with any DAP based project.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <stdlib.h>
+#include <stdint.h>
+#include <errno.h>
+#if (OS_TARGET == OS_MACOS)
+    #include <stdio.h>
+#else
+    #include <malloc.h>
+#endif
+#include <string.h>
+#include <sys/stat.h>
+
+#ifdef _WIN32
+#include <windows.h>
+#include <io.h>
+#endif
+
+#include "dap_common.h"
+#include "dap_strfuncs.h"
+#include "dap_file_utils.h"
+
+/**
+ * Check the directory path for unsupported symbols
+ *
+ * @string
+ * @return true, if the directory path contains only ASCII symbols
+ */
+bool dap_valid_ascii_symbols(const char *a_string)
+{
+    if(!a_string)
+        return true;
+    for(size_t i = 0; i < strlen(a_string); i++) {
+        if((uint8_t) a_string[i] > 0x7f)
+            return false;
+    }
+    return true;
+}
+
+/**
+ * Check the directory for exists
+ *
+ * @dir_path directory pathname
+ * @return true, if the file is a directory
+ */
+bool dap_dir_test(const char * a_dir_path)
+{
+    if(!a_dir_path)
+        return false;
+#ifdef _WIN32
+    int attr = GetFileAttributesA(a_dir_path);
+    if(attr != -1 && (attr & FILE_ATTRIBUTE_DIRECTORY))
+        return true;
+#else
+    struct stat st;
+    if (!stat(a_dir_path, &st)) {
+        if (S_ISDIR(st.st_mode))
+        return true;
+    }
+#endif
+    return false;
+}
+
+/**
+ * Create a new directory with intermediate sub-directories
+ *
+ * @dir_path new directory pathname
+ * @return 0, if the directory was created or already exist, else -1
+ */
+int dap_mkdir_with_parents(const char *a_dir_path)
+{
+
+    char *path, *p;
+    // validation of a pointer
+    if(a_dir_path == NULL || a_dir_path[0] == '\0') {
+        errno = EINVAL;
+        return -1;
+    }
+    path = strdup(a_dir_path);
+    // skip the root component if it is present, i.e. the "/" in Unix or "C:\" in Windows
+#ifdef _WIN32
+    if(((path[0] >= 'a' && path[0] <= 'z') || (path[0] >= 'A' && path[0] <= 'Z'))
+            && (path[1] == ':') && DAP_IS_DIR_SEPARATOR(path[2])) {
+        p = path + 3;
+    }
+#else
+        if (DAP_IS_DIR_SEPARATOR(path[0])) {
+            p = path + 1;
+        }
+#endif
+        else
+        p = path;
+
+    do {
+        while(*p && !DAP_IS_DIR_SEPARATOR(*p))
+            p++;
+
+        if(!*p)
+            p = NULL;
+        else
+            *p = '\0';
+
+        if(!dap_dir_test(path)) {
+#ifdef _WIN32
+            int result = mkdir(path);
+#else
+            int result = mkdir(path, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
+#endif
+            if(result == -1) {
+                free(path);
+                errno = ENOTDIR;
+                return -1;
+            }
+        }
+        if(p) {
+            *p++ = DAP_DIR_SEPARATOR;
+            while(*p && DAP_IS_DIR_SEPARATOR(*p))
+                p++;
+        }
+    } while(p);
+
+    free(path);
+    return 0;
+}
+
+/**
+ * dap_path_get_basename:
+ * @a_file_name: the name of the file
+ *
+ * Gets the last component of the filename.
+ *
+ * If @a_file_name ends with a directory separator it gets the component
+ * before the last slash. If @a_file_name consists only of directory
+ * separators (and on Windows, possibly a drive letter), a single
+ * separator is returned. If @a_file_name is empty, it gets ".".
+ *
+ * Returns: a newly allocated string containing the last
+ *    component of the filename
+ */
+char* dap_path_get_basename(const char *a_file_name)
+{
+    ssize_t l_base;
+    ssize_t l_last_nonslash;
+    const char *l_retval;
+
+    dap_return_val_if_fail(a_file_name != NULL, NULL);
+
+    if(a_file_name[0] == '\0')
+        return dap_strdup(".");
+
+    l_last_nonslash = strlen(a_file_name) - 1;
+
+    while(l_last_nonslash >= 0 && DAP_IS_DIR_SEPARATOR(a_file_name[l_last_nonslash]))
+        l_last_nonslash--;
+
+    if(l_last_nonslash == -1)
+        // string only containing slashes
+        return dap_strdup(DAP_DIR_SEPARATOR_S);
+
+#ifdef _WIN32
+    if (l_last_nonslash == 1 &&
+            dap_ascii_isalpha (a_file_name[0]) &&
+            a_file_name[1] == ':')
+    // string only containing slashes and a drive
+    return dap_strdup (DAP_DIR_SEPARATOR_S);
+#endif
+    l_base = l_last_nonslash;
+
+    while(l_base >= 0 && !DAP_IS_DIR_SEPARATOR(a_file_name[l_base]))
+        l_base--;
+
+#ifdef _WIN32
+    if (l_base == -1 &&
+            dap_ascii_isalpha (a_file_name[0]) &&
+            a_file_name[1] == ':')
+    l_base = 1;
+#endif
+
+    //size_t l_len = l_last_nonslash - l_base;
+    l_retval = a_file_name + l_base + 1;
+
+    return dap_strdup(l_retval);
+}
+
+/**
+ * dap_path_is_absolute:
+ * @a_file_name: a file name
+ *
+ * Returns true if the given @a_file_name is an absolute file name.
+ * Note that this is a somewhat vague concept on Windows.
+ *
+ * On POSIX systems, an absolute file name is well-defined. It always
+ * starts from the single root directory. For example "/usr/local".
+ *
+ * On Windows, the concepts of current drive and drive-specific
+ * current directory introduce vagueness. This function interprets as
+ * an absolute file name one that either begins with a directory
+ * separator such as "\Users\tml" or begins with the root on a drive,
+ * for example "C:\Windows". The first case also includes UNC paths
+ * such as "\\myserver\docs\foo". In all cases, either slashes or
+ * backslashes are accepted.
+ *
+ * Returns: true if @a_file_name is absolute
+ */
+bool dap_path_is_absolute(const char *a_file_name)
+{
+    dap_return_val_if_fail(a_file_name != NULL, false);
+
+    if(DAP_IS_DIR_SEPARATOR(a_file_name[0]))
+        return true;
+
+#ifdef _WIN32
+    /* Recognize drive letter on native Windows */
+    if (dap_ascii_isalpha (a_file_name[0]) &&
+            a_file_name[1] == ':' && DAP_IS_DIR_SEPARATOR (a_file_name[2]))
+    return true;
+#endif
+
+    return false;
+}
+
+/**
+ * dap_path_get_dirname:
+ * @a_file_name: the name of the file
+ *
+ * Gets the directory components of a file name.
+ *
+ * If the file name has no directory components "." is returned.
+ * The returned string should be freed when no longer needed.
+ *
+ * Returns: the directory components of the file
+ */
+char* dap_path_get_dirname(const char *a_file_name)
+{
+    char *l_base;
+    size_t l_len;
+
+    dap_return_val_if_fail(a_file_name != NULL, NULL);
+
+    l_base = strrchr(a_file_name, DAP_DIR_SEPARATOR);
+
+#ifdef _WIN32
+    {
+        char *l_q;
+        l_q = strrchr (a_file_name, '/');
+        if (l_base == NULL || (l_q != NULL && l_q > l_base))
+        l_base = l_q;
+    }
+#endif
+
+    if(!l_base)
+    {
+#ifdef _WIN32
+        if (dap_ascii_isalpha (a_file_name[0]) && a_file_name[1] == ':')
+        {
+            char l_drive_colon_dot[4];
+
+            l_drive_colon_dot[0] = a_file_name[0];
+            l_drive_colon_dot[1] = ':';
+            l_drive_colon_dot[2] = '.';
+            l_drive_colon_dot[3] = '\0';
+
+            return dap_strdup (l_drive_colon_dot);
+        }
+#endif
+        return dap_strdup(".");
+    }
+
+    while(l_base > a_file_name && DAP_IS_DIR_SEPARATOR(*l_base))
+        l_base--;
+
+#ifdef _WIN32
+    /* base points to the char before the last slash.
+     *
+     * In case file_name is the root of a drive (X:\) or a child of the
+     * root of a drive (X:\foo), include the slash.
+     *
+     * In case file_name is the root share of an UNC path
+     * (\\server\share), add a slash, returning \\server\share\ .
+     *
+     * In case file_name is a direct child of a share in an UNC path
+     * (\\server\share\foo), include the slash after the share name,
+     * returning \\server\share\ .
+     */
+    if (l_base == a_file_name + 1 &&
+            dap_ascii_isalpha (a_file_name[0]) &&
+            a_file_name[1] == ':')
+    l_base++;
+    else if (DAP_IS_DIR_SEPARATOR (a_file_name[0]) &&
+            DAP_IS_DIR_SEPARATOR (a_file_name[1]) &&
+            a_file_name[2] &&
+            !DAP_IS_DIR_SEPARATOR (a_file_name[2]) &&
+            l_base >= a_file_name + 2)
+    {
+        const char *l_p = a_file_name + 2;
+        while (*l_p && !DAP_IS_DIR_SEPARATOR (*l_p))
+        l_p++;
+        if (l_p == l_base + 1)
+        {
+            l_len = (uint32_t) strlen (a_file_name) + 1;
+            l_base = DAP_NEW_SIZE (char, l_len + 1);
+            strcpy (l_base, a_file_name);
+            l_base[l_len-1] = DAP_DIR_SEPARATOR;
+            l_base[l_len] = 0;
+            return l_base;
+        }
+        if (DAP_IS_DIR_SEPARATOR (*l_p))
+        {
+            l_p++;
+            while (*l_p && !DAP_IS_DIR_SEPARATOR (*l_p))
+            l_p++;
+            if (l_p == l_base + 1)
+            l_base++;
+        }
+    }
+#endif
+
+    l_len = (uint32_t) 1 + l_base - a_file_name;
+    l_base = DAP_NEW_SIZE(char, l_len + 1);
+    memmove(l_base, a_file_name, l_len);
+    l_base[l_len] = 0;
+
+    return l_base;
+}
diff --git a/core/file_utils.h b/core/dap_file_utils.h
similarity index 59%
rename from core/file_utils.h
rename to core/dap_file_utils.h
index d05ef8c64610db7892cda76e1b0cb06fc804782b..4576faab6bbfe5ada5c17ed8948f256324cce237 100644
--- a/core/file_utils.h
+++ b/core/dap_file_utils.h
@@ -3,7 +3,7 @@
  * Aleksandr Lysikov <alexander.lysikov@demlabs.net>
  * DeM Labs Inc.   https://demlabs.net
  * Kelvin Project https://github.com/kelvinblockchain
- * Copyright  (c) 2017-2018
+ * Copyright  (c) 2017-2019
  * All rights reserved.
 
  This file is part of DAP (Deus Applications Prototypes) the open source project
@@ -25,7 +25,29 @@
 #include <stdbool.h>
 
 #ifndef _FILE_UTILS_H_
-#define _FILE_UTILS_H_
+#define _DAP_FILE_UTILS_H_
+
+#ifdef _WIN32
+
+/* On Win32, the canonical directory separator is the backslash, and
+ * the search path separator is the semicolon. Note that also the
+ * (forward) slash works as directory separator.
+ */
+#define DAP_DIR_SEPARATOR '\\'
+#define DAP_DIR_SEPARATOR_S "\\"
+#define DAP_IS_DIR_SEPARATOR(c) ((c) == DAP_DIR_SEPARATOR || (c) == '/')
+#define DAP_SEARCHPATH_SEPARATOR ';'
+#define DAP_SEARCHPATH_SEPARATOR_S ";"
+
+#else
+
+#define DAP_DIR_SEPARATOR '/'
+#define DAP_DIR_SEPARATOR_S "/"
+#define DAP_IS_DIR_SEPARATOR(c) ((c) == DAP_DIR_SEPARATOR)
+#define DAP_SEARCHPATH_SEPARATOR ':'
+#define DAP_SEARCHPATH_SEPARATOR_S ":"
+
+#endif
 
 /**
  * Check the directory path for unsupported symbols
@@ -33,7 +55,7 @@
  * @dir_path directory pathname
  * @return true, if the directory path contains only ASCII symbols
  */
-bool valid_ascii_symbols(const char *dir_path);
+bool dap_valid_ascii_symbols(const char *a_dir_path);
 
 /**
  * Check the directory for exists
@@ -41,7 +63,7 @@ bool valid_ascii_symbols(const char *dir_path);
  * @dir_path directory pathname
  * @return true, if the file is a directory
  */
-bool dir_test(const char * dir_path);
+bool dap_dir_test(const char * a_dir_path);
 
 /**
  * Create a new directory with intermediate sub-directories
@@ -49,6 +71,11 @@ bool dir_test(const char * dir_path);
  * @dir_path new directory pathname
  * @return 0, if the directory was created or already exist, else -1
  */
-int mkdir_with_parents(const char *dir_path);
+int dap_mkdir_with_parents(const char *a_dir_path);
+
+
+char* dap_path_get_basename(const char *a_file_name);
+bool  dap_path_is_absolute(const char *a_file_name);
+char* dap_path_get_dirname(const char *a_file_name);
 
 #endif // _FILE_UTILS_H_
diff --git a/core/dap_list.c b/core/dap_list.c
new file mode 100755
index 0000000000000000000000000000000000000000..6d28340db8969022342905c32d224109c775a3c8
--- /dev/null
+++ b/core/dap_list.c
@@ -0,0 +1,1005 @@
+/*
+ * Doubly-Linked Lists — linked lists that can be iterated over in both directions
+ */
+
+#include <stddef.h>
+
+#include "dap_common.h"
+#include "dap_strfuncs.h"
+#include "dap_list.h"
+
+
+#define LOG_TAG "dap_list"
+
+/**
+ * dap_list_alloc:
+ * Returns: a pointer to the newly-allocated DapList element
+ **/
+dap_list_t *dap_list_alloc(void)
+{
+    dap_list_t *list = DAP_NEW(dap_list_t);
+    return list;
+}
+
+dap_list_t *dap_list_alloc0(void)
+{
+    dap_list_t *list = DAP_NEW_Z(dap_list_t);
+    return list;
+}
+
+/**
+ * dap_list_free:
+ * Frees all of the memory used by a DapList.
+ * The freed elements are returned to the slice allocator.
+ * If list elements contain dynamically-allocated memory, you should
+ * either use dap_list_free_full() or free them manually first.
+ */
+void dap_list_free(dap_list_t *list)
+{
+    while(list)
+    {
+        dap_list_t *next = list->next;
+        DAP_DELETE(list);
+        list = next;
+    }
+}
+
+/**
+ * dap_list_free_1:
+ *
+ * Frees one DapList element.
+ * It is usually used after dap_list_remove_link().
+ */
+void dap_list_free1(dap_list_t *list)
+{
+    DAP_DELETE(list);
+}
+
+/**
+ * dap_list_free_full:
+ * @list: a pointer to a DapList
+ * @free_func: the function to be called to free each element's data
+ *
+ * Convenience method, which frees all the memory used by a DapList,
+ * and calls @free_func on every element's data.
+ */
+void dap_list_free_full(dap_list_t *list, DapDestroyNotify free_func)
+{
+    dap_list_foreach(list, (DapFunc) free_func, NULL);
+    dap_list_free(list);
+}
+
+/**
+ * dap_list_append:
+ * @list: a pointer to a DapList
+ * @data: the data for the new element
+ *
+ * Adds a new element on to the end of the list.
+ *
+ * Note that the return value is the new start of the list,
+ * if @list was empty; make sure you store the new value.
+ *
+ * dap_list_append() has to traverse the entire list to find the end,
+ * which is inefficient when adding multiple elements. A common idiom
+ * to avoid the inefficiency is to use dap_list_prepend() and reverse
+ * the list with dap_list_reverse() when all elements have been added.
+ *
+ * |[<!-- language="C" -->
+ * // Notice that these are initialized to the empty list.
+ * DapList *string_list = NULL, *number_list = NULL;
+ *
+ * // This is a list of strings.
+ * string_list = dap_list_append (string_list, "first");
+ * string_list = dap_list_append (string_list, "second");
+ * 
+ * // This is a list of integers.
+ * number_list = dap_list_append (number_list, INT_TO_POINTER (27));
+ * number_list = dap_list_append (number_list, INT_TO_POINTER (14));
+ * ]|
+ *
+ * Returns: either @list or the new start of the DapList if @list was %NULL
+ */
+dap_list_t * dap_list_append(dap_list_t *list, void* data)
+{
+    dap_list_t *new_list;
+    dap_list_t *last;
+
+    new_list = dap_list_alloc();
+    new_list->data = data;
+    new_list->next = NULL;
+
+    if(list)
+    {
+        last = dap_list_last(list);
+        /* assert (last != NULL); */
+        last->next = new_list;
+        new_list->prev = last;
+
+        return list;
+    }
+    else
+    {
+        new_list->prev = NULL;
+        return new_list;
+    }
+}
+
+/**
+ * dap_list_prepend:
+ * @list: a pointer to a DapList, this must point to the top of the list
+ * @data: the data for the new element
+ *
+ * Prepends a new element on to the start of the list.
+ *
+ * Note that the return value is the new start of the list,
+ * which will have changed, so make sure you store the new value. 
+ *
+ * |[<!-- language="C" -->
+ * // Notice that it is initialized to the empty list.
+ * DapList *list = NULL;
+ *
+ * list = dap_list_prepend (list, "last");
+ * list = dap_list_prepend (list, "first");
+ * ]|
+ *
+ * Do not use this function to prepend a new element to a different
+ * element than the start of the list. Use dap_list_insert_before() instead.
+ *
+ * Returns: a pointer to the newly prepended element, which is the new 
+ *     start of the DapList
+ */
+dap_list_t *dap_list_prepend(dap_list_t *list, void* data)
+{
+    dap_list_t *new_list;
+
+    new_list = dap_list_alloc();
+    new_list->data = data;
+    new_list->next = list;
+
+    if(list)
+    {
+        new_list->prev = list->prev;
+        if(list->prev)
+            list->prev->next = new_list;
+        list->prev = new_list;
+    }
+    else
+        new_list->prev = NULL;
+
+    return new_list;
+}
+
+/**
+ * dap_list_insert:
+ * @list: a pointer to a DapList, this must point to the top of the list
+ * @data: the data for the new element
+ * @position: the position to insert the element. If this is 
+ *     negative, or is larger than the number of elements in the 
+ *     list, the new element is added on to the end of the list.
+ * 
+ * Inserts a new element into the list at the given position.
+ *
+ * Returns: the (possibly changed) start of the DapList
+ */
+dap_list_t *dap_list_insert(dap_list_t *list, void* data, int position)
+{
+    dap_list_t *new_list;
+    dap_list_t *tmp_list;
+
+    if(position < 0)
+        return dap_list_append(list, data);
+    else if(position == 0)
+        return dap_list_prepend(list, data);
+
+    tmp_list = dap_list_nth(list, position);
+    if(!tmp_list)
+        return dap_list_append(list, data);
+
+    new_list = dap_list_alloc();
+    new_list->data = data;
+    new_list->prev = tmp_list->prev;
+    tmp_list->prev->next = new_list;
+    new_list->next = tmp_list;
+    tmp_list->prev = new_list;
+
+    return list;
+}
+
+/**
+ * dap_list_insert_before:
+ * @list: a pointer to a DapList, this must point to the top of the list
+ * @sibling: the list element before which the new element 
+ *     is inserted or %NULL to insert at the end of the list
+ * @data: the data for the new element
+ *
+ * Inserts a new element into the list before the given position.
+ *
+ * Returns: the (possibly changed) start of the DapList
+ */
+dap_list_t *dap_list_insert_before(dap_list_t *list, dap_list_t *sibling, void* data)
+{
+    if(!list)
+    {
+        list = dap_list_alloc();
+        list->data = data;
+        dap_return_val_if_fail(sibling == NULL, list);
+        return list;
+    }
+    else if(sibling)
+    {
+        dap_list_t *node;
+
+        node = dap_list_alloc();
+        node->data = data;
+        node->prev = sibling->prev;
+        node->next = sibling;
+        sibling->prev = node;
+        if(node->prev)
+        {
+            node->prev->next = node;
+            return list;
+        }
+        else
+        {
+            dap_return_val_if_fail(sibling == list, node);
+            return node;
+        }
+    }
+    else
+    {
+        dap_list_t *last;
+
+        last = list;
+        while(last->next)
+            last = last->next;
+
+        last->next = dap_list_alloc();
+        last->next->data = data;
+        last->next->prev = last;
+        last->next->next = NULL;
+
+        return list;
+    }
+}
+
+/**
+ * dap_list_concat:
+ * @list1: a DapList, this must point to the top of the list
+ * @list2: the DapList to add to the end of the first DapList,
+ *     this must point  to the top of the list
+ *
+ * Adds the second DapList onto the end of the first DapList.
+ * Note that the elements of the second DapList are not copied.
+ * They are used directly.
+ *
+ * This function is for example used to move an element in the list.
+ * The following example moves an element to the top of the list:
+ * |[<!-- language="C" -->
+ * list = dap_list_remove_link (list, llink);
+ * list = dap_list_concat (llink, list);
+ * ]|
+ *
+ * Returns: the start of the new DapList, which equals @list1 if not %NULL
+ */
+dap_list_t *dap_list_concat(dap_list_t *list1, dap_list_t *list2)
+{
+    dap_list_t *tmp_list;
+
+    if(list2)
+    {
+        tmp_list = dap_list_last(list1);
+        if(tmp_list)
+            tmp_list->next = list2;
+        else
+            list1 = list2;
+        list2->prev = tmp_list;
+    }
+
+    return list1;
+}
+
+static inline dap_list_t * _dap_list_remove_link(dap_list_t *list, dap_list_t *link)
+{
+    if(link == NULL)
+        return list;
+
+    if(link->prev)
+    {
+        if(link->prev->next == link)
+            link->prev->next = link->next;
+        else
+            log_it(L_ERROR, "corrupted double-linked list detected");
+    }
+    if(link->next)
+    {
+        if(link->next->prev == link)
+            link->next->prev = link->prev;
+        else
+            log_it(L_ERROR, "corrupted double-linked list detected");
+    }
+
+    if(link == list)
+        list = list->next;
+
+    link->next = NULL;
+    link->prev = NULL;
+
+    return list;
+}
+
+/**
+ * dap_list_remove:
+ * @list: a DapList, this must point to the top of the list
+ * @data: the data of the element to remove
+ *
+ * Removes an element from a DapList.
+ * If two elements contain the same data, only the first is removed.
+ * If none of the elements contain the data, the DapList is unchanged.
+ *
+ * Returns: the (possibly changed) start of the DapList
+ */
+dap_list_t *dap_list_remove(dap_list_t *list, const void * data)
+{
+    dap_list_t *tmp;
+
+    tmp = list;
+    while(tmp)
+    {
+        if(tmp->data != data)
+            tmp = tmp->next;
+        else
+        {
+            list = _dap_list_remove_link(list, tmp);
+            dap_list_free1(tmp);
+
+            break;
+        }
+    }
+    return list;
+}
+
+/**
+ * dap_list_remove_all:
+ * @list: a DapList, this must point to the top of the list
+ * @data: data to remove
+ *
+ * Removes all list nodes with data equal to @data.
+ * Returns the new head of the list. Contrast with
+ * dap_list_remove() which removes only the first node
+ * matching the given data.
+ *
+ * Returns: the (possibly changed) start of the DapList
+ */
+dap_list_t *dap_list_remove_all(dap_list_t *list, const void * data)
+{
+    dap_list_t *tmp = list;
+
+    while(tmp)
+    {
+        if(tmp->data != data)
+            tmp = tmp->next;
+        else
+        {
+            dap_list_t *next = tmp->next;
+
+            if(tmp->prev)
+                tmp->prev->next = next;
+            else
+                list = next;
+            if(next)
+                next->prev = tmp->prev;
+
+            dap_list_free1(tmp);
+            tmp = next;
+        }
+    }
+    return list;
+}
+
+/**
+ * dap_list_remove_link:
+ * @list: a DapList, this must point to the top of the list
+ * @llink: an element in the DapList
+ *
+ * Removes an element from a DapList, without freeing the element.
+ * The removed element's prev and next links are set to %NULL, so 
+ * that it becomes a self-contained list with one element.
+ *
+ * This function is for example used to move an element in the list
+ * (see the example for dap_list_concat()) or to remove an element in
+ * the list before freeing its data:
+ * |[<!-- language="C" --> 
+ * list = dap_list_remove_link (list, llink);
+ * free_some_data_that_may_access_the_list_again (llink->data);
+ * dap_list_free (llink);
+ * ]|
+ *
+ * Returns: the (possibly changed) start of the DapList
+ */
+dap_list_t *dap_list_remove_link(dap_list_t *list, dap_list_t *llink)
+{
+    return _dap_list_remove_link(list, llink);
+}
+
+/**
+ * dap_list_delete_link:
+ * @list: a DapList, this must point to the top of the list
+ * @link_: node to delete from @list
+ *
+ * Removes the node link_ from the list and frees it. 
+ * Compare this to dap_list_remove_link() which removes the node
+ * without freeing it.
+ *
+ * Returns: the (possibly changed) start of the DapList
+ */
+dap_list_t *dap_list_delete_link(dap_list_t *list, dap_list_t *link_)
+{
+    list = _dap_list_remove_link(list, link_);
+    dap_list_free1(link_);
+
+    return list;
+}
+
+/**
+ * dap_list_copy:
+ * @list: a DapList, this must point to the top of the list
+ *
+ * Copies a DapList.
+ *
+ * Note that this is a "shallow" copy. If the list elements 
+ * consist of pointers to data, the pointers are copied but 
+ * the actual data is not. See dap_list_copy_deep() if you need
+ * to copy the data as well.
+ *
+ * Returns: the start of the new list that holds the same data as @list
+ */
+dap_list_t *dap_list_copy(dap_list_t *list)
+{
+    return dap_list_copy_deep(list, NULL, NULL);
+}
+
+/**
+ * dap_list_copy_deep:
+ * @list: a DapList, this must point to the top of the list
+ * @func: a copy function used to copy every element in the list
+ * @user_data: user data passed to the copy function @func, or %NULL
+ *
+ * Makes a full (deep) copy of a DapList.
+ *
+ * In contrast with dap_list_copy(), this function uses @func to make
+ * a copy of each list element, in addition to copying the list
+ * container itself.
+ *
+ * @func, as a #DapCopyFunc, takes two arguments, the data to be copied
+ * and a @user_data pointer. It's safe to pass %NULL as user_data,
+ * if the copy function takes only one argument.
+ *
+ * For instance,
+ * |[<!-- language="C" -->   
+ * another_list = dap_list_copy_deep (list, (DapCopyFunc) dap_object_ref, NULL);
+ * ]|
+ *
+ * And, to entirely free the new list, you could do:
+ * |[<!-- language="C" --> 
+ * dap_list_free_full (another_list, dap_object_unref);
+ * ]|
+ *
+ * Returns: the start of the new list that holds a full copy of @list, 
+ *     use dap_list_free_full() to free it
+ */
+dap_list_t *dap_list_copy_deep(dap_list_t *list, DapCopyFunc func, void* user_data)
+{
+    dap_list_t *new_list = NULL;
+
+    if(list)
+    {
+        dap_list_t *last;
+
+        new_list = dap_list_alloc();
+        if(func)
+            new_list->data = func(list->data, user_data);
+        else
+            new_list->data = list->data;
+        new_list->prev = NULL;
+        last = new_list;
+        list = list->next;
+        while(list)
+        {
+            last->next = dap_list_alloc();
+            last->next->prev = last;
+            last = last->next;
+            if(func)
+                last->data = func(list->data, user_data);
+            else
+                last->data = list->data;
+            list = list->next;
+        }
+        last->next = NULL;
+    }
+
+    return new_list;
+}
+
+/**
+ * dap_list_reverse:
+ * @list: a DapList, this must point to the top of the list
+ *
+ * Reverses a DapList.
+ * It simply switches the next and prev pointers of each element.
+ *
+ * Returns: the start of the reversed DapList
+ */
+dap_list_t * dap_list_reverse(dap_list_t *list)
+{
+    dap_list_t *last;
+
+    last = NULL;
+    while(list)
+    {
+        last = list;
+        list = last->next;
+        last->next = last->prev;
+        last->prev = list;
+    }
+
+    return last;
+}
+
+/**
+ * dap_list_nth:
+ * @list: a DapList, this must point to the top of the list
+ * @n: the position of the element, counting from 0
+ *
+ * Gets the element at the given position in a DapList.
+ *
+ * Returns: the element, or %NULL if the position is off 
+ *     the end of the DapList
+ */
+dap_list_t *dap_list_nth(dap_list_t *list, unsigned int n)
+{
+    while((n-- > 0) && list)
+        list = list->next;
+
+    return list;
+}
+
+/**
+ * dap_list_nth_prev:
+ * @list: a DapList
+ * @n: the position of the element, counting from 0
+ *
+ * Gets the element @n places before @list.
+ *
+ * Returns: the element, or %NULL if the position is 
+ *     off the end of the DapList
+ */
+dap_list_t *dap_list_nth_prev(dap_list_t *list, unsigned int n)
+{
+    while((n-- > 0) && list)
+        list = list->prev;
+
+    return list;
+}
+
+/**
+ * dap_list_nth_data:
+ * @list: a DapList, this must point to the top of the list
+ * @n: the position of the element
+ *
+ * Gets the data of the element at the given position.
+ *
+ * Returns: the element's data, or %NULL if the position 
+ *     is off the end of the DapList
+ */
+void* dap_list_nth_data(dap_list_t *list, unsigned int n)
+{
+    while((n-- > 0) && list)
+        list = list->next;
+
+    return list ? list->data : NULL ;
+}
+
+/**
+ * dap_list_find:
+ * @list: a DapList, this must point to the top of the list
+ * @data: the element data to find
+ *
+ * Finds the element in a DapList which contains the given data.
+ *
+ * Returns: the found DapList element, or %NULL if it is not found
+ */
+dap_list_t *dap_list_find(dap_list_t *list, const void * data)
+{
+    while(list)
+    {
+        if(list->data == data)
+            break;
+        list = list->next;
+    }
+
+    return list;
+}
+
+/**
+ * dap_list_find_custom:
+ * @list: a DapList, this must point to the top of the list
+ * @data: user data passed to the function
+ * @func: the function to call for each element. 
+ *     It should return 0 when the desired element is found
+ *
+ * Finds an element in a DapList, using a supplied function to
+ * find the desired element. It iterates over the list, calling 
+ * the given function which should return 0 when the desired 
+ * element is found. The function takes two const pointer arguments,
+ * the DapList element's data as the first argument and the
+ * given user data.
+ *
+ * Returns: the found DapList element, or %NULL if it is not found
+ */
+dap_list_t *dap_list_find_custom(dap_list_t *list, const void * data, DapCompareFunc func)
+{
+    dap_return_val_if_fail(func != NULL, list);
+
+    while(list)
+    {
+        if(!func(list->data, data))
+            return list;
+        list = list->next;
+    }
+
+    return NULL ;
+}
+
+/**
+ * dap_list_position:
+ * @list: a DapList, this must point to the top of the list
+ * @llink: an element in the DapList
+ *
+ * Gets the position of the given element 
+ * in the DapList (starting from 0).
+ *
+ * Returns: the position of the element in the DapList,
+ *     or -1 if the element is not found
+ */
+int dap_list_position(dap_list_t *list, dap_list_t *llink)
+{
+    int i;
+
+    i = 0;
+    while(list)
+    {
+        if(list == llink)
+            return i;
+        i++;
+        list = list->next;
+    }
+
+    return -1;
+}
+
+/**
+ * dap_list_index:
+ * @list: a DapList, this must point to the top of the list
+ * @data: the data to find
+ *
+ * Gets the position of the element containing 
+ * the given data (starting from 0).
+ *
+ * Returns: the index of the element containing the data, 
+ *     or -1 if the data is not found
+ */
+int dap_list_index(dap_list_t *list, const void * data)
+{
+    int i;
+
+    i = 0;
+    while(list)
+    {
+        if(list->data == data)
+            return i;
+        i++;
+        list = list->next;
+    }
+
+    return -1;
+}
+
+/**
+ * dap_list_last:
+ * @list: any DapList element
+ *
+ * Gets the last element in a DapList.
+ *
+ * Returns: the last element in the DapList,
+ *     or %NULL if the DapList has no elements
+ */
+dap_list_t * dap_list_last(dap_list_t *list)
+{
+    if(list)
+    {
+        while(list->next)
+            list = list->next;
+    }
+
+    return list;
+}
+
+/**
+ * dap_list_first:
+ * @list: any DapList element
+ *
+ * Gets the first element in a DapList.
+ *
+ * Returns: the first element in the DapList,
+ *     or %NULL if the DapList has no elements
+ */
+dap_list_t *dap_list_first(dap_list_t *list)
+{
+    if(list)
+    {
+        while(list->prev)
+            list = list->prev;
+    }
+
+    return list;
+}
+
+/**
+ * dap_list_length:
+ * @list: a DapList, this must point to the top of the list
+ *
+ * Gets the number of elements in a DapList.
+ *
+ * This function iterates over the whole list to count its elements.
+ *
+ * Returns: the number of elements in the DapList
+ */
+unsigned int dap_list_length(dap_list_t *list)
+{
+    unsigned int length;
+
+    length = 0;
+    while(list)
+    {
+        length++;
+        list = list->next;
+    }
+
+    return length;
+}
+
+/**
+ * dap_list_foreach:
+ * @list: a DapList, this must point to the top of the list
+ * @func: the function to call with each element's data
+ * @user_data: user data to pass to the function
+ *
+ * Calls a function for each element of a DapList.
+ */
+void dap_list_foreach(dap_list_t *list, DapFunc func, void* user_data)
+{
+    while(list)
+    {
+        dap_list_t *next = list->next;
+        (*func)(list->data, user_data);
+        list = next;
+    }
+}
+
+static dap_list_t* dap_list_insert_sorted_real(dap_list_t *list, void* data, DapFunc func, void* user_data)
+{
+    dap_list_t *tmp_list = list;
+    dap_list_t *new_list;
+    int cmp;
+
+    dap_return_val_if_fail(func != NULL, list);
+
+    if(!list)
+    {
+        new_list = dap_list_alloc0();
+        new_list->data = data;
+        return new_list;
+    }
+
+    cmp = ((DapCompareDataFunc) func)(data, tmp_list->data, user_data);
+
+    while((tmp_list->next) && (cmp > 0))
+    {
+        tmp_list = tmp_list->next;
+
+        cmp = ((DapCompareDataFunc) func)(data, tmp_list->data, user_data);
+    }
+
+    new_list = dap_list_alloc0();
+    new_list->data = data;
+
+    if((!tmp_list->next) && (cmp > 0))
+            {
+        tmp_list->next = new_list;
+        new_list->prev = tmp_list;
+        return list;
+    }
+
+    if(tmp_list->prev)
+    {
+        tmp_list->prev->next = new_list;
+        new_list->prev = tmp_list->prev;
+    }
+    new_list->next = tmp_list;
+    tmp_list->prev = new_list;
+
+    if(tmp_list == list)
+        return new_list;
+    else
+        return list;
+}
+
+/**
+ * dap_list_insert_sorted:
+ * @list: a pointer to a DapList, this must point to the top of the
+ *     already sorted list
+ * @data: the data for the new element
+ * @func: the function to compare elements in the list. It should
+ *     return a number > 0 if the first parameter comes after the
+ *     second parameter in the sort order.
+ *
+ * Inserts a new element into the list, using the given comparison
+ * function to determine its position.
+ *
+ * If you are adding many new elements to a list, and the number of
+ * new elements is much larger than the length of the list, use
+ * dap_list_prepend() to add the new items and sort the list afterwards
+ * with dap_list_sort().
+ *
+ * Returns: the (possibly changed) start of the DapList
+ */
+dap_list_t *dap_list_insert_sorted(dap_list_t *list, void* data, DapCompareFunc func)
+{
+    return dap_list_insert_sorted_real(list, data, (DapFunc) func, NULL);
+}
+
+/**
+ * dap_list_insert_sorted_with_data:
+ * @list: a pointer to a DapList, this must point to the top of the
+ *     already sorted list
+ * @data: the data for the new element
+ * @func: the function to compare elements in the list. It should
+ *     return a number > 0 if the first parameter  comes after the
+ *     second parameter in the sort order.
+ * @user_data: user data to pass to comparison function
+ *
+ * Inserts a new element into the list, using the given comparison 
+ * function to determine its position.
+ *
+ * If you are adding many new elements to a list, and the number of
+ * new elements is much larger than the length of the list, use
+ * dap_list_prepend() to add the new items and sort the list afterwards
+ * with dap_list_sort().
+ *
+ * Returns: the (possibly changed) start of the DapList
+ */
+dap_list_t * dap_list_insert_sorted_with_data(dap_list_t *list, void* data, DapCompareDataFunc func, void* user_data)
+{
+    return dap_list_insert_sorted_real(list, data, (DapFunc) func, user_data);
+}
+
+static dap_list_t *dap_list_sort_merge(dap_list_t *l1, dap_list_t *l2, DapFunc compare_func, void* user_data)
+{
+    dap_list_t list, *l, *lprev;
+    int cmp;
+
+    l = &list;
+    lprev = NULL;
+
+    while(l1 && l2)
+    {
+        cmp = ((DapCompareDataFunc) compare_func)(l1->data, l2->data, user_data);
+
+        if(cmp <= 0)
+                {
+            l->next = l1;
+            l1 = l1->next;
+        }
+        else
+        {
+            l->next = l2;
+            l2 = l2->next;
+        }
+        l = l->next;
+        l->prev = lprev;
+        lprev = l;
+    }
+    l->next = l1 ? l1 : l2;
+    l->next->prev = l;
+
+    return list.next;
+}
+
+static dap_list_t *dap_list_sort_real(dap_list_t *list, DapFunc compare_func, void* user_data)
+{
+    dap_list_t *l1, *l2;
+
+    if(!list)
+        return NULL ;
+    if(!list->next)
+        return list;
+
+    l1 = list;
+    l2 = list->next;
+
+    while((l2 = l2->next) != NULL )
+    {
+        if((l2 = l2->next) == NULL)
+            break;
+        l1 = l1->next;
+    }
+    l2 = l1->next;
+    l1->next = NULL;
+
+    return dap_list_sort_merge(dap_list_sort_real(list, compare_func, user_data),
+            dap_list_sort_real(l2, compare_func, user_data),
+            compare_func,
+            user_data);
+}
+
+/**
+ * dap_list_sort:
+ * @list: a DapList, this must point to the top of the list
+ * @compare_func: the comparison function used to sort the DapList.
+ *     This function is passed the data from 2 elements of the DapList
+ *     and should return 0 if they are equal, a negative value if the 
+ *     first element comes before the second, or a positive value if 
+ *     the first element comes after the second.
+ *
+ * Sorts a DapList using the given comparison function. The algorithm
+ * used is a stable sort.
+ *
+ * Returns: the (possibly changed) start of the DapList
+ */
+/**
+ * DapCompareFunc:
+ * @a: a value
+ * @b: a value to compare with
+ *
+ * Specifies the type of a comparison function used to compare two
+ * values.  The function should return a negative integer if the first
+ * value comes before the second, 0 if they are equal, or a positive
+ * integer if the first value comes after the second.
+ *
+ * Returns: negative value if @a < @b; zero if @a = @b; positive
+ *          value if @a > @b
+ */
+dap_list_t *dap_list_sort(dap_list_t *list, DapCompareFunc compare_func)
+{
+    return dap_list_sort_real(list, (DapFunc) compare_func, NULL);
+}
+
+/**
+ * dap_list_sort_with_data:
+ * @list: a DapList, this must point to the top of the list
+ * @compare_func: comparison function
+ * @user_data: user data to pass to comparison function
+ *
+ * Like dap_list_sort(), but the comparison function accepts
+ * a user data argument.
+ *
+ * Returns: the (possibly changed) start of the DapList
+ */
+/**
+ * DapCompareFunc:
+ * @a: a value
+ * @b: a value to compare with
+ * @user_data: user data
+ *
+ * Specifies the type of a comparison function used to compare two
+ * values.  The function should return a negative integer if the first
+ * value comes before the second, 0 if they are equal, or a positive
+ * integer if the first value comes after the second.
+ *
+ * Returns: negative value if @a < @b; zero if @a = @b; positive
+ *          value if @a > @b
+ */
+dap_list_t *dap_list_sort_with_data(dap_list_t *list, DapCompareDataFunc compare_func, void* user_data)
+{
+    return dap_list_sort_real(list, (DapFunc) compare_func, user_data);
+}
diff --git a/core/dap_list.h b/core/dap_list.h
new file mode 100755
index 0000000000000000000000000000000000000000..5bcf9605538c282fe17c993b1821e19fa56c20eb
--- /dev/null
+++ b/core/dap_list.h
@@ -0,0 +1,62 @@
+/*
+ * Doubly-Linked Lists — linked lists that can be iterated over in both directions
+ */
+
+#ifndef __DAP_LIST_H__
+#define __DAP_LIST_H__
+
+typedef void (*DapDestroyNotify)(void* data);
+typedef void (*DapFunc)(void* data, void* user_data);
+typedef void* (*DapCopyFunc)(const void * src, void* data);
+typedef int (*DapCompareFunc)(const void * a, const void * b);
+typedef int (*DapCompareDataFunc)(const void * a, const void * b, void* user_data);
+
+typedef struct _dap_list dap_list_t;
+
+struct _dap_list
+{
+    void* data;
+    dap_list_t *next;
+    dap_list_t *prev;
+};
+
+/* Doubly linked lists
+ */
+dap_list_t* dap_list_alloc(void);
+void dap_list_free(dap_list_t *list);
+void dap_list_free1(dap_list_t *list);
+void dap_list_free_full(dap_list_t *list, DapDestroyNotify free_func);
+dap_list_t* dap_list_append(dap_list_t *list, void* data);
+dap_list_t* dap_list_prepend(dap_list_t *list, void* data);
+dap_list_t* dap_list_insert(dap_list_t *list, void* data, int position);
+dap_list_t* dap_list_insert_sorted(dap_list_t *list, void* data, DapCompareFunc func);
+dap_list_t* dap_list_insert_sorted_with_data(dap_list_t *list, void* data, DapCompareDataFunc func, void* user_data);
+dap_list_t* dap_list_insert_before(dap_list_t *list, dap_list_t *sibling, void* data);
+dap_list_t* dap_list_concat(dap_list_t *list1, dap_list_t *list2);
+dap_list_t* dap_list_remove(dap_list_t *list, const void * data);
+dap_list_t* dap_list_remove_all(dap_list_t *list, const void * data);
+dap_list_t* dap_list_remove_link(dap_list_t *list, dap_list_t *llink);
+dap_list_t* dap_list_delete_link(dap_list_t *list, dap_list_t *link_);
+dap_list_t* dap_list_reverse(dap_list_t *list);
+dap_list_t* dap_list_copy(dap_list_t *list);
+
+dap_list_t* dap_list_copy_deep(dap_list_t *list, DapCopyFunc func, void* user_data);
+
+dap_list_t* dap_list_nth(dap_list_t *list, unsigned int n);
+dap_list_t* dap_list_nth_prev(dap_list_t *list, unsigned int n);
+dap_list_t* dap_list_find(dap_list_t *list, const void * data);
+dap_list_t* dap_list_find_custom(dap_list_t *list, const void * data, DapCompareFunc func);
+int dap_list_position(dap_list_t *list, dap_list_t *llink);
+int dap_list_index(dap_list_t *list, const void * data);
+dap_list_t* dap_list_last(dap_list_t *list);
+dap_list_t* dap_list_first(dap_list_t *list);
+unsigned int dap_list_length(dap_list_t *list);
+void dap_list_foreach(dap_list_t *list, DapFunc func, void* user_data);
+dap_list_t* dap_list_sort(dap_list_t *list, DapCompareFunc compare_func);
+dap_list_t* dap_list_sort_with_data(dap_list_t *list, DapCompareDataFunc compare_func, void* user_data);
+void* dap_list_nth_data(dap_list_t *list, unsigned int n);
+
+#define dap_list_previous(list)	        ((list) ? (((dap_list_t *)(list))->prev) : NULL)
+#define dap_list_next(list)	        ((list) ? (((dap_list_t *)(list))->next) : NULL)
+
+#endif /* __DAP_LIST_H__ */
diff --git a/core/dap_strfuncs.c b/core/dap_strfuncs.c
new file mode 100755
index 0000000000000000000000000000000000000000..88dff5d82e3292234e0c1d444113943d6faeed95
--- /dev/null
+++ b/core/dap_strfuncs.c
@@ -0,0 +1,696 @@
+/* DAP String Functions */
+#ifdef _WIN32
+#define _CRT_SECURE_NO_WARNINGS
+#include <windows.h>
+#endif
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <time.h>
+#include <errno.h>
+
+#include "dap_common.h"
+#include "dap_strfuncs.h"
+
+/**
+ * dap_strdup:
+ * @a_str: (nullable): the string to duplicate
+ *
+ * Duplicates a string. If @a_str is %NULL it returns %NULL.
+ * The returned string should be freed
+ * when no longer needed.
+ *
+ * Returns: a newly-allocated copy of @a_str
+ */
+char* dap_strdup(const char *a_str)
+{
+    char *l_new_str;
+    size_t l_length;
+
+    if(a_str)
+    {
+        l_length = (size_t) (strlen(a_str) + 1);
+        l_new_str = DAP_NEW_SIZE(char, l_length);
+        memcpy(l_new_str, a_str, l_length);
+    }
+    else
+        l_new_str = NULL;
+
+    return l_new_str;
+}
+
+/**
+ * dap_strdup_vprintf:
+ * @a_format: a standard printf() format string, but notice
+ *     [string precision pitfalls][string-precision]
+ * @a_args: the list of parameters to insert into the format string
+ *
+ * Similar to the standard C vsprintf() function but safer, since it
+ * calculates the maximum space required and allocates memory to hold
+ * the result. The returned string should be freed with DAP_DELETE()
+ * when no longer needed.
+ *
+ * Returns: a newly-allocated string holding the result
+ */
+char* dap_strdup_vprintf(const char *a_format, va_list a_args)
+{
+    char *l_string = NULL;
+    int len = vasprintf(&l_string, a_format, a_args);
+    if(len < 0)
+        l_string = NULL;
+    return l_string;
+}
+
+/**
+ * dap_strdup_printf:
+ * @a_format: a standard printf() format string
+ *
+ * Similar to the standard C sprintf() function but safer, since it
+ * calculates the maximum space required and allocates memory to hold
+ * the result. The returned string should be freed with DAP_DELETE()
+ * when no longer needed.
+ *
+ * Returns: a newly-allocated string holding the result
+ */
+char* dap_strdup_printf(const char *a_format, ...)
+{
+    char *l_buffer;
+    va_list l_args;
+
+    va_start(l_args, a_format);
+    l_buffer = dap_strdup_vprintf(a_format, l_args);
+    va_end(l_args);
+
+    return l_buffer;
+}
+
+/*
+// alternative version
+char* dap_strdup_printf2(const char *a_format, ...)
+{
+    size_t l_buffer_size = 0;
+    char *l_buffer = NULL;
+    va_list l_args;
+
+    va_start(l_args, a_format);
+    l_buffer_size += vsnprintf(l_buffer, 0, a_format, l_args);
+    va_end(l_args);
+
+    if(!l_buffer_size)
+        return NULL;
+    l_buffer = DAP_NEW_SIZE(char, l_buffer_size + 1);
+
+    va_start(l_args, a_format);
+    vsnprintf(l_buffer, l_buffer_size + 1, a_format, l_args);
+    va_end(l_args);
+
+    return l_buffer;
+}*/
+
+/**
+ * dap_stpcpy:
+ * @a_dest: destination buffer.
+ * @a_src: source string.
+ *
+ * Copies a null-terminated string into the dest buffer, include the
+ * trailing null, and return a pointer to the trailing null byte.
+ * This is useful for concatenating multiple strings together
+ * without having to repeatedly scan for the end.
+ *
+ * Returns: a pointer to trailing null byte.
+ **/
+char* dap_stpcpy(char *a_dest, const char *a_src)
+{
+    char *l_d = a_dest;
+    const char *l_s = a_src;
+
+    dap_return_val_if_fail(a_dest != NULL, NULL);
+    dap_return_val_if_fail(a_src != NULL, NULL);
+    do
+        *l_d++ = *l_s;
+    while(*l_s++ != '\0');
+
+    return l_d - 1;
+}
+
+/**
+ * dap_strstr_len:
+ * @a_haystack: a string
+ * @a_haystack_len: the maximum length of @a_haystack. Note that -1 is
+ *     a valid length, if @a_haystack is null-terminated, meaning it will
+ *     search through the whole string.
+ * @a_needle: the string to search for
+ *
+ * Searches the string @a_haystack for the first occurrence
+ * of the string @a_needle, limiting the length of the search
+ * to @a_haystack_len.
+ *
+ * Returns: a pointer to the found occurrence, or
+ *    %NULL if not found.
+ */
+char* dap_strstr_len(const char *a_haystack, ssize_t a_haystack_len, const char *a_needle)
+{
+    dap_return_val_if_fail(a_haystack != NULL, NULL);
+    dap_return_val_if_fail(a_needle != NULL, NULL);
+
+    if(a_haystack_len < 0)
+        return strstr(a_haystack, a_needle);
+    else
+    {
+        const char *l_p = a_haystack;
+        ssize_t l_needle_len = (ssize_t) strlen(a_needle);
+        const char *l_end;
+        ssize_t l_i;
+
+        if(l_needle_len == 0)
+            return (char *) a_haystack;
+
+        if(a_haystack_len < l_needle_len)
+            return NULL;
+
+        l_end = a_haystack + a_haystack_len - l_needle_len;
+
+        while(l_p <= l_end && *l_p)
+        {
+            for(l_i = 0; l_i < l_needle_len; l_i++)
+                if(l_p[l_i] != a_needle[l_i])
+                    goto next;
+
+            return (char *) l_p;
+
+            next:
+            l_p++;
+        }
+
+        return NULL;
+    }
+}
+
+/**
+ * dap_strjoinv:
+ * @a_separator: (allow-none): a string to insert between each of the
+ *     strings, or %NULL
+ * @a_str_array: a %NULL-terminated array of strings to join
+ *
+ * Joins a number of strings together to form one long string, with the
+ * optional @separator inserted between each of them. The returned string
+ * should be freed.
+ *
+ * If @str_array has no items, the return value will be an
+ * empty string. If @a_str_array contains a single item, @a_separator will not
+ * appear in the resulting string.
+ *
+ * Returns: a newly-allocated string containing all of the strings joined
+ *     together, with @a_separator between them
+ */
+char* dap_strjoinv(const char *a_separator, char **a_str_array)
+{
+    char *l_string;
+    char *l_ptr;
+
+    dap_return_val_if_fail(a_str_array != NULL, NULL);
+
+    if(a_separator == NULL)
+        a_separator = "";
+
+    if(*a_str_array)
+    {
+        int l_i;
+        size_t l_len;
+        size_t l_separator_len;
+
+        l_separator_len = strlen(a_separator);
+        /* First part, getting length */
+        l_len = 1 + strlen(a_str_array[0]);
+        for(l_i = 1; a_str_array[l_i] != NULL; l_i++)
+            l_len += strlen(a_str_array[l_i]);
+        l_len += l_separator_len * (l_i - 1);
+
+        /* Second part, building string */
+        l_string = DAP_NEW_SIZE(char, l_len);
+        l_ptr = dap_stpcpy(l_string, *a_str_array);
+        for(l_i = 1; a_str_array[l_i] != NULL; l_i++)
+                {
+            l_ptr = dap_stpcpy(l_ptr, a_separator);
+            l_ptr = dap_stpcpy(l_ptr, a_str_array[l_i]);
+        }
+    }
+    else
+        l_string = dap_strdup("");
+
+    return l_string;
+}
+
+/**
+ * dap_strjoin:
+ * @a_separator: (allow-none): a string to insert between each of the
+ *     strings, or %NULL
+ * @...: a %NULL-terminated list of strings to join
+ *
+ * Joins a number of strings together to form one long string, with the
+ * optional @a_separator inserted between each of them. The returned string
+ * should be freed.
+ *
+ * Returns: a newly-allocated string containing all of the strings joined
+ *     together, with @a_separator between them
+ */
+char* dap_strjoin(const char *a_separator, ...)
+{
+    char *string, *l_s;
+    va_list l_args;
+    size_t l_len;
+    size_t l_separator_len;
+    char *l_ptr;
+
+    if(a_separator == NULL)
+        a_separator = "";
+
+    l_separator_len = strlen(a_separator);
+
+    va_start(l_args, a_separator);
+
+    l_s = va_arg(l_args, char*);
+
+    if(l_s)
+    {
+        /* First part, getting length */
+        l_len = 1 + strlen(l_s);
+
+        l_s = va_arg(l_args, char*);
+        while(l_s)
+        {
+            l_len += l_separator_len + strlen(l_s);
+            l_s = va_arg(l_args, char*);
+        }
+        va_end(l_args);
+
+        /* Second part, building string */
+        string = DAP_NEW_SIZE(char, l_len);
+
+        va_start(l_args, a_separator);
+
+        l_s = va_arg(l_args, char*);
+        l_ptr = dap_stpcpy(string, l_s);
+
+        l_s = va_arg(l_args, char*);
+        while(l_s)
+        {
+            l_ptr = dap_stpcpy(l_ptr, a_separator);
+            l_ptr = dap_stpcpy(l_ptr, l_s);
+            l_s = va_arg(l_args, char*);
+        }
+    }
+    else
+        string = dap_strdup("");
+
+    va_end(l_args);
+
+    return string;
+}
+
+typedef struct _dap_slist dap_slist;
+
+struct _dap_slist
+{
+    void* data;
+    dap_slist *next;
+};
+
+static dap_slist* dap_slist_prepend(dap_slist *a_list, void* a_data)
+{
+    dap_slist *l_new_list;
+
+    l_new_list = DAP_NEW_Z(dap_slist);
+    l_new_list->data = a_data;
+    l_new_list->next = a_list;
+
+    return l_new_list;
+}
+
+static void dap_slist_free(dap_slist *a_list)
+{
+    if(a_list)
+    {
+        dap_slist *l_cur_node;
+        dap_slist *l_last_node = a_list;
+        while(l_last_node)
+        {
+            l_cur_node = l_last_node;
+            l_last_node = l_last_node->next;
+            DAP_DELETE(l_cur_node);
+        }
+    }
+}
+
+/**
+ * dap_strsplit:
+ * @a_string: a string to split
+ * @a_delimiter: a string which specifies the places at which to split
+ *     the string. The delimiter is not included in any of the resulting
+ *     strings, unless @a_max_tokens is reached.
+ * @a_max_tokens: the maximum number of pieces to split @a_string into.
+ *     If this is less than 1, the string is split completely.
+ *
+ * Splits a string into a maximum of @a_max_tokens pieces, using the given
+ * @a_delimiter. If @a_max_tokens is reached, the remainder of @a_string is
+ * appended to the last token.
+ *
+ * As an example, the result of dap_strsplit (":a:bc::d:", ":", -1) is a
+ * %NULL-terminated vector containing the six strings "", "a", "bc", "", "d"
+ * and "".
+ *
+ * As a special case, the result of splitting the empty string "" is an empty
+ * vector, not a vector containing a single string. The reason for this
+ * special case is that being able to represent a empty vector is typically
+ * more useful than consistent handling of empty elements. If you do need
+ * to represent empty elements, you'll need to check for the empty string
+ * before calling dap_strsplit().
+ *
+ * Returns: a newly-allocated %NULL-terminated array of strings.
+ * Use dap_strfreev() to free it.
+ */
+char** dap_strsplit(const char *a_string, const char *a_delimiter, int a_max_tokens)
+{
+    dap_slist *l_string_list = NULL, *l_slist;
+    char **l_str_array, *l_s;
+    uint32_t l_n = 1;
+
+    dap_return_val_if_fail(a_string != NULL, NULL);
+    dap_return_val_if_fail(a_delimiter != NULL, NULL);
+
+    if(a_max_tokens < 1)
+        a_max_tokens = INT_MAX;
+
+    l_s = strstr(a_string, a_delimiter);
+    if(l_s)
+    {
+        uint32_t delimiter_len = (uint32_t) strlen(a_delimiter);
+
+        do
+        {
+            uint32_t len;
+            char *new_string;
+
+            len = (uint32_t) (l_s - a_string);
+            new_string = DAP_NEW_SIZE(char, len + 1);
+            strncpy(new_string, a_string, len);
+            new_string[len] = 0;
+            l_string_list = dap_slist_prepend(l_string_list, new_string);
+            l_n++;
+            a_string = l_s + delimiter_len;
+            l_s = strstr(a_string, a_delimiter);
+        }
+        while(--a_max_tokens && l_s);
+    }
+    l_string_list = dap_slist_prepend(l_string_list, dap_strdup(a_string));
+
+    l_str_array = DAP_NEW_SIZE(char*, (l_n + 1) * sizeof(char*));
+
+    l_str_array[l_n--] = NULL;
+    for(l_slist = l_string_list; l_slist; l_slist = l_slist->next)
+        l_str_array[l_n--] = l_slist->data;
+
+    dap_slist_free(l_string_list);
+
+    return l_str_array;
+}
+
+int dap_str_countv(char **a_str_array)
+{
+    int l_i = 0;
+    if(a_str_array)
+    {
+        for(l_i = 0; a_str_array[l_i] != NULL; l_i++)
+            ;
+    }
+    return l_i;
+}
+
+/**
+ * dap_strdupv:
+ * @a_str_array: (nullable): a %NULL-terminated array of strings
+ *
+ * Copies %NULL-terminated array of strings. The copy is a deep copy;
+ * the new array should be freed by first freeing each string, then
+ * the array itself. g_strfreev() does this for you. If called
+ * on a %NULL value, g_strdupv() simply returns %NULL.
+ *
+ * Returns: (nullable): a new %NULL-terminated array of strings.
+ */
+char** dap_strdupv(char **a_str_array)
+{
+    if(a_str_array)
+    {
+        int l_i;
+        char **l_retval;
+
+        l_i = 0;
+        while(a_str_array[l_i])
+            ++l_i;
+
+        l_retval = DAP_NEW_SIZE(char*, (l_i + 1) * sizeof(char*));
+
+        l_i = 0;
+        while(a_str_array[l_i])
+        {
+            l_retval[l_i] = dap_strdup(a_str_array[l_i]);
+            ++l_i;
+        }
+        l_retval[l_i] = NULL;
+
+        return l_retval;
+    }
+    else
+        return NULL;
+}
+
+/**
+ * dap_strfreev:
+ * @a_str_array: (nullable): a %NULL-terminated array of strings to free
+ *
+ * Frees a %NULL-terminated array of strings, as well as each
+ * string it contains.
+ *
+ * If @a_str_array is %NULL, this function simply returns.
+ */
+void dap_strfreev(char **a_str_array)
+{
+    if(a_str_array)
+    {
+        int l_i;
+        for(l_i = 0; a_str_array[l_i] != NULL; l_i++)
+            DAP_DELETE(a_str_array[l_i]);
+
+        DAP_DELETE(a_str_array);
+    }
+}
+
+static const uint16_t s_ascii_table_data[256] = {
+    0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004,
+    0x004, 0x104, 0x104, 0x004, 0x104, 0x104, 0x004, 0x004,
+    0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004,
+    0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004,
+    0x140, 0x0d0, 0x0d0, 0x0d0, 0x0d0, 0x0d0, 0x0d0, 0x0d0,
+    0x0d0, 0x0d0, 0x0d0, 0x0d0, 0x0d0, 0x0d0, 0x0d0, 0x0d0,
+    0x459, 0x459, 0x459, 0x459, 0x459, 0x459, 0x459, 0x459,
+    0x459, 0x459, 0x0d0, 0x0d0, 0x0d0, 0x0d0, 0x0d0, 0x0d0,
+    0x0d0, 0x653, 0x653, 0x653, 0x653, 0x653, 0x653, 0x253,
+    0x253, 0x253, 0x253, 0x253, 0x253, 0x253, 0x253, 0x253,
+    0x253, 0x253, 0x253, 0x253, 0x253, 0x253, 0x253, 0x253,
+    0x253, 0x253, 0x253, 0x0d0, 0x0d0, 0x0d0, 0x0d0, 0x0d0,
+    0x0d0, 0x473, 0x473, 0x473, 0x473, 0x473, 0x473, 0x073,
+    0x073, 0x073, 0x073, 0x073, 0x073, 0x073, 0x073, 0x073,
+    0x073, 0x073, 0x073, 0x073, 0x073, 0x073, 0x073, 0x073,
+    0x073, 0x073, 0x073, 0x0d0, 0x0d0, 0x0d0, 0x0d0, 0x004
+/* the upper 128 are all zeroes */
+};
+/* Functions like the ones in <ctype.h> that are not affected by locale. */
+typedef enum {
+    DAP_ASCII_ALNUM = 1 << 0,
+    DAP_ASCII_ALPHA = 1 << 1,
+    DAP_ASCII_CNTRL = 1 << 2,
+    DAP_ASCII_DIGIT = 1 << 3,
+    DAP_ASCII_GRAPH = 1 << 4,
+    DAP_ASCII_LOWER = 1 << 5,
+    DAP_ASCII_PRINT = 1 << 6,
+    DAP_ASCII_PUNCT = 1 << 7,
+    DAP_ASCII_SPACE = 1 << 8,
+    DAP_ASCII_UPPER = 1 << 9,
+    DAP_ASCII_XDIGIT = 1 << 10
+} DapAsciiType;
+
+const uint16_t * const c_dap_ascii_table = s_ascii_table_data;
+
+#define dap_ascii_isspace(c) ((c_dap_ascii_table[(unsigned char) (c)] & DAP_ASCII_SPACE) != 0)
+#define dap_ascii_isalpha(c) ((c_dap_ascii_table[(unsigned char) (c)] & DAP_ASCII_ALPHA) != 0)
+
+/**
+ * dap_strchug:
+ * @a_string: a string to remove the leading whitespace from
+ *
+ * Removes leading whitespace from a string, by moving the rest
+ * of the characters forward.
+ *
+ * This function doesn't allocate or reallocate any memory;
+ * it modifies @a_string in place. Therefore, it cannot be used on
+ * statically allocated strings.
+ *
+ * The pointer to @a_string is returned to allow the nesting of functions.
+ * Returns: @a_string
+ */
+char* dap_strchug(char *a_string)
+{
+    unsigned char *l_start;
+
+    dap_return_val_if_fail(a_string != NULL, NULL);
+
+    for(l_start = (unsigned char*) a_string; *l_start && dap_ascii_isspace(*l_start); l_start++)
+        ;
+
+    memmove(a_string, l_start, strlen((char *) l_start) + 1);
+
+    return a_string;
+}
+
+/**
+ * dap_strchomp:
+ * @a_string: a string to remove the trailing whitespace from
+ *
+ * Removes trailing whitespace from a string.
+ *
+ * This function doesn't allocate or reallocate any memory;
+ * it modifies @a_string in place. Therefore, it cannot be used
+ * on statically allocated strings.
+ *
+ * The pointer to @a_string is returned to allow the nesting of functions.
+ *
+ * Returns: @a_string
+ */
+char* dap_strchomp(char *a_string)
+{
+    size_t l_len;
+
+    dap_return_val_if_fail(a_string != NULL, NULL);
+
+    l_len = (size_t) strlen(a_string);
+    while(l_len--)
+    {
+        if(dap_ascii_isspace((unsigned char ) a_string[l_len]))
+            a_string[l_len] = '\0';
+        else
+            break;
+    }
+    return a_string;
+}
+
+/**
+ * dap_strup:
+ * @a_str: a string
+ * @a_len: length of @a_str in bytes, or -1 if @a_str is nul-terminated
+ *
+ * Converts all lower case ASCII letters to upper case ASCII letters.
+ *
+ * Returns: a newly allocated string, with all the lower case
+ *     characters in @a_str converted to upper case
+ */
+char* dap_strup(const char *a_str, ssize_t a_len)
+{
+    char *l_result, *l_s;
+
+    dap_return_val_if_fail(a_str != NULL, NULL);
+
+    if(a_len < 0)
+        a_len = strlen(a_str);
+
+    l_result = strndup(a_str, a_len);
+    for(l_s = l_result; *l_s; l_s++)
+        *l_s = toupper(*l_s);
+
+    return l_result;
+}
+
+/**
+ * dap_strdown:
+ * @a_str: a string
+ * @a_len: length of @a_str in bytes, or -1 if @a_str is nul-terminated
+ *
+ * Converts all upper case ASCII letters to lower case ASCII letters.
+ *
+ * Returns: a newly-allocated string, with all the upper case
+ *     characters in @a_str converted to lower case
+ */
+char* dap_strdown(const char *a_str, ssize_t a_len)
+{
+    char *l_result, *l_s;
+
+    dap_return_val_if_fail(a_str != NULL, NULL);
+
+    if(a_len < 0)
+        a_len = strlen(a_str);
+
+    l_result = strndup(a_str, a_len);
+    for(l_s = l_result; *l_s; l_s++)
+        *l_s = tolower(*l_s);
+
+    return l_result;
+}
+
+/**
+ * dap_strreverse:
+ * @a_string: the string to reverse
+ *
+ * Reverses all of the bytes in a string. For example,
+ * `dap_strreverse("abcdef")` will result in "fedcba".
+ *
+ * Note that g_strreverse() doesn't work on UTF-8 strings
+ * containing multibyte characters.
+ *
+ * Returns: the same pointer passed in as @a_string
+ */
+char* dap_strreverse(char *a_string)
+{
+    dap_return_val_if_fail(a_string != NULL, NULL);
+
+    if(*a_string)
+    {
+        register char *l_h, *l_t;
+
+        l_h = a_string;
+        l_t = a_string + strlen(a_string) - 1;
+
+        while(l_h < l_t)
+        {
+            register char l_c;
+
+            l_c = *l_h;
+            *l_h = *l_t;
+            l_h++;
+            *l_t = l_c;
+            l_t--;
+        }
+    }
+
+    return a_string;
+}
+
+/**
+ * dap_usleep:
+ * @a_microseconds: number of microseconds to pause
+ *
+ * Pauses the current thread for the given number of microseconds.
+ */
+void dap_usleep(time_t a_microseconds)
+{
+#ifdef _WIN32
+    Sleep (a_microseconds / 1000);
+#else
+    struct timespec l_request, l_remaining;
+    l_request.tv_sec = a_microseconds / DAP_USEC_PER_SEC;
+    l_request.tv_nsec = 1000 * (a_microseconds % DAP_USEC_PER_SEC);
+    while(nanosleep(&l_request, &l_remaining) == -1 && errno == EINTR)
+        l_request = l_remaining;
+#endif
+}
+
diff --git a/core/dap_strfuncs.h b/core/dap_strfuncs.h
new file mode 100755
index 0000000000000000000000000000000000000000..eb2d8241b9859a0b9b4bcf8e63f527a2009649d0
--- /dev/null
+++ b/core/dap_strfuncs.h
@@ -0,0 +1,65 @@
+/* DAP String Functions */
+
+#ifndef __DAP_STR_FUNC_H
+#define __DAP_STR_FUNC_H
+
+#include <limits.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdarg.h>
+
+#define dap_return_if_fail(expr)			{if(!(expr)) {return;}}
+#define dap_return_val_if_fail(expr,val)	{if(!(expr)) {return (val);}}
+
+#define POINTER_TO_INT(p)	((int)   (p))
+#define POINTER_TO_UINT(p)	((unsigned int)  (p))
+
+#define INT_TO_POINTER(i)	((void*)  (i))
+#define UINT_TO_POINTER(u)	((void*)  (u))
+
+#undef	max
+#define max(a, b)  (((a) > (b)) ? (a) : (b))
+
+#undef	min
+#define min(a, b)  (((a) < (b)) ? (a) : (b))
+
+#undef	abs
+#define abs(a)	   (((a) < 0) ? -(a) : (a))
+
+#undef	clamp
+#define clamp(x, low, high)  (((x) > (high)) ? (high) : (((x) < (low)) ? (low) : (x)))
+
+char* dap_strdup(const char *a_str);
+char* dap_strdup_vprintf(const char *a_format, va_list a_args);
+char* dap_strdup_printf(const char *a_format, ...);
+
+char* dap_stpcpy(char *a_dest, const char *a_src);
+char* dap_strstr_len(const char *a_haystack, ssize_t a_haystack_len, const char *a_needle);
+// concatenates all of str_array's strings, sliding in an optional separator, the returned string is newly allocated.
+char* dap_strjoinv(const char *a_separator, char **a_str_array);
+char* dap_strjoin(const char *a_separator, ...);
+// split up string into max_tokens tokens at delimiter and return a newly allocated string array
+char** dap_strsplit(const char *a_string, const char *a_delimiter, int a_max_tokens);
+int dap_str_countv(char **a_str_array);
+// copies a NULL-terminated array of strings
+char** dap_strdupv(char **a_str_array);
+// frees the array itself and all of its strings.
+void dap_strfreev(char **a_str_array);
+
+// removes leading spaces
+char* dap_strchug(char *a_string);
+// removes trailing spaces
+char* dap_strchomp(char *a_string);
+// removes leading & trailing spaces 
+#define dap_strstrip( a_string )	dap_strchomp (dap_strchug (a_string))
+
+// Converts all lower case ASCII letters to upper case ASCII letters.
+char* dap_strup(const char *a_str, ssize_t a_len);
+// Converts a string to lower case.
+char* dap_strdown(const char *a_str, ssize_t a_len);
+char* dap_strreverse(char *a_string);
+
+#define DAP_USEC_PER_SEC 1000000
+void dap_usleep(time_t a_microseconds);
+
+#endif /* __DAP_STR_FUNC_H */
diff --git a/core/dap_string.c b/core/dap_string.c
new file mode 100755
index 0000000000000000000000000000000000000000..994a2f6b9d068965e3fe8f64d0eda0bdbf08479b
--- /dev/null
+++ b/core/dap_string.c
@@ -0,0 +1,911 @@
+// dap_string_t is an object that handles the memory management of a C string for you.
+
+#include <stdarg.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+
+#include "dap_common.h"
+#include "dap_strfuncs.h"
+#include "dap_string.h"
+
+#define MY_MAXSIZE ((size_t)-1)
+
+static inline size_t nearest_power(size_t a_base, size_t a_num)
+{
+    if(a_num > MY_MAXSIZE / 2) {
+        return MY_MAXSIZE;
+    }
+    else {
+        size_t l_n = a_base;
+
+        while(l_n < a_num)
+            l_n <<= 1;
+
+        return l_n;
+    }
+}
+
+static void dap_string_maybe_expand(dap_string_t *a_string, size_t a_len)
+{
+    if(a_string->len + a_len >= a_string->allocated_len) {
+        a_string->allocated_len = nearest_power(1, a_string->len + a_len + 1);
+        a_string->str = DAP_REALLOC(a_string->str, a_string->allocated_len);
+    }
+}
+
+/**
+ * dap_string_sized_new:
+ * @a_a_dfl_size: the default size of the space allocated to
+ *     hold the string
+ *
+ * Creates a new #dap_string_t, with enough space for @a_a_dfl_size
+ * bytes. This is useful if you are going to add a lot of
+ * text to the string and don't want it to be reallocated
+ * too often.
+ *
+ * Returns: the new #dap_string_t
+ */
+dap_string_t * dap_string_sized_new(size_t a_dfl_size)
+{
+    dap_string_t *l_string = DAP_NEW(dap_string_t);
+
+    l_string->allocated_len = 0;
+    l_string->len = 0;
+    l_string->str = NULL;
+
+    dap_string_maybe_expand(l_string, max(a_dfl_size, 2));
+    l_string->str[0] = 0;
+
+    return l_string;
+}
+
+/**
+ * dap_string_new:
+ * @a_a_init: (nullable): the initial text to copy into the string, or %NULL to
+ * start with an empty string
+ *
+ * Creates a new #dap_string_t, initialized with the given string.
+ *
+ * Returns: the new #dap_string_t
+ */
+dap_string_t* dap_string_new(const char *a_init)
+{
+    dap_string_t *l_string;
+
+    if(a_init == NULL || *a_init == '\0')
+        l_string = dap_string_sized_new(2);
+    else
+    {
+        int len;
+
+        len = strlen(a_init);
+        l_string = dap_string_sized_new(len + 2);
+
+        dap_string_append_len(l_string, a_init, len);
+    }
+
+    return l_string;
+}
+
+/**
+ * dap_string_new_len:
+ * @a_init: initial contents of the string
+ * @a_len: length of @a_init to use
+ *
+ * Creates a new #dap_string_t with @a_len bytes of the @a_init buffer.
+ * Because a length is provided, @a_init need not be nul-terminated,
+ * and can contain embedded nul bytes.
+ *
+ * Since this function does not stop at nul bytes, it is the caller's
+ * responsibility to ensure that @a_init has at least @a_len addressable
+ * bytes.
+ *
+ * Returns: a new #dap_string_t
+ */
+dap_string_t* dap_string_new_len(const char *a_init, ssize_t a_len)
+{
+    dap_string_t *l_string;
+
+    if(a_len < 0)
+        return dap_string_new(a_init);
+    else
+    {
+        l_string = dap_string_sized_new(a_len);
+
+        if(a_init)
+            dap_string_append_len(l_string, a_init, a_len);
+
+        return l_string;
+    }
+}
+
+/**
+ * dap_string_free:
+ * @a_string: (transfer full): a #dap_string_t
+ * @a_free_segment: if %true, the actual character data is freed as well
+ *
+ * Frees the memory allocated for the #dap_string_t.
+ * If @a_free_segment is %true it also frees the character data.  If
+ * it's %false, the caller gains ownership of the buffer and must
+ * free it after use with dap_free().
+ *
+ * Returns: (nullable): the character data of @a_string
+ *          (i.e. %NULL if @a_free_segment is %true)
+ */
+char* dap_string_free(dap_string_t *a_string, bool a_free_segment)
+{
+    char *l_segment;
+
+    dap_return_val_if_fail(a_string != NULL, NULL);
+
+    if(a_free_segment)
+    {
+        DAP_DELETE(a_string->str);
+        l_segment = NULL;
+    }
+    else
+        l_segment = a_string->str;
+
+    DAP_DELETE(a_string);
+
+    return l_segment;
+}
+
+/**
+ * dap_string_equal:
+ * @a_v: a #dap_string_t
+ * @a_v2: another #dap_string_t
+ *
+ * Compares two strings for equality, returning %true if they are equal.
+ * For use with #GHashTable.
+ *
+ * Returns: %true if the strings are the same length and contain the
+ *     same bytes
+ */
+bool dap_string_equal(const dap_string_t *a_v, const dap_string_t *a_v2)
+{
+    char *p, *q;
+    dap_string_t *l_string1 = (dap_string_t *) a_v;
+    dap_string_t *l_string2 = (dap_string_t *) a_v2;
+    size_t i = l_string1->len;
+
+    if(i != l_string2->len)
+        return false;
+
+    p = l_string1->str;
+    q = l_string2->str;
+    while(i)
+    {
+        if(*p != *q)
+            return false;
+        p++;
+        q++;
+        i--;
+    }
+    return true;
+}
+
+/**
+ * dap_string_hash:
+ * @a_str: a string to hash
+ *
+ * Creates a hash code for @a_str
+ *
+ * Returns: hash code for @a_str
+ */
+unsigned int dap_string_hash(const dap_string_t *a_str)
+{
+    const char *p = a_str->str;
+    size_t n = a_str->len;
+    unsigned int h = 0;
+
+    /* 31 bit hash function */
+    while(n--)
+    {
+        h = (h << 5) - h + *p;
+        p++;
+    }
+
+    return h;
+}
+
+/**
+ * dap_string_assign:
+ * @a_string: the destination #dap_string_t. Its current contents
+ *          are destroyed.
+ * @a_rval: the string to copy into @a_string
+ *
+ * Copies the bytes from a string into a #dap_string_t,
+ * destroying any previous contents. It is rather like
+ * the standard strcpy() function, except that you do not
+ * have to worry about having enough space to copy the string.
+ *
+ * Returns: (transfer none): @a_string
+ */
+dap_string_t* dap_string_assign(dap_string_t *a_string, const char *a_rval)
+{
+    dap_return_val_if_fail(a_string != NULL, NULL);
+    dap_return_val_if_fail(a_rval != NULL, a_string);
+
+    /* Make sure assigning to itself doesn't corrupt the string. */
+    if(a_string->str != a_rval)
+            {
+        /* Assigning from substring should be ok, since
+         * dap_string_truncate() does not reallocate.
+         */
+        dap_string_truncate(a_string, 0);
+        dap_string_append(a_string, a_rval);
+    }
+
+    return a_string;
+}
+
+/**
+ * dap_string_truncate:
+ * @a_string: a #dap_string_t
+ * @a_len: the new size of @a_string
+ *
+ * Cuts off the end of the dap_string_t, leaving the first @a_len bytes.
+ *
+ * Returns: (transfer none): @a_string
+ */
+dap_string_t* dap_string_truncate(dap_string_t *string, size_t len)
+{
+    dap_return_val_if_fail(string != NULL, NULL);
+
+    string->len = min(len, string->len);
+    string->str[string->len] = 0;
+
+    return string;
+}
+
+/**
+ * dap_string_set_size:
+ * @a_string: a #dap_string_t
+ * @a_len: the new length
+ *
+ * Sets the length of a #dap_string_t. If the length is less than
+ * the current length, the string will be truncated. If the
+ * length is greater than the current length, the contents
+ * of the newly added area are undefined. (However, as
+ * always, string->str[string->len] will be a nul byte.)
+ *
+ * Returns: (transfer none): @a_string
+ */
+dap_string_t* dap_string_set_size(dap_string_t *string, size_t len)
+{
+    dap_return_val_if_fail(string != NULL, NULL);
+
+    if(len >= string->allocated_len)
+        dap_string_maybe_expand(string, len - string->len);
+
+    string->len = len;
+    string->str[len] = 0;
+
+    return string;
+}
+
+/**
+ * dap_string_insert_len:
+ * @a_string: a #dap_string_t
+ * @a_pos: position in @a_string where insertion should
+ *       happen, or -1 for at the end
+ * @a_val: bytes to insert
+ * @a_len: number of bytes of @a_val to insert
+ *
+ * Inserts @a_len bytes of @a_val into @a_string at @a_pos.
+ * Because @a_len is provided, @a_val may contain embedded
+ * nuls and need not be nul-terminated. If @a_pos is -1,
+ * bytes are inserted at the end of the string.
+ *
+ * Since this function does not stop at nul bytes, it is
+ * the caller's responsibility to ensure that @a_val has at
+ * least @a_len addressable bytes.
+ *
+ * Returns: (transfer none): @a_string
+ */
+dap_string_t* dap_string_insert_len(dap_string_t *string, ssize_t pos, const char *val, ssize_t len)
+{
+    dap_return_val_if_fail(string != NULL, NULL);
+    dap_return_val_if_fail(len == 0 || val != NULL, string);
+
+    if(len == 0)
+        return string;
+
+    if(len < 0)
+        len = strlen(val);
+
+    if(pos < 0)
+        pos = string->len;
+    else
+        dap_return_val_if_fail((size_t )pos <= string->len, string);
+
+    /* Check whether val represents a substring of string.
+     * This test probably violates chapter and verse of the C standards,
+     * since ">=" and "<=" are only valid when val really is a substring.
+     * In practice, it will work on modern archs.
+     */
+    if(val >= string->str && val <= string->str + string->len)
+            {
+        size_t offset = val - string->str;
+        size_t precount = 0;
+
+        dap_string_maybe_expand(string, len);
+        val = string->str + offset;
+        /* At this point, val is valid again.  */
+
+        /* Open up space where we are going to insert.  */
+        if((size_t) pos < string->len)
+            memmove(string->str + pos + len, string->str + pos, string->len - pos);
+
+        /* Move the source part before the gap, if any.  */
+        if(offset < (size_t) pos) {
+            precount = min(len, pos - (ssize_t )offset);
+            memcpy(string->str + pos, val, precount);
+        }
+
+        /* Move the source part after the gap, if any.  */
+        if((size_t) len > precount)
+            memcpy(string->str + pos + precount,
+                    val + /* Already moved: */precount + /* Space opened up: */len,
+                    len - precount);
+    }
+    else
+    {
+        dap_string_maybe_expand(string, len);
+
+        /* If we aren't appending at the end, move a hunk
+         * of the old string to the end, opening up space
+         */
+        if((size_t) pos < string->len)
+            memmove(string->str + pos + len, string->str + pos, string->len - pos);
+
+        /* insert the new string */
+        if(len == 1)
+            string->str[pos] = *val;
+        else
+            memcpy(string->str + pos, val, len);
+    }
+
+    string->len += len;
+
+    string->str[string->len] = 0;
+
+    return string;
+}
+
+/**
+ * dap_string_append:
+ * @a_string: a #dap_string_t
+ * @a_val: the string to append onto the end of @a_string
+ *
+ * Adds a string onto the end of a #dap_string_t, expanding
+ * it if necessary.
+ *
+ * Returns: (transfer none): @a_string
+ */
+dap_string_t* dap_string_append(dap_string_t *string, const char *val)
+{
+    return dap_string_insert_len(string, -1, val, -1);
+}
+
+/**
+ * dap_string_append_len:
+ * @a_string: a #dap_string_t
+ * @a_val: bytes to append
+ * @a_len: number of bytes of @a_val to use
+ *
+ * Appends @a_len bytes of @a_val to @a_string. Because @a_len is
+ * provided, @a_val may contain embedded nuls and need not
+ * be nul-terminated.
+ *
+ * Since this function does not stop at nul bytes, it is
+ * the caller's responsibility to ensure that @a_val has at
+ * least @a_len addressable bytes.
+ *
+ * Returns: (transfer none): @a_string
+ */
+dap_string_t* dap_string_append_len(dap_string_t *string, const char *val, ssize_t len)
+{
+    return dap_string_insert_len(string, -1, val, len);
+}
+
+/**
+ * dap_string_append_c:
+ * @a_string: a #dap_string_t
+ * @a_c: the byte to append onto the end of @a_string
+ *
+ * Adds a byte onto the end of a #dap_string_t, expanding
+ * it if necessary.
+ *
+ * Returns: (transfer none): @a_string
+ */
+#undef dap_string_append_c
+dap_string_t* dap_string_append_c(dap_string_t *string, char c)
+{
+    dap_return_val_if_fail(string != NULL, NULL);
+
+    return dap_string_insert_c(string, -1, c);
+}
+
+/**
+ * dap_string_append_unichar:
+ * @a_string: a #dap_string_t
+ * @a_wc: a Unicode character
+ *
+ * Converts a Unicode character into UTF-8, and appends it
+ * to the string.
+ *
+ * Returns: (transfer none): @a_string
+ */
+dap_string_t* dap_string_append_unichar(dap_string_t *string, uint32_t wc)
+{
+    dap_return_val_if_fail(string != NULL, NULL);
+
+    return dap_string_insert_unichar(string, -1, wc);
+}
+
+/**
+ * dap_string_prepend:
+ * @a_string: a #dap_string_t
+ * @a_val: the string to prepend on the start of @a_string
+ *
+ * Adds a string on to the start of a #dap_string_t,
+ * expanding it if necessary.
+ *
+ * Returns: (transfer none): @a_string
+ */
+dap_string_t* dap_string_prepend(dap_string_t *string, const char *val)
+{
+    return dap_string_insert_len(string, 0, val, -1);
+}
+
+/**
+ * dap_string_prepend_len:
+ * @a_string: a #dap_string_t
+ * @a_val: bytes to prepend
+ * @a_len: number of bytes in @a_val to prepend
+ *
+ * Prepends @a_len bytes of @a_val to @a_string.
+ * Because @a_len is provided, @a_val may contain
+ * embedded nuls and need not be nul-terminated.
+ *
+ * Since this function does not stop at nul bytes,
+ * it is the caller's responsibility to ensure that
+ * @a_val has at least @a_len addressable bytes.
+ *
+ * Returns: (transfer none): @a_string
+ */
+dap_string_t* dap_string_prepend_len(dap_string_t *string, const char *val, ssize_t len)
+{
+    return dap_string_insert_len(string, 0, val, len);
+}
+
+/**
+ * dap_string_prepend_c:
+ * @a_string: a #dap_string_t
+ * @a_c: the byte to prepend on the start of the #dap_string_t
+ *
+ * Adds a byte onto the start of a #dap_string_t,
+ * expanding it if necessary.
+ *
+ * Returns: (transfer none): @a_string
+ */
+dap_string_t* dap_string_prepend_c(dap_string_t *string, char c)
+{
+    dap_return_val_if_fail(string != NULL, NULL);
+
+    return dap_string_insert_c(string, 0, c);
+}
+
+/**
+ * dap_string_prepend_unichar:
+ * @a_string: a #dap_string_t
+ * @a_wc: a Unicode character
+ *
+ * Converts a Unicode character into UTF-8, and prepends it
+ * to the string.
+ *
+ * Returns: (transfer none): @a_string
+ */
+dap_string_t* dap_string_prepend_unichar(dap_string_t *string, uint32_t wc)
+{
+    dap_return_val_if_fail(string != NULL, NULL);
+
+    return dap_string_insert_unichar(string, 0, wc);
+}
+
+/**
+ * dap_string_insert:
+ * @a_string: a #dap_string_t
+ * @a_pos: the position to insert the copy of the string
+ * @a_val: the string to insert
+ *
+ * Inserts a copy of a string into a #dap_string_t,
+ * expanding it if necessary.
+ *
+ * Returns: (transfer none): @a_string
+ */
+dap_string_t* dap_string_insert(dap_string_t *string, ssize_t pos, const char *val)
+{
+    return dap_string_insert_len(string, pos, val, -1);
+}
+
+/**
+ * dap_string_insert_c:
+ * @a_string: a #dap_string_t
+ * @a_pos: the position to insert the byte
+ * @a_c: the byte to insert
+ *
+ * Inserts a byte into a #dap_string_t, expanding it if necessary.
+ *
+ * Returns: (transfer none): @a_string
+ */
+dap_string_t* dap_string_insert_c(dap_string_t *string, ssize_t pos, char c)
+{
+    dap_return_val_if_fail(string != NULL, NULL);
+
+    dap_string_maybe_expand(string, 1);
+
+    if(pos < 0)
+        pos = string->len;
+    else
+        dap_return_val_if_fail((size_t )pos <= string->len, string);
+
+    /* If not just an append, move the old stuff */
+    if((size_t) pos < string->len)
+        memmove(string->str + pos + 1, string->str + pos, string->len - pos);
+
+    string->str[pos] = c;
+
+    string->len += 1;
+
+    string->str[string->len] = 0;
+
+    return string;
+}
+
+/**
+ * dap_string_insert_unichar:
+ * @a_string: a #dap_string_t
+ * @a_pos: the position at which to insert character, or -1
+ *     to append at the end of the string
+ * @a_wc: a Unicode character
+ *
+ * Converts a Unicode character into UTF-8, and insert it
+ * into the string at the given position.
+ *
+ * Returns: (transfer none): @a_string
+ */
+dap_string_t* dap_string_insert_unichar(dap_string_t *string, ssize_t pos, uint32_t wc)
+{
+    int charlen, first, i;
+    char *dest;
+
+    dap_return_val_if_fail(string != NULL, NULL);
+
+    /* Code copied from dap_unichar_to_utf() */
+    if(wc < 0x80)
+            {
+        first = 0;
+        charlen = 1;
+    }
+    else if(wc < 0x800)
+            {
+        first = 0xc0;
+        charlen = 2;
+    }
+    else if(wc < 0x10000)
+            {
+        first = 0xe0;
+        charlen = 3;
+    }
+    else if(wc < 0x200000)
+            {
+        first = 0xf0;
+        charlen = 4;
+    }
+    else if(wc < 0x4000000)
+            {
+        first = 0xf8;
+        charlen = 5;
+    }
+    else
+    {
+        first = 0xfc;
+        charlen = 6;
+    }
+    /* End of copied code */
+
+    dap_string_maybe_expand(string, charlen);
+
+    if(pos < 0)
+        pos = string->len;
+    else
+        dap_return_val_if_fail((size_t )pos <= string->len, string);
+
+    /* If not just an append, move the old stuff */
+    if((size_t) pos < string->len)
+        memmove(string->str + pos + charlen, string->str + pos, string->len - pos);
+
+    dest = string->str + pos;
+    /* Code copied from dap_unichar_to_utf() */
+    for(i = charlen - 1; i > 0; --i)
+            {
+        dest[i] = (wc & 0x3f) | 0x80;
+        wc >>= 6;
+    }
+    dest[0] = wc | first;
+    /* End of copied code */
+
+    string->len += charlen;
+
+    string->str[string->len] = 0;
+
+    return string;
+}
+
+/**
+ * dap_string_overwrite:
+ * @a_string: a #dap_string_t
+ * @a_pos: the position at which to start overwriting
+ * @a_val: the string that will overwrite the @a_string starting at @a_pos
+ *
+ * Overwrites part of a string, lengthening it if necessary.
+ *
+ * Returns: (transfer none): @a_string
+ */
+dap_string_t* dap_string_overwrite(dap_string_t *string, size_t pos, const char *val)
+{
+    dap_return_val_if_fail(val != NULL, string);
+    return dap_string_overwrite_len(string, pos, val, strlen(val));
+}
+
+/**
+ * dap_string_overwrite_len:
+ * @a_string: a #dap_string_t
+ * @a_pos: the position at which to start overwriting
+ * @a_val: the string that will overwrite the @a_string starting at @a_pos
+ * @a_len: the number of bytes to write from @a_val
+ *
+ * Overwrites part of a string, lengthening it if necessary.
+ * This function will work with embedded nuls.
+ *
+ * Returns: (transfer none): @a_string
+ */
+dap_string_t* dap_string_overwrite_len(dap_string_t *string, size_t pos, const char *val, ssize_t len)
+{
+    size_t end;
+
+    dap_return_val_if_fail(string != NULL, NULL);
+
+    if(!len)
+        return string;
+
+    dap_return_val_if_fail(val != NULL, string);
+    dap_return_val_if_fail(pos <= string->len, string);
+
+    if(len < 0)
+        len = strlen(val);
+
+    end = pos + len;
+
+    if(end > string->len)
+        dap_string_maybe_expand(string, end - string->len);
+
+    memcpy(string->str + pos, val, len);
+
+    if(end > string->len)
+            {
+        string->str[end] = '\0';
+        string->len = end;
+    }
+
+    return string;
+}
+
+/**
+ * dap_string_erase:
+ * @a_string: a #dap_string_t
+ * @a_pos: the position of the content to remove
+ * @a_len: the number of bytes to remove, or -1 to remove all
+ *       following bytes
+ *
+ * Removes @a_len bytes from a #dap_string_t, starting at position @a_pos.
+ * The rest of the #dap_string_t is shifted down to fill the gap.
+ *
+ * Returns: (transfer none): @a_string
+ */
+dap_string_t* dap_string_erase(dap_string_t *string, ssize_t pos, ssize_t len)
+{
+    dap_return_val_if_fail(string != NULL, NULL);
+    dap_return_val_if_fail(pos >= 0, string);
+    dap_return_val_if_fail((size_t )pos <= string->len, string);
+
+    if(len < 0)
+        len = string->len - pos;
+    else
+    {
+        dap_return_val_if_fail((size_t )(pos + len) <= string->len, string);
+
+        if((size_t) (pos + len) < string->len)
+            memmove(string->str + pos, string->str + pos + len, string->len - (pos + len));
+    }
+
+    string->len -= len;
+
+    string->str[string->len] = 0;
+
+    return string;
+}
+
+/**
+ * dap_string_down:
+ * @a_string: a #dap_string_t
+ *
+ * Converts a #dap_string_t to lowercase.
+ *
+ * Returns: (transfer none): the #dap_string_t
+ *
+ * Deprecated:2.2: This function uses the locale-specific
+ *     tolower() function, which is almost never the right thing.
+ *     Use dap_string_ascii_down() or dap_utf8_strdown() instead.
+ */
+dap_string_t* dap_string_down(dap_string_t *string)
+{
+    uint8_t *s;
+    long n;
+
+    dap_return_val_if_fail(string != NULL, NULL);
+
+    n = string->len;
+    s = (uint8_t *) string->str;
+
+    while(n)
+    {
+        if(isupper(*s))
+            *s = tolower(*s);
+        s++;
+        n--;
+    }
+
+    return string;
+}
+
+/**
+ * dap_string_up:
+ * @a_string: a #dap_string_t
+ *
+ * Converts a #dap_string_t to uppercase.
+ *
+ * Returns: (transfer none): @a_string
+ *
+ * Deprecated:2.2: This function uses the locale-specific
+ *     toupper() function, which is almost never the right thing.
+ *     Use dap_string_ascii_up() or dap_utf8_strup() instead.
+ */
+dap_string_t* dap_string_up(dap_string_t *string)
+{
+    uint8_t *s;
+    long n;
+
+    dap_return_val_if_fail(string != NULL, NULL);
+
+    n = string->len;
+    s = (uint8_t *) string->str;
+
+    while(n)
+    {
+        if(islower(*s))
+            *s = toupper(*s);
+        s++;
+        n--;
+    }
+
+    return string;
+}
+
+/**
+ * dap_string_append_vprintf:
+ * @a_string: a #dap_string_t
+ * @a_format: the string format. See the printf() documentation
+ * @a_args: the list of arguments to insert in the output
+ *
+ * Appends a formatted string onto the end of a #dap_string_t.
+ * This function is similar to dap_string_append_printf()
+ * except that the arguments to the format string are passed
+ * as a va_list.
+ */
+void dap_string_append_vprintf(dap_string_t *string, const char *format, va_list args)
+{
+    char *buf;
+    int len;
+
+    dap_return_if_fail(string != NULL);
+    dap_return_if_fail(format != NULL);
+
+    len = vasprintf(&buf, format, args);
+
+    if(len >= 0) {
+        dap_string_maybe_expand(string, len);
+        memcpy(string->str + string->len, buf, len + 1);
+        string->len += len;
+        DAP_DELETE(buf);
+    }
+}
+
+/**
+ * dap_string_vprintf:
+ * @a_string: a #dap_string_t
+ * @a_format: the string format. See the printf() documentation
+ * @a_args: the parameters to insert into the format string
+ *
+ * Writes a formatted string into a #dap_string_t.
+ * This function is similar to dap_string_printf() except that
+ * the arguments to the format string are passed as a va_list.
+ */
+void dap_string_vprintf(dap_string_t *string, const char *format, va_list args)
+{
+    dap_string_truncate(string, 0);
+    dap_string_append_vprintf(string, format, args);
+}
+
+/**
+ * dap_string_sprintf:
+ * @a_string: a #dap_string_t
+ * @a_format: the string format. See the sprintf() documentation
+ * @...: the parameters to insert into the format string
+ *
+ * Writes a formatted string into a #dap_string_t.
+ * This is similar to the standard sprintf() function,
+ * except that the #dap_string_t buffer automatically expands
+ * to contain the results. The previous contents of the
+ * #dap_string_t are destroyed.
+ *
+ * Deprecated: This function has been renamed to dap_string_printf().
+ */
+
+/**
+ * dap_string_printf:
+ * @a_string: a #dap_string_t
+ * @a_format: the string format. See the printf() documentation
+ * @...: the parameters to insert into the format string
+ *
+ * Writes a formatted string into a #dap_string_t.
+ * This is similar to the standard sprintf() function,
+ * except that the #dap_string_t buffer automatically expands
+ * to contain the results. The previous contents of the
+ * #dap_string_t are destroyed.
+ */
+void dap_string_printf(dap_string_t *string, const char *format, ...)
+{
+    va_list args;
+
+    dap_string_truncate(string, 0);
+
+    va_start(args, format);
+    dap_string_append_vprintf(string, format, args);
+    va_end(args);
+}
+
+/**
+ * dap_string_append_printf:
+ * @a_string: a #dap_string_t
+ * @a_format: the string format. See the printf() documentation
+ * @...: the parameters to insert into the format string
+ *
+ * Appends a formatted string onto the end of a #dap_string_t.
+ * This function is similar to dap_string_printf() except
+ * that the text is appended to the #dap_string_t.
+ */
+void dap_string_append_printf(dap_string_t *string, const char *format, ...)
+{
+    va_list args;
+
+    va_start(args, format);
+    dap_string_append_vprintf(string, format, args);
+    va_end(args);
+}
diff --git a/core/dap_string.h b/core/dap_string.h
new file mode 100755
index 0000000000000000000000000000000000000000..6519f6acd17d04f18d8a62590a7647d60760f706
--- /dev/null
+++ b/core/dap_string.h
@@ -0,0 +1,93 @@
+// dap_string_t is an object that handles the memory management of a C string for you.
+
+#ifndef __DAP_STRING_H__
+#define __DAP_STRING_H__
+
+#include <stdbool.h>
+#include <stdio.h>
+
+typedef struct _dap_string dap_string_t;
+
+struct _dap_string
+{
+    char *str;
+    size_t len;
+    size_t allocated_len;
+};
+
+dap_string_t* dap_string_new(const char *init);
+dap_string_t* dap_string_new_len(const char *init, ssize_t len);
+dap_string_t * dap_string_sized_new(size_t a_dfl_size);
+char* dap_string_free(dap_string_t *string, bool free_segment);
+
+bool dap_string_equal(const dap_string_t *v, const dap_string_t *v2);
+
+unsigned int dap_string_hash(const dap_string_t *str);
+
+dap_string_t* dap_string_assign(dap_string_t *string, const char *rval);
+
+dap_string_t* dap_string_truncate(dap_string_t *string, size_t len);
+
+dap_string_t* dap_string_set_size(dap_string_t *string, size_t len);
+
+dap_string_t* dap_string_insert_len(dap_string_t *string, ssize_t pos, const char *val, ssize_t len);
+
+dap_string_t* dap_string_append(dap_string_t *string, const char *val);
+
+dap_string_t* dap_string_append_len(dap_string_t *string, const char *val, ssize_t len);
+
+dap_string_t* dap_string_append_c(dap_string_t *string, char a_c);
+
+dap_string_t* dap_string_append_unichar(dap_string_t *string, uint32_t wc);
+
+dap_string_t* dap_string_prepend(dap_string_t *string, const char *val);
+
+dap_string_t* dap_string_prepend_c(dap_string_t *string, char a_c);
+
+dap_string_t* dap_string_prepend_unichar(dap_string_t *string, uint32_t wc);
+
+dap_string_t* dap_string_prepend_len(dap_string_t *string, const char *val, ssize_t len);
+
+dap_string_t* dap_string_insert(dap_string_t *string, ssize_t pos, const char *val);
+
+dap_string_t* dap_string_insert_c(dap_string_t *string, ssize_t pos, char a_c);
+
+dap_string_t* dap_string_insert_unichar(dap_string_t *string, ssize_t pos, uint32_t wc);
+
+dap_string_t* dap_string_overwrite(dap_string_t *string, size_t pos, const char *val);
+
+dap_string_t* dap_string_overwrite_len(dap_string_t *string, size_t pos, const char *val, ssize_t len);
+
+dap_string_t* dap_string_erase(dap_string_t *string, ssize_t pos, ssize_t len);
+
+void dap_string_vprintf(dap_string_t *string, const char *format, va_list args);
+void dap_string_printf(dap_string_t *string, const char *format, ...);
+void dap_string_append_vprintf(dap_string_t *string, const char *format, va_list args);
+void dap_string_append_printf(dap_string_t *string, const char *format, ...);
+
+/* -- optimize dap_strig_append_c --- */
+#ifdef G_CAN_INLINE
+static inline dap_string_t* dap_string_append_c_inline(dap_string_t *a_string, char a_c)
+{
+    if(a_string->len + 1 < a_string->allocated_len)
+    {
+        a_string->str[a_string->len++] = a_c;
+        a_string->str[a_string->len] = 0;
+    }
+    else
+    dap_string_insert_c(a_string, -1, a_c);
+    return a_string;
+}
+#define dap_string_append_c(a_string,a_c)       dap_string_append_c_inline (a_string, a_c)
+#endif /* G_CAN_INLINE */
+
+dap_string_t *dap_string_down(dap_string_t *string);
+
+dap_string_t *dap_string_up(dap_string_t *string);
+
+#ifndef G_DISABLE_DEPRECATED
+#define  dap_string_sprintf  dap_string_printf
+#define  dap_string_sprintfa dap_string_append_printf
+#endif
+
+#endif /* __DAP_STRING_H__ */
diff --git a/core/file_utils.c b/core/file_utils.c
deleted file mode 100644
index 7d2358d2460dfa27c5d8bc20f0966c428f495860..0000000000000000000000000000000000000000
--- a/core/file_utils.c
+++ /dev/null
@@ -1,148 +0,0 @@
-/*
- * Authors:
- * Aleksandr Lysikov <alexander.lysikov@demlabs.net>
- * DeM Labs Inc.   https://demlabs.net
- * Kelvin Project https://github.com/kelvinblockchain
- * Copyright  (c) 2017-2018
- * All rights reserved.
-
- This file is part of DAP (Deus Applications Prototypes) the open source project
-
- DAP (Deus Applicaions Prototypes) is free software: you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
-
- DAP is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with any DAP based project.  If not, see <http://www.gnu.org/licenses/>.
- */
-
-#include <stdlib.h>
-#include <stdint.h>
-#include <errno.h>
-#if (OS_TARGET == OS_MACOS)
-    #include <stdio.h>
-#else
-    #include <malloc.h>
-#endif
-#include <string.h>
-#include <sys/stat.h>
-#include "file_utils.h"
-
-#ifdef _WIN32
-#include <windows.h>
-#include <io.h>
-#define DIR_SEPARATOR '\\'
-#else
-#define DIR_SEPARATOR '/'
-#endif
-#define IS_DIR_SEPARATOR(c) ((c) == DIR_SEPARATOR || (c) == '/')
-
-/**
- * Check the directory path for unsupported symbols
- *
- * @string
- * @return true, if the directory path contains only ASCII symbols
- */
-bool valid_ascii_symbols(const char *string)
-{
-    if(!string)
-        return true;
-    for(size_t i = 0; i < strlen(string); i++) {
-        if((uint8_t) string[i] > 0x7f)
-            return false;
-    }
-    return true;
-}
-
-/**
- * Check the directory for exists
- *
- * @dir_path directory pathname
- * @return true, if the file is a directory
- */
-bool dir_test(const char * dir_path)
-{
-    if(!dir_path)
-        return false;
-#ifdef _WIN32
-    int attr = GetFileAttributesA(dir_path);
-    if(attr != -1 && (attr & FILE_ATTRIBUTE_DIRECTORY))
-        return true;
-#else
-    struct stat st;
-    if (!stat(dir_path, &st)) {
-        if (S_ISDIR(st.st_mode))
-        return true;
-    }
-#endif
-    return false;
-}
-
-/**
- * Create a new directory with intermediate sub-directories
- *
- * @dir_path new directory pathname
- * @return 0, if the directory was created or already exist, else -1
- */
-int mkdir_with_parents(const char *dir_path)
-{
-
-    char *path, *p;
-    // validation of a pointer
-    if(dir_path == NULL || dir_path[0] == '\0') {
-        errno = EINVAL;
-        return -1;
-    }
-    path = strdup(dir_path);
-    // skip the root component if it is present, i.e. the "/" in Unix or "C:\" in Windows
-#ifdef _WIN32
-    if(((path[0] >= 'a' && path[0] <= 'z') || (path[0] >= 'A' && path[0] <= 'Z'))
-            && (path[1] == ':') && IS_DIR_SEPARATOR(path[2])) {
-        p = path + 3;
-    }
-#else
-        if (IS_DIR_SEPARATOR(path[0])) {
-            p = path + 1;
-        }
-#endif
-        else
-        p = path;
-
-    do {
-        while(*p && !IS_DIR_SEPARATOR(*p))
-            p++;
-
-        if(!*p)
-            p = NULL;
-        else
-            *p = '\0';
-
-        if(!dir_test(path)) {
-#ifdef _WIN32
-            int result = mkdir(path);
-#else
-            int result = mkdir(path, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
-#endif
-            if(result == -1) {
-                free(path);
-                errno = ENOTDIR;
-                return -1;
-            }
-        }
-        if(p) {
-            *p++ = DIR_SEPARATOR;
-            while(*p && IS_DIR_SEPARATOR(*p))
-                p++;
-        }
-    } while(p);
-
-    free(path);
-    return 0;
-}
-
diff --git a/crypto/iaes/iaes256_cbc_cernal.c b/crypto/iaes/iaes256_cbc_cernal.c
index a5989857b1553c8c5a7817564ec296a6d69917f9..4163ed1c0967643d705749659555087becfa708f 100755
--- a/crypto/iaes/iaes256_cbc_cernal.c
+++ b/crypto/iaes/iaes256_cbc_cernal.c
@@ -488,15 +488,16 @@ size_t IAES_256_CBC_decrypt(const uint8_t *cdata, uint8_t *data, uint8_t *ivec,
     }
     swap_endian((uint32_t *)masterkey, IAES_KEYSIZE/sizeof(uint32_t));
 
-    size_t i,  padding = 0;
-    size_t end = length - 16 > 0 ? length - 16 : 0;
-    for(i = length-1; i >= end; i--)
-    {
-        if(data[i] == 0)
-            padding++;
-        else
-            break;
-    }
+    size_t i, padding = 0;
+    size_t end = length > 16 ? length - 16 : 0;
+    if(length>0)
+        for(i = length-1; i >= end; i--)
+        {
+            if(data[i] == 0)
+                padding++;
+            else
+                break;
+        }
 
     return length - padding;
 }
diff --git a/test/core/dap_circular_test.c b/test/core/dap_circular_test.c
index ed5cbe5230755225a757ee342064ceacd35b8bbf..2e3f108118b98362368134d302ae5cd35503f8ce 100644
--- a/test/core/dap_circular_test.c
+++ b/test/core/dap_circular_test.c
@@ -174,6 +174,7 @@ void dap_circular_load_test()
     count_writed_bytes = 4;
 
     char expectedBuffer[MAX_RESULT_BUF_LEN];
+    memset(expectedBuffer, 0, MAX_RESULT_BUF_LEN);
     circular_buffer_read(cb, count_writed_bytes, expectedBuffer);
 
     int count_write_bytes = 4;
diff --git a/test/core/dap_strfuncs_test.c b/test/core/dap_strfuncs_test.c
new file mode 100644
index 0000000000000000000000000000000000000000..964255816f8faed73d90e79d0af452d527f1990e
--- /dev/null
+++ b/test/core/dap_strfuncs_test.c
@@ -0,0 +1,145 @@
+#include "dap_common.h"
+#include "dap_strfuncs_test.h"
+#include "dap_list.h"
+#include "dap_string.h"
+
+void dap_str_dup_test(void)
+{
+    int l_a = rand(), l_b = rand();
+    const char *l_s = "test string";
+    char l_str0[1024];
+    sprintf(l_str0, "a=%d b=%d s=%s", l_a, l_b, l_s);
+    char *l_str1 = dap_strdup_printf("a=%d b=%d s=%s", l_a, l_b, l_s);
+    size_t str_size0 = strlen(l_str0);
+    size_t str_size1 = strlen(l_str1);
+
+    char *l_str2 = DAP_NEW_SIZE(char, str_size1 + 1);
+    dap_stpcpy(l_str2, l_str1);
+
+    size_t str_size2 = strlen(l_str2);
+    dap_assert_PIF(str_size0 == str_size1, "Strings sizes must be equal");
+    dap_assert_PIF(str_size1 == str_size2, "Strings sizes must be equal");
+
+    dap_assert(!strcmp(l_str0, l_str1), "Test dap_strdup_printf()");
+    dap_assert(!strcmp(l_str1, l_str2), "Test dap_stpcpy()");
+    DAP_DELETE(l_str1);
+    DAP_DELETE(l_str2);
+}
+
+void dap_str_modify_test(void)
+{
+    const char *l_s_in = "Test String";
+    const char *l_s_up_check = "TEST STRING";
+    const char *l_s_down_check = "test string";
+    char *l_s_out;
+
+    l_s_out = dap_strup(l_s_in, -1);
+    dap_assert(!strcmp(l_s_out, l_s_up_check), "Test dap_strup()");
+    DAP_DELETE(l_s_out);
+
+    l_s_out = dap_strdown(l_s_in, -1);
+    dap_assert(!strcmp(l_s_out, l_s_down_check), "Test dap_strdown()");
+    DAP_DELETE(l_s_out);
+
+    l_s_out = dap_strdup(l_s_in);
+    dap_strreverse(l_s_out);
+    dap_assert_PIF(strcmp(l_s_out, l_s_in), "String not modified");
+    dap_strreverse(l_s_out);
+    dap_assert(!strcmp(l_s_out, l_s_in), "Test dap_strreverse()");
+    DAP_DELETE(l_s_out);
+
+    l_s_out = dap_strdup_printf("      %s  ", l_s_in);
+    dap_strstrip(l_s_out);
+    dap_assert(!strcmp(l_s_out, l_s_in), "Test dap_strstrip()");
+    DAP_DELETE(l_s_out);
+}
+
+void dap_str_array_test(void)
+{
+    const char *l_s_in = "1:23:: Test:  :\n:String:";
+    char **l_s_array = dap_strsplit(l_s_in, ":", -1);
+
+    int l_count = 1;
+    char *l_s_tmp = dap_strstr_len(l_s_in, -1, ":");
+    while(l_s_tmp) {
+        l_s_tmp = dap_strstr_len(l_s_tmp + 1, -1, ":");
+        l_count++;
+    }
+
+    char **l_s_array_copy = dap_strdupv(l_s_array);
+
+    dap_assert_PIF(dap_str_countv(l_s_array) == l_count, "String split");
+    dap_assert_PIF(dap_str_countv(l_s_array_copy) == l_count, "String copy");
+    char *l_s_out = dap_strjoinv(":", l_s_array);
+    dap_assert(!strcmp(l_s_out, l_s_in), "Test string array functions");
+
+    dap_strfreev(l_s_array);
+    dap_strfreev(l_s_array_copy);
+    DAP_DELETE(l_s_out);
+}
+
+static void list_delete(void* a_pointer)
+{
+    DAP_DELETE(a_pointer);
+}
+
+void dap_list_test(void)
+{
+    dap_list_t *l_list = NULL;
+    l_list = dap_list_append(l_list, "item 1");
+    l_list = dap_list_append(l_list, "item 2");
+    l_list = dap_list_append(l_list, "item 3");
+    l_list = dap_list_prepend(l_list, "item 0");
+
+    dap_list_t *l_list_tmp = dap_list_find(l_list, "item 2");
+    unsigned int l_count = dap_list_length(l_list);
+    dap_list_remove(l_list, "item 3");
+    unsigned int l_count_after = dap_list_length(l_list);
+
+    dap_assert_PIF(l_count == 4, "Test dap_list_length()");
+    dap_assert_PIF(l_count_after == 3, "Test dap_list_remove()");
+    dap_assert_PIF(!strcmp(l_list_tmp->data, "item 2"), "Test dap_list_find()");
+    dap_list_free(l_list);
+
+    // for test dap_list_free_full()
+    l_list = NULL;
+    l_list = dap_list_append(l_list, dap_strdup("item 1"));
+    l_list = dap_list_append(l_list, dap_strdup("item 2"));
+
+    dap_assert(l_list, "Test dap_list_t");
+    dap_list_free_full(l_list, list_delete);
+}
+
+void dap_string_test(void)
+{
+    dap_string_t *l_str = dap_string_new(NULL);
+    dap_string_append(l_str, "str=string 1");
+    dap_assert_PIF(!strcmp(l_str->str, "str=string 1"), "Test dap_string_append()");
+
+    dap_string_append_printf(l_str, " int=%d", 123);
+    dap_assert_PIF(!strcmp(l_str->str, "str=string 1 int=123"), "Test dap_string_append()");
+
+    dap_string_erase(l_str, 3, 9);
+    dap_assert_PIF(!strcmp(l_str->str, "str int=123"), "Test dap_string_erase()");
+
+    dap_string_append_len(l_str, " string2", strlen(" string2"));
+    dap_assert_PIF(!strcmp(l_str->str, "str int=123 string2"), "Test dap_string_append_len()");
+
+    dap_assert(l_str, "Test dap_string_t");
+    dap_string_free(l_str, true);
+}
+
+void dap_strfuncs_tests_run(void)
+{
+    dap_print_module_name("dap_strfuncs");
+
+    dap_str_dup_test();
+    dap_str_modify_test();
+    dap_str_array_test();
+    dap_list_test();
+    dap_string_test();
+
+    dap_usleep(0.5 * DAP_USEC_PER_SEC);
+    dap_assert(1, "Test dap_usleep(0.5 sec.)");
+
+}
diff --git a/test/core/dap_strfuncs_test.h b/test/core/dap_strfuncs_test.h
new file mode 100644
index 0000000000000000000000000000000000000000..ef2e7963d900896e007b6cd96040f75e8e7ea122
--- /dev/null
+++ b/test/core/dap_strfuncs_test.h
@@ -0,0 +1,5 @@
+#pragma once
+#include "dap_test.h"
+#include "dap_strfuncs.h"
+
+extern void dap_strfuncs_tests_run(void);
diff --git a/test/core/main.c b/test/core/main.c
index 84291baf4fe4083f5b1728f2a44c791d731163ba..d4c1229dd4ba131c6be3b9f4d718b363a7ce6f98 100755
--- a/test/core/main.c
+++ b/test/core/main.c
@@ -1,12 +1,14 @@
 #include "dap_config_test.h"
 #include "dap_common_test.h"
 #include "dap_network_monitor_test.h"
+#include "dap_strfuncs_test.h"
 #include "dap_common.h"
 
 
 int main(void) {
     // switch off debug info from library
     set_log_level(L_CRITICAL);
+    dap_strfuncs_tests_run();
     dap_config_tests_run();
     dap_common_test_run();
 #ifdef __unix__
diff --git a/test/libdap-test b/test/libdap-test
index d2257789e0c796a5a3b637e14dcbaf8a8c7880cc..b76175acc517f085c319c8e66c62bd143f96bf94 160000
--- a/test/libdap-test
+++ b/test/libdap-test
@@ -1 +1 @@
-Subproject commit d2257789e0c796a5a3b637e14dcbaf8a8c7880cc
+Subproject commit b76175acc517f085c319c8e66c62bd143f96bf94