Add trim and trim_empty support to strsplit and update all callers

This commit is contained in:
Andrea Mazzoleni 2026-09-19 11:12:49 +02:00
parent 929ba5cebe
commit c6c41192a7
4 changed files with 77 additions and 27 deletions

View file

@ -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 */

View file

@ -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;

View file

@ -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,

View file

@ -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;