2
0
mirror of https://github.com/openvswitch/ovs synced 2025-08-31 06:15:47 +00:00

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 <blp@nicira.com>
This commit is contained in:
Ben Pfaff
2013-11-15 08:54:56 -08:00
parent 1af1ed8500
commit ed2232fc77
5 changed files with 982 additions and 1 deletions

View File

@@ -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

View File

@@ -16,6 +16,7 @@
#include <config.h>
#include "util.h"
#include <ctype.h>
#include <errno.h>
#include <limits.h>
#include <pthread.h>
@@ -26,6 +27,7 @@
#include <string.h>
#include <sys/stat.h>
#include <unistd.h>
#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;
}

View File

@@ -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);

View File

@@ -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], [], [])

View File

@@ -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},
};