diff --git a/src/basic/parse-util.c b/src/basic/parse-util.c index 07f43b94e5..b25bc6e215 100644 --- a/src/basic/parse-util.c +++ b/src/basic/parse-util.c @@ -632,6 +632,58 @@ int parse_percent(const char *p) { return v; } +int parse_permille_unbounded(const char *p) { + const char *pc, *pm, *dot, *n; + int r, q, v; + + pm = endswith(p, "‰"); + if (pm) { + n = strndupa(p, pm - p); + r = safe_atoi(n, &v); + if (r < 0) + return r; + } else { + pc = endswith(p, "%"); + if (!pc) + return -EINVAL; + + dot = memchr(p, '.', pc - p); + if (dot) { + if (dot + 2 != pc) + return -EINVAL; + if (dot[1] < '0' || dot[1] > '9') + return -EINVAL; + q = dot[1] - '0'; + n = strndupa(p, dot - p); + } else { + q = 0; + n = strndupa(p, pc - p); + } + r = safe_atoi(n, &v); + if (r < 0) + return r; + if (v > ((INT_MAX - q) / 10)) + return -ERANGE; + + v = v * 10 + q; + } + + if (v < 0) + return -ERANGE; + + return v; +} + +int parse_permille(const char *p) { + int v; + + v = parse_permille_unbounded(p); + if (v > 1000) + return -ERANGE; + + return v; +} + int parse_nice(const char *p, int *ret) { int n, r; diff --git a/src/basic/parse-util.h b/src/basic/parse-util.h index 2b75b938c7..b3edfbaecc 100644 --- a/src/basic/parse-util.h +++ b/src/basic/parse-util.h @@ -115,6 +115,9 @@ int parse_fractional_part_u(const char **s, size_t digits, unsigned *res); int parse_percent_unbounded(const char *p); int parse_percent(const char *p); +int parse_permille_unbounded(const char *p); +int parse_permille(const char *p); + int parse_nice(const char *p, int *ret); int parse_ip_port(const char *s, uint16_t *ret); diff --git a/src/test/test-parse-util.c b/src/test/test-parse-util.c index a04ab9c9aa..429eaa479d 100644 --- a/src/test/test-parse-util.c +++ b/src/test/test-parse-util.c @@ -648,6 +648,54 @@ static void test_parse_percent_unbounded(void) { assert_se(parse_percent_unbounded("400%") == 400); } +static void test_parse_permille(void) { + assert_se(parse_permille("") == -EINVAL); + assert_se(parse_permille("foo") == -EINVAL); + assert_se(parse_permille("0") == -EINVAL); + assert_se(parse_permille("50") == -EINVAL); + assert_se(parse_permille("100") == -EINVAL); + assert_se(parse_permille("-1") == -EINVAL); + + assert_se(parse_permille("0‰") == 0); + assert_se(parse_permille("555‰") == 555); + assert_se(parse_permille("1000‰") == 1000); + assert_se(parse_permille("-7‰") == -ERANGE); + assert_se(parse_permille("1007‰") == -ERANGE); + assert_se(parse_permille("‰") == -EINVAL); + assert_se(parse_permille("‰‰") == -EINVAL); + assert_se(parse_permille("‰1") == -EINVAL); + assert_se(parse_permille("1‰‰") == -EINVAL); + assert_se(parse_permille("3.2‰") == -EINVAL); + + assert_se(parse_permille("0%") == 0); + assert_se(parse_permille("55%") == 550); + assert_se(parse_permille("55.5%") == 555); + assert_se(parse_permille("100%") == 1000); + assert_se(parse_permille("-7%") == -ERANGE); + assert_se(parse_permille("107%") == -ERANGE); + assert_se(parse_permille("%") == -EINVAL); + assert_se(parse_permille("%%") == -EINVAL); + assert_se(parse_permille("%1") == -EINVAL); + assert_se(parse_permille("1%%") == -EINVAL); + assert_se(parse_permille("3.21%") == -EINVAL); +} + +static void test_parse_permille_unbounded(void) { + assert_se(parse_permille_unbounded("1001‰") == 1001); + assert_se(parse_permille_unbounded("4000‰") == 4000); + assert_se(parse_permille_unbounded("2147483647‰") == 2147483647); + assert_se(parse_permille_unbounded("2147483648‰") == -ERANGE); + assert_se(parse_permille_unbounded("4294967295‰") == -ERANGE); + assert_se(parse_permille_unbounded("4294967296‰") == -ERANGE); + + assert_se(parse_permille_unbounded("101%") == 1010); + assert_se(parse_permille_unbounded("400%") == 4000); + assert_se(parse_permille_unbounded("214748364.7%") == 2147483647); + assert_se(parse_permille_unbounded("214748364.8%") == -ERANGE); + assert_se(parse_permille_unbounded("429496729.5%") == -ERANGE); + assert_se(parse_permille_unbounded("429496729.6%") == -ERANGE); +} + static void test_parse_nice(void) { int n; @@ -797,6 +845,8 @@ int main(int argc, char *argv[]) { test_safe_atod(); test_parse_percent(); test_parse_percent_unbounded(); + test_parse_permille(); + test_parse_permille_unbounded(); test_parse_nice(); test_parse_dev(); test_parse_errno();