The branch main has been updated by des:

URL: 
https://cgit.FreeBSD.org/src/commit/?id=4849767cb16a4dbd4d1b923db25d34029c09e7b0

commit 4849767cb16a4dbd4d1b923db25d34029c09e7b0
Author:     Dag-Erling Smørgrav <d...@freebsd.org>
AuthorDate: 2023-05-08 06:56:09 +0000
Commit:     Dag-Erling Smørgrav <d...@freebsd.org>
CommitDate: 2023-05-08 06:56:22 +0000

    md5: Improve compatibility.
    
    * Overhaul the GNU compatibility mode to more closely emulate what the GNU 
tools do.
    
    * Add a Perl compatibility mode which emulates the shasum tool that ships 
with Perl.  This is currently not installed.
    
    * Overhaul the tests.
    
    Sponsored by:   Klara, Inc.
    Reviewed by:    kevans
    Differential Revision:  https://reviews.freebsd.org/D39446
---
 ObsoleteFiles.inc                       |  73 ++++
 sbin/md5/Makefile                       |   6 +
 sbin/md5/md5.1                          | 313 +++++++++++----
 sbin/md5/md5.c                          | 665 ++++++++++++++++++++++++--------
 sbin/md5/tests/1.inp                    |   0
 sbin/md5/tests/1.sha512-p.chk           |   1 -
 sbin/md5/tests/1.sha512sum-p.chk        |   1 -
 sbin/md5/tests/2.inp                    |   1 -
 sbin/md5/tests/2.sha512-p.chk           |   1 -
 sbin/md5/tests/2.sha512sum-p.chk        |   1 -
 sbin/md5/tests/3.inp                    |   1 -
 sbin/md5/tests/3.sha512-p.chk           |   1 -
 sbin/md5/tests/3.sha512sum-p.chk        |   1 -
 sbin/md5/tests/4.inp                    |   1 -
 sbin/md5/tests/4.sha512-p.chk           |   1 -
 sbin/md5/tests/4.sha512sum-p.chk        |   1 -
 sbin/md5/tests/5.inp                    |   1 -
 sbin/md5/tests/5.sha512-p.chk           |   1 -
 sbin/md5/tests/5.sha512sum-p.chk        |   1 -
 sbin/md5/tests/6.inp                    |   1 -
 sbin/md5/tests/6.sha512-p.chk           |   1 -
 sbin/md5/tests/6.sha512sum-p.chk        |   1 -
 sbin/md5/tests/7.inp                    |   1 -
 sbin/md5/tests/7.sha512-p.chk           |   1 -
 sbin/md5/tests/7.sha512sum-p.chk        |   1 -
 sbin/md5/tests/8.inp                    |   1 -
 sbin/md5/tests/8.sha512-p.chk           |   1 -
 sbin/md5/tests/8.sha512sum-p.chk        |   1 -
 sbin/md5/tests/Makefile                 |  35 --
 sbin/md5/tests/bsd-c-test.SH            |  23 --
 sbin/md5/tests/bsd-p-test.SH            |  24 --
 sbin/md5/tests/bsd-s-test.SH            |  30 --
 sbin/md5/tests/coreutils-c-test.SH      |  21 -
 sbin/md5/tests/md5.digest               |   8 -
 sbin/md5/tests/md5_test.sh              | 346 +++++++++++++++--
 sbin/md5/tests/md5sum.digest            |   8 -
 sbin/md5/tests/rmd160.digest            |   8 -
 sbin/md5/tests/rmd160sum.digest         |   8 -
 sbin/md5/tests/self-test.SH             |   8 -
 sbin/md5/tests/self-test.md5.chk        |   9 -
 sbin/md5/tests/self-test.rmd160.chk     |   9 -
 sbin/md5/tests/self-test.sh_inp         |   8 -
 sbin/md5/tests/self-test.sha1.chk       |   9 -
 sbin/md5/tests/self-test.sha224.chk     |   9 -
 sbin/md5/tests/self-test.sha256.chk     |   9 -
 sbin/md5/tests/self-test.sha384.chk     |   9 -
 sbin/md5/tests/self-test.sha512.chk     |   9 -
 sbin/md5/tests/self-test.sha512t224.chk |   9 -
 sbin/md5/tests/self-test.sha512t256.chk |   9 -
 sbin/md5/tests/self-test.skein1024.chk  |   9 -
 sbin/md5/tests/self-test.skein256.chk   |   9 -
 sbin/md5/tests/self-test.skein512.chk   |   9 -
 sbin/md5/tests/sha1.digest              |   8 -
 sbin/md5/tests/sha1sum.digest           |   8 -
 sbin/md5/tests/sha224.digest            |   8 -
 sbin/md5/tests/sha224sum.digest         |   8 -
 sbin/md5/tests/sha256.digest            |   8 -
 sbin/md5/tests/sha256sum.digest         |   8 -
 sbin/md5/tests/sha384.digest            |   8 -
 sbin/md5/tests/sha384sum.digest         |   8 -
 sbin/md5/tests/sha512.digest            |   8 -
 sbin/md5/tests/sha512sum.digest         |   8 -
 sbin/md5/tests/sha512t224.digest        |   8 -
 sbin/md5/tests/sha512t224sum.digest     |   8 -
 sbin/md5/tests/sha512t256.digest        |   8 -
 sbin/md5/tests/sha512t256sum.digest     |   8 -
 sbin/md5/tests/skein1024.digest         |   8 -
 sbin/md5/tests/skein1024sum.digest      |   8 -
 sbin/md5/tests/skein256.digest          |   8 -
 sbin/md5/tests/skein256sum.digest       |   8 -
 sbin/md5/tests/skein512.digest          |   8 -
 sbin/md5/tests/skein512sum.digest       |   8 -
 sbin/md5/tests/sum_a.in                 |   1 -
 sbin/md5/tests/sum_b.in                 |   1 -
 sbin/md5/tests/sum_c.in                 |   1 -
 sbin/md5/tests/sum_sums.digest          |   3 -
 76 files changed, 1134 insertions(+), 747 deletions(-)

