/* sexp-format.c * * Writing s-expressions. */ /* nettle, low-level cryptographics library * * Copyright (C) 2002 Niels Möller * * The nettle library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or (at your * option) any later version. * * The nettle library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with the nettle library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, * MA 02111-1307, USA. */ #if HAVE_CONFIG_H # include "config.h" #endif #include #include #include #include #include #include "sexp.h" #include "buffer.h" #if HAVE_LIBGMP # include "bignum.h" #endif /* Code copied from sexp-conv.c: sexp_put_length */ static unsigned format_prefix(struct nettle_buffer *buffer, unsigned length) { unsigned digit = 1; unsigned prefix_length = 1; for (;;) { unsigned next = digit * 10; if (next > length) break; prefix_length++; digit = next; } 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 unsigned format_string(struct nettle_buffer *buffer, unsigned length, const uint8_t *s) { unsigned prefix_length = format_prefix(buffer, length); if (!prefix_length) return 0; if (buffer && !nettle_buffer_write(buffer, length, s)) return 0; return prefix_length + length; } unsigned sexp_vformat(struct nettle_buffer *buffer, const char *format, va_list args) { unsigned nesting = 0; unsigned done = 0; for (;;) switch (*format++) { default: { const char *start = format - 1; unsigned length = 1 + strcspn(format, "()% \t"); unsigned output_length = format_string(buffer, length, start); if (!output_length) return 0; done += output_length; format = start + length; break; } case ' ': case '\t': break; case '\0': assert(!nesting); return done; case '(': if (buffer && !NETTLE_BUFFER_PUTC(buffer, '(')) return 0; done++; nesting++; break; case ')': assert (nesting); if (buffer && !NETTLE_BUFFER_PUTC(buffer, ')')) return 0; done++; nesting--; break; case '%': { int nul_flag = 0; if (*format == '0') { format++; nul_flag = 1; } switch (*format++) { default: abort(); case '(': case ')': /* Allow unbalanced parenthesis */ if (buffer && !NETTLE_BUFFER_PUTC(buffer, format[-1])) return 0; done++; break; case 's': { const char *s; unsigned length; unsigned output_length; if (nul_flag) { s = va_arg(args, const char *); length = strlen(s); } else { length = va_arg(args, unsigned); s = va_arg(args, const char *); } output_length = format_string(buffer, length, s); if (!output_length) return 0; done += output_length; break; } case 't': { const char *s; unsigned length; unsigned output_length; if (nul_flag) { s = va_arg(args, const char *); if (!s) break; length = strlen(s); } else { length = va_arg(args, unsigned); s = va_arg(args, const char *); if (!s) break; } if (buffer && !NETTLE_BUFFER_PUTC(buffer, '[')) return 0; done++; output_length = format_string(buffer, length, s); if (!output_length) return 0; done += output_length; if (buffer && !NETTLE_BUFFER_PUTC(buffer, ']')) return 0; done++; break; } case 'l': { const char *s; unsigned length; if (nul_flag) { s = va_arg(args, const char *); length = strlen(s); } else { length = va_arg(args, unsigned); s = va_arg(args, const char *); } 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 < 0x80) length = 1; else if (x < 0x8000L) length = 2; else if (x < 0x800000L) length = 3; else if (x < 0x80000000L) length = 4; else length = 5; if (buffer && !(NETTLE_BUFFER_PUTC(buffer, '0' + length) && NETTLE_BUFFER_PUTC(buffer, ':'))) return 0; done += (2 + length); if (buffer) switch(length) { case 5: /* Leading byte needed for the sign. */ if (!NETTLE_BUFFER_PUTC(buffer, 0)) return 0; /* Fall through */ 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 *); unsigned length; unsigned prefix_length; length = nettle_mpz_sizeinbase_256_s(n); prefix_length = format_prefix(buffer, length); if (!prefix_length) return 0; done += prefix_length; 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 */ break; } } } } } 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; }