#include "execv-const.h"
#include "str.h"
#include "strescape.h"
+#include "str-parse.h"
#include "var-expand.h"
#include "settings-parser.h"
#include <stdio.h>
#include <unistd.h>
-#include <ctype.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/wait.h>
int settings_get_bool(const char *value, bool *result_r,
const char **error_r)
{
- /* FIXME: eventually we'd want to support only yes/no */
- if (strcasecmp(value, "yes") == 0 ||
- strcasecmp(value, "y") == 0 || strcmp(value, "1") == 0)
- *result_r = TRUE;
- else if (strcasecmp(value, "no") == 0)
- *result_r = FALSE;
- else {
- *error_r = t_strdup_printf("Invalid boolean value: %s (use yes or no)",
- value);
- return -1;
- }
-
- return 0;
+ return str_parse_get_bool(value, result_r, error_r);
}
static int
return 0;
}
-static int settings_get_time_full(const char *str, unsigned int *interval_r,
- bool milliseconds, const char **error_r)
-{
- uintmax_t num, multiply = milliseconds ? 1000 : 1;
- const char *p;
-
- if (str_parse_uintmax(str, &num, &p) < 0) {
- *error_r = t_strconcat("Invalid time interval: ", str, NULL);
- return -1;
- }
- while (*p == ' ') p++;
- if (*p == '\0' && num != 0) {
- *error_r = t_strdup_printf("Time interval '%s' is missing units "
- "(add e.g. 's' for seconds)", str);
- return -1;
- }
- switch (i_toupper(*p)) {
- case 'S':
- multiply *= 1;
- if (str_begins_icase_with("secs", p) ||
- str_begins_icase_with("seconds", p))
- p = "";
- break;
- case 'M':
- multiply *= 60;
- if (str_begins_icase_with("mins", p) ||
- str_begins_icase_with("minutes", p))
- p = "";
- else if (str_begins_icase_with("msecs", p) ||
- str_begins_icase_with("mseconds", p) ||
- str_begins_icase_with("millisecs", p) ||
- str_begins_icase_with("milliseconds", p)) {
- if (milliseconds || (num % 1000) == 0) {
- if (!milliseconds) {
- /* allow ms also for seconds, as long
- as it's divisible by seconds */
- num /= 1000;
- }
- multiply = 1;
- p = "";
- break;
- }
- *error_r = t_strdup_printf(
- "Milliseconds not supported for this setting: %s", str);
- return -1;
- }
- break;
- case 'H':
- multiply *= 60*60;
- if (str_begins_icase_with("hours", p))
- p = "";
- break;
- case 'D':
- multiply *= 60*60*24;
- if (str_begins_icase_with("days", p))
- p = "";
- break;
- case 'W':
- multiply *= 60*60*24*7;
- if (str_begins_icase_with("weeks", p))
- p = "";
- break;
- }
-
- if (*p != '\0') {
- *error_r = t_strconcat("Invalid time interval: ", str, NULL);
- return -1;
- }
- if (num > UINT_MAX / multiply) {
- *error_r = t_strconcat("Time interval is too large: ",
- str, NULL);
- return -1;
- }
- *interval_r = num * multiply;
- return 0;
-}
-
int settings_get_time(const char *str, unsigned int *secs_r,
const char **error_r)
{
- return settings_get_time_full(str, secs_r, FALSE, error_r);
+ return str_parse_get_interval(str, secs_r, error_r);
}
int settings_get_time_msecs(const char *str, unsigned int *msecs_r,
const char **error_r)
{
- return settings_get_time_full(str, msecs_r, TRUE, error_r);
+ return str_parse_get_interval_msecs(str, msecs_r, error_r);
}
int settings_get_size(const char *str, uoff_t *bytes_r,
const char **error_r)
{
- uintmax_t num, multiply = 1;
- const char *p;
-
- if (str_parse_uintmax(str, &num, &p) < 0) {
- *error_r = t_strconcat("Invalid size: ", str, NULL);
- return -1;
- }
- while (*p == ' ') p++;
- switch (i_toupper(*p)) {
- case 'B':
- multiply = 1;
- p += 1;
- break;
- case 'K':
- multiply = 1024;
- p += 1;
- break;
- case 'M':
- multiply = 1024*1024;
- p += 1;
- break;
- case 'G':
- multiply = 1024*1024*1024;
- p += 1;
- break;
- case 'T':
- multiply = 1024ULL*1024*1024*1024;
- p += 1;
- break;
- }
-
- if (multiply > 1) {
- /* Allow: k, ki, kiB */
- if (i_toupper(*p) == 'I')
- p++;
- if (i_toupper(*p) == 'B')
- p++;
- }
- if (*p != '\0') {
- *error_r = t_strconcat("Invalid size: ", str, NULL);
- return -1;
- }
- if (num > (UOFF_T_MAX) / multiply) {
- *error_r = t_strconcat("Size is too large: ", str, NULL);
- return -1;
- }
- *bytes_r = num * multiply;
- return 0;
+ return str_parse_get_size(str, bytes_r, error_r);
}
static int get_enum(struct setting_parser_context *ctx, const char *value,
#ifndef SETTINGS_PARSER_H
#define SETTINGS_PARSER_H
+#include "str-parse.h"
+
struct var_expand_table;
struct var_expand_func_table;
"\n",
};
-
-static void test_settings_get_time(void)
-{
- static const struct {
- const char *input;
- unsigned int output;
- } tests[] = {
- { "0", 0 },
-
- { "59s", 59 },
- { "59 s", 59 },
- { "59se", 59 },
- { "59sec", 59 },
- { "59secs", 59 },
- { "59seco", 59 },
- { "59secon", 59 },
- { "59second", 59 },
- { "59seconds", 59 },
- { "123456 seconds", 123456 },
-
- { "123m", 123*60 },
- { "123 m", 123*60 },
- { "123 mi", 123*60 },
- { "123 min", 123*60 },
- { "123 mins", 123*60 },
- { "123 minu", 123*60 },
- { "123 minut", 123*60 },
- { "123 minute", 123*60 },
- { "123 minutes", 123*60 },
-
- { "123h", 123*60*60 },
- { "123 h", 123*60*60 },
- { "123 ho", 123*60*60 },
- { "123 hou", 123*60*60 },
- { "123 hour", 123*60*60 },
- { "123 hours", 123*60*60 },
-
- { "12d", 12*60*60*24 },
- { "12 d", 12*60*60*24 },
- { "12 da", 12*60*60*24 },
- { "12 day", 12*60*60*24 },
- { "12 days", 12*60*60*24 },
-
- { "3w", 3*60*60*24*7 },
- { "3 w", 3*60*60*24*7 },
- { "3 we", 3*60*60*24*7 },
- { "3 wee", 3*60*60*24*7 },
- { "3 week", 3*60*60*24*7 },
- { "3 weeks", 3*60*60*24*7 },
-
- { "1000ms", 1 },
- { "50000ms", 50 },
- };
- struct {
- const char *input;
- unsigned int output;
- } msecs_tests[] = {
- { "0ms", 0 },
- { "1ms", 1 },
- { "123456ms", 123456 },
- { "123456 ms", 123456 },
- { "123456mse", 123456 },
- { "123456msec", 123456 },
- { "123456msecs", 123456 },
- { "123456mseco", 123456 },
- { "123456msecon", 123456 },
- { "123456msecond", 123456 },
- { "123456mseconds", 123456 },
- { "123456mil", 123456 },
- { "123456mill", 123456 },
- { "123456milli", 123456 },
- { "123456millis", 123456 },
- { "123456millisec", 123456 },
- { "123456millisecs", 123456 },
- { "123456milliseco", 123456 },
- { "123456millisecon", 123456 },
- { "123456millisecond", 123456 },
- { "123456milliseconds", 123456 },
- { "4294967295 ms", 4294967295 },
- };
- const char *secs_errors[] = {
- "-1",
- "1",
- /* wrong spellings: */
- "1ss",
- "1secss",
- "1secondss",
- "1ma",
- "1minsa",
- "1hu",
- "1hoursa",
- "1dd",
- "1days?",
- "1wa",
- "1weeksb",
-
- /* milliseconds: */
- "1ms",
- "999ms",
- "1001ms",
- /* overflows: */
- "7102 w",
- "4294967296 s",
- };
- const char *msecs_errors[] = {
- "-1",
- "1",
- /* wrong spellings: */
- "1mis",
- "1mss",
- /* overflows: */
- "8 w",
- "4294967296 ms",
- };
- unsigned int i, secs, msecs;
- const char *error;
-
- test_begin("settings_get_time()");
- for (i = 0; i < N_ELEMENTS(tests); i++) {
- test_assert_idx(settings_get_time(tests[i].input, &secs, &error) == 0, i);
- test_assert_idx(secs == tests[i].output, i);
-
- test_assert_idx(settings_get_time_msecs(tests[i].input, &msecs, &error) == 0, i);
- test_assert_idx(msecs == tests[i].output*1000, i);
- }
- for (i = 0; i < N_ELEMENTS(msecs_tests); i++) {
- test_assert_idx(settings_get_time_msecs(msecs_tests[i].input, &msecs, &error) == 0, i);
- test_assert_idx(msecs == msecs_tests[i].output, i);
- }
- for (i = 0; i < N_ELEMENTS(secs_errors); i++)
- test_assert_idx(settings_get_time(secs_errors[i], &secs, &error) < 0, i);
- for (i = 0; i < N_ELEMENTS(msecs_errors); i++)
- test_assert_idx(settings_get_time_msecs(msecs_errors[i], &msecs, &error) < 0, i);
- test_end();
-}
-
-static void test_settings_get_size(void)
-{
- test_begin("settings_get_size()");
-
- static const struct {
- const char *input;
- uoff_t output;
- } tests[] = {
- { "0", 0 },
- { "0000", 0 },
- { "1b", 1 },
- { "1B", 1 },
- { "1 b", 1 },
- { "1k", 1024 },
- { "1K", 1024 },
- { "1 k", 1024 },
- { "1m", 1024*1024 },
- { "1M", 1024*1024 },
- { "1 m", 1024*1024 },
- { "1g", 1024*1024*1024ULL },
- { "1G", 1024*1024*1024ULL },
- { "1 g", 1024*1024*1024ULL },
- { "1t", 1024*1024*1024*1024ULL },
- { "1T", 1024*1024*1024*1024ULL },
- { "1 t", 1024*1024*1024*1024ULL },
- };
-
- const char *size_errors[] = {
- "-1",
- "one",
- "",
- "340282366920938463463374607431768211456",
- "2^32",
- "2**32",
- "1e10",
- "1 byte",
- };
-
- size_t i;
- uoff_t size;
- const char *error;
-
- for (i = 0; i < N_ELEMENTS(tests); i++) {
- error = NULL;
- test_assert_idx(settings_get_size(tests[i].input, &size, &error) == 0, i);
- test_assert_idx(size == tests[i].output, i);
- test_assert(error == NULL);
- }
- for (i = 0; i < N_ELEMENTS(size_errors); i++) {
- error = NULL;
- test_assert_idx(settings_get_size(size_errors[i], &size, &error) < 0, i);
- test_assert(error != NULL);
- };
-
- test_end();
-}
-
static void test_settings_parser_get(void)
{
struct test_settings {
int main(void)
{
static void (*const test_functions[])(void) = {
- test_settings_get_time,
- test_settings_get_size,
test_settings_parser_get,
NULL
};
str.c \
str-find.c \
str-sanitize.c \
+ str-parse.c \
str-table.c \
strescape.c \
strfuncs.c \
str.h \
str-find.h \
str-sanitize.h \
+ str-parse.h \
str-table.h \
strescape.h \
strfuncs.h \
test-strnum.c \
test-str-find.c \
test-str-sanitize.c \
+ test-str-parse.c \
test-str-table.c \
test-time-util.c \
test-unichar.c \
--- /dev/null
+/* Copyright (c) 2022 Dovecot authors, see the included COPYING file */
+
+#include "lib.h"
+#include "str-parse.h"
+
+#include <ctype.h>
+
+static int str_parse_get_interval_full(
+ const char *str, unsigned int *interval_r, bool milliseconds,
+ const char **error_r)
+{
+ uintmax_t num, multiply = milliseconds ? 1000 : 1;
+ const char *p;
+
+ if (str_parse_uintmax(str, &num, &p) < 0) {
+ *error_r = t_strconcat("Invalid time interval: ", str, NULL);
+ return -1;
+ }
+ while (*p == ' ') p++;
+ if (*p == '\0' && num != 0) {
+ *error_r = t_strdup_printf("Time interval '%s' is missing units "
+ "(add e.g. 's' for seconds)", str);
+ return -1;
+ }
+ switch (i_toupper(*p)) {
+ case 'S':
+ multiply *= 1;
+ if (str_begins_icase_with("secs", p) ||
+ str_begins_icase_with("seconds", p))
+ p = "";
+ break;
+ case 'M':
+ multiply *= 60;
+ if (str_begins_icase_with("mins", p) ||
+ str_begins_icase_with("minutes", p))
+ p = "";
+ else if (str_begins_icase_with("msecs", p) ||
+ str_begins_icase_with("mseconds", p) ||
+ str_begins_icase_with("millisecs", p) ||
+ str_begins_icase_with("milliseconds", p)) {
+ if (milliseconds || (num % 1000) == 0) {
+ if (!milliseconds) {
+ /* allow ms also for seconds, as long
+ as it's divisible by seconds */
+ num /= 1000;
+ }
+ multiply = 1;
+ p = "";
+ break;
+ }
+ *error_r = t_strdup_printf(
+ "Milliseconds not supported for this setting: %s", str);
+ return -1;
+ }
+ break;
+ case 'H':
+ multiply *= 60*60;
+ if (str_begins_icase_with("hours", p))
+ p = "";
+ break;
+ case 'D':
+ multiply *= 60*60*24;
+ if (str_begins_icase_with("days", p))
+ p = "";
+ break;
+ case 'W':
+ multiply *= 60*60*24*7;
+ if (str_begins_icase_with("weeks", p))
+ p = "";
+ break;
+ }
+
+ if (*p != '\0') {
+ *error_r = t_strconcat("Invalid time interval: ", str, NULL);
+ return -1;
+ }
+ if (num > UINT_MAX / multiply) {
+ *error_r = t_strconcat("Time interval is too large: ",
+ str, NULL);
+ return -1;
+ }
+ *interval_r = num * multiply;
+ return 0;
+}
+
+int str_parse_get_interval(const char *str, unsigned int *secs_r,
+ const char **error_r)
+{
+ return str_parse_get_interval_full(str, secs_r, FALSE, error_r);
+}
+
+int str_parse_get_interval_msecs(const char *str, unsigned int *msecs_r,
+ const char **error_r)
+{
+ return str_parse_get_interval_full(str, msecs_r, TRUE, error_r);
+}
+
+int str_parse_get_size(const char *str, uoff_t *bytes_r,
+ const char **error_r)
+{
+ uintmax_t num, multiply = 1;
+ const char *p;
+
+ if (str_parse_uintmax(str, &num, &p) < 0) {
+ *error_r = t_strconcat("Invalid size: ", str, NULL);
+ return -1;
+ }
+ while (*p == ' ') p++;
+ switch (i_toupper(*p)) {
+ case 'B':
+ multiply = 1;
+ p += 1;
+ break;
+ case 'K':
+ multiply = 1024;
+ p += 1;
+ break;
+ case 'M':
+ multiply = 1024*1024;
+ p += 1;
+ break;
+ case 'G':
+ multiply = 1024*1024*1024;
+ p += 1;
+ break;
+ case 'T':
+ multiply = 1024ULL*1024*1024*1024;
+ p += 1;
+ break;
+ }
+
+ if (multiply > 1) {
+ /* Allow: k, ki, kiB */
+ if (i_toupper(*p) == 'I')
+ p++;
+ if (i_toupper(*p) == 'B')
+ p++;
+ }
+ if (*p != '\0') {
+ *error_r = t_strconcat("Invalid size: ", str, NULL);
+ return -1;
+ }
+ if (num > (UOFF_T_MAX) / multiply) {
+ *error_r = t_strconcat("Size is too large: ", str, NULL);
+ return -1;
+ }
+ *bytes_r = num * multiply;
+ return 0;
+}
+
+int str_parse_get_bool(const char *value, bool *result_r,
+ const char **error_r)
+{
+ /* FIXME: eventually we'd want to support only yes/no */
+ if (strcasecmp(value, "yes") == 0 ||
+ strcasecmp(value, "y") == 0 || strcmp(value, "1") == 0)
+ *result_r = TRUE;
+ else if (strcasecmp(value, "no") == 0)
+ *result_r = FALSE;
+ else {
+ *error_r = t_strdup_printf("Invalid boolean value: %s (use yes or no)",
+ value);
+ return -1;
+ }
+
+ return 0;
+}
--- /dev/null
+/* Copyright (c) 2022 Dovecot authors, see the included COPYING file */
+#ifndef STR_PARSE_H
+#define STR_PARSE_H
+
+/* Parse time interval string, return as seconds. */
+int str_parse_get_interval(const char *str, unsigned int *secs_r,
+ const char **error_r);
+/* Parse time interval string, return as milliseconds. */
+int str_parse_get_interval_msecs(const char *str, unsigned int *msecs_r,
+ const char **error_r);
+/* Parse size string, return as bytes. */
+int str_parse_get_size(const char *str, uoff_t *bytes_r,
+ const char **error_r);
+/* Parse boolean string, return as boolean */
+int str_parse_get_bool(const char *value, bool *result_r,
+ const char **error_r);
+
+#endif // STR_PARSE_H
FATAL(fatal_strfuncs)
TEST(test_strnum)
TEST(test_str_find)
+TEST(test_str_parse)
TEST(test_str_sanitize)
TEST(test_str_table)
TEST(test_time_util)
--- /dev/null
+/* Copyright (c) 2009-2018 Dovecot authors, see the included COPYING file */
+
+#include "test-lib.h"
+#include "str-parse.h"
+
+static void test_str_parse_get_interval(void)
+{
+ static const struct {
+ const char *input;
+ unsigned int output;
+ } tests[] = {
+ { "0", 0 },
+
+ { "59s", 59 },
+ { "59 s", 59 },
+ { "59se", 59 },
+ { "59sec", 59 },
+ { "59secs", 59 },
+ { "59seco", 59 },
+ { "59secon", 59 },
+ { "59second", 59 },
+ { "59seconds", 59 },
+ { "123456 seconds", 123456 },
+
+ { "123m", 123*60 },
+ { "123 m", 123*60 },
+ { "123 mi", 123*60 },
+ { "123 min", 123*60 },
+ { "123 mins", 123*60 },
+ { "123 minu", 123*60 },
+ { "123 minut", 123*60 },
+ { "123 minute", 123*60 },
+ { "123 minutes", 123*60 },
+
+ { "123h", 123*60*60 },
+ { "123 h", 123*60*60 },
+ { "123 ho", 123*60*60 },
+ { "123 hou", 123*60*60 },
+ { "123 hour", 123*60*60 },
+ { "123 hours", 123*60*60 },
+
+ { "12d", 12*60*60*24 },
+ { "12 d", 12*60*60*24 },
+ { "12 da", 12*60*60*24 },
+ { "12 day", 12*60*60*24 },
+ { "12 days", 12*60*60*24 },
+
+ { "3w", 3*60*60*24*7 },
+ { "3 w", 3*60*60*24*7 },
+ { "3 we", 3*60*60*24*7 },
+ { "3 wee", 3*60*60*24*7 },
+ { "3 week", 3*60*60*24*7 },
+ { "3 weeks", 3*60*60*24*7 },
+
+ { "1000ms", 1 },
+ { "50000ms", 50 },
+ };
+ struct {
+ const char *input;
+ unsigned int output;
+ } msecs_tests[] = {
+ { "0ms", 0 },
+ { "1ms", 1 },
+ { "123456ms", 123456 },
+ { "123456 ms", 123456 },
+ { "123456mse", 123456 },
+ { "123456msec", 123456 },
+ { "123456msecs", 123456 },
+ { "123456mseco", 123456 },
+ { "123456msecon", 123456 },
+ { "123456msecond", 123456 },
+ { "123456mseconds", 123456 },
+ { "123456mil", 123456 },
+ { "123456mill", 123456 },
+ { "123456milli", 123456 },
+ { "123456millis", 123456 },
+ { "123456millisec", 123456 },
+ { "123456millisecs", 123456 },
+ { "123456milliseco", 123456 },
+ { "123456millisecon", 123456 },
+ { "123456millisecond", 123456 },
+ { "123456milliseconds", 123456 },
+ { "4294967295 ms", 4294967295 },
+ };
+ const char *secs_errors[] = {
+ "-1",
+ "1",
+ /* wrong spellings: */
+ "1ss",
+ "1secss",
+ "1secondss",
+ "1ma",
+ "1minsa",
+ "1hu",
+ "1hoursa",
+ "1dd",
+ "1days?",
+ "1wa",
+ "1weeksb",
+
+ /* milliseconds: */
+ "1ms",
+ "999ms",
+ "1001ms",
+ /* overflows: */
+ "7102 w",
+ "4294967296 s",
+ };
+ const char *msecs_errors[] = {
+ "-1",
+ "1",
+ /* wrong spellings: */
+ "1mis",
+ "1mss",
+ /* overflows: */
+ "8 w",
+ "4294967296 ms",
+ };
+ unsigned int i, secs, msecs;
+ const char *error;
+
+ test_begin("str_parse_get_interval()");
+ for (i = 0; i < N_ELEMENTS(tests); i++) {
+ test_assert_idx(str_parse_get_interval(tests[i].input, &secs,
+ &error) == 0, i);
+ test_assert_idx(secs == tests[i].output, i);
+
+ test_assert_idx(str_parse_get_interval_msecs(
+ tests[i].input, &msecs, &error) == 0, i);
+ test_assert_idx(msecs == tests[i].output*1000, i);
+ }
+ for (i = 0; i < N_ELEMENTS(msecs_tests); i++) {
+ test_assert_idx(str_parse_get_interval_msecs(
+ msecs_tests[i].input, &msecs, &error) == 0, i);
+ test_assert_idx(msecs == msecs_tests[i].output, i);
+ }
+ for (i = 0; i < N_ELEMENTS(secs_errors); i++)
+ test_assert_idx(str_parse_get_interval(secs_errors[i], &secs,
+ &error) < 0, i);
+ for (i = 0; i < N_ELEMENTS(msecs_errors); i++)
+ test_assert_idx(str_parse_get_interval_msecs(
+ msecs_errors[i], &msecs, &error) < 0, i);
+ test_end();
+}
+
+static void test_str_parse_get_size(void)
+{
+ test_begin("str_parse_get_size()");
+
+ static const struct {
+ const char *input;
+ uoff_t output;
+ } tests[] = {
+ { "0", 0 },
+ { "0000", 0 },
+ { "1b", 1 },
+ { "1B", 1 },
+ { "1 b", 1 },
+ { "1k", 1024 },
+ { "1K", 1024 },
+ { "1 k", 1024 },
+ { "1m", 1024*1024 },
+ { "1M", 1024*1024 },
+ { "1 m", 1024*1024 },
+ { "1g", 1024*1024*1024ULL },
+ { "1G", 1024*1024*1024ULL },
+ { "1 g", 1024*1024*1024ULL },
+ { "1t", 1024*1024*1024*1024ULL },
+ { "1T", 1024*1024*1024*1024ULL },
+ { "1 t", 1024*1024*1024*1024ULL },
+ };
+
+ const char *size_errors[] = {
+ "-1",
+ "one",
+ "",
+ "340282366920938463463374607431768211456",
+ "2^32",
+ "2**32",
+ "1e10",
+ "1 byte",
+ };
+
+ size_t i;
+ uoff_t size;
+ const char *error;
+
+ for (i = 0; i < N_ELEMENTS(tests); i++) {
+ error = NULL;
+ test_assert_idx(str_parse_get_size(tests[i].input, &size,
+ &error) == 0, i);
+ test_assert_idx(size == tests[i].output, i);
+ test_assert(error == NULL);
+ }
+ for (i = 0; i < N_ELEMENTS(size_errors); i++) {
+ error = NULL;
+ test_assert_idx(str_parse_get_size(size_errors[i], &size,
+ &error) < 0, i);
+ test_assert(error != NULL);
+ };
+
+ test_end();
+}
+
+void test_str_parse(void)
+{
+ test_str_parse_get_interval();
+ test_str_parse_get_size();
+}