diff --git a/ObsoleteFiles.inc b/ObsoleteFiles.inc
index ede2871455ae..97881f40ac59 100644
--- a/ObsoleteFiles.inc
+++ b/ObsoleteFiles.inc
@@ -52,6 +52,79 @@
 #   xargs -n1 | sort | uniq -d;
 # done
 
+# 20230505: md5 tests are now self-contained
+OLD_FILES+=usr/tests/sbin/md5/1.inp
+OLD_FILES+=usr/tests/sbin/md5/1.sha512-p.chk
+OLD_FILES+=usr/tests/sbin/md5/1.sha512sum-p.chk
+OLD_FILES+=usr/tests/sbin/md5/2.inp
+OLD_FILES+=usr/tests/sbin/md5/2.sha512-p.chk
+OLD_FILES+=usr/tests/sbin/md5/2.sha512sum-p.chk
+OLD_FILES+=usr/tests/sbin/md5/3.inp
+OLD_FILES+=usr/tests/sbin/md5/3.sha512-p.chk
+OLD_FILES+=usr/tests/sbin/md5/3.sha512sum-p.chk
+OLD_FILES+=usr/tests/sbin/md5/4.inp
+OLD_FILES+=usr/tests/sbin/md5/4.sha512-p.chk
+OLD_FILES+=usr/tests/sbin/md5/4.sha512sum-p.chk
+OLD_FILES+=usr/tests/sbin/md5/5.inp
+OLD_FILES+=usr/tests/sbin/md5/5.sha512-p.chk
+OLD_FILES+=usr/tests/sbin/md5/5.sha512sum-p.chk
+OLD_FILES+=usr/tests/sbin/md5/6.inp
+OLD_FILES+=usr/tests/sbin/md5/6.sha512-p.chk
+OLD_FILES+=usr/tests/sbin/md5/6.sha512sum-p.chk
+OLD_FILES+=usr/tests/sbin/md5/7.inp
+OLD_FILES+=usr/tests/sbin/md5/7.sha512-p.chk
+OLD_FILES+=usr/tests/sbin/md5/7.sha512sum-p.chk
+OLD_FILES+=usr/tests/sbin/md5/8.inp
+OLD_FILES+=usr/tests/sbin/md5/8.sha512-p.chk
+OLD_FILES+=usr/tests/sbin/md5/8.sha512sum-p.chk
+OLD_FILES+=usr/tests/sbin/md5/algorithms.txt
+OLD_FILES+=usr/tests/sbin/md5/bsd-c-test
+OLD_FILES+=usr/tests/sbin/md5/bsd-p-test
+OLD_FILES+=usr/tests/sbin/md5/bsd-s-test
+OLD_FILES+=usr/tests/sbin/md5/coreutils-c-test
+OLD_FILES+=usr/tests/sbin/md5/md5.digest
+OLD_FILES+=usr/tests/sbin/md5/md5sum.digest
+OLD_FILES+=usr/tests/sbin/md5/rmd160.digest
+OLD_FILES+=usr/tests/sbin/md5/rmd160sum.digest
+OLD_FILES+=usr/tests/sbin/md5/self-test
+OLD_FILES+=usr/tests/sbin/md5/self-test.md5.chk
+OLD_FILES+=usr/tests/sbin/md5/self-test.rmd160.chk
+OLD_FILES+=usr/tests/sbin/md5/self-test.sh_inp
+OLD_FILES+=usr/tests/sbin/md5/self-test.sha1.chk
+OLD_FILES+=usr/tests/sbin/md5/self-test.sha224.chk
+OLD_FILES+=usr/tests/sbin/md5/self-test.sha256.chk
+OLD_FILES+=usr/tests/sbin/md5/self-test.sha384.chk
+OLD_FILES+=usr/tests/sbin/md5/self-test.sha512.chk
+OLD_FILES+=usr/tests/sbin/md5/self-test.sha512t224.chk
+OLD_FILES+=usr/tests/sbin/md5/self-test.sha512t256.chk
+OLD_FILES+=usr/tests/sbin/md5/self-test.skein1024.chk
+OLD_FILES+=usr/tests/sbin/md5/self-test.skein256.chk
+OLD_FILES+=usr/tests/sbin/md5/self-test.skein512.chk
+OLD_FILES+=usr/tests/sbin/md5/sha1.digest
+OLD_FILES+=usr/tests/sbin/md5/sha1sum.digest
+OLD_FILES+=usr/tests/sbin/md5/sha224.digest
+OLD_FILES+=usr/tests/sbin/md5/sha224sum.digest
+OLD_FILES+=usr/tests/sbin/md5/sha256.digest
+OLD_FILES+=usr/tests/sbin/md5/sha256sum.digest
+OLD_FILES+=usr/tests/sbin/md5/sha384.digest
+OLD_FILES+=usr/tests/sbin/md5/sha384sum.digest
+OLD_FILES+=usr/tests/sbin/md5/sha512.digest
+OLD_FILES+=usr/tests/sbin/md5/sha512sum.digest
+OLD_FILES+=usr/tests/sbin/md5/sha512t224.digest
+OLD_FILES+=usr/tests/sbin/md5/sha512t224sum.digest
+OLD_FILES+=usr/tests/sbin/md5/sha512t256.digest
+OLD_FILES+=usr/tests/sbin/md5/sha512t256sum.digest
+OLD_FILES+=usr/tests/sbin/md5/skein1024.digest
+OLD_FILES+=usr/tests/sbin/md5/skein1024sum.digest
+OLD_FILES+=usr/tests/sbin/md5/skein256.digest
+OLD_FILES+=usr/tests/sbin/md5/skein256sum.digest
+OLD_FILES+=usr/tests/sbin/md5/skein512.digest
+OLD_FILES+=usr/tests/sbin/md5/skein512sum.digest
+OLD_FILES+=usr/tests/sbin/md5/sum_a.in
+OLD_FILES+=usr/tests/sbin/md5/sum_b.in
+OLD_FILES+=usr/tests/sbin/md5/sum_c.in
+OLD_FILES+=usr/tests/sbin/md5/sum_sums.digest
+
 # 20230420: portsnap.8 removed
 OLD_FILES+=etc/portsnap.conf
 OLD_FILES+=usr/libexec/make_index
