sexp-format.c 6.18 KB
Newer Older
1
/* sexp-format.c
2
3
4
5
6
7
 *
 * Writing s-expressions.
 */

/* nettle, low-level cryptographics library
 *
Niels Möller's avatar
Niels Möller committed
8
 * Copyright (C) 2002 Niels Möller
9
10
11
12
13
14
15
16
17
18
19
20
21
 *  
 * 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
22
23
 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
 * MA 02111-1301, USA.
24
25
26
 */

#if HAVE_CONFIG_H
27
# include "config.h"
28
29
#endif

30
#include <assert.h>
31
32
33
34
35
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

36
37
38
#include "sexp.h"
#include "buffer.h"

39
#include "bignum.h"
40

41
static unsigned
Niels Möller's avatar
Niels Möller committed
42
format_prefix(struct nettle_buffer *buffer,
43
	      size_t length)
44
{
45
  size_t digit = 1;
46
47
48
49
  unsigned prefix_length = 1;
  
  for (;;)
    {
50
      size_t next = digit * 10;
51
52
      if (next > length)
	break;
53

54
55
56
      prefix_length++;
      digit = next;
    }
57

58
59
60
61
62
63
64
65
66
67
68
  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;
69
70
}

71
static size_t
Niels Möller's avatar
Niels Möller committed
72
format_string(struct nettle_buffer *buffer,
73
	      size_t length, const uint8_t *s)
Niels Möller's avatar
Niels Möller committed
74
75
76
77
78
79
80
81
82
83
84
{
  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;
}

85
size_t
86
sexp_vformat(struct nettle_buffer *buffer, const char *format, va_list args)
87
88
{
  unsigned nesting = 0;
89
  size_t done = 0;
90

91
92
93
  for (;;)
    switch (*format++)
      {
94
      default:
Niels Möller's avatar
Niels Möller committed
95
96
	{
	  const char *start = format - 1;
97
98
	  size_t length = 1 + strcspn(format, "()% \t");
	  size_t output_length = format_string(buffer, length, start);
Niels Möller's avatar
Niels Möller committed
99
100
101
102
103
104
105
106
	  if (!output_length)
	    return 0;
	  
	  done += output_length;
	  format = start + length;

	  break;
	}
107
108
109
      case ' ': case '\t':
	break;
	
110
      case '\0':
111
112
113
114
	assert(!nesting);
	    
	return done;

115
      case '(':
116
117
	if (buffer && !NETTLE_BUFFER_PUTC(buffer, '('))
	  return 0;
118

119
	done++;
120
121
122
123
	nesting++;
	break;

      case ')':
124
125
126
127
128
	assert (nesting);
	if (buffer && !NETTLE_BUFFER_PUTC(buffer, ')'))
	  return 0;

	done++;
129
130
131
132
	nesting--;
	break;

      case '%':
133
134
	{
	  int nul_flag = 0;
135

136
137
138
139
	  if (*format == '0')
	    {
	      format++;
	      nul_flag = 1;
140
	    }
141
	  switch (*format++)
142
	    {
143
144
	    default:
	      abort();
145

146
147
148
149
150
151
152
153
	    case '(':
	    case ')':
	      /* Allow unbalanced parenthesis */
	      if (buffer && !NETTLE_BUFFER_PUTC(buffer, format[-1]))
		return 0;
	      done++;
	      break;
	      
154
155
156
	    case 's':
	      {
		const char *s;
157
158
		size_t length;
		size_t output_length;
159
160
161
162
163
164
165
166
		
		if (nul_flag)
		  {
		    s = va_arg(args, const char *);
		    length = strlen(s);
		  }
		else
		  {
167
		    length = va_arg(args, size_t);
168
169
170
		    s = va_arg(args, const char *);
		  }
		
Niels Möller's avatar
Niels Möller committed
171
172
		output_length = format_string(buffer, length, s);
		if (!output_length)
173
174
		  return 0;

Niels Möller's avatar
Niels Möller committed
175
		done += output_length;
176
177
178
179
180
		break;
	      }
	    case 't':
	      {
		const char *s;
181
182
		size_t length;
		size_t output_length;
183
184
185
186
187
188
189
190
191
192
193
		
		if (nul_flag)
		  {
		    s = va_arg(args, const char *);
		    if (!s)
		      break;
		    
		    length = strlen(s);
		  }
		else
		  {
194
		    length = va_arg(args, size_t);
195
196
197
198
199
200
201
202
203
		    s = va_arg(args, const char *);
		    if (!s)
		      break;
		  }
		
		if (buffer && !NETTLE_BUFFER_PUTC(buffer, '['))
		  return 0;
		done++;
		
Niels Möller's avatar
Niels Möller committed
204
		output_length = format_string(buffer, length, s);
205
	      
Niels Möller's avatar
Niels Möller committed
206
		if (!output_length)
207
		  return 0;
208

Niels Möller's avatar
Niels Möller committed
209
210
		done += output_length;
		
211
212
213
214
215
216
		if (buffer && !NETTLE_BUFFER_PUTC(buffer, ']'))
		  return 0;
		done++;
		
		break;
	      }
217
	      
218
219
220
	    case 'l':
	      {
		const char *s;
221
		size_t length;
222
223
224
225
226
227
228
229
		
		if (nul_flag)
		  {
		    s = va_arg(args, const char *);
		    length = strlen(s);
		  }
		else
		  {
230
		    length = va_arg(args, size_t);
231
232
233
234
235
		    s = va_arg(args, const char *);
		  }

		if (buffer && !nettle_buffer_write(buffer, length, s))
		  return 0;
236
	      
237
238
239
240
241
242
243
		done += length;
		break;
	      }
	    case 'i':
	      {
		uint32_t x = va_arg(args, uint32_t);
		unsigned length;
244
	      
245
246
247
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
		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 *);
294
		size_t length;
295
		unsigned prefix_length;
296
	      
297
298
299
300
301
302
303
304
305
306
307
308
		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;
309
		  
310
311
		    nettle_mpz_get_str_256(length, space, n);
		  }
312

313
		done += length;
314
	      
315
316
		break;
	      }
317
	    }
318
	}
319
320
      }
}
321

322
size_t
323
324
325
sexp_format(struct nettle_buffer *buffer, const char *format, ...)
{
  va_list args;
326
  size_t done;
327
328
329
330
331
332
333
  
  va_start(args, format);
  done = sexp_vformat(buffer, format, args);
  va_end(args);

  return done;
}