From fe5c484c4e3da3465f63c393a0386c7f87aaae71 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20M=C3=B6ller?= <nisse@lysator.liu.se> Date: Thu, 7 Nov 2002 09:26:30 +0100 Subject: [PATCH] (sexp_format): Return length of output. Allow buffer == NULL, and onyl compute the needed length in this case. Renamed %s to %z. New format specifiers %s, %i, and %l. (sexp_vformat): New function. (format_prefix): Rewrote to not use snprintf. Rev: src/nettle/sexp-format.c:1.2 Rev: src/nettle/sexp.h:1.7 --- sexp-format.c | 210 ++++++++++++++++++++++++++++++++++++++------------ sexp.h | 42 ++++++++-- 2 files changed, 195 insertions(+), 57 deletions(-) diff --git a/sexp-format.c b/sexp-format.c index 2480613c..22b7bc4a 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 24d19063..21e88eaf 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 */ -- GitLab