diff --git a/sbin/md5/Makefile b/sbin/md5/Makefile
index 6bda75437275..c9faec285aea 100644
--- a/sbin/md5/Makefile
+++ b/sbin/md5/Makefile
@@ -52,6 +52,12 @@ MLINKS=      md5.1 md5sum.1 \
        md5.1 skein1024.1 \
        md5.1 skein1024sum.1
 
+# md5 can also emulate the shasum script that comes with Perl, except
+# that, in bits input mode, it can only handle input lengths that are
+# a multiple of 8 (see manual page).
+#LINKS+= ${BINDIR}/md5 ${BINDIR}/shasum
+#MLINKS+= md5.1 shasum.1
+
 LIBADD=        md
 
 .ifndef(BOOTSTRAPPING)
diff --git a/sbin/md5/md5.1 b/sbin/md5/md5.1
index ba654e131c3c..bd619587e7a9 100644
--- a/sbin/md5/md5.1
+++ b/sbin/md5/md5.1
@@ -1,5 +1,5 @@
 .\" $FreeBSD$
-.Dd February 6, 2023
+.Dd April 12, 2023
 .Dt MD5 1
 .Os
 .Sh NAME
@@ -8,7 +8,8 @@
 .Nm rmd160 , skein256 , skein512 , skein1024 ,
 .Nm md5sum , sha1sum , sha224sum , sha256sum , sha384sum ,
 .Nm sha512sum , sha512t224sum , sha512t256sum ,
-.Nm rmd160sum , skein256sum , skein512sum , skein1024sum
+.Nm rmd160sum , skein256sum , skein512sum , skein1024sum ,
+.Nm shasum
 .Nd calculate a message-digest fingerprint (checksum) for a file
 .Sh SYNOPSIS
 .Nm
@@ -18,12 +19,40 @@
 .Op Ar
 .Pp
 .Nm md5sum
-.Op Fl pqrtx
-.Op Fl c Ar file
-.Op Fl s Ar string
+.Op Fl bctwz
+.Op Fl -binary
+.Op Fl -check
+.Op Fl -help
+.Op Fl -ignore-missing
+.Op Fl -quiet
+.Op Fl -status
+.Op Fl -strict
+.Op Fl -tag
+.Op Fl -text
+.Op Fl -version
+.Op Fl -warn
+.Op Fl -zero
 .Op Ar
 .Pp
 (All other hashes have the same options and usage.)
+.Pp
+.Nm shasum
+.Op Fl 0bchqstUvw
+.Op Fl -01
+.Op Fl a | -algorithm Ar alg
+.Op Fl -binary
+.Op Fl -check
+.Op Fl -help
+.Op Fl -ignore-missing
+.Op Fl -quiet
+.Op Fl -status
+.Op Fl -strict
+.Op Fl -tag
+.Op Fl -text
+.Op Fl -UNIVERSAL
+.Op Fl -version
+.Op Fl -warn
+.Op Ar
 .Sh DESCRIPTION
 The
 .Nm md5 , sha1 , sha224 , sha256 , sha384 , sha512 , sha512t224 , sha512t256 ,
@@ -36,15 +65,21 @@ output a
 or
 .Dq message digest
 of the input.
+.Pp
 The
 .Nm md5sum , sha1sum , sha224sum , sha256sum , sha384sum , sha512sum ,
 .Nm sha512t224sum , sha512t256sum , rmd160sum , skein256sum , skein512sum ,
 and
 .Nm skein1024sum
-utilities do the same, but default to the reversed format of
-the
-.Fl r
-flag.
+utilities do the same, but with command-line options and an output
+format that match those of their similary named GNU utilities.
+.Pp
+The
+.Nm shasum
+utility does the same, but with command-line options and an output
+format that match those of the similarly named utility that ships with
+Perl.
+.Pp
 It is conjectured that it is computationally infeasible to
 produce two messages having the same message digest, or to produce any
 message having a given prespecified target message digest.
@@ -75,73 +110,171 @@ to 224 bits.
 .Pp
 It is recommended that all new applications use SHA-512 or SKEIN-512
 instead of one of the other hash functions.
-.Pp
-The following options may be used in any combination and must
-precede any files named on the command line.
-The hexadecimal checksum of each file listed on the command line is printed
-after the options are processed.
+.Ss BSD OPTIONS
+The following options are available in BSD mode, i.e. when the program
+is invoked with a name that does not end in
+.Dq sum :
 .Bl -tag -width indent
