diff --git a/sexp-format.c b/sexp-format.c index 2480613cc4cb86d9d1d98805664b944700c18640..22b7bc4a98deed5755581a5b992c7e6f15cab732 100644 --- a/sexp-format.c +++ b/sexp-format.c @@ -30,6 +30,7 @@ #include "sexp.h" #include "buffer.h" +#include <assert.h> #include <stdarg.h> #include <stdio.h> #include <stdlib.h> @@ -39,109 +40,203 @@ # include "bignum.h" #endif -static int +/* Code copied from sexp-conv.c: sexp_put_length */ +static unsigned format_prefix(struct nettle_buffer *buffer, unsigned length) { - unsigned prefix_length; - char prefix[10]; + unsigned digit = 1; + unsigned prefix_length = 1; + + for (;;) + { + unsigned next = digit * 10; + if (next > length) + break; - /* NOTE: Using the return value of sprintf is not entirely - * portable. */ - prefix_length = snprintf(prefix, sizeof(prefix), "%u:", length); - if (prefix_length >= sizeof(prefix)) - return 0; + prefix_length++; + digit = next; + } - return nettle_buffer_write(buffer, prefix_length, prefix); + if (buffer) + { + for (; digit; length %= digit, digit /= 10) + if (!NETTLE_BUFFER_PUTC(buffer, '0' + length / digit)) + return 0; + + if (!NETTLE_BUFFER_PUTC(buffer, ':')) + return 0; + } + + return prefix_length + 1; } -static int +static unsigned format_length_string(struct nettle_buffer *buffer, unsigned length, const char *s) { - return format_prefix(buffer, length) - && nettle_buffer_write(buffer, length, s); -} + unsigned done = format_prefix(buffer, length); + if (!done) + return 0; -static uint8_t * -format_space(struct nettle_buffer *buffer, - unsigned length) -{ - return format_prefix(buffer, length) - ? nettle_buffer_space(buffer, length) : NULL; + if (buffer && !nettle_buffer_write(buffer, length, s)) + return 0; + + return done + length; } -static int +static unsigned format_string(struct nettle_buffer *buffer, const char *s) { return format_length_string(buffer, strlen(s), s); } -int -sexp_format(struct nettle_buffer *buffer, const char *format, ...) +unsigned +sexp_vformat(struct nettle_buffer *buffer, const char *format, va_list args) { - va_list args; unsigned nesting = 0; - - va_start(args, format); + unsigned done = 0; for (;;) switch (*format++) { case '\0': - if (nesting) - { - fail: - va_end(args); - return 0; - } - else - { - va_end(args); - return 1; - } + assert(!nesting); + + return done; + case '(': - if (!NETTLE_BUFFER_PUTC(buffer, '(')) - goto fail; + if (buffer && !NETTLE_BUFFER_PUTC(buffer, '(')) + return 0; + done++; nesting++; break; case ')': - if (!nesting) - abort(); - if (!NETTLE_BUFFER_PUTC(buffer, ')')) - goto fail; - + assert (nesting); + if (buffer && !NETTLE_BUFFER_PUTC(buffer, ')')) + return 0; + + done++; nesting--; break; case '%': switch (*format++) { + case 'z': + { + const char *s = va_arg(args, const char *); + unsigned length = format_string(buffer, s); + + if (!length) + return 0; + + done += length; + break; + } case 's': { + unsigned length = va_arg(args, unsigned); + const char *s = va_arg(args, const char *); + unsigned prefix_length = format_prefix(buffer, length); + + if (!prefix_length) + return 0; + + done += prefix_length; + + if (buffer && !nettle_buffer_write(buffer, length, s)) + return 0; + + done += length; + + break; + } + case 'l': + { + unsigned length = va_arg(args, unsigned); const char *s = va_arg(args, const char *); - format_string(buffer, s); + + if (buffer && !nettle_buffer_write(buffer, length, s)) + return 0; + + done += length; + break; + } + case 'i': + { + uint32_t x = va_arg(args, uint32_t); + unsigned length; + + if (x < 0x100) + length = 1; + else if (x < 0x10000L) + length = 2; + else if (x < 0x1000000L) + length = 3; + else + length = 4; + + if (buffer && !(NETTLE_BUFFER_PUTC(buffer, '0' + length) + && NETTLE_BUFFER_PUTC(buffer, ':'))) + return 0; + + done += (2 + length); + + if (buffer) + switch(length) + { + case 4: + if (!NETTLE_BUFFER_PUTC(buffer, x >> 24)) + return 0; + /* Fall through */ + case 3: + if (!NETTLE_BUFFER_PUTC(buffer, (x >> 16) & 0xff)) + return 0; + /* Fall through */ + case 2: + if (!NETTLE_BUFFER_PUTC(buffer, (x >> 8) & 0xff)) + return 0; + /* Fall through */ + case 1: + if (!NETTLE_BUFFER_PUTC(buffer, x & 0xff)) + return 0; + break; + default: + abort(); + } break; } case 'b': { #if HAVE_LIBGMP const MP_INT *n = va_arg(args, const MP_INT *); - uint8_t *space; unsigned length; + unsigned prefix_length; - if (mpz_sgn(n) < 0) - goto fail; + assert(mpz_sgn(n) >= 0); length = nettle_mpz_sizeinbase_256(n); + prefix_length = format_prefix(buffer, length); + if (!prefix_length) + return 0; + + done += prefix_length; - space = format_space(buffer, length); - if (!space) - goto fail; - nettle_mpz_get_str_256(length, space, n); + if (buffer) + { + uint8_t *space = nettle_buffer_space(buffer, length); + if (!space) + return 0; + + nettle_mpz_get_str_256(length, space, n); + } + + done += length; + #else /* ! HAVE_LIBGMP */ abort(); #endif /* ! HAVE_LIBGMP */ @@ -152,3 +247,16 @@ sexp_format(struct nettle_buffer *buffer, const char *format, ...) } } } + +unsigned +sexp_format(struct nettle_buffer *buffer, const char *format, ...) +{ + va_list args; + unsigned done; + + va_start(args, format); + done = sexp_vformat(buffer, format, args); + va_end(args); + + return done; +} diff --git a/sexp.h b/sexp.h index 24d1906315be2bffccc565904d3f306697284571..21e88eafe004d892cc98cb58310ca9e0f996890e 100644 --- a/sexp.h +++ b/sexp.h @@ -27,6 +27,7 @@ #define NETTLE_SEXP_H_INCLUDED #include <inttypes.h> +#include <stdarg.h> enum sexp_type { SEXP_ATOM, SEXP_LIST, SEXP_END }; @@ -94,6 +95,8 @@ sexp_iterator_check_types(struct sexp_iterator *iterator, * * For a matching key, the corresponding iterator is initialized * pointing at the start of REST. + * + * On success, exits the current list. */ int sexp_iterator_assoc(struct sexp_iterator *iterator, @@ -108,15 +111,42 @@ sexp_iterator_assoc(struct sexp_iterator *iterator, /* Declared for real in buffer.h */ struct nettle_buffer; -int -sexp_format(struct nettle_buffer *buffer, const char *format, ...); +/* Returns the number of output characters, or 0 on out of memory. If + * buffer == NULL, just compute length. + * + * Format strings can contained matched parentheses, and the following + * formatting specifiers: + * + * %z NUL-terminated string, const uint8_t *. + * + * %s String represented as unsigned length, const uint8_t *data. + * + * %i Non-negative small integer, uint32_t. + * + * %b Non-negative bignum, mpz_t. + * + * %l Literal string (no length added), typically a balanced + * subexpression. Represented as unsigned length, const uint8_t + * *data. + */ -int +unsigned +sexp_format(struct nettle_buffer *buffer, + const char *format, ...); + +unsigned +sexp_vformat(struct nettle_buffer *buffer, + const char *format, va_list args); + +/* FIXME: Add argument LINE_WIDTH. If non-zero, break lines to at most + * that width. */ + +unsigned sexp_transport_format(struct nettle_buffer *buffer, - /* If non-zero, break lines to at most - * line_length characters. */ - unsigned line_length, const char *format, ...); +unsigned +sexp_transport_vformat(struct nettle_buffer *buffer, + const char *format, va_list args); #endif /* NETTLE_SEXP_H_INCLUDED */