diff --git a/lib/dynamic-string.c b/lib/dynamic-string.c index 914af64c1..a6c8f6c76 100644 --- a/lib/dynamic-string.c +++ b/lib/dynamic-string.c @@ -361,6 +361,29 @@ ds_swap(struct ds *a, struct ds *b) *b = temp; } +void +ds_put_hex(struct ds *ds, const void *buf_, size_t size) +{ + const uint8_t *buf = buf_; + bool printed = false; + int i; + + for (i = 0; i < size; i++) { + uint8_t val = buf[i]; + if (val || printed) { + if (!printed) { + ds_put_format(ds, "0x%"PRIx8, val); + } else { + ds_put_format(ds, "%02"PRIx8, val); + } + printed = true; + } + } + if (!printed) { + ds_put_char(ds, '0'); + } +} + /* Writes the 'size' bytes in 'buf' to 'string' as hex bytes arranged 16 per * line. Numeric offsets are also included, starting at 'ofs' for the first * byte in 'buf'. If 'ascii' is true then the corresponding ASCII characters diff --git a/lib/dynamic-string.h b/lib/dynamic-string.h index dc5981ac2..95172d102 100644 --- a/lib/dynamic-string.h +++ b/lib/dynamic-string.h @@ -55,6 +55,7 @@ void ds_put_format(struct ds *, const char *, ...) OVS_PRINTF_FORMAT(2, 3); void ds_put_format_valist(struct ds *, const char *, va_list) OVS_PRINTF_FORMAT(2, 0); void ds_put_printable(struct ds *, const char *, size_t); +void ds_put_hex(struct ds *ds, const void *buf, size_t size); void ds_put_hex_dump(struct ds *ds, const void *buf_, size_t size, uintptr_t ofs, bool ascii); int ds_get_line(struct ds *, FILE *); diff --git a/lib/learn.c b/lib/learn.c index 99d56e601..8ff1e0a89 100644 --- a/lib/learn.c +++ b/lib/learn.c @@ -190,29 +190,14 @@ static char * OVS_WARN_UNUSED_RESULT learn_parse_load_immediate(const char *s, struct ofpact_learn_spec *spec) { const char *full_s = s; - const char *arrow = strstr(s, "->"); struct mf_subfield dst; union mf_subvalue imm; char *error; + int err; - memset(&imm, 0, sizeof imm); - if (s[0] == '0' && (s[1] == 'x' || s[1] == 'X') && arrow) { - const char *in = arrow - 1; - uint8_t *out = imm.u8 + sizeof imm.u8 - 1; - int n = arrow - (s + 2); - int i; - - for (i = 0; i < n; i++) { - int hexit = hexit_value(in[-i]); - if (hexit < 0) { - return xasprintf("%s: bad hex digit in value", full_s); - } - out[-(i / 2)] |= i % 2 ? hexit << 4 : hexit; - } - s = arrow; - } else { - ovs_be64 *last_be64 = &imm.be64[ARRAY_SIZE(imm.be64) - 1]; - *last_be64 = htonll(strtoull(s, (char **) &s, 0)); + err = parse_int_string(s, imm.u8, sizeof imm.u8, (char **) &s); + if (err) { + return xasprintf("%s: bad hex digit in value", full_s); } if (strncmp(s, "->", 2)) { diff --git a/lib/meta-flow.c b/lib/meta-flow.c index 124b5256c..757843dfb 100644 --- a/lib/meta-flow.c +++ b/lib/meta-flow.c @@ -2300,18 +2300,7 @@ mf_get_subfield(const struct mf_subfield *sf, const struct flow *flow) void mf_format_subvalue(const union mf_subvalue *subvalue, struct ds *s) { - int i; - - for (i = 0; i < ARRAY_SIZE(subvalue->u8); i++) { - if (subvalue->u8[i]) { - ds_put_format(s, "0x%"PRIx8, subvalue->u8[i]); - for (i++; i < ARRAY_SIZE(subvalue->u8); i++) { - ds_put_format(s, "%02"PRIx8, subvalue->u8[i]); - } - return; - } - } - ds_put_char(s, '0'); + ds_put_hex(s, subvalue->u8, sizeof subvalue->u8); } void diff --git a/lib/util.c b/lib/util.c index bcf770051..c7e2b77f5 100644 --- a/lib/util.c +++ b/lib/util.c @@ -738,6 +738,87 @@ hexits_value(const char *s, size_t n, bool *ok) return value; } +/* Parses the string in 's' as an integer in either hex or decimal format and + * puts the result right justified in the array 'valuep' that is 'field_width' + * big. If the string is in hex format, the value may be arbitrarily large; + * integers are limited to 64-bit values. (The rationale is that decimal is + * likely to represent a number and 64 bits is a reasonable maximum whereas + * hex could either be a number or a byte string.) + * + * On return 'tail' points to the first character in the string that was + * not parsed as part of the value. ERANGE is returned if the value is too + * large to fit in the given field. */ +int +parse_int_string(const char *s, uint8_t *valuep, int field_width, char **tail) +{ + unsigned long long int integer; + int i; + + if (!strncmp(s, "0x", 2) || !strncmp(s, "0X", 2)) { + uint8_t *hexit_str; + int len = 0; + int val_idx; + int err = 0; + + s += 2; + hexit_str = xmalloc(field_width * 2); + + for (;;) { + uint8_t hexit; + bool ok; + + s += strspn(s, " \t\r\n"); + hexit = hexits_value(s, 1, &ok); + if (!ok) { + *tail = CONST_CAST(char *, s); + break; + } + + if (hexit != 0 || len) { + if (DIV_ROUND_UP(len + 1, 2) > field_width) { + err = ERANGE; + goto free; + } + + hexit_str[len] = hexit; + len++; + } + s++; + } + + val_idx = field_width; + for (i = len - 1; i >= 0; i -= 2) { + val_idx--; + valuep[val_idx] = hexit_str[i]; + if (i > 0) { + valuep[val_idx] += hexit_str[i - 1] << 4; + } + } + + memset(valuep, 0, val_idx); + +free: + free(hexit_str); + return err; + } + + errno = 0; + integer = strtoull(s, tail, 0); + if (errno) { + return errno; + } + + for (i = field_width - 1; i >= 0; i--) { + valuep[i] = integer; + integer >>= 8; + } + if (integer) { + return ERANGE; + } + + return 0; +} + /* Returns the current working directory as a malloc()'d string, or a null * pointer if the current working directory cannot be determined. */ char * diff --git a/lib/util.h b/lib/util.h index 276edb569..78abfd388 100644 --- a/lib/util.h +++ b/lib/util.h @@ -314,6 +314,9 @@ bool str_to_double(const char *, double *); int hexit_value(int c); uintmax_t hexits_value(const char *s, size_t n, bool *ok); +int parse_int_string(const char *s, uint8_t *valuep, int field_width, + char **tail); + const char *english_list_delimiter(size_t index, size_t total); char *get_cwd(void);