From e5380bdf58d770d05eee7dd0843145871fa97b88 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20M=C3=B6ller?= <nisse@lysator.liu.se> Date: Sun, 10 Nov 2002 14:46:53 +0100 Subject: [PATCH] Use supplied getopt. (werror): New function. (sexp_output_hash_init): New function. (sexp_put_char): Made base64 linebreaking configurable. Implemented hashing. (sexp_put_code_start, sexp_put_code_end): Don't output any delimiters here. (sexp_put_string): Output base64 delimiters. (sexp_put_digest): New function. (sexp_convert_item): Output transport delimiters. (sexp_convert_file): Deleted function, folded with main. (parse_options): New function. (main): Implemented --hash and --once, needed by lsh-authorize. Rev: src/nettle/tools/sexp-conv.c:1.2 --- tools/sexp-conv.c | 353 +++++++++++++++++++++++++++++++++++++--------- 1 file changed, 283 insertions(+), 70 deletions(-) diff --git a/tools/sexp-conv.c b/tools/sexp-conv.c index a3484ee5..060c7ab9 100644 --- a/tools/sexp-conv.c +++ b/tools/sexp-conv.c @@ -3,11 +3,19 @@ * Conversion tool for handling the different flavours of sexp * syntax. */ +#if HAVE_CONFIG_H +# include "config.h" +#endif + +#define BUG_ADDRESS "nettle-bugs@lists.lysator.liu.se" + #include "base16.h" #include "base64.h" #include "buffer.h" #include "nettle-meta.h" +#include "getopt.h" + #include <assert.h> #include <errno.h> #include <stdarg.h> @@ -15,10 +23,7 @@ #include <stdlib.h> #include <string.h> -/* For getopt */ -#include <unistd.h> - -void +static void die(const char *format, ...) #if __GNUC___ __attribute__((__format__ (__printf__,1, 2))) @@ -26,7 +31,7 @@ die(const char *format, ...) #endif ; -void +static void die(const char *format, ...) { va_list args; @@ -37,6 +42,25 @@ die(const char *format, ...) exit(EXIT_FAILURE); } +static void +werror(const char *format, ...) +#if __GNUC___ + __attribute__((__format__ (__printf__,1, 2))) + __attribute__((__noreturn__)) +#endif + ; + +static void +werror(const char *format, ...) +{ + va_list args; + va_start(args, format); + vfprintf(stderr, format, args); + va_end(args); + + exit(EXIT_FAILURE); +} + enum sexp_mode { SEXP_CANONICAL = 0, @@ -520,8 +544,13 @@ struct sexp_output { FILE *f; + unsigned line_width; + const struct nettle_armor *coding; unsigned coding_indent; + + const struct nettle_hash *hash; + void *ctx; union { struct base64_decode_ctx base64; @@ -532,15 +561,25 @@ struct sexp_output }; static void -sexp_output_init(struct sexp_output *output, FILE *f) +sexp_output_init(struct sexp_output *output, FILE *f, unsigned width) { output->f = f; + output->line_width = width; output->coding = NULL; - + output->hash = NULL; + output->ctx = NULL; + output->pos = 0; } -#define LINE_WIDTH 60 +static void +sexp_output_hash_init(struct sexp_output *output, + const struct nettle_hash *hash, void *ctx) +{ + output->hash = hash; + output->ctx = ctx; + hash->init(ctx); +} static void sexp_put_raw_char(struct sexp_output *output, uint8_t c) @@ -582,13 +621,16 @@ sexp_put_char(struct sexp_output *output, uint8_t c) for (i = 0; i<done; i++) { - if (output->pos > LINE_WIDTH - && output->pos > (output->coding_indent + 10)) + if (output->line_width + && output->pos >= output->line_width + && output->pos >= (output->coding_indent + 10)) sexp_put_newline(output, output->coding_indent); sexp_put_raw_char(output, encoded[i]); } } + else if (output->hash) + output->hash->update(output->ctx, 1, &c); else sexp_put_raw_char(output, c); } @@ -623,12 +665,10 @@ sexp_put_length(struct sexp_output *output, static void sexp_put_code_start(struct sexp_output *output, - const struct nettle_armor *coding, - uint8_t c) + const struct nettle_armor *coding) { assert(!output->coding); - sexp_put_raw_char(output, c); output->coding_indent = output->pos; output->coding = coding; @@ -636,7 +676,7 @@ sexp_put_code_start(struct sexp_output *output, } static void -sexp_put_code_end(struct sexp_output *output, uint8_t c) +sexp_put_code_end(struct sexp_output *output) { /* Enough for both hex and base64 */ uint8_t encoded[BASE64_ENCODE_FINAL_LENGTH]; @@ -651,7 +691,6 @@ sexp_put_code_end(struct sexp_output *output, uint8_t c) output->coding = NULL; sexp_put_data(output, done, encoded); - sexp_put_char(output, c); } static void @@ -718,9 +757,11 @@ sexp_put_string(struct sexp_output *output, enum sexp_mode mode, } else { - sexp_put_code_start(output, &nettle_base64, '|'); + sexp_put_char(output, '|'); + sexp_put_code_start(output, &nettle_base64); sexp_put_data(output, string->size, string->contents); - sexp_put_code_end(output, '|'); + sexp_put_code_end(output); + sexp_put_char(output, '|'); } } else @@ -731,6 +772,21 @@ sexp_put_string(struct sexp_output *output, enum sexp_mode mode, } } +static void +sexp_put_digest(struct sexp_output *output) +{ + uint8_t *digest; + + assert(output->hash); + + digest = alloca(output->hash->digest_size); + output->hash->digest(output->ctx, output->hash->digest_size, digest); + + sexp_put_code_start(output, &nettle_base16); + sexp_put_data(output, output->hash->digest_size, digest); + sexp_put_code_end(output); +} + /* Parsing and conversion functions. */ @@ -771,9 +827,11 @@ sexp_convert_item(struct sexp_input *input, enum sexp_mode mode_in, { if (mode_out == SEXP_TRANSPORT) { - sexp_put_code_start(output, &nettle_base64, '{'); + sexp_put_char(output, '{'); + sexp_put_code_start(output, &nettle_base64); sexp_convert_item(input, mode_in, output, SEXP_CANONICAL, 0); - sexp_put_code_end(output, '}'); + sexp_put_code_end(output); + sexp_put_char(output, '}'); } else switch(input->token) { @@ -853,30 +911,60 @@ sexp_convert_list(struct sexp_input *input, enum sexp_mode mode_in, } } -static void -sexp_convert_file(struct sexp_input *input, enum sexp_mode mode_in, - struct sexp_output *output, enum sexp_mode mode_out) -{ - sexp_get_char(input); - sexp_get_token(input, mode_in); - - while (input->token != SEXP_EOF) - { - sexp_convert_item(input, mode_in, output, mode_out, 0); - if (mode_out != SEXP_CANONICAL) - sexp_put_newline(output, 0); - - sexp_get_token(input, mode_in); - } - - if (fflush(output->f) < 0) - die("Final fflush failed: %s.\n", strerror(errno)); -} - /* Argument parsing and main program */ +/* 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; + int once; + unsigned width; + const struct nettle_hash *hash; +}; + +enum { OPT_ONCE = 300, OPT_HASH }; + static int match_argument(const char *given, const char *name) { @@ -884,44 +972,169 @@ match_argument(const char *given, const char *name) return !strcmp(given, name); } -int -main(int argc, char **argv) +static void +parse_options(struct conv_options *o, + int argc, char **argv) { - struct sexp_input input; - struct sexp_output output; - enum sexp_mode mode = SEXP_ADVANCED; - unsigned width; + o->mode = SEXP_ADVANCED; + o->once = 0; + o->hash = NULL; + o->width = 72; - int c; - while ( (c = getopt(argc, argv, "s:w:")) != -1) - switch (c) - { - case 's': - if (match_argument(optarg, "advanced")) - mode = SEXP_ADVANCED; - else if (match_argument(optarg, "transport")) - mode = SEXP_TRANSPORT; - else if (match_argument(optarg, "canonical")) - mode = SEXP_CANONICAL; - else - die("Available syntax variants: advanced, transport, canonical\n"); - break; + 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 }, + { "width", required_argument, NULL, 'w' }, +#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; - case 'w': - die("Option -w not yet implemented.\n"); + case 'w': + { + char *end; + o->width = strtol(optarg, &end , 0); + if (!*optarg || *end || o->width < 0) + die("sexp-conv: Invalid width `%s'.\n", + optarg); + 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; + else + die("Available syntax variants: advanced, transport, canonical\n"); + break; + + case OPT_ONCE: + o->once = 1; + break; - case '?': - printf("Usage: sexp-conv [-m syntax]\n" - "Available syntax variants: advanced, transport, canonical\n"); - return EXIT_FAILURE; + 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; + + 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" + " variants: advanced, transport, canonical\n" + " --once Process only the first s-expression.\n" + " -w, --width=WIDTH Linewidth for base64 encoded data.\n" + " Zero means no limit.\n\n" + "Report bugs to " BUG_ADDRESS ".\n"); + exit(EXIT_SUCCESS); + + case 'V': + printf("sexp-conv (" PACKAGE_STRING ")\n"); + exit (EXIT_SUCCESS); + } + } +} - default: abort(); - } +int +main(int argc, char **argv) +{ + struct conv_options options; + struct sexp_input input; + struct sexp_output output; - sexp_input_init(&input, stdin); - sexp_output_init(&output, stdout); + parse_options(&options, argc, argv); - sexp_convert_file(&input, SEXP_ADVANCED, &output, mode); + sexp_input_init(&input, stdin); + sexp_output_init(&output, stdout, options.width); + if (options.hash) + sexp_output_hash_init(&output, + options.hash, + alloca(options.hash->context_size)); + + sexp_get_char(&input); + sexp_get_token(&input, SEXP_ADVANCED); + + if (input.token == SEXP_EOF) + { + if (options.once) + die("sexp-conv: No input expression.\n"); + return EXIT_SUCCESS; + } + + do + { + sexp_convert_item(&input, SEXP_ADVANCED, &output, options.mode, 0); + if (options.hash) + sexp_put_digest(&output); + else if (options.mode != SEXP_CANONICAL) + sexp_put_newline(&output, 0); + + sexp_get_token(&input, SEXP_ADVANCED); + } + while (!options.once && input.token != SEXP_EOF); + + if (fflush(output.f) < 0) + die("Final fflush failed: %s.\n", strerror(errno)); + return EXIT_SUCCESS; } -- GitLab