sexp-conv.c 17.4 KB
Newer Older
Niels Möller's avatar
Niels Möller committed
1
2
3
4
5
6
/* sexp-conv.c
 *
 * Conversion tool for handling the different flavours of sexp
 * syntax. */

#include "base64.h"
7
8
9
#include "buffer.h"

#include <assert.h>
Niels Möller's avatar
Niels Möller committed
10
11
#include <errno.h>
#include <stdarg.h>
12
13
#include <stdio.h>
#include <stdlib.h>
Niels Möller's avatar
Niels Möller committed
14
#include <string.h>
15

Niels Möller's avatar
Niels Möller committed
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
void
die(const char *format, ...)
#if __GNUC___
     __attribute__((__format__ (__printf__,1, 2)))
     __attribute__((__noreturn__))
#endif
     ;

void
die(const char *format, ...)
{
  va_list args;
  va_start(args, format);
  vfprintf(stderr, format, args);
  va_end(args);

  exit(EXIT_FAILURE);
}
Niels Möller's avatar
Niels Möller committed
34
35
36
37
38
39

enum sexp_mode
  {
    SEXP_CANONICAL = 0,
    SEXP_ADVANCED = 1,
    /* OR:ed with SEXP_CANONICAL or SEXP_ADVANCED when reading
40
     * transport data */
Niels Möller's avatar
Niels Möller committed
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
    SEXP_TRANSPORT = 2,
  };

enum sexp_token
  {
    SEXP_STRING,
    SEXP_DISPLAY_START,
    SEXP_DISPLAY_END,
    SEXP_LIST_START,
    SEXP_LIST_END,
    SEXP_TRANSPORT_START,
    SEXP_TRANSPORT_END,
    SEXP_EOF,
  };

56
57
58
59
60
61
62
enum sexp_coding
  {
    SEXP_PLAIN,
    SEXP_BASE64,
    SEXP_HEX,
  };
    
Niels Möller's avatar
Niels Möller committed
63
64
65
66
struct sexp_input
{
  FILE *f;
  
67
  enum sexp_coding coding;
Niels Möller's avatar
Niels Möller committed
68
69
70
71
72
73
74
75
76
77
78
79
80
  /* Used in transport mode */
  struct base64_decode_ctx base64;

  /* Type of current token */
  enum sexp_token token;

  /* Current token */
  struct nettle_buffer string;

  /* Nesting level */
  unsigned level;
};

81
static void
82
sexp_input_init(struct sexp_input *input, FILE *f)
83
84
{
  input->f = f;
85
  input->coding = SEXP_PLAIN;
86
  input->level = 0;
87
88

  nettle_buffer_init(&input->string);
89
90
}

Niels Möller's avatar
Niels Möller committed
91
92
93
94
struct sexp_output
{
  FILE *f;

95
  enum sexp_coding coding;
Niels Möller's avatar
Niels Möller committed
96
  enum sexp_mode mode;
97
  struct base64_encode_ctx base64;
Niels Möller's avatar
Niels Möller committed
98
99
100
101

  unsigned pos;
};

102
103
104
105
static void
sexp_output_init(struct sexp_output *output, FILE *f, enum sexp_mode mode)
{
  output->f = f;
106
  output->coding = SEXP_PLAIN;
107
108
109
110
111
112
113
  output->mode = mode;
  output->pos = 0;
}


/* Input */

Niels Möller's avatar
Niels Möller committed
114
/* Returns 1 on success. For special tokens,
Niels Möller's avatar
Niels Möller committed
115
116
117
118
 * return 0 and set input->token accordingly. */