-.It Fl b
-Make the
-.Nm -sum
-programs separate hash and digest with a blank followed by an asterisk instead
-of by 2 blank characters for full compatibility with the output generated by 
the
-coreutils versions of these programs.
-.It Fl c Ar string
-If the program was called with a name that does not end in
-.Nm sum ,
-compare the digest of the file against this string.
+.It Fl c Ar string , Fl -check= Ns Ar string
+Compare the digest of the file against this string.
 If combined with the
 .Fl q
+or
+.Fl -quiet
 option, the calculated digest is printed in addition to the exit status being 
set.
 .Pq Note that this option is not yet useful if multiple files are specified.
-.It Fl c Ar file
-If the program was called with a name that does end in
-.Nm sum ,
-the file passed as argument must contain digest lines generated by the same
-digest algorithm with or without the
-.Fl r
-option
-.Pq i.e., in either classical BSD format or in GNU coreutils format .
-A line with the file name followed by a colon
-.Dq ":"
-and either OK or FAILED is written for each well-formed line in the digest 
file.
-If applicable, the number of failed comparisons and the number of lines that 
were
-skipped since they were not well-formed are printed at the end.
-The
-.Fl q
-option can be used to quiesce the output unless there are mismatched entries in
-the digest.
-.Pp
-.It Fl s Ar string
-Print a checksum of the given
-.Ar string .
-.It Fl p
+.It Fl p , -passthrough
 Echo stdin to stdout and append the checksum to stdout.
