#include "sexp.h"
#include "buffer.h"
+#include <assert.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
# 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 */
}
}
}
+
+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;
+}
#define NETTLE_SEXP_H_INCLUDED
#include <inttypes.h>
+#include <stdarg.h>
enum sexp_type
{ SEXP_ATOM, SEXP_LIST, SEXP_END };
*
* 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,
/* 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 */