sexp-conv.c 11.7 KB
Newer Older
Niels Möller's avatar
Niels Möller committed
1
/* sexp-conv.c
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32

   Conversion tool for handling the different flavours of sexp syntax.

   Copyright (C) 2002 Niels Möller

   This file is part of GNU Nettle.

   GNU Nettle is free software: you can redistribute it and/or
   modify it under the terms of either:

     * the GNU Lesser General Public License as published by the Free
       Software Foundation; either version 3 of the License, or (at your
       option) any later version.

   or

     * the GNU General Public License as published by the Free
       Software Foundation; either version 2 of the License, or (at your
       option) any later version.

   or both in parallel, as here.

   GNU Nettle 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
   General Public License for more details.

   You should have received copies of the GNU General Public License and
   the GNU Lesser General Public License along with this program.  If
   not, see http://www.gnu.org/licenses/.
*/
Niels Möller's avatar
Niels Möller committed
33

34
#if HAVE_CONFIG_H
35 36
# include "config.h"
#endif
37

Niels Möller's avatar
Niels Möller committed
38 39 40 41 42 43
#include <assert.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

44 45 46 47 48 49 50 51 52 53
#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

54 55
#include "buffer.h"
#include "nettle-meta.h"
56

57
#include "getopt.h"
58

59 60 61
#include "input.h"
#include "output.h"
#include "parse.h"
62

63
#define BUG_ADDRESS "nettle-bugs@lists.lysator.liu.se"
64

Niels Möller's avatar
Niels Möller committed
65

66
/* Conversion functions. */
Niels Möller's avatar
Niels Möller committed
67 68 69 70 71

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

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

		  default:
		    sexp_put_newline(output, indent);
		    break;
127 128 129 130 131 132 133 134 135
		  }
	      }

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

	break;
      }      
Niels Möller's avatar
Niels Möller committed
136 137
      
    case SEXP_STRING:
138
      sexp_put_string(output, mode_out, &token->string);
Niels Möller's avatar
Niels Möller committed
139 140
      break;

141
    case SEXP_DISPLAY:
Niels Möller's avatar
Niels Möller committed
142
      sexp_put_char(output, '[');
143
      sexp_put_string(output, mode_out, &token->display);
Niels Möller's avatar
Niels Möller committed
144
      sexp_put_char(output, ']');
145
      sexp_put_string(output, mode_out, &token->string);      
Niels Möller's avatar
Niels Möller committed
146
      break;
147

148 149 150 151
    case SEXP_COMMENT:
      if (mode_out == SEXP_ADVANCED)
	{
	  sexp_put_data(output, token->string.size, token->string.contents);
152
	  sexp_put_soft_newline(output, indent);
153 154
	}
      break;
Niels Möller's avatar
Niels Möller committed
155
    default:
156 157
      /* Internal error */
      abort();
Niels Möller's avatar
Niels Möller committed
158 159 160 161 162 163
    }
}


/* Argument parsing and main program */

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 196 197 198 199 200 201 202 203 204 205 206
/* 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;
207
  int prefer_hex;
Niels Möller's avatar
Niels Möller committed
208
  int once;
209
  int lock;
Niels Möller's avatar
Niels Möller committed
210 211 212 213
  unsigned width;
  const struct nettle_hash *hash;
};

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

Niels Möller's avatar
Niels Möller committed
216 217 218 219
static int
match_argument(const char *given, const char *name)
{
  /* FIXME: Allow abbreviations */
220
  assert(given != NULL && name != NULL);
Niels Möller's avatar
Niels Möller committed
221 222 223
  return !strcmp(given, name);
}

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

      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
276

277 278 279
	case '?':
	  exit(EXIT_FAILURE);
	  
Niels Möller's avatar
Niels Möller committed
280 281 282
	case 'w':
	  {
	    char *end;
283 284 285 286
	    int width;
	    assert(optarg != NULL);

	    width = strtol(optarg, &end , 0);
287 288 289 290
	    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
291 292 293 294 295 296 297 298 299 300 301
	    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;
302 303 304 305 306
	  else if (match_argument(optarg, "hex"))
	    {
	      o->mode = SEXP_ADVANCED;
	      o->prefer_hex = 1;
	    }
Niels Möller's avatar
Niels Möller committed
307 308 309 310 311 312 313
	  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
314
	
Niels Möller's avatar
Niels Möller committed
315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332
	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;
333 334 335 336 337
#if HAVE_FCNTL_LOCKING
	case OPT_LOCK:
	  o->lock = 1;
	  break;
#endif
338
	case OPT_HELP:
Niels Möller's avatar
Niels Möller committed
339 340 341 342 343 344 345 346 347 348 349 350 351 352 353
	  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"
354
		 "                            variants: advanced, hex, transport, canonical\n"
Niels Möller's avatar
Niels Möller committed
355 356
		 "       --once               Process only the first s-expression.\n"
		 "   -w, --width=WIDTH        Linewidth for base64 encoded data.\n"
357
		 "                            Zero means no limit.\n"
358 359 360
#if HAVE_FCNTL_LOCKING
		 "       --lock               Lock output file.\n"
#endif
361 362
		 "       --raw-hash           Alias for --hash, for compatibility\n"
		 "                            with lsh-1.x.\n\n"
Niels Möller's avatar
Niels Möller committed
363 364 365 366 367 368 369 370 371
		 "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
372

Niels Möller's avatar
Niels Möller committed
373 374 375 376 377
int
main(int argc, char **argv)
{
  struct conv_options options;
  struct sexp_input input;
378
  struct sexp_parser parser;
379
  struct sexp_compound_token token;
Niels Möller's avatar
Niels Möller committed
380
  struct sexp_output output;
Niels Möller's avatar
Niels Möller committed
381

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

Niels Möller's avatar
Niels Möller committed
384
  sexp_input_init(&input, stdin);
385
  sexp_parse_init(&parser, &input, SEXP_ADVANCED);
386
  sexp_compound_token_init(&token);
387 388
  sexp_output_init(&output, stdout,
		   options.width, options.prefer_hex);
Niels Möller's avatar
Niels Möller committed
389

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

  sexp_compound_token_clear(&token);
Niels Möller's avatar
Niels Möller committed
439 440 441 442
  
  if (fflush(output.f) < 0)
    die("Final fflush failed: %s.\n", strerror(errno));
  
Niels Möller's avatar
Niels Möller committed
443 444
  return EXIT_SUCCESS;
}