summaryrefslogtreecommitdiff
path: root/src/basic/percent-util.c
diff options
context:
space:
mode:
authorLennart Poettering <lennart@poettering.net>2021-02-17 15:23:15 +0100
committerLennart Poettering <lennart@poettering.net>2021-02-18 22:36:34 +0100
commited5033fd6c5e73a1ee874608338cbfd28b9083b4 (patch)
tree2269d651893a01d4f88988d1a153b6196af30996 /src/basic/percent-util.c
parent60dcf3dc1b5b4c63e2c35ad7eba1d6f7ab5e086c (diff)
downloadsystemd-ed5033fd6c5e73a1ee874608338cbfd28b9083b4.tar.gz
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.
Diffstat (limited to 'src/basic/percent-util.c')
-rw-r--r--src/basic/percent-util.c145
1 files changed, 145 insertions, 0 deletions
diff --git a/src/basic/percent-util.c b/src/basic/percent-util.c
new file mode 100644
index 0000000000..f58a51dcf9
--- /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;
+}