diff --git a/core/dap_strfuncs.c b/core/dap_strfuncs.c new file mode 100755 index 0000000000000000000000000000000000000000..03700ebbe5a58701ff1c5de0cf7cdf9d96fe0b8f --- /dev/null +++ b/core/dap_strfuncs.c @@ -0,0 +1,661 @@ +/* DAP String Functions */ +#ifdef _WIN32 +#define _CRT_SECURE_NO_WARNINGS +#include <windows.h> +#endif +#include <stddef.h> +#include <stdarg.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_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 when no + * longer needed. + * + * Returns: a newly-allocated string holding the result + */ +char* dap_strdup_printf(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; + size_t l_needle_len = strlen(a_needle); + const char *l_end; + size_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..5e8d471804f4ab1889be9ff9ca2b40c2aecc7050 --- /dev/null +++ b/core/dap_strfuncs.h @@ -0,0 +1,63 @@ +/* DAP String Functions */ + +#ifndef __DAP_LIB_MINI_H +#define __DAP_LIB_MINI_H + +#include <limits.h> +#include <stdbool.h> +#include <stdint.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 GUINT_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_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_LIB_MINI_H */