/* 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 <errno.h> #include "dap_common.h" #include "dap_strfuncs.h" #ifdef _WIN32 static inline char *strndup(char *str, int len) { char *buf = (char*)malloc(len + 1); memcpy(buf, str, len); buf[len] = 0; return buf; } #endif #ifdef _WIN32 char *strptime( char *buff, const char *fmt, struct tm *tm ) { uint32_t len = strlen( buff ); dap_sscanf( buff,"%u.%u.%u_%u.%u.%u",&tm->tm_year, &tm->tm_mon, &tm->tm_mday, &tm->tm_hour, &tm->tm_min, &tm->tm_sec ); tm->tm_year += 2000; return buff + len; } #endif /** * dap_strlen: * @a_str: (nullable): the string * * If @a_str is %NULL it returns 0 * * Returns: length of the string */ size_t dap_strlen(const char *a_str) { size_t l_length = 0; if(a_str) { l_length = strlen(a_str); } return l_length; } /** * dap_strcmp: * @a_str1: (nullable): the string * @a_str2: (nullable): the string * * Compare a_str1 and a_str2 */ int dap_strcmp(const char *a_str1, const char *a_str2) { if(a_str1 && a_str2) { return strcmp(a_str1, a_str2); } return -1; } /** * dap_strcmp: * @a_str1: (nullable): the string * @a_str2: (nullable): the string * * Compare a_n characters of a_str1 and a_str2 */ int dap_strncmp(const char *a_str1, const char *a_str2, size_t a_n) { if(a_str1 && a_str2) { return strncmp(a_str1, a_str2, a_n); } return -1; } /** * 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 = dap_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; } size_t dap_str_countv(char **a_str_array) { size_t 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(const 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); } } /** * 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 }