diff --git a/cmdline/state.c b/cmdline/state.c index f83ea6dc..c8b3ffe7 100644 --- a/cmdline/state.c +++ b/cmdline/state.c @@ -837,7 +837,7 @@ void state_config(struct snapraid_state* state, const char* path, const char* co /* LCOV_EXCL_STOP */ } - split_mac = strsplit(split_map, SPLIT_MAX + 1, buffer, ","); + split_mac = strsplit(split_map, SPLIT_MAX + 1, buffer, ",", " \t\r\n", 0); if (split_mac > SPLIT_MAX) { /* LCOV_EXCL_START */ diff --git a/cmdline/str.c b/cmdline/str.c index d338a56b..dabcc59e 100644 --- a/cmdline/str.c +++ b/cmdline/str.c @@ -22,27 +22,74 @@ char* strpolish(char* s) return s; } -unsigned strsplit(char** split_map, unsigned split_max, char* str, const char* delimiters) +unsigned strsplit(char** split_map, unsigned split_max, char* str, const char* delimiters, const char* trim, int trim_empty) { unsigned mac = 0; - /* skip initial delimiters */ - str += strspn(str, delimiters); - - while (*str != 0 && mac < split_max) { - /* start of the token */ - split_map[mac] = str; - ++mac; - - /* find the first delimiter or the end of the string */ - str += strcspn(str, delimiters); - - /* put the final terminator if missing */ - if (*str != 0) - *str++ = 0; - - /* skip trailing delimiters */ + if (trim_empty) { + /* skip initial delimiters */ str += strspn(str, delimiters); + + while (*str != 0 && mac < split_max) { + /* start of the token */ + char* tok_start = str; + + /* find the first delimiter or the end of the string */ + str += strcspn(str, delimiters); + + /* put the final terminator if missing */ + if (*str != 0) + *str++ = 0; + + /* skip trailing delimiters */ + str += strspn(str, delimiters); + + /* trim the token if trim is specified */ + if (trim != 0) { + tok_start += strspn(tok_start, trim); + size_t len = strlen(tok_start); + while (len > 0 && strchr(trim, tok_start[len - 1]) != 0) { + tok_start[len - 1] = 0; + --len; + } + } + + /* store the token only if it's not empty */ + if (*tok_start != 0) { + split_map[mac] = tok_start; + ++mac; + } + } + } else { + if (*str == 0) + return 0; + + while (mac < split_max) { + char* tok_start = str; + + str += strcspn(str, delimiters); + + int has_next = *str != 0; + if (has_next) { + *str = 0; + ++str; + } + + if (trim != 0) { + tok_start += strspn(tok_start, trim); + size_t len = strlen(tok_start); + while (len > 0 && strchr(trim, tok_start[len - 1]) != 0) { + tok_start[len - 1] = 0; + --len; + } + } + + split_map[mac] = tok_start; + ++mac; + + if (!has_next) + break; + } } return mac; diff --git a/cmdline/str.h b/cmdline/str.h index e19770e1..e8cf9014 100644 --- a/cmdline/str.h +++ b/cmdline/str.h @@ -26,8 +26,9 @@ char* strpolish(char* s); * null-terminates each token by overwriting its trailing delimiter, * and stores a pointer to each token's start in @split_map. * - * Leading and consecutive delimiters are silently skipped, so the - * output never contains empty tokens. + * If @trim_empty is non-zero, leading and consecutive delimiters are + * silently skipped, and tokens trimmed to empty are discarded. + * If @trim_empty is zero, empty tokens are preserved. * * The input string is modified in-place; @split_map entries point * directly into @str and are only valid for its lifetime. @@ -36,15 +37,17 @@ char* strpolish(char* s); * \param split_max Capacity of @split_map; at most this many tokens are stored. * \param str Input string to tokenise (modified in-place). * \param delimiters Null-terminated set of delimiter characters. + * \param trim Null-terminated set of characters to trim from the start and end of each token. + * \param trim_empty Whether to trim empty tokens and consecutive delimiters. * \return Number of tokens stored in @split_map. * * Example: - * char buf[] = " one,two,,three "; + * char buf[] = " one, two,, , three "; * char* map[8]; - * unsigned n = strsplit(map, 8, buf, " ,"); + * unsigned n = strsplit(map, 8, buf, ",", " ", 1); * // n == 3, map[] == { "one", "two", "three" } */ -unsigned strsplit(char** split_map, unsigned split_max, char* str, const char* delimiters); +unsigned strsplit(char** split_map, unsigned split_max, char* str, const char* delimiters, const char* trim, int trim_empty); /** * Split a command-line string into an argument vector, diff --git a/cmdline/unixapp.c b/cmdline/unixapp.c index d6db1e5e..a4de9f79 100644 --- a/cmdline/unixapp.c +++ b/cmdline/unixapp.c @@ -873,8 +873,8 @@ static int devdereference_bcachefs(uint64_t device, const char* dir, tommy_list* separator += 3; /* split the line */ - id_mac = strsplit(id_map, 8, s, " \t\n"); - fs_mac = strsplit(fs_map, 8, separator, " \t\n"); + id_mac = strsplit(id_map, 8, s, " \t\r\n", 0, 1); + fs_mac = strsplit(fs_map, 8, separator, " \t\r\n", 0, 1); /* if too short, it's the wrong line */ if (id_mac < 5) @@ -914,7 +914,7 @@ static int devdereference_bcachefs(uint64_t device, const char* dir, tommy_list* /* split the device list */ char* dev_map[64]; - unsigned dev_mac = strsplit(dev_map, 64, device_list, ":"); + unsigned dev_mac = strsplit(dev_map, 64, device_list, ":", 0, 0); for (unsigned i = 0; i < dev_mac; ++i) { /* get major:minor, use stat on the path returned */ struct stat st; @@ -998,7 +998,7 @@ static int extract_zfs(const char* dir, char* dataset, size_t dataset_size, char break; /* split the line */ - mac = strsplit(map, 3, s, "\t\n"); + mac = strsplit(map, 3, s, "\t\r\n", 0, 1); if (mac < 3) continue;