static int
sexp_get_char(struct sexp_input *input, uint8_t *out)
{
119
  switch (input->coding)
Niels Möller's avatar
Niels Möller committed
120
    {
121
    case SEXP_BASE64:
Niels Möller's avatar
Niels Möller committed
122
123
124
125
126
      for (;;)
	{
	  int done;
	  int c = getc(input->f);
	  if (c < 0)
Niels Möller's avatar
Niels Möller committed
127
	    die("Unexpected end of file in base64 data.\n");
Niels Möller's avatar
Niels Möller committed
128
129
130

	  if (c == '}')
	    {
131
	      if (base64_decode_status(&input->base64))
Niels Möller's avatar
Niels Möller committed
132
133
134
135
136
		{
		  input->token = SEXP_TRANSPORT_END;
		  return 0;
		}
	      else
Niels Möller's avatar
Niels Möller committed
137
		die("Invalid base64 data.\n");
Niels Möller's avatar
Niels Möller committed
138
139
	    }

140
	  done = base64_decode_single(&input->base64, out, c);
Niels Möller's avatar
Niels Möller committed
141
142
143
	  if (done)
	    return 1;
	}
144
145
146
    case SEXP_PLAIN:
      {
	int c = getc(input->f);
Niels Möller's avatar
Niels Möller committed
147
      
148
149
150
151
	if (c < 0)
	  {
	    if (ferror(input->f))
	      die("Read error: %s\n", strerror(errno));
152
	  
153
154
155
	    input->token = SEXP_EOF;
	    return 0;
	  }
156

157
158
159
160
161
162
	*out = c;
	return 1;
      }
    case SEXP_HEX:
      /* Not yet implemented */
      abort();
163
    }
Niels Möller's avatar
Niels Möller committed
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
}

static const char
token_chars[0x80] =
  {
    /* 0, ... 0x1f */
    0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
    0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
    /* SPC ! " # $ % & '  ( ) * + , - . / */
    0,0,0,0,0,0,0,0, 0,0,1,1,0,1,1,1,
    /* 0 1 2 3 4 5 6 7  8 9 : ; < = > ? */
    1,1,1,1,1,1,1,1, 1,1,1,0,0,1,0,0,
    /* @ A ... O */
    0,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,	
    /* P ...             Z [ \] ^ _ */
    1,1,1,1,1,1,1,1, 1,1,1,0,0,0,0,1,
    /* ` a, ... o */
    0,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,	
    /* p ...             z { | } ~ DEL */
    1,1,1,1,1,1,1,1, 1,1,1,0,0,0,0,0,
  };

#define TOKEN_CHAR(c) ((c) < 0x80 && token_chars[(c)])

/* Returns 0 at end of token */
static uint8_t
sexp_get_token_char(struct sexp_input *input)
{
  int c = getc(input->f);
  if (c >= 0 && TOKEN_CHAR(c))
    return c;

196
  ungetc(c, input->f);
Niels Möller's avatar
Niels Möller committed
197
198
199
  return 0;
}
     
Niels Möller's avatar
Niels Möller committed
200
/* Return 0 at end-of-string */
Niels Möller's avatar
Niels Möller committed
201
202
203
static int
sexp_get_quoted_char(struct sexp_input *input, uint8_t *c)
{
204
  if (!sexp_get_char(input, c))
Niels Möller's avatar
Niels Möller committed
205
    die("Unexpected end of file in quoted string.\n");
Niels Möller's avatar
Niels Möller committed
206
207
208
209

  for (;;)
    switch (*c)
      {
210
211
      default:
	return 1;
Niels Möller's avatar
Niels Möller committed
212
213
214
      case '\"':
	return 0;
      case '\\':
Niels Möller's avatar
Niels Möller committed
215
216
217
	if (!sexp_get_char(input, c))
	  die("Unexpected end of file in quoted string.\n");

Niels Möller's avatar
Niels Möller committed
218
219
220
221
222
223
224
225
226
227
228
229
230
	switch (*c)
	  {
	  case 'b': *c = '\b'; return 1;
	  case 't': *c = '\t'; return 1;
	  case 'n': *c = '\n'; return 1;
	  case 'f': *c = '\f'; return 1;
	  case 'r': *c = '\r'; return 1;
	  case '\\': *c = '\\'; return 1;
	  case 'o':
	  case 'x':
	    /* Not implemnted */
	    abort();
	  case '\n':
Niels Möller's avatar
Niels Möller committed
231
232
233
234
	    if (!sexp_get_char(input, c))
	      die("Unexpected end of file in quoted string.\n");
	    if (*c == '\r' && !sexp_get_char(input, c))
	      die("Unexpected end of file in quoted string.\n");
Niels Möller's avatar
Niels Möller committed
235
236
	    break;
	  case '\r':
Niels Möller's avatar
Niels Möller committed
237
238
239
240
	    if (!sexp_get_char(input, c))
	      die("Unexpected end of file in quoted string.\n");
	    if (*c == '\n' && !sexp_get_char(input, c))
	      die("Unexpected end of file in quoted string.\n");
Niels Möller's avatar
Niels Möller committed
241
242
243
244
245
	    break;
	  }
      }
}

