]> git.ipfire.org Git - thirdparty/coreutils.git/commitdiff
maint: rearrange cksum modules so main is in cksum.c
authorPádraig Brady <P@draigBrady.com>
Sat, 15 Nov 2025 13:21:10 +0000 (13:21 +0000)
committerPádraig Brady <P@draigBrady.com>
Sun, 16 Nov 2025 17:18:03 +0000 (17:18 +0000)
* src/cksum.c: -> src/cksum_crc.c
* src/cksum.h: -> src/cksum_crc.h
* src/digest.c: -> src/cksum.c
* src/local.mk: Adjust accordingly.
* cfg.mk: Likewise.
* po/POTFILES.in: Likewise.
* src/cksum_avx2.c: Likewise.
* src/cksum_avx512.c: Likewise.
* src/cksum_pclmul.c: Likewise.
* src/cksum_vmull.c: Likewise.

cfg.mk
po/POTFILES.in
src/cksum.c
src/cksum_avx2.c
src/cksum_avx512.c
src/cksum_crc.c [new file with mode: 0644]
src/cksum_crc.h [moved from src/cksum.h with 100% similarity]
src/cksum_pclmul.c
src/cksum_vmull.c
src/digest.c [deleted file]
src/local.mk

diff --git a/cfg.mk b/cfg.mk
index e5df89659ed815cacbeff0a2194a39ffc28a1e3c..336275fc89c6df86452bd48af23f534ecca078e6 100644 (file)
--- a/cfg.mk
+++ b/cfg.mk
@@ -895,7 +895,7 @@ update-copyright-env = \
 exclude_file_name_regexp--sc_space_tab = \
   ^(tests/pr/|tests/misc/nl\.sh$$|gl/.*\.diff$$|man/help2man$$)
 exclude_file_name_regexp--sc_bindtextdomain = \
-  ^(gl/.*|lib/euidaccess-stat|src/make-prime-list|src/cksum)\.c$$
+  ^(gl/.*|lib/euidaccess-stat|src/make-prime-list|src/cksum_crc)\.c$$
 exclude_file_name_regexp--sc_trailing_blank = \
   ^(tests/pr/|gl/.*\.diff$$|man/help2man)
 _x_system_h := (system|copy|chown-core|find-mount-point)\.h
index 444202840b6e9b15ba12ddefe7e98f1a82627863..015f888fb850977c8b6a0d5e0be087ae2e7fa213 100644 (file)
@@ -43,6 +43,7 @@ src/chown-core.c
 src/chown.c
 src/chroot.c
 src/cksum.c
+src/cksum_crc.c
 src/comm.c
 src/copy.c
 src/copy-file-data.c
@@ -81,7 +82,6 @@ src/link.c
 src/ln.c
 src/logname.c
 src/ls.c
-src/digest.c
 src/mkdir.c
 src/mkfifo.c
 src/mknod.c
index a74af07a084fca9032b5c794a886973a6f6dd3d1..35c89afee1a16276d9fc50e43e94a3e54bce5763 100644 (file)
@@ -1,5 +1,5 @@
-/* cksum -- calculate and print POSIX checksums and sizes of files
-   Copyright (C) 1992-2025 Free Software Foundation, Inc.
+/* Compute checksums of files or strings.
+   Copyright (C) 1995-2025 Free Software Foundation, Inc.
 
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <https://www.gnu.org/licenses/>.  */
 
-/* Written by Q. Frank Xia, qx@math.columbia.edu.
-   Cosmetic changes and reorganization by David MacKenzie, djm@gnu.ai.mit.edu.
+/* Written by Ulrich Drepper <drepper@gnu.ai.mit.edu>.  */
 
-  Usage: cksum [file...]
+#include <config.h>
 
-  The code segment between "#ifdef CRCTAB" and "#else" is the code
-  which generates crctab.c
+#include <getopt.h>
+#include <sys/types.h>
 
-  This software is compatible with neither the System V nor the BSD
-  'sum' program.  It is supposed to conform to POSIX, except perhaps
-  for foreign language support.  Any inconsistency with the standard
-  (other than foreign language support) is a bug.  */
+#include "assure.h"
+#include "system.h"
+#include "argmatch.h"
+#include "c-ctype.h"
+#include "quote.h"
+#include "xdectoint.h"
+#include "xstrtol.h"
 
-/* This include must be at the top of the file to satisfy
-   sc_require_config_h_first.  */
-#ifndef CRCTAB
-# include <config.h>
+#ifndef HASH_ALGO_CKSUM
+# define HASH_ALGO_CKSUM 0
 #endif
 
-#ifdef CRCTAB
+#if HASH_ALGO_SUM || HASH_ALGO_CKSUM
+# include "sum.h"
+#endif
+#if HASH_ALGO_CKSUM
+# include "cksum_crc.h"
+# include "base64.h"
+#endif
+#if HASH_ALGO_BLAKE2 || HASH_ALGO_CKSUM
+# include "blake2/b2sum.h"
+#endif
+#if HASH_ALGO_MD5 || HASH_ALGO_CKSUM
+# include "md5.h"
+#endif
+#if HASH_ALGO_SHA1 || HASH_ALGO_CKSUM
+# include "sha1.h"
+#endif
+#if HASH_ALGO_SHA256 || HASH_ALGO_SHA224 || HASH_ALGO_CKSUM
+# include "sha256.h"
+#endif
+#if HASH_ALGO_SHA512 || HASH_ALGO_SHA384 || HASH_ALGO_CKSUM
+# include "sha512.h"
+#endif
+#if HASH_ALGO_CKSUM
+# include "sha3.h"
+#endif
+#if HASH_ALGO_CKSUM
+# include "sm3.h"
+#endif
+#include "fadvise.h"
+#include "stdio--.h"
+#include "xbinary-io.h"
+
+/* The official name of this program (e.g., no 'g' prefix).  */
+#if HASH_ALGO_SUM
+# define PROGRAM_NAME "sum"
+# define DIGEST_TYPE_STRING "BSD"
+# define DIGEST_STREAM sumfns[sum_algorithm]
+# define DIGEST_OUT sum_output_fns[sum_algorithm]
+# define DIGEST_BITS 16
+# define DIGEST_ALIGN 4
+#elif HASH_ALGO_CKSUM
+# define MAX_DIGEST_BITS 512
+# define MAX_DIGEST_ALIGN 8
+# define PROGRAM_NAME "cksum"
+# define DIGEST_TYPE_STRING algorithm_tags[cksum_algorithm]
+# define DIGEST_STREAM cksumfns[cksum_algorithm]
+# define DIGEST_OUT cksum_output_fns[cksum_algorithm]
+# define DIGEST_BITS MAX_DIGEST_BITS
+# define DIGEST_ALIGN MAX_DIGEST_ALIGN
+#elif HASH_ALGO_MD5
+# define PROGRAM_NAME "md5sum"
+# define DIGEST_TYPE_STRING "MD5"
+# define DIGEST_STREAM md5_stream
+# define DIGEST_BITS 128
+# define DIGEST_REFERENCE "RFC 1321"
+# define DIGEST_ALIGN 4
+#elif HASH_ALGO_BLAKE2
+# define PROGRAM_NAME "b2sum"
+# define DIGEST_TYPE_STRING "BLAKE2b"
+# define DIGEST_STREAM blake2b_stream
+# define DIGEST_BITS 512
+# define DIGEST_REFERENCE "RFC 7693"
+# define DIGEST_ALIGN 8
+#elif HASH_ALGO_SHA1
+# define PROGRAM_NAME "sha1sum"
+# define DIGEST_TYPE_STRING "SHA1"
+# define DIGEST_STREAM sha1_stream
+# define DIGEST_BITS 160
+# define DIGEST_REFERENCE "FIPS-180-1"
+# define DIGEST_ALIGN 4
+#elif HASH_ALGO_SHA256
+# define PROGRAM_NAME "sha256sum"
+# define DIGEST_TYPE_STRING "SHA256"
+# define DIGEST_STREAM sha256_stream
+# define DIGEST_BITS 256
+# define DIGEST_REFERENCE "FIPS-180-2"
+# define DIGEST_ALIGN 4
+#elif HASH_ALGO_SHA224
+# define PROGRAM_NAME "sha224sum"
+# define DIGEST_TYPE_STRING "SHA224"
+# define DIGEST_STREAM sha224_stream
+# define DIGEST_BITS 224
+# define DIGEST_REFERENCE "RFC 3874"
+# define DIGEST_ALIGN 4
+#elif HASH_ALGO_SHA512
+# define PROGRAM_NAME "sha512sum"
+# define DIGEST_TYPE_STRING "SHA512"
+# define DIGEST_STREAM sha512_stream
+# define DIGEST_BITS 512
+# define DIGEST_REFERENCE "FIPS-180-2"
+# define DIGEST_ALIGN 8
+#elif HASH_ALGO_SHA384
+# define PROGRAM_NAME "sha384sum"
+# define DIGEST_TYPE_STRING "SHA384"
+# define DIGEST_STREAM sha384_stream
+# define DIGEST_BITS 384
+# define DIGEST_REFERENCE "FIPS-180-2"
+# define DIGEST_ALIGN 8
+#else
+# error "Can't decide which hash algorithm to compile."
+#endif
+#if !HASH_ALGO_SUM && !HASH_ALGO_CKSUM
+# define DIGEST_OUT output_file
+#endif
+
+#if HASH_ALGO_SUM
+# define AUTHORS \
+  proper_name ("Kayvan Aghaiepour"), \
+  proper_name ("David MacKenzie")
+#elif HASH_ALGO_CKSUM
+# define AUTHORS \
+  proper_name_lite ("Padraig Brady", "P\303\241draig Brady"), \
+  proper_name ("Q. Frank Xia")
+#elif HASH_ALGO_BLAKE2
+# define AUTHORS \
+  proper_name_lite ("Padraig Brady", "P\303\241draig Brady"), \
+  proper_name ("Samuel Neves")
+#else
+# define AUTHORS \
+  proper_name ("Ulrich Drepper"), \
+  proper_name ("Scott Miller"), \
+  proper_name ("David Madore")
+#endif
+#if !HASH_ALGO_BLAKE2 && !HASH_ALGO_CKSUM
+# define DIGEST_HEX_BYTES (DIGEST_BITS / 4)
+#endif
+#define DIGEST_BIN_BYTES (DIGEST_BITS / 8)
+
+/* The minimum length of a valid digest line.  This length does
+   not include any newline character at the end of a line.  */
+#if HASH_ALGO_BLAKE2 || HASH_ALGO_CKSUM
+# define MIN_DIGEST_LINE_LENGTH 3 /* With -l 8.  */
+#else
+# define MIN_DIGEST_LINE_LENGTH \
+   (DIGEST_HEX_BYTES /* length of hexadecimal message digest */ \
+    + 1 /* blank */ \
+    + 1 /* minimum filename length */ )
+#endif
 
-# include <stdio.h>
+#if !HASH_ALGO_SUM
+static void
+output_file (char const *file, int binary_file, void const *digest,
+             bool raw, bool tagged, unsigned char delim, bool args,
+             uintmax_t length);
+#endif
 
-# define BIT(x)        (1u << (x))
-# define SBIT  BIT (31)
+/* True if any of the files read were the standard input. */
+static bool have_read_stdin;
 
-/* The generating polynomial is
+/* The minimum length of a valid checksum line for the selected algorithm.  */
+static size_t min_digest_line_length;
 
-          32   26   23   22   16   12   11   10   8   7   5   4   2   1
-    G(X)=X  + X  + X  + X  + X  + X  + X  + X  + X + X + X + X + X + X + 1
+/* Set to the length of a digest hex string for the selected algorithm.  */
+static size_t digest_hex_bytes;
 
-  The i bit in GEN is set if X^i is a summand of G(X) except X^32.  */
+/* With --check, don't generate any output.
+   The exit code indicates success or failure.  */
+static bool status_only = false;
 
-# define GEN   (BIT (26) | BIT (23) | BIT (22) | BIT (16) | BIT (12) \
-                 | BIT (11) | BIT (10) | BIT (8) | BIT (7) | BIT (5) \
-                 | BIT (4) | BIT (2) | BIT (1) | BIT (0))
+/* With --check, print a message to standard error warning about each
+   improperly formatted checksum line.  */
+static bool warn = false;
 
-static unsigned int r[8];
+/* With --check, ignore missing files.  */
+static bool ignore_missing = false;
 
-static void
-fill_r (void)
+/* With --check, suppress the "OK" printed for each verified file.  */
+static bool quiet = false;
+
+/* With --check, exit with a non-zero return code if any line is
+   improperly formatted. */
+static bool strict = false;
+
+/* Whether a BSD reversed format checksum is detected.  */
+static int bsd_reversed = -1;
+
+/* line delimiter.  */
+static unsigned char digest_delim = '\n';
+
+#if HASH_ALGO_CKSUM
+/* If true, print base64-encoded digests, not hex.  */
+static bool base64_digest = false;
+#endif
+
+/* If true, print binary digests, not hex.  */
+static bool raw_digest = false;
+
+/* blake2b and sha3 allow the -l option.  Luckily they both have the same
+   maximum digest size.  */
+#if HASH_ALGO_BLAKE2 || HASH_ALGO_CKSUM
+# if HASH_ALGO_CKSUM
+static_assert (BLAKE2B_OUTBYTES == SHA3_512_DIGEST_SIZE);
+# endif
+# define DIGEST_MAX_LEN BLAKE2B_OUTBYTES
+static uintmax_t digest_length;
+#endif /* HASH_ALGO_BLAKE2 || HASH_ALGO_CKSUM */
+
+typedef void (*digest_output_fn)(char const *, int, void const *, bool,
+                                 bool, unsigned char, bool, uintmax_t);
+#if HASH_ALGO_SUM
+enum Algorithm
+{
+  bsd,
+  sysv,
+};
+
+static enum Algorithm sum_algorithm;
+static sumfn sumfns[]=
+{
+  bsd_sum_stream,
+  sysv_sum_stream,
+};
+static digest_output_fn sum_output_fns[]=
+{
+  output_bsd,
+  output_sysv,
+};
+#endif
+
+#if HASH_ALGO_CKSUM
+static int
+md5_sum_stream (FILE *stream, void *resstream, MAYBE_UNUSED uintmax_t *length)
+{
+  return md5_stream (stream, resstream);
+}
+static int
+sha1_sum_stream (FILE *stream, void *resstream, MAYBE_UNUSED uintmax_t *length)
+{
+  return sha1_stream (stream, resstream);
+}
+static int
+sha224_sum_stream (FILE *stream, void *resstream,
+                   MAYBE_UNUSED uintmax_t *length)
 {
-  r[0] = GEN;
-  for (int i = 1; i < 8; i++)
-    r[i] = (r[i - 1] << 1) ^ ((r[i - 1] & SBIT) ? GEN : 0);
+  return sha224_stream (stream, resstream);
+}
+static int
+sha256_sum_stream (FILE *stream, void *resstream,
+                   MAYBE_UNUSED uintmax_t *length)
+{
+  return sha256_stream (stream, resstream);
+}
+static int
+sha384_sum_stream (FILE *stream, void *resstream,
+                   MAYBE_UNUSED uintmax_t *length)
+{
+  return sha384_stream (stream, resstream);
+}
+static int
+sha512_sum_stream (FILE *stream, void *resstream,
+                   MAYBE_UNUSED uintmax_t *length)
+{
+  return sha512_stream (stream, resstream);
+}
+static int
+sha2_sum_stream (FILE *stream, void *resstream, uintmax_t *length)
+{
+  switch (*length)
+    {
+    case SHA224_DIGEST_SIZE:
+      return sha224_stream (stream, resstream);
+    case SHA256_DIGEST_SIZE:
+      return sha256_stream (stream, resstream);
+    case SHA384_DIGEST_SIZE:
+      return sha384_stream (stream, resstream);
+    case SHA512_DIGEST_SIZE:
+      return sha512_stream (stream, resstream);
+    default:
+      affirm (false);
+    }
+}
+static int
+sha3_sum_stream (FILE *stream, void *resstream, uintmax_t *length)
+{
+  switch (*length)
+    {
+    case SHA3_224_DIGEST_SIZE:
+      return sha3_224_stream (stream, resstream);
+    case SHA3_256_DIGEST_SIZE:
+      return sha3_256_stream (stream, resstream);
+    case SHA3_384_DIGEST_SIZE:
+      return sha3_384_stream (stream, resstream);
+    case SHA3_512_DIGEST_SIZE:
+      return sha3_512_stream (stream, resstream);
+    default:
+      affirm (false);
+    }
+}
+static int
+blake2b_sum_stream (FILE *stream, void *resstream, uintmax_t *length)
+{
+  return blake2b_stream (stream, resstream, *length);
+}
+static int
+sm3_sum_stream (FILE *stream, void *resstream, MAYBE_UNUSED uintmax_t *length)
+{
+  return sm3_stream (stream, resstream);
 }
 
-static unsigned int
-crc_remainder (int m)
+enum Algorithm
+{
+  bsd,
+  sysv,
+  crc,
+  crc32b,
+  md5,
+  sha1,
+  sha224,
+  sha256,
+  sha384,
+  sha512,
+  sha2,
+  sha3,
+  blake2b,
+  sm3,
+};
+
+static char const *const algorithm_args[] =
+{
+  "bsd", "sysv", "crc", "crc32b", "md5", "sha1",
+  "sha224", "sha256", "sha384", "sha512", /* Legacy naming */
+  "sha2", "sha3", "blake2b", "sm3", nullptr
+};
+static enum Algorithm const algorithm_types[] =
 {
-  unsigned int rem = 0;
+  bsd, sysv, crc, crc32b, md5, sha1,
+  sha224, sha256, sha384, sha512,
+  sha2, sha3, blake2b, sm3,
+};
+ARGMATCH_VERIFY (algorithm_args, algorithm_types);
 
-  for (int i = 0; i < 8; i++)
-    if (BIT (i) & m)
-      rem ^= r[i];
+static char const *const algorithm_tags[] =
+{
+  "BSD", "SYSV", "CRC", "CRC32B", "MD5", "SHA1",
+  "SHA224", "SHA256", "SHA384", "SHA512",
+  "SHA2", "SHA3", "BLAKE2b", "SM3", nullptr
+};
+static int const algorithm_bits[] =
+{
+  16, 16, 32, 32, 128, 160,
+  224, 256, 384, 512,
+  512, 512, 512, 256, 0
+};
 
-  return rem & 0xFFFFFFFF;     /* Make it run on 64-bit machine.  */
-}
+static_assert (countof (algorithm_bits) == countof (algorithm_args));
 
-int
-main (void)
+static bool algorithm_specified = false;
+static enum Algorithm cksum_algorithm = crc;
+static sumfn cksumfns[]=
+{
+  bsd_sum_stream,
+  sysv_sum_stream,
+  crc_sum_stream,
+  crc32b_sum_stream,
+  md5_sum_stream,
+  sha1_sum_stream,
+  sha224_sum_stream,
+  sha256_sum_stream,
+  sha384_sum_stream,
+  sha512_sum_stream,
+  sha2_sum_stream,
+  sha3_sum_stream,
+  blake2b_sum_stream,
+  sm3_sum_stream,
+};
+static digest_output_fn cksum_output_fns[]=
 {
-  static unsigned int crctab[8][256];
+  output_bsd,
+  output_sysv,
+  output_crc,
+  output_crc,
+  output_file,
+  output_file,
+  output_file,
+  output_file,
+  output_file,
+  output_file,
+  output_file,
+  output_file,
+  output_file,
+  output_file,
+};
+bool cksum_debug;
+#endif
 
