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 | 591 | size_t buf_parse_uintmax(const char *str, size_t len, uintmax_t *valp) | |
34 | { | ||
35 |
4/4✓ Branch 0 (2→3) taken 577 times.
✓ Branch 1 (2→12) taken 14 times.
✓ Branch 2 (3→4) taken 313 times.
✓ Branch 3 (3→12) taken 264 times.
|
591 | if (unlikely(len == 0 || !ascii_isdigit(str[0]))) { |
36 | return 0; | ||
37 | } | ||
38 | |||
39 | 313 | uintmax_t val = str[0] - '0'; | |
40 | 313 | size_t i = 1; | |
41 | |||
42 |
4/4✓ Branch 0 (9→10) taken 545 times.
✓ Branch 1 (9→11) taken 232 times.
✓ Branch 2 (10→5) taken 470 times.
✓ Branch 3 (10→11) taken 75 times.
|
777 | while (i < len && ascii_isdigit(str[i])) { |
43 |
2/2✓ Branch 0 (6→7) taken 464 times.
✓ Branch 1 (6→12) taken 6 times.
|
470 | if (unlikely(umax_multiply_overflows(val, 10, &val))) { |
44 | return 0; | ||
45 | } | ||
46 |
1/2✓ Branch 0 (8→9) taken 464 times.
✗ Branch 1 (8→12) not taken.
|
464 | if (unlikely(umax_add_overflows(val, str[i++] - '0', &val))) { |
47 | return 0; | ||
48 | } | ||
49 | } | ||
50 | |||
51 | 307 | *valp = val; | |
52 | 307 | 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 | 57 | static size_t buf_parse_long(const char *str, size_t len, long *valp) | |
89 | { | ||
90 |
1/2✓ Branch 0 (2→3) taken 57 times.
✗ Branch 1 (2→13) not taken.
|
57 | if (unlikely(len == 0)) { |
91 | return 0; | ||
92 | } | ||
93 | |||
94 | 57 | bool negative = false; | |
95 | 57 | 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 40 times.
|
57 | 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 | 57 | uintmax_t val; | |
108 | 57 | size_t n = buf_parse_uintmax(str, len, &val); | |
109 |
3/4✓ Branch 0 (7→8) taken 52 times.
✓ Branch 1 (7→13) taken 5 times.
✓ Branch 2 (8→9) taken 52 times.
✗ Branch 3 (8→13) not taken.
|
57 | 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 44 times.
|
52 | if (negative) { |
114 | 8 | *valp = -((long)val); | |
115 | } else { | ||
116 | 44 | *valp = (long)val; | |
117 | } | ||
118 | |||
119 | 52 | return n + skipped; | |
120 | } | ||
121 | |||
122 | 58 | bool str_to_int(const char *str, int *valp) | |
123 | { | ||
124 | 58 | const size_t len = strlen(str); | |
125 |
2/2✓ Branch 0 (2→3) taken 57 times.
✓ Branch 1 (2→8) taken 1 times.
|
58 | if (unlikely(len == 0)) { |
126 | return false; | ||
127 | } | ||
128 | 57 | long val; | |
129 | 57 | const size_t n = buf_parse_long(str, len, &val); | |
130 |
4/6✓ Branch 0 (4→5) taken 51 times.
✓ Branch 1 (4→8) taken 6 times.
✓ Branch 2 (5→6) taken 51 times.
✗ Branch 3 (5→8) not taken.
✓ Branch 4 (6→7) taken 51 times.
✗ Branch 5 (6→8) not taken.
|
57 | if (n != len || val < INT_MIN || val > INT_MAX) { |
131 | return false; | ||
132 | } | ||
133 | 51 | *valp = (int)val; | |
134 | 51 | return true; | |
135 | } | ||
136 | |||
137 | 146 | static bool str_to_uintmax(const char *str, uintmax_t *valp) | |
138 | { | ||
139 | 146 | const size_t len = strlen(str); | |
140 |
2/2✓ Branch 0 (2→3) taken 144 times.
✓ Branch 1 (2→6) taken 2 times.
|
146 | if (unlikely(len == 0)) { |
141 | return false; | ||
142 | } | ||
143 | 144 | uintmax_t val; | |
144 | 144 | const size_t n = buf_parse_uintmax(str, len, &val); | |
145 |
2/2✓ Branch 0 (4→5) taken 130 times.
✓ Branch 1 (4→6) taken 14 times.
|
144 | if (n != len) { |
146 | return false; | ||
147 | } | ||
148 | 130 | *valp = val; | |
149 | 130 | return true; | |
150 | } | ||
151 | |||
152 | 59 | bool str_to_uint(const char *str, unsigned int *valp) | |
153 | { | ||
154 | 59 | uintmax_t x; | |
155 |
3/4✓ Branch 0 (3→4) taken 54 times.
✓ Branch 1 (3→6) taken 5 times.
✓ Branch 2 (4→5) taken 54 times.
✗ Branch 3 (4→6) not taken.
|
59 | if (!str_to_uintmax(str, &x) || x > UINT_MAX) { |
156 | return false; | ||
157 | } | ||
158 | 54 | *valp = (unsigned int)x; | |
159 | 54 | 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 | 81 | bool str_to_size(const char *str, size_t *valp) | |
173 | { | ||
174 | 81 | uintmax_t x; | |
175 |
2/2✓ Branch 0 (3→4) taken 71 times.
✓ Branch 1 (3→5) taken 10 times.
|
81 | if (!str_to_uintmax(str, &x) || x > SIZE_MAX) { |
176 | return false; | ||
177 | } | ||
178 | 71 | *valp = (size_t)x; | |
179 | 71 | 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 | size_t col; | |
219 | 17 | bool r = str_to_xfilepos(str, linep, &col); | |
220 |
2/2✓ Branch 0 (3→4) taken 4 times.
✓ Branch 1 (3→5) taken 13 times.
|
17 | if (r) { |
221 | 4 | *colp = col + !col; | |
222 | } | ||
223 | 17 | return r; | |
224 | } | ||
225 | |||
226 | // Return the number of decimal digits in `x` | ||
227 | 13 | size_t size_str_width(size_t x) | |
228 | { | ||
229 | 13 | size_t width = 0; | |
230 | 29 | do { | |
231 | 29 | x /= 10; | |
232 | 29 | width++; | |
233 |
2/2✓ Branch 0 (3→4) taken 16 times.
✓ Branch 1 (3→5) taken 13 times.
|
29 | } while (x); |
234 | 13 | return width; | |
235 | } | ||
236 | |||
237 | // Convert a string of decimal digits with an optional KiB/MiB/GiB/etc. | ||
238 | // suffix to an intmax_t value representing the number of bytes, or a | ||
239 | // negated <errno.h> value in the case of errors | ||
240 | 30 | intmax_t parse_filesize(const char *str) | |
241 | { | ||
242 | 30 | uintmax_t x; | |
243 | 30 | size_t ndigits = buf_parse_uintmax(str, strlen(str), &x); | |
244 |
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)) { |
245 |
2/2✓ Branch 0 (5→6) taken 2 times.
✓ Branch 1 (5→7) taken 1 times.
|
5 | return ascii_isdigit(str[0]) ? -EOVERFLOW : -EINVAL; |
246 | } | ||
247 | |||
248 | 27 | const char *suffix = str + ndigits; | |
249 | 27 | unsigned int shift; | |
250 |
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]) { |
251 | case 'K': shift = 10; break; | ||
252 | 3 | case 'M': shift = 20; break; | |
253 | 9 | case 'G': shift = 30; break; | |
254 | 1 | case 'T': shift = 40; break; | |
255 | 1 | case 'P': shift = 50; break; | |
256 | 2 | case 'E': shift = 60; break; | |
257 | 4 | case '\0': return x; | |
258 | default: return -EINVAL; | ||
259 | } | ||
260 | |||
261 |
2/2✓ Branch 0 (15→16) taken 13 times.
✓ Branch 1 (15→18) taken 8 times.
|
21 | if (unlikely(!streq(suffix + 1, "iB"))) { |
262 | return -EINVAL; | ||
263 | } | ||
264 | |||
265 | 13 | uintmax_t val = x << shift; | |
266 |
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)) { |
267 | return -EOVERFLOW; | ||
268 | } | ||
269 | |||
270 | 11 | return val; | |
271 | } | ||
272 |