Niels Möller's avatar
Niels Möller committed
246
static void
Niels Möller's avatar
Niels Möller committed
247
248
sexp_get_quoted_string(struct sexp_input *input)
{
Niels Möller's avatar
Niels Möller committed
249
250
  uint8_t c;

251
252
  assert(input->coding == SEXP_PLAIN);
  
Niels Möller's avatar
Niels Möller committed
253
254
255
  while (sexp_get_quoted_char(input, &c))
    if (!NETTLE_BUFFER_PUTC(&input->string, c))
      die("Virtual memory exhasuted.\n");
Niels Möller's avatar
Niels Möller committed
256
257
}

Niels Möller's avatar
Niels Möller committed
258
static void
Niels Möller's avatar
Niels Möller committed
259
260
261
262
263
264
sexp_get_hex_string(struct sexp_input *input)
{
  /* Not implemented */
  abort();
}

Niels Möller's avatar
Niels Möller committed
265
static void
Niels Möller's avatar
Niels Möller committed
266
267
268
269
sexp_get_base64_string(struct sexp_input *input)
{
  struct base64_decode_ctx ctx;

270
  assert(input->coding == SEXP_PLAIN);
Niels Möller's avatar
Niels Möller committed
271
272
273
274
275
276
277
278

  base64_decode_init(&ctx);
  
  for (;;)
    {
      uint8_t c;
      uint8_t decoded;
      
Niels Möller's avatar
Niels Möller committed
279
280
      if (!sexp_get_char(input, &c))
	die("Unexpected end of file in base64 string.\n");
Niels Möller's avatar
Niels Möller committed
281
282

      if (c == '|')
Niels Möller's avatar
Niels Möller committed
283
284
285
286
287
	{
	  if (!base64_decode_status(&ctx))
	    die("Invalid base64 string.\n");
	  return;
	}
Niels Möller's avatar
Niels Möller committed
288
      
Niels Möller's avatar
Niels Möller committed
289
290
291
      if (base64_decode_single(&ctx, &decoded, c)
	  && !NETTLE_BUFFER_PUTC(&input->string, decoded))
	die("Virtual memory exhasuted.\n");	
Niels Möller's avatar
Niels Möller committed
292
293
294
    }
}

Niels Möller's avatar
Niels Möller committed
295
static void
296
sexp_get_token_string(struct sexp_input *input, uint8_t c)
Niels Möller's avatar
Niels Möller committed
297
{
298
  assert(input->coding == SEXP_PLAIN);
Niels Möller's avatar
Niels Möller committed
299
300

  if (!TOKEN_CHAR(c) || ! NETTLE_BUFFER_PUTC(&input->string, c))
Niels Möller's avatar
Niels Möller committed
301
    die("Invalid token.\n");
Niels Möller's avatar
Niels Möller committed
302
303
304
305
  
  while ( (c = sexp_get_token_char(input)) > 0)
    {
      if (!NETTLE_BUFFER_PUTC(&input->string, c))
Niels Möller's avatar
Niels Möller committed
306
	die("Virtual memory exhasuted.\n");	
Niels Möller's avatar
Niels Möller committed
307
308
309
310
311
    }

  assert (input->string.size);
}

