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