1 /* Compute checksums of files or strings.
2 Copyright (C) 1995-2023 Free Software Foundation, Inc.
3
4 This program is free software: you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation, either version 3 of the License, or
7 (at your option) any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
13
14 You should have received a copy of the GNU General Public License
15 along with this program. If not, see <https://www.gnu.org/licenses/>. */
16
17 /* Written by Ulrich Drepper <drepper@gnu.ai.mit.edu>. */
18
19 #include <config.h>
20
21 #include <getopt.h>
22 #include <sys/types.h>
23
24 #include "system.h"
25 #include "argmatch.h"
26 #include "quote.h"
27 #include "xdectoint.h"
28 #include "xstrtol.h"
29
30 #if HASH_ALGO_SUM || HASH_ALGO_CKSUM
31 # include "sum.h"
32 #endif
33 #if HASH_ALGO_CKSUM
34 # include "cksum.h"
35 # include "base64.h"
36 #endif
37 #if HASH_ALGO_BLAKE2 || HASH_ALGO_CKSUM
38 # include "blake2/b2sum.h"
39 #endif
40 #if HASH_ALGO_MD5 || HASH_ALGO_CKSUM
41 # include "md5.h"
42 #endif
43 #if HASH_ALGO_SHA1 || HASH_ALGO_CKSUM
44 # include "sha1.h"
45 #endif
46 #if HASH_ALGO_SHA256 || HASH_ALGO_SHA224 || HASH_ALGO_CKSUM
47 # include "sha256.h"
48 #endif
49 #if HASH_ALGO_SHA512 || HASH_ALGO_SHA384 || HASH_ALGO_CKSUM
50 # include "sha512.h"
51 #endif
52 #if HASH_ALGO_CKSUM
53 # include "sm3.h"
54 #endif
55 #include "fadvise.h"
56 #include "stdio--.h"
57 #include "xbinary-io.h"
58
59 /* The official name of this program (e.g., no 'g' prefix). */
60 #if HASH_ALGO_SUM
61 # define PROGRAM_NAME "sum"
62 # define DIGEST_TYPE_STRING "BSD"
63 # define DIGEST_STREAM sumfns[sum_algorithm]
64 # define DIGEST_OUT sum_output_fns[sum_algorithm]
65 # define DIGEST_BITS 16
66 # define DIGEST_ALIGN 4
67 #elif HASH_ALGO_CKSUM
68 # define MAX_DIGEST_BITS 512
69 # define MAX_DIGEST_ALIGN 8
70 # define PROGRAM_NAME "cksum"
71 # define DIGEST_TYPE_STRING algorithm_tags[cksum_algorithm]
72 # define DIGEST_STREAM cksumfns[cksum_algorithm]
73 # define DIGEST_OUT cksum_output_fns[cksum_algorithm]
74 # define DIGEST_BITS MAX_DIGEST_BITS
75 # define DIGEST_ALIGN MAX_DIGEST_ALIGN
76 #elif HASH_ALGO_MD5
77 # define PROGRAM_NAME "md5sum"
78 # define DIGEST_TYPE_STRING "MD5"
79 # define DIGEST_STREAM md5_stream
80 # define DIGEST_BITS 128
81 # define DIGEST_REFERENCE "RFC 1321"
82 # define DIGEST_ALIGN 4
83 #elif HASH_ALGO_BLAKE2
84 # define PROGRAM_NAME "b2sum"
85 # define DIGEST_TYPE_STRING "BLAKE2b"
86 # define DIGEST_STREAM blake2b_stream
87 # define DIGEST_BITS 512
88 # define DIGEST_REFERENCE "RFC 7693"
89 # define DIGEST_ALIGN 8
90 #elif HASH_ALGO_SHA1
91 # define PROGRAM_NAME "sha1sum"
92 # define DIGEST_TYPE_STRING "SHA1"
93 # define DIGEST_STREAM sha1_stream
94 # define DIGEST_BITS 160
95 # define DIGEST_REFERENCE "FIPS-180-1"
96 # define DIGEST_ALIGN 4
97 #elif HASH_ALGO_SHA256
98 # define PROGRAM_NAME "sha256sum"
99 # define DIGEST_TYPE_STRING "SHA256"
100 # define DIGEST_STREAM sha256_stream
101 # define DIGEST_BITS 256
102 # define DIGEST_REFERENCE "FIPS-180-2"
103 # define DIGEST_ALIGN 4
104 #elif HASH_ALGO_SHA224
105 # define PROGRAM_NAME "sha224sum"
106 # define DIGEST_TYPE_STRING "SHA224"
107 # define DIGEST_STREAM sha224_stream
108 # define DIGEST_BITS 224
109 # define DIGEST_REFERENCE "RFC 3874"
110 # define DIGEST_ALIGN 4
111 #elif HASH_ALGO_SHA512
112 # define PROGRAM_NAME "sha512sum"
113 # define DIGEST_TYPE_STRING "SHA512"
114 # define DIGEST_STREAM sha512_stream
115 # define DIGEST_BITS 512
116 # define DIGEST_REFERENCE "FIPS-180-2"
117 # define DIGEST_ALIGN 8
118 #elif HASH_ALGO_SHA384
119 # define PROGRAM_NAME "sha384sum"
120 # define DIGEST_TYPE_STRING "SHA384"
121 # define DIGEST_STREAM sha384_stream
122 # define DIGEST_BITS 384
123 # define DIGEST_REFERENCE "FIPS-180-2"
124 # define DIGEST_ALIGN 8
125 #else
126 # error "Can't decide which hash algorithm to compile."
127 #endif
128 #if !HASH_ALGO_SUM && !HASH_ALGO_CKSUM
129 # define DIGEST_OUT output_file
130 #endif
131
132 #if HASH_ALGO_SUM
133 # define AUTHORS \
134 proper_name ("Kayvan Aghaiepour"), \
135 proper_name ("David MacKenzie")
136 #elif HASH_ALGO_CKSUM
137 # define AUTHORS \
138 proper_name_lite ("Padraig Brady", "P\303\241draig Brady"), \
139 proper_name ("Q. Frank Xia")
140 #elif HASH_ALGO_BLAKE2
141 # define AUTHORS \
142 proper_name_lite ("Padraig Brady", "P\303\241draig Brady"), \
143 proper_name ("Samuel Neves")
144 #else
145 # define AUTHORS \
146 proper_name ("Ulrich Drepper"), \
147 proper_name ("Scott Miller"), \
148 proper_name ("David Madore")
149 #endif
150 #if !HASH_ALGO_BLAKE2 && !HASH_ALGO_CKSUM
151 # define DIGEST_HEX_BYTES (DIGEST_BITS / 4)
152 #endif
153 #define DIGEST_BIN_BYTES (DIGEST_BITS / 8)
154
155 /* The minimum length of a valid digest line. This length does
156 not include any newline character at the end of a line. */
157 #if HASH_ALGO_BLAKE2 || HASH_ALGO_CKSUM
158 # define MIN_DIGEST_LINE_LENGTH 3 /* With -l 8. */
159 #else
160 # define MIN_DIGEST_LINE_LENGTH \
161 (DIGEST_HEX_BYTES /* length of hexadecimal message digest */ \
162 + 1 /* blank */ \
163 + 1 /* minimum filename length */ )
164 #endif
165
166 #if !HASH_ALGO_SUM
167 static void
168 output_file (char const *file, int binary_file, void const *digest,
169 bool raw, bool tagged, unsigned char delim, bool args,
170 uintmax_t length);
171 #endif
172
173 /* True if any of the files read were the standard input. */
174 static bool have_read_stdin;
175
176 /* The minimum length of a valid checksum line for the selected algorithm. */
177 static size_t min_digest_line_length;
178
179 /* Set to the length of a digest hex string for the selected algorithm. */
180 static size_t digest_hex_bytes;
181
182 /* With --check, don't generate any output.
183 The exit code indicates success or failure. */
184 static bool status_only = false;
185
186 /* With --check, print a message to standard error warning about each
187 improperly formatted checksum line. */
188 static bool warn = false;
189
190 /* With --check, ignore missing files. */
191 static bool ignore_missing = false;
192
193 /* With --check, suppress the "OK" printed for each verified file. */
194 static bool quiet = false;
195
196 /* With --check, exit with a non-zero return code if any line is
197 improperly formatted. */
198 static bool strict = false;
199
200 /* Whether a BSD reversed format checksum is detected. */
201 static int bsd_reversed = -1;
202
203 /* line delimiter. */
204 static unsigned char digest_delim = '\n';
205
206 #if HASH_ALGO_CKSUM
207 /* If true, print base64-encoded digests, not hex. */
208 static bool base64_digest = false;
209 #endif
210
211 /* If true, print binary digests, not hex. */
212 static bool raw_digest = false;
213
214 #if HASH_ALGO_BLAKE2 || HASH_ALGO_CKSUM
215 # define BLAKE2B_MAX_LEN BLAKE2B_OUTBYTES
216 static uintmax_t digest_length;
217 #endif /* HASH_ALGO_BLAKE2 */
218
219 typedef void (*digest_output_fn)(char const *, int, void const *, bool,
220 bool, unsigned char, bool, uintmax_t);
221 #if HASH_ALGO_SUM
222 enum Algorithm
223 {
224 bsd,
225 sysv,
226 };
227
228 static enum Algorithm sum_algorithm;
229 static sumfn sumfns[]=
230 {
231 bsd_sum_stream,
232 sysv_sum_stream,
233 };
234 static digest_output_fn sum_output_fns[]=
235 {
236 output_bsd,
237 output_sysv,
238 };
239 #endif
240
241 #if HASH_ALGO_CKSUM
242 static int
md5_sum_stream(FILE * stream,void * resstream,uintmax_t * length)243 md5_sum_stream (FILE *stream, void *resstream, uintmax_t *length)
244 {
245 return md5_stream (stream, resstream);
246 }
247 static int
sha1_sum_stream(FILE * stream,void * resstream,uintmax_t * length)248 sha1_sum_stream (FILE *stream, void *resstream, uintmax_t *length)
249 {
250 return sha1_stream (stream, resstream);
251 }
252 static int
sha224_sum_stream(FILE * stream,void * resstream,uintmax_t * length)253 sha224_sum_stream (FILE *stream, void *resstream, uintmax_t *length)
254 {
255 return sha224_stream (stream, resstream);
256 }
257 static int
sha256_sum_stream(FILE * stream,void * resstream,uintmax_t * length)258 sha256_sum_stream (FILE *stream, void *resstream, uintmax_t *length)
259 {
260 return sha256_stream (stream, resstream);
261 }
262 static int
sha384_sum_stream(FILE * stream,void * resstream,uintmax_t * length)263 sha384_sum_stream (FILE *stream, void *resstream, uintmax_t *length)
264 {
265 return sha384_stream (stream, resstream);
266 }
267 static int
sha512_sum_stream(FILE * stream,void * resstream,uintmax_t * length)268 sha512_sum_stream (FILE *stream, void *resstream, uintmax_t *length)
269 {
270 return sha512_stream (stream, resstream);
271 }
272 static int
blake2b_sum_stream(FILE * stream,void * resstream,uintmax_t * length)273 blake2b_sum_stream (FILE *stream, void *resstream, uintmax_t *length)
274 {
275 return blake2b_stream (stream, resstream, *length);
276 }
277 static int
sm3_sum_stream(FILE * stream,void * resstream,uintmax_t * length)278 sm3_sum_stream (FILE *stream, void *resstream, uintmax_t *length)
279 {
280 return sm3_stream (stream, resstream);
281 }
282
283 enum Algorithm
284 {
285 bsd,
286 sysv,
287 crc,
288 md5,
289 sha1,
290 sha224,
291 sha256,
292 sha384,
293 sha512,
294 blake2b,
295 sm3,
296 };
297
298 static char const *const algorithm_args[] =
299 {
300 "bsd", "sysv", "crc", "md5", "sha1", "sha224",
301 "sha256", "sha384", "sha512", "blake2b", "sm3", nullptr
302 };
303 static enum Algorithm const algorithm_types[] =
304 {
305 bsd, sysv, crc, md5, sha1, sha224,
306 sha256, sha384, sha512, blake2b, sm3,
307 };
308 ARGMATCH_VERIFY (algorithm_args, algorithm_types);
309
310 static char const *const algorithm_tags[] =
311 {
312 "BSD", "SYSV", "CRC", "MD5", "SHA1", "SHA224",
313 "SHA256", "SHA384", "SHA512", "BLAKE2b", "SM3", nullptr
314 };
315 static int const algorithm_bits[] =
316 {
317 16, 16, 32, 128, 160, 224,
318 256, 384, 512, 512, 256, 0
319 };
320
321 static_assert (ARRAY_CARDINALITY (algorithm_bits)
322 == ARRAY_CARDINALITY (algorithm_args));
323
324 static bool algorithm_specified = false;
325 static enum Algorithm cksum_algorithm = crc;
326 static sumfn cksumfns[]=
327 {
328 bsd_sum_stream,
329 sysv_sum_stream,
330 crc_sum_stream,
331 md5_sum_stream,
332 sha1_sum_stream,
333 sha224_sum_stream,
334 sha256_sum_stream,
335 sha384_sum_stream,
336 sha512_sum_stream,
337 blake2b_sum_stream,
338 sm3_sum_stream,
339 };
340 static digest_output_fn cksum_output_fns[]=
341 {
342 output_bsd,
343 output_sysv,
344 output_crc,
345 output_file,
346 output_file,
347 output_file,
348 output_file,
349 output_file,
350 output_file,
351 output_file,
352 output_file,
353 };
354 bool cksum_debug;
355 #endif
356
357 /* For long options that have no equivalent short option, use a
358 non-character as a pseudo short option, starting with CHAR_MAX + 1. */
359
360 enum
361 {
362 IGNORE_MISSING_OPTION = CHAR_MAX + 1,
363 STATUS_OPTION,
364 QUIET_OPTION,
365 STRICT_OPTION,
366 TAG_OPTION,
367 UNTAG_OPTION,
368 DEBUG_PROGRAM_OPTION,
369 RAW_OPTION,
370 BASE64_OPTION,
371 };
372
373 static struct option const long_options[] =
374 {
375 #if HASH_ALGO_BLAKE2 || HASH_ALGO_CKSUM
376 { "length", required_argument, nullptr, 'l'},
377 #endif
378
379 #if !HASH_ALGO_SUM
380 { "check", no_argument, nullptr, 'c' },
381 { "ignore-missing", no_argument, nullptr, IGNORE_MISSING_OPTION},
382 { "quiet", no_argument, nullptr, QUIET_OPTION },
383 { "status", no_argument, nullptr, STATUS_OPTION },
384 { "warn", no_argument, nullptr, 'w' },
385 { "strict", no_argument, nullptr, STRICT_OPTION },
386 { "tag", no_argument, nullptr, TAG_OPTION },
387 { "zero", no_argument, nullptr, 'z' },
388
389 # if HASH_ALGO_CKSUM
390 { "algorithm", required_argument, nullptr, 'a'},
391 { "base64", no_argument, nullptr, BASE64_OPTION },
392 { "debug", no_argument, nullptr, DEBUG_PROGRAM_OPTION},
393 { "raw", no_argument, nullptr, RAW_OPTION},
394 { "untagged", no_argument, nullptr, UNTAG_OPTION },
395 # endif
396 { "binary", no_argument, nullptr, 'b' },
397 { "text", no_argument, nullptr, 't' },
398
399 #else
400 {"sysv", no_argument, nullptr, 's'},
401 #endif
402
403 { GETOPT_HELP_OPTION_DECL },
404 { GETOPT_VERSION_OPTION_DECL },
405 { nullptr, 0, nullptr, 0 }
406 };
407
408 void
usage(int status)409 usage (int status)
410 {
411 if (status != EXIT_SUCCESS)
412 emit_try_help ();
413 else
414 {
415 printf (_("\
416 Usage: %s [OPTION]... [FILE]...\n\
417 "), program_name);
418 #if HASH_ALGO_CKSUM
419 fputs (_("\
420 Print or verify checksums.\n\
421 By default use the 32 bit CRC algorithm.\n\
422 "), stdout);
423 #else
424 printf (_("\
425 Print or check %s (%d-bit) checksums.\n\
426 "),
427 DIGEST_TYPE_STRING,
428 DIGEST_BITS);
429 #endif
430
431 emit_stdin_note ();
432 #if HASH_ALGO_SUM
433 fputs (_("\
434 \n\
435 -r use BSD sum algorithm (the default), use 1K blocks\n\
436 -s, --sysv use System V sum algorithm, use 512 bytes blocks\n\
437 "), stdout);
438 #endif
439 #if HASH_ALGO_BLAKE2 || HASH_ALGO_CKSUM
440 emit_mandatory_arg_note ();
441 #endif
442 #if HASH_ALGO_CKSUM
443 fputs (_("\
444 -a, --algorithm=TYPE select the digest type to use. See DIGEST below.\
445 \n\
446 "), stdout);
447 fputs (_("\
448 --base64 emit base64-encoded digests, not hexadecimal\
449 \n\
450 "), stdout);
451 #endif
452 #if !HASH_ALGO_SUM
453 # if !HASH_ALGO_CKSUM
454 if (O_BINARY)
455 fputs (_("\
456 -b, --binary read in binary mode (default unless reading tty stdin)\
457 \n\
458 "), stdout);
459 else
460 fputs (_("\
461 -b, --binary read in binary mode\n\
462 "), stdout);
463 # endif
464 fputs (_("\
465 -c, --check read checksums from the FILEs and check them\n\
466 "), stdout);
467 # if HASH_ALGO_BLAKE2 || HASH_ALGO_CKSUM
468 fputs (_("\
469 -l, --length=BITS digest length in bits; must not exceed the max for\n\
470 the blake2 algorithm and must be a multiple of 8\n\
471 "), stdout);
472 # endif
473 # if HASH_ALGO_CKSUM
474 fputs (_("\
475 --raw emit a raw binary digest, not hexadecimal\
476 \n\
477 "), stdout);
478 fputs (_("\
479 --tag create a BSD-style checksum (the default)\n\
480 "), stdout);
481 fputs (_("\
482 --untagged create a reversed style checksum, without digest type\n\
483 "), stdout);
484 # else
485 fputs (_("\
486 --tag create a BSD-style checksum\n\
487 "), stdout);
488 # endif
489 # if !HASH_ALGO_CKSUM
490 if (O_BINARY)
491 fputs (_("\
492 -t, --text read in text mode (default if reading tty stdin)\n\
493 "), stdout);
494 else
495 fputs (_("\
496 -t, --text read in text mode (default)\n\
497 "), stdout);
498 # endif
499 fputs (_("\
500 -z, --zero end each output line with NUL, not newline,\n\
501 and disable file name escaping\n\
502 "), stdout);
503 fputs (_("\
504 \n\
505 The following five options are useful only when verifying checksums:\n\
506 --ignore-missing don't fail or report status for missing files\n\
507 --quiet don't print OK for each successfully verified file\n\
508 --status don't output anything, status code shows success\n\
509 --strict exit non-zero for improperly formatted checksum lines\n\
510 -w, --warn warn about improperly formatted checksum lines\n\
511 \n\
512 "), stdout);
513 #endif
514 #if HASH_ALGO_CKSUM
515 fputs (_("\
516 --debug indicate which implementation used\n\
517 "), stdout);
518 #endif
519 fputs (HELP_OPTION_DESCRIPTION, stdout);
520 fputs (VERSION_OPTION_DESCRIPTION, stdout);
521 #if HASH_ALGO_CKSUM
522 fputs (_("\
523 \n\
524 DIGEST determines the digest algorithm and default output format:\n\
525 sysv (equivalent to sum -s)\n\
526 bsd (equivalent to sum -r)\n\
527 crc (equivalent to cksum)\n\
528 md5 (equivalent to md5sum)\n\
529 sha1 (equivalent to sha1sum)\n\
530 sha224 (equivalent to sha224sum)\n\
531 sha256 (equivalent to sha256sum)\n\
532 sha384 (equivalent to sha384sum)\n\
533 sha512 (equivalent to sha512sum)\n\
534 blake2b (equivalent to b2sum)\n\
535 sm3 (only available through cksum)\n\
536 \n"), stdout);
537 #endif
538 #if !HASH_ALGO_SUM && !HASH_ALGO_CKSUM
539 printf (_("\
540 \n\
541 The sums are computed as described in %s.\n"), DIGEST_REFERENCE);
542 fputs (_("\
543 When checking, the input should be a former output of this program.\n\
544 The default mode is to print a line with: checksum, a space,\n\
545 a character indicating input mode ('*' for binary, ' ' for text\n\
546 or where binary is insignificant), and name for each FILE.\n\
547 \n\
548 Note: There is no difference between binary mode and text mode on GNU systems.\
549 \n"), stdout);
550 #endif
551 #if HASH_ALGO_CKSUM
552 fputs (_("\
553 When checking, the input should be a former output of this program,\n\
554 or equivalent standalone program.\
555 \n"), stdout);
556 #endif
557 emit_ancillary_info (PROGRAM_NAME);
558 }
559
560 exit (status);
561 }
562
563 /* Given a string S, return TRUE if it contains problematic characters
564 that need escaping. Note we escape '\' itself to provide some forward
565 compat to introduce escaping of other characters. */
566
567 ATTRIBUTE_PURE
568 static bool
problematic_chars(char const * s)569 problematic_chars (char const *s)
570 {
571 size_t length = strcspn (s, "\\\n\r");
572 return s[length] != '\0';
573 }
574
575 #define ISWHITE(c) ((c) == ' ' || (c) == '\t')
576
577 /* Given a file name, S of length S_LEN, that is not NUL-terminated,
578 modify it in place, performing the equivalent of this sed substitution:
579 's/\\n/\n/g;s/\\r/\r/g;s/\\\\/\\/g' i.e., replacing each "\\n" string
580 with a newline, each "\\r" string with a carriage return,
581 and each "\\\\" with a single backslash, NUL-terminate it and return S.
582 If S is not a valid escaped file name, i.e., if it ends with an odd number
583 of backslashes or if it contains a backslash followed by anything other
584 than "n" or another backslash, return nullptr. */
585
586 static char *
filename_unescape(char * s,size_t s_len)587 filename_unescape (char *s, size_t s_len)
588 {
589 char *dst = s;
590
591 for (size_t i = 0; i < s_len; i++)
592 {
593 switch (s[i])
594 {
595 case '\\':
596 if (i == s_len - 1)
597 {
598 /* File name ends with an unescaped backslash: invalid. */
599 return nullptr;
600 }
601 ++i;
602 switch (s[i])
603 {
604 case 'n':
605 *dst++ = '\n';
606 break;
607 case 'r':
608 *dst++ = '\r';
609 break;
610 case '\\':
611 *dst++ = '\\';
612 break;
613 default:
614 /* Only '\', 'n' or 'r' may follow a backslash. */
615 return nullptr;
616 }
617 break;
618
619 case '\0':
620 /* The file name may not contain a NUL. */
621 return nullptr;
622
623 default:
624 *dst++ = s[i];
625 break;
626 }
627 }
628 if (dst < s + s_len)
629 *dst = '\0';
630
631 return s;
632 }
633
634 /* Return true if S is a LEN-byte NUL-terminated string of hex or base64
635 digits and has the expected length. Otherwise, return false. */
636 ATTRIBUTE_PURE
637 static bool
valid_digits(unsigned char const * s,size_t len)638 valid_digits (unsigned char const *s, size_t len)
639 {
640 #if HASH_ALGO_CKSUM
641 if (len == BASE64_LENGTH (digest_length / 8))
642 {
643 size_t i;
644 for (i = 0; i < len - digest_length % 3; i++)
645 {
646 if (!isbase64 (*s))
647 return false;
648 ++s;
649 }
650 for ( ; i < len; i++)
651 {
652 if (*s != '=')
653 return false;
654 ++s;
655 }
656 }
657 else
658 #endif
659 if (len == digest_hex_bytes)
660 {
661 for (idx_t i = 0; i < digest_hex_bytes; i++)
662 {
663 if (!isxdigit (*s))
664 return false;
665 ++s;
666 }
667 }
668 else
669 return false;
670
671 return *s == '\0';
672 }
673
674 /* Split the checksum string S (of length S_LEN) from a BSD 'md5' or
675 'sha1' command into two parts: a hexadecimal digest, and the file
676 name. S is modified. Set *D_LEN to the length of the digest string.
677 Return true if successful. */
678
679 static bool
bsd_split_3(char * s,size_t s_len,unsigned char ** digest,size_t * d_len,char ** file_name,bool escaped_filename)680 bsd_split_3 (char *s, size_t s_len,
681 unsigned char **digest, size_t *d_len,
682 char **file_name, bool escaped_filename)
683 {
684 if (s_len == 0)
685 return false;
686
687 /* Find end of filename. */
688 size_t i = s_len - 1;
689 while (i && s[i] != ')')
690 i--;
691
692 if (s[i] != ')')
693 return false;
694
695 *file_name = s;
696
697 if (escaped_filename && filename_unescape (s, i) == nullptr)
698 return false;
699
700 s[i++] = '\0';
701
702 while (ISWHITE (s[i]))
703 i++;
704
705 if (s[i] != '=')
706 return false;
707
708 i++;
709
710 while (ISWHITE (s[i]))
711 i++;
712
713 *digest = (unsigned char *) &s[i];
714
715 *d_len = s_len - i;
716 return valid_digits (*digest, *d_len);
717 }
718
719 #if HASH_ALGO_CKSUM
720 /* Return the corresponding Algorithm for the string S,
721 or -1 for no match. */
722
723 static ptrdiff_t
algorithm_from_tag(char * s)724 algorithm_from_tag (char *s)
725 {
726 /* Limit check size to this length for perf reasons. */
727 static size_t max_tag_len;
728 if (! max_tag_len)
729 {
730 char const * const * tag = algorithm_tags;
731 while (*tag)
732 {
733 size_t tag_len = strlen (*tag++);
734 max_tag_len = MAX (tag_len, max_tag_len);
735 }
736 }
737
738 size_t i = 0;
739
740 /* Find end of tag */
741 while (i <= max_tag_len && s[i] && ! ISWHITE (s[i])
742 && s[i] != '-' && s[i] != '(')
743 ++i;
744
745 if (i > max_tag_len)
746 return -1;
747
748 /* Terminate tag, and lookup. */
749 char sep = s[i];
750 s[i] = '\0';
751 ptrdiff_t algo = argmatch_exact (s, algorithm_tags);
752 s[i] = sep;
753
754 return algo;
755 }
756 #endif
757
758 /* Split the string S (of length S_LEN) into three parts:
759 a hexadecimal digest, binary flag, and the file name.
760 S is modified. Set *D_LEN to the length of the digest string.
761 Return true if successful. */
762
763 static bool
split_3(char * s,size_t s_len,unsigned char ** digest,size_t * d_len,int * binary,char ** file_name)764 split_3 (char *s, size_t s_len,
765 unsigned char **digest, size_t *d_len, int *binary, char **file_name)
766 {
767 bool escaped_filename = false;
768 size_t algo_name_len;
769
770 size_t i = 0;
771 while (ISWHITE (s[i]))
772 ++i;
773
774 if (s[i] == '\\')
775 {
776 ++i;
777 escaped_filename = true;
778 }
779
780 /* Check for BSD-style checksum line. */
781
782 #if HASH_ALGO_CKSUM
783 if (! algorithm_specified)
784 {
785 ptrdiff_t algo_tag = algorithm_from_tag (s + i);
786 if (algo_tag >= 0)
787 {
788 if (algo_tag <= crc)
789 return false; /* We don't support checking these older formats. */
790 cksum_algorithm = algo_tag;
791 }
792 else
793 return false; /* We only support tagged format without -a. */
794 }
795 #endif
796
797 algo_name_len = strlen (DIGEST_TYPE_STRING);
798 if (STREQ_LEN (s + i, DIGEST_TYPE_STRING, algo_name_len))
799 {
800 i += algo_name_len;
801 #if HASH_ALGO_BLAKE2 || HASH_ALGO_CKSUM
802 /* Terminate and match algorithm name. */
803 char const *algo_name = &s[i - algo_name_len];
804 bool length_specified = s[i] == '-';
805 bool openssl_format = s[i] == '('; /* and no length_specified */
806 s[i++] = '\0';
807 if (!STREQ (algo_name, DIGEST_TYPE_STRING))
808 return false;
809 if (openssl_format)
810 s[--i] = '(';
811
812 # if HASH_ALGO_BLAKE2
813 digest_length = BLAKE2B_MAX_LEN * 8;
814 # else
815 digest_length = algorithm_bits[cksum_algorithm];
816 # endif
817 if (length_specified)
818 {
819 uintmax_t length;
820 char *siend;
821 if (! (xstrtoumax (s + i, &siend, 0, &length, nullptr) == LONGINT_OK
822 && 0 < length && length <= digest_length
823 && length % 8 == 0))
824 return false;
825
826 i = siend - s;
827 digest_length = length;
828 }
829 digest_hex_bytes = digest_length / 4;
830 #endif
831 if (s[i] == ' ')
832 ++i;
833 if (s[i] == '(')
834 {
835 ++i;
836 *binary = 0;
837 return bsd_split_3 (s + i, s_len - i,
838 digest, d_len, file_name, escaped_filename);
839 }
840 return false;
841 }
842
843 /* Ignore this line if it is too short.
844 Each line must have at least 'min_digest_line_length - 1' (or one more, if
845 the first is a backslash) more characters to contain correct message digest
846 information. */
847 if (s_len - i < min_digest_line_length + (s[i] == '\\'))
848 return false;
849
850 *digest = (unsigned char *) &s[i];
851
852 #if HASH_ALGO_BLAKE2 || HASH_ALGO_CKSUM
853 /* Auto determine length. */
854 # if HASH_ALGO_CKSUM
855 if (cksum_algorithm == blake2b) {
856 # endif
857 unsigned char const *hp = *digest;
858 digest_hex_bytes = 0;
859 while (isxdigit (*hp++))
860 digest_hex_bytes++;
861 if (digest_hex_bytes < 2 || digest_hex_bytes % 2
862 || BLAKE2B_MAX_LEN * 2 < digest_hex_bytes)
863 return false;
864 digest_length = digest_hex_bytes * 4;
865 # if HASH_ALGO_CKSUM
866 }
867 # endif
868 #endif
869
870 /* This field must be the hexadecimal or base64 representation
871 of the message digest. */
872 while (s[i] && !ISWHITE (s[i]))
873 i++;
874
875 /* The digest must be followed by at least one whitespace character. */
876 if (i == s_len)
877 return false;
878
879 *d_len = &s[i] - (char *) *digest;
880 s[i++] = '\0';
881
882 if (! valid_digits (*digest, *d_len))
883 return false;
884
885 /* If "bsd reversed" format detected. */
886 if ((s_len - i == 1) || (s[i] != ' ' && s[i] != '*'))
887 {
888 /* Don't allow mixing bsd and standard formats,
889 to minimize security issues with attackers
890 renaming files with leading spaces.
891 This assumes that with bsd format checksums
892 that the first file name does not have
893 a leading ' ' or '*'. */
894 if (bsd_reversed == 0)
895 return false;
896 bsd_reversed = 1;
897 }
898 else if (bsd_reversed != 1)
899 {
900 bsd_reversed = 0;
901 *binary = (s[i++] == '*');
902 }
903
904 /* All characters between the type indicator and end of line are
905 significant -- that includes leading and trailing white space. */
906 *file_name = &s[i];
907
908 if (escaped_filename)
909 return filename_unescape (&s[i], s_len - i) != nullptr;
910
911 return true;
912 }
913
914 /* If ESCAPE is true, then translate each:
915 NEWLINE byte to the string, "\\n",
916 CARRIAGE RETURN byte to the string, "\\r",
917 and each backslash to "\\\\". */
918 static void
print_filename(char const * file,bool escape)919 print_filename (char const *file, bool escape)
920 {
921 if (! escape)
922 {
923 fputs (file, stdout);
924 return;
925 }
926
927 while (*file)
928 {
929 switch (*file)
930 {
931 case '\n':
932 fputs ("\\n", stdout);
933 break;
934
935 case '\r':
936 fputs ("\\r", stdout);
937 break;
938
939 case '\\':
940 fputs ("\\\\", stdout);
941 break;
942
943 default:
944 putchar (*file);
945 break;
946 }
947 file++;
948 }
949 }
950
951 /* An interface to the function, DIGEST_STREAM.
952 Operate on FILENAME (it may be "-").
953
954 *BINARY indicates whether the file is binary. BINARY < 0 means it
955 depends on whether binary mode makes any difference and the file is
956 a terminal; in that case, clear *BINARY if the file was treated as
957 text because it was a terminal.
958
959 Put the checksum in *BIN_RESULT, which must be properly aligned.
960 Put true in *MISSING if the file can't be opened due to ENOENT.
961 Return true if successful. */
962
963 static bool
digest_file(char const * filename,int * binary,unsigned char * bin_result,bool * missing,MAYBE_UNUSED uintmax_t * length)964 digest_file (char const *filename, int *binary, unsigned char *bin_result,
965 bool *missing, MAYBE_UNUSED uintmax_t *length)
966 {
967 FILE *fp;
968 int err;
969 bool is_stdin = STREQ (filename, "-");
970
971 *missing = false;
972
973 if (is_stdin)
974 {
975 have_read_stdin = true;
976 fp = stdin;
977 if (O_BINARY && *binary)
978 {
979 if (*binary < 0)
980 *binary = ! isatty (STDIN_FILENO);
981 if (*binary)
982 xset_binary_mode (STDIN_FILENO, O_BINARY);
983 }
984 }
985 else
986 {
987 fp = fopen (filename, (O_BINARY && *binary ? "rb" : "r"));
988 if (fp == nullptr)
989 {
990 if (ignore_missing && errno == ENOENT)
991 {
992 *missing = true;
993 return true;
994 }
995 error (0, errno, "%s", quotef (filename));
996 return false;
997 }
998 }
999
1000 fadvise (fp, FADVISE_SEQUENTIAL);
1001
1002 #if HASH_ALGO_CKSUM
1003 if (cksum_algorithm == blake2b)
1004 *length = digest_length / 8;
1005 err = DIGEST_STREAM (fp, bin_result, length);
1006 #elif HASH_ALGO_SUM
1007 err = DIGEST_STREAM (fp, bin_result, length);
1008 #elif HASH_ALGO_BLAKE2
1009 err = DIGEST_STREAM (fp, bin_result, digest_length / 8);
1010 #else
1011 err = DIGEST_STREAM (fp, bin_result);
1012 #endif
1013 err = err ? errno : 0;
1014 if (is_stdin)
1015 clearerr (fp);
1016 else if (fclose (fp) != 0 && !err)
1017 err = errno;
1018
1019 if (err)
1020 {
1021 error (0, err, "%s", quotef (filename));
1022 return false;
1023 }
1024
1025 return true;
1026 }
1027
1028 #if !HASH_ALGO_SUM
1029 static void
output_file(char const * file,int binary_file,void const * digest,bool raw,bool tagged,unsigned char delim,MAYBE_UNUSED bool args,MAYBE_UNUSED uintmax_t length)1030 output_file (char const *file, int binary_file, void const *digest,
1031 bool raw, bool tagged, unsigned char delim, MAYBE_UNUSED bool args,
1032 MAYBE_UNUSED uintmax_t length)
1033 {
1034 # if HASH_ALGO_CKSUM
1035 if (raw)
1036 {
1037 fwrite (digest, 1, digest_length / 8, stdout);
1038 return;
1039 }
1040 # endif
1041
1042 unsigned char const *bin_buffer = digest;
1043
1044 /* Output a leading backslash if the file name contains problematic chars. */
1045 bool needs_escape = delim == '\n' && problematic_chars (file);
1046
1047 if (needs_escape)
1048 putchar ('\\');
1049
1050 if (tagged)
1051 {
1052 fputs (DIGEST_TYPE_STRING, stdout);
1053 # if HASH_ALGO_BLAKE2
1054 if (digest_length < BLAKE2B_MAX_LEN * 8)
1055 printf ("-%ju", digest_length);
1056 # elif HASH_ALGO_CKSUM
1057 if (cksum_algorithm == blake2b)
1058 {
1059 if (digest_length < BLAKE2B_MAX_LEN * 8)
1060 printf ("-%ju", digest_length);
1061 }
1062 # endif
1063 fputs (" (", stdout);
1064 print_filename (file, needs_escape);
1065 fputs (") = ", stdout);
1066 }
1067
1068 # if HASH_ALGO_CKSUM
1069 if (base64_digest)
1070 {
1071 char b64[BASE64_LENGTH (DIGEST_BIN_BYTES) + 1];
1072 base64_encode ((char const *) bin_buffer, digest_length / 8,
1073 b64, sizeof b64);
1074 fputs (b64, stdout);
1075 }
1076 else
1077 # endif
1078 {
1079 for (size_t i = 0; i < (digest_hex_bytes / 2); ++i)
1080 printf ("%02x", bin_buffer[i]);
1081 }
1082
1083 if (!tagged)
1084 {
1085 putchar (' ');
1086 putchar (binary_file ? '*' : ' ');
1087 print_filename (file, needs_escape);
1088 }
1089
1090 putchar (delim);
1091 }
1092 #endif
1093
1094 #if HASH_ALGO_CKSUM
1095 /* Return true if B64_DIGEST is the same as the base64 digest of the
1096 DIGEST_LENGTH/8 bytes at BIN_BUFFER. */
1097 static bool
b64_equal(unsigned char const * b64_digest,unsigned char const * bin_buffer)1098 b64_equal (unsigned char const *b64_digest, unsigned char const *bin_buffer)
1099 {
1100 size_t b64_n_bytes = BASE64_LENGTH (digest_length / 8);
1101 char b64[BASE64_LENGTH (DIGEST_BIN_BYTES) + 1];
1102 base64_encode ((char const *) bin_buffer, digest_length / 8, b64, sizeof b64);
1103 return memcmp (b64_digest, b64, b64_n_bytes + 1) == 0;
1104 }
1105 #endif
1106
1107 /* Return true if HEX_DIGEST is the same as the hex-encoded digest of the
1108 DIGEST_LENGTH/8 bytes at BIN_BUFFER. */
1109 static bool
hex_equal(unsigned char const * hex_digest,unsigned char const * bin_buffer)1110 hex_equal (unsigned char const *hex_digest, unsigned char const *bin_buffer)
1111 {
1112 static const char bin2hex[] = { '0', '1', '2', '3',
1113 '4', '5', '6', '7',
1114 '8', '9', 'a', 'b',
1115 'c', 'd', 'e', 'f' };
1116 size_t digest_bin_bytes = digest_hex_bytes / 2;
1117
1118 /* Compare generated binary number with text representation
1119 in check file. Ignore case of hex digits. */
1120 size_t cnt;
1121 for (cnt = 0; cnt < digest_bin_bytes; ++cnt)
1122 {
1123 if (tolower (hex_digest[2 * cnt])
1124 != bin2hex[bin_buffer[cnt] >> 4]
1125 || (tolower (hex_digest[2 * cnt + 1])
1126 != (bin2hex[bin_buffer[cnt] & 0xf])))
1127 break;
1128 }
1129 return cnt == digest_bin_bytes;
1130 }
1131
1132 static bool
digest_check(char const * checkfile_name)1133 digest_check (char const *checkfile_name)
1134 {
1135 FILE *checkfile_stream;
1136 uintmax_t n_misformatted_lines = 0;
1137 uintmax_t n_mismatched_checksums = 0;
1138 uintmax_t n_open_or_read_failures = 0;
1139 bool properly_formatted_lines = false;
1140 bool matched_checksums = false;
1141 unsigned char bin_buffer_unaligned[DIGEST_BIN_BYTES + DIGEST_ALIGN];
1142 /* Make sure bin_buffer is properly aligned. */
1143 unsigned char *bin_buffer = ptr_align (bin_buffer_unaligned, DIGEST_ALIGN);
1144 uintmax_t line_number;
1145 char *line;
1146 size_t line_chars_allocated;
1147 bool is_stdin = STREQ (checkfile_name, "-");
1148
1149 if (is_stdin)
1150 {
1151 have_read_stdin = true;
1152 checkfile_name = _("standard input");
1153 checkfile_stream = stdin;
1154 }
1155 else
1156 {
1157 checkfile_stream = fopen (checkfile_name, "r");
1158 if (checkfile_stream == nullptr)
1159 {
1160 error (0, errno, "%s", quotef (checkfile_name));
1161 return false;
1162 }
1163 }
1164
1165 line_number = 0;
1166 line = nullptr;
1167 line_chars_allocated = 0;
1168 do
1169 {
1170 char *filename;
1171 int binary;
1172 unsigned char *digest;
1173 ssize_t line_length;
1174
1175 ++line_number;
1176 if (line_number == 0)
1177 error (EXIT_FAILURE, 0, _("%s: too many checksum lines"),
1178 quotef (checkfile_name));
1179
1180 line_length = getline (&line, &line_chars_allocated, checkfile_stream);
1181 if (line_length <= 0)
1182 break;
1183
1184 /* Ignore comment lines, which begin with a '#' character. */
1185 if (line[0] == '#')
1186 continue;
1187
1188 /* Remove any trailing newline. */
1189 line_length -= line[line_length - 1] == '\n';
1190 /* Remove any trailing carriage return. */
1191 line_length -= line[line_length - (0 < line_length)] == '\r';
1192
1193 /* Ignore empty lines. */
1194 if (line_length == 0)
1195 continue;
1196
1197 line[line_length] = '\0';
1198
1199 size_t d_len;
1200 if (! (split_3 (line, line_length, &digest, &d_len, &binary, &filename)
1201 && ! (is_stdin && STREQ (filename, "-"))))
1202 {
1203 ++n_misformatted_lines;
1204
1205 if (warn)
1206 {
1207 error (0, 0,
1208 _("%s: %ju"
1209 ": improperly formatted %s checksum line"),
1210 quotef (checkfile_name), line_number,
1211 DIGEST_TYPE_STRING);
1212 }
1213 }
1214 else
1215 {
1216 bool ok;
1217 bool missing;
1218 bool needs_escape = ! status_only && problematic_chars (filename);
1219
1220 properly_formatted_lines = true;
1221
1222 uintmax_t length;
1223 ok = digest_file (filename, &binary, bin_buffer, &missing, &length);
1224
1225 if (!ok)
1226 {
1227 ++n_open_or_read_failures;
1228 if (!status_only)
1229 {
1230 if (needs_escape)
1231 putchar ('\\');
1232 print_filename (filename, needs_escape);
1233 printf (": %s\n", _("FAILED open or read"));
1234 }
1235 }
1236 else if (ignore_missing && missing)
1237 {
1238 /* Ignore missing files with --ignore-missing. */
1239 ;
1240 }
1241 else
1242 {
1243 bool match = false;
1244 #if HASH_ALGO_CKSUM
1245 if (d_len < digest_hex_bytes)
1246 match = b64_equal (digest, bin_buffer);
1247 else
1248 #endif
1249 if (d_len == digest_hex_bytes)
1250 match = hex_equal (digest, bin_buffer);
1251
1252 if (match)
1253 matched_checksums = true;
1254 else
1255 ++n_mismatched_checksums;
1256
1257 if (!status_only)
1258 {
1259 if (! match || ! quiet)
1260 {
1261 if (needs_escape)
1262 putchar ('\\');
1263 print_filename (filename, needs_escape);
1264 }
1265
1266 if (! match)
1267 printf (": %s\n", _("FAILED"));
1268 else if (!quiet)
1269 printf (": %s\n", _("OK"));
1270 }
1271 }
1272 }
1273 }
1274 while (!feof (checkfile_stream) && !ferror (checkfile_stream));
1275
1276 free (line);
1277
1278 int err = ferror (checkfile_stream) ? 0 : -1;
1279 if (is_stdin)
1280 clearerr (checkfile_stream);
1281 else if (fclose (checkfile_stream) != 0 && err < 0)
1282 err = errno;
1283
1284 if (0 <= err)
1285 {
1286 error (0, err, err ? "%s" : _("%s: read error"),
1287 quotef (checkfile_name));
1288 return false;
1289 }
1290
1291 if (! properly_formatted_lines)
1292 {
1293 /* Warn if no tests are found. */
1294 error (0, 0, _("%s: no properly formatted checksum lines found"),
1295 quotef (checkfile_name));
1296 }
1297 else
1298 {
1299 if (!status_only)
1300 {
1301 if (n_misformatted_lines != 0)
1302 error (0, 0,
1303 (ngettext
1304 ("WARNING: %ju line is improperly formatted",
1305 "WARNING: %ju lines are improperly formatted",
1306 select_plural (n_misformatted_lines))),
1307 n_misformatted_lines);
1308
1309 if (n_open_or_read_failures != 0)
1310 error (0, 0,
1311 (ngettext
1312 ("WARNING: %ju listed file could not be read",
1313 "WARNING: %ju listed files could not be read",
1314 select_plural (n_open_or_read_failures))),
1315 n_open_or_read_failures);
1316
1317 if (n_mismatched_checksums != 0)
1318 error (0, 0,
1319 (ngettext
1320 ("WARNING: %ju computed checksum did NOT match",
1321 "WARNING: %ju computed checksums did NOT match",
1322 select_plural (n_mismatched_checksums))),
1323 n_mismatched_checksums);
1324
1325 if (ignore_missing && ! matched_checksums)
1326 error (0, 0, _("%s: no file was verified"),
1327 quotef (checkfile_name));
1328 }
1329 }
1330
1331 return (properly_formatted_lines
1332 && matched_checksums
1333 && n_mismatched_checksums == 0
1334 && n_open_or_read_failures == 0
1335 && (!strict || n_misformatted_lines == 0));
1336 }
1337
1338 int
main(int argc,char ** argv)1339 main (int argc, char **argv)
1340 {
1341 unsigned char bin_buffer_unaligned[DIGEST_BIN_BYTES + DIGEST_ALIGN];
1342 /* Make sure bin_buffer is properly aligned. */
1343 unsigned char *bin_buffer = ptr_align (bin_buffer_unaligned, DIGEST_ALIGN);
1344 bool do_check = false;
1345 int opt;
1346 bool ok = true;
1347 int binary = -1;
1348 #if HASH_ALGO_CKSUM
1349 bool prefix_tag = true;
1350 #else
1351 bool prefix_tag = false;
1352 #endif
1353
1354 /* Setting values of global variables. */
1355 initialize_main (&argc, &argv);
1356 set_program_name (argv[0]);
1357 setlocale (LC_ALL, "");
1358 bindtextdomain (PACKAGE, LOCALEDIR);
1359 textdomain (PACKAGE);
1360
1361 atexit (close_stdout);
1362
1363 /* Line buffer stdout to ensure lines are written atomically and immediately
1364 so that processes running in parallel do not intersperse their output. */
1365 setvbuf (stdout, nullptr, _IOLBF, 0);
1366
1367 #if HASH_ALGO_SUM
1368 char const *short_opts = "rs";
1369 #elif HASH_ALGO_CKSUM
1370 char const *short_opts = "a:l:bctwz";
1371 char const *digest_length_str = "";
1372 #elif HASH_ALGO_BLAKE2
1373 char const *short_opts = "l:bctwz";
1374 char const *digest_length_str = "";
1375 #else
1376 char const *short_opts = "bctwz";
1377 #endif
1378
1379 while ((opt = getopt_long (argc, argv, short_opts, long_options, nullptr))
1380 != -1)
1381 switch (opt)
1382 {
1383 #if HASH_ALGO_CKSUM
1384 case 'a':
1385 cksum_algorithm = XARGMATCH_EXACT ("--algorithm", optarg,
1386 algorithm_args, algorithm_types);
1387 algorithm_specified = true;
1388 break;
1389
1390 case DEBUG_PROGRAM_OPTION:
1391 cksum_debug = true;
1392 break;
1393 #endif
1394 #if HASH_ALGO_BLAKE2 || HASH_ALGO_CKSUM
1395 case 'l':
1396 digest_length = xdectoumax (optarg, 0, UINTMAX_MAX, "",
1397 _("invalid length"), 0);
1398 digest_length_str = optarg;
1399 if (digest_length % 8 != 0)
1400 {
1401 error (0, 0, _("invalid length: %s"), quote (digest_length_str));
1402 error (EXIT_FAILURE, 0, _("length is not a multiple of 8"));
1403 }
1404 break;
1405 #endif
1406 #if !HASH_ALGO_SUM
1407 case 'c':
1408 do_check = true;
1409 break;
1410 case STATUS_OPTION:
1411 status_only = true;
1412 warn = false;
1413 quiet = false;
1414 break;
1415 case 'b':
1416 binary = 1;
1417 break;
1418 case 't':
1419 binary = 0;
1420 break;
1421 case 'w':
1422 status_only = false;
1423 warn = true;
1424 quiet = false;
1425 break;
1426 case IGNORE_MISSING_OPTION:
1427 ignore_missing = true;
1428 break;
1429 case QUIET_OPTION:
1430 status_only = false;
1431 warn = false;
1432 quiet = true;
1433 break;
1434 case STRICT_OPTION:
1435 strict = true;
1436 break;
1437 # if HASH_ALGO_CKSUM
1438 case BASE64_OPTION:
1439 base64_digest = true;
1440 break;
1441 case RAW_OPTION:
1442 raw_digest = true;
1443 break;
1444 case UNTAG_OPTION:
1445 prefix_tag = false;
1446 break;
1447 # endif
1448 case TAG_OPTION:
1449 prefix_tag = true;
1450 binary = 1;
1451 break;
1452 case 'z':
1453 digest_delim = '\0';
1454 break;
1455 #endif
1456 #if HASH_ALGO_SUM
1457 case 'r': /* For SysV compatibility. */
1458 sum_algorithm = bsd;
1459 break;
1460
1461 case 's':
1462 sum_algorithm = sysv;
1463 break;
1464 #endif
1465 case_GETOPT_HELP_CHAR;
1466 case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
1467 default:
1468 usage (EXIT_FAILURE);
1469 }
1470
1471 min_digest_line_length = MIN_DIGEST_LINE_LENGTH;
1472 #if HASH_ALGO_BLAKE2 || HASH_ALGO_CKSUM
1473 # if HASH_ALGO_CKSUM
1474 if (digest_length && cksum_algorithm != blake2b)
1475 error (EXIT_FAILURE, 0,
1476 _("--length is only supported with --algorithm=blake2b"));
1477 # endif
1478 if (digest_length > BLAKE2B_MAX_LEN * 8)
1479 {
1480 error (0, 0, _("invalid length: %s"), quote (digest_length_str));
1481 error (EXIT_FAILURE, 0,
1482 _("maximum digest length for %s is %d bits"),
1483 quote (DIGEST_TYPE_STRING),
1484 BLAKE2B_MAX_LEN * 8);
1485 }
1486 if (digest_length == 0)
1487 {
1488 # if HASH_ALGO_BLAKE2
1489 digest_length = BLAKE2B_MAX_LEN * 8;
1490 # else
1491 digest_length = algorithm_bits[cksum_algorithm];
1492 # endif
1493 }
1494 digest_hex_bytes = digest_length / 4;
1495 #else
1496 digest_hex_bytes = DIGEST_HEX_BYTES;
1497 #endif
1498
1499 #if HASH_ALGO_CKSUM
1500 switch (cksum_algorithm)
1501 {
1502 case bsd:
1503 case sysv:
1504 case crc:
1505 if (do_check && algorithm_specified)
1506 error (EXIT_FAILURE, 0,
1507 _("--check is not supported with --algorithm={bsd,sysv,crc}"));
1508 break;
1509 default:
1510 break;
1511 }
1512
1513 if (base64_digest && raw_digest)
1514 {
1515 error (0, 0, _("--base64 and --raw are mutually exclusive"));
1516 usage (EXIT_FAILURE);
1517 }
1518 #endif
1519
1520 if (prefix_tag && !binary)
1521 {
1522 /* This could be supported in a backwards compatible way
1523 by prefixing the output line with a space in text mode.
1524 However that's invasive enough that it was agreed to
1525 not support this mode with --tag, as --text use cases
1526 are adequately supported by the default output format. */
1527 #if !HASH_ALGO_CKSUM
1528 error (0, 0, _("--tag does not support --text mode"));
1529 #else
1530 error (0, 0, _("--text mode is only supported with --untagged"));
1531 #endif
1532 usage (EXIT_FAILURE);
1533 }
1534
1535 if (digest_delim != '\n' && do_check)
1536 {
1537 error (0, 0, _("the --zero option is not supported when "
1538 "verifying checksums"));
1539 usage (EXIT_FAILURE);
1540 }
1541 #if !HASH_ALGO_CKSUM
1542 if (prefix_tag && do_check)
1543 {
1544 error (0, 0, _("the --tag option is meaningless when "
1545 "verifying checksums"));
1546 usage (EXIT_FAILURE);
1547 }
1548 #endif
1549
1550 if (0 <= binary && do_check)
1551 {
1552 error (0, 0, _("the --binary and --text options are meaningless when "
1553 "verifying checksums"));
1554 usage (EXIT_FAILURE);
1555 }
1556
1557 if (ignore_missing && !do_check)
1558 {
1559 error (0, 0,
1560 _("the --ignore-missing option is meaningful only when "
1561 "verifying checksums"));
1562 usage (EXIT_FAILURE);
1563 }
1564
1565 if (status_only && !do_check)
1566 {
1567 error (0, 0,
1568 _("the --status option is meaningful only when verifying checksums"));
1569 usage (EXIT_FAILURE);
1570 }
1571
1572 if (warn && !do_check)
1573 {
1574 error (0, 0,
1575 _("the --warn option is meaningful only when verifying checksums"));
1576 usage (EXIT_FAILURE);
1577 }
1578
1579 if (quiet && !do_check)
1580 {
1581 error (0, 0,
1582 _("the --quiet option is meaningful only when verifying checksums"));
1583 usage (EXIT_FAILURE);
1584 }
1585
1586 if (strict & !do_check)
1587 {
1588 error (0, 0,
1589 _("the --strict option is meaningful only when verifying checksums"));
1590 usage (EXIT_FAILURE);
1591 }
1592
1593 if (!O_BINARY && binary < 0)
1594 binary = 0;
1595
1596 char **operand_lim = argv + argc;
1597 if (optind == argc)
1598 *operand_lim++ = bad_cast ("-");
1599 else if (1 < argc - optind && raw_digest)
1600 error (EXIT_FAILURE, 0,
1601 _("the --raw option is not supported with multiple files"));
1602
1603 for (char **operandp = argv + optind; operandp < operand_lim; operandp++)
1604 {
1605 char *file = *operandp;
1606 if (do_check)
1607 ok &= digest_check (file);
1608 else
1609 {
1610 int binary_file = binary;
1611 bool missing;
1612 uintmax_t length;
1613
1614 if (! digest_file (file, &binary_file, bin_buffer, &missing, &length))
1615 ok = false;
1616 else
1617 {
1618 DIGEST_OUT (file, binary_file, bin_buffer, raw_digest, prefix_tag,
1619 digest_delim, optind != argc, length);
1620 }
1621 }
1622 }
1623
1624 if (have_read_stdin && fclose (stdin) == EOF)
1625 error (EXIT_FAILURE, errno, _("standard input"));
1626
1627 return ok ? EXIT_SUCCESS : EXIT_FAILURE;
1628 }
1629