Niels Möller's avatar
Niels Möller committed
312
static void
Niels Möller's avatar
Niels Möller committed
313
314
315
316
317
318
319
320
sexp_get_string(struct sexp_input *input, uint8_t c)
{
  input->string.size = 0;
  input->token = SEXP_STRING;
  
  switch (c)
    {
    case '\"':
Niels Möller's avatar
Niels Möller committed
321
322
323
      sexp_get_quoted_string(input);
      break;
      
Niels Möller's avatar
Niels Möller committed
324
    case '#':
Niels Möller's avatar
Niels Möller committed
325
326
      sexp_get_hex_string(input);
      break;;
Niels Möller's avatar
Niels Möller committed
327
328

    case '|':
Niels Möller's avatar
Niels Möller committed
329
330
      sexp_get_base64_string(input);
      break;
331

Niels Möller's avatar
Niels Möller committed
332
    default:
Niels Möller's avatar
Niels Möller committed
333
334
      sexp_get_token_string(input, c);
      break;
Niels Möller's avatar
Niels Möller committed
335
336
337
    }
}

Niels Möller's avatar
Niels Möller committed
338
static void
339
340
sexp_get_string_length(struct sexp_input *input, enum sexp_mode mode,
		       unsigned length)
Niels Möller's avatar
Niels Möller committed
341
342
343
344
345
346
347
348
349
{
  uint8_t c;
	
  input->string.size = 0;
  input->token = SEXP_STRING;
  
  if (!length)
    {
      /* There must ne no more digits */
Niels Möller's avatar
Niels Möller committed
350
351
      if (!sexp_get_char(input, &c))
	die("Unexpected end of file in string.\n");
Niels Möller's avatar
Niels Möller committed
352
353
354
355
356
    }
  else
    /* Get rest of digits */
    for (;;)
      {
Niels Möller's avatar
Niels Möller committed
357
358
	if (!sexp_get_char(input, &c))
	  die("Unexpected end of file in string.\n");
Niels Möller's avatar
Niels Möller committed
359

360
	if (c < '0' || c > '9')
Niels Möller's avatar
Niels Möller committed
361
362
363
364
365
366
367
368
369
370
371
	  break;
	
	/* FIXME: Check for overflow? */
	length = length * 10 + c - '0';
      }

  switch(c)
    {
    case ':':
      /* Verbatim */
      for (; length; length--)
Niels Möller's avatar
Niels Möller committed
372
	if (!sexp_get_char(input, &c)
Niels Möller's avatar
Niels Möller committed
373
	    || !NETTLE_BUFFER_PUTC(&input->string, c))
Niels Möller's avatar
Niels Möller committed
374
	  die("Unexpected end of file in string.\n");
Niels Möller's avatar
Niels Möller committed
375
      
Niels Möller's avatar
Niels Möller committed
376
      return;
Niels Möller's avatar
Niels Möller committed
377
378

    case '"':
379
      if (mode != SEXP_ADVANCED)
Niels Möller's avatar
Niels Möller committed
380
	die("Encountered quoted string in canonical mode.\n");
Niels Möller's avatar
Niels Möller committed
381
382

      for (; length; length--)
Niels Möller's avatar
Niels Möller committed
383
	if (!sexp_get_quoted_char(input, &c)
Niels Möller's avatar
Niels Möller committed
384
	    || !NETTLE_BUFFER_PUTC(&input->string, c))
Niels Möller's avatar
Niels Möller committed
385
	  die("Unexpected end of string.\n");
Niels Möller's avatar
Niels Möller committed
386

Niels Möller's avatar
Niels Möller committed
387
388
      if (sexp_get_quoted_char(input, &c))
	die("Quoted string longer than expected.\n");
Niels Möller's avatar
Niels Möller committed
389
390
391
392
393
394
395
      
    case '#':
    case '|':
      /* Not yet implemented */
      abort();

    default:
Niels Möller's avatar
Niels Möller committed
396
      die("Invalid string.\n");
Niels Möller's avatar
Niels Möller committed
397
398
399
    }
}