-  fill_r ();
+/* For long options that have no equivalent short option, use a
+   non-character as a pseudo short option, starting with CHAR_MAX + 1.  */
+
+enum
+{
+  IGNORE_MISSING_OPTION = CHAR_MAX + 1,
+  STATUS_OPTION,
+  QUIET_OPTION,
+  STRICT_OPTION,
+  TAG_OPTION,
+  UNTAG_OPTION,
+  DEBUG_PROGRAM_OPTION,
+  RAW_OPTION,
+  BASE64_OPTION,
+};
+
+static struct option const long_options[] =
+{
+#if HASH_ALGO_BLAKE2 || HASH_ALGO_CKSUM
+  { "length", required_argument, nullptr, 'l'},
+#endif
+
+#if !HASH_ALGO_SUM
+  { "check", no_argument, nullptr, 'c' },
+  { "ignore-missing", no_argument, nullptr, IGNORE_MISSING_OPTION},
+  { "quiet", no_argument, nullptr, QUIET_OPTION },
+  { "status", no_argument, nullptr, STATUS_OPTION },
+  { "warn", no_argument, nullptr, 'w' },
+  { "strict", no_argument, nullptr, STRICT_OPTION },
+  { "tag", no_argument, nullptr, TAG_OPTION },
+  { "zero", no_argument, nullptr, 'z' },
+
+# if HASH_ALGO_CKSUM
+  { "algorithm", required_argument, nullptr, 'a'},
+  { "base64", no_argument, nullptr, BASE64_OPTION },
+  { "debug", no_argument, nullptr, DEBUG_PROGRAM_OPTION},
+  { "raw", no_argument, nullptr, RAW_OPTION},
+  { "untagged", no_argument, nullptr, UNTAG_OPTION },
+# endif
+  { "binary", no_argument, nullptr, 'b' },
+  { "text", no_argument, nullptr, 't' },
+
+#else
+  {"sysv", no_argument, nullptr, 's'},
+#endif
 
-  for (int i = 0; i < 256; i++)
-    crctab[0][i] = crc_remainder (i);
+  { GETOPT_HELP_OPTION_DECL },
+  { GETOPT_VERSION_OPTION_DECL },
+  { nullptr, 0, nullptr, 0 }
+};
 
-  /* CRC(0x11 0x22 0x33 0x44)
-     is equal to
-     CRC(0x11 0x00 0x00 0x00) XOR CRC(0x22 0x00 0x00) XOR
-     CRC(0x33 0x00) XOR CRC(0x44)
-     We precompute the CRC values for the offset values into
-     separate CRC tables. We can then use them to speed up
-     CRC calculation by processing multiple bytes at the time. */
-  for (int i = 0; i < 256; i++)
+void
+usage (int status)
+{
+  if (status != EXIT_SUCCESS)
+    emit_try_help ();
+  else
     {
-      unsigned int crc = 0;
+      printf (_("\
+Usage: %s [OPTION]... [FILE]...\n\
+"), program_name);
+#if HASH_ALGO_CKSUM
+      fputs (_("\
+Print or verify checksums.\n\
+By default use the 32 bit CRC algorithm.\n\
+"), stdout);
+#else
+      printf (_("\
+Print or check %s (%d-bit) checksums.\n\
+"),
+              DIGEST_TYPE_STRING,
+              DIGEST_BITS);
+#endif
+
+      emit_stdin_note ();
+#if HASH_ALGO_SUM
+      fputs (_("\
+\n\
+  -r              use BSD sum algorithm (the default), use 1K blocks\n\
+  -s, --sysv      use System V sum algorithm, use 512 bytes blocks\n\
+"), stdout);
+#endif
+#if HASH_ALGO_BLAKE2 || HASH_ALGO_CKSUM
+        emit_mandatory_arg_note ();
+#endif
+#if HASH_ALGO_CKSUM
+        fputs (_("\
+  -a, --algorithm=TYPE  select the digest type to use.  See DIGEST below\
+\n\
+"), stdout);
+        fputs (_("\
+      --base64          emit base64-encoded digests, not hexadecimal\
+\n\
+"), stdout);
+#endif
+#if !HASH_ALGO_SUM
+# if !HASH_ALGO_CKSUM
+      if (O_BINARY)
+        fputs (_("\
+  -b, --binary          read in binary mode (default unless reading tty stdin)\
+\n\
+"), stdout);
+      else
+        fputs (_("\
+  -b, --binary          read in binary mode\n\
+"), stdout);
+# endif
+        fputs (_("\
+  -c, --check           read checksums from the FILEs and check them\n\
+"), stdout);
+# if HASH_ALGO_BLAKE2 || HASH_ALGO_CKSUM
+        fputs (_("\
+  -l, --length=BITS     digest length in bits; must not exceed the max size\n\
+                          and must be a multiple of 8 for blake2b;\n\
+                          must be 224, 256, 384, or 512 for sha2 or sha3\n\
+"), stdout);
+# endif
+# if HASH_ALGO_CKSUM
+        fputs (_("\
+      --raw             emit a raw binary digest, not hexadecimal\
+\n\
+"), stdout);
+      fputs (_("\
+      --tag             create a BSD-style checksum (the default)\n\
+"), stdout);
+      fputs (_("\
+      --untagged        create a reversed style checksum, without digest type\n\
+"), stdout);
+# else
+      fputs (_("\
+      --tag             create a BSD-style checksum\n\
+"), stdout);
+# endif
+# if !HASH_ALGO_CKSUM
+      if (O_BINARY)
+        fputs (_("\
+  -t, --text            read in text mode (default if reading tty stdin)\n\
+"), stdout);
+      else
+        fputs (_("\
+  -t, --text            read in text mode (default)\n\
+"), stdout);
+# endif
+      fputs (_("\
+  -z, --zero            end each output line with NUL, not newline,\n\
+                          and disable file name escaping\n\
+"), stdout);
+      fputs (_("\
+\n\
+The following five options are useful only when verifying checksums:\n\
+      --ignore-missing  don't fail or report status for missing files\n\
+      --quiet           don't print OK for each successfully verified file\n\
+      --status          don't output anything, status code shows success\n\
+      --strict          exit non-zero for improperly formatted checksum lines\n\
+  -w, --warn            warn about improperly formatted checksum lines\n\
+\n\
+"), stdout);
+#endif
+#if HASH_ALGO_CKSUM
+      fputs (_("\
+      --debug           indicate which implementation used\n\
+"), stdout);
+#endif
+      fputs (HELP_OPTION_DESCRIPTION, stdout);
+      fputs (VERSION_OPTION_DESCRIPTION, stdout);
+#if HASH_ALGO_CKSUM
+      fputs (_("\
+\n\
+DIGEST determines the digest algorithm and default output format:\n\
+  sysv      (equivalent to sum -s)\n\
+  bsd       (equivalent to sum -r)\n\
+  crc       (equivalent to cksum)\n\
+  crc32b    (only available through cksum)\n\
+  md5       (equivalent to md5sum)\n\
+  sha1      (equivalent to sha1sum)\n\
+  sha2      (equivalent to sha{224,256,384,512}sum)\n\
+  sha3      (only available through cksum)\n\
+  blake2b   (equivalent to b2sum)\n\
+  sm3       (only available through cksum)\n\
+\n"), stdout);
+#endif
+#if !HASH_ALGO_SUM && !HASH_ALGO_CKSUM
+      printf (_("\
+\n\
+The sums are computed as described in %s.\n"), DIGEST_REFERENCE);
+      fputs (_("\
+When checking, the input should be a former output of this program.\n\
+The default mode is to print a line with: checksum, a space,\n\
+a character indicating input mode ('*' for binary, ' ' for text\n\
+or where binary is insignificant), and name for each FILE.\n\
+\n\
+There is no difference between binary mode and text mode on GNU systems.\
+\n"), stdout);
+#endif
+#if HASH_ALGO_CKSUM
+      fputs (_("\
+When checking, the input should be a former output of this program,\n\
+or equivalent standalone program.\
+\n"), stdout);
+#endif
+      emit_ancillary_info (PROGRAM_NAME);
+    }
+
+  exit (status);
+}
+
+/* Given a string S, return TRUE if it contains problematic characters
+   that need escaping.  Note we escape '\' itself to provide some forward
+   compat to introduce escaping of other characters.  */
+
+ATTRIBUTE_PURE
+static bool
+problematic_chars (char const *s)
+{
+  size_t length = strcspn (s, "\\\n\r");
+  return s[length] != '\0';
+}
+
+#define ISWHITE(c) ((c) == ' ' || (c) == '\t')
+
+/* Given a file name, S of length S_LEN, that is not NUL-terminated,
+   modify it in place, performing the equivalent of this sed substitution:
+   's/\\n/\n/g;s/\\r/\r/g;s/\\\\/\\/g' i.e., replacing each "\\n" string
+   with a newline, each "\\r" string with a carriage return,
+   and each "\\\\" with a single backslash, NUL-terminate it and return S.
+   If S is not a valid escaped file name, i.e., if it ends with an odd number
+   of backslashes or if it contains a backslash followed by anything other
+   than "n" or another backslash, return nullptr.  */
 
-      crc = (crc << 8) ^ crctab[0][((crc >> 24) ^ i) & 0xFF];
-      for (int offset = 1; offset < 8; offset++)
+static char *
+filename_unescape (char *s, size_t s_len)
+{
+  char *dst = s;
+
+  for (size_t i = 0; i < s_len; i++)
+    {
+      switch (s[i])
         {
-          crc = (crc << 8) ^ crctab[0][((crc >> 24) ^ 0) & 0xFF];
-          crctab[offset][i] = crc & 0xFFFFFFFF;
+        case '\\':
+          if (i == s_len - 1)
+            {
+              /* File name ends with an unescaped backslash: invalid.  */
+              return nullptr;
+            }
+          ++i;
+          switch (s[i])
+            {
+            case 'n':
+              *dst++ = '\n';
+              break;
+            case 'r':
+              *dst++ = '\r';
+              break;
+            case '\\':
+              *dst++ = '\\';
+              break;
+            default:
+              /* Only '\', 'n' or 'r' may follow a backslash.  */
+              return nullptr;
+            }
+          break;
+
+        case '\0':
+          /* The file name may not contain a NUL.  */
+          return nullptr;
+
+        default:
+          *dst++ = s[i];
+          break;
         }
     }
+  if (dst < s + s_len)
+    *dst = '\0';
 
-  printf ("#include <config.h>\n");
-  printf ("#include \"cksum.h\"\n");
-  printf ("\n");
-  printf ("uint_fast32_t const crctab[8][256] = {\n");
-  for (int y = 0; y < 8; y++)
+  return s;
+}
+
+/* Return true if S is a LEN-byte NUL-terminated string of hex or base64
+   digits and has the expected length.  Otherwise, return false.  */
+ATTRIBUTE_PURE
+static bool
+valid_digits (unsigned char const *s, size_t len)
+{
+#if HASH_ALGO_CKSUM
+  if (len == BASE64_LENGTH (digest_length / 8))
+    {
+      size_t i;
+      for (i = 0; i < len - digest_length % 3; i++)
+        {
+          if (!isbase64 (*s))
+            return false;
+          ++s;
+        }
+      for ( ; i < len; i++)
+        {
+          if (*s != '=')
+            return false;
+          ++s;
+        }
+    }
+  else
+#endif
+  if (len == digest_hex_bytes)
     {
-      printf ("{\n  0x%08x", crctab[y][0]);
-      for (int i = 0; i < 51; i++)
+      for (idx_t i = 0; i < digest_hex_bytes; i++)
         {
-          printf (",\n  0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x",
-                  crctab[y][i * 5 + 1], crctab[y][i * 5 + 2],
-                  crctab[y][i * 5 + 3], crctab[y][i * 5 + 4],
-                  crctab[y][i * 5 + 5]);
+          if (!c_isxdigit (*s))
+            return false;
+          ++s;
         }
-        printf ("\n},\n");
     }
-  printf ("};\n");
+  else
+    return false;
+
+  return *s == '\0';
 }
 
-#else /* !CRCTAB */
+/* Split the checksum string S (of length S_LEN) from a BSD 'md5' or
+   'sha1' command into two parts: a hexadecimal digest, and the file
+   name.  S is modified.  Set *D_LEN to the length of the digest string.
+   Return true if successful.  */
 
