mirror of
https://github.com/amadvance/snapraid.git
synced 2026-10-26 10:51:54 -04:00
Add trim and trim_empty support to strsplit and update all callers
This commit is contained in:
parent
929ba5cebe
commit
8e66b42ceb
5 changed files with 163 additions and 23 deletions
|
|
@ -1063,6 +1063,125 @@ static void test_wnmatch(void)
|
|||
}
|
||||
}
|
||||
|
||||
static void test_strsplit(void)
|
||||
{
|
||||
char buf[64];
|
||||
char* map[8];
|
||||
unsigned n;
|
||||
|
||||
/* empty string */
|
||||
strcpy(buf, "");
|
||||
n = strsplit(map, 8, buf, ",", " ", 1);
|
||||
if (n != 0) {
|
||||
/* LCOV_EXCL_START */
|
||||
log_fatal(EINTERNAL, "Failed strsplit empty string test with trim_empty=1\n");
|
||||
exit(EXIT_FAILURE);
|
||||
/* LCOV_EXCL_STOP */
|
||||
}
|
||||
strcpy(buf, "");
|
||||
n = strsplit(map, 8, buf, ",", " ", 0);
|
||||
if (n != 0) {
|
||||
/* LCOV_EXCL_START */
|
||||
log_fatal(EINTERNAL, "Failed strsplit empty string test with trim_empty=0\n");
|
||||
exit(EXIT_FAILURE);
|
||||
/* LCOV_EXCL_STOP */
|
||||
}
|
||||
|
||||
/* trim_empty = 1 with delimiters only */
|
||||
strcpy(buf, ",,,");
|
||||
n = strsplit(map, 8, buf, ",", " ", 1);
|
||||
if (n != 0) {
|
||||
/* LCOV_EXCL_START */
|
||||
log_fatal(EINTERNAL, "Failed strsplit consecutive delimiters with trim_empty=1\n");
|
||||
exit(EXIT_FAILURE);
|
||||
/* LCOV_EXCL_STOP */
|
||||
}
|
||||
|
||||
/* trim_empty = 0 with delimiters only */
|
||||
strcpy(buf, ",,,");
|
||||
n = strsplit(map, 8, buf, ",", " ", 0);
|
||||
if (n != 4 || strcmp(map[0], "") != 0 || strcmp(map[1], "") != 0 || strcmp(map[2], "") != 0 || strcmp(map[3], "") != 0) {
|
||||
/* LCOV_EXCL_START */
|
||||
log_fatal(EINTERNAL, "Failed strsplit consecutive delimiters with trim_empty=0\n");
|
||||
exit(EXIT_FAILURE);
|
||||
/* LCOV_EXCL_STOP */
|
||||
}
|
||||
|
||||
/* trim_empty = 1 with mixed spaces and commas */
|
||||
strcpy(buf, " one, two,, , three ");
|
||||
n = strsplit(map, 8, buf, ",", " ", 1);
|
||||
if (n != 3 || strcmp(map[0], "one") != 0 || strcmp(map[1], "two") != 0 || strcmp(map[2], "three") != 0) {
|
||||
/* LCOV_EXCL_START */
|
||||
log_fatal(EINTERNAL, "Failed strsplit mixed test with trim_empty=1\n");
|
||||
exit(EXIT_FAILURE);
|
||||
/* LCOV_EXCL_STOP */
|
||||
}
|
||||
|
||||
/* trim_empty = 0 with mixed spaces and commas */
|
||||
strcpy(buf, " one, two,, , three ");
|
||||
n = strsplit(map, 8, buf, ",", " ", 0);
|
||||
if (n != 5 || strcmp(map[0], "one") != 0 || strcmp(map[1], "two") != 0 || strcmp(map[2], "") != 0 || strcmp(map[3], "") != 0 || strcmp(map[4], "three") != 0) {
|
||||
/* LCOV_EXCL_START */
|
||||
log_fatal(EINTERNAL, "Failed strsplit mixed test with trim_empty=0\n");
|
||||
exit(EXIT_FAILURE);
|
||||
/* LCOV_EXCL_STOP */
|
||||
}
|
||||
|
||||
/* trailing delimiter */
|
||||
strcpy(buf, "a,");
|
||||
n = strsplit(map, 8, buf, ",", 0, 1);
|
||||
if (n != 1 || strcmp(map[0], "a") != 0) {
|
||||
/* LCOV_EXCL_START */
|
||||
log_fatal(EINTERNAL, "Failed strsplit trailing delimiter with trim_empty=1\n");
|
||||
exit(EXIT_FAILURE);
|
||||
/* LCOV_EXCL_STOP */
|
||||
}
|
||||
strcpy(buf, "a,");
|
||||
n = strsplit(map, 8, buf, ",", 0, 0);
|
||||
if (n != 2 || strcmp(map[0], "a") != 0 || strcmp(map[1], "") != 0) {
|
||||
/* LCOV_EXCL_START */
|
||||
log_fatal(EINTERNAL, "Failed strsplit trailing delimiter with trim_empty=0\n");
|
||||
exit(EXIT_FAILURE);
|
||||
/* LCOV_EXCL_STOP */
|
||||
}
|
||||
|
||||
/* leading delimiter */
|
||||
strcpy(buf, ",a");
|
||||
n = strsplit(map, 8, buf, ",", 0, 1);
|
||||
if (n != 1 || strcmp(map[0], "a") != 0) {
|
||||
/* LCOV_EXCL_START */
|
||||
log_fatal(EINTERNAL, "Failed strsplit leading delimiter with trim_empty=1\n");
|
||||
exit(EXIT_FAILURE);
|
||||
/* LCOV_EXCL_STOP */
|
||||
}
|
||||
strcpy(buf, ",a");
|
||||
n = strsplit(map, 8, buf, ",", 0, 0);
|
||||
if (n != 2 || strcmp(map[0], "") != 0 || strcmp(map[1], "a") != 0) {
|
||||
/* LCOV_EXCL_START */
|
||||
log_fatal(EINTERNAL, "Failed strsplit leading delimiter with trim_empty=0\n");
|
||||
exit(EXIT_FAILURE);
|
||||
/* LCOV_EXCL_STOP */
|
||||
}
|
||||
|
||||
/* capacity limit (split_max) */
|
||||
strcpy(buf, "a,b,c");
|
||||
n = strsplit(map, 2, buf, ",", 0, 1);
|
||||
if (n != 2 || strcmp(map[0], "a") != 0 || strcmp(map[1], "b") != 0) {
|
||||
/* LCOV_EXCL_START */
|
||||
log_fatal(EINTERNAL, "Failed strsplit split_max test with trim_empty=1\n");
|
||||
exit(EXIT_FAILURE);
|
||||
/* LCOV_EXCL_STOP */
|
||||
}
|
||||
strcpy(buf, "a,b,c");
|
||||
n = strsplit(map, 2, buf, ",", 0, 0);
|
||||
if (n != 2 || strcmp(map[0], "a") != 0 || strcmp(map[1], "b") != 0) {
|
||||
/* LCOV_EXCL_START */
|
||||
log_fatal(EINTERNAL, "Failed strsplit split_max test with trim_empty=0\n");
|
||||
exit(EXIT_FAILURE);
|
||||
/* LCOV_EXCL_STOP */
|
||||
}
|
||||
}
|
||||
|
||||
static void test_path(void)
|
||||
{
|
||||
char dst[PATH_MAX];
|
||||
|
|
@ -2801,6 +2920,9 @@ void test(int argc, char* argv[])
|
|||
/* pathname import and export */
|
||||
test_path();
|
||||
|
||||
/* string split */
|
||||
test_strsplit();
|
||||
|
||||
/* smartctl output parsing */
|
||||
test_parse_smartctl();
|
||||
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
|
|
|||
|
|
@ -22,27 +22,42 @@ 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);
|
||||
if (*str == 0)
|
||||
return 0;
|
||||
|
||||
while (*str != 0 && mac < split_max) {
|
||||
/* start of the token */
|
||||
split_map[mac] = str;
|
||||
++mac;
|
||||
while (mac < split_max) {
|
||||
char* begin = 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;
|
||||
char* end = str;
|
||||
|
||||
/* skip trailing delimiters */
|
||||
str += strspn(str, delimiters);
|
||||
int has_next = *str != 0;
|
||||
if (has_next) {
|
||||
*str = 0;
|
||||
++str;
|
||||
}
|
||||
|
||||
if (trim != 0) {
|
||||
begin += strspn(begin, trim);
|
||||
|
||||
while (end > begin && strchr(trim, end[-1]) != 0) {
|
||||
--end;
|
||||
*end = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (!trim_empty || *begin != 0) {
|
||||
split_map[mac] = begin;
|
||||
++mac;
|
||||
}
|
||||
|
||||
if (!has_next)
|
||||
break;
|
||||
}
|
||||
|
||||
return mac;
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue