|
@@ -2694,9 +2694,9 @@ digit_to_num(char d)
|
|
|
* success, store the result in <b>out</b>, advance bufp to the next
|
|
|
* character, and return 0. On failure, return -1. */
|
|
|
static int
|
|
|
-scan_unsigned(const char **bufp, unsigned *out, int width, int base)
|
|
|
+scan_unsigned(const char **bufp, unsigned long *out, int width, int base)
|
|
|
{
|
|
|
- unsigned result = 0;
|
|
|
+ unsigned long result = 0;
|
|
|
int scanned_so_far = 0;
|
|
|
const int hex = base==16;
|
|
|
tor_assert(base == 10 || base == 16);
|
|
@@ -2708,8 +2708,8 @@ scan_unsigned(const char **bufp, unsigned *out, int width, int base)
|
|
|
while (**bufp && (hex?TOR_ISXDIGIT(**bufp):TOR_ISDIGIT(**bufp))
|
|
|
&& scanned_so_far < width) {
|
|
|
int digit = hex?hex_decode_digit(*(*bufp)++):digit_to_num(*(*bufp)++);
|
|
|
- unsigned new_result = result * base + digit;
|
|
|
- if (new_result > UINT32_MAX || new_result < result)
|
|
|
+ unsigned long new_result = result * base + digit;
|
|
|
+ if (new_result < result)
|
|
|
return -1;
|
|
|
result = new_result;
|
|
|
++scanned_so_far;
|
|
@@ -2722,6 +2722,89 @@ scan_unsigned(const char **bufp, unsigned *out, int width, int base)
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
+
|
|
|
+ * characters. (Handle arbitrary width if <b>width</b> is less than 0.) On
|
|
|
+ * success, store the result in <b>out</b>, advance bufp to the next
|
|
|
+ * character, and return 0. On failure, return -1. */
|
|
|
+static int
|
|
|
+scan_signed(const char **bufp, long *out, int width)
|
|
|
+{
|
|
|
+ int neg = 0;
|
|
|
+ unsigned long result = 0;
|
|
|
+
|
|
|
+ if (!bufp || !*bufp || !out)
|
|
|
+ return -1;
|
|
|
+ if (width<0)
|
|
|
+ width=MAX_SCANF_WIDTH;
|
|
|
+
|
|
|
+ if (**bufp == '-') {
|
|
|
+ neg = 1;
|
|
|
+ ++*bufp;
|
|
|
+ --width;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (scan_unsigned(bufp, &result, width, 10) < 0)
|
|
|
+ return -1;
|
|
|
+
|
|
|
+ if (neg) {
|
|
|
+ if (result > ((unsigned long)LONG_MAX) + 1)
|
|
|
+ return -1;
|
|
|
+ *out = -(long)result;
|
|
|
+ } else {
|
|
|
+ if (result > LONG_MAX)
|
|
|
+ return -1;
|
|
|
+ *out = (long)result;
|
|
|
+ }
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+ * <b>width</b> characters. (Handle arbitrary width if <b>width</b> is less
|
|
|
+ * than 0.) On success, store the result in <b>out</b>, advance bufp to the
|
|
|
+ * next character, and return 0. On failure, return -1. */
|
|
|
+static int
|
|
|
+scan_double(const char **bufp, double *out, int width)
|
|
|
+{
|
|
|
+ int neg = 0;
|
|
|
+ double result = 0;
|
|
|
+ int scanned_so_far = 0;
|
|
|
+
|
|
|
+ if (!bufp || !*bufp || !out)
|
|
|
+ return -1;
|
|
|
+ if (width<0)
|
|
|
+ width=MAX_SCANF_WIDTH;
|
|
|
+
|
|
|
+ if (**bufp == '-') {
|
|
|
+ neg = 1;
|
|
|
+ ++*bufp;
|
|
|
+ }
|
|
|
+
|
|
|
+ while (**bufp && TOR_ISDIGIT(**bufp) && scanned_so_far < width) {
|
|
|
+ const int digit = digit_to_num(*(*bufp)++);
|
|
|
+ result = result * 10 + digit;
|
|
|
+ ++scanned_so_far;
|
|
|
+ }
|
|
|
+ if (**bufp == '.') {
|
|
|
+ double fracval = 0, denominator = 1;
|
|
|
+ ++*bufp;
|
|
|
+ ++scanned_so_far;
|
|
|
+ while (**bufp && TOR_ISDIGIT(**bufp) && scanned_so_far < width) {
|
|
|
+ const int digit = digit_to_num(*(*bufp)++);
|
|
|
+ fracval = fracval * 10 + digit;
|
|
|
+ denominator *= 10;
|
|
|
+ ++scanned_so_far;
|
|
|
+ }
|
|
|
+ result += fracval / denominator;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!scanned_so_far)
|
|
|
+ return -1;
|
|
|
+
|
|
|
+ *out = neg ? -result : result;
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
|
|
|
* <b>out</b>. Make sure <b>out</b> is nul-terminated. Advance <b>bufp</b>
|
|
|
* to the next non-space character or the EOS. */
|
|
@@ -2758,6 +2841,7 @@ tor_vsscanf(const char *buf, const char *pattern, va_list ap)
|
|
|
}
|
|
|
} else {
|
|
|
int width = -1;
|
|
|
+ int longmod = 0;
|
|
|
++pattern;
|
|
|
if (TOR_ISDIGIT(*pattern)) {
|
|
|
width = digit_to_num(*pattern++);
|
|
@@ -2770,17 +2854,57 @@ tor_vsscanf(const char *buf, const char *pattern, va_list ap)
|
|
|
if (!width)
|
|
|
return -1;
|
|
|
}
|
|
|
+ if (*pattern == 'l') {
|
|
|
+ longmod = 1;
|
|
|
+ ++pattern;
|
|
|
+ }
|
|
|
if (*pattern == 'u' || *pattern == 'x') {
|
|
|
- unsigned *u = va_arg(ap, unsigned *);
|
|
|
+ unsigned long u;
|
|
|
const int base = (*pattern == 'u') ? 10 : 16;
|
|
|
if (!*buf)
|
|
|
return n_matched;
|
|
|
- if (scan_unsigned(&buf, u, width, base)<0)
|
|
|
+ if (scan_unsigned(&buf, &u, width, base)<0)
|
|
|
+ return n_matched;
|
|
|
+ if (longmod) {
|
|
|
+ unsigned long *out = va_arg(ap, unsigned long *);
|
|
|
+ *out = u;
|
|
|
+ } else {
|
|
|
+ unsigned *out = va_arg(ap, unsigned *);
|
|
|
+ if (u > UINT_MAX)
|
|
|
+ return n_matched;
|
|
|
+ *out = u;
|
|
|
+ }
|
|
|
+ ++pattern;
|
|
|
+ ++n_matched;
|
|
|
+ } else if (*pattern == 'f') {
|
|
|
+ double *d = va_arg(ap, double *);
|
|
|
+ if (!longmod)
|
|
|
+ return -1;
|
|
|
+ if (!*buf)
|
|
|
+ return n_matched;
|
|
|
+ if (scan_double(&buf, d, width)<0)
|
|
|
return n_matched;
|
|
|
++pattern;
|
|
|
++n_matched;
|
|
|
+ } else if (*pattern == 'd') {
|
|
|
+ long lng=0;
|
|
|
+ if (scan_signed(&buf, &lng, width)<0)
|
|
|
+ return n_matched;
|
|
|
+ if (longmod) {
|
|
|
+ long *out = va_arg(ap, long *);
|
|
|
+ *out = lng;
|
|
|
+ } else {
|
|
|
+ int *out = va_arg(ap, int *);
|
|
|
+ if (lng < INT_MIN || lng > INT_MAX)
|
|
|
+ return n_matched;
|
|
|
+ *out = (int)lng;
|
|
|
+ }
|
|
|
+ ++pattern;
|
|
|
+ ++n_matched;
|
|
|
} else if (*pattern == 's') {
|
|
|
char *s = va_arg(ap, char *);
|
|
|
+ if (longmod)
|
|
|
+ return -1;
|
|
|
if (width < 0)
|
|
|
return -1;
|
|
|
if (scan_string(&buf, s, width)<0)
|
|
@@ -2789,6 +2913,8 @@ tor_vsscanf(const char *buf, const char *pattern, va_list ap)
|
|
|
++n_matched;
|
|
|
} else if (*pattern == 'c') {
|
|
|
char *ch = va_arg(ap, char *);
|
|
|
+ if (longmod)
|
|
|
+ return -1;
|
|
|
if (width != -1)
|
|
|
return -1;
|
|
|
if (!*buf)
|
|
@@ -2799,6 +2925,8 @@ tor_vsscanf(const char *buf, const char *pattern, va_list ap)
|
|
|
} else if (*pattern == '%') {
|
|
|
if (*buf != '%')
|
|
|
return n_matched;
|
|
|
+ if (longmod)
|
|
|
+ return -1;
|
|
|
++buf;
|
|
|
++pattern;
|
|
|
} else {
|
|
@@ -2812,9 +2940,14 @@ tor_vsscanf(const char *buf, const char *pattern, va_list ap)
|
|
|
|
|
|
|
|
|
* and store the results in the corresponding argument fields. Differs from
|
|
|
- * sscanf in that it: Only handles %u, %x, %c and %Ns. Does not handle
|
|
|
- * arbitrarily long widths. %u and %x do not consume any space. Is
|
|
|
- * locale-independent. Returns -1 on malformed patterns.
|
|
|
+ * sscanf in that:
|
|
|
+ * <ul><li>It only handles %u, %lu, %x, %lx, %<NUM>s, %d, %ld, %lf, and %c.
|
|
|
+ * <li>It only handles decimal inputs for %lf. (12.3, not 1.23e1)
|
|
|
+ * <li>It does not handle arbitrarily long widths.
|
|
|
+ * <li>Numbers do not consume any space characters.
|
|
|
+ * <li>It is locale-independent.
|
|
|
+ * <li>%u and %x do not consume any space.
|
|
|
+ * <li>It returns -1 on malformed patterns.</ul>
|
|
|
*
|
|
|
* (As with other locale-independent functions, we need this to parse data that
|
|
|
* is in ASCII without worrying that the C library's locale-handling will make
|