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