]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
parse-util: add permyriad parsing
authorAnita Zhang <the.anitazha@gmail.com>
Tue, 2 Feb 2021 09:47:08 +0000 (01:47 -0800)
committerAnita Zhang <the.anitazha@gmail.com>
Tue, 2 Feb 2021 09:47:08 +0000 (01:47 -0800)
src/basic/parse-util.c
src/basic/parse-util.h
src/test/test-parse-util.c

index 2a7280fc3821a1afbf3d993c0711886ff4835e56..97d224f16545c12e099da65a44327d847930f589 100644 (file)
@@ -627,11 +627,11 @@ int parse_fractional_part_u(const char **p, size_t digits, unsigned *res) {
         return 0;
 }
 
-int parse_percent_unbounded(const char *p) {
+static int parse_parts_value_whole(const char *p, const char *symbol) {
         const char *pc, *n;
         int r, v;
 
-        pc = endswith(p, "%");
+        pc = endswith(p, symbol);
         if (!pc)
                 return -EINVAL;
 
@@ -645,6 +645,74 @@ int parse_percent_unbounded(const char *p) {
         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;
 
@@ -656,46 +724,13 @@ int parse_percent(const char *p) {
 }
 
 int parse_permille_unbounded(const char *p) {
-        const char *pc, *pm, *dot, *n;
-        int r, q, v;
+        const char *pm;
 
         pm = endswith(p, "‰");
-        if (pm) {
-                n = strndupa(p, pm - p);
-                r = safe_atoi(n, &v);
-                if (r < 0)
-                        return r;
-                if (v < 0)
-                        return -ERANGE;
-        } 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 < 0)
-                        return -ERANGE;
-                if (v > (INT_MAX - q) / 10)
-                        return -ERANGE;
+        if (pm)
+                return parse_parts_value_whole(p, "‰");
 
-                v = v * 10 + q;
-        }
-
-        return v;
+        return parse_parts_value_with_tenths_place(p, "%");
 }
 
 int parse_permille(const char *p) {
@@ -708,6 +743,30 @@ int parse_permille(const char *p) {
         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;
 
index 1ff76ded446c468d0aea374f4f8e9978705f768d..29e04cf5628f70a823a91a5763aa918a057cba41 100644 (file)
@@ -133,6 +133,9 @@ 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);
index 1c969091ef7052bd108bb56c1f3bb635be3a99b7..6e23efe13482870c8f9c9d26aa5b0186167cc4ba 100644 (file)
@@ -790,6 +790,72 @@ static void test_parse_permille_unbounded(void) {
         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;
 
@@ -987,6 +1053,8 @@ int main(int argc, char *argv[]) {
         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();