1 /* Copyright (c) 2003-2004, Roger Dingledine
2 * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
3 * Copyright (c) 2007-2021, The Tor Project, Inc. */
4 /* See LICENSE for licensing information */
8 * \brief Locale-independent minimal implementation of sscanf().
11 #include "lib/string/scanf.h"
12 #include "lib/string/compat_ctype.h"
13 #include "lib/cc/torint.h"
14 #include "lib/err/torerr.h"
18 #define MAX_SCANF_WIDTH 9999
20 /** Helper: given an ASCII-encoded decimal digit, return its numeric value.
21 * NOTE: requires that its input be in-bounds. */
25 int num
= ((int)d
) - (int)'0';
26 raw_assert(num
<= 9 && num
>= 0);
30 /** Helper: Read an unsigned int from *<b>bufp</b> of up to <b>width</b>
31 * characters. (Handle arbitrary width if <b>width</b> is less than 0.) On
32 * success, store the result in <b>out</b>, advance bufp to the next
33 * character, and return 0. On failure, return -1. */
35 scan_unsigned(const char **bufp
, unsigned long *out
, int width
, unsigned base
)
37 unsigned long result
= 0;
38 int scanned_so_far
= 0;
39 const int hex
= base
==16;
40 raw_assert(base
== 10 || base
== 16);
41 if (!bufp
|| !*bufp
|| !out
)
44 width
=MAX_SCANF_WIDTH
;
46 while (**bufp
&& (hex
?TOR_ISXDIGIT(**bufp
):TOR_ISDIGIT(**bufp
))
47 && scanned_so_far
< width
) {
48 unsigned digit
= hex
?hex_decode_digit(*(*bufp
)++):digit_to_num(*(*bufp
)++);
49 // Check for overflow beforehand, without actually causing any overflow
50 // This preserves functionality on compilers that don't wrap overflow
51 // (i.e. that trap or optimise away overflow)
52 // result * base + digit > ULONG_MAX
53 // result * base > ULONG_MAX - digit
54 if (result
> (ULONG_MAX
- digit
)/base
)
55 return -1; /* Processing this digit would overflow */
56 result
= result
* base
+ digit
;
60 if (!scanned_so_far
) /* No actual digits scanned */
67 /** Helper: Read an signed int from *<b>bufp</b> of up to <b>width</b>
68 * characters. (Handle arbitrary width if <b>width</b> is less than 0.) On
69 * success, store the result in <b>out</b>, advance bufp to the next
70 * character, and return 0. On failure, return -1. */
72 scan_signed(const char **bufp
, long *out
, int width
)
75 unsigned long result
= 0;
77 if (!bufp
|| !*bufp
|| !out
)
80 width
=MAX_SCANF_WIDTH
;
88 if (scan_unsigned(bufp
, &result
, width
, 10) < 0)
91 if (neg
&& result
> 0) {
92 if (result
> ((unsigned long)LONG_MAX
) + 1)
93 return -1; /* Underflow */
94 else if (result
== ((unsigned long)LONG_MAX
) + 1)
97 /* We once had a far more clever no-overflow conversion here, but
98 * some versions of GCC apparently ran it into the ground. Now
99 * we just check for LONG_MIN explicitly.
101 *out
= -(long)result
;
104 if (result
> LONG_MAX
)
105 return -1; /* Overflow */
112 /** Helper: Read a decimal-formatted double from *<b>bufp</b> of up to
113 * <b>width</b> characters. (Handle arbitrary width if <b>width</b> is less
114 * than 0.) On success, store the result in <b>out</b>, advance bufp to the
115 * next character, and return 0. On failure, return -1. */
117 scan_double(const char **bufp
, double *out
, int width
)
121 int scanned_so_far
= 0;
123 if (!bufp
|| !*bufp
|| !out
)
126 width
=MAX_SCANF_WIDTH
;
133 while (**bufp
&& TOR_ISDIGIT(**bufp
) && scanned_so_far
< width
) {
134 const int digit
= digit_to_num(*(*bufp
)++);
135 result
= result
* 10 + digit
;
139 double fracval
= 0, denominator
= 1;
142 while (**bufp
&& TOR_ISDIGIT(**bufp
) && scanned_so_far
< width
) {
143 const int digit
= digit_to_num(*(*bufp
)++);
144 fracval
= fracval
* 10 + digit
;
148 result
+= fracval
/ denominator
;
151 if (!scanned_so_far
) /* No actual digits scanned */
154 *out
= neg
? -result
: result
;
158 /** Helper: copy up to <b>width</b> non-space characters from <b>bufp</b> to
159 * <b>out</b>. Make sure <b>out</b> is nul-terminated. Advance <b>bufp</b>
160 * to the next non-space character or the EOS. */
162 scan_string(const char **bufp
, char *out
, int width
)
164 int scanned_so_far
= 0;
165 if (!bufp
|| !out
|| width
< 0)
167 while (**bufp
&& ! TOR_ISSPACE(**bufp
) && scanned_so_far
< width
) {
175 /** Locale-independent, minimal, no-surprises scanf variant, accepting only a
176 * restricted pattern format. For more info on what it supports, see
177 * tor_sscanf() documentation. */
179 tor_vsscanf(const char *buf
, const char *pattern
, va_list ap
)
184 if (*pattern
!= '%') {
185 if (*buf
== *pattern
) {
196 if (TOR_ISDIGIT(*pattern
)) {
197 width
= digit_to_num(*pattern
++);
198 while (TOR_ISDIGIT(*pattern
)) {
200 width
+= digit_to_num(*pattern
++);
201 if (width
> MAX_SCANF_WIDTH
)
204 if (!width
) /* No zero-width things. */
207 if (*pattern
== 'l') {
211 if (*pattern
== 'u' || *pattern
== 'x') {
213 const int base
= (*pattern
== 'u') ? 10 : 16;
216 if (scan_unsigned(&buf
, &u
, width
, base
)<0)
219 unsigned long *out
= va_arg(ap
, unsigned long *);
222 unsigned *out
= va_arg(ap
, unsigned *);
229 } else if (*pattern
== 'f') {
230 double *d
= va_arg(ap
, double *);
232 return -1; /* float not supported */
235 if (scan_double(&buf
, d
, width
)<0)
239 } else if (*pattern
== 'd') {
241 if (scan_signed(&buf
, &lng
, width
)<0)
244 long *out
= va_arg(ap
, long *);
247 int *out
= va_arg(ap
, int *);
248 #if LONG_MAX > INT_MAX
249 if (lng
< INT_MIN
|| lng
> INT_MAX
)
256 } else if (*pattern
== 's') {
257 char *s
= va_arg(ap
, char *);
262 if (scan_string(&buf
, s
, width
)<0)
266 } else if (*pattern
== 'c') {
267 char *ch
= va_arg(ap
, char *);
277 } else if (*pattern
== '%') {
285 return -1; /* Unrecognized pattern component. */
293 /** Minimal sscanf replacement: parse <b>buf</b> according to <b>pattern</b>
294 * and store the results in the corresponding argument fields. Differs from
296 * <ul><li>It only handles %u, %lu, %x, %lx, %[NUM]s, %d, %ld, %lf, and %c.
297 * <li>It only handles decimal inputs for %lf. (12.3, not 1.23e1)
298 * <li>It does not handle arbitrarily long widths.
299 * <li>Numbers do not consume any space characters.
300 * <li>It is locale-independent.
301 * <li>%u and %x do not consume any space.
302 * <li>It returns -1 on malformed patterns.</ul>
304 * (As with other locale-independent functions, we need this to parse data that
305 * is in ASCII without worrying that the C library's locale-handling will make
306 * miscellaneous characters look like numbers, spaces, and so on.)
309 tor_sscanf(const char *buf
, const char *pattern
, ...)
313 va_start(ap
, pattern
);
314 r
= tor_vsscanf(buf
, pattern
, ap
);