sexp-conv.c 11.5 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
/* Conversion functions. */
Niels Möller's avatar
Niels Möller committed
59
60
61
62
63

/* 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
64
65
sexp_convert_item(struct sexp_parser *parser,
		  struct sexp_compound_token *token,
Niels Möller's avatar
Niels Möller committed
66
67
68
69
70
		  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
71
72
      sexp_put_char(output, '{');
      sexp_put_code_start(output, &nettle_base64);
73
      sexp_convert_item(parser, token, output, SEXP_CANONICAL, 0);
Niels Möller's avatar
Niels Möller committed
74
75
      sexp_put_code_end(output);
      sexp_put_char(output, '}');
Niels Möller's avatar
Niels Möller committed
76
    }
77
  else switch(token->type)
Niels Möller's avatar
Niels Möller committed
78
79
    {
    case SEXP_LIST_END:
80
      die("Unmatched end of list.\n");
Niels Möller's avatar
Niels Möller committed
81
82
83
84
85
86
    case SEXP_EOF:
      die("Unexpected end of file.\n");
    case SEXP_CODING_END:
      die("Unexpected end of coding.\n");

    case SEXP_LIST_START:
87
88
89
90
91
92
93
94
95
96
97
98
99
      {
	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. */
100
		switch (item)
101
		  {
102
103
104
105
106
107
108
109
110
111
		  case 0:
		    if (token->type == SEXP_COMMENT)
		      {
			indent = output->pos;
			/* Disable the indentation setup for next item */
			item++;
		      }
		    break;
		    
		  case  1:
112
113
		    sexp_put_char(output, ' ');
		    indent = output->pos;
114
115
116
117
118
		    break;

		  default:
		    sexp_put_newline(output, indent);
		    break;
119
120
121
122
123
124
125
126
127
		  }
	      }

	    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
    case SEXP_COMMENT:
      if (mode_out == SEXP_ADVANCED)
	{
	  sexp_put_data(output, token->string.size, token->string.contents);
144
	  sexp_put_soft_newline(output, indent);
145
146
	}
      break;
Niels Möller's avatar
Niels Möller committed
147
    default:
148
149
      /* Internal error */
      abort();
Niels Möller's avatar
Niels Möller committed
150
151
152
153
154
155
    }
}


/* Argument parsing and main program */

Niels Möller's avatar
Niels Möller committed
156
157
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
/* 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;
199
  int prefer_hex;
Niels Möller's avatar
Niels Möller committed
200
  int once;
201
  int lock;
Niels Möller's avatar
Niels Möller committed
202
203
204
205
  unsigned width;
  const struct nettle_hash *hash;
};

206
enum { OPT_ONCE = 300, OPT_HASH, OPT_LOCK, OPT_HELP };
Niels Möller's avatar
Niels Möller committed
207

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

      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
267

268
269
270
	case '?':
	  exit(EXIT_FAILURE);
	  
Niels Möller's avatar
Niels Möller committed
271
272
273
	case 'w':
	  {
	    char *end;
274
275
276
277
278
	    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
279
280
281
282
283
284
285
286
287
288
289
	    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;
290
291
292
293
294
	  else if (match_argument(optarg, "hex"))
	    {
	      o->mode = SEXP_ADVANCED;
	      o->prefer_hex = 1;
	    }
Niels Möller's avatar
Niels Möller committed
295
296
297
298
299
300
301
	  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
302
	
Niels Möller's avatar
Niels Möller committed
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
	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;
321
322
323
324
325
#if HAVE_FCNTL_LOCKING
	case OPT_LOCK:
	  o->lock = 1;
	  break;
#endif
326
	case OPT_HELP:
Niels Möller's avatar
Niels Möller committed
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
	  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"
342
		 "                            variants: advanced, hex, transport, canonical\n"
Niels Möller's avatar
Niels Möller committed
343
344
		 "       --once               Process only the first s-expression.\n"
		 "   -w, --width=WIDTH        Linewidth for base64 encoded data.\n"
345
		 "                            Zero means no limit.\n"
346
347
348
#if HAVE_FCNTL_LOCKING
		 "       --lock               Lock output file.\n"
#endif
349
350
		 "       --raw-hash           Alias for --hash, for compatibility\n"
		 "                            with lsh-1.x.\n\n"
Niels Möller's avatar
Niels Möller committed
351
352
353
354
355
356
357
358
359
		 "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
360

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

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

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

378
379
380
381
382
383
#if HAVE_FCNTL_LOCKING
  if (options.lock)
    {
      struct flock fl;
  
      memset(&fl, 0, sizeof(fl));
Niels Möller's avatar
Niels Möller committed
384
385
386
387
388
      fl.l_type = F_WRLCK;
      fl.l_whence = SEEK_SET;
      fl.l_start = 0;
      fl.l_len = 0; /* Means entire file. */
      
389
      if (fcntl(STDOUT_FILENO, F_SETLKW, &fl) == -1)
Niels Möller's avatar
Niels Möller committed
390
	die("Locking output file failed: %s\n", strerror(errno));
391
392
    }
#endif /* HAVE_FCNTL_LOCKING */
Niels Möller's avatar
Niels Möller committed
393
  if (options.hash)
Niels Möller's avatar
Niels Möller committed
394
395
396
397
398
    {
      /* 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
399
400
  
  sexp_get_char(&input);
401
  
402
  sexp_parse(&parser, &token);
403
  
404
  if (token.type == SEXP_EOF)
Niels Möller's avatar
Niels Möller committed
405
406
407
408
409
410
411
412
    {
      if (options.once)
	die("sexp-conv: No input expression.\n");
      return EXIT_SUCCESS;
    }
  
  do 
    {
413
      sexp_convert_item(&parser, &token, &output, options.mode, 0);
Niels Möller's avatar
Niels Möller committed
414
      if (options.hash)
415
416
417
418
	{
	  sexp_put_digest(&output);
	  sexp_put_newline(&output, 0);
	}
Niels Möller's avatar
Niels Möller committed
419
420
421
      else if (options.mode != SEXP_CANONICAL)
	sexp_put_newline(&output, 0);
	  
422
      sexp_parse(&parser, &token);
Niels Möller's avatar
Niels Möller committed
423
    }
424
  while (!options.once && token.type != SEXP_EOF);
425
426

  sexp_compound_token_clear(&token);
Niels Möller's avatar
Niels Möller committed
427
428
429
430
  
  if (fflush(output.f) < 0)
    die("Final fflush failed: %s.\n", strerror(errno));
  
Niels Möller's avatar
Niels Möller committed
431
432
  return EXIT_SUCCESS;
}