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

Niels Möller's avatar
Niels Möller committed
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
/* 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.
 */

Niels Möller's avatar
Niels Möller committed
26
#if HAVE_CONFIG_H
27
28
# include "config.h"
#endif
29

Niels Möller's avatar
Niels Möller committed
30
31
32
33
34
35
#include <assert.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

36
37
38
39
40
41
42
43
44
45
#if HAVE_FCNTL_LOCKING
# if HAVE_SYS_TYPES_H
#  include <sys/types.h>
# endif
# if HAVE_UNISTD_H
#  include <unistd.h>
# endif
# include <fcntl.h>
#endif

46
47
#include "buffer.h"
#include "nettle-meta.h"
48

49
#include "getopt.h"
50

51
52
53
#include "input.h"
#include "output.h"
#include "parse.h"
54

55
#define BUG_ADDRESS "nettle-bugs@lists.lysator.liu.se"
56

Niels Möller's avatar
Niels Möller committed
57
58
59
60
61
62
63
64
65
66
67
68
69
static void *
xalloc(size_t size)
{
  void *p = malloc(size);
  if (!p)
    {
      fprintf(stderr, "Virtual memory exhausted.\n");
      abort();
    }

  return p;
}

Niels Möller's avatar
Niels Möller committed
70

71
/* Conversion functions. */
Niels Möller's avatar
Niels Möller committed
72
73
74
75
76

/* Should be called with input->token being the first token of the
 * expression, to be converted, and return with input->token being the
 * last token of the expression. */
static void
77
78
sexp_convert_item(struct sexp_parser *parser,
		  struct sexp_compound_token *token,
Niels Möller's avatar
Niels Möller committed
79
80
81
82
83
		  struct sexp_output *output, enum sexp_mode mode_out,
		  unsigned indent)
{
  if (mode_out == SEXP_TRANSPORT)
    {
Niels Möller's avatar
Niels Möller committed
84
85
      sexp_put_char(output, '{');
      sexp_put_code_start(output, &nettle_base64);
86
      sexp_convert_item(parser, token, output, SEXP_CANONICAL, 0);
Niels Möller's avatar
Niels Möller committed
87
88
      sexp_put_code_end(output);
      sexp_put_char(output, '}');
Niels Möller's avatar
Niels Möller committed
89
    }
90
  else switch(token->type)
Niels Möller's avatar
Niels Möller committed
91
92
    {
    case SEXP_LIST_END:
93
      die("Unmatched end of list.\n");
Niels Möller's avatar
Niels Möller committed
94
95
96
97
98
99
    case SEXP_EOF:
      die("Unexpected end of file.\n");
    case SEXP_CODING_END:
      die("Unexpected end of coding.\n");

    case SEXP_LIST_START:
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
      {
	unsigned item;

	sexp_put_char(output, '(');
  
	for (item = 0;
	     sexp_parse(parser, token), token->type != SEXP_LIST_END;
	     item++)
	  {
	    if (mode_out == SEXP_ADVANCED)
	      {
		/* FIXME: Adapt pretty printing to handle a big first
		 * element. */
		if (item == 1)
		  {
		    sexp_put_char(output, ' ');
		    indent = output->pos;
		  }
		else if (item > 1)
		  sexp_put_newline(output, indent);
	      }

	    sexp_convert_item(parser, token, output, mode_out, indent);
	  }
	sexp_put_char(output, ')');

	break;
      }      
Niels Möller's avatar
Niels Möller committed
128
129
      
    case SEXP_STRING:
130
      sexp_put_string(output, mode_out, &token->string);
Niels Möller's avatar
Niels Möller committed
131
132
      break;

133
    case SEXP_DISPLAY:
Niels Möller's avatar
Niels Möller committed
134
      sexp_put_char(output, '[');
135
      sexp_put_string(output, mode_out, &token->display);
Niels Möller's avatar
Niels Möller committed
136
      sexp_put_char(output, ']');
137
      sexp_put_string(output, mode_out, &token->string);      
Niels Möller's avatar
Niels Möller committed
138
      break;
139

140
141
142
143
144
145
146
147
148
    case SEXP_COMMENT:
      if (mode_out == SEXP_ADVANCED)
	{
	  sexp_put_data(output, token->string.size, token->string.contents);
	  /* This newline is necessary only if the comment comes first
	     in a list. It would be nice to supress extra newlines. */
	  sexp_put_newline(output, indent);
	}
      break;
Niels Möller's avatar
Niels Möller committed
149
    default:
150
151
      /* Internal error */
      abort();
Niels Möller's avatar
Niels Möller committed
152
153
154
155
156
157
    }
}


