From: Ben Pfaff Date: Fri, 15 Nov 2013 16:54:56 +0000 (-0800) Subject: util: New function ovs_scan(). X-Git-Tag: sliver-openvswitch-2.0.90-1~5^2~20 X-Git-Url: http://git.onelab.eu/?p=sliver-openvswitch.git;a=commitdiff_plain;h=ed2232fc7720ca482d3be097706e70588a31cb1f util: New function ovs_scan(). This new function is essentially an implementation of sscanf() with slightly different behavior (see the comment) that is more convenient for Open vSwitch internal use. Also, this implementation ought to work out of the box on Windows, which has a defective sscanf() that lacks the 'hh' modifier required to scan into a char variable. Signed-off-by: Ben Pfaff --- diff --git a/lib/compiler.h b/lib/compiler.h index ccc949ba1..0dbacbfcc 100644 --- a/lib/compiler.h +++ b/lib/compiler.h @@ -28,6 +28,7 @@ #define NO_RETURN __attribute__((__noreturn__)) #define OVS_UNUSED __attribute__((__unused__)) #define PRINTF_FORMAT(FMT, ARG1) __attribute__((__format__(printf, FMT, ARG1))) +#define SCANF_FORMAT(FMT, ARG1) __attribute__((__format__(scanf, FMT, ARG1))) #define STRFTIME_FORMAT(FMT) __attribute__((__format__(__strftime__, FMT, 0))) #define MALLOC_LIKE __attribute__((__malloc__)) #define ALWAYS_INLINE __attribute__((always_inline)) @@ -39,6 +40,7 @@ #define NO_RETURN #define OVS_UNUSED #define PRINTF_FORMAT(FMT, ARG1) +#define SCANF_FORMAT(FMT, ARG1) #define STRFTIME_FORMAT(FMT) #define MALLOC_LIKE #define ALWAYS_INLINE diff --git a/lib/util.c b/lib/util.c index a32c80aaa..b63316666 100644 --- a/lib/util.c +++ b/lib/util.c @@ -16,6 +16,7 @@ #include #include "util.h" +#include #include #include #include @@ -26,6 +27,7 @@ #include #include #include +#include "bitmap.h" #include "byte-order.h" #include "coverage.h" #include "ovs-thread.h" @@ -1245,3 +1247,444 @@ bitwise_get(const void *src, unsigned int src_len, n_bits); return ntohll(value); } + +/* ovs_scan */ + +struct scan_spec { + unsigned int width; + enum { + SCAN_DISCARD, + SCAN_CHAR, + SCAN_SHORT, + SCAN_INT, + SCAN_LONG, + SCAN_LLONG, + SCAN_INTMAX_T, + SCAN_PTRDIFF_T, + SCAN_SIZE_T + } type; +}; + +static const char * +skip_spaces(const char *s) +{ + while (isspace((unsigned char) *s)) { + s++; + } + return s; +} + +static const char * +scan_int(const char *s, const struct scan_spec *spec, int base, va_list *args) +{ + const char *start = s; + uintmax_t value; + bool negative; + int n_digits; + + negative = *s == '-'; + s += *s == '-' || *s == '+'; + + if ((!base || base == 16) && *s == '0' && (s[1] == 'x' || s[1] == 'X')) { + base = 16; + s += 2; + } else if (!base) { + base = *s == '0' ? 8 : 10; + } + + if (s - start >= spec->width) { + return NULL; + } + + value = 0; + n_digits = 0; + while (s - start < spec->width) { + int digit = hexit_value(*s); + + if (digit < 0 || digit >= base) { + break; + } + value = value * base + digit; + n_digits++; + s++; + } + if (!n_digits) { + return NULL; + } + + if (negative) { + value = -value; + } + + switch (spec->type) { + case SCAN_DISCARD: + break; + case SCAN_CHAR: + *va_arg(*args, char *) = value; + break; + case SCAN_SHORT: + *va_arg(*args, short int *) = value; + break; + case SCAN_INT: + *va_arg(*args, int *) = value; + break; + case SCAN_LONG: + *va_arg(*args, long int *) = value; + break; + case SCAN_LLONG: + *va_arg(*args, long long int *) = value; + break; + case SCAN_INTMAX_T: + *va_arg(*args, intmax_t *) = value; + break; + case SCAN_PTRDIFF_T: + *va_arg(*args, ptrdiff_t *) = value; + break; + case SCAN_SIZE_T: + *va_arg(*args, size_t *) = value; + break; + } + return s; +} + +static const char * +skip_digits(const char *s) +{ + while (*s >= '0' && *s <= '9') { + s++; + } + return s; +} + +static const char * +scan_float(const char *s, const struct scan_spec *spec, va_list *args) +{ + const char *start = s; + long double value; + char *tail; + char *copy; + bool ok; + + s += *s == '+' || *s == '-'; + s = skip_digits(s); + if (*s == '.') { + s = skip_digits(s + 1); + } + if (*s == 'e' || *s == 'E') { + s++; + s += *s == '+' || *s == '-'; + s = skip_digits(s); + } + + if (s - start > spec->width) { + s = start + spec->width; + } + + copy = xmemdup0(start, s - start); + value = strtold(copy, &tail); + ok = *tail == '\0'; + free(copy); + if (!ok) { + return NULL; + } + + switch (spec->type) { + case SCAN_DISCARD: + break; + case SCAN_INT: + *va_arg(*args, float *) = value; + break; + case SCAN_LONG: + *va_arg(*args, double *) = value; + break; + case SCAN_LLONG: + *va_arg(*args, long double *) = value; + break; + + case SCAN_CHAR: + case SCAN_SHORT: + case SCAN_INTMAX_T: + case SCAN_PTRDIFF_T: + case SCAN_SIZE_T: + NOT_REACHED(); + } + return s; +} + +static void +scan_output_string(const struct scan_spec *spec, + const char *s, size_t n, + va_list *args) +{ + if (spec->type != SCAN_DISCARD) { + char *out = va_arg(*args, char *); + memcpy(out, s, n); + out[n] = '\0'; + } +} + +static const char * +scan_string(const char *s, const struct scan_spec *spec, va_list *args) +{ + size_t n; + + for (n = 0; n < spec->width; n++) { + if (!s[n] || isspace((unsigned char) s[n])) { + break; + } + } + if (!n) { + return NULL; + } + + scan_output_string(spec, s, n, args); + return s + n; +} + +static const char * +parse_scanset(const char *p_, unsigned long *set, bool *complemented) +{ + const uint8_t *p = (const uint8_t *) p_; + + *complemented = *p == '^'; + p += *complemented; + + if (*p == ']') { + bitmap_set1(set, ']'); + p++; + } + + while (*p && *p != ']') { + if (p[1] == '-' && p[2] != ']' && p[2] > *p) { + bitmap_set_multiple(set, *p, p[2] - *p + 1, true); + p += 3; + } else { + bitmap_set1(set, *p++); + } + } + if (*p == ']') { + p++; + } + return (const char *) p; +} + +static const char * +scan_set(const char *s, const struct scan_spec *spec, const char **pp, + va_list *args) +{ + unsigned long set[BITMAP_N_LONGS(UCHAR_MAX + 1)]; + bool complemented; + unsigned int n; + + /* Parse the scan set. */ + memset(set, 0, sizeof set); + *pp = parse_scanset(*pp, set, &complemented); + + /* Parse the data. */ + n = 0; + while (s[n] + && bitmap_is_set(set, (unsigned char) s[n]) == !complemented + && n < spec->width) { + n++; + } + if (!n) { + return NULL; + } + scan_output_string(spec, s, n, args); + return s + n; +} + +static const char * +scan_chars(const char *s, const struct scan_spec *spec, va_list *args) +{ + unsigned int n = spec->width == SIZE_MAX ? 1 : spec->width; + + if (strlen(s) < n) { + return NULL; + } + if (spec->type != SCAN_DISCARD) { + memcpy(va_arg(*args, char *), s, n); + } + return s + n; +} + +/* This is an implementation of the standard sscanf() function, with the + * following exceptions: + * + * - It returns true if the entire template was successfully scanned and + * converted, false if any conversion failed. + * + * - The standard doesn't define sscanf() behavior when an out-of-range value + * is scanned, e.g. if a "%"PRIi8 conversion scans "-1" or "0x1ff". Some + * implementations consider this an error and stop scanning. This + * implementation never considers an out-of-range value an error; instead, + * it stores the least-significant bits of the converted value in the + * destination, e.g. the value 255 for both examples earlier. + * + * - Only single-byte characters are supported, that is, the 'l' modifier + * on %s, %[, and %c is not supported. The GNU extension 'a' modifier is + * also not supported. + * + * - %p is not supported. + */ +bool +ovs_scan(const char *s, const char *template, ...) +{ + const char *const start = s; + bool ok = false; + const char *p; + va_list args; + + va_start(args, template); + p = template; + while (*p != '\0') { + struct scan_spec spec; + unsigned char c = *p++; + bool discard; + + if (isspace(c)) { + s = skip_spaces(s); + continue; + } else if (c != '%') { + if (*s != c) { + goto exit; + } + s++; + continue; + } else if (*p == '%') { + if (*s++ != '%') { + goto exit; + } + p++; + continue; + } + + /* Parse '*' flag. */ + discard = *p == '*'; + p += discard; + + /* Parse field width. */ + spec.width = 0; + while (*p >= '0' && *p <= '9') { + spec.width = spec.width * 10 + (*p++ - '0'); + } + if (spec.width == 0) { + spec.width = UINT_MAX; + } + + /* Parse type modifier. */ + switch (*p) { + case 'h': + if (p[1] == 'h') { + spec.type = SCAN_CHAR; + p += 2; + } else { + spec.type = SCAN_SHORT; + p++; + } + break; + + case 'j': + spec.type = SCAN_INTMAX_T; + p++; + break; + + case 'l': + if (p[1] == 'l') { + spec.type = SCAN_LLONG; + p += 2; + } else { + spec.type = SCAN_LONG; + p++; + } + break; + + case 'L': + case 'q': + spec.type = SCAN_LLONG; + p++; + break; + + case 't': + spec.type = SCAN_PTRDIFF_T; + p++; + break; + + case 'z': + spec.type = SCAN_SIZE_T; + p++; + break; + + default: + spec.type = SCAN_INT; + break; + } + + if (discard) { + spec.type = SCAN_DISCARD; + } + + c = *p++; + if (c != 'c' && c != 'n' && c != '[') { + s = skip_spaces(s); + } + switch (c) { + case 'd': + s = scan_int(s, &spec, 10, &args); + break; + + case 'i': + s = scan_int(s, &spec, 0, &args); + break; + + case 'o': + s = scan_int(s, &spec, 8, &args); + break; + + case 'u': + s = scan_int(s, &spec, 10, &args); + break; + + case 'x': + case 'X': + s = scan_int(s, &spec, 16, &args); + break; + + case 'e': + case 'f': + case 'g': + case 'E': + case 'G': + s = scan_float(s, &spec, &args); + break; + + case 's': + s = scan_string(s, &spec, &args); + break; + + case '[': + s = scan_set(s, &spec, &p, &args); + break; + + case 'c': + s = scan_chars(s, &spec, &args); + break; + + case 'n': + if (spec.type != SCAN_DISCARD) { + *va_arg(args, int *) = s - start; + } + break; + } + + if (!s) { + goto exit; + } + } + ok = true; + +exit: + va_end(args); + return ok; +} + diff --git a/lib/util.h b/lib/util.h index 32a7c8cd8..fd686d622 100644 --- a/lib/util.h +++ b/lib/util.h @@ -264,6 +264,8 @@ bool str_to_uint(const char *, int base, unsigned int *); bool str_to_ulong(const char *, int base, unsigned long *); bool str_to_ullong(const char *, int base, unsigned long long *); +bool ovs_scan(const char *s, const char *template, ...) SCANF_FORMAT(2, 3); + bool str_to_double(const char *, double *); int hexit_value(int c); diff --git a/tests/library.at b/tests/library.at index 6d1c6da71..5cd6c4eff 100644 --- a/tests/library.at +++ b/tests/library.at @@ -119,7 +119,8 @@ m4_foreach( [bitwise_copy], [bitwise_zero], [bitwise_one], - [bitwise_is_all_zeros]], + [bitwise_is_all_zeros], + [ovs_scan]], [AT_SETUP([testname[()] function]) AT_KEYWORDS([testname]) AT_CHECK([test-util testname], [0], [], []) diff --git a/tests/test-util.c b/tests/test-util.c index 6ed2f771d..42565091b 100644 --- a/tests/test-util.c +++ b/tests/test-util.c @@ -406,6 +406,538 @@ test_assert(int argc OVS_UNUSED, char *argv[] OVS_UNUSED) { ovs_assert(false); } + +static void +test_ovs_scan(int argc OVS_UNUSED, char *argv[] OVS_UNUSED) +{ + char str[16], str2[16], str3[16]; + long double ld, ld2; + long long ll, ll2; + signed char c, c2; + ptrdiff_t pd, pd2; + intmax_t im, im2; + size_t sz, sz2; + int n, n2, n3; + double d, d2; + short s, s2; + float f, f2; + long l, l2; + int i, i2; + + ovs_assert(ovs_scan("", "")); + ovs_assert(ovs_scan("", " ")); + ovs_assert(ovs_scan(" ", " ")); + ovs_assert(ovs_scan(" ", " ")); + ovs_assert(ovs_scan(" \t ", " ")); + + ovs_assert(ovs_scan("xyzzy", "xyzzy")); + ovs_assert(ovs_scan("xy%zzy", "xy%%zzy")); + ovs_assert(!ovs_scan(" xy%zzy", "xy%%zzy")); + ovs_assert(ovs_scan(" xy%\tzzy", " xy%% zzy")); + + ovs_assert(ovs_scan("123", "%d", &i)); + ovs_assert(i == 123); + ovs_assert(ovs_scan("0", "%d", &i)); + ovs_assert(i == 0); + ovs_assert(!ovs_scan("123", "%d%d", &i, &i2)); + ovs_assert(ovs_scan("+123", "%d", &i)); + ovs_assert(i == 123); + ovs_assert(ovs_scan("-123", "%d", &i)); + ovs_assert(i == -123); + ovs_assert(ovs_scan("0123", "%d", &i)); + ovs_assert(i == 123); + ovs_assert(ovs_scan(" 123", "%d", &i)); + ovs_assert(i == 123); + ovs_assert(ovs_scan("0x123", "%d", &i)); + ovs_assert(i == 0); + ovs_assert(ovs_scan("123", "%2d %d", &i, &i2)); + ovs_assert(i == 12); + ovs_assert(i2 == 3); + ovs_assert(ovs_scan("+123", "%2d %d", &i, &i2)); + ovs_assert(i == 1); + ovs_assert(i2 == 23); + ovs_assert(ovs_scan("-123", "%2d %d", &i, &i2)); + ovs_assert(i == -1); + ovs_assert(i2 == 23); + ovs_assert(ovs_scan("0123", "%2d %d", &i, &i2)); + ovs_assert(i == 1); + ovs_assert(i2 == 23); + ovs_assert(ovs_scan("123", "%*2d %d", &i)); + ovs_assert(i == 3); + ovs_assert(ovs_scan("+123", "%2d %*d", &i)); + ovs_assert(i == 1); + ovs_assert(i2 == 23); + ovs_assert(ovs_scan("-123", "%*2d %*d")); + + ovs_assert(ovs_scan("123", "%u", &i)); + ovs_assert(i == 123); + ovs_assert(ovs_scan("0", "%u", &i)); + ovs_assert(i == 0); + ovs_assert(!ovs_scan("123", "%u%u", &i, &i2)); + ovs_assert(ovs_scan("+123", "%u", &i)); + ovs_assert(i == 123); + ovs_assert(ovs_scan("-123", "%u", &i)); + ovs_assert(i == -123); + ovs_assert(ovs_scan("0123", "%u", &i)); + ovs_assert(i == 123); + ovs_assert(ovs_scan(" 123", "%u", &i)); + ovs_assert(i == 123); + ovs_assert(ovs_scan("0x123", "%u", &i)); + ovs_assert(i == 0); + ovs_assert(ovs_scan("123", "%2u %u", &i, &i2)); + ovs_assert(i == 12); + ovs_assert(i2 == 3); + ovs_assert(ovs_scan("+123", "%2u %u", &i, &i2)); + ovs_assert(i == 1); + ovs_assert(i2 == 23); + ovs_assert(ovs_scan("-123", "%2u %u", &i, &i2)); + ovs_assert(i == -1); + ovs_assert(i2 == 23); + ovs_assert(ovs_scan("0123", "%2u %u", &i, &i2)); + ovs_assert(i == 1); + ovs_assert(i2 == 23); + ovs_assert(ovs_scan("123", "%*2u %u", &i)); + ovs_assert(i == 3); + ovs_assert(ovs_scan("+123", "%2u %*u", &i)); + ovs_assert(i == 1); + ovs_assert(i2 == 23); + ovs_assert(ovs_scan("-123", "%*2u %*u")); + + ovs_assert(ovs_scan("123", "%i", &i)); + ovs_assert(i == 123); + ovs_assert(ovs_scan("0", "%i", &i)); + ovs_assert(i == 0); + ovs_assert(!ovs_scan("123", "%i%i", &i, &i2)); + ovs_assert(ovs_scan("+123", "%i", &i)); + ovs_assert(i == 123); + ovs_assert(ovs_scan("-123", "%i", &i)); + ovs_assert(i == -123); + ovs_assert(ovs_scan("0123", "%i", &i)); + ovs_assert(i == 0123); + ovs_assert(ovs_scan(" 123", "%i", &i)); + ovs_assert(i == 123); + ovs_assert(ovs_scan("0x123", "%i", &i)); + ovs_assert(i == 0x123); + ovs_assert(ovs_scan("123", "%2i %i", &i, &i2)); + ovs_assert(i == 12); + ovs_assert(i2 == 3); + ovs_assert(ovs_scan("+123", "%2i %i", &i, &i2)); + ovs_assert(i == 1); + ovs_assert(i2 == 23); + ovs_assert(ovs_scan("-123", "%2i %i", &i, &i2)); + ovs_assert(i == -1); + ovs_assert(i2 == 23); + ovs_assert(ovs_scan("0123", "%2i %i", &i, &i2)); + ovs_assert(i == 1); + ovs_assert(i2 == 23); + ovs_assert(ovs_scan("123", "%*2i %i", &i)); + ovs_assert(i == 3); + ovs_assert(ovs_scan("+123", "%2i %*i", &i)); + ovs_assert(i == 1); + ovs_assert(i2 == 23); + ovs_assert(ovs_scan("-123", "%*2i %*i")); + + ovs_assert(ovs_scan("123", "%o", &i)); + ovs_assert(i == 0123); + ovs_assert(ovs_scan("0", "%o", &i)); + ovs_assert(i == 0); + ovs_assert(!ovs_scan("123", "%o%o", &i, &i2)); + ovs_assert(ovs_scan("+123", "%o", &i)); + ovs_assert(i == 0123); + ovs_assert(ovs_scan("-123", "%o", &i)); + ovs_assert(i == -0123); + ovs_assert(ovs_scan("0123", "%o", &i)); + ovs_assert(i == 0123); + ovs_assert(ovs_scan(" 123", "%o", &i)); + ovs_assert(i == 0123); + ovs_assert(ovs_scan("0x123", "%o", &i)); + ovs_assert(i == 0); + ovs_assert(ovs_scan("123", "%2o %o", &i, &i2)); + ovs_assert(i == 012); + ovs_assert(i2 == 3); + ovs_assert(ovs_scan("+123", "%2o %o", &i, &i2)); + ovs_assert(i == 1); + ovs_assert(i2 == 023); + ovs_assert(ovs_scan("-123", "%2o %o", &i, &i2)); + ovs_assert(i == -1); + ovs_assert(i2 == 023); + ovs_assert(ovs_scan("0123", "%2o %o", &i, &i2)); + ovs_assert(i == 1); + ovs_assert(i2 == 023); + ovs_assert(ovs_scan("123", "%*2o %o", &i)); + ovs_assert(i == 3); + ovs_assert(ovs_scan("+123", "%2o %*o", &i)); + ovs_assert(i == 1); + ovs_assert(i2 == 023); + ovs_assert(ovs_scan("-123", "%*2o %*o")); + + ovs_assert(ovs_scan("123", "%x", &i)); + ovs_assert(i == 0x123); + ovs_assert(ovs_scan("0", "%x", &i)); + ovs_assert(i == 0); + ovs_assert(!ovs_scan("123", "%x%x", &i, &i2)); + ovs_assert(ovs_scan("+123", "%x", &i)); + ovs_assert(i == 0x123); + ovs_assert(ovs_scan("-123", "%x", &i)); + ovs_assert(i == -0x123); + ovs_assert(ovs_scan("0123", "%x", &i)); + ovs_assert(i == 0x123); + ovs_assert(ovs_scan(" 123", "%x", &i)); + ovs_assert(i == 0x123); + ovs_assert(ovs_scan("0x123", "%x", &i)); + ovs_assert(i == 0x123); + ovs_assert(ovs_scan("123", "%2x %x", &i, &i2)); + ovs_assert(i == 0x12); + ovs_assert(i2 == 3); + ovs_assert(ovs_scan("+123", "%2x %x", &i, &i2)); + ovs_assert(i == 1); + ovs_assert(i2 == 0x23); + ovs_assert(ovs_scan("-123", "%2x %x", &i, &i2)); + ovs_assert(i == -1); + ovs_assert(i2 == 0x23); + ovs_assert(ovs_scan("0123", "%2x %x", &i, &i2)); + ovs_assert(i == 1); + ovs_assert(i2 == 0x23); + ovs_assert(ovs_scan("123", "%*2x %x", &i)); + ovs_assert(i == 3); + ovs_assert(ovs_scan("+123", "%2x %*x", &i)); + ovs_assert(i == 1); + ovs_assert(i2 == 0x23); + ovs_assert(ovs_scan("-123", "%*2x %*x")); + + ovs_assert(ovs_scan("123", "%hd", &s)); + ovs_assert(s == 123); + ovs_assert(!ovs_scan("123", "%hd%hd", &s, &s2)); + ovs_assert(ovs_scan("+123", "%hd", &s)); + ovs_assert(s == 123); + ovs_assert(ovs_scan("-123", "%hd", &s)); + ovs_assert(s == -123); + ovs_assert(ovs_scan("0123", "%hd", &s)); + ovs_assert(s == 123); + ovs_assert(ovs_scan(" 123", "%hd", &s)); + ovs_assert(s == 123); + ovs_assert(ovs_scan("0x123", "%hd", &s)); + ovs_assert(s == 0); + ovs_assert(ovs_scan("123", "%2hd %hd", &s, &s2)); + ovs_assert(s == 12); + ovs_assert(s2 == 3); + ovs_assert(ovs_scan("+123", "%2hd %hd", &s, &s2)); + ovs_assert(s == 1); + ovs_assert(s2 == 23); + ovs_assert(ovs_scan("-123", "%2hd %hd", &s, &s2)); + ovs_assert(s == -1); + ovs_assert(s2 == 23); + ovs_assert(ovs_scan("0123", "%2hd %hd", &s, &s2)); + ovs_assert(s == 1); + ovs_assert(s2 == 23); + + ovs_assert(ovs_scan("123", "%hhd", &c)); + ovs_assert(c == 123); + ovs_assert(ovs_scan("0", "%hhd", &c)); + ovs_assert(c == 0); + ovs_assert(!ovs_scan("123", "%hhd%hhd", &c, &c2)); + ovs_assert(ovs_scan("+123", "%hhd", &c)); + ovs_assert(c == 123); + ovs_assert(ovs_scan("-123", "%hhd", &c)); + ovs_assert(c == -123); + ovs_assert(ovs_scan("0123", "%hhd", &c)); + ovs_assert(c == 123); + ovs_assert(ovs_scan(" 123", "%hhd", &c)); + ovs_assert(c == 123); + ovs_assert(ovs_scan("0x123", "%hhd", &c)); + ovs_assert(c == 0); + ovs_assert(ovs_scan("123", "%2hhd %hhd", &c, &c2)); + ovs_assert(c == 12); + ovs_assert(c2 == 3); + ovs_assert(ovs_scan("+123", "%2hhd %hhd", &c, &c2)); + ovs_assert(c == 1); + ovs_assert(c2 == 23); + ovs_assert(ovs_scan("-123", "%2hhd %hhd", &c, &c2)); + ovs_assert(c == -1); + ovs_assert(c2 == 23); + ovs_assert(ovs_scan("0123", "%2hhd %hhd", &c, &c2)); + ovs_assert(c == 1); + ovs_assert(c2 == 23); + + ovs_assert(ovs_scan("123", "%ld", &l)); + ovs_assert(l == 123); + ovs_assert(ovs_scan("0", "%ld", &l)); + ovs_assert(l == 0); + ovs_assert(!ovs_scan("123", "%ld%ld", &l, &l2)); + ovs_assert(ovs_scan("+123", "%ld", &l)); + ovs_assert(l == 123); + ovs_assert(ovs_scan("-123", "%ld", &l)); + ovs_assert(l == -123); + ovs_assert(ovs_scan("0123", "%ld", &l)); + ovs_assert(l == 123); + ovs_assert(ovs_scan(" 123", "%ld", &l)); + ovs_assert(l == 123); + ovs_assert(ovs_scan("0x123", "%ld", &l)); + ovs_assert(l == 0); + ovs_assert(ovs_scan("123", "%2ld %ld", &l, &l2)); + ovs_assert(l == 12); + ovs_assert(l2 == 3); + ovs_assert(ovs_scan("+123", "%2ld %ld", &l, &l2)); + ovs_assert(l == 1); + ovs_assert(l2 == 23); + ovs_assert(ovs_scan("-123", "%2ld %ld", &l, &l2)); + ovs_assert(l == -1); + ovs_assert(l2 == 23); + ovs_assert(ovs_scan("0123", "%2ld %ld", &l, &l2)); + ovs_assert(l == 1); + ovs_assert(l2 == 23); + + ovs_assert(ovs_scan("123", "%lld", &ll)); + ovs_assert(ll == 123); + ovs_assert(ovs_scan("0", "%lld", &ll)); + ovs_assert(ll == 0); + ovs_assert(!ovs_scan("123", "%lld%lld", &ll, &ll2)); + ovs_assert(ovs_scan("+123", "%lld", &ll)); + ovs_assert(ll == 123); + ovs_assert(ovs_scan("-123", "%lld", &ll)); + ovs_assert(ll == -123); + ovs_assert(ovs_scan("0123", "%lld", &ll)); + ovs_assert(ll == 123); + ovs_assert(ovs_scan(" 123", "%lld", &ll)); + ovs_assert(ll == 123); + ovs_assert(ovs_scan("0x123", "%lld", &ll)); + ovs_assert(ll == 0); + ovs_assert(ovs_scan("123", "%2lld %lld", &ll, &ll2)); + ovs_assert(ll == 12); + ovs_assert(ll2 == 3); + ovs_assert(ovs_scan("+123", "%2lld %lld", &ll, &ll2)); + ovs_assert(ll == 1); + ovs_assert(ll2 == 23); + ovs_assert(ovs_scan("-123", "%2lld %lld", &ll, &ll2)); + ovs_assert(ll == -1); + ovs_assert(ll2 == 23); + ovs_assert(ovs_scan("0123", "%2lld %lld", &ll, &ll2)); + ovs_assert(ll == 1); + ovs_assert(ll2 == 23); + + ovs_assert(ovs_scan("123", "%jd", &im)); + ovs_assert(im == 123); + ovs_assert(ovs_scan("0", "%jd", &im)); + ovs_assert(im == 0); + ovs_assert(!ovs_scan("123", "%jd%jd", &im, &im2)); + ovs_assert(ovs_scan("+123", "%jd", &im)); + ovs_assert(im == 123); + ovs_assert(ovs_scan("-123", "%jd", &im)); + ovs_assert(im == -123); + ovs_assert(ovs_scan("0123", "%jd", &im)); + ovs_assert(im == 123); + ovs_assert(ovs_scan(" 123", "%jd", &im)); + ovs_assert(im == 123); + ovs_assert(ovs_scan("0x123", "%jd", &im)); + ovs_assert(im == 0); + ovs_assert(ovs_scan("123", "%2jd %jd", &im, &im2)); + ovs_assert(im == 12); + ovs_assert(im2 == 3); + ovs_assert(ovs_scan("+123", "%2jd %jd", &im, &im2)); + ovs_assert(im == 1); + ovs_assert(im2 == 23); + ovs_assert(ovs_scan("-123", "%2jd %jd", &im, &im2)); + ovs_assert(im == -1); + ovs_assert(im2 == 23); + ovs_assert(ovs_scan("0123", "%2jd %jd", &im, &im2)); + ovs_assert(im == 1); + ovs_assert(im2 == 23); + + ovs_assert(ovs_scan("123", "%td", &pd)); + ovs_assert(pd == 123); + ovs_assert(ovs_scan("0", "%td", &pd)); + ovs_assert(pd == 0); + ovs_assert(!ovs_scan("123", "%td%td", &pd, &pd2)); + ovs_assert(ovs_scan("+123", "%td", &pd)); + ovs_assert(pd == 123); + ovs_assert(ovs_scan("-123", "%td", &pd)); + ovs_assert(pd == -123); + ovs_assert(ovs_scan("0123", "%td", &pd)); + ovs_assert(pd == 123); + ovs_assert(ovs_scan(" 123", "%td", &pd)); + ovs_assert(pd == 123); + ovs_assert(ovs_scan("0x123", "%td", &pd)); + ovs_assert(pd == 0); + ovs_assert(ovs_scan("123", "%2td %td", &pd, &pd2)); + ovs_assert(pd == 12); + ovs_assert(pd2 == 3); + ovs_assert(ovs_scan("+123", "%2td %td", &pd, &pd2)); + ovs_assert(pd == 1); + ovs_assert(pd2 == 23); + ovs_assert(ovs_scan("-123", "%2td %td", &pd, &pd2)); + ovs_assert(pd == -1); + ovs_assert(pd2 == 23); + ovs_assert(ovs_scan("0123", "%2td %td", &pd, &pd2)); + ovs_assert(pd == 1); + ovs_assert(pd2 == 23); + + ovs_assert(ovs_scan("123", "%zd", &sz)); + ovs_assert(sz == 123); + ovs_assert(ovs_scan("0", "%zd", &sz)); + ovs_assert(sz == 0); + ovs_assert(!ovs_scan("123", "%zd%zd", &sz, &sz2)); + ovs_assert(ovs_scan("+123", "%zd", &sz)); + ovs_assert(sz == 123); + ovs_assert(ovs_scan("-123", "%zd", &sz)); + ovs_assert(sz == -123); + ovs_assert(ovs_scan("0123", "%zd", &sz)); + ovs_assert(sz == 123); + ovs_assert(ovs_scan(" 123", "%zd", &sz)); + ovs_assert(sz == 123); + ovs_assert(ovs_scan("0x123", "%zd", &sz)); + ovs_assert(sz == 0); + ovs_assert(ovs_scan("123", "%2zd %zd", &sz, &sz2)); + ovs_assert(sz == 12); + ovs_assert(sz2 == 3); + ovs_assert(ovs_scan("+123", "%2zd %zd", &sz, &sz2)); + ovs_assert(sz == 1); + ovs_assert(sz2 == 23); + ovs_assert(ovs_scan("-123", "%2zd %zd", &sz, &sz2)); + ovs_assert(sz == -1); + ovs_assert(sz2 == 23); + ovs_assert(ovs_scan("0123", "%2zd %zd", &sz, &sz2)); + ovs_assert(sz == 1); + ovs_assert(sz2 == 23); + + ovs_assert(ovs_scan("0.25", "%f", &f)); + ovs_assert(f == 0.25); + ovs_assert(ovs_scan("1.0", "%f", &f)); + ovs_assert(f == 1.0); + ovs_assert(ovs_scan("-5", "%f", &f)); + ovs_assert(f == -5.0); + ovs_assert(ovs_scan("+6", "%f", &f)); + ovs_assert(f == 6.0); + ovs_assert(ovs_scan("-1e5", "%f", &f)); + ovs_assert(f == -1e5); + ovs_assert(ovs_scan("-.25", "%f", &f)); + ovs_assert(f == -.25); + ovs_assert(ovs_scan("+123.e1", "%f", &f)); + ovs_assert(f == 1230.0); + ovs_assert(ovs_scan("25e-2", "%f", &f)); + ovs_assert(f == 0.25); + ovs_assert(ovs_scan("0.25", "%1f %f", &f, &f2)); + ovs_assert(f == 0); + ovs_assert(f2 == 0.25); + ovs_assert(ovs_scan("1.0", "%2f %f", &f, &f2)); + ovs_assert(f == 1.0); + ovs_assert(f2 == 0.0); + ovs_assert(!ovs_scan("-5", "%1f", &f)); + ovs_assert(!ovs_scan("+6", "%1f", &f)); + ovs_assert(!ovs_scan("-1e5", "%2f %*f", &f)); + ovs_assert(f == -1); + ovs_assert(!ovs_scan("-.25", "%2f", &f)); + ovs_assert(!ovs_scan("+123.e1", "%6f", &f)); + ovs_assert(!ovs_scan("25e-2", "%4f", &f)); + + ovs_assert(ovs_scan("0.25", "%lf", &d)); + ovs_assert(d == 0.25); + ovs_assert(ovs_scan("1.0", "%lf", &d)); + ovs_assert(d == 1.0); + ovs_assert(ovs_scan("-5", "%lf", &d)); + ovs_assert(d == -5.0); + ovs_assert(ovs_scan("+6", "%lf", &d)); + ovs_assert(d == 6.0); + ovs_assert(ovs_scan("-1e5", "%lf", &d)); + ovs_assert(d == -1e5); + ovs_assert(ovs_scan("-.25", "%lf", &d)); + ovs_assert(d == -.25); + ovs_assert(ovs_scan("+123.e1", "%lf", &d)); + ovs_assert(d == 1230.0); + ovs_assert(ovs_scan("25e-2", "%lf", &d)); + ovs_assert(d == 0.25); + ovs_assert(ovs_scan("0.25", "%1lf %lf", &d, &d2)); + ovs_assert(d == 0); + ovs_assert(d2 == 0.25); + ovs_assert(ovs_scan("1.0", "%2lf %lf", &d, &d2)); + ovs_assert(d == 1.0); + ovs_assert(d2 == 0.0); + ovs_assert(!ovs_scan("-5", "%1lf", &d)); + ovs_assert(!ovs_scan("+6", "%1lf", &d)); + ovs_assert(!ovs_scan("-1e5", "%2lf %*f", &d)); + ovs_assert(d == -1); + ovs_assert(!ovs_scan("-.25", "%2lf", &d)); + ovs_assert(!ovs_scan("+123.e1", "%6lf", &d)); + ovs_assert(!ovs_scan("25e-2", "%4lf", &d)); + + ovs_assert(ovs_scan("0.25", "%Lf", &ld)); + ovs_assert(ld == 0.25); + ovs_assert(ovs_scan("1.0", "%Lf", &ld)); + ovs_assert(ld == 1.0); + ovs_assert(ovs_scan("-5", "%Lf", &ld)); + ovs_assert(ld == -5.0); + ovs_assert(ovs_scan("+6", "%Lf", &ld)); + ovs_assert(ld == 6.0); + ovs_assert(ovs_scan("-1e5", "%Lf", &ld)); + ovs_assert(ld == -1e5); + ovs_assert(ovs_scan("-.25", "%Lf", &ld)); + ovs_assert(ld == -.25); + ovs_assert(ovs_scan("+123.e1", "%Lf", &ld)); + ovs_assert(ld == 1230.0); + ovs_assert(ovs_scan("25e-2", "%Lf", &ld)); + ovs_assert(ld == 0.25); + ovs_assert(ovs_scan("0.25", "%1Lf %Lf", &ld, &ld2)); + ovs_assert(ld == 0); + ovs_assert(ld2 == 0.25); + ovs_assert(ovs_scan("1.0", "%2Lf %Lf", &ld, &ld2)); + ovs_assert(ld == 1.0); + ovs_assert(ld2 == 0.0); + ovs_assert(!ovs_scan("-5", "%1Lf", &ld)); + ovs_assert(!ovs_scan("+6", "%1Lf", &ld)); + ovs_assert(!ovs_scan("-1e5", "%2Lf %*f", &ld)); + ovs_assert(ld == -1); + ovs_assert(!ovs_scan("-.25", "%2Lf", &ld)); + ovs_assert(!ovs_scan("+123.e1", "%6Lf", &ld)); + ovs_assert(!ovs_scan("25e-2", "%4Lf", &ld)); + + ovs_assert(ovs_scan(" Hello,\tworld ", "%*s%n%*s%n", &n, &n2)); + ovs_assert(n == 7); + ovs_assert(n2 == 13); + ovs_assert(!ovs_scan(" Hello,\tworld ", "%*s%*s%*s")); + ovs_assert(ovs_scan(" Hello,\tworld ", "%6s%n%5s%n", str, &n, str2, &n2)); + ovs_assert(!strcmp(str, "Hello,")); + ovs_assert(n == 7); + ovs_assert(!strcmp(str2, "world")); + ovs_assert(n2 == 13); + ovs_assert(ovs_scan(" Hello,\tworld ", "%5s%5s%5s", str, str2, str3)); + ovs_assert(!strcmp(str, "Hello")); + ovs_assert(!strcmp(str2, ",")); + ovs_assert(!strcmp(str3, "world")); + ovs_assert(!ovs_scan(" ", "%*s")); + + ovs_assert(ovs_scan(" Hello,\tworld ", "%*c%n%*c%n%c%n", + &n, &n2, &c, &n3)); + ovs_assert(n == 1); + ovs_assert(n2 == 2); + ovs_assert(c == 'e'); + ovs_assert(n3 == 3); + ovs_assert(ovs_scan(" Hello,\tworld ", "%*5c%5c", str)); + ovs_assert(!memcmp(str, "o,\two", 5)); + ovs_assert(!ovs_scan(" Hello,\tworld ", "%*15c")); + + ovs_assert(ovs_scan("0x1234xyzzy", "%9[x0-9a-fA-F]%n", str, &n)); + ovs_assert(!strcmp(str, "0x1234x")); + ovs_assert(n == 7); + ovs_assert(ovs_scan("foo:bar=baz", "%5[^:=]%n:%5[^:=]%n=%5[^:=]%n", + str, &n, str2, &n2, str3, &n3)); + ovs_assert(!strcmp(str, "foo")); + ovs_assert(n == 3); + ovs_assert(!strcmp(str2, "bar")); + ovs_assert(n2 == 7); + ovs_assert(!strcmp(str3, "baz")); + ovs_assert(n3 == 11); + ovs_assert(!ovs_scan(" ", "%*[0-9]")); + ovs_assert(ovs_scan("0x123a]4xyzzy-", "%[]x0-9a-fA-F]", str)); + ovs_assert(!strcmp(str, "0x123a]4x")); + ovs_assert(ovs_scan("abc]xyz","%[^]xyz]", str)); + ovs_assert(!strcmp(str, "abc")); + ovs_assert(!ovs_scan("0x123a]4xyzzy-", "%[x0-9]a-fA-F]", str)); + ovs_assert(ovs_scan("0x12-3]xyz", "%[x0-9a-f-]", str)); + ovs_assert(!strcmp(str, "0x12-3")); + ovs_assert(ovs_scan("0x12-3]xyz", "%[^a-f-]", str)); + ovs_assert(!strcmp(str, "0x12")); + ovs_assert(sscanf("0x12-3]xyz", "%[^-a-f]", str)); + ovs_assert(!strcmp(str, "0x12")); +} static const struct command commands[] = { {"ctz", 0, 0, test_ctz}, @@ -419,6 +951,7 @@ static const struct command commands[] = { {"bitwise_is_all_zeros", 0, 0, test_bitwise_is_all_zeros}, {"follow-symlinks", 1, INT_MAX, test_follow_symlinks}, {"assert", 0, 0, test_assert}, + {"ovs_scan", 0, 0, test_ovs_scan}, {NULL, 0, 0, NULL}, };