]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/basic/percent-util.c
hexdecoct: make unbase64mem and unhexmem always use SIZE_MAX
[thirdparty/systemd.git] / src / basic / percent-util.c
CommitLineData
ed5033fd
LP
1/* SPDX-License-Identifier: LGPL-2.1-or-later */
2
3#include "percent-util.h"
4#include "string-util.h"
5#include "parse-util.h"
6
7static int parse_parts_value_whole(const char *p, const char *symbol) {
8 const char *pc, *n;
9 int r, v;
10
11 pc = endswith(p, symbol);
12 if (!pc)
13 return -EINVAL;
14
2f82562b 15 n = strndupa_safe(p, pc - p);
ed5033fd
LP
16 r = safe_atoi(n, &v);
17 if (r < 0)
18 return r;
19 if (v < 0)
20 return -ERANGE;
21
22 return v;
23}
24
25static int parse_parts_value_with_tenths_place(const char *p, const char *symbol) {
26 const char *pc, *dot, *n;
27 int r, q, v;
28
29 pc = endswith(p, symbol);
30 if (!pc)
31 return -EINVAL;
32
33 dot = memchr(p, '.', pc - p);
34 if (dot) {
35 if (dot + 2 != pc)
36 return -EINVAL;
37 if (dot[1] < '0' || dot[1] > '9')
38 return -EINVAL;
39 q = dot[1] - '0';
2f82562b 40 n = strndupa_safe(p, dot - p);
ed5033fd
LP
41 } else {
42 q = 0;
2f82562b 43 n = strndupa_safe(p, pc - p);
ed5033fd
LP
44 }
45 r = safe_atoi(n, &v);
46 if (r < 0)
47 return r;
48 if (v < 0)
49 return -ERANGE;
50 if (v > (INT_MAX - q) / 10)
51 return -ERANGE;
52
53 v = v * 10 + q;
54 return v;
55}
56
57static int parse_parts_value_with_hundredths_place(const char *p, const char *symbol) {
58 const char *pc, *dot, *n;
59 int r, q, v;
60
61 pc = endswith(p, symbol);
62 if (!pc)
63 return -EINVAL;
64
65 dot = memchr(p, '.', pc - p);
66 if (dot) {
38d0c270
LP
67 if (dot + 3 == pc) {
68 /* Support two places after the dot */
69
70 if (dot[1] < '0' || dot[1] > '9' || dot[2] < '0' || dot[2] > '9')
71 return -EINVAL;
72 q = (dot[1] - '0') * 10 + (dot[2] - '0');
73
74 } else if (dot + 2 == pc) {
75 /* Support one place after the dot */
76
77 if (dot[1] < '0' || dot[1] > '9')
78 return -EINVAL;
79 q = (dot[1] - '0') * 10;
80 } else
81 /* We do not support zero or more than two places */
ed5033fd 82 return -EINVAL;
38d0c270 83
2f82562b 84 n = strndupa_safe(p, dot - p);
ed5033fd
LP
85 } else {
86 q = 0;
2f82562b 87 n = strndupa_safe(p, pc - p);
ed5033fd
LP
88 }
89 r = safe_atoi(n, &v);
90 if (r < 0)
91 return r;
92 if (v < 0)
93 return -ERANGE;
94 if (v > (INT_MAX - q) / 100)
95 return -ERANGE;
96
97 v = v * 100 + q;
98 return v;
99}
100
101int parse_percent_unbounded(const char *p) {
102 return parse_parts_value_whole(p, "%");
103}
104
105int parse_percent(const char *p) {
106 int v;
107
108 v = parse_percent_unbounded(p);
109 if (v > 100)
110 return -ERANGE;
111
112 return v;
113}
114
115int parse_permille_unbounded(const char *p) {
116 const char *pm;
117
118 pm = endswith(p, "‰");
119 if (pm)
120 return parse_parts_value_whole(p, "‰");
121
122 return parse_parts_value_with_tenths_place(p, "%");
123}
124
125int parse_permille(const char *p) {
126 int v;
127
128 v = parse_permille_unbounded(p);
129 if (v > 1000)
130 return -ERANGE;
131
132 return v;
133}
134
135int parse_permyriad_unbounded(const char *p) {
136 const char *pm;
137
138 pm = endswith(p, "‱");
139 if (pm)
140 return parse_parts_value_whole(p, "‱");
141
142 pm = endswith(p, "‰");
143 if (pm)
144 return parse_parts_value_with_tenths_place(p, "‰");
145
146 return parse_parts_value_with_hundredths_place(p, "%");
147}
148
149int parse_permyriad(const char *p) {
150 int v;
151
152 v = parse_permyriad_unbounded(p);
153 if (v > 10000)
154 return -ERANGE;
155
156 return v;
157}