Niels Möller's avatar
Niels Möller committed
400
static void
401
sexp_get_token(struct sexp_input *input, enum sexp_mode mode)
Niels Möller's avatar
Niels Möller committed
402
403
{
  uint8_t c;
404

405
  for(;;)
Niels Möller's avatar
Niels Möller committed
406
407
408
409
410
411
412
    if (!sexp_get_char(input, &c))
      return;
    else
      switch(c)
	{
	case '0': case '1': case '2': case '3': case '4':
	case '5': case '6': case '7': case '8': case '9':
413
	  sexp_get_string_length(input, mode, c - '0');
Niels Möller's avatar
Niels Möller committed
414
	  return;
415
	  
Niels Möller's avatar
Niels Möller committed
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
	case '(':
	  input->token = SEXP_LIST_START;
	  return;
	  
	case ')':
	  input->token = SEXP_LIST_END;
	  return;

	case '[':
	  input->token = SEXP_DISPLAY_START;
	  return;

	case ']':
	  input->token = SEXP_DISPLAY_END;
	  return;

	case ' ':  /* SPC, TAB, LF, CR */
	case '\t':
	case '\n':
	case '\r':
436
	  if (mode == SEXP_CANONICAL)
Niels Möller's avatar
Niels Möller committed
437
438
	    die("Whitespace encountered in canonical mode.\n");
	  break;
Niels Möller's avatar
Niels Möller committed
439

Niels Möller's avatar
Niels Möller committed
440
	case ';': /* Comments */
441
	  if (mode == SEXP_CANONICAL)
Niels Möller's avatar
Niels Möller committed
442
	    die("Comment encountered in canonical mode.\n");
Niels Möller's avatar
Niels Möller committed
443

Niels Möller's avatar
Niels Möller committed
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
	  for (;;)
	    {
	      int c = getc(input->f);
	      if (c < 0)
		{
		  if (ferror(input->f))
		    die("Read failed: %s.\n", strerror(errno));
		  else
		    {
		      input->token = SEXP_EOF;
		      return;
		    }
		}
	      if (c == '\n')
		break;
	    }
	  break;
Niels Möller's avatar
Niels Möller committed
461
	  
Niels Möller's avatar
Niels Möller committed
462
463
	default:
	  /* Ought to be a string */
464
	  if (mode != SEXP_ADVANCED)
Niels Möller's avatar
Niels Möller committed
465
466
467
468
469
	    die("Encountered advanced string in canonical mode.\n");

	  sexp_get_string(input, c);
	  return;
	}
Niels Möller's avatar
Niels Möller committed
470
471
}

472
473
474
475
476
477


/* Output routines */

#define LINE_WIDTH 60

478
static void 
479
480
sexp_put_newline(struct sexp_output *output,
		 unsigned indent)
Niels Möller's avatar
Niels Möller committed
481
482
483
{
  unsigned i;
  if (putc('\n', output->f) < 0)
484
    die("Write failed: %s\n", strerror(errno));
Niels Möller's avatar
Niels Möller committed
485

486
  for(i = 0; i < indent; i++)
Niels Möller's avatar
Niels Möller committed
487
    if (putc(' ', output->f) < 0)
488
      die("Write failed: %s\n", strerror(errno));
Niels Möller's avatar
Niels Möller committed
489

490
  output->pos = indent;
Niels Möller's avatar
Niels Möller committed
491
492
}

493
static void
494
sexp_put_char(struct sexp_output *output, unsigned indent,
Niels Möller's avatar
Niels Möller committed
495
496
	      uint8_t c)
{
497
  switch (output->coding)
Niels Möller's avatar
Niels Möller committed
498
    {
499
500
501
502
503
    case SEXP_BASE64:
      {
	uint8_t encoded[2];
	unsigned done;
	unsigned i;
504
      
505
	done = base64_encode_single(&output->base64, encoded, c);
506

507
	assert(done <= sizeof(encoded));
508

509
510
511
512
513
514
	for (i = 0; i<done; i++)
	  {
	    if (indent &&
		output->pos > LINE_WIDTH
		&& output->pos > (indent + 10))
	      sexp_put_newline(output, indent);
515
	  
516
517
	    if (putc(encoded[i], output->f) < 0)
	      die("Write failed: %s\n", strerror(errno));
518

519
520
521
522
523
	    output->pos++;
	  }
	break;
      }
    case SEXP_PLAIN:
524
      output->pos++;
525
526
      if (putc(c, output->f) < 0)
	die("Write failed: %s\n", strerror(errno));
527
528
529
530
531
      break;

    case SEXP_HEX:
      /* Not implemented */
      abort();
532
533
534
    }
}

