d03ab969 |
1 | /* -*-c-*- |
2 | * |
cc321283 |
3 | * $Id: keyutil.c,v 1.2 1999/10/15 21:05:28 mdw Exp $ |
d03ab969 |
4 | * |
5 | * Simple key manager program |
6 | * |
7 | * (c) 1999 Mark Wooding |
8 | */ |
9 | |
10 | /*----- Licensing notice --------------------------------------------------* |
11 | * |
12 | * This file is part of Catacomb. |
13 | * |
14 | * Catacomb is free software; you can redistribute it and/or modify |
15 | * it under the terms of the GNU Library General Public License as |
16 | * published by the Free Software Foundation; either version 2 of the |
17 | * License, or (at your option) any later version. |
18 | * |
19 | * Catacomb is distributed in the hope that it will be useful, |
20 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
21 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
22 | * GNU Library General Public License for more details. |
23 | * |
24 | * You should have received a copy of the GNU Library General Public |
25 | * License along with Catacomb; if not, write to the Free |
26 | * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, |
27 | * MA 02111-1307, USA. |
28 | */ |
29 | |
30 | /*----- Revision history --------------------------------------------------* |
31 | * |
32 | * $Log: keyutil.c,v $ |
cc321283 |
33 | * Revision 1.2 1999/10/15 21:05:28 mdw |
34 | * In `key list', show timezone for local times, and support `-u' option |
35 | * for UTC output. |
36 | * |
d03ab969 |
37 | * Revision 1.1 1999/09/03 08:41:12 mdw |
38 | * Initial import. |
39 | * |
40 | */ |
41 | |
42 | /*----- Header files ------------------------------------------------------*/ |
43 | |
44 | #include "config.h" |
45 | |
46 | #include <errno.h> |
47 | #include <stdio.h> |
48 | #include <stdlib.h> |
49 | #include <string.h> |
50 | #include <time.h> |
51 | |
52 | #include <mLib/mdwopt.h> |
53 | #include <mLib/quis.h> |
54 | #include <mLib/report.h> |
55 | #include <mLib/sub.h> |
56 | |
57 | #include <noise.h> |
58 | #include <rand.h> |
59 | |
60 | #include "getdate.h" |
61 | #include "key.h" |
62 | |
63 | /*----- Handy global state ------------------------------------------------*/ |
64 | |
65 | static const char *keyfile = "keyring"; |
66 | |
67 | /*----- Useful shared functions -------------------------------------------*/ |
68 | |
69 | /* --- @doopen@ --- * |
70 | * |
71 | * Arguments: @key_file *f@ = pointer to key file block |
72 | * @unsigned how@ = method to open file with |
73 | * |
74 | * Returns: --- |
75 | * |
76 | * Use: Opens a key file and handles errors by panicking |
77 | * appropriately. |
78 | */ |
79 | |
80 | static void doopen(key_file *f, unsigned how) |
81 | { |
82 | if (key_open(f, keyfile, how)) |
83 | die(1, "couldn't open file `%s': %s", keyfile, strerror(errno)); |
84 | } |
85 | |
86 | /* --- @doclose@ --- * |
87 | * |
88 | * Arguments: @key_file *f@ = pointer to key file block |
89 | * |
90 | * Returns: --- |
91 | * |
92 | * Use: Closes a key file and handles errors by panicking |
93 | * appropriately. |
94 | */ |
95 | |
96 | static void doclose(key_file *f) |
97 | { |
98 | switch (key_close(f)) { |
99 | case KWRITE_FAIL: |
100 | die(EXIT_FAILURE, "couldn't write file `%s': %s", |
101 | keyfile, strerror(errno)); |
102 | case KWRITE_BROKEN: |
103 | die(EXIT_FAILURE, "keyring file `%s' broken: %s (repair manually)", |
104 | keyfile, strerror(errno)); |
105 | } |
106 | } |
107 | |
108 | /* --- @setattr@ --- * |
109 | * |
110 | * Arguments: @key_file *f@ = pointer to key file block |
111 | * @key *k@ = pointer to key block |
112 | * @char *v[]@ = array of assignments (overwritten!) |
113 | * |
114 | * Returns: --- |
115 | * |
116 | * Use: Applies the attribute assignments to the key. |
117 | */ |
118 | |
119 | static void setattr(key_file *f, key *k, char *v[]) |
120 | { |
121 | while (*v) { |
122 | char *p = *v; |
123 | size_t eq = strcspn(p, "="); |
124 | if (p[eq] == 0) |
125 | moan("invalid assignment: `%s'", p); |
126 | p[eq] = 0; |
127 | p += eq + 1; |
128 | key_putattr(f, k, *v, *p ? p : 0); |
129 | v++; |
130 | } |
131 | } |
132 | |
133 | /*----- Command implementation --------------------------------------------*/ |
134 | |
135 | /* --- @cmd_add@ --- */ |
136 | |
137 | static int cmd_add(int argc, char *argv[]) |
138 | { |
139 | key_file f; |
140 | key *k; |
141 | int bits = 128; |
142 | time_t exp = KEXP_EXPIRE; |
143 | unsigned fl = 0; |
144 | unsigned char *p; |
145 | size_t sz; |
146 | const char *c = 0; |
147 | |
148 | /* --- Various useful flag bits --- */ |
149 | |
150 | enum { |
151 | f_bogus = 1 |
152 | }; |
153 | |
154 | /* --- Parse options for the subcommand --- */ |
155 | |
156 | for (;;) { |
157 | static struct option opt[] = { |
158 | { "bits", OPTF_ARGREQ, 0, 'b' }, |
159 | { "expire", OPTF_ARGREQ, 0, 'e' }, |
160 | { "comment", OPTF_ARGREQ, 0, 'c' }, |
161 | { 0, 0, 0, 0 } |
162 | }; |
163 | int i = mdwopt(argc, argv, "+b:e:c:", opt, 0, 0, 0); |
164 | if (i < 0) |
165 | break; |
166 | |
167 | /* --- Handle the various options --- */ |
168 | |
169 | switch (i) { |
170 | |
171 | /* --- Bits must be nonzero and a multiple of 8 --- */ |
172 | |
173 | case 'b': |
174 | if (!(bits = atoi(optarg)) || bits % 8) |
175 | die(EXIT_FAILURE, "bad number of bits: `%s'", optarg); |
176 | break; |
177 | |
178 | /* --- Expiry dates get passed to @get_date@ for parsing --- */ |
179 | |
180 | case 'e': |
181 | if (strcmp(optarg, "forever") == 0) |
182 | exp = KEXP_FOREVER; |
183 | else { |
184 | exp = get_date(optarg, 0); |
185 | if (exp == -1) |
186 | die(EXIT_FAILURE, "bad expiry date: `%s'", optarg); |
187 | } |
188 | break; |
189 | |
190 | /* --- Store comments without interpretation --- */ |
191 | |
192 | case 'c': |
193 | if (key_chkcomment(c)) |
194 | die(EXIT_FAILURE, "bad comment string: `%s'", optarg); |
195 | c = optarg; |
196 | break; |
197 | |
198 | /* --- Other things are bogus --- */ |
199 | |
200 | default: |
201 | fl |= f_bogus; |
202 | break; |
203 | } |
204 | } |
205 | |
206 | /* --- Various sorts of bogusity --- */ |
207 | |
208 | if (fl & f_bogus || optind + 1 > argc) { |
209 | die(EXIT_FAILURE, |
210 | "Usage: add [-b BITS] [-e EXPIRE] [-c COMMENT] TYPE [ATTR...]"); |
211 | } |
212 | if (key_chktype(argv[optind])) |
213 | die(EXIT_FAILURE, "bad key type: `%s'", argv[optind]); |
214 | if (exp == KEXP_EXPIRE) |
215 | exp = time(0) + 14 * 24 * 60 * 60; |
216 | |
217 | /* --- Initialize the Catacomb random number generator --- */ |
218 | |
219 | rand_init(RAND_GLOBAL); |
220 | rand_noisesrc(RAND_GLOBAL, &noise_source); |
221 | |
222 | /* --- Extract the key data from the generator --- */ |
223 | |
224 | sz = bits / 8; |
225 | p = xmalloc(sz); |
226 | rand_getgood(RAND_GLOBAL, p, sz); |
227 | |
228 | /* --- Open the file, add the key, set attributes, close, return --- */ |
229 | |
230 | doopen(&f, KOPEN_WRITE); |
231 | if (!(k = key_new(&f, argv[optind], p, sz, exp, c))) |
232 | moan("key not added: expiry date in past?"); |
233 | setattr(&f, k, argv + optind + 1); |
234 | doclose(&f); |
235 | return (0); |
236 | } |
237 | |
238 | /* --- @cmd_expire@ --- */ |
239 | |
240 | static int cmd_expire(int argc, char *argv[]) |
241 | { |
242 | key_file f; |
243 | key *k; |
244 | uint32 id; |
245 | int i; |
246 | int rc = 0; |
247 | |
248 | if (argc < 2) |
249 | die(EXIT_FAILURE, "Usage: expire KEYID..."); |
250 | doopen(&f, KOPEN_WRITE); |
251 | for (i = 1; i < argc; i++) { |
252 | id = (uint32)strtoul(argv[i], 0, 16); |
253 | if ((k = key_byid(&f, id)) != 0) |
254 | key_expire(&f, k); |
255 | else { |
256 | moan("keyid %lx not found", (unsigned long)id); |
257 | rc = 1; |
258 | } |
259 | } |
260 | doclose(&f); |
261 | return (rc); |
262 | } |
263 | |
264 | /* --- @cmd_delete@ --- */ |
265 | |
266 | static int cmd_delete(int argc, char *argv[]) |
267 | { |
268 | key_file f; |
269 | key *k; |
270 | uint32 id; |
271 | int i; |
272 | int rc = 0; |
273 | |
274 | if (argc < 2) |
275 | die(EXIT_FAILURE, "Usage: delete KEYID..."); |
276 | doopen(&f, KOPEN_WRITE); |
277 | for (i = 1; i < argc; i++) { |
278 | id = (uint32)strtoul(argv[i], 0, 16); |
279 | if ((k = key_byid(&f, id)) != 0) |
280 | key_delete(&f, k); |
281 | else { |
282 | moan("keyid %lx not found", (unsigned long)id); |
283 | rc = 1; |
284 | } |
285 | } |
286 | doclose(&f); |
287 | return (rc); |
288 | } |
289 | |
290 | /* --- @cmd_setattr@ --- */ |
291 | |
292 | static int cmd_setattr(int argc, char *argv[]) |
293 | { |
294 | key_file f; |
295 | key *k; |
296 | uint32 id; |
297 | |
298 | if (argc < 3) |
299 | die(EXIT_FAILURE, "Usage: setattr KEYID ATTR..."); |
300 | doopen(&f, KOPEN_WRITE); |
301 | id = (uint32)strtoul(argv[1], 0, 16); |
302 | if ((k = key_byid(&f, id)) == 0) |
303 | die(EXIT_FAILURE, "keyid %lx not found", (unsigned long)id); |
304 | setattr(&f, k, argv + 2); |
305 | doclose(&f); |
306 | return (0); |
307 | } |
308 | |
309 | /* --- @cmd_comment@ --- */ |
310 | |
311 | static int cmd_comment(int argc, char *argv[]) |
312 | { |
313 | uint32 id; |
314 | key_file f; |
315 | key *k; |
316 | |
317 | if (argc < 2 || argc > 3) |
318 | die(EXIT_FAILURE, "Usage: comment KEYID [COMMENT]"); |
319 | doopen(&f, KOPEN_WRITE); |
320 | id = (uint32)strtoul(argv[1], 0, 16); |
321 | if ((k = key_byid(&f, id)) == 0) |
322 | die(EXIT_FAILURE, "keyid %lx not found", (unsigned long)id); |
323 | if (key_chkcomment(argv[2])) |
324 | die(EXIT_FAILURE, "bad comment: `%s'", argv[2]); |
325 | key_setcomment(&f, k, argv[2]); |
326 | doclose(&f); |
327 | return (0); |
328 | } |
329 | |
330 | /* --- @cmd_list@ --- */ |
331 | |
332 | static int cmd_list(int argc, char *argv[]) |
333 | { |
334 | key_file f; |
335 | key *k; |
336 | key_iter i; |
337 | unsigned fl = 0; |
338 | const char *tfmt; |
339 | int v = 0; |
340 | time_t t; |
341 | |
342 | enum { |
343 | f_bogus = 1, |
344 | f_newline = 2, |
cc321283 |
345 | f_attr = 4, |
346 | f_utc = 8 |
d03ab969 |
347 | }; |
348 | |
349 | /* --- Parse subcommand options --- */ |
350 | |
351 | for (;;) { |
352 | static struct option opt[] = { |
353 | { "quiet", 0, 0, 'q' }, |
354 | { "verbose", 0, 0, 'v' }, |
cc321283 |
355 | { "utc", 0, 0, 'u' }, |
d03ab969 |
356 | { 0, 0, 0, 0 } |
357 | }; |
cc321283 |
358 | int i = mdwopt(argc, argv, "+uqv", opt, 0, 0, 0); |
d03ab969 |
359 | if (i < 0) |
360 | break; |
361 | |
362 | switch (i) { |
cc321283 |
363 | case 'u': |
364 | fl |= f_utc; |
365 | break; |
d03ab969 |
366 | case 'q': |
367 | if (v) |
368 | v--; |
369 | break; |
370 | case 'v': |
371 | v++; |
372 | break; |
373 | default: |
374 | fl |= f_bogus; |
375 | break; |
376 | } |
377 | } |
378 | |
379 | if (fl & f_bogus || optind != argc) |
cc321283 |
380 | die(EXIT_FAILURE, "Usage: list [-uqv]"); |
d03ab969 |
381 | |
382 | /* --- Open the key file --- */ |
383 | |
384 | doopen(&f, KOPEN_READ); |
385 | t = time(0); |
386 | |
387 | /* --- Write the header --- */ |
388 | |
cc321283 |
389 | if (!v) |
390 | tfmt = "%Y-%m-%d"; |
391 | else if (fl & f_utc) |
392 | tfmt = "%Y-%m-%d %H:%M:%S UTC"; |
393 | else |
394 | tfmt = "%Y-%m-%d %H:%M:%S %Z"; |
d03ab969 |
395 | |
396 | /* --- Now iterate through the keys --- */ |
397 | |
398 | for (key_mkiter(&i, &f); (k = key_next(&i)) != 0; ) { |
399 | char ebuf[24], dbuf[24]; |
400 | struct tm *tm; |
401 | |
402 | /* --- Sort out the expiry times --- */ |
403 | |
404 | if (KEY_EXPIRED(t, k->exp)) { |
405 | strcpy(ebuf, "expired"); |
406 | if (KEY_DELETED(t, k->del)) { |
407 | strcpy(dbuf, "deleted"); |
408 | goto donetime; |
409 | } else |
410 | goto deltime; |
411 | } |
412 | |
413 | if (k->exp == KEXP_FOREVER) |
414 | strcpy(ebuf, "forever"); |
415 | else { |
cc321283 |
416 | tm = (fl & f_utc) ? gmtime(&k->exp) : localtime(&k->exp); |
d03ab969 |
417 | strftime(ebuf, sizeof(ebuf), tfmt, tm); |
418 | } |
419 | |
420 | /* --- Sort out the delete times --- */ |
421 | |
422 | deltime: |
423 | if (k->del == KEXP_UNUSED) |
424 | strcpy(dbuf, "on expiry"); |
425 | else if (k->del == KEXP_FOREVER) |
426 | strcpy(dbuf, "forever"); |
427 | else { |
428 | tm = localtime(&k->del); |
429 | strftime(dbuf, sizeof(dbuf), tfmt, tm); |
430 | } |
431 | |
432 | donetime:; |
433 | |
434 | /* --- Display the data obtained so far --- */ |
435 | |
436 | if (!v) { |
437 | if (!(fl & f_newline)) { |
438 | printf("%8s %-20s %-10s %-10s %-23s\n", |
439 | "Id", "Type", "Expire", "Delete", "Comment"); |
440 | } |
441 | printf("%08lx %-20s %-10s %-10s %-23s\n", |
442 | (unsigned long)k->id, k->type, ebuf, dbuf, |
443 | k->c ? k->c : "<none>"); |
444 | fl |= f_newline; |
445 | } else { |
446 | |
447 | /* --- Display the standard header --- */ |
448 | |
449 | if (fl & f_newline) |
450 | fputc('\n', stdout); |
451 | printf("keyid: %08lx\n", (unsigned long)k->id); |
452 | printf("type: %s\n", k->type); |
453 | printf("expiry: %s\n", ebuf); |
454 | printf("delete: %s\n", dbuf); |
455 | printf("comment: %s\n", k->c ? k->c : "<none>"); |
456 | |
457 | /* --- Display the attributes --- */ |
458 | |
459 | if (v > 1) { |
460 | key_attriter i; |
461 | const char *av, *an; |
462 | |
463 | fl &= ~f_attr; |
464 | printf("attributes:"); |
465 | for (key_mkattriter(&i, &f, k); key_nextattr(&i, &an, &av); ) { |
466 | printf("\n\t%s = %s", an, av); |
467 | fl |= f_attr; |
468 | } |
469 | if (fl & f_attr) |
470 | fputc('\n', stdout); |
471 | else |
472 | puts(" <none>"); |
473 | } |
474 | |
475 | /* --- If dumping requested, dump the raw key data in hex --- */ |
476 | |
477 | if (v > 2) { |
478 | unsigned char *p = k->k; |
479 | unsigned char *l = p + k->ksz; |
480 | size_t sz = 0; |
481 | |
482 | fputs("key:", stdout); |
483 | while (p < l) { |
484 | if (sz % 16 == 0) |
485 | fputs("\n\t", stdout); |
486 | else if (sz % 8 == 0) |
487 | fputs(" ", stdout); |
488 | else |
489 | fputc(' ', stdout); |
490 | printf("%02x", *p++); |
491 | sz++; |
492 | } |
493 | fputc('\n', stdout); |
494 | } |
495 | } |
496 | } |
497 | |
498 | doclose(&f); |
499 | return (0); |
500 | } |
501 | |
502 | /* --- @cmd_extract@ --- */ |
503 | |
504 | static int cmd_extract(int argc, char *argv[]) |
505 | { |
506 | key_file f; |
507 | key *k; |
508 | uint32 id; |
509 | int i; |
510 | int rc = 0; |
511 | FILE *fp; |
512 | |
513 | if (argc < 3) |
514 | die(EXIT_FAILURE, "Usage: extract FILE KEYID..."); |
515 | if (strcmp(argv[1], "-") == 0) |
516 | fp = stdout; |
517 | else if (!(fp = fopen(argv[1], "w"))) { |
518 | die(EXIT_FAILURE, "couldn't open `%s' for writing: %s", |
519 | argv[1], strerror(errno)); |
520 | } |
521 | |
522 | doopen(&f, KOPEN_WRITE); |
523 | for (i = 2; i < argc; i++) { |
524 | id = (uint32)strtoul(argv[i], 0, 16); |
525 | if ((k = key_byid(&f, id)) != 0) |
526 | key_extract(&f, k, fp); |
527 | else { |
528 | moan("keyid %lx not found", (unsigned long)id); |
529 | rc = 1; |
530 | } |
531 | } |
532 | doclose(&f); |
533 | return (rc); |
534 | } |
535 | |
536 | /* --- @cmd_tidy@ --- */ |
537 | |
538 | static int cmd_tidy(int argc, char *argv[]) |
539 | { |
540 | key_file f; |
541 | if (argc != 1) |
542 | die(EXIT_FAILURE, "usage: tidy"); |
543 | doopen(&f, KOPEN_WRITE); |
544 | f.f |= KF_MODIFIED; /* Nasty hack */ |
545 | doclose(&f); |
546 | return (0); |
547 | } |
548 | |
549 | /* --- @cmd_merge@ --- */ |
550 | |
551 | static int cmd_merge(int argc, char *argv[]) |
552 | { |
553 | key_file f; |
554 | FILE *fp; |
555 | |
556 | if (argc != 2) |
557 | die(EXIT_FAILURE, "Usage: merge FILE"); |
558 | if (strcmp(argv[1], "-") == 0) |
559 | fp = stdin; |
560 | else if (!(fp = fopen(argv[1], "r"))) { |
561 | die(EXIT_FAILURE, "couldn't open `%s' for writing: %s", |
562 | argv[1], strerror(errno)); |
563 | } |
564 | |
565 | doopen(&f, KOPEN_WRITE); |
566 | key_merge(&f, argv[1], fp); |
567 | doclose(&f); |
568 | return (0); |
569 | } |
570 | |
571 | /*----- Main command table ------------------------------------------------*/ |
572 | |
573 | static struct cmd { |
574 | const char *name; |
575 | int (*cmd)(int /*argc*/, char */*argv*/[]); |
576 | const char *help; |
577 | } cmds[] = { |
578 | { "add", cmd_add, |
579 | "add [-b BITS] [-e EXPIRE] [-c COMMENT] TYPE [ATTR...]" }, |
580 | { "expire", cmd_expire, "expire KEYID..." }, |
581 | { "delete", cmd_delete, "delete KEYID..." }, |
582 | { "setattr", cmd_setattr, "setattr KEYID ATTR..." }, |
583 | { "comment", cmd_comment, "comment KEYID [COMMENT]" }, |
cc321283 |
584 | { "list", cmd_list, "list [-uqv]" }, |
d03ab969 |
585 | { "tidy", cmd_tidy, "tidy" }, |
586 | { "extract", cmd_extract, "extract FILE KEYID..." }, |
587 | { "merge", cmd_merge, "merge FILE" }, |
588 | { 0, 0, 0 } |
589 | }; |
590 | |
591 | typedef struct cmd cmd; |
592 | |
593 | /*----- Main code ---------------------------------------------------------*/ |
594 | |
595 | /* --- Helpful GNUy functions --- */ |
596 | |
597 | void usage(FILE *fp) |
598 | { |
599 | fprintf(fp, "Usage: %s [-k file] command [args]\n", QUIS); |
600 | } |
601 | |
602 | void version(FILE *fp) |
603 | { |
604 | fprintf(fp, "%s, Catacomb version " VERSION "\n", QUIS); |
605 | } |
606 | |
607 | void help(FILE *fp) |
608 | { |
609 | cmd *c; |
610 | version(fp); |
611 | fputc('\n', fp); |
612 | usage(fp); |
613 | fputs("\n\ |
614 | Performs various simple key management operations. Command line options\n\ |
615 | recognized are:\n\ |
616 | \n\ |
617 | -h, --help Display this help text.\n\ |
618 | -v, --version Display version number.\n\ |
619 | -u, --usage Display short usage summary.\n\ |
620 | \n\ |
621 | -k, --keyring=FILE Read and write keys in FILE.\n\ |
622 | \n\ |
623 | The following commands are understood:\n\n", |
624 | fp); |
625 | for (c = cmds; c->name; c++) |
626 | fprintf(fp, "%s\n", c->help); |
627 | } |
628 | |
629 | /* --- @main@ --- * |
630 | * |
631 | * Arguments: @int argc@ = number of command line arguments |
632 | * @char *argv[]@ = array of command line arguments |
633 | * |
634 | * Returns: Nonzero on failure. |
635 | * |
636 | * Use: Main program. Performs simple key management functions. |
637 | */ |
638 | |
639 | int main(int argc, char *argv[]) |
640 | { |
641 | unsigned f = 0; |
642 | |
643 | enum { |
644 | f_bogus = 1 |
645 | }; |
646 | |
647 | /* --- Initialization --- */ |
648 | |
649 | ego(argv[0]); |
650 | sub_init(); |
651 | |
652 | /* --- Parse command line options --- */ |
653 | |
654 | for (;;) { |
655 | static struct option opt[] = { |
656 | |
657 | /* --- Standard GNUy help options --- */ |
658 | |
659 | { "help", 0, 0, 'h' }, |
660 | { "version", 0, 0, 'v' }, |
661 | { "usage", 0, 0, 'u' }, |
662 | |
663 | /* --- Real live useful options --- */ |
664 | |
665 | { "keyring", OPTF_ARGREQ, 0, 'k' }, |
666 | |
667 | /* --- Magic terminator --- */ |
668 | |
669 | { 0, 0, 0, 0 } |
670 | }; |
671 | int i = mdwopt(argc, argv, "+hvu k:", opt, 0, 0, 0); |
672 | |
673 | if (i < 0) |
674 | break; |
675 | switch (i) { |
676 | |
677 | /* --- GNU help options --- */ |
678 | case 'h': |
679 | help(stdout); |
680 | exit(0); |
681 | case 'v': |
682 | version(stdout); |
683 | exit(0); |
684 | case 'u': |
685 | usage(stdout); |
686 | exit(0); |
687 | |
688 | /* --- Real useful options --- */ |
689 | |
690 | case 'k': |
691 | keyfile = optarg; |
692 | break; |
693 | |
694 | /* --- Bogosity --- */ |
695 | |
696 | default: |
697 | f |= f_bogus; |
698 | break; |
699 | } |
700 | } |
701 | |
702 | /* --- Complain about excessive bogons --- */ |
703 | |
704 | if (f & f_bogus || optind == argc) { |
705 | usage(stderr); |
706 | exit(1); |
707 | } |
708 | |
709 | /* --- Dispatch to appropriate command handler --- */ |
710 | |
711 | argc -= optind; |
712 | argv += optind; |
713 | optind = 0; |
714 | |
715 | { |
716 | cmd *c, *chosen = 0; |
717 | size_t sz = strlen(argv[0]); |
718 | |
719 | for (c = cmds; c->name; c++) { |
720 | if (strncmp(argv[0], c->name, sz) == 0) { |
721 | if (c->name[sz] == 0) { |
722 | chosen = c; |
723 | break; |
724 | } else if (chosen) |
725 | die(EXIT_FAILURE, "ambiguous command name `%s'", argv[0]); |
726 | else |
727 | chosen = c; |
728 | } |
729 | } |
730 | if (!chosen) |
731 | die(EXIT_FAILURE, "unknown command name `%s'", argv[0]); |
732 | return (chosen->cmd(argc, argv)); |
733 | } |
734 | } |
735 | |
736 | /*----- That's all, folks -------------------------------------------------*/ |