Initialize Tizen 2.3
[external/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., 59 Temple Place - Suite 330, Boston,
23  * MA 02111-1307, 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 static void *
58 xalloc(size_t size)
59 {
60   void *p = malloc(size);
61   if (!p)
62     {
63       fprintf(stderr, "Virtual memory exhausted.\n");
64       abort();
65     }
66
67   return p;
68 }
69
70 \f
71 /* Conversion functions. */
72
73 /* Should be called with input->token being the first token of the
74  * expression, to be converted, and return with input->token being the
75  * last token of the expression. */
76 static void
77 sexp_convert_item(struct sexp_parser *parser,
78                   struct sexp_compound_token *token,
79                   struct sexp_output *output, enum sexp_mode mode_out,
80                   unsigned indent)
81 {
82   if (mode_out == SEXP_TRANSPORT)
83     {
84       sexp_put_char(output, '{');
85       sexp_put_code_start(output, &nettle_base64);
86       sexp_convert_item(parser, token, output, SEXP_CANONICAL, 0);
87       sexp_put_code_end(output);
88       sexp_put_char(output, '}');
89     }
90   else switch(token->type)
91     {
92     case SEXP_LIST_END:
93       die("Unmatched end of list.\n");
94     case SEXP_EOF:
95       die("Unexpected end of file.\n");
96     case SEXP_CODING_END:
97       die("Unexpected end of coding.\n");
98
99     case SEXP_LIST_START:
100       {
101         unsigned item;
102
103         sexp_put_char(output, '(');
104   
105         for (item = 0;
106              sexp_parse(parser, token), token->type != SEXP_LIST_END;
107              item++)
108           {
109             if (mode_out == SEXP_ADVANCED)
110               {
111                 /* FIXME: Adapt pretty printing to handle a big first
112                  * element. */
113                 switch (item)
114                   {
115                   case 0:
116                     if (token->type == SEXP_COMMENT)
117                       {
118                         indent = output->pos;
119                         /* Disable the indentation setup for next item */
120                         item++;
121                       }
122                     break;
123                     
124                   case  1:
125                     sexp_put_char(output, ' ');
126                     indent = output->pos;
127                     break;
128
129                   default:
130                     sexp_put_newline(output, indent);
131                     break;
132                   }
133               }
134
135             sexp_convert_item(parser, token, output, mode_out, indent);
136           }
137         sexp_put_char(output, ')');
138
139         break;
140       }      
141       
142     case SEXP_STRING:
143       sexp_put_string(output, mode_out, &token->string);
144       break;
145
146     case SEXP_DISPLAY:
147       sexp_put_char(output, '[');
148       sexp_put_string(output, mode_out, &token->display);
149       sexp_put_char(output, ']');
150       sexp_put_string(output, mode_out, &token->string);      
151       break;
152
153     case SEXP_COMMENT:
154       if (mode_out == SEXP_ADVANCED)
155         {
156           sexp_put_data(output, token->string.size, token->string.contents);
157           sexp_put_soft_newline(output, indent);
158         }
159       break;
160     default:
161       /* Internal error */
162       abort();
163     }
164 }
165
166 \f
167 /* Argument parsing and main program */
168
169 /* The old lsh sexp-conv program took the following options:
170  *
171  * Usage: sexp-conv [OPTION...]
172  *             Conversion: sexp-conv [options] <INPUT-SEXP >OUTPUT
173  *   or:  sexp-conv [OPTION...]
174  *             Fingerprinting: sexp-conv --raw-hash [ --hash=ALGORITHM ]
175  *             <PUBLIC-KEY
176  * Reads an s-expression on stdin, and outputs the same s-expression on stdout,
177  * possibly using a different encoding. By default, output uses the advanced
178  * encoding. 
179  * 
180  *       --hash=Algorithm       Hash algorithm (default sha1).
181  *       --once                 Process exactly one s-expression.
182  *       --raw-hash             Output the hash for the canonical representation
183  *                              of the object, in hexadecimal.
184  *       --replace=Substitution An expression `/before/after/' replaces all
185  *                              occurances of the atom `before' with `after'. The
186  *                              delimiter `/' can be any single character.
187  *       --select=Operator      Select a subexpression (e.g `caddr') for
188  *                              processing.
189  *       --spki-hash            Output an SPKI hash for the object.
190  *       --debug                Print huge amounts of debug information
191  *       --log-file=File name   Append messages to this file.
192  *   -q, --quiet                Suppress all warnings and diagnostic messages
193  *       --trace                Detailed trace
194  *   -v, --verbose              Verbose diagnostic messages
195  * 
196  *  Valid sexp-formats are transport, canonical, advanced, and international.
197  * 
198  *  Valid sexp-formats are transport, canonical, advanced, advanced-hex and
199  *  international.
200  *   -f, --output-format=format Variant of the s-expression syntax to generate.
201  *   -i, --input-format=format  Variant of the s-expression syntax to accept.
202  * 
203  *   -?, --help                 Give this help list
204  *       --usage                Give a short usage message
205  *   -V, --version              Print program version
206  */ 
207
208 struct conv_options
209 {
210   /* Output mode */
211   enum sexp_mode mode;
212   int prefer_hex;
213   int once;
214   int lock;
215   unsigned width;
216   const struct nettle_hash *hash;
217 };
218
219 enum { OPT_ONCE = 300, OPT_HASH, OPT_LOCK };
220
221 static int
222 match_argument(const char *given, const char *name)
223 {
224   /* FIXME: Allow abbreviations */
225   return !strcmp(given, name);
226 }
227
228 static void
229 parse_options(struct conv_options *o,
230               int argc, char **argv)
231 {  
232   o->mode = SEXP_ADVANCED;
233   o->prefer_hex = 0;
234   o->once = 0;
235   o->lock = 0;
236   o->hash = NULL;
237   o->width = 72;
238   
239   for (;;)
240     {
241       static const struct nettle_hash *hashes[] =
242         { &nettle_md5, &nettle_sha1, &nettle_sha256, NULL };
243   
244       static const struct option options[] =
245         {
246           /* Name, args, flag, val */
247           { "help", no_argument, NULL, '?' },
248           { "version", no_argument, NULL, 'V' },
249           { "once", no_argument, NULL, OPT_ONCE },
250           { "syntax", required_argument, NULL, 's' },
251           { "hash", optional_argument, NULL, OPT_HASH },
252           { "raw-hash", optional_argument, NULL, OPT_HASH },
253           { "width", required_argument, NULL, 'w' },
254 #if HAVE_FCNTL_LOCKING
255           { "lock", no_argument, NULL, OPT_LOCK },
256 #endif
257 #if 0
258           /* Not yet implemented */
259           { "replace", required_argument, NULL, OPT_REPLACE },
260           { "select", required_argument, NULL, OPT_SELECT },
261           { "spki-hash", optional_argument, NULL, OPT_SPKI_HASH },
262 #endif
263           { NULL, 0, NULL, 0 }
264         };
265       int c;
266       int option_index = 0;
267       unsigned i;
268      
269       c = getopt_long(argc, argv, "V?s:w:", options, &option_index);
270
271       switch (c)
272         {
273         default:
274           abort();
275           
276         case -1:
277           if (optind != argc)
278             die("sexp-conv: Command line takes no arguments, only options.\n");
279           return;
280
281         case 'w':
282           {
283             char *end;
284             int width = strtol(optarg, &end , 0);
285             if (!*optarg || *end || width < 0)
286               die("sexp-conv: Invalid width `%s'.\n", optarg);
287
288             o->width = width;
289             break;
290           }
291         case 's':
292           if (o->hash)
293             werror("sexp-conv: Combining --hash and -s usually makes no sense.\n");
294           if (match_argument(optarg, "advanced"))
295             o->mode = SEXP_ADVANCED;
296           else if (match_argument(optarg, "transport"))
297             o->mode = SEXP_TRANSPORT;
298           else if (match_argument(optarg, "canonical"))
299             o->mode = SEXP_CANONICAL;
300           else if (match_argument(optarg, "hex"))
301             {
302               o->mode = SEXP_ADVANCED;
303               o->prefer_hex = 1;
304             }
305           else
306             die("Available syntax variants: advanced, transport, canonical\n");
307           break;
308
309         case OPT_ONCE:
310           o->once = 1;
311           break;
312         
313         case OPT_HASH:
314           o->mode = SEXP_CANONICAL;
315           if (!optarg)
316             o->hash = &nettle_sha1;
317           else
318             for (i = 0;; i++)
319               {
320                 if (!hashes[i])
321                   die("sexp_conv: Unknown hash algorithm `%s'\n",
322                       optarg);
323               
324                 if (match_argument(optarg, hashes[i]->name))
325                   {
326                     o->hash = hashes[i];
327                     break;
328                   }
329               }
330           break;
331 #if HAVE_FCNTL_LOCKING
332         case OPT_LOCK:
333           o->lock = 1;
334           break;
335 #endif
336         case '?':
337           printf("Usage: sexp-conv [OPTION...]\n"
338                  "  Conversion:     sexp-conv [OPTION...] <INPUT-SEXP\n"
339                  "  Fingerprinting: sexp-conv --hash=HASH <INPUT-SEXP\n\n"
340                  "Reads an s-expression on stdin, and outputs the same\n"
341                  "sexp on stdout, possibly with a different syntax.\n\n"
342                  "       --hash[=ALGORITHM]   Outputs only the hash of the expression.\n"
343                  "                            Available hash algorithms:\n"
344                  "                            ");
345           for(i = 0; hashes[i]; i++)
346             {
347               if (i) printf(", ");
348               printf("%s", hashes[i]->name);
349             }
350           printf(" (default is sha1).\n"
351                  "   -s, --syntax=SYNTAX      The syntax used for the output. Available\n"
352                  "                            variants: advanced, hex, transport, canonical\n"
353                  "       --once               Process only the first s-expression.\n"
354                  "   -w, --width=WIDTH        Linewidth for base64 encoded data.\n"
355                  "                            Zero means no limit.\n"
356 #if HAVE_FCNTL_LOCKING
357                  "       --lock               Lock output file.\n"
358 #endif
359                  "       --raw-hash           Alias for --hash, for compatibility\n"
360                  "                            with lsh-1.x.\n\n"
361                  "Report bugs to " BUG_ADDRESS ".\n");
362           exit(EXIT_SUCCESS);
363
364         case 'V':
365           printf("sexp-conv (" PACKAGE_STRING ")\n");
366           exit (EXIT_SUCCESS);
367         }
368     }
369 }
370
371 int
372 main(int argc, char **argv)
373 {
374   struct conv_options options;
375   struct sexp_input input;
376   struct sexp_parser parser;
377   struct sexp_compound_token token;
378   struct sexp_output output;
379
380   parse_options(&options, argc, argv);
381
382   sexp_input_init(&input, stdin);
383   sexp_parse_init(&parser, &input, SEXP_ADVANCED);
384   sexp_compound_token_init(&token);
385   sexp_output_init(&output, stdout,
386                    options.width, options.prefer_hex);
387
388 #if HAVE_FCNTL_LOCKING
389   if (options.lock)
390     {
391       struct flock fl;
392   
393       memset(&fl, 0, sizeof(fl));
394       fl.l_type = F_WRLCK;
395       fl.l_whence = SEEK_SET;
396       fl.l_start = 0;
397       fl.l_len = 0; /* Means entire file. */
398       
399       if (fcntl(STDOUT_FILENO, F_SETLKW, &fl) == -1)
400         die("Locking output file failed: %s\n", strerror(errno));
401     }
402 #endif /* HAVE_FCNTL_LOCKING */
403   if (options.hash)
404     {
405       /* Leaks the context, but that doesn't matter */
406       void *ctx = xalloc(options.hash->context_size);
407       sexp_output_hash_init(&output, options.hash, ctx);
408     }
409   
410   sexp_get_char(&input);
411   
412   sexp_parse(&parser, &token);
413   
414   if (token.type == SEXP_EOF)
415     {
416       if (options.once)
417         die("sexp-conv: No input expression.\n");
418       return EXIT_SUCCESS;
419     }
420   
421   do 
422     {
423       sexp_convert_item(&parser, &token, &output, options.mode, 0);
424       if (options.hash)
425         {
426           sexp_put_digest(&output);
427           sexp_put_newline(&output, 0);
428         }
429       else if (options.mode != SEXP_CANONICAL)
430         sexp_put_newline(&output, 0);
431           
432       sexp_parse(&parser, &token);
433     }
434   while (!options.once && token.type != SEXP_EOF);
435
436   sexp_compound_token_clear(&token);
437   
438   if (fflush(output.f) < 0)
439     die("Final fflush failed: %s.\n", strerror(errno));
440   
441   return EXIT_SUCCESS;
442 }