aboutsummaryrefslogtreecommitdiffstats
path: root/text_cmds/md5
diff options
context:
space:
mode:
authorCameron Katri <me@cameronkatri.com>2021-05-09 14:20:58 -0400
committerCameron Katri <me@cameronkatri.com>2021-05-09 14:20:58 -0400
commit5fd83771641d15c418f747bd343ba6738d3875f7 (patch)
tree5abf0f78f680d9837dbd93d4d4c3933bb7509599 /text_cmds/md5
downloadapple_cmds-5fd83771641d15c418f747bd343ba6738d3875f7.tar.gz
apple_cmds-5fd83771641d15c418f747bd343ba6738d3875f7.tar.zst
apple_cmds-5fd83771641d15c418f747bd343ba6738d3875f7.zip
Import macOS userland
adv_cmds-176 basic_cmds-55 bootstrap_cmds-116.100.1 developer_cmds-66 diskdev_cmds-667.40.1 doc_cmds-53.60.1 file_cmds-321.40.3 mail_cmds-35 misc_cmds-34 network_cmds-606.40.1 patch_cmds-17 remote_cmds-63 shell_cmds-216.60.1 system_cmds-880.60.2 text_cmds-106
Diffstat (limited to 'text_cmds/md5')
-rw-r--r--text_cmds/md5/commoncrypto.c108
-rw-r--r--text_cmds/md5/commoncrypto.h8
-rw-r--r--text_cmds/md5/md5.195
-rw-r--r--text_cmds/md5/md5.c419
4 files changed, 630 insertions, 0 deletions
diff --git a/text_cmds/md5/commoncrypto.c b/text_cmds/md5/commoncrypto.c
new file mode 100644
index 0000000..3551afe
--- /dev/null
+++ b/text_cmds/md5/commoncrypto.c
@@ -0,0 +1,108 @@
+/* Generic CommonDigest wrappers to match the semantics of libmd. */
+
+#include <dispatch/dispatch.h>
+#include <os/assumes.h>
+#include <errno.h>
+#include <fcntl.h>
+
+#include "commoncrypto.h"
+
+#define CHUNK_SIZE (10 * 1024 * 1024)
+
+char *
+Digest_End(CCDigestRef ctx, char *buf)
+{
+ static const char hex[] = "0123456789abcdef";
+ uint8_t digest[32]; // SHA256 is the biggest
+ size_t i, length;
+
+ (void)os_assumes_zero(CCDigestFinal(ctx, digest));
+ length = CCDigestOutputSize(ctx);
+ os_assert(length <= sizeof(digest));
+ for (i = 0; i < length; i++) {
+ buf[i+i] = hex[digest[i] >> 4];
+ buf[i+i+1] = hex[digest[i] & 0x0f];
+ }
+ buf[i+i] = '\0';
+ return buf;
+}
+
+char *
+Digest_Data(CCDigestAlg algorithm, const void *data, size_t len, char *buf)
+{
+ CCDigestCtx ctx;
+
+ (void)os_assumes_zero(CCDigestInit(algorithm, &ctx));
+ (void)os_assumes_zero(CCDigestUpdate(&ctx, data, len));
+ return Digest_End(&ctx, buf);
+}
+
+char *
+Digest_File(CCDigestAlg algorithm, const char *filename, char *buf)
+{
+ int fd;
+ __block CCDigestCtx ctx;
+ dispatch_queue_t queue;
+ dispatch_semaphore_t sema;
+ dispatch_io_t io;
+ __block int s_error = 0;
+ __block bool eof = false;
+ off_t chunk_offset;
+
+ /* dispatch_io_create_with_path requires an absolute path */
+ fd = open(filename, O_RDONLY);
+ if (fd < 0) {
+ return NULL;
+ }
+
+ (void)fcntl(fd, F_NOCACHE, 1);
+
+ (void)os_assumes_zero(CCDigestInit(algorithm, &ctx));
+
+ queue = dispatch_queue_create("com.apple.mtree.io", NULL);
+ os_assert(queue);
+ sema = dispatch_semaphore_create(0);
+ os_assert(sema);
+
+ io = dispatch_io_create(DISPATCH_IO_STREAM, fd, queue, ^(int error) {
+ if (error != 0) {
+ s_error = error;
+ }
+ (void)close(fd);
+ (void)dispatch_semaphore_signal(sema);
+ });
+ os_assert(io);
+ for (chunk_offset = 0; eof == false && s_error == 0; chunk_offset += CHUNK_SIZE) {
+ dispatch_io_read(io, chunk_offset, CHUNK_SIZE, queue, ^(bool done, dispatch_data_t data, int error) {
+ if (data != NULL) {
+ (void)dispatch_data_apply(data, ^(__unused dispatch_data_t region, __unused size_t offset, const void *buffer, size_t size) {
+ (void)os_assumes_zero(CCDigestUpdate(&ctx, buffer, size));
+ return (bool)true;
+ });
+ }
+
+ if (error != 0) {
+ s_error = error;
+ }
+
+ if (done) {
+ eof = (data == dispatch_data_empty);
+ dispatch_semaphore_signal(sema);
+ }
+ });
+ dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);
+ }
+ dispatch_release(io); // it will close on its own
+
+ (void)dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);
+
+ dispatch_release(queue);
+ dispatch_release(sema);
+
+ if (s_error != 0) {
+ errno = s_error;
+ return NULL;
+ }
+
+ return Digest_End(&ctx, buf);
+}
diff --git a/text_cmds/md5/commoncrypto.h b/text_cmds/md5/commoncrypto.h
new file mode 100644
index 0000000..ed10d9a
--- /dev/null
+++ b/text_cmds/md5/commoncrypto.h
@@ -0,0 +1,8 @@
+#include <CommonCrypto/CommonDigest.h>
+#include <CommonCrypto/CommonDigestSPI.h>
+
+char *Digest_End(CCDigestRef, char *);
+
+char *Digest_Data(CCDigestAlg, const void *, size_t, char *);
+
+char *Digest_File(CCDigestAlg, const char *, char *);
diff --git a/text_cmds/md5/md5.1 b/text_cmds/md5/md5.1
new file mode 100644
index 0000000..01df82b
--- /dev/null
+++ b/text_cmds/md5/md5.1
@@ -0,0 +1,95 @@
+.\" $FreeBSD: src/sbin/md5/md5.1,v 1.24 2005/03/10 09:56:39 cperciva Exp $
+.Dd June 6, 2004
+.Dt MD5 1
+.Os
+.Sh NAME
+.Nm md5
+.Nd calculate a message-digest fingerprint (checksum) for a file
+.Sh SYNOPSIS
+.Nm md5
+.Op Fl pqrtx
+.Op Fl s Ar string
+.Op Ar
+.Sh DESCRIPTION
+The
+.Nm
+utility takes as input a message of arbitrary length and produces as
+output a
+.Dq fingerprint
+or
+.Dq message digest
+of the input.
+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.
+The
+.Tn MD5
+algorithm is intended for digital signature applications, where a
+large file must be
+.Dq compressed
+in a secure manner before being encrypted with a private
+(secret)
+key under a public-key cryptosystem such as
+.Tn RSA .
+.Pp
+.Tn MD5's
+designer Ron Rivest has stated "md5 and sha1 are both clearly broken (in terms
+of collision-resistance)".
+So
+.Tn MD5
+should be avoided when creating new protocols, or implementing protocols with better options.
+.Tn SHA256
+and
+.Tn SHA512
+are better options as they have been more resilient to attacks (as of 2009).
+.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.
+.Bl -tag -width indent
+.It Fl s Ar string
+Print a checksum of the given
+.Ar string .
+.It Fl p
+Echo stdin to stdout and append the checksum to stdout.
+.It Fl q
+Quiet mode - only the checksum is printed out.
+Overrides the
+.Fl r
+option.
+.It Fl r
+Reverses the format of the output.
+This helps with visual diffs.
+Does nothing
+when combined with the
+.Fl ptx
+options.
+.It Fl t
+Run a built-in time trial.
+.It Fl x
+Run a built-in test script.
+.El
+.Sh EXIT STATUS
+The
+.Nm
+utility exits 0 on success,
+and 1 if at least one of the input files could not be read.
+.Sh SEE ALSO
+.Xr cksum 1 ,
+.Xr md5 3 ,
+.Xr ripemd 3 ,
+.Xr sha 3 ,
+.Xr CC_SHA256_Init 3
+.Rs
+.%A R. Rivest
+.%T The MD5 Message-Digest Algorithm
+.%O RFC1321
+.Rs
+.%A Vlastimil Klima
+.%T Finding MD5 Collisions - a Toy For a Notebook
+.%O Cryptology ePrint Archive: Report 2005/075
+.Re
+.Sh ACKNOWLEDGMENTS
+This program is placed in the public domain for free general use by
+RSA Data Security.
diff --git a/text_cmds/md5/md5.c b/text_cmds/md5/md5.c
new file mode 100644
index 0000000..9be53d3
--- /dev/null
+++ b/text_cmds/md5/md5.c
@@ -0,0 +1,419 @@
+/*
+ * Derived from:
+ *
+ * MDDRIVER.C - test driver for MD2, MD4 and MD5
+ */
+
+/*
+ * Copyright (C) 1990-2, RSA Data Security, Inc. Created 1990. All
+ * rights reserved.
+ *
+ * RSA Data Security, Inc. makes no representations concerning either
+ * the merchantability of this software or the suitability of this
+ * software for any particular purpose. It is provided "as is"
+ * without express or implied warranty of any kind.
+ *
+ * These notices must be retained in any copies of any part of this
+ * documentation and/or software.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD: src/sbin/md5/md5.c,v 1.34 2005/03/09 19:23:04 cperciva Exp $");
+
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <err.h>
+#ifndef __APPLE__
+#include <md5.h>
+#include <ripemd.h>
+#include <sha.h>
+#include <sha256.h>
+#endif /* !__APPLE__ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+#include <sysexits.h>
+
+#ifdef __APPLE__
+#include "commoncrypto.h"
+#endif /* __APPLE__ */
+
+/*
+ * Length of test block, number of test blocks.
+ */
+#define TEST_BLOCK_LEN 10000
+#define TEST_BLOCK_COUNT 100000
+#define MDTESTCOUNT 8
+
+int qflag;
+int rflag;
+int sflag;
+
+typedef void (DIGEST_Init)(void *);
+typedef void (DIGEST_Update)(void *, const unsigned char *, size_t);
+typedef char *(DIGEST_End)(void *, char *);
+
+extern const char *MD5TestOutput[MDTESTCOUNT];
+extern const char *SHA1_TestOutput[MDTESTCOUNT];
+extern const char *SHA256_TestOutput[MDTESTCOUNT];
+extern const char *RIPEMD160_TestOutput[MDTESTCOUNT];
+
+typedef struct Algorithm_t {
+ const char *progname;
+ const char *name;
+ const char *(*TestOutput)[MDTESTCOUNT];
+#ifdef __APPLE__
+ CCDigestAlg algorithm;
+#else /* !__APPLE__ */
+ DIGEST_Init *Init;
+ DIGEST_Update *Update;
+ DIGEST_End *End;
+ char *(*Data)(const unsigned char *, unsigned int, char *);
+ char *(*File)(const char *, char *);
+#endif /* __APPLE__ */
+} Algorithm_t;
+
+#ifndef __APPLE__
+static void MD5_Update(MD5_CTX *, const unsigned char *, size_t);
+#endif /* !__APPLE__ */
+static void MDString(Algorithm_t *, const char *);
+static void MDTimeTrial(Algorithm_t *);
+static void MDTestSuite(Algorithm_t *);
+static void MDFilter(Algorithm_t *, int);
+static void usage(Algorithm_t *);
+
+#ifdef __APPLE__
+typedef CCDigestCtx DIGEST_CTX;
+#else /* !__APPLE__ */
+typedef union {
+ MD5_CTX md5;
+ SHA1_CTX sha1;
+ SHA256_CTX sha256;
+ RIPEMD160_CTX ripemd160;
+} DIGEST_CTX;
+#endif /* __APPLE__ */
+
+/* max(MD5_DIGEST_LENGTH, SHA_DIGEST_LENGTH,
+ SHA256_DIGEST_LENGTH, RIPEMD160_DIGEST_LENGTH)*2+1 */
+#define HEX_DIGEST_LENGTH 65
+
+/* algorithm function table */
+
+struct Algorithm_t Algorithm[] = {
+#ifdef __APPLE__
+ { "md5", "MD5", &MD5TestOutput, kCCDigestMD5, },
+ { "sha1", "SHA1", &SHA1_TestOutput, kCCDigestSHA1 },
+ { "sha256", "SHA256", &SHA256_TestOutput, kCCDigestSHA256 },
+ { "rmd160", "RMD160", &RIPEMD160_TestOutput, kCCDigestRMD160 },
+#else
+ { "md5", "MD5", &MD5TestOutput, (DIGEST_Init*)&MD5Init,
+ (DIGEST_Update*)&MD5_Update, (DIGEST_End*)&MD5End,
+ &MD5Data, &MD5File },
+ { "sha1", "SHA1", &SHA1_TestOutput, (DIGEST_Init*)&SHA1_Init,
+ (DIGEST_Update*)&SHA1_Update, (DIGEST_End*)&SHA1_End,
+ &SHA1_Data, &SHA1_File },
+ { "sha256", "SHA256", &SHA256_TestOutput, (DIGEST_Init*)&SHA256_Init,
+ (DIGEST_Update*)&SHA256_Update, (DIGEST_End*)&SHA256_End,
+ &SHA256_Data, &SHA256_File },
+ { "rmd160", "RMD160", &RIPEMD160_TestOutput,
+ (DIGEST_Init*)&RIPEMD160_Init, (DIGEST_Update*)&RIPEMD160_Update,
+ (DIGEST_End*)&RIPEMD160_End, &RIPEMD160_Data, &RIPEMD160_File }
+#endif
+};
+
+#ifndef __APPLE__
+static void
+MD5_Update(MD5_CTX *c, const unsigned char *data, size_t len)
+{
+ MD5Update(c, data, len);
+}
+#endif /* !__APPLE__ */
+
+/* Main driver.
+
+Arguments (may be any combination):
+ -sstring - digests string
+ -t - runs time trial
+ -x - runs test script
+ filename - digests file
+ (none) - digests standard input
+ */
+int
+main(int argc, char *argv[])
+{
+ int ch;
+ char *p;
+ char buf[HEX_DIGEST_LENGTH];
+ int failed=0;
+ unsigned digest=0;
+ const char* progname;
+
+ if(*argv) {
+ if ((progname = strrchr(argv[0], '/')) == NULL)
+ progname = argv[0];
+ else
+ progname++;
+
+ for (digest = 0; digest < sizeof(Algorithm)/sizeof(*Algorithm); digest++)
+ if (strcasecmp(Algorithm[digest].progname, progname) == 0)
+ break;
+
+ if (digest == sizeof(Algorithm)/sizeof(*Algorithm))
+ digest = 0;
+ }
+
+ while ((ch = getopt(argc, argv, "pqrs:tx")) != -1)
+ switch (ch) {
+ case 'p':
+ MDFilter(&Algorithm[digest], 1);
+ break;
+ case 'q':
+ qflag = 1;
+ break;
+ case 'r':
+ rflag = 1;
+ break;
+ case 's':
+ sflag = 1;
+ MDString(&Algorithm[digest], optarg);
+ break;
+ case 't':
+ MDTimeTrial(&Algorithm[digest]);
+ break;
+ case 'x':
+ MDTestSuite(&Algorithm[digest]);
+ break;
+ default:
+ usage(&Algorithm[digest]);
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (*argv) {
+ do {
+#ifdef __APPLE__
+ p = Digest_File(Algorithm[digest].algorithm, *argv, buf);
+#else
+ p = Algorithm[digest].File(*argv, buf);
+#endif
+ if (!p) {
+ warn("%s", *argv);
+ failed++;
+ } else {
+ if (qflag)
+ printf("%s\n", p);
+ else if (rflag)
+ printf("%s %s\n", p, *argv);
+ else
+ printf("%s (%s) = %s\n", Algorithm[digest].name, *argv, p);
+ }
+ } while (*++argv);
+ } else if (!sflag && (optind == 1 || qflag || rflag))
+ MDFilter(&Algorithm[digest], 0);
+
+ if (failed != 0)
+ return (1);
+
+ return (0);
+}
+/*
+ * Digests a string and prints the result.
+ */
+static void
+MDString(Algorithm_t *alg, const char *string)
+{
+ size_t len = strlen(string);
+ char buf[HEX_DIGEST_LENGTH];
+
+ if (qflag)
+#ifdef __APPLE__
+ printf("%s\n", Digest_Data(alg->algorithm, string, len, buf));
+ else if (rflag)
+ printf("%s \"%s\"\n", Digest_Data(alg->algorithm, string, len, buf), string);
+ else
+ printf("%s (\"%s\") = %s\n", alg->name, string, Digest_Data(alg->algorithm, string, len, buf));
+#else /* !__APPLE__ */
+ printf("%s\n", alg->Data(string, len, buf));
+ else if (rflag)
+ printf("%s \"%s\"\n", alg->Data(string, len, buf), string);
+ else
+ printf("%s (\"%s\") = %s\n", alg->name, string, alg->Data(string, len, buf));
+#endif /* __APPLE__ */
+}
+/*
+ * Measures the time to digest TEST_BLOCK_COUNT TEST_BLOCK_LEN-byte blocks.
+ */
+static void
+MDTimeTrial(Algorithm_t *alg)
+{
+ DIGEST_CTX context;
+ struct rusage before, after;
+ struct timeval total;
+ float seconds;
+ unsigned char block[TEST_BLOCK_LEN];
+ unsigned int i;
+ char *p, buf[HEX_DIGEST_LENGTH];
+
+ printf
+ ("%s time trial. Digesting %d %d-byte blocks ...",
+ alg->name, TEST_BLOCK_COUNT, TEST_BLOCK_LEN);
+ fflush(stdout);
+
+ /* Initialize block */
+ for (i = 0; i < TEST_BLOCK_LEN; i++)
+ block[i] = (unsigned char) (i & 0xff);
+
+ /* Start timer */
+ getrusage(0, &before);
+
+ /* Digest blocks */
+#ifdef __APPLE__
+ CCDigestInit(alg->algorithm, &context);
+ for (i = 0; i < TEST_BLOCK_COUNT; i++)
+ CCDigestUpdate(&context, block, TEST_BLOCK_LEN);
+ p = Digest_End(&context, buf);
+#else
+ alg->Init(&context);
+ for (i = 0; i < TEST_BLOCK_COUNT; i++)
+ alg->Update(&context, block, TEST_BLOCK_LEN);
+ p = alg->End(&context, buf);
+#endif
+
+ /* Stop timer */
+ getrusage(0, &after);
+ timersub(&after.ru_utime, &before.ru_utime, &total);
+ seconds = total.tv_sec + (float) total.tv_usec / 1000000;
+
+ printf(" done\n");
+ printf("Digest = %s", p);
+ printf("\nTime = %f seconds\n", seconds);
+ printf
+ ("Speed = %f bytes/second\n",
+ (float) TEST_BLOCK_LEN * (float) TEST_BLOCK_COUNT / seconds);
+}
+/*
+ * Digests a reference suite of strings and prints the results.
+ */
+
+const char *MDTestInput[MDTESTCOUNT] = {
+ "",
+ "a",
+ "abc",
+ "message digest",
+ "abcdefghijklmnopqrstuvwxyz",
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789",
+ "12345678901234567890123456789012345678901234567890123456789012345678901234567890",
+ "MD5 has not yet (2001-09-03) been broken, but sufficient attacks have been made \
+that its security is in some doubt"
+};
+
+const char *MD5TestOutput[MDTESTCOUNT] = {
+ "d41d8cd98f00b204e9800998ecf8427e",
+ "0cc175b9c0f1b6a831c399e269772661",
+ "900150983cd24fb0d6963f7d28e17f72",
+ "f96b697d7cb7938d525a2f31aaf161d0",
+ "c3fcd3d76192e4007dfb496cca67e13b",
+ "d174ab98d277d9f5a5611c2c9f419d9f",
+ "57edf4a22be3c955ac49da2e2107b67a",
+ "b50663f41d44d92171cb9976bc118538"
+};
+
+const char *SHA1_TestOutput[MDTESTCOUNT] = {
+ "da39a3ee5e6b4b0d3255bfef95601890afd80709",
+ "86f7e437faa5a7fce15d1ddcb9eaeaea377667b8",
+ "a9993e364706816aba3e25717850c26c9cd0d89d",
+ "c12252ceda8be8994d5fa0290a47231c1d16aae3",
+ "32d10c7b8cf96570ca04ce37f2a19d84240d3a89",
+ "761c457bf73b14d27e9e9265c46f4b4dda11f940",
+ "50abf5706a150990a08b2c5ea40fa0e585554732",
+ "18eca4333979c4181199b7b4fab8786d16cf2846"
+};
+
+const char *SHA256_TestOutput[MDTESTCOUNT] = {
+ "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
+ "ca978112ca1bbdcafac231b39a23dc4da786eff8147c4e72b9807785afee48bb",
+ "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad",
+ "f7846f55cf23e14eebeab5b4e1550cad5b509e3348fbc4efa3a1413d393cb650",
+ "71c480df93d6ae2f1efad1447c66c9525e316218cf51fc8d9ed832f2daf18b73",
+ "db4bfcbd4da0cd85a60c3c37d3fbd8805c77f15fc6b1fdfe614ee0a7c8fdb4c0",
+ "f371bc4a311f2b009eef952dd83ca80e2b60026c8e935592d0f9c308453c813e",
+ "e6eae09f10ad4122a0e2a4075761d185a272ebd9f5aa489e998ff2f09cbfdd9f"
+};
+
+const char *RIPEMD160_TestOutput[MDTESTCOUNT] = {
+ "9c1185a5c5e9fc54612808977ee8f548b2258d31",
+ "0bdc9d2d256b3ee9daae347be6f4dc835a467ffe",
+ "8eb208f7e05d987a9b044a8e98c6b087f15a0bfc",
+ "5d0689ef49d2fae572b881b123a85ffa21595f36",
+ "f71c27109c692c1b56bbdceb5b9d2865b3708dbc",
+ "b0e20b6e3116640286ed3a87a5713079b21f5189",
+ "9b752e45573d4b39f4dbd3323cab82bf63326bfb",
+ "5feb69c6bf7c29d95715ad55f57d8ac5b2b7dd32"
+};
+
+static void
+MDTestSuite(Algorithm_t *alg)
+{
+ int i;
+ char buffer[HEX_DIGEST_LENGTH];
+
+ printf("%s test suite:\n", alg->name);
+ for (i = 0; i < MDTESTCOUNT; i++) {
+#ifdef __APPLE__
+ Digest_Data(alg->algorithm, MDTestInput[i], strlen(MDTestInput[i]), buffer);
+#else
+ (*alg->Data)(MDTestInput[i], strlen(MDTestInput[i]), buffer);
+#endif
+ printf("%s (\"%s\") = %s", alg->name, MDTestInput[i], buffer);
+ if (strcmp(buffer, (*alg->TestOutput)[i]) == 0)
+ printf(" - verified correct\n");
+ else
+ printf(" - INCORRECT RESULT!\n");
+ }
+}
+
+/*
+ * Digests the standard input and prints the result.
+ */
+static void
+MDFilter(Algorithm_t *alg, int tee)
+{
+ DIGEST_CTX context;
+ unsigned int len;
+ unsigned char buffer[BUFSIZ];
+ char buf[HEX_DIGEST_LENGTH];
+
+#ifdef __APPLE__
+ CCDigestInit(alg->algorithm, &context);
+#else
+ alg->Init(&context);
+#endif
+ while ((len = fread(buffer, 1, BUFSIZ, stdin))) {
+ if (tee && len != fwrite(buffer, 1, len, stdout))
+ err(1, "stdout");
+#ifdef __APPLE__
+ CCDigestUpdate(&context, buffer, len);
+#else
+ alg->Update(&context, buffer, len);
+#endif
+ }
+ if (ferror(stdin)) {
+ errx(EX_IOERR, NULL);
+ }
+#ifdef __APPLE__
+ printf("%s\n", Digest_End(&context, buf));
+#else
+ printf("%s\n", alg->End(&context, buf));
+#endif
+}
+
+static void
+usage(Algorithm_t *alg)
+{
+
+ fprintf(stderr, "usage: %s [-pqrtx] [-s string] [files ...]\n", alg->progname);
+ exit(1);
+}