535
static void
536
537
538
539
540
541
sexp_put_data(struct sexp_output *output, unsigned indent,
	      unsigned length, const uint8_t *data)
{
  unsigned i;

  for (i = 0; i<length; i++)
542
    sexp_put_char(output, indent, data[i]);
543
544
}

545
static void
546
547
548
549
sexp_puts(struct sexp_output *output, unsigned indent,
	  const uint8_t *s)
{
  while (*s)
550
    sexp_put_char(output, indent, *s++);
551
552
}

553
static void
554
555
556
557
558
559
560
561
562
sexp_put_length(struct sexp_output *output, unsigned indent,
		unsigned length)
{
  unsigned digit = 1;

  while (digit < length)
    digit *= 10;

  for (; digit; length %= digit, digit /= 10)
563
    sexp_put_char(output, indent, '0' + length / digit);
564
565
}

566
static void
567
568
sexp_put_base64_start(struct sexp_output *output, uint8_t c)
{
569
  assert(output->coding == SEXP_PLAIN);
570
  
571
  sexp_put_char(output, 0, c);
572
573

  base64_encode_init(&output->base64);
574
  output->coding = SEXP_BASE64;
Niels Möller's avatar
Niels Möller committed
575
576
}

577
static void
578
579
580
581
582
sexp_put_base64_end(struct sexp_output *output, uint8_t c)
{
  uint8_t encoded[BASE64_ENCODE_FINAL_LENGTH];
  unsigned done;

583
  assert(output->coding = SEXP_BASE64);
584
585
586

  done = base64_encode_final(&output->base64, encoded);

587
  assert(done <= sizeof(encoded));
588
  
589
  output->coding = SEXP_PLAIN;
590

591
592
  sexp_put_data(output, 0, done, encoded);
  sexp_put_char(output, 0, c);
593
}
Niels Möller's avatar
Niels Möller committed
594

595
static void
596
sexp_put_string(struct sexp_output *output, unsigned indent,
Niels Möller's avatar
Niels Möller committed
597
598
599
		struct nettle_buffer *string)
{
  if (!string->size)
600
601
602
603
    sexp_puts(output, indent,
	      (output->mode == SEXP_ADVANCED) ? "\"\"": "0:");

  else if (output->mode == SEXP_ADVANCED)
Niels Möller's avatar
Niels Möller committed
604
605
    {
      unsigned i;
606
      int token = (string->contents[0] < '0' || string->contents[0] > '9');
Niels Möller's avatar
Niels Möller committed
607
      int quote_friendly = 1;
608
609
610
      static const char escape_names[0x10] =
	{ 0,0,0,0,0,0,0,0, 'b','t','n',0,'f','r',0,0 };

611
      for (i = 0; i<string->size; i++)
Niels Möller's avatar
Niels Möller committed
612
	{
613
	  uint8_t c = string->contents[i];
Niels Möller's avatar
Niels Möller committed
614
615
616
617
	  
	  if (token & !TOKEN_CHAR(c))
	    token = 0;
	  
618
619
620
621
622
623
624
	  if (quote_friendly)
	    {
	      if (c >= 0x7f)
		quote_friendly = 0;
	      else if (c < 0x20 && !escape_names[c])
		quote_friendly = 0;
	    }
Niels Möller's avatar
Niels Möller committed
625
626
627
	}
      
      if (token)
628
	sexp_put_data(output, indent, string->size, string->contents);
Niels Möller's avatar
Niels Möller committed
629
630
631

      else if (quote_friendly)
	{
632
633
	  sexp_put_char(output, indent, '"');

634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
	  for (i = 0; i<string->size; i++)
	    {
	      int escape = 0;
	      uint8_t c = string->contents[i];

	      assert(c < 0x7f);
	      
	      if (c == '\\' || c == '"')
		escape = 1;
	      else if (c < 0x20)
		{
		  escape = 1;
		  c = escape_names[c];
		  assert(c);
		}
649
650
	      if (escape)
		sexp_put_char(output, indent, '\\');
651

652
	      sexp_put_char(output, indent, c);
653
	    }
654
655
	  
	  sexp_put_char(output, indent, '"');
Niels Möller's avatar
Niels Möller committed
656
657
	}
      else
658
659
660
661
662
663
	{
	  sexp_put_base64_start(output, '|');
	  sexp_put_data(output, output->pos,
			string->size, string->contents);
	  sexp_put_base64_end(output, '|');
	}
664
665
    }
  else
666
667
668
669
670
    {
      sexp_put_length(output, indent, string->size);
      sexp_put_char(output, indent, ':');
      sexp_put_data(output, indent, string->size, string->contents);
    }
671
}
Niels Möller's avatar
Niels Möller committed
672

