Line | Branch | Exec | Source |
---|---|---|---|
1 | #include <errno.h> | ||
2 | #include <string.h> | ||
3 | #include "strtonum.h" | ||
4 | #include "arith.h" | ||
5 | #include "ascii.h" | ||
6 | #include "xstring.h" | ||
7 | |||
8 | enum { | ||
9 | A = 0xA, B = 0xB, C = 0xC, | ||
10 | D = 0xD, E = 0xE, F = 0xF, | ||
11 | I = HEX_INVALID | ||
12 | }; | ||
13 | |||
14 | const uint8_t hex_decode_table[256] = { | ||
15 | I, I, I, I, I, I, I, I, I, I, I, I, I, I, I, I, | ||
16 | I, I, I, I, I, I, I, I, I, I, I, I, I, I, I, I, | ||
17 | I, I, I, I, I, I, I, I, I, I, I, I, I, I, I, I, | ||
18 | 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, I, I, I, I, I, I, | ||
19 | I, A, B, C, D, E, F, I, I, I, I, I, I, I, I, I, | ||
20 | I, I, I, I, I, I, I, I, I, I, I, I, I, I, I, I, | ||
21 | I, A, B, C, D, E, F, I, I, I, I, I, I, I, I, I, | ||
22 | I, I, I, I, I, I, I, I, I, I, I, I, I, I, I, I, | ||
23 | I, I, I, I, I, I, I, I, I, I, I, I, I, I, I, I, | ||
24 | I, I, I, I, I, I, I, I, I, I, I, I, I, I, I, I, | ||
25 | I, I, I, I, I, I, I, I, I, I, I, I, I, I, I, I, | ||
26 | I, I, I, I, I, I, I, I, I, I, I, I, I, I, I, I, | ||
27 | I, I, I, I, I, I, I, I, I, I, I, I, I, I, I, I, | ||
28 | I, I, I, I, I, I, I, I, I, I, I, I, I, I, I, I, | ||
29 | I, I, I, I, I, I, I, I, I, I, I, I, I, I, I, I, | ||
30 | I, I, I, I, I, I, I, I, I, I, I, I, I, I, I, I | ||
31 | }; | ||
32 | |||
33 | 583 | size_t buf_parse_uintmax(const char *str, size_t len, uintmax_t *valp) | |
34 | { | ||
35 |
4/4✓ Branch 0 (2→3) taken 569 times.
✓ Branch 1 (2→12) taken 14 times.
✓ Branch 2 (3→4) taken 306 times.
✓ Branch 3 (3→12) taken 263 times.
|
583 | if (unlikely(len == 0 || !ascii_isdigit(str[0]))) { |
36 | return 0; | ||
37 | } | ||
38 | |||
39 | 306 | uintmax_t val = str[0] - '0'; | |
40 | 306 | size_t i = 1; | |
41 | |||
42 |
4/4✓ Branch 0 (9→10) taken 538 times.
✓ Branch 1 (9→11) taken 225 times.
✓ Branch 2 (10→5) taken 463 times.
✓ Branch 3 (10→11) taken 75 times.
|
763 | while (i < len && ascii_isdigit(str[i])) { |
43 |
2/2✓ Branch 0 (6→7) taken 457 times.
✓ Branch 1 (6→12) taken 6 times.
|
463 | if (unlikely(umax_multiply_overflows(val, 10, &val))) { |
44 | return 0; | ||
45 | } | ||
46 |
1/2✓ Branch 0 (8→9) taken 457 times.
✗ Branch 1 (8→12) not taken.
|
457 | if (unlikely(umax_add_overflows(val, str[i++] - '0', &val))) { |
47 | return 0; | ||
48 | } | ||
49 | } | ||
50 | |||
51 | 300 | *valp = val; | |
52 | 300 | return i; | |
53 | } | ||
54 | |||
55 | 31 | size_t buf_parse_ulong(const char *str, size_t len, unsigned long *valp) | |
56 | { | ||
57 | 31 | uintmax_t val; | |
58 | 31 | size_t n = buf_parse_uintmax(str, len, &val); | |
59 |
2/2✓ Branch 0 (3→4) taken 27 times.
✓ Branch 1 (3→5) taken 4 times.
|
31 | if (n == 0 || val > ULONG_MAX) { |
60 | return 0; | ||
61 | } | ||
62 | 27 | *valp = (unsigned long)val; | |
63 | 27 | return n; | |
64 | } | ||
65 | |||
66 | 2 | size_t buf_parse_uint(const char *str, size_t len, unsigned int *valp) | |
67 | { | ||
68 | 2 | uintmax_t val; | |
69 | 2 | size_t n = buf_parse_uintmax(str, len, &val); | |
70 |
2/4✓ Branch 0 (3→4) taken 2 times.
✗ Branch 1 (3→6) not taken.
✓ Branch 2 (4→5) taken 2 times.
✗ Branch 3 (4→6) not taken.
|
2 | if (n == 0 || val > UINT_MAX) { |
71 | return 0; | ||
72 | } | ||
73 | 2 | *valp = (unsigned int)val; | |
74 | 2 | return n; | |
75 | } | ||
76 | |||
77 | 25 | size_t buf_parse_size(const char *str, size_t len, size_t *valp) | |
78 | { | ||
79 | 25 | uintmax_t val; | |
80 | 25 | size_t n = buf_parse_uintmax(str, len, &val); | |
81 |
2/2✓ Branch 0 (3→4) taken 20 times.
✓ Branch 1 (3→5) taken 5 times.
|
25 | if (n == 0 || val > SIZE_MAX) { |
82 | return 0; | ||
83 | } | ||
84 | 20 | *valp = (size_t)val; | |
85 | 20 | return n; | |
86 | } | ||
87 | |||
88 | 53 | static size_t buf_parse_long(const char *str, size_t len, long *valp) | |
89 | { | ||
90 |
1/2✓ Branch 0 (2→3) taken 53 times.
✗ Branch 1 (2→13) not taken.
|
53 | if (unlikely(len == 0)) { |
91 | return 0; | ||
92 | } | ||
93 | |||
94 | 53 | bool negative = false; | |
95 | 53 | size_t skipped = 0; | |
96 |
3/3✓ Branch 0 (3→4) taken 8 times.
✓ Branch 1 (3→5) taken 9 times.
✓ Branch 2 (3→6) taken 36 times.
|
53 | switch (str[0]) { |
97 | 8 | case '-': | |
98 | 8 | negative = true; | |
99 | // Fallthrough | ||
100 | 17 | case '+': | |
101 | 17 | skipped = 1; | |
102 | 17 | str++; | |
103 | 17 | len--; | |
104 | 17 | break; | |
105 | } | ||
106 | |||
107 | 53 | uintmax_t val; | |
108 | 53 | size_t n = buf_parse_uintmax(str, len, &val); | |
109 |
3/4✓ Branch 0 (7→8) taken 48 times.
✓ Branch 1 (7→13) taken 5 times.
✓ Branch 2 (8→9) taken 48 times.
✗ Branch 3 (8→13) not taken.
|
53 | if (n == 0 || val > LONG_MAX) { |
110 | return 0; | ||
111 | } | ||
112 | |||
113 |
2/2✓ Branch 0 (9→10) taken 8 times.
✓ Branch 1 (9→11) taken 40 times.
|
48 | if (negative) { |
114 | 8 | *valp = -((long)val); | |
115 | } else { | ||
116 | 40 | *valp = (long)val; | |
117 | } | ||
118 | |||
119 | 48 | return n + skipped; | |
120 | } | ||
121 | |||
122 | 54 | bool str_to_int(const char *str, int *valp) | |
123 | { | ||
124 | 54 | const size_t len = strlen(str); | |
125 |
2/2✓ Branch 0 (2→3) taken 53 times.
✓ Branch 1 (2→8) taken 1 times.
|
54 | if (unlikely(len == 0)) { |
126 | return false; | ||
127 | } | ||
128 | 53 | long val; | |
129 | 53 | const size_t n = buf_parse_long(str, len, &val); | |
130 |
4/6✓ Branch 0 (4→5) taken 47 times.
✓ Branch 1 (4→8) taken 6 times.
✓ Branch 2 (5→6) taken 47 times.
✗ Branch 3 (5→8) not taken.
✓ Branch 4 (6→7) taken 47 times.
✗ Branch 5 (6→8) not taken.
|
53 | if (n != len || val < INT_MIN || val > INT_MAX) { |
131 | return false; | ||
132 | } | ||
133 | 47 | *valp = (int)val; | |
134 | 47 | return true; | |
135 | } | ||
136 | |||
137 | 142 | static bool str_to_uintmax(const char *str, uintmax_t *valp) | |
138 | { | ||
139 | 142 | const size_t len = strlen(str); | |
140 |
2/2✓ Branch 0 (2→3) taken 140 times.
✓ Branch 1 (2→6) taken 2 times.
|
142 | if (unlikely(len == 0)) { |
141 | return false; | ||
142 | } | ||
143 | 140 | uintmax_t val; | |
144 | 140 | const size_t n = buf_parse_uintmax(str, len, &val); | |
145 |
2/2✓ Branch 0 (4→5) taken 127 times.
✓ Branch 1 (4→6) taken 13 times.
|
140 | if (n != len) { |
146 | return false; | ||
147 | } | ||
148 | 127 | *valp = val; | |
149 | 127 | return true; | |
150 | } | ||
151 | |||
152 | 56 | bool str_to_uint(const char *str, unsigned int *valp) | |
153 | { | ||
154 | 56 | uintmax_t x; | |
155 |
3/4✓ Branch 0 (3→4) taken 52 times.
✓ Branch 1 (3→6) taken 4 times.
✓ Branch 2 (4→5) taken 52 times.
✗ Branch 3 (4→6) not taken.
|
56 | if (!str_to_uintmax(str, &x) || x > UINT_MAX) { |
156 | return false; | ||
157 | } | ||
158 | 52 | *valp = (unsigned int)x; | |
159 | 52 | return true; | |
160 | } | ||
161 | |||
162 | 6 | bool str_to_ulong(const char *str, unsigned long *valp) | |
163 | { | ||
164 | 6 | uintmax_t x; | |
165 |
2/2✓ Branch 0 (3→4) taken 5 times.
✓ Branch 1 (3→5) taken 1 times.
|
6 | if (!str_to_uintmax(str, &x) || x > ULONG_MAX) { |
166 | return false; | ||
167 | } | ||
168 | 5 | *valp = (unsigned long)x; | |
169 | 5 | return true; | |
170 | } | ||
171 | |||
172 | 80 | bool str_to_size(const char *str, size_t *valp) | |
173 | { | ||
174 | 80 | uintmax_t x; | |
175 |
2/2✓ Branch 0 (3→4) taken 70 times.
✓ Branch 1 (3→5) taken 10 times.
|
80 | if (!str_to_uintmax(str, &x) || x > SIZE_MAX) { |
176 | return false; | ||
177 | } | ||
178 | 70 | *valp = (size_t)x; | |
179 | 70 | return true; | |
180 | } | ||
181 | |||
182 | // Parse line and column number from line[,col] or line[:col] | ||
183 | 23 | bool str_to_xfilepos(const char *str, size_t *linep, size_t *colp) | |
184 | { | ||
185 | 23 | size_t len = strlen(str); | |
186 | 23 | size_t line, col; | |
187 | 23 | size_t i = buf_parse_size(str, len, &line); | |
188 |
4/4✓ Branch 0 (3→4) taken 19 times.
✓ Branch 1 (3→14) taken 4 times.
✓ Branch 2 (4→5) taken 16 times.
✓ Branch 3 (4→14) taken 3 times.
|
23 | if (i == 0 || line < 1) { |
189 | return false; | ||
190 | } | ||
191 | |||
192 | // If an explicit column wasn't specified in `str`, set *colp to 0 | ||
193 | // (which is NOT a valid column number). Callers should be prepared | ||
194 | // to check this and substitute it for something more appropriate, | ||
195 | // or otherwise use str_to_filepos() instead. | ||
196 |
2/2✓ Branch 0 (5→6) taken 3 times.
✓ Branch 1 (5→7) taken 13 times.
|
16 | if (i == len) { |
197 | 3 | col = 0; | |
198 | 3 | goto out; | |
199 | } | ||
200 | |||
201 | 13 | char c = str[i]; | |
202 |
6/6✓ Branch 0 (7→8) taken 10 times.
✓ Branch 1 (7→11) taken 3 times.
✓ Branch 2 (9→10) taken 5 times.
✓ Branch 3 (9→11) taken 5 times.
✓ Branch 4 (10→11) taken 1 times.
✓ Branch 5 (10→12) taken 4 times.
|
13 | if ((c != ':' && c != ',') || !str_to_size(str + i + 1, &col) || col < 1) { |
203 | 9 | return false; | |
204 | } | ||
205 | |||
206 | 4 | out: | |
207 | 7 | *linep = line; | |
208 | 7 | *colp = col; | |
209 | 7 | return true; | |
210 | } | ||
211 | |||
212 | // This is much like str_to_xfilepos(), except *colp is set to 1 if no | ||
213 | // explicit column number is specified in `str`. This is a convenience | ||
214 | // to callers that want a valid column number (when omitted) and don't | ||
215 | // need to do anything different for e.g. "32" vs. "32:1". | ||
216 | 17 | bool str_to_filepos(const char *str, size_t *linep, size_t *colp) | |
217 | { | ||
218 | 17 | bool r = str_to_xfilepos(str, linep, colp); | |
219 |
4/4✓ Branch 0 (3→4) taken 4 times.
✓ Branch 1 (3→6) taken 13 times.
✓ Branch 2 (4→5) taken 1 times.
✓ Branch 3 (4→6) taken 3 times.
|
17 | if (r && *colp == 0) { |
220 | 1 | *colp = 1; | |
221 | } | ||
222 | 17 | return r; | |
223 | } | ||
224 | |||
225 | // Return the number of decimal digits in `x` | ||
226 | 13 | size_t size_str_width(size_t x) | |
227 | { | ||
228 | 13 | size_t width = 0; | |
229 | 29 | do { | |
230 | 29 | x /= 10; | |
231 | 29 | width++; | |
232 |
2/2✓ Branch 0 (3→4) taken 16 times.
✓ Branch 1 (3→5) taken 13 times.
|
29 | } while (x); |
233 | 13 | return width; | |
234 | } | ||
235 | |||
236 | // Convert a string of decimal digits with an optional KiB/MiB/GiB/etc. | ||
237 | // suffix to an intmax_t value representing the number of bytes, or a | ||
238 | // negated <errno.h> value in the case of errors | ||
239 | 30 | intmax_t parse_filesize(const char *str) | |
240 | { | ||
241 | 30 | uintmax_t x; | |
242 | 30 | size_t ndigits = buf_parse_uintmax(str, strlen(str), &x); | |
243 |
4/4✓ Branch 0 (3→4) taken 28 times.
✓ Branch 1 (3→5) taken 2 times.
✓ Branch 2 (4→5) taken 1 times.
✓ Branch 3 (4→8) taken 27 times.
|
30 | if (unlikely(ndigits == 0 || x > INTMAX_MAX)) { |
244 |
2/2✓ Branch 0 (5→6) taken 2 times.
✓ Branch 1 (5→7) taken 1 times.
|
5 | return ascii_isdigit(str[0]) ? -EOVERFLOW : -EINVAL; |
245 | } | ||
246 | |||
247 | 27 | const char *suffix = str + ndigits; | |
248 | 27 | unsigned int shift; | |
249 |
8/8✓ Branch 0 (8→9) taken 3 times.
✓ Branch 1 (8→10) taken 9 times.
✓ Branch 2 (8→11) taken 1 times.
✓ Branch 3 (8→12) taken 1 times.
✓ Branch 4 (8→13) taken 2 times.
✓ Branch 5 (8→14) taken 4 times.
✓ Branch 6 (8→15) taken 5 times.
✓ Branch 7 (8→18) taken 2 times.
|
27 | switch (suffix[0]) { |
250 | case 'K': shift = 10; break; | ||
251 | 3 | case 'M': shift = 20; break; | |
252 | 9 | case 'G': shift = 30; break; | |
253 | 1 | case 'T': shift = 40; break; | |
254 | 1 | case 'P': shift = 50; break; | |
255 | 2 | case 'E': shift = 60; break; | |
256 | 4 | case '\0': return x; | |
257 | default: return -EINVAL; | ||
258 | } | ||
259 | |||
260 |
2/2✓ Branch 0 (15→16) taken 13 times.
✓ Branch 1 (15→18) taken 8 times.
|
21 | if (unlikely(!streq(suffix + 1, "iB"))) { |
261 | return -EINVAL; | ||
262 | } | ||
263 | |||
264 | 13 | uintmax_t val = x << shift; | |
265 |
2/2✓ Branch 0 (16→17) taken 11 times.
✓ Branch 1 (16→18) taken 2 times.
|
13 | if (unlikely(val >> shift != x || x > INTMAX_MAX)) { |
266 | return -EOVERFLOW; | ||
267 | } | ||
268 | |||
269 | 11 | return val; | |
270 | } | ||
271 |