From 1a5bfbf13c3b435158cc895efeabece464909a68 Mon Sep 17 00:00:00 2001 From: Urban Wallasch Date: Mon, 28 Oct 2019 17:31:36 +0100 Subject: [PATCH] * str: added str_ichr, str_irchr, str_skip, str_split, str_[lr]trim, str_clean, str_trans, str_to[upper|lower] --- str/str.h | 241 +++++++++++++++++++++++++-- str/str_test.c | 437 ++++++++++++++++++++++++++++++++++++------------- 2 files changed, 557 insertions(+), 121 deletions(-) diff --git a/str/str.h b/str/str.h index db350ec..04ce878 100644 --- a/str/str.h +++ b/str/str.h @@ -9,7 +9,8 @@ * str_icmp, * str_nicmp - compare two strings ignoring case * - * str_delim - locate the first occurrence of a delimiter + * str_ichr, + * str_irchr - locate character in string ignoring case * * str_istr - locate a substring ignoring case * @@ -17,6 +18,23 @@ * mem_str, * mem_istr - locate a pattern in memory * + * str_delim - locate the first occurrence of a delimiter in a string + + * str_skip - skip initial sequence of specified characters in a string + + * str_split - break down a string into separate parts + * + * str_ltrim, + * str_rtrim, + * str_trim - remove sequences of characters from the ends of a string + * + * str_clean - remove unwanted characters from string + * + * str_trans - replace characters in string + * + * str_toupper, + * str_tolower - convert case of characters in string + * */ #ifndef STR_H_ @@ -28,6 +46,7 @@ extern "C" { #include #include +#include /* @@ -67,19 +86,34 @@ static inline int str_nicmp(const char *s1, const char *s2, size_t n) { /* - * str_delim - locate the first occurrence of a delimiter + * str_ichr, str_irchr - locate character in string ignoring case * - * The str_delim() function finds the first occurrence in the string s - * of one of the characters in the string delim. + * The str_ichr() function looks for the first occurrence of the character + * c in the string s, ignoring the case of both s and c. The str_irchr() + * function is similar, but looks for the last occurrence of c in s. * - * The function returns a pointer to the first occurrence of one of the - * specified delimiters, or a pointer to the terminating null character - * at the end of s, if no such occurrence was found. + * The str_ichr() and str_irchr() functions return a pointer to the + * matched character, or NULL if the character is not found. The + * terminating null byte is considered part of the string, so that if c + * is specified as '\0', these function return a pointer to the terminator. */ -static inline char *str_delim(const char *s, const char *delim) { - while (!strchr(delim, *s)) - ++s; - return (char *)s; +static inline char *str_ichr(const char *s, int c) { + const unsigned char lc = tolower((unsigned char)c); + do { + if (tolower((unsigned char)*s) == lc) + return (char *)s; + } while ( *s++ ); + return NULL; +} + +static inline char *str_irchr(const char *s, int c) { + const unsigned char lc = tolower((unsigned char)c); + const char *last = NULL; + do { + if (tolower((unsigned char)*s) == lc) + last = s; + } while ( *s++ ); + return (char *)last; } @@ -153,6 +187,191 @@ static inline void *mem_istr(const void *haystack, size_t h, const char *needle) return NULL; } + +/* + * str_delim - locate the first occurrence of a delimiter + * + * The str_delim() function finds the first occurrence in the string s + * of one of the characters found in dset, or not found in dset, depending + * on the value of notin. + * + * The function returns a pointer to the first occurrence of one of the + * specified delimiters, or a pointer to the terminating null character + * at the end of s, if no such occurrence was found. + */ +static inline char *str_delim(const char *s, const char *dset, bool notin) { + while (*s && (!strchr(dset, *s) ^ notin)) + ++s; + return (char *)s; +} + + +/* + * str_skip - skip initial sequence of characters + * + * The str_skip() function skips any initial sequence in s that consists + * entirely of characters found in skipset, or not found in skipset, + * depending on the value of notin. + * + * The function returns a pointer to the first occurrence of a character + * not in skipset, or a pointer to the terminating null character at the + * end of s, if no such character could be found. + */ +static inline char *str_skip(const char *s, const char *skipset, bool notin) { + while (*s && (!!strchr(skipset, *s) ^ notin)) + ++s; + return (char *)s; +} + + +/* + * str_split - break down a string into separate parts + * + * The str_split() function looks for the first occurrence in the string + * s of one of the characters in the string sepset. If len is not NULL, + * it is used to store the length of the initial sequence of characters + * leading up to this first separator. If the STR_SPLIT_DESTR flag is + * set, this separator is overwritten with a zero byte. If the + * STR_SPLIT_FOLD flag is set, sequences consisting of only separators + * are treated as a single separator. + * + * The str_split() function returns a pointer to the initial sequence, or + * a pointer to the terminating null character at the end of s. If len + * is not NULL it is used to store the length of this sequence up to the + * first separator. If next is not NULL, it is used to store a pointer + * to the next character after the first separator. This pointer can be + * used in subsequent invocations of str_split() as the first argument + * to successively traverse the original input sting in its entirety. + * + * CAVEAT: + * When the STR_SPLIT_DESTR flag is set, the str_split function modifies + * the string pointed to by the first argument. + * + * EXAMPLE: + * A very simplistic word extractor might look like this: + * + * char input[] = ">> Hello, world! How are you? <<"; + * char *word, *next = input; + * size_t len; + * while (*next) { + * word = str_split(next, " .,!?><", &len, &next, + * STR_SPLIT_FOLD | STR_SPLIT_DESTR); + * printf("'%s' (%zu)\n", word, len); + * } + * + */ +#define STR_SPLIT_FOLD 0x1U +#define STR_SPLIT_DESTR 0x2U + +static inline char *str_split(const char *s, const char *sepset, + size_t *len, char **next, unsigned flags) { + char *p; + + if (flags & STR_SPLIT_FOLD) + s = str_skip(s, sepset, 0); + p = str_delim(s, sepset, 0); + if (len) + *len = (size_t)(p - s); + if (*p) { + if ((flags & STR_SPLIT_DESTR)) + *p = '\0'; + ++p; + } + if (next) + *next = p; + return (char *)s; +} + + +/* + * str_ltrim, str_rtrim, str_trim - + * remove unwanted sequences of characters from the ends of a string + * + * The str_ltrim() function removes from the string s any initial sequence + * that consists entirely of characters found in trimset, or not found + * in trimset dset, depending on the value of notin. The str_rtrim() + * function is similar, except it removes any such sequence from the end + * of the string. The str_trim() function performs both operations back + * to back. + * + * The functions each return a pointer to the modified string. + */ +static inline char *str_ltrim(char *s, const char *trimset, bool notin) { + char *ls = str_skip(s, trimset, notin); + return memmove(s, ls, strlen(ls) + 1); +} + +static inline char *str_rtrim(char *s, const char *trimset, bool notin) { + *(str_delim(s, trimset, notin)) = '\0'; + return s; +} + +static inline char *str_trim(char *s, const char *trimset, bool notin) { + return str_rtrim(str_ltrim(s, trimset, notin), trimset, notin); +} + + +/* + * str_clean - remove unwanted characters from string + * + * The str_clean() function removes from the string s any initial sequence + * that consists entirely of characters found in delset, or not found in + * delset, depending on the value of notin. + * + * The function returns a pointer to the modified string. + */ +static inline char *str_clean(char *s, const char *delset, bool notin) { + char *cs = s; + for (char *p = cs; *p; ++p ) + if ( !strchr(delset, *p) ^ notin ) + *cs++ = *p; + *cs = '\0'; + return s; +} + + +/* + * str_trans - replace characters in string + * + * The str_trans() function replaces any occurrence in string s of a + * chracter that is present in set1 with the corresponding character + * from set2. + * + * CAVEAT: + * If set2 is shorter than set1, the behavior is undefined. + */ +static inline char *str_trans(char *s, const char *set1, const char *set2) { + for (char *cs = s; *cs; ++cs) { + char *match; + match = strchr(set1, *cs); + if (match) + *cs = set2[match - set1]; + } + return s; +} + + +/* + * str_toupper, str_tolower - convert case of characters in string + * + * The str_toupper() function replaces any character in s for which + * isupper() is true with its lowercase counterpart. The str_tolower() + * function replaces any character in s for which islower() is true with + * its lowercase counterpart. + */ +static inline char *str_toupper(char *s) { + for (unsigned char *cs = (unsigned char *)s; *cs; ++cs) + *cs = toupper(*cs); + return s; +} + +static inline char *str_tolower(char *s) { + for (unsigned char *cs = (unsigned char *)s; *cs; ++cs) + *cs = tolower(*cs); + return s; +} + + #ifdef cplusplus } #endif diff --git a/str/str_test.c b/str/str_test.c index f58e6bc..ca33ed5 100644 --- a/str/str_test.c +++ b/str/str_test.c @@ -12,6 +12,7 @@ #include #include +#include #include #include "str.h" @@ -20,7 +21,7 @@ #define H(a_) fprintf(stderr, "\n%s\n", a_) #define D(s_) do{ \ - fprintf(stderr, ". %s = '", #s_); \ + fprintf(stderr, ". %s = \"", #s_); \ for (int i = 0; i < (int)sizeof(s_); ++i) { \ if (s_[i] == 0) \ fprintf(stderr, "\\0"); \ @@ -29,7 +30,7 @@ else \ fputc(s_[i], stderr); \ } \ - fprintf(stderr, "'\n"); \ + fprintf(stderr, "\"\n"); \ }while(0) \ @@ -41,114 +42,330 @@ int main(void) { - H("str_icmp"); - T(str_icmp("asdf", "") > 0); - T(str_icmp("", "asdf") < 0); - T(str_icmp("asdf", "b") < 0); - T(str_icmp("b", "asdf") > 0); - T(str_icmp("asdfまほ", "asdfまほ") == 0); - T(str_icmp("Asdf", "asdf") == 0); - T(str_icmp("まほaSdf", "まほasdF") == 0); - T(str_icmp("asdf", "asdF") == 0); - - H("str_nicmp"); - T(str_nicmp(NULL, NULL, 0) == 0); - T(str_nicmp(NULL, "x", 0) == 0); - T(str_nicmp("x", NULL, 0) == 0); - T(str_nicmp("asdf", NULL, 0) == 0); - T(str_nicmp("asdf", NULL, 0) == 0); - T(str_nicmp("asdf", "", 0) == 0); - T(str_nicmp("asdf", "a", 1) == 0); - T(str_nicmp("a", "asdf", 1) == 0); - T(str_nicmp("asdf", "ab", 1) == 0); - T(str_nicmp("ab", "asdf", 1) == 0); - T(str_nicmp("asdf", "ab", 2) > 0); - T(str_nicmp("ab", "asdf", 2) < 0); - T(str_nicmp("asdfまほ", "asdまほ", 1) == 0); - T(str_nicmp("Asdf", "asdf", 1) == 0); - T(str_nicmp("まほaSdf", "まほasdF", 1) == 0); - T(str_nicmp("asdf", "asdF", 1) == 0); - - H("str_delim"); - char s1[] = "abc:def,\r\n\tghi"; - D(s1); - T(str_delim(s1, "") - s1 == (int)strlen(s1)); - T(str_delim(s1, "*") - s1 == (int)strlen(s1)); - T(str_delim(s1, "i") - s1 == (int)strlen(s1)-1); - T(str_delim(s1, ":") - s1 == 3); - T(str_delim(s1, "-_:") - s1 == 3); - T(str_delim(s1, "e,") - s1 == 5); - T(str_delim(s1, ",#'") - s1 == 7); - T(str_delim(s1, "\n\r") - s1 == 8); - - H("str_istr"); - char h1[] = "asdfま\0ほyxc"; - char h2[] = ""; - D(h1); - D(h2); - T(str_istr(NULL, NULL) == NULL); - T(str_istr(h2, NULL) == h2); - T(str_istr(NULL, "") == NULL); - T(str_istr(h2, "") == h2); - T(str_istr(h1, "") == h1); - T(str_istr(h2, "\0\0") == h2); - T(str_istr("", "a") == NULL); - T(str_istr(h1, "xoxoxoxo") == NULL); - T(str_istr(h1, "S") == h1 + 1); - T(str_istr(h1, "as") == h1); - T(str_istr(h1, "df") == h1 + 2); - T(str_istr(h1, "As") == h1); - T(str_istr(h1, "dF") == h1 + 2); - T(str_istr(h1, "X") == NULL); - T(str_istr(h1, "yxc") == NULL); - - H("mem_mem"); - char m1[] = { 1,'s','d','f', 0,'y','x','c', -1 }; - char m2[] = { 'a','b','c', 0 }; - D(m1); - D(m2); - T(mem_mem(NULL, 0, NULL, 0) == NULL); - T(mem_mem(m2, 0, NULL, 0) == m2); - T(mem_mem(m2, 0, "", 0) == m2); - T(mem_mem(NULL, 0, m2, 0) == NULL); - T(mem_mem(NULL, 0, "", 0) == NULL); - T(mem_mem(m2, 3, "abc", 3) == m2); - T(mem_mem(m2, 4, "abc", 4) == m2); - T(mem_mem(m1, sizeof m1, "", 0) == m1); - T(mem_mem(m1, sizeof m1, NULL, 0) == m1); - T(mem_mem(m1, sizeof m1, "\x01s", 2) == m1); - T(mem_mem(m1, sizeof m1, "f\0y", 3) == m1 + 3); - T(mem_mem(m1, sizeof m1, "f\0y" + 1, 2) == m1 + 4); - T(mem_mem(m1, sizeof m1, "f\0Y" + 1, 2) == NULL); - T(mem_mem(m1, sizeof m1, (char[]){-1}, 1) == m1 + 8); - T(mem_mem(m1, sizeof m1, "xc", 3) == NULL); - T(mem_mem(m1, sizeof m1, "xc", 2) == m1 + 6); - - H("mem_str"); - D(m1); - T(mem_str(NULL, 0, NULL) == NULL); - T(mem_str(NULL, 0, "") == NULL); - T(mem_str(m1, 0, NULL) == m1); - T(mem_str(m1, 0, "") == m1); - T(mem_str(m1, sizeof m1, "") == m1); - T(mem_str(m1, sizeof m1, NULL) == m1); - T(mem_str(m1, sizeof m1, "sdf") == m1 + 1); - T(mem_str(m1, sizeof m1, "yxc") == m1 + 5); - T(mem_str(m1, sizeof m1, "yxc\0qwe") == m1 + 5); - T(mem_str(m1, sizeof m1, "yXc\0qwe") == NULL); - - H("mem_istr"); - D(m1); - T(mem_istr(NULL, 0, NULL) == NULL); - T(mem_istr(NULL, 0, "") == NULL); - T(mem_istr(m1, 0, NULL) == m1); - T(mem_istr(m1, 0, "") == m1); - T(mem_istr(m1, sizeof m1, "") == m1); - T(mem_istr(m1, sizeof m1, NULL) == m1); - T(mem_istr(m1, sizeof m1, "") == m1); - T(mem_istr(m1, sizeof m1, "sDf") == m1 + 1); - T(mem_istr(m1, sizeof m1, "Yxc") == m1 + 5); - T(mem_istr(m1, sizeof m1, "yxC\0qwe") == m1 + 5); + /*****************************************/ + + { + H("str_icmp"); + T(str_icmp("asdf", "") > 0); + T(str_icmp("", "asdf") < 0); + T(str_icmp("asdf", "b") < 0); + T(str_icmp("b", "asdf") > 0); + T(str_icmp("asdfまほ", "asdfまほ") == 0); + T(str_icmp("Asdf", "asdf") == 0); + T(str_icmp("まほaSdf", "まほasdF") == 0); + T(str_icmp("asdf", "asdF") == 0); + } + + /*****************************************/ + + { + H("str_nicmp"); + T(str_nicmp(NULL, NULL, 0) == 0); + T(str_nicmp(NULL, "x", 0) == 0); + T(str_nicmp("x", NULL, 0) == 0); + T(str_nicmp("asdf", NULL, 0) == 0); + T(str_nicmp("asdf", NULL, 0) == 0); + T(str_nicmp("asdf", "", 0) == 0); + T(str_nicmp("asdf", "a", 1) == 0); + T(str_nicmp("a", "asdf", 1) == 0); + T(str_nicmp("asdf", "ab", 1) == 0); + T(str_nicmp("ab", "asdf", 1) == 0); + T(str_nicmp("asdf", "ab", 2) > 0); + T(str_nicmp("ab", "asdf", 2) < 0); + T(str_nicmp("asdfまほ", "asdまほ", 1) == 0); + T(str_nicmp("Asdf", "asdf", 1) == 0); + T(str_nicmp("まほaSdf", "まほasdF", 1) == 0); + T(str_nicmp("asdf", "asdF", 1) == 0); + } + + /*****************************************/ + + { + H("str_ichr, str_irchr"); + const char s[] = "abcdABCD"; + D(s); + T(str_ichr(s, 'a') - s == 0); + T(str_ichr(s, 'A') - s == 0); + T(str_ichr(s, 'b') - s == 1); + T(str_ichr(s, 'B') - s == 1); + T(str_irchr(s, 'a') - s == 4); + T(str_irchr(s, 'A') - s == 4); + T(str_irchr(s, 'd') - s == 7); + T(str_irchr(s, 'D') - s == 7); + } + + /*****************************************/ + + { + H("str_istr"); + char h1[] = "asdfま\0ほyxc"; + char h2[] = ""; + D(h1); + D(h2); + T(str_istr(NULL, NULL) == NULL); + T(str_istr(h2, NULL) == h2); + T(str_istr(NULL, "") == NULL); + T(str_istr(h2, "") == h2); + T(str_istr(h1, "") == h1); + T(str_istr(h2, "\0\0") == h2); + T(str_istr("", "a") == NULL); + T(str_istr(h1, "xoxoxoxo") == NULL); + T(str_istr(h1, "S") == h1 + 1); + T(str_istr(h1, "as") == h1); + T(str_istr(h1, "df") == h1 + 2); + T(str_istr(h1, "As") == h1); + T(str_istr(h1, "dF") == h1 + 2); + T(str_istr(h1, "X") == NULL); + T(str_istr(h1, "yxc") == NULL); + } + + /*****************************************/ + + { + H("mem_mem"); + char m1[] = { 1,'s','d','f', 0,'y','x','c', -1 }; + char m2[] = { 'a','b','c', 0 }; + D(m1); + D(m2); + T(mem_mem(NULL, 0, NULL, 0) == NULL); + T(mem_mem(m2, 0, NULL, 0) == m2); + T(mem_mem(m2, 0, "", 0) == m2); + T(mem_mem(NULL, 0, m2, 0) == NULL); + T(mem_mem(NULL, 0, "", 0) == NULL); + T(mem_mem(m2, 3, "abc", 3) == m2); + T(mem_mem(m2, 4, "abc", 4) == m2); + T(mem_mem(m1, sizeof m1, "", 0) == m1); + T(mem_mem(m1, sizeof m1, NULL, 0) == m1); + T(mem_mem(m1, sizeof m1, "\x01s", 2) == m1); + T(mem_mem(m1, sizeof m1, "f\0y", 3) == m1 + 3); + T(mem_mem(m1, sizeof m1, "f\0y" + 1, 2) == m1 + 4); + T(mem_mem(m1, sizeof m1, "f\0Y" + 1, 2) == NULL); + T(mem_mem(m1, sizeof m1, (char[]){-1}, 1) == m1 + 8); + T(mem_mem(m1, sizeof m1, "xc", 3) == NULL); + T(mem_mem(m1, sizeof m1, "xc", 2) == m1 + 6); + } + + /*****************************************/ + + { + H("mem_str"); + char m1[] = { 1,'s','d','f', 0,'y','x','c', -1 }; + D(m1); + T(mem_str(NULL, 0, NULL) == NULL); + T(mem_str(NULL, 0, "") == NULL); + T(mem_str(m1, 0, NULL) == m1); + T(mem_str(m1, 0, "") == m1); + T(mem_str(m1, sizeof m1, "") == m1); + T(mem_str(m1, sizeof m1, NULL) == m1); + T(mem_str(m1, sizeof m1, "sdf") == m1 + 1); + T(mem_str(m1, sizeof m1, "yxc") == m1 + 5); + T(mem_str(m1, sizeof m1, "yxc\0qwe") == m1 + 5); + T(mem_str(m1, sizeof m1, "yXc\0qwe") == NULL); + } + + /*****************************************/ + + { + H("mem_istr"); + char m1[] = { 1,'s','d','f', 0,'y','x','c', -1 }; + D(m1); + T(mem_istr(NULL, 0, NULL) == NULL); + T(mem_istr(NULL, 0, "") == NULL); + T(mem_istr(m1, 0, NULL) == m1); + T(mem_istr(m1, 0, "") == m1); + T(mem_istr(m1, sizeof m1, "") == m1); + T(mem_istr(m1, sizeof m1, NULL) == m1); + T(mem_istr(m1, sizeof m1, "") == m1); + T(mem_istr(m1, sizeof m1, "sDf") == m1 + 1); + T(mem_istr(m1, sizeof m1, "Yxc") == m1 + 5); + T(mem_istr(m1, sizeof m1, "yxC\0qwe") == m1 + 5); + } + + /*****************************************/ + + { + H("str_delim"); + char s1[] = "abc:def,\r\n\tghi"; + D(s1); + T(str_delim(s1, "", false) - s1 == (int)strlen(s1)); + T(str_delim(s1, "*", false) - s1 == (int)strlen(s1)); + T(str_delim(s1, "i", false) - s1 == (int)strlen(s1)-1); + T(str_delim(s1, "i", true) - s1 == 0); + T(str_delim(s1, ":", false) - s1 == 3); + T(str_delim(s1, "abc", true) - s1 == 3); + T(str_delim(s1, "-_:", false) - s1 == 3); + T(str_delim(s1, "e,", false) - s1 == 5); + T(str_delim(s1, ",#'", false) - s1 == 7); + T(str_delim(s1, "\n\r", false) - s1 == 8); + } + + /*****************************************/ + + { + H("str_split"); + char in1[] = ">> Hello, world! How are you? <<"; + char d1[] = " .,!?<>"; + D(in1); + D(d1); + char *tok, *next = in1; + tok = str_split(next, d1, NULL, &next, STR_SPLIT_FOLD|STR_SPLIT_DESTR); + D(in1); + T(strcmp(tok, "Hello") == 0); + tok = str_split(next, d1, NULL, &next, STR_SPLIT_FOLD|STR_SPLIT_DESTR); + D(in1); + T(strcmp(tok, "world") == 0); + tok = str_split(next, d1, NULL, &next, STR_SPLIT_FOLD|STR_SPLIT_DESTR); + D(in1); + T(strcmp(tok, "How") == 0); + tok = str_split(next, d1, NULL, &next, STR_SPLIT_FOLD|STR_SPLIT_DESTR); + D(in1); + T(strcmp(tok, "are") == 0); + tok = str_split(next, d1, NULL, &next, STR_SPLIT_FOLD|STR_SPLIT_DESTR); + D(in1); + T(strcmp(tok, "you") == 0); + tok = str_split(next, d1, NULL, &next, STR_SPLIT_FOLD|STR_SPLIT_DESTR); + D(in1); + T(strcmp(tok, "") == 0); + + char in2[] = ">> Hello, world! How are you? <<"; + char d2[] = ""; + D(in2); + D(d2); + next = in2; + tok = str_split(next, d2, NULL, &next, STR_SPLIT_FOLD|STR_SPLIT_DESTR); + T(strcmp(tok, in2) == 0); + + char in3[] = ">> Hello, world! How are you? <<"; + char d3[] = " "; + D(in3); + D(d3); + next = in3; + tok = str_split(next, d3, NULL, &next, STR_SPLIT_FOLD|STR_SPLIT_DESTR); + T(strcmp(tok, ">>") == 0); + tok = str_split(next, d3, NULL, &next, STR_SPLIT_FOLD|STR_SPLIT_DESTR); + T(strcmp(tok, "Hello,") == 0); + tok = str_split(next, d3, NULL, &next, STR_SPLIT_FOLD|STR_SPLIT_DESTR); + T(strcmp(tok, "world!") == 0); + tok = str_split(next, d3, NULL, &next, STR_SPLIT_FOLD|STR_SPLIT_DESTR); + T(strcmp(tok, "How") == 0); + tok = str_split(next, d3, NULL, &next, STR_SPLIT_FOLD|STR_SPLIT_DESTR); + T(strcmp(tok, "are") == 0); + tok = str_split(next, d3, NULL, &next, STR_SPLIT_FOLD|STR_SPLIT_DESTR); + T(strcmp(tok, "you?") == 0); + tok = str_split(next, d3, NULL, &next, STR_SPLIT_FOLD|STR_SPLIT_DESTR); + T(strcmp(tok, "<<") == 0); + tok = str_split(next, d3, NULL, &next, STR_SPLIT_FOLD|STR_SPLIT_DESTR); + T(strcmp(tok, "") == 0); + + char in4[] = ",,,0,1,,pi,"; + char d4[] = ","; + D(in4); + D(d4); + next = in4; + tok = str_split(next, d4, NULL, &next, STR_SPLIT_DESTR); + T(strcmp(tok, "") == 0); + tok = str_split(next, d4, NULL, &next, STR_SPLIT_DESTR); + T(strcmp(tok, "") == 0); + tok = str_split(next, d4, NULL, &next, STR_SPLIT_DESTR); + T(strcmp(tok, "") == 0); + tok = str_split(next, d4, NULL, &next, STR_SPLIT_DESTR); + T(strcmp(tok, "0") == 0); + tok = str_split(next, d4, NULL, &next, STR_SPLIT_DESTR); + T(strcmp(tok, "1") == 0); + tok = str_split(next, d4, NULL, &next, STR_SPLIT_DESTR); + T(strcmp(tok, "") == 0); + tok = str_split(next, d4, NULL, &next, STR_SPLIT_DESTR); + T(strcmp(tok, "pi") == 0); + T(tok != next); + tok = str_split(next, d4, NULL, &next, STR_SPLIT_DESTR); + T(strcmp(tok, "") == 0); + T(tok == next); + + size_t len; + char in5[] = "fourty;;two"; + char d5[] = ";"; + D(in5); + D(d5); + next = in5; + tok = str_split(next, d5, &len, &next, 0); + T(strncmp(tok, "fourty", len) == 0 && len == 6); + tok = str_split(next, d5, &len, &next, 0); + T(strncmp(tok, "", len) == 0 && len == 0); + tok = str_split(next, d5, &len, &next, 0); + T(strncmp(tok, "two", len) == 0 && len == 3); + tok = str_split(next, d5, &len, &next, 0); + T(strncmp(tok, "", len) == 0 && len == 0); + T(tok == next && len == 0); + } + + /*****************************************/ + + { + H("str_ltrim, str_rtrim, str_trim"); + char s1[] = ",.-XXX,.-"; + D(s1); + T(strcmp(str_ltrim(s1, "-,.", false), "XXX,.-") == 0); + D(s1); + T(strcmp(str_rtrim(s1, "-,.", false), "XXX") == 0); + D(s1); + T(strcmp(str_rtrim(s1, "-,.", true), "") == 0); + char s2[] = "***XXX***"; + D(s2); + T(strcmp(str_trim(s2, "*", false), "XXX") == 0); + char s3[] = "XXX***XXX"; + D(s3); + T(strcmp(str_trim(s3, "*", true), "***") == 0); + } + + /*****************************************/ + + { + H("str_clean"); + char s1[] = "a1b2c3 45def6"; + D(s1); + T(strcmp(str_clean(s1, "0123456789", false), "abc def") == 0); + D(s1); + T(strcmp(str_clean(s1, "0123456789", true), "") == 0); + char s2[] = "a1b2c3 45def6"; + D(s2); + T(strcmp(str_clean(s2, "0123456789", true), "123456") == 0); + D(s2); + T(strcmp(str_clean(s2, "0123456789", false), "") == 0); + char s3[] = "123abc4d"; + char s4[] = "123abc4d"; + D(s3); + D(s4); + T(strcmp(str_clean(s3, "1234", false), str_clean(s4, "abcd", true)) == 0); + } + + /*****************************************/ + + { + H("str_trans"); + char s1[] = "a,b,c,43"; + D(s1); + T(strcmp(str_trans(s1, ",3", ";2"), "a;b;c;42") == 0); + T(strcmp(str_trans(s1, "", ""), "a;b;c;42") == 0); + T(strcmp(str_trans("", "x", "y"), "") == 0); + } + + /*****************************************/ + + { + H("str_toupper, str_tolower"); + char s1[] = "MiXed CaSe 101"; + char s2[] = "202 MiXed CaSe"; + D(s1); + T(strcmp(str_toupper(s1), "MIXED CASE 101") == 0); + T(strcmp(str_tolower(s1), "mixed case 101") == 0); + D(s2); + T(strcmp(str_tolower(s2), "202 mixed case") == 0); + T(strcmp(str_toupper(s2), "202 MIXED CASE") == 0); + T(strcmp(str_toupper(""), "") == 0); + T(strcmp(str_tolower(""), "") == 0); + } + + /*****************************************/ H("All tests passed."); return 0; -- 2.30.2