-.It Fl q
+.It Fl q , -quiet
 Quiet mode \(em only the checksum is printed out.
 Overrides the
 .Fl r
+or
+.Fl -reverse
 option.
-.It Fl r
+.It Fl r , -reverse
 Reverses the format of the output.
 This helps with visual diffs.
 Does nothing
 when combined with the
 .Fl ptx
 options.
-.It Fl t
+.It Fl s Ar string , Fl -string= Ns Ar string
+Print a checksum of the given
+.Ar string .
+.It Fl t , Fl -time-trial
 Run a built-in time trial.
 For the
 .Nm -sum
 versions, this is a nop for compatibility with coreutils.
-.It Fl x
+.It Fl x , Fl -self-test
 Run a built-in test script.
 .El
+.Ss GNU OPTIONS
+The following options are available in GNU mode, i.e. when the program
+is invoked with a name that ends in
+.Dq sum :
+.Bl -tag -width indent
+.It Fl b , Fl -binary
+Read files in binary mode.
+.It Fl c , Fl -check
+The file passed as arguments must contain digest lines generated by the same
+digest algorithm in either classical BSD format or in GNU coreutils format.
+A line with the file name followed by a colon
+.Dq ":"
+and either OK or FAILED is written for each well-formed line in the digest 
file.
+If applicable, the number of failed comparisons and the number of lines that 
were
+skipped since they were not well-formed are printed at the end.
+The
+.Fl -quiet
+option can be used to quiesce the output unless there are mismatched entries in
+the digest.
+.It Fl -help
+Print a usage message and exit.
+.It Fl -ignore-missing
+When verifying checksums, ignore files for which checksums are given
+but which aren't found on disk.
+.It Fl -quiet
+When verifying checksums, do not print anything unless the
+verification fails.
+.It Fl -status
+When verifying checksums, do not print anything at all.
+The exit code will reflect whether verification succeeded.
+.It Fl -strict
+When verifying checksums, fail if the input is malformed.
+.It Fl -tag
+Produce BSD-style output.
+.It Fl t , Fl -text
+Read files in text mode.
+This is the default.
+Note that this implementation does not differentiate between binary
+and text mode.
+.It Fl -version
+Print version information and exit.
+.It Fl w , Fl -warn
+When verifying checksums, warn about malformed input.
+.It Fl z , Fl -zero
+Terminate output lines with NUL rather than with newline.
+.El
+.Ss PERL OPTIONS
+The following options are available in Perl mode, i.e. when the program
+is invoked with the name
+.Dq shasum :
+.Bl -tag -width indent
+.It Fl 0 , Fl -01
+Read files in bits mode: ASCII
+.Sq 0
+and
+.Sq 1
+characters correspond to 0 and 1 bits, respectively, and all other
+characters are ignored.
+See
+.Sx BUGS .
+.It Fl a Ar alg , Fl -algorithm Ar alg
+Use the specified algorithm:
+.Dq 1
+for SHA-1 (default),
+.Dq xxx
+for
+.Va xxx Ns -bit
+SHA-2 (e.g.
+.Dq 256
+for SHA-256)
+or
+.Dq xxxyyy
+for
+.Va xxx Ns -bit
+SHA-2 truncated to
+.Va yyy
+bits (e.g.
+.Dq 512224
+for SHA-512/224).
+.It Fl b , Fl -binary
+Read files in binary mode.
+.It Fl c , Fl -check
+The file passed as arguments must contain digest lines generated by the same
+digest algorithm in either classical BSD format or in GNU coreutils format.
+A line with the file name followed by a colon
+.Dq ":"
+and either OK or FAILED is written for each well-formed line in the digest 
file.
+If applicable, the number of failed comparisons and the number of lines that 
were
+skipped since they were not well-formed are printed at the end.
+The
+.Fl -quiet
+option can be used to quiesce the output unless there are mismatched entries in
+the digest.
+.It Fl -help
+Print a usage message and exit.
+.It Fl -ignore-missing
+When verifying checksums, ignore files for which checksums are given
+but which aren't found on disk.
+.It Fl -quiet
+When verifying checksums, do not print anything unless the
+verification fails.
+.It Fl -status
+When verifying checksums, do not print anything at all.
+The exit code will reflect whether verification succeeded.
+.It Fl -strict
+When verifying checksums, fail if the input is malformed.
+.It Fl -tag
+Produce BSD-style output.
+.It Fl t , Fl -text
+Read files in text mode.
+This is the default.
+Note that this implementation does not differentiate between binary
+and text mode.
+.It Fl U , Fl -UNIVERSAL
+Read files in universal mode: any CR-LF pair, as well as any CR not
+followed by LF, is translated to LF before the digest is computed.
+.It Fl -version
+Print version information and exit.
+.It Fl w , Fl -warn
+When verifying checksums, warn about malformed input.
+.El
 .Sh EXIT STATUS
 The
-.Nm md5 , sha1 , sha224 , sha256 , sha512 , sha512t256 , rmd160 ,
-.Nm skein256 , skein512 ,
+.Nm md5 , sha1 , sha224 , sha256 , sha512 , sha512t224 , sha512t256 ,
+.Nm rmd160 , skein256 , skein512 ,
 and
 .Nm skein1024
 utilities exit 0 on success,
@@ -149,6 +282,16 @@ utilities exit 0 on success,
 and 2 if at least one file does not have the same hash as the
 .Fl c
 option.
+.Pp
+The
+.Nm md5sum , sha1sum , sha224sum , sha256sum , sha512sum ,
+.Nm sha512t224sum , sha512t256sum ,
+.Nm rmd160 , skein256 , skein512 , skein1024
+and
+.Nm shasum
+utilities exit 0 on success and 1 if at least one of the input files
+could not be read or, when verifying checksums, does not have the
+expected checksum.
 .Sh EXAMPLES
 Calculate the MD5 checksum of the string
 .Dq Hello .
@@ -169,11 +312,22 @@ Calculate the checksum of multiple files reversing the 
output:
 $ md5 -r /boot/loader.conf /etc/rc.conf
 ada5f60f23af88ff95b8091d6d67bef6 /boot/loader.conf
 d80bf36c332dc0fdc479366ec3fa44cd /etc/rc.conf
-.Pd
-The
-.Nm -sum
-variants put 2 blank characters between hash and file name for full 
compatibility
-with the coreutils versions of these commands.
+.Ed
+.Pp
+This is almost but not quite identical to the output from GNU mode:
+.Bd -literal -offset indent
+$ md5sum /boot/loader.conf /etc/rc.conf
+ada5f60f23af88ff95b8091d6d67bef6  /boot/loader.conf
+d80bf36c332dc0fdc479366ec3fa44cd  /etc/rc.conf
+.Ed
+.Pp
+Note the two spaces between hash and file name.
+If binary mode is requested, they are instead separated by a space and
+an asterisk:
+.Bd -literal -offset indent
+$ md5sum -b /boot/loader.conf /etc/rc.conf
+ada5f60f23af88ff95b8091d6d67bef6 */boot/loader.conf
+d80bf36c332dc0fdc479366ec3fa44cd */etc/rc.conf
 .Ed
 .Pp
 Write the digest for
@@ -197,9 +351,7 @@ $ md5 -c randomstring /boot/loader.conf
 MD5 (/boot/loader.conf) = ada5f60f23af88ff95b8091d6d67bef6 [ Failed ]
 .Ed
 .Pp
-If invoked with a name ending in
-.Nm -sum
-the
+In GNU mode, the
 .Fl c
 option does not compare against a hash string passed as parameter.
 Instead, it expects a digest file, as created under the name
@@ -212,11 +364,12 @@ $ md5 -c digest /boot/loader.conf
 /boot/loader.conf: OK
 .Ed
 .Pp
-The digest file may contain any number of lines in the format generated with 
or without the
-.Fl r
-option
-.Pq i.e., in either classical BSD format or in GNU coreutils format .
-If a hash value does not match the file, FAILED is printed instead of OK.
+The digest file may contain any number of lines in the format
+generated in either BSD or GNU mode.
+If a hash value does not match the file,
+.Dq FAILED
+is printed instead of
+.Dq OK .
 .Sh SEE ALSO
 .Xr cksum 1 ,
 .Xr md5 3 ,
@@ -252,13 +405,29 @@ Secure Hash Standard (SHS):
 The RIPEMD-160 page:
 .Pa https://homes.esat.kuleuven.be/~bosselae/ripemd160.html
 .Sh BUGS
-All of the utilities that end in
-.Sq sum
-are intended to be compatible with the GNU coreutils programs.
-However, the long option functionality is not provided.
+In bits mode, the original
+.Nm shasum
+script is capable of processing inputs of arbitrary length.
+This implementation is not, and will issue an error if the input
+length is not a multiple of eight bits.
 .Sh ACKNOWLEDGMENTS
-This program is placed in the public domain for free general use by
-RSA Data Security.
+.An -nosplit
+This utility was originally derived from a program which was placed in
+the public domain for free general use by RSA Data Security.
 .Pp
-Support for SHA-1 and RIPEMD-160 has been added by
+Support for SHA-1 and RIPEMD-160 was added by
 .An Oliver Eikemeier Aq Mt e...@freebsd.org .
+.Pp
+Support for SHA-2 was added by
+.An Colin Percival Aq Mt cperc...@freebsd.org
+and
+.An Allan Jude Aq Mt allanj...@freebsd.org .
+.Pp
+Support for SKEIN was added by
+.An Allan Jude Aq Mt allanj...@freebsd.org .
+.Pp
+Compatibility with GNU coreutils was added by
+.An Warner Losh Aq Mt i...@freebsd.org
+and much expanded by
+.An Dag-Erling Sm\(/orgrav Aq Mt d...@freebsd.org ,
+who also added Perl compatibility.
diff --git a/sbin/md5/md5.c b/sbin/md5/md5.c
index 6bc1a780df86..98cfb37110d5 100644
--- a/sbin/md5/md5.c
+++ b/sbin/md5/md5.c
@@ -21,11 +21,13 @@
 __FBSDID("$FreeBSD$");
 
 #include <sys/param.h>
-#include <sys/time.h>
 #include <sys/resource.h>
+#include <sys/stat.h>
+#include <sys/time.h>
 
 #include <err.h>
 #include <fcntl.h>
+#include <getopt.h>
 #include <md5.h>
 #include <ripemd.h>
 #include <sha.h>
@@ -54,16 +56,20 @@ __FBSDID("$FreeBSD$");
 #define TEST_BLOCK_COUNT 100000
 #define MDTESTCOUNT 8
 
-static int bflag;
-static int cflag;
-static int pflag;
-static int qflag;
-static int rflag;
-static int sflag;
-static int skip;
+static char *progname;
+
+static bool cflag;
+static bool pflag;
+static bool qflag;
+static bool sflag;
+static bool wflag;
+static bool strict;
+static bool skip;
+static bool ignoreMissing;
 static char* checkAgainst;
 static int checksFailed;
-static int failed;
+static bool failed;
+static int endl = '\n';
 
 typedef void (DIGEST_Init)(void *);
 typedef void (DIGEST_Update)(void *, const unsigned char *, size_t);
@@ -84,21 +90,22 @@ extern const char *SKEIN1024_TestOutput[MDTESTCOUNT];
 
 typedef struct Algorithm_t {
        const char *progname;
+       const char *perlname;
        const char *name;
        const char *(*TestOutput)[MDTESTCOUNT];
        DIGEST_Init *Init;
        DIGEST_Update *Update;
        DIGEST_End *End;
        char *(*Data)(const void *, unsigned int, char *);
-       char *(*Fd)(int, char *);
 } Algorithm_t;
 
 static void MD5_Update(MD5_CTX *, const unsigned char *, size_t);
-static void MDOutput(const Algorithm_t *, char *, char **);
+static char *MDInput(const Algorithm_t *, FILE *, char *, bool);
+static void MDOutput(const Algorithm_t *, char *, const char *);
 static void MDTimeTrial(const Algorithm_t *);
 static void MDTestSuite(const Algorithm_t *);
-static char *MDFilter(const Algorithm_t *, char*, int);
 static void usage(const Algorithm_t *);
+static void version(void);
 
 typedef union {
        MD5_CTX md5;
@@ -121,47 +128,155 @@ typedef union {
 /* algorithm function table */
 
 static const struct Algorithm_t Algorithm[] = {
-       { "md5", "MD5", &MD5TestOutput, (DIGEST_Init*)&MD5Init,
+       { "md5", NULL, "MD5",
+               &MD5TestOutput, (DIGEST_Init*)&MD5Init,
                (DIGEST_Update*)&MD5_Update, (DIGEST_End*)&MD5End,
-               &MD5Data, &MD5Fd },
-       { "sha1", "SHA1", &SHA1_TestOutput, (DIGEST_Init*)&SHA1_Init,
+               &MD5Data },
+       { "sha1", "1", "SHA1",
+               &SHA1_TestOutput, (DIGEST_Init*)&SHA1_Init,
                (DIGEST_Update*)&SHA1_Update, (DIGEST_End*)&SHA1_End,
-               &SHA1_Data, &SHA1_Fd },
-       { "sha224", "SHA224", &SHA224_TestOutput, (DIGEST_Init*)&SHA224_Init,
+               &SHA1_Data },
+       { "sha224", "224", "SHA224",
+               &SHA224_TestOutput, (DIGEST_Init*)&SHA224_Init,
                (DIGEST_Update*)&SHA224_Update, (DIGEST_End*)&SHA224_End,
-               &SHA224_Data, &SHA224_Fd },
-       { "sha256", "SHA256", &SHA256_TestOutput, (DIGEST_Init*)&SHA256_Init,
+               &SHA224_Data },
+       { "sha256", "256", "SHA256",
+               &SHA256_TestOutput, (DIGEST_Init*)&SHA256_Init,
                (DIGEST_Update*)&SHA256_Update, (DIGEST_End*)&SHA256_End,
-               &SHA256_Data, &SHA256_Fd },
-       { "sha384", "SHA384", &SHA384_TestOutput, (DIGEST_Init*)&SHA384_Init,
+               &SHA256_Data },
+       { "sha384", "384", "SHA384",
+               &SHA384_TestOutput, (DIGEST_Init*)&SHA384_Init,
                (DIGEST_Update*)&SHA384_Update, (DIGEST_End*)&SHA384_End,
-               &SHA384_Data, &SHA384_Fd },
-       { "sha512", "SHA512", &SHA512_TestOutput, (DIGEST_Init*)&SHA512_Init,
+               &SHA384_Data },
+       { "sha512", "512", "SHA512",
+               &SHA512_TestOutput, (DIGEST_Init*)&SHA512_Init,
                (DIGEST_Update*)&SHA512_Update, (DIGEST_End*)&SHA512_End,
-               &SHA512_Data, &SHA512_Fd },
-       { "sha512t224", "SHA512t224", &SHA512t224_TestOutput, 
(DIGEST_Init*)&SHA512_224_Init,
+               &SHA512_Data },
+       { "sha512t224", "512224", "SHA512t224",
+               &SHA512t224_TestOutput, (DIGEST_Init*)&SHA512_224_Init,
                (DIGEST_Update*)&SHA512_224_Update, 
(DIGEST_End*)&SHA512_224_End,
-               &SHA512_224_Data, &SHA512_224_Fd },
-       { "sha512t256", "SHA512t256", &SHA512t256_TestOutput, 
(DIGEST_Init*)&SHA512_256_Init,
+               &SHA512_224_Data },
+       { "sha512t256", "512256", "SHA512t256",
+               &SHA512t256_TestOutput, (DIGEST_Init*)&SHA512_256_Init,
                (DIGEST_Update*)&SHA512_256_Update, 
(DIGEST_End*)&SHA512_256_End,
-               &SHA512_256_Data, &SHA512_256_Fd },
-       { "rmd160", "RMD160", &RIPEMD160_TestOutput,
+               &SHA512_256_Data },
+       { "rmd160", NULL, "RMD160",
+               &RIPEMD160_TestOutput,
                (DIGEST_Init*)&RIPEMD160_Init, 
(DIGEST_Update*)&RIPEMD160_Update,
-               (DIGEST_End*)&RIPEMD160_End, &RIPEMD160_Data, &RIPEMD160_Fd },
-       { "skein256", "Skein256", &SKEIN256_TestOutput,
+               (DIGEST_End*)&RIPEMD160_End, &RIPEMD160_Data },
+       { "skein256", NULL, "Skein256",
+               &SKEIN256_TestOutput,
                (DIGEST_Init*)&SKEIN256_Init, (DIGEST_Update*)&SKEIN256_Update,
-               (DIGEST_End*)&SKEIN256_End, &SKEIN256_Data, &SKEIN256_Fd },
-       { "skein512", "Skein512", &SKEIN512_TestOutput,
+               (DIGEST_End*)&SKEIN256_End, &SKEIN256_Data },
+       { "skein512", NULL, "Skein512",
+               &SKEIN512_TestOutput,
                (DIGEST_Init*)&SKEIN512_Init, (DIGEST_Update*)&SKEIN512_Update,
-               (DIGEST_End*)&SKEIN512_End, &SKEIN512_Data, &SKEIN512_Fd },
-       { "skein1024", "Skein1024", &SKEIN1024_TestOutput,
+               (DIGEST_End*)&SKEIN512_End, &SKEIN512_Data },
+       { "skein1024", NULL, "Skein1024",
+               &SKEIN1024_TestOutput,
                (DIGEST_Init*)&SKEIN1024_Init, 
(DIGEST_Update*)&SKEIN1024_Update,
-               (DIGEST_End*)&SKEIN1024_End, &SKEIN1024_Data, &SKEIN1024_Fd }
+               (DIGEST_End*)&SKEIN1024_End, &SKEIN1024_Data },
+       { }
 };
 
-static unsigned        digest;
-static unsigned        malformed;
-static bool    gnu_emu = false;
+static int digest = -1;
+static unsigned int malformed;
+
+static enum mode {
+       mode_bsd,
+       mode_gnu,
+       mode_perl,
+} mode = mode_bsd;
+
+static enum input_mode {
+       input_binary     = '*',
+       input_text       = ' ',
+       input_universal  = 'U',
+       input_bits       = '^',
+} input_mode = input_binary;
+
+static enum output_mode {
+       output_bare,
+       output_tagged,
+       output_reverse,
+       output_gnu,
+} output_mode = output_tagged;
+
+enum optval {
+       opt_end = -1,
+       /* ensure we don't collide with shortopts */
+       opt_dummy = CHAR_MAX,
+       /* BSD options */
+       opt_check,
+       opt_passthrough,
+       opt_quiet,
+       opt_reverse,
+       opt_string,
+       opt_time_trial,
+       opt_self_test,
+       /* GNU options */
+       opt_binary,
+       opt_help,
+       opt_ignore_missing,
+       opt_status,
+       opt_strict,
+       opt_tag,
+       opt_text,
+       opt_warn,
+       opt_version,
+       opt_zero,
+       /* Perl options */
+       opt_algorithm,
+       opt_bits,
+       opt_universal,
+};
+
+static const struct option bsd_longopts[] = {
+       { "check",              required_argument,      0, opt_check },
+       { "passthrough",        no_argument,            0, opt_passthrough },
+       { "quiet",              no_argument,            0, opt_quiet },
+       { "reverse",            no_argument,            0, opt_reverse },
+       { "string",             required_argument,      0, opt_string },
+       { "time-trial",         no_argument,            0, opt_time_trial },
+       { "self-test",          no_argument,            0, opt_self_test },
+       { }
+};
+static const char *bsd_shortopts = "bc:pqrs:tx";
+
+static const struct option gnu_longopts[] = {
+       { "binary",             no_argument,            0, opt_binary },
+       { "check",              no_argument,            0, opt_check },
+       { "help",               no_argument,            0, opt_help },
+       { "ignore-missing",     no_argument,            0, opt_ignore_missing },
+       { "quiet",              no_argument,            0, opt_quiet },
+       { "status",             no_argument,            0, opt_status },
+       { "strict",             no_argument,            0, opt_strict },
+       { "tag",                no_argument,            0, opt_tag },
+       { "text",               no_argument,            0, opt_text },
+       { "version",            no_argument,            0, opt_version },
+       { "warn",               no_argument,            0, opt_warn },
+       { "zero",               no_argument,            0, opt_zero },
+       { }
+};
+static const char *gnu_shortopts = "bctwz";
+
+static const struct option perl_longopts[] = {
+       { "algorithm",          required_argument,      0, opt_algorithm },
+       { "check",              required_argument,      0, opt_check },
+       { "help",               no_argument,            0, opt_help },
+       { "ignore-missing",     no_argument,            0, opt_ignore_missing },
+       { "quiet",              no_argument,            0, opt_quiet },
+       { "status",             no_argument,            0, opt_status },
+       { "strict",             no_argument,            0, opt_strict },
+       { "tag",                no_argument,            0, opt_tag },
+       { "text",               no_argument,            0, opt_text },
+       { "UNIVERSAL",          no_argument,            0, opt_universal },
+       { "version",            no_argument,            0, opt_version },
+       { "warn",               no_argument,            0, opt_warn },
+       { "01",                 no_argument,            0, opt_bits },
+       { }
+};
+static const char *perl_shortopts = "0a:bchqstUvw";
 
 static void
 MD5_Update(MD5_CTX *c, const unsigned char *data, size_t len)
@@ -177,60 +292,70 @@ struct chksumrec {
 
 static struct chksumrec *head = NULL;
 static struct chksumrec **next = &head;
+static unsigned int numrecs;
 
 #define PADDING        7       /* extra padding for "SHA512t256 (...) = ...\n" 
style */
 #define CHKFILELINELEN (HEX_DIGEST_LENGTH + MAXPATHLEN + PADDING)
 
-static int gnu_check(const char *checksumsfile)
+static void
+gnu_check(const char *checksumsfile)
 {
        FILE    *inp;
-       char    linebuf[CHKFILELINELEN];
-       int     linelen;
+       char    *linebuf = NULL;
+       size_t  linecap;
+       ssize_t linelen;
        int     lineno;
        char    *filename;
        char    *hashstr;
        struct chksumrec        *rec;
        const char      *digestname;
-       int     digestnamelen;
-       int     hashstrlen;
+       size_t  digestnamelen;
+       size_t  hashstrlen;
 
-       if ((inp = fopen(checksumsfile, "r")) == NULL)
+       if (strcmp(checksumsfile, "-") == 0)
+               inp = stdin;
+       else if ((inp = fopen(checksumsfile, "r")) == NULL)
                err(1, "%s", checksumsfile);
        digestname = Algorithm[digest].name;
        digestnamelen = strlen(digestname);
        hashstrlen = strlen(*(Algorithm[digest].TestOutput[0]));
-       lineno = 1;
-       while (fgets(linebuf, sizeof(linebuf), inp) != NULL) {
-               linelen = strlen(linebuf) - 1;
-               if (linelen <= 0)
-                       break;
-               if (linebuf[linelen] != '\n')
-                       errx(1, "malformed input line %d (len=%d)", lineno, 
linelen);
+       lineno = 0;
+       linecap = CHKFILELINELEN;
+       while ((linelen = getline(&linebuf, &linecap, inp)) > 0) {
+               lineno++;
+               while (linelen > 0 && linebuf[linelen - 1] == '\n')
+                       linelen--;
                linebuf[linelen] = '\0';
                filename = linebuf + digestnamelen + 2;
                hashstr = linebuf + linelen - hashstrlen;
                /*
                 * supported formats:
                 * BSD: <DigestName> (<Filename>): <Digest>
-                * GNU: <Digest> [ *]<Filename>
+                * GNU: <Digest> [ *U^]<Filename>
                 */
-               if (linelen >= digestnamelen + hashstrlen + 6 &&
+               if ((size_t)linelen >= digestnamelen + hashstrlen + 6 &&
                    strncmp(linebuf, digestname, digestnamelen) == 0 &&
                    strncmp(filename - 2, " (", 2) == 0 &&
-                   strncmp(hashstr - 4, ") = ", 4) == 0) {
+                   strncmp(hashstr - 4, ") = ", 4) == 0 &&
+                   strspn(hashstr, "0123456789ABCDEFabcdef") == hashstrlen) {
                        *(hashstr - 4) = '\0';
-               } else if (linelen >= hashstrlen + 3 &&
+               } else if ((size_t)linelen >= hashstrlen + 3 &&
+                   strspn(linebuf, "0123456789ABCDEFabcdef") == hashstrlen &&
                    linebuf[hashstrlen] == ' ') {
                        linebuf[hashstrlen] = '\0';
                        hashstr = linebuf;
                        filename = linebuf + hashstrlen + 1;
-                       if (*filename == ' ' || *filename == '*')
-                               filename++;
                } else {
+                       if (wflag) {
+                               warnx("%s: %d: improperly formatted "
+                                   "%s checksum line",
+                                   checksumsfile, lineno,
+                                   mode == mode_perl ? "SHA" : digestname);
+                       }
                        malformed++;
                        continue;
                }
-               rec = malloc(sizeof (*rec));
+               rec = malloc(sizeof(*rec));
                if (rec == NULL)
                        errx(1, "malloc failed");
                rec->chksum = strdup(hashstr);
@@ -240,10 +365,10 @@ static int gnu_check(const char *checksumsfile)
                rec->next = NULL;
                *next = rec;
                next = &rec->next;
-               lineno++;
+               numrecs++;
        }
-       fclose(inp);
-       return (lineno - 1);
+       if (inp != stdin)
+               fclose(inp);
 }
 
 /* Main driver.
@@ -261,18 +386,19 @@ main(int argc, char *argv[])
 #ifdef HAVE_CAPSICUM
        cap_rights_t    rights;
 #endif
-       int     ch, fd;
-       char   *p, *string;
+       const struct option *longopts;
*** 1830 LINES SKIPPED ***

Reply via email to