/* Argument parsing and main program */

Niels Möller's avatar
Niels Möller committed
158
159
160
161
162
163
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
196
197
198
199
200
/* The old lsh sexp-conv program took the following options:
 *
 * Usage: sexp-conv [OPTION...]
 *             Conversion: sexp-conv [options] <INPUT-SEXP >OUTPUT
 *   or:  sexp-conv [OPTION...]
 *             Fingerprinting: sexp-conv --raw-hash [ --hash=ALGORITHM ]
 *             <PUBLIC-KEY
 * Reads an s-expression on stdin, and outputs the same s-expression on stdout,
 * possibly using a different encoding. By default, output uses the advanced
 * encoding. 
 * 
 *       --hash=Algorithm       Hash algorithm (default sha1).
 *       --once                 Process exactly one s-expression.
 *       --raw-hash             Output the hash for the canonical representation
 *                              of the object, in hexadecimal.
 *       --replace=Substitution An expression `/before/after/' replaces all
 *                              occurances of the atom `before' with `after'. The
 *                              delimiter `/' can be any single character.
 *       --select=Operator      Select a subexpression (e.g `caddr') for
 *                              processing.
 *       --spki-hash            Output an SPKI hash for the object.
 *       --debug                Print huge amounts of debug information
 *       --log-file=File name   Append messages to this file.
 *   -q, --quiet                Suppress all warnings and diagnostic messages
 *       --trace                Detailed trace
 *   -v, --verbose              Verbose diagnostic messages
 * 
 *  Valid sexp-formats are transport, canonical, advanced, and international.
 * 
 *  Valid sexp-formats are transport, canonical, advanced, advanced-hex and
 *  international.
 *   -f, --output-format=format Variant of the s-expression syntax to generate.
 *   -i, --input-format=format  Variant of the s-expression syntax to accept.
 * 
 *   -?, --help                 Give this help list
 *       --usage                Give a short usage message
 *   -V, --version              Print program version
 */ 

struct conv_options
{
  /* Output mode */
  enum sexp_mode mode;
201
  int prefer_hex;
Niels Möller's avatar
Niels Möller committed
202
  int once;
203
  int lock;
Niels Möller's avatar
Niels Möller committed
204
205
206
207
  unsigned width;
  const struct nettle_hash *hash;
};

208
enum { OPT_ONCE = 300, OPT_HASH, OPT_LOCK };
Niels Möller's avatar
Niels Möller committed
209

Niels Möller's avatar
Niels Möller committed
210
211
212
213
214
215
216
static int
match_argument(const char *given, const char *name)
{
  /* FIXME: Allow abbreviations */
  return !strcmp(given, name);
}

Niels Möller's avatar
Niels Möller committed
217
218
219
static void
parse_options(struct conv_options *o,
	      int argc, char **argv)
