| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| |
|
| | |
| |
|
| | #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" |
| |
|
| | |
| | #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) |
| |
|
| | |
| | |
| | #if HASH_ALGO_BLAKE2 || HASH_ALGO_CKSUM |
| | # define MIN_DIGEST_LINE_LENGTH 3 |
| | #else |
| | # define MIN_DIGEST_LINE_LENGTH \ |
| | (DIGEST_HEX_BYTES \ |
| | + 1 \ |
| | + 1 ) |
| | #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 |
| |
|
| | |
| | static bool have_read_stdin; |
| |
|
| | |
| | static size_t min_digest_line_length; |
| |
|
| | |
| | static size_t digest_hex_bytes; |
| |
|
| | |
| | |
| | static bool status_only = false; |
| |
|
| | |
| | |
| | static bool warn = false; |
| |
|
| | |
| | static bool ignore_missing = false; |
| |
|
| | |
| | static bool quiet = false; |
| |
|
| | |
| | |
| | static bool strict = false; |
| |
|
| | |
| | static int bsd_reversed = -1; |
| |
|
| | |
| | static unsigned char digest_delim = '\n'; |
| |
|
| | #if HASH_ALGO_CKSUM |
| | |
| | static bool base64_digest = false; |
| | #endif |
| |
|
| | |
| | static bool raw_digest = false; |
| |
|
| | |
| | |
| | #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 |
| |
|
| | 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", |
| | "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 |
| |
|
| | |
| | |
| |
|
| | 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); |
| | } |
| |
|
| | |
| | |
| | |
| |
|
| | 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') |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| |
|
| | 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) |
| | { |
| | |
| | return nullptr; |
| | } |
| | ++i; |
| | switch (s[i]) |
| | { |
| | case 'n': |
| | *dst++ = '\n'; |
| | break; |
| | case 'r': |
| | *dst++ = '\r'; |
| | break; |
| | case '\\': |
| | *dst++ = '\\'; |
| | break; |
| | default: |
| | |
| | return nullptr; |
| | } |
| | break; |
| |
|
| | case '\0': |
| | |
| | return nullptr; |
| |
|
| | default: |
| | *dst++ = s[i]; |
| | break; |
| | } |
| | } |
| | if (dst < s + s_len) |
| | *dst = '\0'; |
| |
|
| | return s; |
| | } |
| |
|
| | |
| | |
| | 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'; |
| | } |
| |
|
| | |
| | |
| | |
| | |
| |
|
| | 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; |
| |
|
| | |
| | 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 |
| | |
| | |
| |
|
| | static ptrdiff_t |
| | algorithm_from_tag (char *s) |
| | { |
| | |
| | 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; |
| |
|
| | |
| | while (i <= max_tag_len && s[i] && ! ISWHITE (s[i]) |
| | && s[i] != '-' && s[i] != '(') |
| | ++i; |
| |
|
| | if (i > max_tag_len) |
| | return -1; |
| |
|
| | |
| | char sep = s[i]; |
| | s[i] = '\0'; |
| | ptrdiff_t algo = argmatch_exact (s, algorithm_tags); |
| | s[i] = sep; |
| |
|
| | return algo; |
| | } |
| | #endif |
| |
|
| | |
| | |
| | |
| | |
| |
|
| | 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; |
| | } |
| |
|
| | |
| |
|
| | #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; |
| | cksum_algorithm = algo_tag; |
| | } |
| | else |
| | return false; |
| | } |
| | 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 |
| |
|
| | |
| | |
| | |
| |
|
| | 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] == '-') |
| | { |
| | ++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] == '(') |
| | { |
| | ++i; |
| | *binary = 0; |
| | return bsd_split_3 (s + i, s_len - i, |
| | digest, d_len, file_name, escaped_filename); |
| | } |
| |
|
| | |
| | |
| | |
| | i = parse_offset; |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | if (s_len - i < min_digest_line_length + (s[i] == '\\')) |
| | return false; |
| |
|
| | *digest = (unsigned char *) &s[i]; |
| |
|
| | #if HASH_ALGO_BLAKE2 || HASH_ALGO_CKSUM |
| | |
| | # 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 |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | 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 |
| |
|
| | |
| | |
| | while (s[i] && !ISWHITE (s[i])) |
| | i++; |
| |
|
| | |
| | if (i == s_len) |
| | return false; |
| |
|
| | *d_len = &s[i] - (char *) *digest; |
| | s[i++] = '\0'; |
| |
|
| | if (! valid_digits (*digest, *d_len)) |
| | return false; |
| |
|
| | |
| | if ((s_len - i == 1) || (s[i] != ' ' && s[i] != '*')) |
| | { |
| | |
| | |
| | |
| | |
| | |
| | |
| | if (bsd_reversed == 0) |
| | return false; |
| | bsd_reversed = 1; |
| | } |
| | else if (bsd_reversed != 1) |
| | { |
| | bsd_reversed = 0; |
| | *binary = (s[i++] == '*'); |
| | } |
| |
|
| | |
| | |
| | *file_name = &s[i]; |
| |
|
| | if (escaped_filename) |
| | return filename_unescape (&s[i], s_len - i) != nullptr; |
| |
|
| | return true; |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | 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++; |
| | } |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| |
|
| | 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" : "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; |
| |
|
| | |
| | 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 |
| | |
| | |
| | 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 |
| |
|
| | |
| | |
| | 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; |
| |
|
| | |
| | |
| | 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]; |
| | |
| | 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; |
| |
|
| | |
| | if (line[0] == '#') |
| | continue; |
| |
|
| | |
| | line_length -= line[line_length - 1] == '\n'; |
| | |
| | line_length -= line[line_length - (0 < line_length)] == '\r'; |
| |
|
| | |
| | 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) |
| | { |
| | |
| | ; |
| | } |
| | 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) |
| | { |
| | |
| | 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]; |
| | |
| | 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; |
| |
|
| | |
| | initialize_main (&argc, &argv); |
| | set_program_name (argv[0]); |
| | setlocale (LC_ALL, ""); |
| | bindtextdomain (PACKAGE, LOCALEDIR); |
| | textdomain (PACKAGE); |
| |
|
| | atexit (close_stdout); |
| |
|
| | |
| | |
| | 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': |
| | 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) |
| | { |
| | |
| | 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 ((! 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 |
| | { |
| | |
| | |
| | # 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) |
| | { |
| | |
| | |
| | |
| | |
| | |
| | #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; |
| | } |
| |
|