catcrypt.c, catsign.c: Shorten chunk sizes.
[u/mdw/catacomb] / hashsum.c
CommitLineData
e375fe33 1/* -*-c-*-
2 *
5685a696 3 * $Id$
e375fe33 4 *
5 * Hash files using some secure hash function
6 *
7 * (c) 2000 Straylight/Edgeware
8 */
9
45c0fd36 10/*----- Licensing notice --------------------------------------------------*
e375fe33 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.
45c0fd36 18 *
e375fe33 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.
45c0fd36 23 *
e375fe33 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
e375fe33 30/*----- Header files ------------------------------------------------------*/
31
43d1332f
MW
32#define _FILE_OFFSET_BITS 64
33
e375fe33 34#include "config.h"
35
5685a696 36#include <assert.h>
e375fe33 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
5685a696 51#include <mLib/hex.h>
52#include <mLib/base32.h>
53#include <mLib/base64.h>
54
e375fe33 55#include "ghash.h"
c65df279 56#include "cc.h"
e375fe33 57
43d1332f
MW
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
e375fe33 68/*----- Static variables --------------------------------------------------*/
69
16efd15b 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
5685a696 78#define f_oddenc 256u
43d1332f 79#define f_progress 512u
5685a696 80
81/*----- Encoding and decoding ---------------------------------------------*/
82
83/* --- Hex encoding --- */
84
85static void puthex(const octet *buf, size_t sz, FILE *fp)
86{
87 while (sz) {
88 fprintf(fp, "%02x", *buf++);
89 sz--;
90 }
91}
92
93static 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;
45c0fd36 110 return (i);
5685a696 111}
112
113/* --- Base64 encoding --- */
114
115static 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
129static 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
148static 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
162static 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
c65df279 181typedef struct encodeops {
5685a696 182 const char *name;
183 void (*put)(const octet *, size_t, FILE *);
184 size_t (*get)(const char *, octet *, size_t, char **);
c65df279 185} encodeops;
5685a696 186
c65df279 187static const encodeops encodingtab[] = {
5685a696 188 { "hex", puthex, gethex },
189 { "base64", putb64, getb64 },
190 { "base32", putb32, getb32 },
191 { 0, 0, 0 }
192};
193
c65df279 194static const encodeops *getencoding(const char *ename)
5685a696 195{
c65df279 196 const encodeops *e;
5685a696 197
c65df279 198 for (e = encodingtab; e->name; e++) {
5685a696 199 if (strcmp(ename, e->name) == 0)
200 return (e);
201 }
202 return (0);
203}
e375fe33 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
43d1332f
MW
219struct unit {
220 const char *name;
221 int m;
222};
223
224static 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
239static 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
e375fe33 254static int fhash(const char *file, unsigned f, const gchash *gch, void *buf)
255{
256 FILE *fp;
43d1332f 257 char fbuf[1024 * 128];
e375fe33 258 size_t sz;
259 ghash *h;
260 int e;
43d1332f
MW
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;
e375fe33 268
d7e6bc66 269 if (!file || strcmp(file, "-") == 0)
e375fe33 270 fp = stdin;
271 else if ((fp = fopen(file, f & f_binary ? "rb" : "r")) == 0)
272 return (-1);
273
43d1332f
MW
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
b817bfc6 295 h = GH_INIT(gch);
43d1332f 296 while ((sz = fread(fbuf, 1, sizeof(fbuf), fp)) > 0) {
b817bfc6 297 GH_HASH(h, fbuf, sz);
43d1332f
MW
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 }
b817bfc6 319 GH_DONE(h, buf);
320 GH_DESTROY(h);
43d1332f 321 if (f & f_progress) fprintf(stderr, "\r%78s\r", "");
e375fe33 322 e = ferror(fp);
323 if (file)
324 fclose(fp);
325 return (e ? -1 : 0);
326}
327
e375fe33 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
337static const gchash *gethash(const char *name)
338{
e9026a0a 339 const gchash *const *g, *gg = 0;
e375fe33 340 size_t sz = strlen(name);
e9026a0a 341 for (g = ghashtab; *g; g++) {
e375fe33 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
367static 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
d470270a 374#define NEXTCH (fp ? getc(fp) : (unsigned char)*p++)
e375fe33 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
396again:
397 ch = NEXTCH;
d470270a 398 while (isspace(ch))
e375fe33 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;
d470270a 447 if (!q && isspace(ch))
e375fe33 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
477static 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
5685a696 532static int checkhash(const char *file, unsigned f,
c65df279 533 const gchash *gch, const encodeops *e)
e375fe33 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
d7e6bc66 542 if (!file || strcmp(file, "-") == 0)
e375fe33 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);
5685a696 569 } else if (strcmp(q, "encoding") == 0) {
c65df279 570 const encodeops *ee;
5685a696 571 if ((q = str_getword(&p)) == 0)
572 continue;
c65df279 573 if ((ee = getencoding(q)) == 0)
5685a696 574 continue;
575 e = ee;
e375fe33 576 } else if (strcmp(q, "escape") == 0)
577 f |= f_escape;
578 continue;
579 }
580
581 /* --- Otherwise it's a hex thing --- */
582
12902a5c 583 q = p;
584 while (*p && *p != ' ')
585 p++;
586 if (!*p)
e375fe33 587 continue;
12902a5c 588 *p++ = 0;
5685a696 589 if (e->get(q, buf, gch->hashsz, 0) < gch->hashsz)
e375fe33 590 continue;
12902a5c 591 if (*p == '*')
e375fe33 592 ff |= f_binary;
12902a5c 593 else if (*p != ' ')
e375fe33 594 continue;
12902a5c 595 p++;
e375fe33 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
5685a696 632static int dohash(const char *file, unsigned f,
c65df279 633 const gchash *gch, const encodeops *e)
e375fe33 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 {
5685a696 642 e->put(p, gch->hashsz, stdout);
e375fe33 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
5685a696 658static int dofile(const char *file, unsigned f,
c65df279 659 const gchash *gch, const encodeops *e)
12902a5c 660{
5685a696 661 return (f & f_check ? checkhash : dohash)(file, f, gch, e);
12902a5c 662}
663
5685a696 664static int hashfiles(const char *file, unsigned f,
c65df279 665 const gchash *gch, const encodeops *e)
e375fe33 666{
667 FILE *fp;
668 dstr d = DSTR_INIT;
669 int rc = 0;
670 int rrc;
671
d7e6bc66 672 if (!file || strcmp(file, "-") == 0)
e375fe33 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;
5685a696 683 if ((rrc = dofile(d.buf, f, gch, e)) != 0)
e375fe33 684 rc = rrc;
685 }
686
687 return (rc);
688}
689
5685a696 690static int hashsum(const char *file, unsigned f,
c65df279 691 const gchash *gch, const encodeops *e)
e375fe33 692{
5685a696 693 return (f & f_files ? hashfiles : dofile)(file, f, gch, e);
e375fe33 694}
695
696/*----- Main driver -------------------------------------------------------*/
697
c65df279 698void version(FILE *fp)
e375fe33 699{
700 pquis(fp, "$, Catacomb version " VERSION "\n");
701}
702
703static void usage(FILE *fp)
704{
c65df279 705 pquis(fp, "Usage: $ [-f0ebcv] [-a ALGORITHM] [-E ENC] [FILES...]\n");
e375fe33 706}
707
708static void help(FILE *fp, const gchash *gch)
709{
710 version(fp);
711 fputc('\n', fp);
712 usage(fp);
713 pquis(fp, "\n\
714Generates 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\
c65df279 719-l, --list [ITEM...] Show known hash functions and/or encodings.\n\
e375fe33 720\n\
721-a, --algorithm=ALG Use the message digest algorithm ALG.\n\
92c494ce 722-E, --encoding=ENC Represent hashes using encoding ENC.\n\
e375fe33 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\
92c494ce 732For a list of hashing algorithms and encodings, type `$ --list'.\n\
e375fe33 733");
734 if (gch)
735 fprintf(fp, "The default message digest algorithm is %s.\n", gch->name);
736}
737
c65df279 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
743MAKELISTTAB(listtab, LISTS)
744
e375fe33 745int main(int argc, char *argv[])
746{
747 unsigned f = 0;
748 const gchash *gch = 0;
c65df279 749 const encodeops *e = &encodingtab[0];
e375fe33 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)
e9026a0a 767 gch = gethash("md5");
e375fe33 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' },
5685a696 781 { "encoding", OPTF_ARGREQ, 0, 'E' },
e375fe33 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' },
43d1332f 792 { "progress", 0, 0, 'p' },
e375fe33 793
794 { 0, 0, 0, 0 }
795 };
43d1332f 796 int i = mdwopt(argc, argv, "hVu a:E:l f0 ecbvp", opts, 0, 0, 0);
e375fe33 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);
c65df279 810 case 'l':
811 exit(displaylists(listtab, argv + optind));
e375fe33 812 case 'a':
813 if ((gch = gethash(optarg)) == 0)
814 die(EXIT_FAILURE, "unknown hash algorithm `%s'", optarg);
815 f |= f_oddhash;
816 break;
5685a696 817 case 'E':
c65df279 818 if ((e = getencoding(optarg)) == 0)
5685a696 819 die(EXIT_FAILURE, "unknown encoding `%s'", optarg);
820 f |= f_oddenc;
821 break;
e375fe33 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;
43d1332f
MW
840 case 'p':
841 f |= f_progress;
842 break;
e375fe33 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
db2d393e
MW
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 }
92c494ce 863 if (!argc)
864 rc = hashsum(0, f, gch, e);
865 else {
e375fe33 866 int i;
867 int rrc;
92c494ce 868
e375fe33 869 rc = 0;
870 for (i = 0; i < argc; i++) {
5685a696 871 if ((rrc = hashsum(argv[i], f, gch, e)) != 0)
e375fe33 872 rc = rrc;
873 }
92c494ce 874 }
e375fe33 875
876 return (rc);
877}
878
879/*----- That's all, folks -------------------------------------------------*/