-# include "cksum.h"
-# include <sys/types.h>
-# include <endian.h>
-# include "system.h"
+static bool
+bsd_split_3 (char *s, size_t s_len,
+             unsigned char **digest, size_t *d_len,
+             char **file_name, bool escaped_filename)
+{
+  if (s_len == 0)
+    return false;
 
-# ifdef USE_VMULL_CRC32
-#  include <sys/auxv.h>
-#  include <asm/hwcap.h>
-# endif
+  /* Find end of filename.  */
+  size_t i = s_len - 1;
+  while (i && s[i] != ')')
+    i--;
 
-# include "crc.h"
-# include "cpu-supports.h"
-
-/* Number of bytes to read at once.  */
-# define BUFLEN (1 << 16)
-
-typedef bool (*cksum_fp_t) (FILE *, uint_fast32_t *, uintmax_t *);
-
-static cksum_fp_t
-pclmul_supported (void)
-{
-# if USE_PCLMUL_CRC32 || GL_CRC_X86_64_PCLMUL
-  bool pclmul_enabled = (cpu_supports ("avx")
-                         && cpu_supports ("pclmul"));
-  if (cksum_debug)
-    error (0, 0, "%s",
-           (pclmul_enabled
-            ? _("using pclmul hardware support")
-            : _("pclmul support not detected")));
-#  if USE_PCLMUL_CRC32
-  if (pclmul_enabled)
-    return cksum_pclmul;
-#  endif
-# endif
+  if (s[i] != ')')
+    return false;
+
+  *file_name = s;
+
+  if (escaped_filename && filename_unescape (s, i) == nullptr)
+    return false;
+
+  s[i++] = '\0';
+
+  while (ISWHITE (s[i]))
+    i++;
 
-  return nullptr;
+  if (s[i] != '=')
+    return false;
+
+  i++;
+
+  while (ISWHITE (s[i]))
+    i++;
+
+  *digest = (unsigned char *) &s[i];
+
+  *d_len = s_len - i;
+  return valid_digits (*digest, *d_len);
 }
 
-static cksum_fp_t
-avx2_supported (void)
-{
-  /* AVX512 processors will not set vpclmulqdq unless they support
-     the avx512 version, but it implies that the avx2 version
-     is supported  */
-# if USE_AVX2_CRC32
-  bool avx2_enabled = (cpu_supports ("avx2")
-                       && cpu_supports ("vpclmulqdq"));
-  if (cksum_debug)
-    error (0, 0, "%s",
-           (avx2_enabled
-            ? _("using avx2 hardware support")
-            : _("avx2 support not detected")));
-  if (avx2_enabled)
-    return cksum_avx2;
-# endif
+#if HASH_ALGO_CKSUM
+/* Return the corresponding Algorithm for the string S,
+   or -1 for no match.  */
+
+static ptrdiff_t
+algorithm_from_tag (char *s)
+{
+  /* Limit check size to this length for perf reasons.  */
+  static size_t max_tag_len;
+  if (! max_tag_len)
+    {
+      char const * const * tag = algorithm_tags;
+      while (*tag)
+        {
+          size_t tag_len = strlen (*tag++);
+          max_tag_len = MAX (tag_len, max_tag_len);
+        }
+    }
+
+  size_t i = 0;
+
+  /* Find end of tag */
+  while (i <= max_tag_len && s[i] && ! ISWHITE (s[i])
+         && s[i] != '-' && s[i] != '(')
+    ++i;
+
+  if (i > max_tag_len)
+    return -1;
+
+  /* Terminate tag, and lookup.  */
+  char sep = s[i];
+  s[i] = '\0';
+  ptrdiff_t algo = argmatch_exact (s, algorithm_tags);
+  s[i] = sep;
 
-  return nullptr;
+  return algo;
 }
+#endif
+
+/* Split the string S (of length S_LEN) into three parts:
+   a hexadecimal digest, binary flag, and the file name.
+   S is modified.  Set *D_LEN to the length of the digest string.
+   Return true if successful.  */
+
+static bool
+split_3 (char *s, size_t s_len,
+         unsigned char **digest, size_t *d_len, int *binary, char **file_name)
+{
+  bool escaped_filename = false;
+  size_t algo_name_len;
+
+  size_t i = 0;
+  while (ISWHITE (s[i]))
+    ++i;
 
-static cksum_fp_t
-avx512_supported (void)
-{
-  /* vpclmulqdq for multiplication
-     mavx512f for most of the avx512 functions we're using
-     mavx512bw for byte swapping  */
-# if USE_AVX512_CRC32
-  bool avx512_enabled = (cpu_supports ("avx512f")
-                         && cpu_supports ("avx512bw")
-                         && cpu_supports ("vpclmulqdq"));
-
-  if (cksum_debug)
-    error (0, 0, "%s",
-           (avx512_enabled
-            ? _("using avx512 hardware support")
-            : _("avx512 support not detected")));
-  if (avx512_enabled)
-    return cksum_avx512;
+  if (s[i] == '\\')
+    {
+      ++i;
+      escaped_filename = true;
+    }
+
+  /* Check for BSD-style checksum line. */
+
+#if HASH_ALGO_CKSUM
+  if (! algorithm_specified || cksum_algorithm == sha2)
+    {
+      ptrdiff_t algo_tag = algorithm_from_tag (s + i);
+      if (! algorithm_specified)
+        {
+          if (algo_tag >= 0)
+            {
+              if (algo_tag <= crc32b)
+                return false;  /* We don't support checking these formats.  */
+              cksum_algorithm = algo_tag;
+            }
+          else
+            return false;      /* We only support tagged format without -a.  */
+        }
+      else
+        {
+          if (cksum_algorithm == sha2 && (algo_tag == sha2
+              || algo_tag == sha224 || algo_tag == sha256
+              || algo_tag == sha384 || algo_tag == sha512))
+            cksum_algorithm = algo_tag;
+        }
+    }
+#endif
+
+  /* Try to parse BSD or OpenSSL tagged format.  I.e.:
+     openssl: MD5(f)= d41d8cd98f00b204e9800998ecf8427e
+     bsd:     MD5 (f) = d41d8cd98f00b204e9800998ecf8427e  */
+
+  size_t parse_offset = i;
+  algo_name_len = strlen (DIGEST_TYPE_STRING);
+  if (STREQ_LEN (s + i, DIGEST_TYPE_STRING, algo_name_len))
+    {
+      i += algo_name_len;
+#if HASH_ALGO_BLAKE2 || HASH_ALGO_CKSUM
+
+# if HASH_ALGO_BLAKE2
+      digest_length = DIGEST_MAX_LEN * 8;
+# else
+      digest_length = algorithm_bits[cksum_algorithm];
+# endif
+      if (s[i] == '-')  /* length specified. Not base64 */
+        {
+          ++i;
+          uintmax_t length;
+          char *siend;
+          if (xstrtoumax (s + i, &siend, 0, &length, nullptr) != LONGINT_OK)
+            return false;
+# if HASH_ALGO_CKSUM
+          else if (cksum_algorithm == sha2 || cksum_algorithm == sha3)
+            {
+              if (length != SHA224_DIGEST_SIZE * 8
+                  && length != SHA256_DIGEST_SIZE * 8
+                  && length != SHA384_DIGEST_SIZE * 8
+                  && length != SHA512_DIGEST_SIZE * 8)
+                return false;
+            }
 # endif
+          else if (!(0 < length && length <= digest_length && length % 8 == 0))
+            return false;
 
-  return nullptr;
-}
+          i = siend - s;
+          digest_length = length;
+        }
+      digest_hex_bytes = digest_length / 4;
+#endif
+      if (s[i] == ' ')
+        ++i;
+      if (s[i] == '(')  /* not base64 */
+        {
+          ++i;
+          *binary = 0;
+          return bsd_split_3 (s + i, s_len - i,
+                              digest, d_len, file_name, escaped_filename);
+        }
+
+      /* Note with --base64 --untagged format, we may have matched a "tag".
+         Even very short digests with: cksum -a blake2b -l24 --untagged --base64
+         So fallback to checking untagged format if issues detecting tags.  */
+      i = parse_offset;
+    }
+
+  /* Ignore this line if it is too short.
+     Each line must have at least 'min_digest_line_length - 1' (or one more, if
+     the first is a backslash) more characters to contain correct message digest
+     information.  */
+  if (s_len - i < min_digest_line_length + (s[i] == '\\'))
+    return false;
 
-static cksum_fp_t
-vmull_supported (void)
-{
-  /* vmull for multiplication  */
-# if USE_VMULL_CRC32
-  bool vmull_enabled = (cpu_may_support ("pmull")
-                        && (getauxval (AT_HWCAP) & HWCAP_PMULL) > 0);
-  if (cksum_debug)
-    error (0, 0, "%s",
-           (vmull_enabled
-            ? _("using vmull hardware support")
-            : _("vmull support not detected")));
-  if (vmull_enabled)
-    return cksum_vmull;
+  *digest = (unsigned char *) &s[i];
+
+#if HASH_ALGO_BLAKE2 || HASH_ALGO_CKSUM
+  /* Auto determine length.  */
+# if HASH_ALGO_CKSUM
+  if (cksum_algorithm == blake2b
+      || cksum_algorithm == sha2 || cksum_algorithm == sha3) {
 # endif
+  unsigned char const *hp = *digest;
+  digest_hex_bytes = 0;
+  for (; c_isxdigit (*hp); ++hp, ++digest_hex_bytes)
+    ;
+# if HASH_ALGO_CKSUM
+  /* Check the number of base64 characters.  This works because the hexadecimal
+     character set is a subset of the base64 character set.
+     Note there is the ambiguity that all characters are hex when they
+     are actually base64 encoded, which could be ambiguous with:
+        cksum -a sha2 -l 384 --base64 --untagged
+        cksum -a sha2 -l 256 --untagged
+     Similarly for sha3 and blake2b.
+     However at this length the chances are exceedingly rare (1 in 480R),
+     and smaller blake2b lengths aren't practical for verification anyway.  */
+  size_t digest_base64_bytes = digest_hex_bytes;
+  size_t trailing_equals = 0;
+  for (; isubase64 (*hp); ++hp, ++digest_base64_bytes)
+    ;
+  for (; *hp == '='; ++hp, ++trailing_equals)
+    ;
+  if ((cksum_algorithm == sha2 || cksum_algorithm == sha3)
+      && digest_hex_bytes / 2 != SHA224_DIGEST_SIZE
+      && digest_hex_bytes / 2 != SHA256_DIGEST_SIZE
+      && digest_hex_bytes / 2 != SHA384_DIGEST_SIZE
+      && digest_hex_bytes / 2 != SHA512_DIGEST_SIZE)
+    {
+      if (digest_base64_bytes + trailing_equals
+          == BASE64_LENGTH (SHA224_DIGEST_SIZE))
+        digest_hex_bytes = SHA224_DIGEST_SIZE * 2;
+      else if (digest_base64_bytes + trailing_equals
+               == BASE64_LENGTH (SHA256_DIGEST_SIZE))
+        digest_hex_bytes = SHA256_DIGEST_SIZE * 2;
+      else if (digest_base64_bytes + trailing_equals
+               == BASE64_LENGTH (SHA384_DIGEST_SIZE))
+        digest_hex_bytes = SHA384_DIGEST_SIZE * 2;
+      else if (digest_base64_bytes + trailing_equals
+               == BASE64_LENGTH (SHA512_DIGEST_SIZE))
+        digest_hex_bytes = SHA512_DIGEST_SIZE * 2;
+      else
+        return false;
+    }
+  else if (cksum_algorithm == blake2b
+           && digest_hex_bytes < digest_base64_bytes)
+    {
+      for (int j = 8; j <= DIGEST_MAX_LEN * 8; j += 8)
+        {
+          if (BASE64_LENGTH (j / 8) == digest_base64_bytes + trailing_equals
+              && j % 3 == trailing_equals)
+            {
+              digest_hex_bytes = j / 4;
+              break;
+            }
+        }
+    }
+# endif
+  if (digest_hex_bytes < 2 || digest_hex_bytes % 2
+      || DIGEST_MAX_LEN * 2 < digest_hex_bytes)
+    return false;
+  digest_length = digest_hex_bytes * 4;
+# if HASH_ALGO_CKSUM
+  }
+# endif
+#endif
+
+  /* This field must be the hexadecimal or base64 representation
+     of the message digest.  */
+  while (s[i] && !ISWHITE (s[i]))
+    i++;
+
+  /* The digest must be followed by at least one whitespace character.  */
+  if (i == s_len)
+    return false;
 
-  return nullptr;
+  *d_len = &s[i] - (char *) *digest;
+  s[i++] = '\0';
+
+  if (! valid_digits (*digest, *d_len))
+    return false;
+
+  /* If "bsd reversed" format detected.  */
+  if ((s_len - i == 1) || (s[i] != ' ' && s[i] != '*'))
+    {
+      /* Don't allow mixing bsd and standard formats,
+         to minimize security issues with attackers
+         renaming files with leading spaces.
+         This assumes that with bsd format checksums
+         that the first file name does not have
+         a leading ' ' or '*'.  */
+      if (bsd_reversed == 0)
+        return false;
+      bsd_reversed = 1;
+    }
+  else if (bsd_reversed != 1)
+    {
+      bsd_reversed = 0;
+      *binary = (s[i++] == '*');
+    }
+
+  /* All characters between the type indicator and end of line are
+     significant -- that includes leading and trailing white space.  */
+  *file_name = &s[i];
+
+  if (escaped_filename)
+    return filename_unescape (&s[i], s_len - i) != nullptr;
+
+  return true;
 }
 
+/* If ESCAPE is true, then translate each:
+   NEWLINE byte to the string, "\\n",
+   CARRIAGE RETURN byte to the string, "\\r",
+   and each backslash to "\\\\".  */
+static void
+print_filename (char const *file, bool escape)
+{
+  if (! escape)
+    {
+      fputs (file, stdout);
+      return;
+    }
+
+  while (*file)
+    {
+      switch (*file)
+        {
+        case '\n':
+          fputs ("\\n", stdout);
+          break;
+
+        case '\r':
+          fputs ("\\r", stdout);
+          break;
+
+        case '\\':
+          fputs ("\\\\", stdout);
+          break;
+
+        default:
+          putchar (*file);
+          break;
+        }
+      file++;
+    }
+}
+
+/* An interface to the function, DIGEST_STREAM.
+   Operate on FILENAME (it may be "-").
+
+   *BINARY indicates whether the file is binary.  BINARY < 0 means it
+   depends on whether binary mode makes any difference and the file is
+   a terminal; in that case, clear *BINARY if the file was treated as
+   text because it was a terminal.
+
+   Put the checksum in *BIN_RESULT, which must be properly aligned.
+   Put true in *MISSING if the file can't be opened due to ENOENT.
+   Return true if successful.  */
+
 static bool
-cksum_slice8 (FILE *fp, uint_fast32_t *crc_out, uintmax_t *length_out)
+digest_file (char const *filename, int *binary, unsigned char *bin_result,
+             bool *missing, MAYBE_UNUSED uintmax_t *length)
 {
-  uint32_t buf[BUFLEN / sizeof (uint32_t)];
-  uint_fast32_t crc = 0;
-  uintmax_t length = 0;
-  size_t bytes_read;
+  FILE *fp;
+  int err;
+  bool is_stdin = streq (filename, "-");
 
-  if (!fp || !crc_out || !length_out)
-    return false;
+  *missing = false;
 
-  while ((bytes_read = fread (buf, 1, BUFLEN, fp)) > 0)
+  if (is_stdin)
     {
-      uint32_t *datap;
-
-      if (ckd_add (&length, length, bytes_read))
+      have_read_stdin = true;
+      fp = stdin;
+      if (O_BINARY && *binary)
         {
-          errno = EOVERFLOW;
+          if (*binary < 0)
+            *binary = ! isatty (STDIN_FILENO);
+          if (*binary)
+            xset_binary_mode (STDIN_FILENO, O_BINARY);
+        }
+    }
+  else
+    {
+      fp = fopen (filename, O_BINARY ? (*binary ? "rb" : "rt") : "r");
+      if (fp == nullptr)
+        {
+          if (ignore_missing && errno == ENOENT)
+            {
+              *missing = true;
+              return true;
+            }
+          error (0, errno, "%s", quotef (filename));
           return false;
         }
+    }
 
-      /* Process multiples of 8 bytes */
-      datap = (uint32_t *)buf;
-      while (bytes_read >= 8)
+  fadvise (fp, FADVISE_SEQUENTIAL);
+
+#if HASH_ALGO_CKSUM
+  if (cksum_algorithm == blake2b
+      || cksum_algorithm == sha2 || cksum_algorithm == sha3)
+    *length = digest_length / 8;
+  err = DIGEST_STREAM (fp, bin_result, length);
+#elif HASH_ALGO_SUM
+  err = DIGEST_STREAM (fp, bin_result, length);
+#elif HASH_ALGO_BLAKE2
+  err = DIGEST_STREAM (fp, bin_result, digest_length / 8);
+#else
+  err = DIGEST_STREAM (fp, bin_result);
+#endif
+  err = err ? errno : 0;
+  if (is_stdin)
+    clearerr (fp);
+  else if (fclose (fp) != 0 && !err)
+    err = errno;
+
+  if (err)
+    {
+      error (0, err, "%s", quotef (filename));
+      return false;
+    }
+
+  return true;
+}
+
+#if !HASH_ALGO_SUM
+static void
+output_file (char const *file, int binary_file, void const *digest,
+             MAYBE_UNUSED bool raw, bool tagged, unsigned char delim,
+             MAYBE_UNUSED bool args, MAYBE_UNUSED uintmax_t length)
+{
+# if HASH_ALGO_CKSUM
+  if (raw)
+    {
+      fwrite (digest, 1, digest_length / 8, stdout);
+      return;
+    }
+# endif
+
+  unsigned char const *bin_buffer = digest;
+
+  /* Output a leading backslash if the file name contains problematic chars.  */
+  bool needs_escape = delim == '\n' && problematic_chars (file);
+
+  if (needs_escape)
+    putchar ('\\');
+
+  if (tagged)
+    {
+# if HASH_ALGO_CKSUM
+      if (cksum_algorithm == sha2)
+        printf ("SHA%ju", digest_length);
+      else
+# endif
+      fputs (DIGEST_TYPE_STRING, stdout);
+# if HASH_ALGO_BLAKE2
+      if (digest_length < DIGEST_MAX_LEN * 8)
+        printf ("-%ju", digest_length);
+# elif HASH_ALGO_CKSUM
+      if (cksum_algorithm == sha3)
+        printf ("-%ju", digest_length);
+      if (cksum_algorithm == blake2b)
         {
-          uint32_t first = *datap++, second = *datap++;
-          crc ^= htobe32 (first);
-          second = htobe32 (second);
-          crc = (crctab[7][(crc >> 24) & 0xFF]
-                 ^ crctab[6][(crc >> 16) & 0xFF]
-                 ^ crctab[5][(crc >> 8) & 0xFF]
-                 ^ crctab[4][(crc) & 0xFF]
-                 ^ crctab[3][(second >> 24) & 0xFF]
-                 ^ crctab[2][(second >> 16) & 0xFF]
-                 ^ crctab[1][(second >> 8) & 0xFF]
-                 ^ crctab[0][(second) & 0xFF]);
-          bytes_read -= 8;
+          if (digest_length < DIGEST_MAX_LEN * 8)
+            printf ("-%ju", digest_length);
         }
+# endif
+      fputs (" (", stdout);
+      print_filename (file, needs_escape);
+      fputs (") = ", stdout);
+    }
 
-      /* And finish up last 0-7 bytes in a byte by byte fashion */
-      unsigned char *cp = (unsigned char *)datap;
-      while (bytes_read--)
-        crc = (crc << 8) ^ crctab[0][((crc >> 24) ^ *cp++) & 0xFF];
-      if (feof (fp))
-        break;
+# if HASH_ALGO_CKSUM
+  if (base64_digest)
+    {
+      char b64[BASE64_LENGTH (DIGEST_BIN_BYTES) + 1];
+      base64_encode ((char const *) bin_buffer, digest_length / 8,
+                     b64, sizeof b64);
+      fputs (b64, stdout);
+    }
+  else
+# endif
+    {
+      for (size_t i = 0; i < (digest_hex_bytes / 2); ++i)
+        printf ("%02x", bin_buffer[i]);
     }
 
-  *crc_out = crc;
-  *length_out = length;
+  if (!tagged)
+    {
+      putchar (' ');
+      putchar (binary_file ? '*' : ' ');
+      print_filename (file, needs_escape);
+    }
 
-  return !ferror (fp);
+  putchar (delim);
+}
+#endif
+
+#if HASH_ALGO_CKSUM
+/* Return true if B64_DIGEST is the same as the base64 digest of the
+   DIGEST_LENGTH/8 bytes at BIN_BUFFER.  */
+static bool
+b64_equal (unsigned char const *b64_digest, unsigned char const *bin_buffer)
+{
+  size_t b64_n_bytes = BASE64_LENGTH (digest_length / 8);
+  char b64[BASE64_LENGTH (DIGEST_BIN_BYTES) + 1];
+  base64_encode ((char const *) bin_buffer, digest_length / 8, b64, sizeof b64);
+  return memeq (b64_digest, b64, b64_n_bytes + 1);
 }
+#endif
 
-/* Calculate the checksum and length in bytes of stream STREAM.
-   Return -1 on error, 0 on success.  */
+/* Return true if HEX_DIGEST is the same as the hex-encoded digest of the
+   DIGEST_LENGTH/8 bytes at BIN_BUFFER.  */
+static bool
+hex_equal (unsigned char const *hex_digest, unsigned char const *bin_buffer)
+{
+  static const char bin2hex[] = { '0', '1', '2', '3',
+                                  '4', '5', '6', '7',
+                                  '8', '9', 'a', 'b',
+                                  'c', 'd', 'e', 'f' };
+  size_t digest_bin_bytes = digest_hex_bytes / 2;
+
+  /* Compare generated binary number with text representation
+     in check file.  Ignore case of hex digits.  */
+  size_t cnt;
+  for (cnt = 0; cnt < digest_bin_bytes; ++cnt)
+    {
+      if (c_tolower (hex_digest[2 * cnt])
+          != bin2hex[bin_buffer[cnt] >> 4]
+          || (c_tolower (hex_digest[2 * cnt + 1])
+              != (bin2hex[bin_buffer[cnt] & 0xf])))
+        break;
+    }
+  return cnt == digest_bin_bytes;
+}
 
-int
-crc_sum_stream (FILE *stream, void *resstream, uintmax_t *length)
-{
-  uintmax_t total_bytes = 0;
-  uint_fast32_t crc = 0;
-
-  static cksum_fp_t cksum_fp;
-  if (! cksum_fp)
-    cksum_fp = avx512_supported ();
-  if (! cksum_fp)
-    cksum_fp = avx2_supported ();
-  if (! cksum_fp)
-    cksum_fp = pclmul_supported ();
-  if (! cksum_fp)
-    cksum_fp = vmull_supported ();
-  if (! cksum_fp)
-    cksum_fp = cksum_slice8;
-
-  if (! cksum_fp (stream, &crc, &total_bytes))
-    return -1;
+static bool
+digest_check (char const *checkfile_name)
+{
+  FILE *checkfile_stream;
+  uintmax_t n_misformatted_lines = 0;
+  uintmax_t n_mismatched_checksums = 0;
+  uintmax_t n_open_or_read_failures = 0;
+  bool properly_formatted_lines = false;
+  bool matched_checksums = false;
+  unsigned char bin_buffer_unaligned[DIGEST_BIN_BYTES + DIGEST_ALIGN];
+  /* Make sure bin_buffer is properly aligned. */
+  unsigned char *bin_buffer = ptr_align (bin_buffer_unaligned, DIGEST_ALIGN);
+  uintmax_t line_number;
+  char *line;
+  size_t line_chars_allocated;
+  bool is_stdin = streq (checkfile_name, "-");
+
+  if (is_stdin)
+    {
+      have_read_stdin = true;
+      checkfile_name = _("standard input");
+      checkfile_stream = stdin;
+    }
+  else
+    {
+      checkfile_stream = fopen (checkfile_name, "r");
+      if (checkfile_stream == nullptr)
+        {
+          error (0, errno, "%s", quotef (checkfile_name));
+          return false;
+        }
+    }
 
-  *length = total_bytes;
+  line_number = 0;
+  line = nullptr;
+  line_chars_allocated = 0;
+  do
+    {
+      char *filename;
+      int binary;
+      unsigned char *digest;
+      ssize_t line_length;
+
+      ++line_number;
+      if (line_number == 0)
+        error (EXIT_FAILURE, 0, _("%s: too many checksum lines"),
+               quotef (checkfile_name));
+
+      line_length = getline (&line, &line_chars_allocated, checkfile_stream);
+      if (line_length <= 0)
+        break;
 
-  for (; total_bytes; total_bytes >>= 8)
-    crc = (crc << 8) ^ crctab[0][((crc >> 24) ^ total_bytes) & 0xFF];
-  crc = ~crc & 0xFFFFFFFF;
+      /* Ignore comment lines, which begin with a '#' character.  */
+      if (line[0] == '#')
+        continue;
 
-  unsigned int crc_out = crc;
-  memcpy (resstream, &crc_out, sizeof crc_out);
+      /* Remove any trailing newline.  */
+      line_length -= line[line_length - 1] == '\n';
+      /* Remove any trailing carriage return.  */
+      line_length -= line[line_length - (0 < line_length)] == '\r';
 
-  return 0;
-}
+      /* Ignore empty lines.  */
+      if (line_length == 0)
+        continue;
 
-/* Calculate the crc32b checksum and length in bytes of stream STREAM.
-   Return -1 on error, 0 on success.  */
+      line[line_length] = '\0';
+
+      size_t d_len;
+      if (! (split_3 (line, line_length, &digest, &d_len, &binary, &filename)
+             && ! (is_stdin && streq (filename, "-"))))
+        {
+          ++n_misformatted_lines;
+
+          if (warn)
+            {
+              error (0, 0,
+                     _("%s: %ju"
+                       ": improperly formatted %s checksum line"),
+                     quotef (checkfile_name), line_number,
+                     DIGEST_TYPE_STRING);
+            }
+        }
+      else
+        {
+          bool ok;
+          bool missing;
+          bool needs_escape = ! status_only && problematic_chars (filename);
+
+          properly_formatted_lines = true;
+
+          uintmax_t length;
+          ok = digest_file (filename, &binary, bin_buffer, &missing, &length);
+
+          if (!ok)
+            {
+              ++n_open_or_read_failures;
+              if (!status_only)
+                {
+                  if (needs_escape)
+                    putchar ('\\');
+                  print_filename (filename, needs_escape);
+                  printf (": %s\n", _("FAILED open or read"));
+                }
+            }
+          else if (ignore_missing && missing)
+            {
+              /* Ignore missing files with --ignore-missing.  */
+              ;
+            }
+          else
+            {
+              bool match = false;
+#if HASH_ALGO_CKSUM
+              if (d_len == BASE64_LENGTH (digest_length / 8))
+                match = b64_equal (digest, bin_buffer);
+              else
+#endif
+                if (d_len == digest_hex_bytes)
+                  match = hex_equal (digest, bin_buffer);
+
+              if (match)
+                matched_checksums = true;
+              else
+                ++n_mismatched_checksums;
+
+              if (!status_only)
+                {
+                  if (! match || ! quiet)
+                    {
+                      if (needs_escape)
+                        putchar ('\\');
+                      print_filename (filename, needs_escape);
+                    }
+
+                  if (! match)
+                    printf (": %s\n", _("FAILED"));
+                  else if (!quiet)
+                    printf (": %s\n", _("OK"));
+                }
+            }
+        }
+    }
+  while (!feof (checkfile_stream) && !ferror (checkfile_stream));
+
+  free (line);
+
+  int err = ferror (checkfile_stream) ? 0 : -1;
+  if (is_stdin)
+    clearerr (checkfile_stream);
+  else if (fclose (checkfile_stream) != 0 && err < 0)
+    err = errno;
+
+  if (0 <= err)
+    {
+      error (0, err, err ? "%s" : _("%s: read error"),
+             quotef (checkfile_name));
+      return false;
+    }
+
+  if (! properly_formatted_lines)
+    {
+      /* Warn if no tests are found.  */
+      error (0, 0, _("%s: no properly formatted checksum lines found"),
+             quotef (checkfile_name));
+    }
+  else
+    {
+      if (!status_only)
+        {
+          if (n_misformatted_lines != 0)
+            error (0, 0,
+                   (ngettext
+                    ("WARNING: %ju line is improperly formatted",
+                     "WARNING: %ju lines are improperly formatted",
+                     select_plural (n_misformatted_lines))),
+                   n_misformatted_lines);
+
+          if (n_open_or_read_failures != 0)
+            error (0, 0,
+                   (ngettext
+                    ("WARNING: %ju listed file could not be read",
+                     "WARNING: %ju listed files could not be read",
+                     select_plural (n_open_or_read_failures))),
+                   n_open_or_read_failures);
+
+          if (n_mismatched_checksums != 0)
+            error (0, 0,
+                   (ngettext
+                    ("WARNING: %ju computed checksum did NOT match",
+                     "WARNING: %ju computed checksums did NOT match",
+                     select_plural (n_mismatched_checksums))),
+                   n_mismatched_checksums);
+
+          if (ignore_missing && ! matched_checksums)
+            error (0, 0, _("%s: no file was verified"),
+                   quotef (checkfile_name));
+        }
+    }
+
+  return (properly_formatted_lines
+          && matched_checksums
+          && n_mismatched_checksums == 0
+          && n_open_or_read_failures == 0
+          && (!strict || n_misformatted_lines == 0));
+}
 
 int
-crc32b_sum_stream (FILE *stream, void *resstream, uintmax_t *reslen)
+main (int argc, char **argv)
 {
-  uint32_t buf[BUFLEN / sizeof (uint32_t)];
-  uint32_t crc = 0;
-  uintmax_t len = 0;
-  size_t bytes_read;
+  unsigned char bin_buffer_unaligned[DIGEST_BIN_BYTES + DIGEST_ALIGN];
+  /* Make sure bin_buffer is properly aligned. */
+  unsigned char *bin_buffer = ptr_align (bin_buffer_unaligned, DIGEST_ALIGN);
+  bool do_check = false;
+  int opt;
+  bool ok = true;
+  int binary = -1;
+  int prefix_tag = -1;
+
+  /* Setting values of global variables.  */
+  initialize_main (&argc, &argv);
+  set_program_name (argv[0]);
+  setlocale (LC_ALL, "");
+  bindtextdomain (PACKAGE, LOCALEDIR);
+  textdomain (PACKAGE);
+
+  atexit (close_stdout);
+
+  /* Line buffer stdout to ensure lines are written atomically and immediately
+     so that processes running in parallel do not intersperse their output.  */
+  setvbuf (stdout, nullptr, _IOLBF, 0);
+
+#if HASH_ALGO_SUM
+  char const *short_opts = "rs";
+#elif HASH_ALGO_CKSUM
+  char const *short_opts = "a:l:bctwz";
+  char const *digest_length_str = "";
+#elif HASH_ALGO_BLAKE2
+  char const *short_opts = "l:bctwz";
+  char const *digest_length_str = "";
+#else
+  char const *short_opts = "bctwz";
+#endif
 
-  if (!stream || !resstream || !reslen)
-    return -1;
+  while ((opt = getopt_long (argc, argv, short_opts, long_options, nullptr))
+         != -1)
+    switch (opt)
+      {
+#if HASH_ALGO_CKSUM
+      case 'a':
+        cksum_algorithm = XARGMATCH_EXACT ("--algorithm", optarg,
+                                           algorithm_args, algorithm_types);
+        algorithm_specified = true;
+        break;
 
-# if GL_CRC_X86_64_PCLMUL
-  if (cksum_debug)
-    (void) pclmul_supported ();
+      case DEBUG_PROGRAM_OPTION:
+        cksum_debug = true;
+        break;
+#endif
+#if HASH_ALGO_BLAKE2 || HASH_ALGO_CKSUM
+      case 'l':
+        digest_length = xnumtoumax (optarg, 10, 0, UINTMAX_MAX, "",
+                                    _("invalid length"), 0,
+                                    XTOINT_MAX_QUIET);
+        digest_length_str = optarg;
+        break;
+#endif
+#if !HASH_ALGO_SUM
+      case 'c':
+        do_check = true;
+        break;
+      case STATUS_OPTION:
+        status_only = true;
+        warn = false;
+        quiet = false;
+        break;
+      case 'b':
+        binary = 1;
+        break;
+      case 't':
+        binary = 0;
+        break;
+      case 'w':
+        status_only = false;
+        warn = true;
+        quiet = false;
+        break;
+      case IGNORE_MISSING_OPTION:
+        ignore_missing = true;
+        break;
+      case QUIET_OPTION:
+        status_only = false;
+        warn = false;
+        quiet = true;
+        break;
+      case STRICT_OPTION:
+        strict = true;
+        break;
+# if HASH_ALGO_CKSUM
+      case BASE64_OPTION:
+        base64_digest = true;
+        break;
+      case RAW_OPTION:
+        raw_digest = true;
+        break;
+      case UNTAG_OPTION:
+        if (prefix_tag == 1)
+          binary = -1;
+        prefix_tag = 0;
+        break;
 # endif
+      case TAG_OPTION:
+        prefix_tag = 1;
+        binary = 1;
+        break;
+      case 'z':
+        digest_delim = '\0';
+        break;
+#endif
+#if HASH_ALGO_SUM
+      case 'r':                /* For SysV compatibility. */
+        sum_algorithm = bsd;
+        break;
 
-  while ((bytes_read = fread (buf, 1, BUFLEN, stream)) > 0)
+      case 's':
+        sum_algorithm = sysv;
+        break;
+#endif
+      case_GETOPT_HELP_CHAR;
+      case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
+      default:
+        usage (EXIT_FAILURE);
+      }
+
+  min_digest_line_length = MIN_DIGEST_LINE_LENGTH;
+#if HASH_ALGO_BLAKE2 || HASH_ALGO_CKSUM
+# if HASH_ALGO_CKSUM
+  if (digest_length && (cksum_algorithm != blake2b
+                        && cksum_algorithm != sha2
+                        && cksum_algorithm != sha3))
+    error (EXIT_FAILURE, 0,
+           _("--length is only supported with --algorithm "
+             "blake2b, sha2, or sha3"));
+  if (cksum_algorithm == sha2 || cksum_algorithm == sha3)
     {
-      if (ckd_add (&len, len, bytes_read))
+      /* Do not require --length with --check.  */
+      if (digest_length == 0 && *digest_length_str == '\0' && ! do_check)
+        error (EXIT_FAILURE, 0, _("--algorithm=%s requires specifying "
+                                  "--length 224, 256, 384, or 512"),
+               algorithm_args[cksum_algorithm]);
+      /* If --check and --length are used we verify the digest length.  */
+      if ((! do_check || *digest_length_str != '\0')
+          && digest_length != SHA224_DIGEST_SIZE * 8
+          && digest_length != SHA256_DIGEST_SIZE * 8
+          && digest_length != SHA384_DIGEST_SIZE * 8
+          && digest_length != SHA512_DIGEST_SIZE * 8)
         {
-          errno = EOVERFLOW;
-          return -1;
+          error (0, 0, _("invalid length: %s"), quote (digest_length_str));
+          error (EXIT_FAILURE, 0, _("digest length for %s must be "
+                                    "224, 256, 384, or 512"),
+                 quote (DIGEST_TYPE_STRING));
         }
+    }
+  else
+    {
+      /* If the digest length checks for SHA-3 are satisfied, the less strict
+         checks for BLAKE2 will also be.  */
+# else
+    {
+# endif
+      if (digest_length > DIGEST_MAX_LEN * 8)
+        {
+          error (0, 0, _("invalid length: %s"), quote (digest_length_str));
+          error (EXIT_FAILURE, 0,
+                 _("maximum digest length for %s is %d bits"),
+                 quote (DIGEST_TYPE_STRING),
+                 DIGEST_MAX_LEN * 8);
+        }
+      if (digest_length % 8 != 0)
+        {
+          error (0, 0, _("invalid length: %s"), quote (digest_length_str));
+          error (EXIT_FAILURE, 0, _("length is not a multiple of 8"));
+        }
+    }
+  if (digest_length == 0)
+    {
+# if HASH_ALGO_BLAKE2
+      digest_length = DIGEST_MAX_LEN * 8;
+# else
+      digest_length = algorithm_bits[cksum_algorithm];
+# endif
+    }
+  digest_hex_bytes = digest_length / 4;
+#else
+  digest_hex_bytes = DIGEST_HEX_BYTES;
+#endif
 
-      crc = crc32_update (crc, (char const *)buf, bytes_read);
-
-      if (feof (stream))
+#if HASH_ALGO_CKSUM
+  switch (+cksum_algorithm)
+    {
+    case bsd:
+    case sysv:
+    case crc:
+    case crc32b:
+        if (do_check && algorithm_specified)
+          error (EXIT_FAILURE, 0,
+                 _("--check is not supported with "
+                   "--algorithm={bsd,sysv,crc,crc32b}"));
         break;
     }
 
-  unsigned int crc_out = crc;
-  memcpy (resstream, &crc_out, sizeof crc_out);
+  if (base64_digest && raw_digest)
+   {
+     error (0, 0, _("--base64 and --raw are mutually exclusive"));
+     usage (EXIT_FAILURE);
+   }
+#endif
 
-  *reslen = len;
+  if (prefix_tag == -1)
+    prefix_tag = HASH_ALGO_CKSUM;
+
+  if (prefix_tag && !binary)
+   {
+     /* This could be supported in a backwards compatible way
+        by prefixing the output line with a space in text mode.
+        However that's invasive enough that it was agreed to
+        not support this mode with --tag, as --text use cases
+        are adequately supported by the default output format.  */
+#if !HASH_ALGO_CKSUM
+     error (0, 0, _("--tag does not support --text mode"));
+#else
+     error (0, 0, _("--text mode is only supported with --untagged"));
+#endif
+     usage (EXIT_FAILURE);
+   }
 
-  return ferror (stream) ? -1 : 0;
-}
+  if (digest_delim != '\n' && do_check)
+    {
+      error (0, 0, _("the --zero option is not supported when "
+                     "verifying checksums"));
+      usage (EXIT_FAILURE);
+    }
+#if !HASH_ALGO_CKSUM
+  if (prefix_tag && do_check)
+    {
+      error (0, 0, _("the --tag option is meaningless when "
+                     "verifying checksums"));
+      usage (EXIT_FAILURE);
+    }
+#endif
 
-/* Print the checksum and size to stdout.
-   If ARGS is true, also print the FILE name.  */
+  if (0 <= binary && do_check)
+    {
+      error (0, 0, _("the --binary and --text options are meaningless when "
+                     "verifying checksums"));
+      usage (EXIT_FAILURE);
+    }
 
-void
-output_crc (char const *file, MAYBE_UNUSED int binary_file,
-            void const *digest, bool raw, MAYBE_UNUSED bool tagged,
-            unsigned char delim, bool args, uintmax_t length)
-{
-  if (raw)
+  if (ignore_missing && !do_check)
     {
-      /* Output in network byte order (big endian).  */
-      uint32_t out_int = htobe32 (*(uint32_t *)digest);
-      fwrite (&out_int, 1, 32/8, stdout);
-      return;
+      error (0, 0,
+             _("the --ignore-missing option is meaningful only when "
+               "verifying checksums"));
+      usage (EXIT_FAILURE);
     }
 
-  printf ("%u %ju", *(unsigned int *)digest, length);
-  if (args)
-    printf (" %s", file);
-  putchar (delim);
-}
+  if (status_only && !do_check)
+    {
+      error (0, 0,
+       _("the --status option is meaningful only when verifying checksums"));
+      usage (EXIT_FAILURE);
+    }
+
+  if (warn && !do_check)
+    {
+      error (0, 0,
+       _("the --warn option is meaningful only when verifying checksums"));
+      usage (EXIT_FAILURE);
+    }
 
-#endif /* !CRCTAB */
+  if (quiet && !do_check)
+    {
+      error (0, 0,
+       _("the --quiet option is meaningful only when verifying checksums"));
+      usage (EXIT_FAILURE);
+    }
+
+  if (strict & !do_check)
+   {
+     error (0, 0,
+        _("the --strict option is meaningful only when verifying checksums"));
+     usage (EXIT_FAILURE);
+   }
+
+  if (!O_BINARY && binary < 0)
+    binary = 0;
+
+  char **operand_lim = argv + argc;
+  if (optind == argc)
+    *operand_lim++ = bad_cast ("-");
+  else if (1 < argc - optind && raw_digest)
+    error (EXIT_FAILURE, 0,
+           _("the --raw option is not supported with multiple files"));
+
+  for (char **operandp = argv + optind; operandp < operand_lim; operandp++)
+    {
+      char *file = *operandp;
+      if (do_check)
+        ok &= digest_check (file);
+      else
+        {
+          int binary_file = binary;
+          bool missing;
+          uintmax_t length;
+
+          if (! digest_file (file, &binary_file, bin_buffer, &missing, &length))
+            ok = false;
+          else
+            {
+              DIGEST_OUT (file, binary_file, bin_buffer, raw_digest, prefix_tag,
+                          digest_delim, optind != argc, length);
+            }
+        }
+    }
+
+  if (have_read_stdin && fclose (stdin) == EOF)
+    error (EXIT_FAILURE, errno, _("standard input"));
+
+  return ok ? EXIT_SUCCESS : EXIT_FAILURE;
+}
index 23ecc66e5248388a4d3ad9fb1965c906712c5659..90ca00915a56df65f20e204e400e3914cd6fcb52 100644 (file)
@@ -1,4 +1,4 @@
-/* cksum -- calculate and print POSIX checksums and sizes of files
+/* cksum_crc -- calculate and print POSIX checksums and sizes of files
    Copyright (C) 2024-2025 Free Software Foundation, Inc.
 
    This program is free software: you can redistribute it and/or modify
@@ -16,7 +16,7 @@
 
 #include <config.h>
 
-#include "cksum.h"
+#include "cksum_crc.h"
 
 #include <stdio.h>
 #include <sys/types.h>
index df6abf569522b71965af6337b2ab4b658640c4a9..e0b09b14e889e56af7f69d7e1a0bf4d0fbc3f0b8 100644 (file)
@@ -1,4 +1,4 @@
-/* cksum -- calculate and print POSIX checksums and sizes of files
+/* cksum_crc -- calculate and print POSIX checksums and sizes of files
    Copyright (C) 2024-2025 Free Software Foundation, Inc.
 
    This program is free software: you can redistribute it and/or modify
@@ -16,7 +16,7 @@
 
 #include <config.h>
 
-#include "cksum.h"
+#include "cksum_crc.h"
 
 #include <sys/types.h>
 #include <x86intrin.h>
diff --git a/src/cksum_crc.c b/src/cksum_crc.c
new file mode 100644 (file)
index 0000000..fccf3d2
--- /dev/null
@@ -0,0 +1,377 @@
+/* cksum_crc -- calculate and print POSIX checksums and sizes of files
+   Copyright (C) 1992-2025 Free Software Foundation, Inc.
+
+   This program is free software: you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation, either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <https://www.gnu.org/licenses/>.  */
+
+/* Written by Q. Frank Xia, qx@math.columbia.edu.
+   Cosmetic changes and reorganization by David MacKenzie, djm@gnu.ai.mit.edu.
+
+  The code segment between "#ifdef CRCTAB" and "#else" is the code
+  which generates crctab.c
+
+  This software is compatible with neither the System V nor the BSD
+  'sum' program.  It is supposed to conform to POSIX, except perhaps
+  for foreign language support.  Any inconsistency with the standard
+  (other than foreign language support) is a bug.  */
+
+/* This include must be at the top of the file to satisfy
+   sc_require_config_h_first.  */
+#ifndef CRCTAB
+# include <config.h>
+#endif
+
+#ifdef CRCTAB
+
+# include <stdio.h>
+
+# define BIT(x)        (1u << (x))
+# define SBIT  BIT (31)
+
+/* The generating polynomial is
+
+          32   26   23   22   16   12   11   10   8   7   5   4   2   1
+    G(X)=X  + X  + X  + X  + X  + X  + X  + X  + X + X + X + X + X + X + 1
+
+  The i bit in GEN is set if X^i is a summand of G(X) except X^32.  */
+
+# define GEN   (BIT (26) | BIT (23) | BIT (22) | BIT (16) | BIT (12) \
+                 | BIT (11) | BIT (10) | BIT (8) | BIT (7) | BIT (5) \
+                 | BIT (4) | BIT (2) | BIT (1) | BIT (0))
+
+static unsigned int r[8];
+
+static void
+fill_r (void)
+{
+  r[0] = GEN;
+  for (int i = 1; i < 8; i++)
+    r[i] = (r[i - 1] << 1) ^ ((r[i - 1] & SBIT) ? GEN : 0);
+}
+
+static unsigned int
+crc_remainder (int m)
+{
+  unsigned int rem = 0;
+
+  for (int i = 0; i < 8; i++)
+    if (BIT (i) & m)
+      rem ^= r[i];
+
+  return rem & 0xFFFFFFFF;     /* Make it run on 64-bit machine.  */
+}
+
+int
+main (void)
+{
+  static unsigned int crctab[8][256];
+
+  fill_r ();
+
+  for (int i = 0; i < 256; i++)
+    crctab[0][i] = crc_remainder (i);
+
+  /* CRC(0x11 0x22 0x33 0x44)
+     is equal to
+     CRC(0x11 0x00 0x00 0x00) XOR CRC(0x22 0x00 0x00) XOR
+     CRC(0x33 0x00) XOR CRC(0x44)
+     We precompute the CRC values for the offset values into
+     separate CRC tables. We can then use them to speed up
+     CRC calculation by processing multiple bytes at the time. */
+  for (int i = 0; i < 256; i++)
+    {
+      unsigned int crc = 0;
+
+      crc = (crc << 8) ^ crctab[0][((crc >> 24) ^ i) & 0xFF];
+      for (int offset = 1; offset < 8; offset++)
+        {
+          crc = (crc << 8) ^ crctab[0][((crc >> 24) ^ 0) & 0xFF];
+          crctab[offset][i] = crc & 0xFFFFFFFF;
+        }
+    }
+
+  printf ("#include <config.h>\n");
+  printf ("#include \"cksum_crc.h\"\n");
+  printf ("\n");
+  printf ("uint_fast32_t const crctab[8][256] = {\n");
+  for (int y = 0; y < 8; y++)
+    {
+      printf ("{\n  0x%08x", crctab[y][0]);
+      for (int i = 0; i < 51; i++)
+        {
+          printf (",\n  0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x",
+                  crctab[y][i * 5 + 1], crctab[y][i * 5 + 2],
+                  crctab[y][i * 5 + 3], crctab[y][i * 5 + 4],
+                  crctab[y][i * 5 + 5]);
+        }
+        printf ("\n},\n");
+    }
+  printf ("};\n");
+}
+
+#else /* !CRCTAB */
+
+# include "cksum_crc.h"
+# include <sys/types.h>
+# include <endian.h>
+# include "system.h"
+
+# ifdef USE_VMULL_CRC32
+#  include <sys/auxv.h>
+#  include <asm/hwcap.h>
+# endif
+
+# include "crc.h"
+# include "cpu-supports.h"
+
+/* Number of bytes to read at once.  */
+# define BUFLEN (1 << 16)
+
+typedef bool (*cksum_fp_t) (FILE *, uint_fast32_t *, uintmax_t *);
+
+static cksum_fp_t
+pclmul_supported (void)
+{
+# if USE_PCLMUL_CRC32 || GL_CRC_X86_64_PCLMUL
+  bool pclmul_enabled = (cpu_supports ("avx")
+                         && cpu_supports ("pclmul"));
+  if (cksum_debug)
+    error (0, 0, "%s",
+           (pclmul_enabled
+            ? _("using pclmul hardware support")
+            : _("pclmul support not detected")));
+#  if USE_PCLMUL_CRC32
+  if (pclmul_enabled)
+    return cksum_pclmul;
+#  endif
+# endif
+
+  return nullptr;
+}
+
+static cksum_fp_t
+avx2_supported (void)
+{
+  /* AVX512 processors will not set vpclmulqdq unless they support
+     the avx512 version, but it implies that the avx2 version
+     is supported  */
+# if USE_AVX2_CRC32
+  bool avx2_enabled = (cpu_supports ("avx2")
+                       && cpu_supports ("vpclmulqdq"));
+  if (cksum_debug)
+    error (0, 0, "%s",
+           (avx2_enabled
+            ? _("using avx2 hardware support")
+            : _("avx2 support not detected")));
+  if (avx2_enabled)
+    return cksum_avx2;
+# endif
+
+  return nullptr;
+}
+
+static cksum_fp_t
+avx512_supported (void)
+{
+  /* vpclmulqdq for multiplication
+     mavx512f for most of the avx512 functions we're using
+     mavx512bw for byte swapping  */
+# if USE_AVX512_CRC32
+  bool avx512_enabled = (cpu_supports ("avx512f")
+                         && cpu_supports ("avx512bw")
+                         && cpu_supports ("vpclmulqdq"));
+
+  if (cksum_debug)
+    error (0, 0, "%s",
+           (avx512_enabled
+            ? _("using avx512 hardware support")
+            : _("avx512 support not detected")));
+  if (avx512_enabled)
+    return cksum_avx512;
+# endif
+
+  return nullptr;
+}
+
+static cksum_fp_t
+vmull_supported (void)
+{
+  /* vmull for multiplication  */
+# if USE_VMULL_CRC32
+  bool vmull_enabled = (cpu_may_support ("pmull")
+                        && (getauxval (AT_HWCAP) & HWCAP_PMULL) > 0);
+  if (cksum_debug)
+    error (0, 0, "%s",
+           (vmull_enabled
+            ? _("using vmull hardware support")
+            : _("vmull support not detected")));
+  if (vmull_enabled)
+    return cksum_vmull;
+# endif
+
+  return nullptr;
+}
+
+static bool
+cksum_slice8 (FILE *fp, uint_fast32_t *crc_out, uintmax_t *length_out)
+{
+  uint32_t buf[BUFLEN / sizeof (uint32_t)];
+  uint_fast32_t crc = 0;
+  uintmax_t length = 0;
+  size_t bytes_read;
+
+  if (!fp || !crc_out || !length_out)
+    return false;
+
+  while ((bytes_read = fread (buf, 1, BUFLEN, fp)) > 0)
+    {
+      uint32_t *datap;
+
+      if (ckd_add (&length, length, bytes_read))
+        {
+          errno = EOVERFLOW;
+          return false;
+        }
+
+      /* Process multiples of 8 bytes */
+      datap = (uint32_t *)buf;
+      while (bytes_read >= 8)
+        {
+          uint32_t first = *datap++, second = *datap++;
+          crc ^= htobe32 (first);
+          second = htobe32 (second);
+          crc = (crctab[7][(crc >> 24) & 0xFF]
+                 ^ crctab[6][(crc >> 16) & 0xFF]
+                 ^ crctab[5][(crc >> 8) & 0xFF]
+                 ^ crctab[4][(crc) & 0xFF]
+                 ^ crctab[3][(second >> 24) & 0xFF]
+                 ^ crctab[2][(second >> 16) & 0xFF]
+                 ^ crctab[1][(second >> 8) & 0xFF]
+                 ^ crctab[0][(second) & 0xFF]);
+          bytes_read -= 8;
+        }
+
+      /* And finish up last 0-7 bytes in a byte by byte fashion */
+      unsigned char *cp = (unsigned char *)datap;
+      while (bytes_read--)
+        crc = (crc << 8) ^ crctab[0][((crc >> 24) ^ *cp++) & 0xFF];
+      if (feof (fp))
+        break;
+    }
+
+  *crc_out = crc;
+  *length_out = length;
+
+  return !ferror (fp);
+}
+
+/* Calculate the checksum and length in bytes of stream STREAM.
+   Return -1 on error, 0 on success.  */
+
+int
+crc_sum_stream (FILE *stream, void *resstream, uintmax_t *length)
+{
+  uintmax_t total_bytes = 0;
+  uint_fast32_t crc = 0;
+
+  static cksum_fp_t cksum_fp;
+  if (! cksum_fp)
+    cksum_fp = avx512_supported ();
+  if (! cksum_fp)
+    cksum_fp = avx2_supported ();
+  if (! cksum_fp)
+    cksum_fp = pclmul_supported ();
+  if (! cksum_fp)
+    cksum_fp = vmull_supported ();
+  if (! cksum_fp)
+    cksum_fp = cksum_slice8;
+
+  if (! cksum_fp (stream, &crc, &total_bytes))
+    return -1;
+
+  *length = total_bytes;
+
+  for (; total_bytes; total_bytes >>= 8)
+    crc = (crc << 8) ^ crctab[0][((crc >> 24) ^ total_bytes) & 0xFF];
+  crc = ~crc & 0xFFFFFFFF;
+
+  unsigned int crc_out = crc;
+  memcpy (resstream, &crc_out, sizeof crc_out);
+
+  return 0;
+}
+
+/* Calculate the crc32b checksum and length in bytes of stream STREAM.
+   Return -1 on error, 0 on success.  */
+
+int
+crc32b_sum_stream (FILE *stream, void *resstream, uintmax_t *reslen)
+{
+  uint32_t buf[BUFLEN / sizeof (uint32_t)];
+  uint32_t crc = 0;
+  uintmax_t len = 0;
+  size_t bytes_read;
+
+  if (!stream || !resstream || !reslen)
+    return -1;
+
+# if GL_CRC_X86_64_PCLMUL
+  if (cksum_debug)
+    (void) pclmul_supported ();
+# endif
+
+  while ((bytes_read = fread (buf, 1, BUFLEN, stream)) > 0)
+    {
+      if (ckd_add (&len, len, bytes_read))
+        {
+          errno = EOVERFLOW;
+          return -1;
+        }
+
+      crc = crc32_update (crc, (char const *)buf, bytes_read);
+
+      if (feof (stream))
+        break;
+    }
+
+  unsigned int crc_out = crc;
+  memcpy (resstream, &crc_out, sizeof crc_out);
+
+  *reslen = len;
+
+  return ferror (stream) ? -1 : 0;
+}
+
+/* Print the checksum and size to stdout.
+   If ARGS is true, also print the FILE name.  */
+
+void
+output_crc (char const *file, MAYBE_UNUSED int binary_file,
+            void const *digest, bool raw, MAYBE_UNUSED bool tagged,
+            unsigned char delim, bool args, uintmax_t length)
+{
+  if (raw)
+    {
+      /* Output in network byte order (big endian).  */
+      uint32_t out_int = htobe32 (*(uint32_t *)digest);
+      fwrite (&out_int, 1, 32/8, stdout);
+      return;
+    }
+
+  printf ("%u %ju", *(unsigned int *)digest, length);
+  if (args)
+    printf (" %s", file);
+  putchar (delim);
+}
+
+#endif /* !CRCTAB */
similarity index 100%
rename from src/cksum.h
rename to src/cksum_crc.h
index 240d475fcc651f90ccb6f05eb6145e985423ff26..26fbf5959027177a7fa067b618119488f5edee5d 100644 (file)
@@ -1,4 +1,4 @@
-/* cksum -- calculate and print POSIX checksums and sizes of files
+/* cksum_crc -- calculate and print POSIX checksums and sizes of files
    Copyright (C) 2021-2025 Free Software Foundation, Inc.
 
    This program is free software: you can redistribute it and/or modify
@@ -16,7 +16,7 @@
 
 #include <config.h>
 
-#include "cksum.h"
+#include "cksum_crc.h"
 
 #include <stdio.h>
 #include <sys/types.h>
index fddfaa47b9885d3e045b5136b18da734c72a854e..9d16e486665de7d6cfc4faf7b07af88db3951e74 100644 (file)
@@ -1,4 +1,4 @@
-/* cksum -- calculate and print POSIX checksums and sizes of files
+/* cksum_crc -- calculate and print POSIX checksums and sizes of files
    Copyright (C) 2024-2025 Free Software Foundation, Inc.
 
    This program is free software: you can redistribute it and/or modify
@@ -16,7 +16,7 @@
 
 #include <config.h>
 
-#include "cksum.h"
+#include "cksum_crc.h"
 
 #include <stdio.h>
 #include <sys/types.h>
diff --git a/src/digest.c b/src/digest.c
deleted file mode 100644 (file)
index f330bc1..0000000
+++ /dev/null
@@ -1,1805 +0,0 @@
-/* Compute checksums of files or strings.
-   Copyright (C) 1995-2025 Free Software Foundation, Inc.
-
-   This program is free software: you can redistribute it and/or modify
-   it under the terms of the GNU General Public License as published by
-   the Free Software Foundation, either version 3 of the License, or
-   (at your option) any later version.
-
-   This program is distributed in the hope that it will be useful,
-   but WITHOUT ANY WARRANTY; without even the implied warranty of
-   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-   GNU General Public License for more details.
-
-   You should have received a copy of the GNU General Public License
-   along with this program.  If not, see <https://www.gnu.org/licenses/>.  */
-
-/* Written by Ulrich Drepper <drepper@gnu.ai.mit.edu>.  */
-
-#include <config.h>
-
-#include <getopt.h>
-#include <sys/types.h>
-
-#include "assure.h"
-#include "system.h"
-#include "argmatch.h"
-#include "c-ctype.h"
-#include "quote.h"
-#include "xdectoint.h"
-#include "xstrtol.h"
-
-#ifndef HASH_ALGO_CKSUM
-# define HASH_ALGO_CKSUM 0
-#endif
-
-#if HASH_ALGO_SUM || HASH_ALGO_CKSUM
-# include "sum.h"
-#endif
-#if HASH_ALGO_CKSUM
-# include "cksum.h"
-# include "base64.h"
-#endif
-#if HASH_ALGO_BLAKE2 || HASH_ALGO_CKSUM
-# include "blake2/b2sum.h"
-#endif
-#if HASH_ALGO_MD5 || HASH_ALGO_CKSUM
-# include "md5.h"
-#endif
-#if HASH_ALGO_SHA1 || HASH_ALGO_CKSUM
-# include "sha1.h"
-#endif
-#if HASH_ALGO_SHA256 || HASH_ALGO_SHA224 || HASH_ALGO_CKSUM
-# include "sha256.h"
-#endif
-#if HASH_ALGO_SHA512 || HASH_ALGO_SHA384 || HASH_ALGO_CKSUM
-# include "sha512.h"
-#endif
-#if HASH_ALGO_CKSUM
-# include "sha3.h"
-#endif
-#if HASH_ALGO_CKSUM
-# include "sm3.h"
-#endif
-#include "fadvise.h"
-#include "stdio--.h"
-#include "xbinary-io.h"
-
-/* The official name of this program (e.g., no 'g' prefix).  */
-#if HASH_ALGO_SUM
-# define PROGRAM_NAME "sum"
-# define DIGEST_TYPE_STRING "BSD"
-# define DIGEST_STREAM sumfns[sum_algorithm]
-# define DIGEST_OUT sum_output_fns[sum_algorithm]
-# define DIGEST_BITS 16
-# define DIGEST_ALIGN 4
-#elif HASH_ALGO_CKSUM
-# define MAX_DIGEST_BITS 512
-# define MAX_DIGEST_ALIGN 8
-# define PROGRAM_NAME "cksum"
-# define DIGEST_TYPE_STRING algorithm_tags[cksum_algorithm]
-# define DIGEST_STREAM cksumfns[cksum_algorithm]
-# define DIGEST_OUT cksum_output_fns[cksum_algorithm]
-# define DIGEST_BITS MAX_DIGEST_BITS
-# define DIGEST_ALIGN MAX_DIGEST_ALIGN
-#elif HASH_ALGO_MD5
-# define PROGRAM_NAME "md5sum"
-# define DIGEST_TYPE_STRING "MD5"
-# define DIGEST_STREAM md5_stream
-# define DIGEST_BITS 128
-# define DIGEST_REFERENCE "RFC 1321"
-# define DIGEST_ALIGN 4
-#elif HASH_ALGO_BLAKE2
-# define PROGRAM_NAME "b2sum"
-# define DIGEST_TYPE_STRING "BLAKE2b"
-# define DIGEST_STREAM blake2b_stream
-# define DIGEST_BITS 512
-# define DIGEST_REFERENCE "RFC 7693"
-# define DIGEST_ALIGN 8
-#elif HASH_ALGO_SHA1
-# define PROGRAM_NAME "sha1sum"
-# define DIGEST_TYPE_STRING "SHA1"
-# define DIGEST_STREAM sha1_stream
-# define DIGEST_BITS 160
-# define DIGEST_REFERENCE "FIPS-180-1"
-# define DIGEST_ALIGN 4
-#elif HASH_ALGO_SHA256
-# define PROGRAM_NAME "sha256sum"
-# define DIGEST_TYPE_STRING "SHA256"
-# define DIGEST_STREAM sha256_stream
-# define DIGEST_BITS 256
-# define DIGEST_REFERENCE "FIPS-180-2"
-# define DIGEST_ALIGN 4
-#elif HASH_ALGO_SHA224
-# define PROGRAM_NAME "sha224sum"
-# define DIGEST_TYPE_STRING "SHA224"
-# define DIGEST_STREAM sha224_stream
-# define DIGEST_BITS 224
-# define DIGEST_REFERENCE "RFC 3874"
-# define DIGEST_ALIGN 4
-#elif HASH_ALGO_SHA512
-# define PROGRAM_NAME "sha512sum"
-# define DIGEST_TYPE_STRING "SHA512"
-# define DIGEST_STREAM sha512_stream
-# define DIGEST_BITS 512
-# define DIGEST_REFERENCE "FIPS-180-2"
-# define DIGEST_ALIGN 8
-#elif HASH_ALGO_SHA384
-# define PROGRAM_NAME "sha384sum"
-# define DIGEST_TYPE_STRING "SHA384"
-# define DIGEST_STREAM sha384_stream
-# define DIGEST_BITS 384
-# define DIGEST_REFERENCE "FIPS-180-2"
-# define DIGEST_ALIGN 8
-#else
-# error "Can't decide which hash algorithm to compile."
-#endif
-#if !HASH_ALGO_SUM && !HASH_ALGO_CKSUM
-# define DIGEST_OUT output_file
-#endif
-
-#if HASH_ALGO_SUM
-# define AUTHORS \
-  proper_name ("Kayvan Aghaiepour"), \
-  proper_name ("David MacKenzie")
-#elif HASH_ALGO_CKSUM
-# define AUTHORS \
-  proper_name_lite ("Padraig Brady", "P\303\241draig Brady"), \
-  proper_name ("Q. Frank Xia")
-#elif HASH_ALGO_BLAKE2
-# define AUTHORS \
-  proper_name_lite ("Padraig Brady", "P\303\241draig Brady"), \
-  proper_name ("Samuel Neves")
-#else
-# define AUTHORS \
-  proper_name ("Ulrich Drepper"), \
-  proper_name ("Scott Miller"), \
-  proper_name ("David Madore")
-#endif
-#if !HASH_ALGO_BLAKE2 && !HASH_ALGO_CKSUM
-# define DIGEST_HEX_BYTES (DIGEST_BITS / 4)
-#endif
-#define DIGEST_BIN_BYTES (DIGEST_BITS / 8)
-
-/* The minimum length of a valid digest line.  This length does
-   not include any newline character at the end of a line.  */
-#if HASH_ALGO_BLAKE2 || HASH_ALGO_CKSUM
-# define MIN_DIGEST_LINE_LENGTH 3 /* With -l 8.  */
-#else
-# define MIN_DIGEST_LINE_LENGTH \
-   (DIGEST_HEX_BYTES /* length of hexadecimal message digest */ \
-    + 1 /* blank */ \
-    + 1 /* minimum filename length */ )
-#endif
-
-#if !HASH_ALGO_SUM
-static void
-output_file (char const *file, int binary_file, void const *digest,
-             bool raw, bool tagged, unsigned char delim, bool args,
-             uintmax_t length);
-#endif
-
-/* True if any of the files read were the standard input. */
-static bool have_read_stdin;
-
-/* The minimum length of a valid checksum line for the selected algorithm.  */
-static size_t min_digest_line_length;
-
-/* Set to the length of a digest hex string for the selected algorithm.  */
-static size_t digest_hex_bytes;
-
-/* With --check, don't generate any output.
-   The exit code indicates success or failure.  */
-static bool status_only = false;
-
-/* With --check, print a message to standard error warning about each
-   improperly formatted checksum line.  */
-static bool warn = false;
-
-/* With --check, ignore missing files.  */
-static bool ignore_missing = false;
-
-/* With --check, suppress the "OK" printed for each verified file.  */
-static bool quiet = false;
-
-/* With --check, exit with a non-zero return code if any line is
-   improperly formatted. */
-static bool strict = false;
-
-/* Whether a BSD reversed format checksum is detected.  */
-static int bsd_reversed = -1;
-
-/* line delimiter.  */
-static unsigned char digest_delim = '\n';
-
-#if HASH_ALGO_CKSUM
-/* If true, print base64-encoded digests, not hex.  */
-static bool base64_digest = false;
-#endif
-
-/* If true, print binary digests, not hex.  */
-static bool raw_digest = false;
-
-/* blake2b and sha3 allow the -l option.  Luckily they both have the same
-   maximum digest size.  */
-#if HASH_ALGO_BLAKE2 || HASH_ALGO_CKSUM
-# if HASH_ALGO_CKSUM
-static_assert (BLAKE2B_OUTBYTES == SHA3_512_DIGEST_SIZE);
-# endif
-# define DIGEST_MAX_LEN BLAKE2B_OUTBYTES
-static uintmax_t digest_length;
-#endif /* HASH_ALGO_BLAKE2 || HASH_ALGO_CKSUM */
-
-typedef void (*digest_output_fn)(char const *, int, void const *, bool,
-                                 bool, unsigned char, bool, uintmax_t);
-#if HASH_ALGO_SUM
-enum Algorithm
-{
-  bsd,
-  sysv,
-};
-
-static enum Algorithm sum_algorithm;
-static sumfn sumfns[]=
-{
-  bsd_sum_stream,
-  sysv_sum_stream,
-};
-static digest_output_fn sum_output_fns[]=
-{
-  output_bsd,
-  output_sysv,
-};
-#endif
-
-#if HASH_ALGO_CKSUM
-static int
-md5_sum_stream (FILE *stream, void *resstream, MAYBE_UNUSED uintmax_t *length)
-{
-  return md5_stream (stream, resstream);
-}
-static int
-sha1_sum_stream (FILE *stream, void *resstream, MAYBE_UNUSED uintmax_t *length)
-{
-  return sha1_stream (stream, resstream);
-}
-static int
-sha224_sum_stream (FILE *stream, void *resstream,
-                   MAYBE_UNUSED uintmax_t *length)
-{
-  return sha224_stream (stream, resstream);
-}
-static int
-sha256_sum_stream (FILE *stream, void *resstream,
-                   MAYBE_UNUSED uintmax_t *length)
-{
-  return sha256_stream (stream, resstream);
-}
-static int
-sha384_sum_stream (FILE *stream, void *resstream,
-                   MAYBE_UNUSED uintmax_t *length)
-{
-  return sha384_stream (stream, resstream);
-}
-static int
-sha512_sum_stream (FILE *stream, void *resstream,
-                   MAYBE_UNUSED uintmax_t *length)
-{
-  return sha512_stream (stream, resstream);
-}
-static int
-sha2_sum_stream (FILE *stream, void *resstream, uintmax_t *length)
-{
-  switch (*length)
-    {
-    case SHA224_DIGEST_SIZE:
-      return sha224_stream (stream, resstream);
-    case SHA256_DIGEST_SIZE:
-      return sha256_stream (stream, resstream);
-    case SHA384_DIGEST_SIZE:
-      return sha384_stream (stream, resstream);
-    case SHA512_DIGEST_SIZE:
-      return sha512_stream (stream, resstream);
-    default:
-      affirm (false);
-    }
-}
-static int
-sha3_sum_stream (FILE *stream, void *resstream, uintmax_t *length)
-{
-  switch (*length)
-    {
-    case SHA3_224_DIGEST_SIZE:
-      return sha3_224_stream (stream, resstream);
-    case SHA3_256_DIGEST_SIZE:
-      return sha3_256_stream (stream, resstream);
-    case SHA3_384_DIGEST_SIZE:
-      return sha3_384_stream (stream, resstream);
-    case SHA3_512_DIGEST_SIZE:
-      return sha3_512_stream (stream, resstream);
-    default:
-      affirm (false);
-    }
-}
-static int
-blake2b_sum_stream (FILE *stream, void *resstream, uintmax_t *length)
-{
-  return blake2b_stream (stream, resstream, *length);
-}
-static int
-sm3_sum_stream (FILE *stream, void *resstream, MAYBE_UNUSED uintmax_t *length)
-{
-  return sm3_stream (stream, resstream);
-}
-
-enum Algorithm
-{
-  bsd,
-  sysv,
-  crc,
-  crc32b,
-  md5,
-  sha1,
-  sha224,
-  sha256,
-  sha384,
-  sha512,
-  sha2,
-  sha3,
-  blake2b,
-  sm3,
-};
-
-static char const *const algorithm_args[] =
-{
-  "bsd", "sysv", "crc", "crc32b", "md5", "sha1",
-  "sha224", "sha256", "sha384", "sha512", /* Legacy naming */
-  "sha2", "sha3", "blake2b", "sm3", nullptr
-};
-static enum Algorithm const algorithm_types[] =
-{
-  bsd, sysv, crc, crc32b, md5, sha1,
-  sha224, sha256, sha384, sha512,
-  sha2, sha3, blake2b, sm3,
-};
-ARGMATCH_VERIFY (algorithm_args, algorithm_types);
-
-static char const *const algorithm_tags[] =
-{
-  "BSD", "SYSV", "CRC", "CRC32B", "MD5", "SHA1",
-  "SHA224", "SHA256", "SHA384", "SHA512",
-  "SHA2", "SHA3", "BLAKE2b", "SM3", nullptr
-};
-static int const algorithm_bits[] =
-{
-  16, 16, 32, 32, 128, 160,
-  224, 256, 384, 512,
-  512, 512, 512, 256, 0
-};
-
-static_assert (countof (algorithm_bits) == countof (algorithm_args));
-
-static bool algorithm_specified = false;
-static enum Algorithm cksum_algorithm = crc;
-static sumfn cksumfns[]=
-{
-  bsd_sum_stream,
-  sysv_sum_stream,
-  crc_sum_stream,
-  crc32b_sum_stream,
-  md5_sum_stream,
-  sha1_sum_stream,
-  sha224_sum_stream,
-  sha256_sum_stream,
-  sha384_sum_stream,
-  sha512_sum_stream,
-  sha2_sum_stream,
-  sha3_sum_stream,
-  blake2b_sum_stream,
-  sm3_sum_stream,
-};
-static digest_output_fn cksum_output_fns[]=
-{
-  output_bsd,
-  output_sysv,
-  output_crc,
-  output_crc,
-  output_file,
-  output_file,
-  output_file,
-  output_file,
-  output_file,
-  output_file,
-  output_file,
-  output_file,
-  output_file,
-  output_file,
-};
-bool cksum_debug;
-#endif
-
-/* For long options that have no equivalent short option, use a
-   non-character as a pseudo short option, starting with CHAR_MAX + 1.  */
-
-enum
-{
-  IGNORE_MISSING_OPTION = CHAR_MAX + 1,
-  STATUS_OPTION,
-  QUIET_OPTION,
-  STRICT_OPTION,
-  TAG_OPTION,
-  UNTAG_OPTION,
-  DEBUG_PROGRAM_OPTION,
-  RAW_OPTION,
-  BASE64_OPTION,
-};
-
-static struct option const long_options[] =
-{
-#if HASH_ALGO_BLAKE2 || HASH_ALGO_CKSUM
-  { "length", required_argument, nullptr, 'l'},
-#endif
-
-#if !HASH_ALGO_SUM
-  { "check", no_argument, nullptr, 'c' },
-  { "ignore-missing", no_argument, nullptr, IGNORE_MISSING_OPTION},
-  { "quiet", no_argument, nullptr, QUIET_OPTION },
-  { "status", no_argument, nullptr, STATUS_OPTION },
-  { "warn", no_argument, nullptr, 'w' },
-  { "strict", no_argument, nullptr, STRICT_OPTION },
-  { "tag", no_argument, nullptr, TAG_OPTION },
-  { "zero", no_argument, nullptr, 'z' },
-
-# if HASH_ALGO_CKSUM
-  { "algorithm", required_argument, nullptr, 'a'},
-  { "base64", no_argument, nullptr, BASE64_OPTION },
-  { "debug", no_argument, nullptr, DEBUG_PROGRAM_OPTION},
-  { "raw", no_argument, nullptr, RAW_OPTION},
-  { "untagged", no_argument, nullptr, UNTAG_OPTION },
-# endif
-  { "binary", no_argument, nullptr, 'b' },
-  { "text", no_argument, nullptr, 't' },
-
-#else
-  {"sysv", no_argument, nullptr, 's'},
-#endif
-
-  { GETOPT_HELP_OPTION_DECL },
-  { GETOPT_VERSION_OPTION_DECL },
-  { nullptr, 0, nullptr, 0 }
-};
-
-void
-usage (int status)
-{
-  if (status != EXIT_SUCCESS)
-    emit_try_help ();
-  else
-    {
-      printf (_("\
-Usage: %s [OPTION]... [FILE]...\n\
-"), program_name);
-#if HASH_ALGO_CKSUM
-      fputs (_("\
-Print or verify checksums.\n\
-By default use the 32 bit CRC algorithm.\n\
-"), stdout);
-#else
-      printf (_("\
-Print or check %s (%d-bit) checksums.\n\
-"),
-              DIGEST_TYPE_STRING,
-              DIGEST_BITS);
-#endif
-
-      emit_stdin_note ();
-#if HASH_ALGO_SUM
-      fputs (_("\
-\n\
-  -r              use BSD sum algorithm (the default), use 1K blocks\n\
-  -s, --sysv      use System V sum algorithm, use 512 bytes blocks\n\
-"), stdout);
-#endif
-#if HASH_ALGO_BLAKE2 || HASH_ALGO_CKSUM
-        emit_mandatory_arg_note ();
-#endif
-#if HASH_ALGO_CKSUM
-        fputs (_("\
-  -a, --algorithm=TYPE  select the digest type to use.  See DIGEST below\
-\n\
-"), stdout);
-        fputs (_("\
-      --base64          emit base64-encoded digests, not hexadecimal\
-\n\
-"), stdout);
-#endif
-#if !HASH_ALGO_SUM
-# if !HASH_ALGO_CKSUM
-      if (O_BINARY)
-        fputs (_("\
-  -b, --binary          read in binary mode (default unless reading tty stdin)\
-\n\
-"), stdout);
-      else
-        fputs (_("\
-  -b, --binary          read in binary mode\n\
-"), stdout);
-# endif
-        fputs (_("\
-  -c, --check           read checksums from the FILEs and check them\n\
-"), stdout);
-# if HASH_ALGO_BLAKE2 || HASH_ALGO_CKSUM
-        fputs (_("\
-  -l, --length=BITS     digest length in bits; must not exceed the max size\n\
-                          and must be a multiple of 8 for blake2b;\n\
-                          must be 224, 256, 384, or 512 for sha2 or sha3\n\
-"), stdout);
-# endif
-# if HASH_ALGO_CKSUM
-        fputs (_("\
-      --raw             emit a raw binary digest, not hexadecimal\
-\n\
-"), stdout);
-      fputs (_("\
-      --tag             create a BSD-style checksum (the default)\n\
-"), stdout);
-      fputs (_("\
-      --untagged        create a reversed style checksum, without digest type\n\
-"), stdout);
-# else
-      fputs (_("\
-      --tag             create a BSD-style checksum\n\
-"), stdout);
-# endif
-# if !HASH_ALGO_CKSUM
-      if (O_BINARY)
-        fputs (_("\
-  -t, --text            read in text mode (default if reading tty stdin)\n\
-"), stdout);
-      else
-        fputs (_("\
-  -t, --text            read in text mode (default)\n\
-"), stdout);
-# endif
-      fputs (_("\
-  -z, --zero            end each output line with NUL, not newline,\n\
-                          and disable file name escaping\n\
-"), stdout);
-      fputs (_("\
-\n\
-The following five options are useful only when verifying checksums:\n\
-      --ignore-missing  don't fail or report status for missing files\n\
-      --quiet           don't print OK for each successfully verified file\n\
-      --status          don't output anything, status code shows success\n\
-      --strict          exit non-zero for improperly formatted checksum lines\n\
-  -w, --warn            warn about improperly formatted checksum lines\n\
-\n\
-"), stdout);
-#endif
-#if HASH_ALGO_CKSUM
-      fputs (_("\
-      --debug           indicate which implementation used\n\
-"), stdout);
-#endif
-      fputs (HELP_OPTION_DESCRIPTION, stdout);
-      fputs (VERSION_OPTION_DESCRIPTION, stdout);
-#if HASH_ALGO_CKSUM
-      fputs (_("\
-\n\
-DIGEST determines the digest algorithm and default output format:\n\
-  sysv      (equivalent to sum -s)\n\
-  bsd       (equivalent to sum -r)\n\
-  crc       (equivalent to cksum)\n\
-  crc32b    (only available through cksum)\n\
-  md5       (equivalent to md5sum)\n\
-  sha1      (equivalent to sha1sum)\n\
-  sha2      (equivalent to sha{224,256,384,512}sum)\n\
-  sha3      (only available through cksum)\n\
-  blake2b   (equivalent to b2sum)\n\
-  sm3       (only available through cksum)\n\
-\n"), stdout);
-#endif
-#if !HASH_ALGO_SUM && !HASH_ALGO_CKSUM
-      printf (_("\
-\n\
-The sums are computed as described in %s.\n"), DIGEST_REFERENCE);
-      fputs (_("\
-When checking, the input should be a former output of this program.\n\
-The default mode is to print a line with: checksum, a space,\n\
-a character indicating input mode ('*' for binary, ' ' for text\n\
-or where binary is insignificant), and name for each FILE.\n\
-\n\
-There is no difference between binary mode and text mode on GNU systems.\
-\n"), stdout);
-#endif
-#if HASH_ALGO_CKSUM
-      fputs (_("\
-When checking, the input should be a former output of this program,\n\
-or equivalent standalone program.\
-\n"), stdout);
-#endif
-      emit_ancillary_info (PROGRAM_NAME);
-    }
-
-  exit (status);
-}
-
-/* Given a string S, return TRUE if it contains problematic characters
-   that need escaping.  Note we escape '\' itself to provide some forward
-   compat to introduce escaping of other characters.  */
-
-ATTRIBUTE_PURE
-static bool
-problematic_chars (char const *s)
-{
-  size_t length = strcspn (s, "\\\n\r");
-  return s[length] != '\0';
-}
-
-#define ISWHITE(c) ((c) == ' ' || (c) == '\t')
-
-/* Given a file name, S of length S_LEN, that is not NUL-terminated,
-   modify it in place, performing the equivalent of this sed substitution:
-   's/\\n/\n/g;s/\\r/\r/g;s/\\\\/\\/g' i.e., replacing each "\\n" string
-   with a newline, each "\\r" string with a carriage return,
-   and each "\\\\" with a single backslash, NUL-terminate it and return S.
-   If S is not a valid escaped file name, i.e., if it ends with an odd number
-   of backslashes or if it contains a backslash followed by anything other
-   than "n" or another backslash, return nullptr.  */
-
-static char *
-filename_unescape (char *s, size_t s_len)
-{
-  char *dst = s;
-
-  for (size_t i = 0; i < s_len; i++)
-    {
-      switch (s[i])
-        {
-        case '\\':
-          if (i == s_len - 1)
-            {
-              /* File name ends with an unescaped backslash: invalid.  */
-              return nullptr;
-            }
-          ++i;
-          switch (s[i])
-            {
-            case 'n':
-              *dst++ = '\n';
-              break;
-            case 'r':
-              *dst++ = '\r';
-              break;
-            case '\\':
-              *dst++ = '\\';
-              break;
-            default:
-              /* Only '\', 'n' or 'r' may follow a backslash.  */
-              return nullptr;
-            }
-          break;
-
-        case '\0':
-          /* The file name may not contain a NUL.  */
-          return nullptr;
-
-        default:
-          *dst++ = s[i];
-          break;
-        }
-    }
-  if (dst < s + s_len)
-    *dst = '\0';
-
-  return s;
-}
-
-/* Return true if S is a LEN-byte NUL-terminated string of hex or base64
-   digits and has the expected length.  Otherwise, return false.  */
-ATTRIBUTE_PURE
-static bool
-valid_digits (unsigned char const *s, size_t len)
-{
-#if HASH_ALGO_CKSUM
-  if (len == BASE64_LENGTH (digest_length / 8))
-    {
-      size_t i;
-      for (i = 0; i < len - digest_length % 3; i++)
-        {
-          if (!isbase64 (*s))
-            return false;
-          ++s;
-        }
-      for ( ; i < len; i++)
-        {
-          if (*s != '=')
-            return false;
-          ++s;
-        }
-    }
-  else
-#endif
-  if (len == digest_hex_bytes)
-    {
-      for (idx_t i = 0; i < digest_hex_bytes; i++)
-        {
-          if (!c_isxdigit (*s))
-            return false;
-          ++s;
-        }
-    }
-  else
-    return false;
-
-  return *s == '\0';
-}
-
-/* Split the checksum string S (of length S_LEN) from a BSD 'md5' or
-   'sha1' command into two parts: a hexadecimal digest, and the file
-   name.  S is modified.  Set *D_LEN to the length of the digest string.
-   Return true if successful.  */
-
-static bool
-bsd_split_3 (char *s, size_t s_len,
-             unsigned char **digest, size_t *d_len,
-             char **file_name, bool escaped_filename)
-{
-  if (s_len == 0)
-    return false;
-
-  /* Find end of filename.  */
-  size_t i = s_len - 1;
-  while (i && s[i] != ')')
-    i--;
-
-  if (s[i] != ')')
-    return false;
-
-  *file_name = s;
-
-  if (escaped_filename && filename_unescape (s, i) == nullptr)
-    return false;
-
-  s[i++] = '\0';
-
-  while (ISWHITE (s[i]))
-    i++;
-
-  if (s[i] != '=')
-    return false;
-
-  i++;
-
-  while (ISWHITE (s[i]))
-    i++;
-
-  *digest = (unsigned char *) &s[i];
-
-  *d_len = s_len - i;
-  return valid_digits (*digest, *d_len);
-}
-
-#if HASH_ALGO_CKSUM
-/* Return the corresponding Algorithm for the string S,
-   or -1 for no match.  */
-
-static ptrdiff_t
-algorithm_from_tag (char *s)
-{
-  /* Limit check size to this length for perf reasons.  */
-  static size_t max_tag_len;
-  if (! max_tag_len)
-    {
-      char const * const * tag = algorithm_tags;
-      while (*tag)
-        {
-          size_t tag_len = strlen (*tag++);
-          max_tag_len = MAX (tag_len, max_tag_len);
-        }
-    }
-
-  size_t i = 0;
-
-  /* Find end of tag */
-  while (i <= max_tag_len && s[i] && ! ISWHITE (s[i])
-         && s[i] != '-' && s[i] != '(')
-    ++i;
-
-  if (i > max_tag_len)
-    return -1;
-
-  /* Terminate tag, and lookup.  */
-  char sep = s[i];
-  s[i] = '\0';
-  ptrdiff_t algo = argmatch_exact (s, algorithm_tags);
-  s[i] = sep;
-
-  return algo;
-}
-#endif
-
-/* Split the string S (of length S_LEN) into three parts:
-   a hexadecimal digest, binary flag, and the file name.
-   S is modified.  Set *D_LEN to the length of the digest string.
-   Return true if successful.  */
-
-static bool
-split_3 (char *s, size_t s_len,
-         unsigned char **digest, size_t *d_len, int *binary, char **file_name)
-{
-  bool escaped_filename = false;
-  size_t algo_name_len;
-
-  size_t i = 0;
-  while (ISWHITE (s[i]))
-    ++i;
-
-  if (s[i] == '\\')
-    {
-      ++i;
-      escaped_filename = true;
-    }
-
-  /* Check for BSD-style checksum line. */
-
-#if HASH_ALGO_CKSUM
-  if (! algorithm_specified || cksum_algorithm == sha2)
-    {
-      ptrdiff_t algo_tag = algorithm_from_tag (s + i);
-      if (! algorithm_specified)
-        {
-          if (algo_tag >= 0)
-            {
-              if (algo_tag <= crc32b)
-                return false;  /* We don't support checking these formats.  */
-              cksum_algorithm = algo_tag;
-            }
-          else
-            return false;      /* We only support tagged format without -a.  */
-        }
-      else
-        {
-          if (cksum_algorithm == sha2 && (algo_tag == sha2
-              || algo_tag == sha224 || algo_tag == sha256
-              || algo_tag == sha384 || algo_tag == sha512))
-            cksum_algorithm = algo_tag;
-        }
-    }
-#endif
-
-  /* Try to parse BSD or OpenSSL tagged format.  I.e.:
-     openssl: MD5(f)= d41d8cd98f00b204e9800998ecf8427e
-     bsd:     MD5 (f) = d41d8cd98f00b204e9800998ecf8427e  */
-
-  size_t parse_offset = i;
-  algo_name_len = strlen (DIGEST_TYPE_STRING);
-  if (STREQ_LEN (s + i, DIGEST_TYPE_STRING, algo_name_len))
-    {
-      i += algo_name_len;
-#if HASH_ALGO_BLAKE2 || HASH_ALGO_CKSUM
-
-# if HASH_ALGO_BLAKE2
-      digest_length = DIGEST_MAX_LEN * 8;
-# else
-      digest_length = algorithm_bits[cksum_algorithm];
-# endif
-      if (s[i] == '-')  /* length specified. Not base64 */
-        {
-          ++i;
-          uintmax_t length;
-          char *siend;
-          if (xstrtoumax (s + i, &siend, 0, &length, nullptr) != LONGINT_OK)
-            return false;
-# if HASH_ALGO_CKSUM
-          else if (cksum_algorithm == sha2 || cksum_algorithm == sha3)
-            {
-              if (length != SHA224_DIGEST_SIZE * 8
-                  && length != SHA256_DIGEST_SIZE * 8
-                  && length != SHA384_DIGEST_SIZE * 8
-                  && length != SHA512_DIGEST_SIZE * 8)
-                return false;
-            }
-# endif
-          else if (!(0 < length && length <= digest_length && length % 8 == 0))
-            return false;
-
-          i = siend - s;
-          digest_length = length;
-        }
-      digest_hex_bytes = digest_length / 4;
-#endif
-      if (s[i] == ' ')
-        ++i;
-      if (s[i] == '(')  /* not base64 */
-        {
-          ++i;
-          *binary = 0;
-          return bsd_split_3 (s + i, s_len - i,
-                              digest, d_len, file_name, escaped_filename);
-        }
-
-      /* Note with --base64 --untagged format, we may have matched a "tag".
-         Even very short digests with: cksum -a blake2b -l24 --untagged --base64
-         So fallback to checking untagged format if issues detecting tags.  */
-      i = parse_offset;
-    }
-
-  /* Ignore this line if it is too short.
-     Each line must have at least 'min_digest_line_length - 1' (or one more, if
-     the first is a backslash) more characters to contain correct message digest
-     information.  */
-  if (s_len - i < min_digest_line_length + (s[i] == '\\'))
-    return false;
-
-  *digest = (unsigned char *) &s[i];
-
-#if HASH_ALGO_BLAKE2 || HASH_ALGO_CKSUM
-  /* Auto determine length.  */
-# if HASH_ALGO_CKSUM
-  if (cksum_algorithm == blake2b
-      || cksum_algorithm == sha2 || cksum_algorithm == sha3) {
-# endif
-  unsigned char const *hp = *digest;
-  digest_hex_bytes = 0;
-  for (; c_isxdigit (*hp); ++hp, ++digest_hex_bytes)
-    ;
-# if HASH_ALGO_CKSUM
-  /* Check the number of base64 characters.  This works because the hexadecimal
-     character set is a subset of the base64 character set.
-     Note there is the ambiguity that all characters are hex when they
-     are actually base64 encoded, which could be ambiguous with:
-        cksum -a sha2 -l 384 --base64 --untagged
-        cksum -a sha2 -l 256 --untagged
-     Similarly for sha3 and blake2b.
-     However at this length the chances are exceedingly rare (1 in 480R),
-     and smaller blake2b lengths aren't practical for verification anyway.  */
-  size_t digest_base64_bytes = digest_hex_bytes;
-  size_t trailing_equals = 0;
-  for (; isubase64 (*hp); ++hp, ++digest_base64_bytes)
-    ;
-  for (; *hp == '='; ++hp, ++trailing_equals)
-    ;
-  if ((cksum_algorithm == sha2 || cksum_algorithm == sha3)
-      && digest_hex_bytes / 2 != SHA224_DIGEST_SIZE
-      && digest_hex_bytes / 2 != SHA256_DIGEST_SIZE
-      && digest_hex_bytes / 2 != SHA384_DIGEST_SIZE
-      && digest_hex_bytes / 2 != SHA512_DIGEST_SIZE)
-    {
-      if (digest_base64_bytes + trailing_equals
-          == BASE64_LENGTH (SHA224_DIGEST_SIZE))
-        digest_hex_bytes = SHA224_DIGEST_SIZE * 2;
-      else if (digest_base64_bytes + trailing_equals
-               == BASE64_LENGTH (SHA256_DIGEST_SIZE))
-        digest_hex_bytes = SHA256_DIGEST_SIZE * 2;
-      else if (digest_base64_bytes + trailing_equals
-               == BASE64_LENGTH (SHA384_DIGEST_SIZE))
-        digest_hex_bytes = SHA384_DIGEST_SIZE * 2;
-      else if (digest_base64_bytes + trailing_equals
-               == BASE64_LENGTH (SHA512_DIGEST_SIZE))
-        digest_hex_bytes = SHA512_DIGEST_SIZE * 2;
-      else
-        return false;
-    }
-  else if (cksum_algorithm == blake2b
-           && digest_hex_bytes < digest_base64_bytes)
-    {
-      for (int j = 8; j <= DIGEST_MAX_LEN * 8; j += 8)
-        {
-          if (BASE64_LENGTH (j / 8) == digest_base64_bytes + trailing_equals
-              && j % 3 == trailing_equals)
-            {
-              digest_hex_bytes = j / 4;
-              break;
-            }
-        }
-    }
-# endif
-  if (digest_hex_bytes < 2 || digest_hex_bytes % 2
-      || DIGEST_MAX_LEN * 2 < digest_hex_bytes)
-    return false;
-  digest_length = digest_hex_bytes * 4;
-# if HASH_ALGO_CKSUM
-  }
-# endif
-#endif
-
-  /* This field must be the hexadecimal or base64 representation
-     of the message digest.  */
-  while (s[i] && !ISWHITE (s[i]))
-    i++;
-
-  /* The digest must be followed by at least one whitespace character.  */
-  if (i == s_len)
-    return false;
-
-  *d_len = &s[i] - (char *) *digest;
-  s[i++] = '\0';
-
-  if (! valid_digits (*digest, *d_len))
-    return false;
-
-  /* If "bsd reversed" format detected.  */
-  if ((s_len - i == 1) || (s[i] != ' ' && s[i] != '*'))
-    {
-      /* Don't allow mixing bsd and standard formats,
-         to minimize security issues with attackers
-         renaming files with leading spaces.
-         This assumes that with bsd format checksums
-         that the first file name does not have
-         a leading ' ' or '*'.  */
-      if (bsd_reversed == 0)
-        return false;
-      bsd_reversed = 1;
-    }
-  else if (bsd_reversed != 1)
-    {
-      bsd_reversed = 0;
-      *binary = (s[i++] == '*');
-    }
-
-  /* All characters between the type indicator and end of line are
-     significant -- that includes leading and trailing white space.  */
-  *file_name = &s[i];
-
-  if (escaped_filename)
-    return filename_unescape (&s[i], s_len - i) != nullptr;
-
-  return true;
-}
-
-/* If ESCAPE is true, then translate each:
-   NEWLINE byte to the string, "\\n",
-   CARRIAGE RETURN byte to the string, "\\r",
-   and each backslash to "\\\\".  */
-static void
-print_filename (char const *file, bool escape)
-{
-  if (! escape)
-    {
-      fputs (file, stdout);
-      return;
-    }
-
-  while (*file)
-    {
-      switch (*file)
-        {
-        case '\n':
-          fputs ("\\n", stdout);
-          break;
-
-        case '\r':
-          fputs ("\\r", stdout);
-          break;
-
-        case '\\':
-          fputs ("\\\\", stdout);
-          break;
-
-        default:
-          putchar (*file);
-          break;
-        }
-      file++;
-    }
-}
-
-/* An interface to the function, DIGEST_STREAM.
-   Operate on FILENAME (it may be "-").
-
-   *BINARY indicates whether the file is binary.  BINARY < 0 means it
-   depends on whether binary mode makes any difference and the file is
-   a terminal; in that case, clear *BINARY if the file was treated as
-   text because it was a terminal.
-
-   Put the checksum in *BIN_RESULT, which must be properly aligned.
-   Put true in *MISSING if the file can't be opened due to ENOENT.
-   Return true if successful.  */
-
-static bool
-digest_file (char const *filename, int *binary, unsigned char *bin_result,
-             bool *missing, MAYBE_UNUSED uintmax_t *length)
-{
-  FILE *fp;
-  int err;
-  bool is_stdin = streq (filename, "-");
-
-  *missing = false;
-
-  if (is_stdin)
-    {
-      have_read_stdin = true;
-      fp = stdin;
-      if (O_BINARY && *binary)
-        {
-          if (*binary < 0)
-            *binary = ! isatty (STDIN_FILENO);
-          if (*binary)
-            xset_binary_mode (STDIN_FILENO, O_BINARY);
-        }
-    }
-  else
-    {
-      fp = fopen (filename, O_BINARY ? (*binary ? "rb" : "rt") : "r");
-      if (fp == nullptr)
-        {
-          if (ignore_missing && errno == ENOENT)
-            {
-              *missing = true;
-              return true;
-            }
-          error (0, errno, "%s", quotef (filename));
-          return false;
-        }
-    }
-
-  fadvise (fp, FADVISE_SEQUENTIAL);
-
-#if HASH_ALGO_CKSUM
-  if (cksum_algorithm == blake2b
-      || cksum_algorithm == sha2 || cksum_algorithm == sha3)
-    *length = digest_length / 8;
-  err = DIGEST_STREAM (fp, bin_result, length);
-#elif HASH_ALGO_SUM
-  err = DIGEST_STREAM (fp, bin_result, length);
-#elif HASH_ALGO_BLAKE2
-  err = DIGEST_STREAM (fp, bin_result, digest_length / 8);
-#else
-  err = DIGEST_STREAM (fp, bin_result);
-#endif
-  err = err ? errno : 0;
-  if (is_stdin)
-    clearerr (fp);
-  else if (fclose (fp) != 0 && !err)
-    err = errno;
-
-  if (err)
-    {
-      error (0, err, "%s", quotef (filename));
-      return false;
-    }
-
-  return true;
-}
-
-#if !HASH_ALGO_SUM
-static void
-output_file (char const *file, int binary_file, void const *digest,
-             MAYBE_UNUSED bool raw, bool tagged, unsigned char delim,
-             MAYBE_UNUSED bool args, MAYBE_UNUSED uintmax_t length)
-{
-# if HASH_ALGO_CKSUM
-  if (raw)
-    {
-      fwrite (digest, 1, digest_length / 8, stdout);
-      return;
-    }
-# endif
-
-  unsigned char const *bin_buffer = digest;
-
-  /* Output a leading backslash if the file name contains problematic chars.  */
-  bool needs_escape = delim == '\n' && problematic_chars (file);
-
-  if (needs_escape)
-    putchar ('\\');
-
-  if (tagged)
-    {
-# if HASH_ALGO_CKSUM
-      if (cksum_algorithm == sha2)
-        printf ("SHA%ju", digest_length);
-      else
-# endif
-      fputs (DIGEST_TYPE_STRING, stdout);
-# if HASH_ALGO_BLAKE2
-      if (digest_length < DIGEST_MAX_LEN * 8)
-        printf ("-%ju", digest_length);
-# elif HASH_ALGO_CKSUM
-      if (cksum_algorithm == sha3)
-        printf ("-%ju", digest_length);
-      if (cksum_algorithm == blake2b)
-        {
-          if (digest_length < DIGEST_MAX_LEN * 8)
-            printf ("-%ju", digest_length);
-        }
-# endif
-      fputs (" (", stdout);
-      print_filename (file, needs_escape);
-      fputs (") = ", stdout);
-    }
-
-# if HASH_ALGO_CKSUM
-  if (base64_digest)
-    {
-      char b64[BASE64_LENGTH (DIGEST_BIN_BYTES) + 1];
-      base64_encode ((char const *) bin_buffer, digest_length / 8,
-                     b64, sizeof b64);
-      fputs (b64, stdout);
-    }
-  else
-# endif
-    {
-      for (size_t i = 0; i < (digest_hex_bytes / 2); ++i)
-        printf ("%02x", bin_buffer[i]);
-    }
-
-  if (!tagged)
-    {
-      putchar (' ');
-      putchar (binary_file ? '*' : ' ');
-      print_filename (file, needs_escape);
-    }
-
-  putchar (delim);
-}
-#endif
-
-#if HASH_ALGO_CKSUM
-/* Return true if B64_DIGEST is the same as the base64 digest of the
-   DIGEST_LENGTH/8 bytes at BIN_BUFFER.  */
-static bool
-b64_equal (unsigned char const *b64_digest, unsigned char const *bin_buffer)
-{
-  size_t b64_n_bytes = BASE64_LENGTH (digest_length / 8);
-  char b64[BASE64_LENGTH (DIGEST_BIN_BYTES) + 1];
-  base64_encode ((char const *) bin_buffer, digest_length / 8, b64, sizeof b64);
-  return memeq (b64_digest, b64, b64_n_bytes + 1);
-}
-#endif
-
-/* Return true if HEX_DIGEST is the same as the hex-encoded digest of the
-   DIGEST_LENGTH/8 bytes at BIN_BUFFER.  */
-static bool
-hex_equal (unsigned char const *hex_digest, unsigned char const *bin_buffer)
-{
-  static const char bin2hex[] = { '0', '1', '2', '3',
-                                  '4', '5', '6', '7',
-                                  '8', '9', 'a', 'b',
-                                  'c', 'd', 'e', 'f' };
-  size_t digest_bin_bytes = digest_hex_bytes / 2;
-
-  /* Compare generated binary number with text representation
-     in check file.  Ignore case of hex digits.  */
-  size_t cnt;
-  for (cnt = 0; cnt < digest_bin_bytes; ++cnt)
-    {
-      if (c_tolower (hex_digest[2 * cnt])
-          != bin2hex[bin_buffer[cnt] >> 4]
-          || (c_tolower (hex_digest[2 * cnt + 1])
-              != (bin2hex[bin_buffer[cnt] & 0xf])))
-        break;
-    }
-  return cnt == digest_bin_bytes;
-}
-
-static bool
-digest_check (char const *checkfile_name)
-{
-  FILE *checkfile_stream;
-  uintmax_t n_misformatted_lines = 0;
-  uintmax_t n_mismatched_checksums = 0;
-  uintmax_t n_open_or_read_failures = 0;
-  bool properly_formatted_lines = false;
-  bool matched_checksums = false;
-  unsigned char bin_buffer_unaligned[DIGEST_BIN_BYTES + DIGEST_ALIGN];
-  /* Make sure bin_buffer is properly aligned. */
-  unsigned char *bin_buffer = ptr_align (bin_buffer_unaligned, DIGEST_ALIGN);
-  uintmax_t line_number;
-  char *line;
-  size_t line_chars_allocated;
-  bool is_stdin = streq (checkfile_name, "-");
-
-  if (is_stdin)
-    {
-      have_read_stdin = true;
-      checkfile_name = _("standard input");
-      checkfile_stream = stdin;
-    }
-  else
-    {
-      checkfile_stream = fopen (checkfile_name, "r");
-      if (checkfile_stream == nullptr)
-        {
-          error (0, errno, "%s", quotef (checkfile_name));
-          return false;
-        }
-    }
-
-  line_number = 0;
-  line = nullptr;
-  line_chars_allocated = 0;
-  do
-    {
-      char *filename;
-      int binary;
-      unsigned char *digest;
-      ssize_t line_length;
-
-      ++line_number;
-      if (line_number == 0)
-        error (EXIT_FAILURE, 0, _("%s: too many checksum lines"),
-               quotef (checkfile_name));
-
-      line_length = getline (&line, &line_chars_allocated, checkfile_stream);
-      if (line_length <= 0)
-        break;
-
-      /* Ignore comment lines, which begin with a '#' character.  */
-      if (line[0] == '#')
-        continue;
-
-      /* Remove any trailing newline.  */
-      line_length -= line[line_length - 1] == '\n';
-      /* Remove any trailing carriage return.  */
-      line_length -= line[line_length - (0 < line_length)] == '\r';
-
-      /* Ignore empty lines.  */
-      if (line_length == 0)
-        continue;
-
-      line[line_length] = '\0';
-
-      size_t d_len;
-      if (! (split_3 (line, line_length, &digest, &d_len, &binary, &filename)
-             && ! (is_stdin && streq (filename, "-"))))
-        {
-          ++n_misformatted_lines;
-
-          if (warn)
-            {
-              error (0, 0,
-                     _("%s: %ju"
-                       ": improperly formatted %s checksum line"),
-                     quotef (checkfile_name), line_number,
-                     DIGEST_TYPE_STRING);
-            }
-        }
-      else
-        {
-          bool ok;
-          bool missing;
-          bool needs_escape = ! status_only && problematic_chars (filename);
-
-          properly_formatted_lines = true;
-
-          uintmax_t length;
-          ok = digest_file (filename, &binary, bin_buffer, &missing, &length);
-
-          if (!ok)
-            {
-              ++n_open_or_read_failures;
-              if (!status_only)
-                {
-                  if (needs_escape)
-                    putchar ('\\');
-                  print_filename (filename, needs_escape);
-                  printf (": %s\n", _("FAILED open or read"));
-                }
-            }
-          else if (ignore_missing && missing)
-            {
-              /* Ignore missing files with --ignore-missing.  */
-              ;
-            }
-          else
-            {
-              bool match = false;
-#if HASH_ALGO_CKSUM
-              if (d_len == BASE64_LENGTH (digest_length / 8))
-                match = b64_equal (digest, bin_buffer);
-              else
-#endif
-                if (d_len == digest_hex_bytes)
-                  match = hex_equal (digest, bin_buffer);
-
-              if (match)
-                matched_checksums = true;
-              else
-                ++n_mismatched_checksums;
-
-              if (!status_only)
-                {
-                  if (! match || ! quiet)
-                    {
-                      if (needs_escape)
-                        putchar ('\\');
-                      print_filename (filename, needs_escape);
-                    }
-
-                  if (! match)
-                    printf (": %s\n", _("FAILED"));
-                  else if (!quiet)
-                    printf (": %s\n", _("OK"));
-                }
-            }
-        }
-    }
-  while (!feof (checkfile_stream) && !ferror (checkfile_stream));
-
-  free (line);
-
-  int err = ferror (checkfile_stream) ? 0 : -1;
-  if (is_stdin)
-    clearerr (checkfile_stream);
-  else if (fclose (checkfile_stream) != 0 && err < 0)
-    err = errno;
-
-  if (0 <= err)
-    {
-      error (0, err, err ? "%s" : _("%s: read error"),
-             quotef (checkfile_name));
-      return false;
-    }
-
-  if (! properly_formatted_lines)
-    {
-      /* Warn if no tests are found.  */
-      error (0, 0, _("%s: no properly formatted checksum lines found"),
-             quotef (checkfile_name));
-    }
-  else
-    {
-      if (!status_only)
-        {
-          if (n_misformatted_lines != 0)
-            error (0, 0,
-                   (ngettext
-                    ("WARNING: %ju line is improperly formatted",
-                     "WARNING: %ju lines are improperly formatted",
-                     select_plural (n_misformatted_lines))),
-                   n_misformatted_lines);
-
-          if (n_open_or_read_failures != 0)
-            error (0, 0,
-                   (ngettext
-                    ("WARNING: %ju listed file could not be read",
-                     "WARNING: %ju listed files could not be read",
-                     select_plural (n_open_or_read_failures))),
-                   n_open_or_read_failures);
-
-          if (n_mismatched_checksums != 0)
-            error (0, 0,
-                   (ngettext
-                    ("WARNING: %ju computed checksum did NOT match",
-                     "WARNING: %ju computed checksums did NOT match",
-                     select_plural (n_mismatched_checksums))),
-                   n_mismatched_checksums);
-
-          if (ignore_missing && ! matched_checksums)
-            error (0, 0, _("%s: no file was verified"),
-                   quotef (checkfile_name));
-        }
-    }
-
-  return (properly_formatted_lines
-          && matched_checksums
-          && n_mismatched_checksums == 0
-          && n_open_or_read_failures == 0
-          && (!strict || n_misformatted_lines == 0));
-}
-
-int
-main (int argc, char **argv)
-{
-  unsigned char bin_buffer_unaligned[DIGEST_BIN_BYTES + DIGEST_ALIGN];
-  /* Make sure bin_buffer is properly aligned. */
-  unsigned char *bin_buffer = ptr_align (bin_buffer_unaligned, DIGEST_ALIGN);
-  bool do_check = false;
-  int opt;
-  bool ok = true;
-  int binary = -1;
-  int prefix_tag = -1;
-
-  /* Setting values of global variables.  */
-  initialize_main (&argc, &argv);
-  set_program_name (argv[0]);
-  setlocale (LC_ALL, "");
-  bindtextdomain (PACKAGE, LOCALEDIR);
-  textdomain (PACKAGE);
-
-  atexit (close_stdout);
-
-  /* Line buffer stdout to ensure lines are written atomically and immediately
-     so that processes running in parallel do not intersperse their output.  */
-  setvbuf (stdout, nullptr, _IOLBF, 0);
-
-#if HASH_ALGO_SUM
-  char const *short_opts = "rs";
-#elif HASH_ALGO_CKSUM
-  char const *short_opts = "a:l:bctwz";
-  char const *digest_length_str = "";
-#elif HASH_ALGO_BLAKE2
-  char const *short_opts = "l:bctwz";
-  char const *digest_length_str = "";
-#else
-  char const *short_opts = "bctwz";
-#endif
-
-  while ((opt = getopt_long (argc, argv, short_opts, long_options, nullptr))
-         != -1)
-    switch (opt)
-      {
-#if HASH_ALGO_CKSUM
-      case 'a':
-        cksum_algorithm = XARGMATCH_EXACT ("--algorithm", optarg,
-                                           algorithm_args, algorithm_types);
-        algorithm_specified = true;
-        break;
-
-      case DEBUG_PROGRAM_OPTION:
-        cksum_debug = true;
-        break;
-#endif
-#if HASH_ALGO_BLAKE2 || HASH_ALGO_CKSUM
-      case 'l':
-        digest_length = xnumtoumax (optarg, 10, 0, UINTMAX_MAX, "",
-                                    _("invalid length"), 0,
-                                    XTOINT_MAX_QUIET);
-        digest_length_str = optarg;
-        break;
-#endif
-#if !HASH_ALGO_SUM
-      case 'c':
-        do_check = true;
-        break;
-      case STATUS_OPTION:
-        status_only = true;
-        warn = false;
-        quiet = false;
-        break;
-      case 'b':
-        binary = 1;
-        break;
-      case 't':
-        binary = 0;
-        break;
-      case 'w':
-        status_only = false;
-        warn = true;
-        quiet = false;
-        break;
-      case IGNORE_MISSING_OPTION:
-        ignore_missing = true;
-        break;
-      case QUIET_OPTION:
-        status_only = false;
-        warn = false;
-        quiet = true;
-        break;
-      case STRICT_OPTION:
-        strict = true;
-        break;
-# if HASH_ALGO_CKSUM
-      case BASE64_OPTION:
-        base64_digest = true;
-        break;
-      case RAW_OPTION:
-        raw_digest = true;
-        break;
-      case UNTAG_OPTION:
-        if (prefix_tag == 1)
-          binary = -1;
-        prefix_tag = 0;
-        break;
-# endif
-      case TAG_OPTION:
-        prefix_tag = 1;
-        binary = 1;
-        break;
-      case 'z':
-        digest_delim = '\0';
-        break;
-#endif
-#if HASH_ALGO_SUM
-      case 'r':                /* For SysV compatibility. */
-        sum_algorithm = bsd;
-        break;
-
-      case 's':
-        sum_algorithm = sysv;
-        break;
-#endif
-      case_GETOPT_HELP_CHAR;
-      case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
-      default:
-        usage (EXIT_FAILURE);
-      }
-
-  min_digest_line_length = MIN_DIGEST_LINE_LENGTH;
-#if HASH_ALGO_BLAKE2 || HASH_ALGO_CKSUM
-# if HASH_ALGO_CKSUM
-  if (digest_length && (cksum_algorithm != blake2b
-                        && cksum_algorithm != sha2
-                        && cksum_algorithm != sha3))
-    error (EXIT_FAILURE, 0,
-           _("--length is only supported with --algorithm "
-             "blake2b, sha2, or sha3"));
-  if (cksum_algorithm == sha2 || cksum_algorithm == sha3)
-    {
-      /* Do not require --length with --check.  */
-      if (digest_length == 0 && *digest_length_str == '\0' && ! do_check)
-        error (EXIT_FAILURE, 0, _("--algorithm=%s requires specifying "
-                                  "--length 224, 256, 384, or 512"),
-               algorithm_args[cksum_algorithm]);
-      /* If --check and --length are used we verify the digest length.  */
-      if ((! do_check || *digest_length_str != '\0')
-          && digest_length != SHA224_DIGEST_SIZE * 8
-          && digest_length != SHA256_DIGEST_SIZE * 8
-          && digest_length != SHA384_DIGEST_SIZE * 8
-          && digest_length != SHA512_DIGEST_SIZE * 8)
-        {
-          error (0, 0, _("invalid length: %s"), quote (digest_length_str));
-          error (EXIT_FAILURE, 0, _("digest length for %s must be "
-                                    "224, 256, 384, or 512"),
-                 quote (DIGEST_TYPE_STRING));
-        }
-    }
-  else
-    {
-      /* If the digest length checks for SHA-3 are satisfied, the less strict
-         checks for BLAKE2 will also be.  */
-# else
-    {
-# endif
-      if (digest_length > DIGEST_MAX_LEN * 8)
-        {
-          error (0, 0, _("invalid length: %s"), quote (digest_length_str));
-          error (EXIT_FAILURE, 0,
-                 _("maximum digest length for %s is %d bits"),
-                 quote (DIGEST_TYPE_STRING),
-                 DIGEST_MAX_LEN * 8);
-        }
-      if (digest_length % 8 != 0)
-        {
-          error (0, 0, _("invalid length: %s"), quote (digest_length_str));
-          error (EXIT_FAILURE, 0, _("length is not a multiple of 8"));
-        }
-    }
-  if (digest_length == 0)
-    {
-# if HASH_ALGO_BLAKE2
-      digest_length = DIGEST_MAX_LEN * 8;
-# else
-      digest_length = algorithm_bits[cksum_algorithm];
-# endif
-    }
-  digest_hex_bytes = digest_length / 4;
-#else
-  digest_hex_bytes = DIGEST_HEX_BYTES;
-#endif
-
-#if HASH_ALGO_CKSUM
-  switch (+cksum_algorithm)
-    {
-    case bsd:
-    case sysv:
-    case crc:
-    case crc32b:
-        if (do_check && algorithm_specified)
-          error (EXIT_FAILURE, 0,
-                 _("--check is not supported with "
-                   "--algorithm={bsd,sysv,crc,crc32b}"));
-        break;
-    }
-
-  if (base64_digest && raw_digest)
-   {
-     error (0, 0, _("--base64 and --raw are mutually exclusive"));
-     usage (EXIT_FAILURE);
-   }
-#endif
-
-  if (prefix_tag == -1)
-    prefix_tag = HASH_ALGO_CKSUM;
-
-  if (prefix_tag && !binary)
-   {
-     /* This could be supported in a backwards compatible way
-        by prefixing the output line with a space in text mode.
-        However that's invasive enough that it was agreed to
-        not support this mode with --tag, as --text use cases
-        are adequately supported by the default output format.  */
-#if !HASH_ALGO_CKSUM
-     error (0, 0, _("--tag does not support --text mode"));
-#else
-     error (0, 0, _("--text mode is only supported with --untagged"));
-#endif
-     usage (EXIT_FAILURE);
-   }
-
-  if (digest_delim != '\n' && do_check)
-    {
-      error (0, 0, _("the --zero option is not supported when "
-                     "verifying checksums"));
-      usage (EXIT_FAILURE);
-    }
-#if !HASH_ALGO_CKSUM
-  if (prefix_tag && do_check)
-    {
-      error (0, 0, _("the --tag option is meaningless when "
-                     "verifying checksums"));
-      usage (EXIT_FAILURE);
-    }
-#endif
-
-  if (0 <= binary && do_check)
-    {
-      error (0, 0, _("the --binary and --text options are meaningless when "
-                     "verifying checksums"));
-      usage (EXIT_FAILURE);
-    }
-
-  if (ignore_missing && !do_check)
-    {
-      error (0, 0,
-             _("the --ignore-missing option is meaningful only when "
-               "verifying checksums"));
-      usage (EXIT_FAILURE);
-    }
-
-  if (status_only && !do_check)
-    {
-      error (0, 0,
-       _("the --status option is meaningful only when verifying checksums"));
-      usage (EXIT_FAILURE);
-    }
-
-  if (warn && !do_check)
-    {
-      error (0, 0,
-       _("the --warn option is meaningful only when verifying checksums"));
-      usage (EXIT_FAILURE);
-    }
-
-  if (quiet && !do_check)
-    {
-      error (0, 0,
-       _("the --quiet option is meaningful only when verifying checksums"));
-      usage (EXIT_FAILURE);
-    }
-
-  if (strict & !do_check)
-   {
-     error (0, 0,
-        _("the --strict option is meaningful only when verifying checksums"));
-     usage (EXIT_FAILURE);
-   }
-
-  if (!O_BINARY && binary < 0)
-    binary = 0;
-
-  char **operand_lim = argv + argc;
-  if (optind == argc)
-    *operand_lim++ = bad_cast ("-");
-  else if (1 < argc - optind && raw_digest)
-    error (EXIT_FAILURE, 0,
-           _("the --raw option is not supported with multiple files"));
-
-  for (char **operandp = argv + optind; operandp < operand_lim; operandp++)
-    {
-      char *file = *operandp;
-      if (do_check)
-        ok &= digest_check (file);
-      else
-        {
-          int binary_file = binary;
-          bool missing;
-          uintmax_t length;
-
-          if (! digest_file (file, &binary_file, bin_buffer, &missing, &length))
-            ok = false;
-          else
-            {
-              DIGEST_OUT (file, binary_file, bin_buffer, raw_digest, prefix_tag,
-                          digest_delim, optind != argc, length);
-            }
-        }
-    }
-
-  if (have_read_stdin && fclose (stdin) == EOF)
-    error (EXIT_FAILURE, errno, _("standard input"));
-
-  return ok ? EXIT_SUCCESS : EXIT_FAILURE;
-}
index a8ad6b43f8497d702f279d859af7c49e9df7aae3..0050439a8e59155ccb96e7b1ec351e5140e6ead5 100644 (file)
@@ -429,55 +429,55 @@ src_tac_SOURCES = src/tac.c src/temp-stream.c
 src_tail_SOURCES = src/tail.c src/iopoll.c
 src_tee_SOURCES = src/tee.c src/iopoll.c
 
-src_sum_SOURCES = src/sum.c src/sum.h src/digest.c
+src_sum_SOURCES = src/sum.c src/sum.h src/cksum.c
 src_sum_CPPFLAGS = -DHASH_ALGO_SUM=1 $(AM_CPPFLAGS)
 
-src_md5sum_SOURCES = src/digest.c
+src_md5sum_SOURCES = src/cksum.c
 src_md5sum_CPPFLAGS = -DHASH_ALGO_MD5=1 $(AM_CPPFLAGS)
-src_sha1sum_SOURCES = src/digest.c
+src_sha1sum_SOURCES = src/cksum.c
 src_sha1sum_CPPFLAGS = -DHASH_ALGO_SHA1=1 $(AM_CPPFLAGS)
-src_sha224sum_SOURCES = src/digest.c
+src_sha224sum_SOURCES = src/cksum.c
 src_sha224sum_CPPFLAGS = -DHASH_ALGO_SHA224=1 $(AM_CPPFLAGS)
-src_sha256sum_SOURCES = src/digest.c
+src_sha256sum_SOURCES = src/cksum.c
 src_sha256sum_CPPFLAGS = -DHASH_ALGO_SHA256=1 $(AM_CPPFLAGS)
-src_sha384sum_SOURCES = src/digest.c
+src_sha384sum_SOURCES = src/cksum.c
 src_sha384sum_CPPFLAGS = -DHASH_ALGO_SHA384=1 $(AM_CPPFLAGS)
-src_sha512sum_SOURCES = src/digest.c
+src_sha512sum_SOURCES = src/cksum.c
 src_sha512sum_CPPFLAGS = -DHASH_ALGO_SHA512=1 $(AM_CPPFLAGS)
 src_b2sum_CPPFLAGS = -DHASH_ALGO_BLAKE2=1 -DHAVE_CONFIG_H $(AM_CPPFLAGS)
-src_b2sum_SOURCES = src/digest.c \
+src_b2sum_SOURCES = src/cksum.c \
                    src/blake2/blake2.h src/blake2/blake2-impl.h \
                    src/blake2/blake2b-ref.c \
                    src/blake2/b2sum.c src/blake2/b2sum.h
 
 src_cksum_SOURCES = $(src_b2sum_SOURCES) src/sum.c src/sum.h \
-                   src/cksum.c src/cksum.h src/crctab.c
+                   src/cksum_crc.c src/crc.h src/crctab.c
 src_cksum_CPPFLAGS = -DHASH_ALGO_CKSUM=1 -DHAVE_CONFIG_H $(AM_CPPFLAGS)
 
 if USE_AVX512_CRC32
 noinst_LIBRARIES += src/libcksum_avx512.a
-src_libcksum_avx512_a_SOURCES = src/cksum_avx512.c src/cksum.h
+src_libcksum_avx512_a_SOURCES = src/cksum_avx512.c src/cksum_crc.h
 cksum_avx512_ldadd = src/libcksum_avx512.a
 src_cksum_LDADD += $(cksum_avx512_ldadd)
 src_libcksum_avx512_a_CFLAGS = -mavx512bw -mavx512f -mvpclmulqdq $(AM_CFLAGS)
 endif
 if USE_AVX2_CRC32
 noinst_LIBRARIES += src/libcksum_avx2.a
-src_libcksum_avx2_a_SOURCES = src/cksum_avx2.c src/cksum.h
+src_libcksum_avx2_a_SOURCES = src/cksum_avx2.c src/cksum_crc.h
 cksum_avx2_ldadd = src/libcksum_avx2.a
 src_cksum_LDADD += $(cksum_avx2_ldadd)
 src_libcksum_avx2_a_CFLAGS = -mpclmul -mavx -mavx2 -mvpclmulqdq $(AM_CFLAGS)
 endif
 if USE_PCLMUL_CRC32
 noinst_LIBRARIES += src/libcksum_pclmul.a
-src_libcksum_pclmul_a_SOURCES = src/cksum_pclmul.c src/cksum.h
+src_libcksum_pclmul_a_SOURCES = src/cksum_pclmul.c src/cksum_crc.h
 cksum_pclmul_ldadd = src/libcksum_pclmul.a
 src_cksum_LDADD += $(cksum_pclmul_ldadd)
 src_libcksum_pclmul_a_CFLAGS = -mavx -mpclmul $(AM_CFLAGS)
 endif
 if USE_VMULL_CRC32
 noinst_LIBRARIES += src/libcksum_vmull.a
-src_libcksum_vmull_a_SOURCES = src/cksum_vmull.c src/cksum.h
+src_libcksum_vmull_a_SOURCES = src/cksum_vmull.c src/cksum_crc.h
 cksum_vmull_ldadd = src/libcksum_vmull.a
 src_cksum_LDADD += $(cksum_vmull_ldadd)
 src_libcksum_vmull_a_CFLAGS = -march=armv8-a+crypto $(AM_CFLAGS)
@@ -598,13 +598,13 @@ $(top_srcdir)/src/primes.h: $(top_srcdir)/src/make-prime-list.c
 
 # We build crctab in a similar manner to primes.h.
 BUILT_SOURCES += $(top_srcdir)/src/crctab.c
-$(top_srcdir)/src/crctab.c: $(top_srcdir)/src/cksum.c
+$(top_srcdir)/src/crctab.c: $(top_srcdir)/src/cksum_crc.c
        $(AM_V_GEN)if test -n '$(BUILD_CC)'; then \
          $(MKDIR_P) $(top_srcdir)/src/crctab-tmp \
          && (cd $(top_srcdir)/src/crctab-tmp \
              && $(BUILD_CC) $(BUILD_CPPFLAGS) $(BUILD_CFLAGS) \
                $(BUILD_LDFLAGS) -DCRCTAB -o crctab$(EXEEXT) \
-               $(abs_top_srcdir)/src/cksum.c) \
+               $(abs_top_srcdir)/src/cksum_crc.c) \
          && rm -f $@ $@-t \
          && $(top_srcdir)/src/crctab-tmp/crctab$(EXEEXT) > $@-t \
          && chmod a-w $@-t \