Niels Möller's avatar
Niels Möller committed
220
{  
Niels Möller's avatar
Niels Möller committed
221
  o->mode = SEXP_ADVANCED;
222
  o->prefer_hex = 0;
Niels Möller's avatar
Niels Möller committed
223
  o->once = 0;
224
  o->lock = 0;
Niels Möller's avatar
Niels Möller committed
225
226
  o->hash = NULL;
  o->width = 72;
Niels Möller's avatar
Niels Möller committed
227
  
Niels Möller's avatar
Niels Möller committed
228
229
230
231
232
233
234
235
236
237
238
239
240
  for (;;)
    {
      static const struct nettle_hash *hashes[] =
	{ &nettle_md5, &nettle_sha1, &nettle_sha256, NULL };
  
      static const struct option options[] =
	{
	  /* Name, args, flag, val */
	  { "help", no_argument, NULL, '?' },
	  { "version", no_argument, NULL, 'V' },
	  { "once", no_argument, NULL, OPT_ONCE },
	  { "syntax", required_argument, NULL, 's' },
	  { "hash", optional_argument, NULL, OPT_HASH },
241
	  { "raw-hash", optional_argument, NULL, OPT_HASH },
Niels Möller's avatar
Niels Möller committed
242
	  { "width", required_argument, NULL, 'w' },
243
244
245
#if HAVE_FCNTL_LOCKING
	  { "lock", no_argument, NULL, OPT_LOCK },
#endif
Niels Möller's avatar
Niels Möller committed
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
#if 0
	  /* Not yet implemented */
	  { "replace", required_argument, NULL, OPT_REPLACE },
	  { "select", required_argument, NULL, OPT_SELECT },
	  { "spki-hash", optional_argument, NULL, OPT_SPKI_HASH },
#endif
	  { NULL, 0, NULL, 0 }
	};
      int c;
      int option_index = 0;
      unsigned i;
     
      c = getopt_long(argc, argv, "V?s:w:", options, &option_index);

      switch (c)
	{
	default:
	  abort();
	  
	case -1:
	  if (optind != argc)
	    die("sexp-conv: Command line takes no arguments, only options.\n");
	  return;
Niels Möller's avatar
Niels Möller committed
269

Niels Möller's avatar
Niels Möller committed
270
271
272
	case 'w':
	  {
	    char *end;
273
274
275
276
277
	    int width = strtol(optarg, &end , 0);
	    if (!*optarg || *end || width < 0)
	      die("sexp-conv: Invalid width `%s'.\n", optarg);

	    o->width = width;
Niels Möller's avatar
Niels Möller committed
278
279
280
281
282
283
284
285
286
287
288
	    break;
	  }
	case 's':
	  if (o->hash)
	    werror("sexp-conv: Combining --hash and -s usually makes no sense.\n");
	  if (match_argument(optarg, "advanced"))
	    o->mode = SEXP_ADVANCED;
	  else if (match_argument(optarg, "transport"))
	    o->mode = SEXP_TRANSPORT;
	  else if (match_argument(optarg, "canonical"))
	    o->mode = SEXP_CANONICAL;
289
290
291
292
293
	  else if (match_argument(optarg, "hex"))
	    {
	      o->mode = SEXP_ADVANCED;
	      o->prefer_hex = 1;
	    }
Niels Möller's avatar
Niels Möller committed
294
295
296
297
298
299
300
	  else
	    die("Available syntax variants: advanced, transport, canonical\n");
	  break;

	case OPT_ONCE:
	  o->once = 1;
	  break;
Niels Möller's avatar
Niels Möller committed
301
	
Niels Möller's avatar
Niels Möller committed
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
	case OPT_HASH:
	  o->mode = SEXP_CANONICAL;
	  if (!optarg)
	    o->hash = &nettle_sha1;
	  else
	    for (i = 0;; i++)
	      {
		if (!hashes[i])
		  die("sexp_conv: Unknown hash algorithm `%s'\n",
		      optarg);
	      
		if (match_argument(optarg, hashes[i]->name))
		  {
		    o->hash = hashes[i];
		    break;
		  }
	      }
	  break;
320
321
322
323
324
#if HAVE_FCNTL_LOCKING
	case OPT_LOCK:
	  o->lock = 1;
	  break;
#endif
Niels Möller's avatar
Niels Möller committed
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
	case '?':
	  printf("Usage: sexp-conv [OPTION...]\n"
		 "  Conversion:     sexp-conv [OPTION...] <INPUT-SEXP\n"
		 "  Fingerprinting: sexp-conv --hash=HASH <INPUT-SEXP\n\n"
		 "Reads an s-expression on stdin, and outputs the same\n"
		 "sexp on stdout, possibly with a different syntax.\n\n"
		 "       --hash[=ALGORITHM]   Outputs only the hash of the expression.\n"
		 "                            Available hash algorithms:\n"
		 "                            ");
	  for(i = 0; hashes[i]; i++)
	    {
	      if (i) printf(", ");
	      printf("%s", hashes[i]->name);
	    }
	  printf(" (default is sha1).\n"
		 "   -s, --syntax=SYNTAX      The syntax used for the output. Available\n"
341
		 "                            variants: advanced, hex, transport, canonical\n"
Niels Möller's avatar
Niels Möller committed
342
343
		 "       --once               Process only the first s-expression.\n"
		 "   -w, --width=WIDTH        Linewidth for base64 encoded data.\n"
344
		 "                            Zero means no limit.\n"
345
346
347
#if HAVE_FCNTL_LOCKING
		 "       --lock               Lock output file.\n"
#endif
348
349
		 "       --raw-hash           Alias for --hash, for compatibility\n"
		 "                            with lsh-1.x.\n\n"
Niels Möller's avatar
Niels Möller committed
350
351
352
353
354
355
356
357
358
		 "Report bugs to " BUG_ADDRESS ".\n");
	  exit(EXIT_SUCCESS);

	case 'V':
	  printf("sexp-conv (" PACKAGE_STRING ")\n");
	  exit (EXIT_SUCCESS);
	}
    }
}
Niels Möller's avatar
Niels Möller committed
359

