Newer
Older
*
* Writing s-expressions.
*/
/* nettle, low-level cryptographics library
*
* Copyright (C) 2002 Niels Mller
*
* 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"
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "sexp.h"
#include "buffer.h"
#if HAVE_LIBGMP
# include "bignum.h"
#endif
/* Code copied from sexp-conv.c: sexp_put_length */
static unsigned
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;
for (;;)
switch (*format++)
{
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;
assert(!nesting);
return done;
if (buffer && !NETTLE_BUFFER_PUTC(buffer, '('))
return 0;
nesting++;
break;
case ')':
assert (nesting);
if (buffer && !NETTLE_BUFFER_PUTC(buffer, ')'))
return 0;
done++;
nesting--;
break;
case '%':
if (*format == '0')
{
format++;
nul_flag = 1;
case '(':
case ')':
/* Allow unbalanced parenthesis */
if (buffer && !NETTLE_BUFFER_PUTC(buffer, format[-1]))
return 0;
done++;
break;
case 's':
{
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 *);
}
output_length = format_string(buffer, length, s);
if (!output_length)
break;
}
case 't':
{
const char *s;
unsigned 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 (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;
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
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':
{
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);
}
#else /* ! HAVE_LIBGMP */
#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;
}