From: Lennart Poettering Date: Wed, 17 Feb 2021 14:23:15 +0000 (+0100) Subject: util: move percent/permille/permyriad parser into percent-util.[ch] X-Git-Tag: v248-rc1~46^2~6 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=ed5033fd6c5e73a1ee874608338cbfd28b9083b4;p=thirdparty%2Fsystemd.git util: move percent/permille/permyriad parser into percent-util.[ch] A good chunk of parse-util.[ch] has been about parsing parts per hundred/thousand/ten-thousand. Let's split that out into its own file. No code changes, just some shuffling around. --- diff --git a/src/basic/meson.build b/src/basic/meson.build index 0b0982d5431..88639bc9b53 100644 --- a/src/basic/meson.build +++ b/src/basic/meson.build @@ -176,6 +176,8 @@ basic_sources = files(''' path-lookup.h path-util.c path-util.h + percent-util.c + percent-util.h prioq.c prioq.h proc-cmdline.c diff --git a/src/basic/parse-util.c b/src/basic/parse-util.c index 97d224f1654..b79c885dfd2 100644 --- a/src/basic/parse-util.c +++ b/src/basic/parse-util.c @@ -627,146 +627,6 @@ int parse_fractional_part_u(const char **p, size_t digits, unsigned *res) { return 0; } -static int parse_parts_value_whole(const char *p, const char *symbol) { - const char *pc, *n; - int r, v; - - pc = endswith(p, symbol); - if (!pc) - return -EINVAL; - - n = strndupa(p, pc - p); - r = safe_atoi(n, &v); - if (r < 0) - return r; - if (v < 0) - return -ERANGE; - - return v; -} - -static int parse_parts_value_with_tenths_place(const char *p, const char *symbol) { - const char *pc, *dot, *n; - int r, q, v; - - pc = endswith(p, symbol); - 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 < 0) - return -ERANGE; - if (v > (INT_MAX - q) / 10) - return -ERANGE; - - v = v * 10 + q; - return v; -} - -static int parse_parts_value_with_hundredths_place(const char *p, const char *symbol) { - const char *pc, *dot, *n; - int r, q, v; - - pc = endswith(p, symbol); - if (!pc) - return -EINVAL; - - dot = memchr(p, '.', pc - p); - if (dot) { - if (dot + 3 != pc) - return -EINVAL; - if (dot[1] < '0' || dot[1] > '9' || dot[2] < '0' || dot[2] > '9') - return -EINVAL; - q = (dot[1] - '0') * 10 + (dot[2] - '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 < 0) - return -ERANGE; - if (v > (INT_MAX - q) / 100) - return -ERANGE; - - v = v * 100 + q; - return v; -} - -int parse_percent_unbounded(const char *p) { - return parse_parts_value_whole(p, "%"); -} - -int parse_percent(const char *p) { - int v; - - v = parse_percent_unbounded(p); - if (v > 100) - return -ERANGE; - - return v; -} - -int parse_permille_unbounded(const char *p) { - const char *pm; - - pm = endswith(p, "‰"); - if (pm) - return parse_parts_value_whole(p, "‰"); - - return parse_parts_value_with_tenths_place(p, "%"); -} - -int parse_permille(const char *p) { - int v; - - v = parse_permille_unbounded(p); - if (v > 1000) - return -ERANGE; - - return v; -} - -int parse_permyriad_unbounded(const char *p) { - const char *pm; - - pm = endswith(p, "‱"); - if (pm) - return parse_parts_value_whole(p, "‱"); - - pm = endswith(p, "‰"); - if (pm) - return parse_parts_value_with_tenths_place(p, "‰"); - - return parse_parts_value_with_hundredths_place(p, "%"); -} - -int parse_permyriad(const char *p) { - int v; - - v = parse_permyriad_unbounded(p); - if (v > 10000) - 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 29e04cf5628..908202dafd2 100644 --- a/src/basic/parse-util.h +++ b/src/basic/parse-util.h @@ -127,15 +127,6 @@ int safe_atod(const char *s, double *ret_d); 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_permyriad_unbounded(const char *p); -int parse_permyriad(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/basic/percent-util.c b/src/basic/percent-util.c new file mode 100644 index 00000000000..f58a51dcf97 --- /dev/null +++ b/src/basic/percent-util.c @@ -0,0 +1,145 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ + +#include "percent-util.h" +#include "string-util.h" +#include "parse-util.h" + +static int parse_parts_value_whole(const char *p, const char *symbol) { + const char *pc, *n; + int r, v; + + pc = endswith(p, symbol); + if (!pc) + return -EINVAL; + + n = strndupa(p, pc - p); + r = safe_atoi(n, &v); + if (r < 0) + return r; + if (v < 0) + return -ERANGE; + + return v; +} + +static int parse_parts_value_with_tenths_place(const char *p, const char *symbol) { + const char *pc, *dot, *n; + int r, q, v; + + pc = endswith(p, symbol); + 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 < 0) + return -ERANGE; + if (v > (INT_MAX - q) / 10) + return -ERANGE; + + v = v * 10 + q; + return v; +} + +static int parse_parts_value_with_hundredths_place(const char *p, const char *symbol) { + const char *pc, *dot, *n; + int r, q, v; + + pc = endswith(p, symbol); + if (!pc) + return -EINVAL; + + dot = memchr(p, '.', pc - p); + if (dot) { + if (dot + 3 != pc) + return -EINVAL; + if (dot[1] < '0' || dot[1] > '9' || dot[2] < '0' || dot[2] > '9') + return -EINVAL; + q = (dot[1] - '0') * 10 + (dot[2] - '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 < 0) + return -ERANGE; + if (v > (INT_MAX - q) / 100) + return -ERANGE; + + v = v * 100 + q; + return v; +} + +int parse_percent_unbounded(const char *p) { + return parse_parts_value_whole(p, "%"); +} + +int parse_percent(const char *p) { + int v; + + v = parse_percent_unbounded(p); + if (v > 100) + return -ERANGE; + + return v; +} + +int parse_permille_unbounded(const char *p) { + const char *pm; + + pm = endswith(p, "‰"); + if (pm) + return parse_parts_value_whole(p, "‰"); + + return parse_parts_value_with_tenths_place(p, "%"); +} + +int parse_permille(const char *p) { + int v; + + v = parse_permille_unbounded(p); + if (v > 1000) + return -ERANGE; + + return v; +} + +int parse_permyriad_unbounded(const char *p) { + const char *pm; + + pm = endswith(p, "‱"); + if (pm) + return parse_parts_value_whole(p, "‱"); + + pm = endswith(p, "‰"); + if (pm) + return parse_parts_value_with_tenths_place(p, "‰"); + + return parse_parts_value_with_hundredths_place(p, "%"); +} + +int parse_permyriad(const char *p) { + int v; + + v = parse_permyriad_unbounded(p); + if (v > 10000) + return -ERANGE; + + return v; +} diff --git a/src/basic/percent-util.h b/src/basic/percent-util.h new file mode 100644 index 00000000000..26707e8703a --- /dev/null +++ b/src/basic/percent-util.h @@ -0,0 +1,11 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +#pragma once + +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_permyriad_unbounded(const char *p); +int parse_permyriad(const char *p); diff --git a/src/core/load-fragment.c b/src/core/load-fragment.c index a1ab5f70eb4..c56c2ffc618 100644 --- a/src/core/load-fragment.c +++ b/src/core/load-fragment.c @@ -46,6 +46,7 @@ #include "nulstr-util.h" #include "parse-util.h" #include "path-util.h" +#include "percent-util.h" #include "process-util.h" #if HAVE_SECCOMP #include "seccomp-util.h" @@ -3842,6 +3843,7 @@ int config_parse_managed_oom_mode( const char *rvalue, void *data, void *userdata) { + ManagedOOMMode *mode = data, m; UnitType t; @@ -3861,6 +3863,7 @@ int config_parse_managed_oom_mode( log_syntax(unit, LOG_WARNING, filename, line, m, "Invalid syntax, ignoring: %s", rvalue); return 0; } + *mode = m; return 0; } @@ -3876,6 +3879,7 @@ int config_parse_managed_oom_mem_pressure_limit( const char *rvalue, void *data, void *userdata) { + uint32_t *limit = data; UnitType t; int r; diff --git a/src/home/homectl.c b/src/home/homectl.c index 98835327cdb..dbe5c3af209 100644 --- a/src/home/homectl.c +++ b/src/home/homectl.c @@ -26,6 +26,7 @@ #include "parse-argument.h" #include "parse-util.h" #include "path-util.h" +#include "percent-util.h" #include "pkcs11-util.h" #include "pretty-print.h" #include "process-util.h" diff --git a/src/import/importd.c b/src/import/importd.c index 9ac2f8dcfe2..f9e8481a6d7 100644 --- a/src/import/importd.c +++ b/src/import/importd.c @@ -22,6 +22,7 @@ #include "mkdir.h" #include "parse-util.h" #include "path-util.h" +#include "percent-util.h" #include "process-util.h" #include "service-util.h" #include "signal-util.h" diff --git a/src/login/logind-user.c b/src/login/logind-user.c index 4406f36614c..a2c468e8dd0 100644 --- a/src/login/logind-user.c +++ b/src/login/logind-user.c @@ -19,11 +19,12 @@ #include "label.h" #include "limits-util.h" #include "logind-dbus.h" -#include "logind-user.h" #include "logind-user-dbus.h" +#include "logind-user.h" #include "mkdir.h" #include "parse-util.h" #include "path-util.h" +#include "percent-util.h" #include "rm-rf.h" #include "serialize.h" #include "special.h" diff --git a/src/login/pam_systemd.c b/src/login/pam_systemd.c index fce27262f15..3fc4f43d288 100644 --- a/src/login/pam_systemd.c +++ b/src/login/pam_systemd.c @@ -34,6 +34,7 @@ #include "pam-util.h" #include "parse-util.h" #include "path-util.h" +#include "percent-util.h" #include "process-util.h" #include "rlimit-util.h" #include "socket-util.h" diff --git a/src/network/tc/tc-util.c b/src/network/tc/tc-util.c index 1890e961643..37811829464 100644 --- a/src/network/tc/tc-util.c +++ b/src/network/tc/tc-util.c @@ -5,6 +5,7 @@ #include "extract-word.h" #include "fileio.h" #include "parse-util.h" +#include "percent-util.h" #include "tc-util.h" #include "time-util.h" diff --git a/src/shared/bus-unit-util.c b/src/shared/bus-unit-util.c index a1a253e8018..a6e5e741326 100644 --- a/src/shared/bus-unit-util.c +++ b/src/shared/bus-unit-util.c @@ -28,6 +28,7 @@ #include "numa-util.h" #include "parse-util.h" #include "path-util.h" +#include "percent-util.h" #include "process-util.h" #include "rlimit-util.h" #if HAVE_SECCOMP diff --git a/src/shared/conf-parser.c b/src/shared/conf-parser.c index c3d7e536ec0..df6472d4bec 100644 --- a/src/shared/conf-parser.c +++ b/src/shared/conf-parser.c @@ -22,6 +22,7 @@ #include "nulstr-util.h" #include "parse-util.h" #include "path-util.h" +#include "percent-util.h" #include "process-util.h" #include "rlimit-util.h" #include "sd-id128.h" diff --git a/src/test/meson.build b/src/test/meson.build index 5bf1ca3490f..c567a4cfcbb 100644 --- a/src/test/meson.build +++ b/src/test/meson.build @@ -547,6 +547,8 @@ tests += [ [['src/test/test-bus-util.c']], + [['src/test/test-percent-util.c']], + [['src/test/test-sd-hwdb.c']], [['src/test/test-sd-path.c']], diff --git a/src/test/test-parse-util.c b/src/test/test-parse-util.c index 6e23efe1348..756934acad5 100644 --- a/src/test/test-parse-util.c +++ b/src/test/test-parse-util.c @@ -718,144 +718,6 @@ static void test_safe_atod(void) { assert_se(r == -EINVAL); } -static void test_parse_percent(void) { - assert_se(parse_percent("") == -EINVAL); - assert_se(parse_percent("foo") == -EINVAL); - assert_se(parse_percent("0") == -EINVAL); - assert_se(parse_percent("50") == -EINVAL); - assert_se(parse_percent("100") == -EINVAL); - assert_se(parse_percent("-1") == -EINVAL); - assert_se(parse_percent("0%") == 0); - assert_se(parse_percent("55%") == 55); - assert_se(parse_percent("100%") == 100); - assert_se(parse_percent("-7%") == -ERANGE); - assert_se(parse_percent("107%") == -ERANGE); - assert_se(parse_percent("%") == -EINVAL); - assert_se(parse_percent("%%") == -EINVAL); - assert_se(parse_percent("%1") == -EINVAL); - assert_se(parse_percent("1%%") == -EINVAL); - assert_se(parse_percent("3.2%") == -EINVAL); -} - -static void test_parse_percent_unbounded(void) { - assert_se(parse_percent_unbounded("101%") == 101); - 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_permyriad(void) { - assert_se(parse_permyriad("") == -EINVAL); - assert_se(parse_permyriad("foo") == -EINVAL); - assert_se(parse_permyriad("0") == -EINVAL); - assert_se(parse_permyriad("50") == -EINVAL); - assert_se(parse_permyriad("100") == -EINVAL); - assert_se(parse_permyriad("-1") == -EINVAL); - - assert_se(parse_permyriad("0‱") == 0); - assert_se(parse_permyriad("555‱") == 555); - assert_se(parse_permyriad("1000‱") == 1000); - assert_se(parse_permyriad("-7‱") == -ERANGE); - assert_se(parse_permyriad("10007‱") == -ERANGE); - assert_se(parse_permyriad("‱") == -EINVAL); - assert_se(parse_permyriad("‱‱") == -EINVAL); - assert_se(parse_permyriad("‱1") == -EINVAL); - assert_se(parse_permyriad("1‱‱") == -EINVAL); - assert_se(parse_permyriad("3.2‱") == -EINVAL); - - assert_se(parse_permyriad("0‰") == 0); - assert_se(parse_permyriad("555.5‰") == 5555); - assert_se(parse_permyriad("1000.0‰") == 10000); - assert_se(parse_permyriad("-7‰") == -ERANGE); - assert_se(parse_permyriad("1007‰") == -ERANGE); - assert_se(parse_permyriad("‰") == -EINVAL); - assert_se(parse_permyriad("‰‰") == -EINVAL); - assert_se(parse_permyriad("‰1") == -EINVAL); - assert_se(parse_permyriad("1‰‰") == -EINVAL); - assert_se(parse_permyriad("3.22‰") == -EINVAL); - - assert_se(parse_permyriad("0%") == 0); - assert_se(parse_permyriad("55%") == 5500); - assert_se(parse_permyriad("55.53%") == 5553); - assert_se(parse_permyriad("100%") == 10000); - assert_se(parse_permyriad("-7%") == -ERANGE); - assert_se(parse_permyriad("107%") == -ERANGE); - assert_se(parse_permyriad("%") == -EINVAL); - assert_se(parse_permyriad("%%") == -EINVAL); - assert_se(parse_permyriad("%1") == -EINVAL); - assert_se(parse_permyriad("1%%") == -EINVAL); - assert_se(parse_permyriad("3.212%") == -EINVAL); -} - -static void test_parse_permyriad_unbounded(void) { - assert_se(parse_permyriad_unbounded("1001‱") == 1001); - assert_se(parse_permyriad_unbounded("4000‱") == 4000); - assert_se(parse_permyriad_unbounded("2147483647‱") == 2147483647); - assert_se(parse_permyriad_unbounded("2147483648‱") == -ERANGE); - assert_se(parse_permyriad_unbounded("4294967295‱") == -ERANGE); - assert_se(parse_permyriad_unbounded("4294967296‱") == -ERANGE); - - assert_se(parse_permyriad_unbounded("101‰") == 1010); - assert_se(parse_permyriad_unbounded("400‰") == 4000); - assert_se(parse_permyriad_unbounded("214748364.7‰") == 2147483647); - assert_se(parse_permyriad_unbounded("214748364.8‰") == -ERANGE); - assert_se(parse_permyriad_unbounded("429496729.5‰") == -ERANGE); - assert_se(parse_permyriad_unbounded("429496729.6‰") == -ERANGE); - - assert_se(parse_permyriad_unbounded("99%") == 9900); - assert_se(parse_permyriad_unbounded("40%") == 4000); - assert_se(parse_permyriad_unbounded("21474836.47%") == 2147483647); - assert_se(parse_permyriad_unbounded("21474836.48%") == -ERANGE); - assert_se(parse_permyriad_unbounded("42949672.95%") == -ERANGE); - assert_se(parse_permyriad_unbounded("42949672.96%") == -ERANGE); -} - static void test_parse_nice(void) { int n; @@ -1049,12 +911,6 @@ int main(int argc, char *argv[]) { test_safe_atoi64(); test_safe_atoux64(); test_safe_atod(); - test_parse_percent(); - test_parse_percent_unbounded(); - test_parse_permille(); - test_parse_permille_unbounded(); - test_parse_permyriad(); - test_parse_permyriad_unbounded(); test_parse_nice(); test_parse_dev(); test_parse_errno(); diff --git a/src/test/test-percent-util.c b/src/test/test-percent-util.c new file mode 100644 index 00000000000..9d0ad90de11 --- /dev/null +++ b/src/test/test-percent-util.c @@ -0,0 +1,155 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ + +#include "percent-util.h" +#include "tests.h" + +static void test_parse_percent(void) { + assert_se(parse_percent("") == -EINVAL); + assert_se(parse_percent("foo") == -EINVAL); + assert_se(parse_percent("0") == -EINVAL); + assert_se(parse_percent("50") == -EINVAL); + assert_se(parse_percent("100") == -EINVAL); + assert_se(parse_percent("-1") == -EINVAL); + assert_se(parse_percent("0%") == 0); + assert_se(parse_percent("55%") == 55); + assert_se(parse_percent("100%") == 100); + assert_se(parse_percent("-7%") == -ERANGE); + assert_se(parse_percent("107%") == -ERANGE); + assert_se(parse_percent("%") == -EINVAL); + assert_se(parse_percent("%%") == -EINVAL); + assert_se(parse_percent("%1") == -EINVAL); + assert_se(parse_percent("1%%") == -EINVAL); + assert_se(parse_percent("3.2%") == -EINVAL); +} + +static void test_parse_percent_unbounded(void) { + assert_se(parse_percent_unbounded("101%") == 101); + 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_permyriad(void) { + assert_se(parse_permyriad("") == -EINVAL); + assert_se(parse_permyriad("foo") == -EINVAL); + assert_se(parse_permyriad("0") == -EINVAL); + assert_se(parse_permyriad("50") == -EINVAL); + assert_se(parse_permyriad("100") == -EINVAL); + assert_se(parse_permyriad("-1") == -EINVAL); + + assert_se(parse_permyriad("0‱") == 0); + assert_se(parse_permyriad("555‱") == 555); + assert_se(parse_permyriad("1000‱") == 1000); + assert_se(parse_permyriad("-7‱") == -ERANGE); + assert_se(parse_permyriad("10007‱") == -ERANGE); + assert_se(parse_permyriad("‱") == -EINVAL); + assert_se(parse_permyriad("‱‱") == -EINVAL); + assert_se(parse_permyriad("‱1") == -EINVAL); + assert_se(parse_permyriad("1‱‱") == -EINVAL); + assert_se(parse_permyriad("3.2‱") == -EINVAL); + + assert_se(parse_permyriad("0‰") == 0); + assert_se(parse_permyriad("555.5‰") == 5555); + assert_se(parse_permyriad("1000.0‰") == 10000); + assert_se(parse_permyriad("-7‰") == -ERANGE); + assert_se(parse_permyriad("1007‰") == -ERANGE); + assert_se(parse_permyriad("‰") == -EINVAL); + assert_se(parse_permyriad("‰‰") == -EINVAL); + assert_se(parse_permyriad("‰1") == -EINVAL); + assert_se(parse_permyriad("1‰‰") == -EINVAL); + assert_se(parse_permyriad("3.22‰") == -EINVAL); + + assert_se(parse_permyriad("0%") == 0); + assert_se(parse_permyriad("55%") == 5500); + assert_se(parse_permyriad("55.53%") == 5553); + assert_se(parse_permyriad("100%") == 10000); + assert_se(parse_permyriad("-7%") == -ERANGE); + assert_se(parse_permyriad("107%") == -ERANGE); + assert_se(parse_permyriad("%") == -EINVAL); + assert_se(parse_permyriad("%%") == -EINVAL); + assert_se(parse_permyriad("%1") == -EINVAL); + assert_se(parse_permyriad("1%%") == -EINVAL); + assert_se(parse_permyriad("3.212%") == -EINVAL); +} + +static void test_parse_permyriad_unbounded(void) { + assert_se(parse_permyriad_unbounded("1001‱") == 1001); + assert_se(parse_permyriad_unbounded("4000‱") == 4000); + assert_se(parse_permyriad_unbounded("2147483647‱") == 2147483647); + assert_se(parse_permyriad_unbounded("2147483648‱") == -ERANGE); + assert_se(parse_permyriad_unbounded("4294967295‱") == -ERANGE); + assert_se(parse_permyriad_unbounded("4294967296‱") == -ERANGE); + + assert_se(parse_permyriad_unbounded("101‰") == 1010); + assert_se(parse_permyriad_unbounded("400‰") == 4000); + assert_se(parse_permyriad_unbounded("214748364.7‰") == 2147483647); + assert_se(parse_permyriad_unbounded("214748364.8‰") == -ERANGE); + assert_se(parse_permyriad_unbounded("429496729.5‰") == -ERANGE); + assert_se(parse_permyriad_unbounded("429496729.6‰") == -ERANGE); + + assert_se(parse_permyriad_unbounded("99%") == 9900); + assert_se(parse_permyriad_unbounded("40%") == 4000); + assert_se(parse_permyriad_unbounded("21474836.47%") == 2147483647); + assert_se(parse_permyriad_unbounded("21474836.48%") == -ERANGE); + assert_se(parse_permyriad_unbounded("42949672.95%") == -ERANGE); + assert_se(parse_permyriad_unbounded("42949672.96%") == -ERANGE); +} + +int main(int argc, char *argv[]) { + test_setup_logging(LOG_DEBUG); + + test_parse_percent(); + test_parse_percent_unbounded(); + test_parse_permille(); + test_parse_permille_unbounded(); + test_parse_permyriad(); + test_parse_permyriad_unbounded(); + + return 0; +}