Niels Möller's avatar
Niels Möller committed
360
361
362
363
364
int
main(int argc, char **argv)
{
  struct conv_options options;
  struct sexp_input input;
365
  struct sexp_parser parser;
366
  struct sexp_compound_token token;
Niels Möller's avatar
Niels Möller committed
367
  struct sexp_output output;
Niels Möller's avatar
Niels Möller committed
368

Niels Möller's avatar
Niels Möller committed
369
  parse_options(&options, argc, argv);
Niels Möller's avatar
Niels Möller committed
370

Niels Möller's avatar
Niels Möller committed
371
  sexp_input_init(&input, stdin);
372
  sexp_parse_init(&parser, &input, SEXP_ADVANCED);
373
  sexp_compound_token_init(&token);
374
375
  sexp_output_init(&output, stdout,
		   options.width, options.prefer_hex);
Niels Möller's avatar
Niels Möller committed
376

377
378
379
380
381
382
383
384
385
386
#if HAVE_FCNTL_LOCKING
  if (options.lock)
    {
      struct flock fl;
  
      memset(&fl, 0, sizeof(fl));
      if (fcntl(STDOUT_FILENO, F_SETLKW, &fl) == -1)
	die("Locking output file failed: $s\n", strerror(errno));
    }
#endif /* HAVE_FCNTL_LOCKING */
Niels Möller's avatar
Niels Möller committed
387
  if (options.hash)
Niels Möller's avatar
Niels Möller committed
388
389
390
391
392
    {
      /* Leaks the context, but that doesn't matter */
      void *ctx = xalloc(options.hash->context_size);
      sexp_output_hash_init(&output, options.hash, ctx);
    }
Niels Möller's avatar
Niels Möller committed
393
394
  
  sexp_get_char(&input);
395
  
396
  sexp_parse(&parser, &token);
397
  
398
  if (token.type == SEXP_EOF)
Niels Möller's avatar
Niels Möller committed
399
400
401
402
403
404
405
406
    {
      if (options.once)
	die("sexp-conv: No input expression.\n");
      return EXIT_SUCCESS;
    }
  
  do 
    {
407
      sexp_convert_item(&parser, &token, &output, options.mode, 0);
Niels Möller's avatar
Niels Möller committed
408
      if (options.hash)
409
410
411
412
	{
	  sexp_put_digest(&output);
	  sexp_put_newline(&output, 0);
	}
Niels Möller's avatar
Niels Möller committed
413
414
415
      else if (options.mode != SEXP_CANONICAL)
	sexp_put_newline(&output, 0);
	  
416
      sexp_parse(&parser, &token);
Niels Möller's avatar
Niels Möller committed
417
    }
418
  while (!options.once && token.type != SEXP_EOF);
419
420

  sexp_compound_token_clear(&token);
Niels Möller's avatar
Niels Möller committed
421
422
423
424
  
  if (fflush(output.f) < 0)
    die("Final fflush failed: %s.\n", strerror(errno));
  
Niels Möller's avatar
Niels Möller committed
425
426
  return EXIT_SUCCESS;
}