673
static void
674
675
sexp_put_list_start(struct sexp_output *output, unsigned indent)
{
676
  sexp_put_char(output, indent, '(');
677
678
}

679
static void
680
681
sexp_put_list_end(struct sexp_output *output, unsigned indent)
{
682
  sexp_put_char(output, indent, ')');
683
684
}

685
static void
686
687
sexp_put_display_start(struct sexp_output *output, unsigned indent)
{
688
  sexp_put_char(output, indent, '[');
689
690
}

691
static void
692
693
sexp_put_display_end(struct sexp_output *output, unsigned indent)
{
694
  sexp_put_char(output, indent, ']');
695
696
}

697
static void
698
699
sexp_convert_string(struct sexp_input *input, enum sexp_mode mode,
		    struct sexp_output *output, unsigned indent)
700
{
701
  sexp_get_token(input, mode);
702
703
704
705
  if (input->token == SEXP_STRING)
    sexp_put_string(output, indent, &input->string);
  else
    die("Invalid string.\n");
706
707
708
}

static int
709
710
sexp_convert_item(struct sexp_input *input, enum sexp_mode mode,
		  struct sexp_output *output, unsigned indent);
711

712
static void
713
sexp_convert_list(struct sexp_input *input, enum sexp_mode mode,
714
		  struct sexp_output *output, 
715
716
		  unsigned indent)
{
717
  unsigned item;
Niels Möller's avatar
Niels Möller committed
718

719
  for (item = 0;; item++)
720
    {
721
      sexp_get_token(input, mode);
Niels Möller's avatar
Niels Möller committed
722
  
723
      /* Check for end of list */
724
725
726
      if (input->token == SEXP_LIST_END
	  || input->token == SEXP_EOF
	  || input->token == SEXP_TRANSPORT_END)
727
	return;
728

729
730
731
732
733
734
      if (output->mode == SEXP_ADVANCED)
	{
	  /* FIXME: Adapt pretty printing to handle a big first
	   * element. */
	  if (item == 1)
	    {
735
	      sexp_put_char(output, indent, ' ');
736
737
	      indent = output->pos;
	    }
738
739
	  else if (item > 1)
	    sexp_put_newline(output, indent);
740
741
	}
      
742
      if (!sexp_convert_item(input, mode, output, indent))
743
744
	/* Should be detected above */
	abort();
745
746
747
    }
}

Niels Möller's avatar
Niels Möller committed
748
749
750
static void
sexp_convert_file(struct sexp_input *input, struct sexp_output *output)
{
751
  sexp_get_token(input, SEXP_ADVANCED);
Niels Möller's avatar
Niels Möller committed
752
753
754

  while (input->token != SEXP_EOF)
    {
755
      sexp_convert_item(input, SEXP_ADVANCED, output, 0);
Niels Möller's avatar
Niels Möller committed
756
      if (output->mode == SEXP_ADVANCED)
757
758
	sexp_put_newline(output, 0);
	  
759
      sexp_get_token(input, SEXP_ADVANCED);
Niels Möller's avatar
Niels Möller committed
760
761
762
763
764
765
766
767
    }

  if (fflush(output->f) < 0)
    die("Final fflush failed: %s.\n", strerror(errno));
}



