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
parse_parts_value_whole(const char * p,const char * symbol)7 static 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
15 n = strndupa_safe(p, pc - p);
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
parse_parts_value_with_tenths_place(const char * p,const char * symbol)25 static 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';
40 n = strndupa_safe(p, dot - p);
41 } else {
42 q = 0;
43 n = strndupa_safe(p, pc - p);
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
parse_parts_value_with_hundredths_place(const char * p,const char * symbol)57 static 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) {
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 */
82 return -EINVAL;
83
84 n = strndupa_safe(p, dot - p);
85 } else {
86 q = 0;
87 n = strndupa_safe(p, pc - p);
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
parse_percent_unbounded(const char * p)101 int parse_percent_unbounded(const char *p) {
102 return parse_parts_value_whole(p, "%");
103 }
104
parse_percent(const char * p)105 int 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
parse_permille_unbounded(const char * p)115 int 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
parse_permille(const char * p)125 int 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
parse_permyriad_unbounded(const char * p)135 int 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
parse_permyriad(const char * p)149 int 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 }
158