Imported Upstream version 2.7.1
[platform/upstream/nettle.git] / tools / sexp-conv.c
1 /* sexp-conv.c
2  *
3  * Conversion tool for handling the different flavours of sexp
4  * syntax. */
5
6 /* nettle, low-level cryptographics library
7  *
8  * Copyright (C) 2002 Niels Möller
9  *  
10  * The nettle library is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU Lesser General Public License as published by
12  * the Free Software Foundation; either version 2.1 of the License, or (at your
13  * option) any later version.
14  * 
15  * The nettle library is distributed in the hope that it will be useful, but
16  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
17  * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
18  * License for more details.
19  * 
20  * You should have received a copy of the GNU Lesser General Public License
21  * along with the nettle library; see the file COPYING.LIB.  If not, write to
22  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
23  * MA 02111-1301, USA.
24  */
25
26 #if HAVE_CONFIG_H
27 # include "config.h"
28 #endif
29
30 #include <assert.h>
31 #include <errno.h>
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <string.h>
35
36 #if HAVE_FCNTL_LOCKING
37 # if HAVE_SYS_TYPES_H
38 #  include <sys/types.h>
39 # endif
40 # if HAVE_UNISTD_H
41 #  include <unistd.h>
42 # endif
43 # include <fcntl.h>
44 #endif
45
46 #include "buffer.h"
47 #include "nettle-meta.h"
48
49 #include "getopt.h"
50
51 #include "input.h"
52 #include "output.h"
53 #include "parse.h"
54
55 #define BUG_ADDRESS "nettle-bugs@lists.lysator.liu.se"
56
57 \f
58 /* Conversion functions. */
59
60 /* Should be called with input->token being the first token of the
61  * expression, to be converted, and return with input->token being the
62  * last token of the expression. */
63 static void
64 sexp_convert_item(struct sexp_parser *parser,
65                   struct sexp_compound_token *token,
66                   struct sexp_output *output, enum sexp_mode mode_out,
67                   unsigned indent)
68 {
69   if (mode_out == SEXP_TRANSPORT)
70     {
71       sexp_put_char(output, '{');
72       sexp_put_code_start(output, &nettle_base64);
73       sexp_convert_item(parser, token, output, SEXP_CANONICAL, 0);
74       sexp_put_code_end(output);
75       sexp_put_char(output, '}');
76     }
77   else switch(token->type)
78     {
79     case SEXP_LIST_END:
80       die("Unmatched end of list.\n");
81     case SEXP_EOF:
82       die("Unexpected end of file.\n");
83     case SEXP_CODING_END:
84       die("Unexpected end of coding.\n");
85
86     case SEXP_LIST_START:
87       {
88         unsigned item;
89
90         sexp_put_char(output, '(');
91   
92         for (item = 0;
93              sexp_parse(parser, token), token->type != SEXP_LIST_END;
94              item++)
95           {
96             if (mode_out == SEXP_ADVANCED)
97               {
98                 /* FIXME: Adapt pretty printing to handle a big first
99                  * element. */
100                 switch (item)
101                   {
102                   case 0:
103                     if (token->type == SEXP_COMMENT)
104                       {
105                         indent = output->pos;
106                         /* Disable the indentation setup for next item */
107                         item++;
108                       }
109                     break;
110                     
111                   case  1:
112                     sexp_put_char(output, ' ');
113                     indent = output->pos;
114                     break;
115
116                   default:
117                     sexp_put_newline(output, indent);
118                     break;
119                   }
120               }
121
122             sexp_convert_item(parser, token, output, mode_out, indent);
123           }
124         sexp_put_char(output, ')');
125
126         break;
127       }      
128       
129     case SEXP_STRING:
130       sexp_put_string(output, mode_out, &token->string);
131       break;
132
133     case SEXP_DISPLAY:
134       sexp_put_char(output, '[');
135       sexp_put_string(output, mode_out, &token->display);
136       sexp_put_char(output, ']');
137       sexp_put_string(output, mode_out, &token->string);      
138       break;
139
140     case SEXP_COMMENT:
141       if (mode_out == SEXP_ADVANCED)
142         {
143           sexp_put_data(output, token->string.size, token->string.contents);
144           sexp_put_soft_newline(output, indent);
145         }
146       break;
147     default:
148       /* Internal error */
149       abort();
150     }
151 }
152
153 \f
154 /* Argument parsing and main program */
155
156 /* The old lsh sexp-conv program took the following options:
157  *
158  * Usage: sexp-conv [OPTION...]
159  *             Conversion: sexp-conv [options] <INPUT-SEXP >OUTPUT
160  *   or:  sexp-conv [OPTION...]
161  *             Fingerprinting: sexp-conv --raw-hash [ --hash=ALGORITHM ]
162  *             <PUBLIC-KEY
163  * Reads an s-expression on stdin, and outputs the same s-expression on stdout,
164  * possibly using a different encoding. By default, output uses the advanced
165  * encoding. 
166  * 
167  *       --hash=Algorithm       Hash algorithm (default sha1).
168  *       --once                 Process exactly one s-expression.
169  *       --raw-hash             Output the hash for the canonical representation
170  *                              of the object, in hexadecimal.
171  *       --replace=Substitution An expression `/before/after/' replaces all
172  *                              occurances of the atom `before' with `after'. The
173  *                              delimiter `/' can be any single character.
174  *       --select=Operator      Select a subexpression (e.g `caddr') for
175  *                              processing.
176  *       --spki-hash            Output an SPKI hash for the object.
177  *       --debug                Print huge amounts of debug information
178  *       --log-file=File name   Append messages to this file.
179  *   -q, --quiet                Suppress all warnings and diagnostic messages
180  *       --trace                Detailed trace
181  *   -v, --verbose              Verbose diagnostic messages
182  * 
183  *  Valid sexp-formats are transport, canonical, advanced, and international.
184  * 
185  *  Valid sexp-formats are transport, canonical, advanced, advanced-hex and
186  *  international.
187  *   -f, --output-format=format Variant of the s-expression syntax to generate.
188  *   -i, --input-format=format  Variant of the s-expression syntax to accept.
189  * 
190  *   -?, --help                 Give this help list
191  *       --usage                Give a short usage message
192  *   -V, --version              Print program version
193  */ 
194
195 struct conv_options
196 {
197   /* Output mode */
198   enum sexp_mode mode;
199   int prefer_hex;
200   int once;
201   int lock;
202   unsigned width;
203   const struct nettle_hash *hash;
204 };
205
206 enum { OPT_ONCE = 300, OPT_HASH, OPT_LOCK, OPT_HELP };
207
208 static int
209 match_argument(const char *given, const char *name)
210 {
211   /* FIXME: Allow abbreviations */
212   return !strcmp(given, name);
213 }
214
215 static void
216 parse_options(struct conv_options *o,
217               int argc, char **argv)
218 {  
219   o->mode = SEXP_ADVANCED;
220   o->prefer_hex = 0;
221   o->once = 0;
222   o->lock = 0;
223   o->hash = NULL;
224   o->width = 72;
225   
226   for (;;)
227     {
228       static const struct nettle_hash *hashes[] =
229         { &nettle_md5, &nettle_sha1, &nettle_sha256, NULL };
230   
231       static const struct option options[] =
232         {
233           /* Name, args, flag, val */
234           { "help", no_argument, NULL, OPT_HELP },
235           { "version", no_argument, NULL, 'V' },
236           { "once", no_argument, NULL, OPT_ONCE },
237           { "syntax", required_argument, NULL, 's' },
238           { "hash", optional_argument, NULL, OPT_HASH },
239           { "raw-hash", optional_argument, NULL, OPT_HASH },
240           { "width", required_argument, NULL, 'w' },
241 #if HAVE_FCNTL_LOCKING
242           { "lock", no_argument, NULL, OPT_LOCK },
243 #endif
244 #if 0
245           /* Not yet implemented */
246           { "replace", required_argument, NULL, OPT_REPLACE },
247           { "select", required_argument, NULL, OPT_SELECT },
248           { "spki-hash", optional_argument, NULL, OPT_SPKI_HASH },
249 #endif
250           { NULL, 0, NULL, 0 }
251         };
252       int c;
253       int option_index = 0;
254       unsigned i;
255      
256       c = getopt_long(argc, argv, "Vs:w:", options, &option_index);
257
258       switch (c)
259         {
260         default:
261           abort();
262           
263         case -1:
264           if (optind != argc)
265             die("sexp-conv: Command line takes no arguments, only options.\n");
266           return;
267
268         case '?':
269           exit(EXIT_FAILURE);
270           
271         case 'w':
272           {
273             char *end;
274             int width = strtol(optarg, &end , 0);
275             if (!*optarg || *end || width < 0)
276               die("sexp-conv: Invalid width `%s'.\n", optarg);
277
278             o->width = width;
279             break;
280           }
281         case 's':
282           if (o->hash)
283             werror("sexp-conv: Combining --hash and -s usually makes no sense.\n");
284           if (match_argument(optarg, "advanced"))
285             o->mode = SEXP_ADVANCED;
286           else if (match_argument(optarg, "transport"))
287             o->mode = SEXP_TRANSPORT;
288           else if (match_argument(optarg, "canonical"))
289             o->mode = SEXP_CANONICAL;
290           else if (match_argument(optarg, "hex"))
291             {
292               o->mode = SEXP_ADVANCED;
293               o->prefer_hex = 1;
294             }
295           else
296             die("Available syntax variants: advanced, transport, canonical\n");
297           break;
298
299         case OPT_ONCE:
300           o->once = 1;
301           break;
302         
303         case OPT_HASH:
304           o->mode = SEXP_CANONICAL;
305           if (!optarg)
306             o->hash = &nettle_sha1;
307           else
308             for (i = 0;; i++)
309               {
310                 if (!hashes[i])
311                   die("sexp_conv: Unknown hash algorithm `%s'\n",
312                       optarg);
313               
314                 if (match_argument(optarg, hashes[i]->name))
315                   {
316                     o->hash = hashes[i];
317                     break;
318                   }
319               }
320           break;
321 #if HAVE_FCNTL_LOCKING
322         case OPT_LOCK:
323           o->lock = 1;
324           break;
325 #endif
326         case OPT_HELP:
327           printf("Usage: sexp-conv [OPTION...]\n"
328                  "  Conversion:     sexp-conv [OPTION...] <INPUT-SEXP\n"
329                  "  Fingerprinting: sexp-conv --hash=HASH <INPUT-SEXP\n\n"
330                  "Reads an s-expression on stdin, and outputs the same\n"
331                  "sexp on stdout, possibly with a different syntax.\n\n"
332                  "       --hash[=ALGORITHM]   Outputs only the hash of the expression.\n"
333                  "                            Available hash algorithms:\n"
334                  "                            ");
335           for(i = 0; hashes[i]; i++)
336             {
337               if (i) printf(", ");
338               printf("%s", hashes[i]->name);
339             }
340           printf(" (default is sha1).\n"
341                  "   -s, --syntax=SYNTAX      The syntax used for the output. Available\n"
342                  "                            variants: advanced, hex, transport, canonical\n"
343                  "       --once               Process only the first s-expression.\n"
344                  "   -w, --width=WIDTH        Linewidth for base64 encoded data.\n"
345                  "                            Zero means no limit.\n"
346 #if HAVE_FCNTL_LOCKING
347                  "       --lock               Lock output file.\n"
348 #endif
349                  "       --raw-hash           Alias for --hash, for compatibility\n"
350                  "                            with lsh-1.x.\n\n"
351                  "Report bugs to " BUG_ADDRESS ".\n");
352           exit(EXIT_SUCCESS);
353
354         case 'V':
355           printf("sexp-conv (" PACKAGE_STRING ")\n");
356           exit (EXIT_SUCCESS);
357         }
358     }
359 }
360
361 int
362 main(int argc, char **argv)
363 {
364   struct conv_options options;
365   struct sexp_input input;
366   struct sexp_parser parser;
367   struct sexp_compound_token token;
368   struct sexp_output output;
369
370   parse_options(&options, argc, argv);
371
372   sexp_input_init(&input, stdin);
373   sexp_parse_init(&parser, &input, SEXP_ADVANCED);
374   sexp_compound_token_init(&token);
375   sexp_output_init(&output, stdout,
376                    options.width, options.prefer_hex);
377
378 #if HAVE_FCNTL_LOCKING
379   if (options.lock)
380     {
381       struct flock fl;
382   
383       memset(&fl, 0, sizeof(fl));
384       fl.l_type = F_WRLCK;
385       fl.l_whence = SEEK_SET;
386       fl.l_start = 0;
387       fl.l_len = 0; /* Means entire file. */
388       
389       if (fcntl(STDOUT_FILENO, F_SETLKW, &fl) == -1)
390         die("Locking output file failed: %s\n", strerror(errno));
391     }
392 #endif /* HAVE_FCNTL_LOCKING */
393   if (options.hash)
394     {
395       /* Leaks the context, but that doesn't matter */
396       void *ctx = xalloc(options.hash->context_size);
397       sexp_output_hash_init(&output, options.hash, ctx);
398     }
399   
400   sexp_get_char(&input);
401   
402   sexp_parse(&parser, &token);
403   
404   if (token.type == SEXP_EOF)
405     {
406       if (options.once)
407         die("sexp-conv: No input expression.\n");
408       return EXIT_SUCCESS;
409     }
410   
411   do 
412     {
413       sexp_convert_item(&parser, &token, &output, options.mode, 0);
414       if (options.hash)
415         {
416           sexp_put_digest(&output);
417           sexp_put_newline(&output, 0);
418         }
419       else if (options.mode != SEXP_CANONICAL)
420         sexp_put_newline(&output, 0);
421           
422       sexp_parse(&parser, &token);
423     }
424   while (!options.once && token.type != SEXP_EOF);
425
426   sexp_compound_token_clear(&token);
427   
428   if (fflush(output.f) < 0)
429     die("Final fflush failed: %s.\n", strerror(errno));
430   
431   return EXIT_SUCCESS;
432 }