768
static void
769
770
sexp_skip_token(struct sexp_input *input, enum sexp_mode mode,
		enum sexp_token token)
771
{
772
  sexp_get_token(input, mode);
773
774
  if (input->token != token)
    die("Syntax error.\n");
775
776
}

777
/* Returns 1 on success  and 0 at end of list/file.
778
779
780
 *
 * Should be called after getting the first token. */
static int
781
782
sexp_convert_item(struct sexp_input *input, enum sexp_mode mode,
		  struct sexp_output *output, unsigned indent)
783
784
785
786
787
788
{
  switch(input->token)
    {
    case SEXP_LIST_START:
      input->level++;
      
789
      sexp_put_list_start(output, indent);
790
      sexp_convert_list(input, mode, output, indent);
791
792
793
      sexp_put_list_end(output, indent);

      if (input->level)
794
	{
795
796
797
	  input->level--;
	  if (input->token == SEXP_LIST_END)
	    break;
798
	}
799
800
801
802
      else if (input->token == SEXP_EOF)
	break;

      die("Invalid list.\n");
803
804
805
      
    case SEXP_LIST_END:
      if (!input->level)
806
	die("Unexpected end of list.\n");
807
808
      
      input->level--;
809
      return 0;
Niels Möller's avatar
Niels Möller committed
810

811
    case SEXP_EOF:
812
813
814
      if (input->level)
	die("Unexpected end of file.\n");
      break;
815
816

    case SEXP_STRING:
817
818
      sexp_put_string(output, indent, &input->string);
      break;
819
    case SEXP_DISPLAY_START:
820
      sexp_put_display_start(output, indent);
821
822
      sexp_convert_string(input, mode, output, indent);
      sexp_skip_token(input, mode, SEXP_DISPLAY_END);
823
      sexp_put_display_end(output, indent);
824
      sexp_convert_string(input, mode, output, indent);
825
826
      break;
      
827
    case SEXP_TRANSPORT_START:
828
      if (mode != SEXP_ADVANCED)
829
	die("Base64 not allowed in canonical mode.\n");
830
831
832
      else
	{
	  unsigned old_level = input->level;
833
834
835
	  assert(input->coding == SEXP_PLAIN);
	  
	  input->coding = SEXP_BASE64;
836
837
838
	  input->level = 0;

	  base64_decode_init(&input->base64);
839
840
841

	  /* FIXME: sexp_convert_list is wrong. */
	  sexp_convert_list(input, SEXP_CANONICAL, output, indent);
842
	  
843
	  input->coding = SEXP_PLAIN;
844
	  input->level = old_level;
845
	  break;
Niels Möller's avatar
Niels Möller committed
846
	}
847
    case SEXP_TRANSPORT_END:
848
849
850
      /* FIXME: Should be moved do sexp_convert_transport */
      if ( (input->coding != SEXP_BASE64)
	   || input->level || !base64_decode_status(&input->base64))
851
	die("Invalid base64.\n");
852

853
      break;
854
    default:
855
      die("Syntax error.\n");
Niels Möller's avatar
Niels Möller committed
856
    }
857
  return 1;
Niels Möller's avatar
Niels Möller committed
858
859
}

860
861
int
main(int argc, char **argv)
Niels Möller's avatar
Niels Möller committed
862
{
863
864
  struct sexp_input input;
  struct sexp_output output;
Niels Möller's avatar
Niels Möller committed
865

866
  sexp_input_init(&input, stdin);
867
  sexp_output_init(&output, stdout, SEXP_ADVANCED);
Niels Möller's avatar
Niels Möller committed
868

Niels Möller's avatar
Niels Möller committed
869
870
871
  sexp_convert_file(&input, &output);

  return EXIT_SUCCESS;
872
}