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