catcrypt.c, catsign.c: Shorten chunk sizes.
[u/mdw/catacomb] / hashsum.c
1 /* -*-c-*-
2 *
3 * $Id$
4 *
5 * Hash files using some secure hash function
6 *
7 * (c) 2000 Straylight/Edgeware
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 /*----- Header files ------------------------------------------------------*/
31
32 #define _FILE_OFFSET_BITS 64
33
34 #include "config.h"
35
36 #include <assert.h>
37 #include <ctype.h>
38 #include <errno.h>
39 #include <stdio.h>
40 #include <stdlib.h>
41 #include <string.h>
42
43 #include <mLib/alloc.h>
44 #include <mLib/dstr.h>
45 #include <mLib/mdwopt.h>
46 #include <mLib/quis.h>
47 #include <mLib/report.h>
48 #include <mLib/sub.h>
49 #include <mLib/str.h>
50
51 #include <mLib/hex.h>
52 #include <mLib/base32.h>
53 #include <mLib/base64.h>
54
55 #include "ghash.h"
56 #include "cc.h"
57
58 #ifndef PATHSEP
59 # if defined(__riscos)
60 # define PATHSEP '.'
61 # elif defined(__unix) || defined(unix)
62 # define PATHSEP '/'
63 # else
64 # define PATHSEP '\\'
65 # endif
66 #endif
67
68 /*----- Static variables --------------------------------------------------*/
69
70 #define f_binary 1u
71 #define f_bogus 2u
72 #define f_verbose 4u
73 #define f_check 8u
74 #define f_files 16u
75 #define f_raw 32u
76 #define f_oddhash 64u
77 #define f_escape 128u
78 #define f_oddenc 256u
79 #define f_progress 512u
80
81 /*----- Encoding and decoding ---------------------------------------------*/
82
83 /* --- Hex encoding --- */
84
85 static void puthex(const octet *buf, size_t sz, FILE *fp)
86 {
87 while (sz) {
88 fprintf(fp, "%02x", *buf++);
89 sz--;
90 }
91 }
92
93 static size_t gethex(const char *p, octet *q, size_t sz, char **pp)
94 {
95 size_t i = 0;
96 while (sz > 0 &&
97 isxdigit((unsigned char)p[0]) &&
98 isxdigit((unsigned char)p[1])) {
99 char buf[3];
100 buf[0] = p[0];
101 buf[1] = p[1];
102 buf[2] = 0;
103 *q++ = strtoul(buf, 0, 16);
104 sz--;
105 p += 2;
106 i++;
107 }
108 if (pp)
109 *pp = (char *)p;
110 return (i);
111 }
112
113 /* --- Base64 encoding --- */
114
115 static void putb64(const octet *buf, size_t sz, FILE *fp)
116 {
117 base64_ctx b;
118 dstr d = DSTR_INIT;
119
120 base64_init(&b);
121 b.indent = "";
122 b.maxline = 0;
123 base64_encode(&b, buf, sz, &d);
124 base64_encode(&b, 0, 0, &d);
125 dstr_write(&d, fp);
126 dstr_destroy(&d);
127 }
128
129 static size_t getb64(const char *p, octet *q, size_t sz, char **pp)
130 {
131 base64_ctx b;
132 dstr d = DSTR_INIT;
133 size_t n = strlen(p);
134
135 base64_init(&b);
136 base64_decode(&b, p, n, &d);
137 if (pp) *pp = (/*unconst*/ char *)p + n;
138 base64_decode(&b, 0, 0, &d);
139 assert(d.len <= sz);
140 memcpy(q, d.buf, sz);
141 n = d.len;
142 dstr_destroy(&d);
143 return (n);
144 }
145
146 /* --- Base32 encoding --- */
147
148 static void putb32(const octet *buf, size_t sz, FILE *fp)
149 {
150 base32_ctx b;
151 dstr d = DSTR_INIT;
152
153 base32_init(&b);
154 b.indent = "";
155 b.maxline = 0;
156 base32_encode(&b, buf, sz, &d);
157 base32_encode(&b, 0, 0, &d);
158 dstr_write(&d, fp);
159 dstr_destroy(&d);
160 }
161
162 static size_t getb32(const char *p, octet *q, size_t sz, char **pp)
163 {
164 base32_ctx b;
165 dstr d = DSTR_INIT;
166 size_t n = strlen(p);
167
168 base32_init(&b);
169 base32_decode(&b, p, n, &d);
170 if (pp) *pp = (/*unconst*/ char *)p + n;
171 base32_decode(&b, 0, 0, &d);
172 assert(d.len <= sz);
173 memcpy(q, d.buf, sz);
174 n = d.len;
175 dstr_destroy(&d);
176 return (n);
177 }
178
179 /* --- Table --- */
180
181 typedef struct encodeops {
182 const char *name;
183 void (*put)(const octet *, size_t, FILE *);
184 size_t (*get)(const char *, octet *, size_t, char **);
185 } encodeops;
186
187 static const encodeops encodingtab[] = {
188 { "hex", puthex, gethex },
189 { "base64", putb64, getb64 },
190 { "base32", putb32, getb32 },
191 { 0, 0, 0 }
192 };
193
194 static const encodeops *getencoding(const char *ename)
195 {
196 const encodeops *e;
197
198 for (e = encodingtab; e->name; e++) {
199 if (strcmp(ename, e->name) == 0)
200 return (e);
201 }
202 return (0);
203 }
204
205 /*----- Support functions -------------------------------------------------*/
206
207 /* --- @fhash@ --- *
208 *
209 * Arguments: @const char *file@ = file name to be hashed (null for stdin)
210 * @unsigned f@ = flags to set
211 * @const gchash *gch@ = pointer to hash function to use
212 * @void *buf@ = pointer to hash output buffer
213 *
214 * Returns: Zero if it worked, nonzero on error.
215 *
216 * Use: Hashes a file.
217 */
218
219 struct unit {
220 const char *name;
221 int m;
222 };
223
224 static void prhuman_time(FILE *fp, unsigned long n)
225 {
226 const static struct unit utime[] = {
227 { "s", 60 }, { "m", 60 }, { "h", 24 }, { "d", 0 }
228 };
229
230 unsigned long m = 0;
231 const struct unit *u = utime;
232
233 while (u[1].m && n > u[0].m*u[1].m) { n /= u->m; u++; }
234 m = n / u[1].m; n %= u[0].m;
235 if (m) fprintf(fp, "%3lu%s%02lu%s", m, u[1].name, n, u[0].name);
236 else fprintf(fp, " %2lu%s", n, u[0].name);
237 }
238
239 static void prhuman_data(FILE *fp, off_t n)
240 {
241 const static struct unit udata[] = {
242 { " ", 1024 }, { "k", 1024 }, { "M", 1024 }, { "G", 1024 },
243 { "T", 1024 }, { "P", 1024 }, { "E", 1024 }, { "Z", 1024 },
244 { "Y", 0 }
245 };
246
247 double x = n;
248 const struct unit *u = udata;
249
250 while (u->m && x >= u->m) { x /= u->m; u++; }
251 fprintf(fp, "%6.1f%s", x, u->name);
252 }
253
254 static int fhash(const char *file, unsigned f, const gchash *gch, void *buf)
255 {
256 FILE *fp;
257 char fbuf[1024 * 128];
258 size_t sz;
259 ghash *h;
260 int e;
261 off_t fsz = -1, fo;
262 const char *p;
263 dstr d = DSTR_INIT;
264 static char baton[] = "-\\|/";
265 char *bp = baton;
266 time_t now, last, start;
267 int i, pc;
268
269 if (!file || strcmp(file, "-") == 0)
270 fp = stdin;
271 else if ((fp = fopen(file, f & f_binary ? "rb" : "r")) == 0)
272 return (-1);
273
274 if (f & f_progress) {
275 if ((fo = ftello(fp)) >= 0 &&
276 fseeko(fp, 0, SEEK_END) >= 0 &&
277 (fsz = ftello(fp),
278 fseeko(fp, fo, SEEK_SET) < 0))
279 return (-1);
280 if (fo != -1 && fsz != -1) fsz -= fo;
281 fo = 0;
282 sz = strlen(file);
283 if (sz < 24)
284 dstr_puts(&d, file);
285 else if ((p = strchr(file + sz - 20, PATHSEP)) != 0) {
286 dstr_puts(&d, "..."); dstr_puts(&d, p);
287 } else {
288 p = strrchr(file, PATHSEP);
289 if (!p) dstr_putf(&d, "%.20s...", file);
290 else dstr_putf(&d, "...%.17s...", p);
291 }
292 start = last = time(0);
293 }
294
295 h = GH_INIT(gch);
296 while ((sz = fread(fbuf, 1, sizeof(fbuf), fp)) > 0) {
297 GH_HASH(h, fbuf, sz);
298 if (f & f_progress) {
299 fo += sz;
300 now = time(0);
301 if (difftime(now, last) < 1) continue;
302 last = now;
303 fprintf(stderr, "\r%-24s", d.buf);
304 fprintf(stderr, "%c ", *bp++); if (!*bp) bp = baton;
305 prhuman_data(stderr, fo);
306 if (fsz >= fo) {
307 fputc('/', stderr);
308 prhuman_data(stderr, fsz);
309 fputs(" [", stderr);
310 pc = (fo*16 + fsz/2)/fsz;
311 for (i = 0; i < pc; i++) fputc('.', stderr);
312 for (; i < 16; i++) fputc(' ', stderr);
313 fprintf(stderr, "] %3d%%", (int)((fo*100 + 50)/fsz));
314 fprintf(stderr, " ETA ");
315 prhuman_time(stderr, difftime(now, start)*(fsz - fo)/fo);
316 }
317 }
318 }
319 GH_DONE(h, buf);
320 GH_DESTROY(h);
321 if (f & f_progress) fprintf(stderr, "\r%78s\r", "");
322 e = ferror(fp);
323 if (file)
324 fclose(fp);
325 return (e ? -1 : 0);
326 }
327
328 /* --- @gethash@ --- *
329 *
330 * Arguments: @const char *name@ = pointer to name string
331 *
332 * Returns: Pointer to appropriate hash class.
333 *
334 * Use: Chooses a hash function by name.
335 */
336
337 static const gchash *gethash(const char *name)
338 {
339 const gchash *const *g, *gg = 0;
340 size_t sz = strlen(name);
341 for (g = ghashtab; *g; g++) {
342 if (strncmp(name, (*g)->name, sz) == 0) {
343 if ((*g)->name[sz] == 0) {
344 gg = *g;
345 break;
346 } else if (gg)
347 return (0);
348 else
349 gg = *g;
350 }
351 }
352 return (gg);
353 }
354
355 /* --- @getstring@ --- *
356 *
357 * Arguments: @FILE *fp@ = stream from which to read
358 * @const char *p@ = string to read from instead
359 * @dstr *d@ = destination string
360 * @unsigned raw@ = raw or cooked read
361 *
362 * Returns: Zero if OK, nonzero on end-of-file.
363 *
364 * Use: Reads a filename (or something similar) from a stream.
365 */
366
367 static int getstring(FILE *fp, const char *p, dstr *d, unsigned raw)
368 {
369 int ch;
370 int q = 0;
371
372 /* --- Raw: just read exactly what's written up to a null byte --- */
373
374 #define NEXTCH (fp ? getc(fp) : (unsigned char)*p++)
375 #define EOFCH (fp ? EOF : 0)
376
377 if (raw) {
378 if ((ch = NEXTCH) == EOFCH)
379 return (EOF);
380 for (;;) {
381 if (!ch)
382 break;
383 DPUTC(d, ch);
384 if ((ch = NEXTCH) == EOFCH)
385 break;
386 }
387 DPUTZ(d);
388 return (0);
389 }
390
391 /* --- Skip as far as whitespace --- *
392 *
393 * Also skip past comments.
394 */
395
396 again:
397 ch = NEXTCH;
398 while (isspace(ch))
399 ch = NEXTCH;
400 if (ch == '#') {
401 do ch = NEXTCH; while (ch != '\n' && ch != EOFCH);
402 goto again;
403 }
404 if (ch == EOFCH)
405 return (EOF);
406
407 /* --- If the character is a quote then read a quoted string --- */
408
409 switch (ch) {
410 case '`':
411 ch = '\'';
412 case '\'':
413 case '\"':
414 q = ch;
415 ch = NEXTCH;
416 break;
417 }
418
419 /* --- Now read all sorts of interesting things --- */
420
421 for (;;) {
422
423 /* --- Handle an escaped thing --- */
424
425 if (ch == '\\') {
426 ch = NEXTCH;
427 if (ch == EOFCH)
428 break;
429 switch (ch) {
430 case 'a': ch = '\a'; break;
431 case 'b': ch = '\b'; break;
432 case 'f': ch = '\f'; break;
433 case 'n': ch = '\n'; break;
434 case 'r': ch = '\r'; break;
435 case 't': ch = '\t'; break;
436 case 'v': ch = '\v'; break;
437 }
438 DPUTC(d, ch);
439 ch = NEXTCH;
440 continue;
441 }
442
443 /* --- If it's a quote or some other end marker then stop --- */
444
445 if (ch == q)
446 break;
447 if (!q && isspace(ch))
448 break;
449
450 /* --- Otherwise contribute and continue --- */
451
452 DPUTC(d, ch);
453 if ((ch = NEXTCH) == EOFCH)
454 break;
455 }
456
457 /* --- Done --- */
458
459 DPUTZ(d);
460 return (0);
461
462 #undef NEXTCH
463 #undef EOFCH
464 }
465
466 /* --- @putstring@ --- *
467 *
468 * Arguments: @FILE *fp@ = stream to write on
469 * @const char *p@ = pointer to text
470 * @unsigned raw@ = whether the string is to be written raw
471 *
472 * Returns: ---
473 *
474 * Use: Emits a string to a stream.
475 */
476
477 static void putstring(FILE *fp, const char *p, unsigned raw)
478 {
479 size_t sz = strlen(p);
480 unsigned qq;
481 const char *q;
482
483 /* --- Just write the string null terminated if raw --- */
484
485 if (raw) {
486 fwrite(p, 1, sz + 1, fp);
487 return;
488 }
489
490 /* --- Check for any dodgy characters --- */
491
492 qq = 0;
493 for (q = p; *q; q++) {
494 if (isspace((unsigned char)*q)) {
495 qq = '\"';
496 break;
497 }
498 }
499
500 if (qq)
501 putc(qq, fp);
502
503 /* --- Emit the string --- */
504
505 for (q = p; *q; q++) {
506 switch (*q) {
507 case '\a': fputc('\\', fp); fputc('a', fp); break;
508 case '\b': fputc('\\', fp); fputc('b', fp); break;
509 case '\f': fputc('\\', fp); fputc('f', fp); break;
510 case '\n': fputc('\\', fp); fputc('n', fp); break;
511 case '\r': fputc('\\', fp); fputc('r', fp); break;
512 case '\t': fputc('\\', fp); fputc('t', fp); break;
513 case '\v': fputc('\\', fp); fputc('v', fp); break;
514 case '`': fputc('\\', fp); fputc('`', fp); break;
515 case '\'': fputc('\\', fp); fputc('\'', fp); break;
516 case '\"': fputc('\\', fp); fputc('\"', fp); break;
517 case '#': fputc('\\', fp); fputc('#', fp); break;
518 default:
519 putc(*q, fp);
520 break;
521 }
522 }
523
524 /* --- Done --- */
525
526 if (qq)
527 putc(qq, fp);
528 }
529
530 /*----- Guts --------------------------------------------------------------*/
531
532 static int checkhash(const char *file, unsigned f,
533 const gchash *gch, const encodeops *e)
534 {
535 int rc;
536 FILE *fp;
537 dstr d = DSTR_INIT;
538 dstr dd = DSTR_INIT;
539 unsigned long n = 0, nfail = 0;
540 octet *buf = xmalloc(2 * gch->hashsz);
541
542 if (!file || strcmp(file, "-") == 0)
543 fp = stdin;
544 else if ((fp = fopen(file, f & f_raw ? "r" : "rb")) == 0) {
545 moan("couldn't open `%s': %s", file, strerror(errno));
546 return (EXIT_FAILURE);
547 }
548
549 while (DRESET(&d), dstr_putline(&d, fp) != EOF) {
550 char *p = d.buf;
551 char *q;
552 unsigned ff = f;
553
554 /* --- Handle a directive --- */
555
556 if (*p == '#') {
557 p++;
558 if ((q = str_getword(&p)) == 0)
559 continue;
560 if (strcmp(q, "hash") == 0) {
561 const gchash *g;
562 if ((q = str_getword(&p)) == 0)
563 continue;
564 if ((g = gethash(q)) == 0)
565 continue;
566 gch = g;
567 xfree(buf);
568 buf = xmalloc(2 * gch->hashsz);
569 } else if (strcmp(q, "encoding") == 0) {
570 const encodeops *ee;
571 if ((q = str_getword(&p)) == 0)
572 continue;
573 if ((ee = getencoding(q)) == 0)
574 continue;
575 e = ee;
576 } else if (strcmp(q, "escape") == 0)
577 f |= f_escape;
578 continue;
579 }
580
581 /* --- Otherwise it's a hex thing --- */
582
583 q = p;
584 while (*p && *p != ' ')
585 p++;
586 if (!*p)
587 continue;
588 *p++ = 0;
589 if (e->get(q, buf, gch->hashsz, 0) < gch->hashsz)
590 continue;
591 if (*p == '*')
592 ff |= f_binary;
593 else if (*p != ' ')
594 continue;
595 p++;
596
597 if (f & f_escape) {
598 DRESET(&dd);
599 getstring(0, p, &dd, 0);
600 p = dd.buf;
601 }
602
603 if (fhash(p, ff, gch, buf + gch->hashsz)) {
604 moan("couldn't read `%s': %s", p, strerror(errno));
605 rc = EXIT_FAILURE;
606 continue;
607 }
608 if (memcmp(buf, buf + gch->hashsz, gch->hashsz) != 0) {
609 if (ff & f_verbose)
610 fprintf(stderr, "FAIL %s\n", p);
611 else
612 moan("%s check failed for `%s'", gch->name, p);
613 nfail++;
614 rc = EXIT_FAILURE;
615 } else {
616 if (ff & f_verbose)
617 fprintf(stderr, "OK %s\n", p);
618 }
619 n++;
620 }
621
622 dstr_destroy(&d);
623 dstr_destroy(&dd);
624 xfree(buf);
625 if ((f & f_verbose) && nfail)
626 moan("%lu of %lu file(s) failed %s check", nfail, n, gch->name);
627 else if (!n)
628 moan("no files checked");
629 return (0);
630 }
631
632 static int dohash(const char *file, unsigned f,
633 const gchash *gch, const encodeops *e)
634 {
635 int rc = 0;
636 octet *p = xmalloc(gch->hashsz);
637
638 if (fhash(file, f, gch, p)) {
639 moan("couldn't read `%s': %s", file ? file : "<stdin>", strerror(errno));
640 rc = EXIT_FAILURE;
641 } else {
642 e->put(p, gch->hashsz, stdout);
643 if (file) {
644 fputc(' ', stdout);
645 fputc(f & f_binary ? '*' : ' ', stdout);
646 if (f & f_escape)
647 putstring(stdout, file, 0);
648 else
649 fputs(file, stdout);
650 }
651 fputc('\n', stdout);
652 }
653
654 xfree(p);
655 return (rc);
656 }
657
658 static int dofile(const char *file, unsigned f,
659 const gchash *gch, const encodeops *e)
660 {
661 return (f & f_check ? checkhash : dohash)(file, f, gch, e);
662 }
663
664 static int hashfiles(const char *file, unsigned f,
665 const gchash *gch, const encodeops *e)
666 {
667 FILE *fp;
668 dstr d = DSTR_INIT;
669 int rc = 0;
670 int rrc;
671
672 if (!file || strcmp(file, "-") == 0)
673 fp = stdin;
674 else if ((fp = fopen(file, f & f_raw ? "r" : "rb")) == 0) {
675 moan("couldn't open `%s': %s", file, strerror(errno));
676 return (EXIT_FAILURE);
677 }
678
679 for (;;) {
680 DRESET(&d);
681 if (getstring(fp, 0, &d, f & f_raw))
682 break;
683 if ((rrc = dofile(d.buf, f, gch, e)) != 0)
684 rc = rrc;
685 }
686
687 return (rc);
688 }
689
690 static int hashsum(const char *file, unsigned f,
691 const gchash *gch, const encodeops *e)
692 {
693 return (f & f_files ? hashfiles : dofile)(file, f, gch, e);
694 }
695
696 /*----- Main driver -------------------------------------------------------*/
697
698 void version(FILE *fp)
699 {
700 pquis(fp, "$, Catacomb version " VERSION "\n");
701 }
702
703 static void usage(FILE *fp)
704 {
705 pquis(fp, "Usage: $ [-f0ebcv] [-a ALGORITHM] [-E ENC] [FILES...]\n");
706 }
707
708 static void help(FILE *fp, const gchash *gch)
709 {
710 version(fp);
711 fputc('\n', fp);
712 usage(fp);
713 pquis(fp, "\n\
714 Generates or checks message digests on files. Options available:\n\
715 \n\
716 -h, --help Display this help message.\n\
717 -V, --version Display program's version number.\n\
718 -u, --usage Display a terse usage message.\n\
719 -l, --list [ITEM...] Show known hash functions and/or encodings.\n\
720 \n\
721 -a, --algorithm=ALG Use the message digest algorithm ALG.\n\
722 -E, --encoding=ENC Represent hashes using encoding ENC.\n\
723 \n\
724 -f, --files Read a list of file names from standard input.\n\
725 -0, --null File names are null terminated, not plain text.\n\
726 \n\
727 -e, --escape Escape funny characters in filenames.\n\
728 -c, --check Check message digests rather than emitting them.\n\
729 -b, --binary When reading files, treat them as binary.\n\
730 -v, --verbose Be verbose when checking digests.\n\
731 \n\
732 For a list of hashing algorithms and encodings, type `$ --list'.\n\
733 ");
734 if (gch)
735 fprintf(fp, "The default message digest algorithm is %s.\n", gch->name);
736 }
737
738 #define LISTS(LI) \
739 LI("Lists", list, listtab[i].name, listtab[i].name) \
740 LI("Hash functions", hash, ghashtab[i], ghashtab[i]->name) \
741 LI("Encodings", enc, encodingtab[i].name, encodingtab[i].name)
742
743 MAKELISTTAB(listtab, LISTS)
744
745 int main(int argc, char *argv[])
746 {
747 unsigned f = 0;
748 const gchash *gch = 0;
749 const encodeops *e = &encodingtab[0];
750 int rc;
751
752 /* --- Initialization --- */
753
754 ego(argv[0]);
755 sub_init();
756
757 /* --- Choose a hash function from the name --- */
758
759 {
760 char *q = xstrdup(QUIS);
761 size_t len = strlen(q);
762 if (len > 3 && strcmp(q + len - 3, "sum") == 0) {
763 q[len - 3] = 0;
764 gch = gethash(q);
765 }
766 if (!gch)
767 gch = gethash("md5");
768 xfree(q);
769 }
770
771 /* --- Read options --- */
772
773 for (;;) {
774 static struct option opts[] = {
775 { "help", 0, 0, 'h' },
776 { "verbose", 0, 0, 'V' },
777 { "usage", 0, 0, 'u' },
778
779 { "algorithm", OPTF_ARGREQ, 0, 'a' },
780 { "hash", OPTF_ARGREQ, 0, 'a' },
781 { "encoding", OPTF_ARGREQ, 0, 'E' },
782 { "list", 0, 0, 'l' },
783
784 { "files", 0, 0, 'f' },
785 { "find", 0, 0, 'f' },
786 { "null", 0, 0, '0' },
787
788 { "escape", 0, 0, 'e' },
789 { "check", 0, 0, 'c' },
790 { "binary", 0, 0, 'b' },
791 { "verbose", 0, 0, 'v' },
792 { "progress", 0, 0, 'p' },
793
794 { 0, 0, 0, 0 }
795 };
796 int i = mdwopt(argc, argv, "hVu a:E:l f0 ecbvp", opts, 0, 0, 0);
797 if (i < 0)
798 break;
799
800 switch (i) {
801 case 'h':
802 help(stdout, gch);
803 exit(0);
804 case 'V':
805 version(stdout);
806 exit(0);
807 case 'u':
808 usage(stdout);
809 exit(0);
810 case 'l':
811 exit(displaylists(listtab, argv + optind));
812 case 'a':
813 if ((gch = gethash(optarg)) == 0)
814 die(EXIT_FAILURE, "unknown hash algorithm `%s'", optarg);
815 f |= f_oddhash;
816 break;
817 case 'E':
818 if ((e = getencoding(optarg)) == 0)
819 die(EXIT_FAILURE, "unknown encoding `%s'", optarg);
820 f |= f_oddenc;
821 break;
822 case 'f':
823 f |= f_files;
824 break;
825 case '0':
826 f |= f_raw;
827 break;
828 case 'e':
829 f |= f_escape;
830 break;
831 case 'c':
832 f |= f_check;
833 break;
834 case 'b':
835 f |= f_binary;
836 break;
837 case 'v':
838 f |= f_verbose;
839 break;
840 case 'p':
841 f |= f_progress;
842 break;
843 default:
844 f |= f_bogus;
845 break;
846 }
847 }
848
849 if (f & f_bogus) {
850 usage(stderr);
851 exit(EXIT_FAILURE);
852 }
853 argv += optind;
854 argc -= optind;
855
856 /* --- Generate output --- */
857
858 if (!(f & f_check) && (argc || (f & f_files))) {
859 if (f & f_oddhash) printf("#hash %s\n", gch->name);
860 if (f & f_oddenc) printf("#encoding %s\n", e->name);
861 if (f & f_escape) fputs("#escape\n", stdout);
862 }
863 if (!argc)
864 rc = hashsum(0, f, gch, e);
865 else {
866 int i;
867 int rrc;
868
869 rc = 0;
870 for (i = 0; i < argc; i++) {
871 if ((rrc = hashsum(argv[i], f, gch, e)) != 0)
872 rc = rrc;
873 }
874 }
875
876 return (rc);
877 }
878
879 /*----- That's all, folks -------------------------------------------------*/