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