summaryrefslogtreecommitdiffstats
path: root/src/kmk/kmkbuiltin/md5sum.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/kmk/kmkbuiltin/md5sum.c')
-rw-r--r--src/kmk/kmkbuiltin/md5sum.c874
1 files changed, 874 insertions, 0 deletions
diff --git a/src/kmk/kmkbuiltin/md5sum.c b/src/kmk/kmkbuiltin/md5sum.c
new file mode 100644
index 0000000..dc8d497
--- /dev/null
+++ b/src/kmk/kmkbuiltin/md5sum.c
@@ -0,0 +1,874 @@
+/* $Id: md5sum.c 3219 2018-03-30 22:30:15Z bird $ */
+/** @file
+ * md5sum.
+ */
+
+/*
+ * Copyright (c) 2007-2010 knut st. osmundsen <bird-kBuild-spamx@anduin.net>
+ *
+ * This file is part of kBuild.
+ *
+ * kBuild is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * kBuild is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with kBuild. If not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+/*******************************************************************************
+* Header Files *
+*******************************************************************************/
+#include "config.h"
+#include <string.h>
+#include <stdio.h>
+#include <errno.h>
+#include <fcntl.h>
+#ifdef _MSC_VER
+# include <io.h>
+#else
+# include <unistd.h>
+#endif
+#include <sys/stat.h>
+#include "err.h"
+#include "kmkbuiltin.h"
+#include "../../lib/md5.h"
+#include <k/kTypes.h>
+
+/*#define MD5SUM_USE_STDIO*/
+
+
+/**
+ * Prints the usage and return 1.
+ */
+static int usage(PKMKBUILTINCTX pCtx, int fIsErr)
+{
+ kmk_builtin_ctx_printf(pCtx, fIsErr,
+ "usage: md5sum [-bt] [-o list-file] file(s)\n"
+ " or: md5sum [-btwq] -c list-file(s)\n"
+ " or: md5sum [-btq] -C MD5 file\n"
+ "\n"
+ " -c, --check Check MD5 and files found in the specified list file(s).\n"
+ " The default is to compute MD5 sums of the specified files\n"
+ " and print them to stdout in list form.\n"
+ " -C, --check-file This is followed by an MD5 sum and the file to check.\n"
+ " -b, --binary Read files in binary mode. (default)\n"
+ " -t, --text Read files in text mode.\n"
+ " -m, --manifest Output in kBuild fetch 'manifest' format.\n"
+ " -p, --progress Show progress indicator on large files.\n"
+ " -o, --output Name of the output list file. Useful with -p.\n"
+ " -q, --status Be quiet.\n"
+ " -w, --warn Ignored. Always warn, unless quiet.\n"
+ " -h, --help This usage info.\n"
+ " -v, --version Show version information and exit.\n"
+ );
+ return 1;
+}
+
+
+/**
+ * Makes a string out of the given digest.
+ *
+ * @param pDigest The MD5 digest.
+ * @param pszDigest Where to put the digest string. Must be able to
+ * hold at least 33 bytes.
+ */
+static void digest_to_string(unsigned char pDigest[16], char *pszDigest)
+{
+ unsigned i;
+ for (i = 0; i < 16; i++)
+ {
+ static char s_achDigits[17] = "0123456789abcdef";
+ pszDigest[i*2] = s_achDigits[(pDigest[i] >> 4)];
+ pszDigest[i*2 + 1] = s_achDigits[(pDigest[i] & 0xf)];
+ }
+ pszDigest[i*2] = '\0';
+}
+
+
+/**
+ * Attempts to convert a string to a MD5 digest.
+ *
+ * @returns 0 on success, 1-based position of the failure first error.
+ * @param pszDigest The string to interpret.
+ * @param pDigest Where to put the MD5 digest.
+ */
+static int string_to_digest(const char *pszDigest, unsigned char pDigest[16])
+{
+ unsigned i;
+ unsigned iBase = 1;
+
+ /* skip blanks */
+ while ( *pszDigest == ' '
+ || *pszDigest == '\t'
+ || *pszDigest == '\n'
+ || *pszDigest == '\r')
+ pszDigest++, iBase++;
+
+ /* convert the digits. */
+ memset(pDigest, 0, 16);
+ for (i = 0; i < 32; i++, pszDigest++)
+ {
+ int iDigit;
+ if (*pszDigest >= '0' && *pszDigest <= '9')
+ iDigit = *pszDigest - '0';
+ else if (*pszDigest >= 'a' && *pszDigest <= 'f')
+ iDigit = *pszDigest - 'a' + 10;
+ else if (*pszDigest >= 'A' && *pszDigest <= 'F')
+ iDigit = *pszDigest - 'A' + 10;
+ else
+ return i + iBase;
+ if (i & 1)
+ pDigest[i >> 1] |= iDigit;
+ else
+ pDigest[i >> 1] |= iDigit << 4;
+ }
+
+ /* the rest of the string must now be blanks. */
+ while ( *pszDigest == ' '
+ || *pszDigest == '\t'
+ || *pszDigest == '\n'
+ || *pszDigest == '\r')
+ pszDigest++, i++;
+
+ return *pszDigest ? i + iBase : 0;
+}
+
+
+/**
+ * Opens the specified file for md5 sum calculation.
+ *
+ * @returns Opaque pointer on success, NULL and errno on failure.
+ * @param pszFilename The filename.
+ * @param fText Whether text or binary mode should be used.
+ */
+static void *open_file(const char *pszFilename, unsigned fText)
+{
+#if defined(MD5SUM_USE_STDIO)
+ FILE *pFile;
+
+ errno = 0;
+ pFile = fopen(pszFilename,
+ fText ? "r" KMK_FOPEN_NO_INHERIT_MODE
+ : "rb" KMK_FOPEN_NO_INHERIT_MODE);
+ if (!pFile && errno == EINVAL && !fText)
+ pFile = fopen(pszFilename, "r" KMK_FOPEN_NO_INHERIT_MODE);
+ return pFile;
+
+#else
+ int fd;
+ int fFlags;
+
+ /* figure out the appropriate flags. */
+ fFlags = O_RDONLY | KMK_OPEN_NO_INHERIT;
+#ifdef O_SEQUENTIAL
+ fFlags |= _O_SEQUENTIAL;
+#elif defined(_O_SEQUENTIAL)
+ fFlags |= _O_SEQUENTIAL;
+#endif
+#ifdef O_BINARY
+ if (!fText) fFlags |= O_BINARY;
+#elif defined(_O_BINARY)
+ if (!fText) fFlags |= _O_BINARY;
+#endif
+#ifdef O_TEXT
+ if (fText) fFlags |= O_TEXT;
+#elif defined(O_TEXT)
+ if (fText) fFlags |= _O_TEXT;
+#else
+ (void)fText;
+#endif
+
+ errno = 0;
+ fd = open(pszFilename, fFlags, 0755);
+ if (fd >= 0)
+ {
+ int *pFd = malloc(sizeof(*pFd));
+ if (pFd)
+ {
+ *pFd = fd;
+ return pFd;
+ }
+ close(fd);
+ errno = ENOMEM;
+ }
+
+ return NULL;
+#endif
+}
+
+
+/**
+ * Closes a file opened by open_file.
+ *
+ * @param pvFile The opaque pointer returned by open_file.
+ */
+static void close_file(void *pvFile)
+{
+#if defined(MD5SUM_USE_STDIO)
+ fclose((FILE *)pvFile);
+#else
+ close(*(int *)pvFile);
+ free(pvFile);
+#endif
+}
+
+
+/**
+ * Reads from a file opened by open_file.
+ *
+ * @returns Number of bytes read on success.
+ * 0 on EOF.
+ * Negated errno on read error.
+ * @param pvFile The opaque pointer returned by open_file.
+ * @param pvBuf Where to put the number of read bytes.
+ * @param cbBuf The max number of bytes to read.
+ * Must be less than a INT_MAX.
+ */
+static int read_file(void *pvFile, void *pvBuf, size_t cbBuf)
+{
+#if defined(MD5SUM_USE_STDIO)
+ int cb;
+
+ errno = 0;
+ cb = (int)fread(pvBuf, 1, cbBuf, (FILE *)pvFile);
+ if (cb >= 0)
+ return (int)cb;
+ if (!errno)
+ return -EINVAL;
+ return -errno;
+#else
+ int cb;
+
+ errno = 0;
+ cb = (int)read(*(int *)pvFile, pvBuf, (int)cbBuf);
+ if (cb >= 0)
+ return (int)cb;
+ if (!errno)
+ return -EINVAL;
+ return -errno;
+#endif
+}
+
+
+/**
+ * Gets the size of the file.
+ * This is informational and not necessarily 100% accurate.
+ *
+ * @returns File size.
+ * @param pvFile The opaque pointer returned by open_file
+ */
+static KU64 size_file(void *pvFile)
+{
+#if defined(_MSC_VER)
+ __int64 cb;
+# if defined(MD5SUM_USE_STDIO)
+ cb = _filelengthi64(fileno((FILE *)pvFile));
+# else
+ cb = _filelengthi64(*(int *)pvFile);
+# endif
+ if (cb >= 0)
+ return cb;
+
+#elif defined(MD5SUM_USE_STDIO)
+ struct stat st;
+ if (!fstat(fileno((FILE *)pvFile), &st))
+ return st.st_size;
+
+#else
+ struct stat st;
+ if (!fstat(*(int *)pvFile, &st))
+ return st.st_size;
+#endif
+ return 1024;
+}
+
+
+/**
+ * Calculates the md5sum of the sepecified file stream.
+ *
+ * @returns errno on failure, 0 on success.
+ * @param pvFile The file stream.
+ * @param pDigest Where to store the MD5 digest.
+ * @param fProgress Whether to show a progress bar.
+ * @param pcbFile Where to return the file size. Optional.
+ */
+static int calc_md5sum(void *pvFile, unsigned char pDigest[16], unsigned fProgress, KU64 *pcbFile)
+{
+ int cb;
+ int rc = 0;
+ struct MD5Context Ctx;
+ unsigned uPercent = 0;
+ KU64 off = 0;
+ KU64 const cbFile = size_file(pvFile);
+
+ /* Get a decent sized buffer assuming we'll be spending more time reading
+ from the storage than doing MD5 sums. (2MB was choosen based on recent
+ SATA storage benchmarks which used that block size for sequential
+ tests.) We align the buffer address on a 16K boundrary to avoid most
+ transfer alignment issues. */
+ char *pabBufAligned;
+ size_t const cbBufAlign = 16*1024 - 1;
+ size_t const cbBufMax = 2048*1024;
+ size_t cbBuf = cbFile >= cbBufMax ? cbBufMax : ((size_t)cbFile + cbBufAlign) & ~(size_t)cbBufAlign;
+ char *pabBuf = (char *)malloc(cbBuf + cbBufAlign);
+ if (pabBuf)
+ pabBufAligned = (char *)(((uintptr_t)pabBuf + cbBufAlign) & ~(uintptr_t)cbBufAlign );
+ else
+ {
+ do
+ {
+ cbBuf /= 2;
+ pabBuf = (char *)malloc(cbBuf);
+ } while (!pabBuf && cbBuf > 4096);
+ if (!pabBuf)
+ return ENOMEM;
+ pabBufAligned = pabBuf;
+ }
+
+ if (cbFile < cbBuf * 4)
+ fProgress = 0;
+
+ MD5Init(&Ctx);
+ for (;;)
+ {
+ /* process a chunk. */
+ cb = read_file(pvFile, pabBufAligned, cbBuf);
+ if (cb > 0)
+ MD5Update(&Ctx, (unsigned char *)pabBufAligned, cb);
+ else if (!cb)
+ break;
+ else
+ {
+ rc = -cb;
+ break;
+ }
+ off += cb;
+
+ /* update the progress indicator. */
+ if (fProgress)
+ {
+ unsigned uNewPercent;
+ uNewPercent = (unsigned)(((double)off / cbFile) * 100);
+ if (uNewPercent != uPercent)
+ {
+ if (uPercent)
+ printf("\b\b\b\b");
+ printf("%3d%%", uNewPercent);
+ fflush(stdout);
+ uPercent = uNewPercent;
+ }
+ }
+ }
+ MD5Final(pDigest, &Ctx);
+
+ if (pcbFile)
+ *pcbFile = off;
+
+ if (fProgress)
+ printf("\b\b\b\b \b\b\b\b");
+
+ free(pabBuf);
+ return rc;
+}
+
+
+/**
+ * Checks the if the specified digest matches the digest of the file stream.
+ *
+ * @returns 0 on match, -1 on mismatch, errno value (positive) on failure.
+ * @param pvFile The file stream.
+ * @param Digest The MD5 digest.
+ * @param fProgress Whether to show an progress indicator on large files.
+ */
+static int check_md5sum(void *pvFile, unsigned char Digest[16], unsigned fProgress)
+{
+ unsigned char DigestFile[16];
+ int rc;
+
+ rc = calc_md5sum(pvFile, DigestFile, fProgress, NULL);
+ if (!rc)
+ rc = memcmp(Digest, DigestFile, 16) ? -1 : 0;
+ return rc;
+}
+
+
+/**
+ * Checks if the specified file matches the given MD5 digest.
+ *
+ * @returns 0 if it matches, 1 if it doesn't or an error occurs.
+ * @param pCtx The command execution context.
+ * @param pszFilename The name of the file to check.
+ * @param pszDigest The MD5 digest string.
+ * @param fText Whether to open the file in text or binary mode.
+ * @param fQuiet Whether to go about this in a quiet fashion or not.
+ * @param fProgress Whether to show an progress indicator on large files.
+ */
+static int check_one_file(PKMKBUILTINCTX pCtx, const char *pszFilename, const char *pszDigest, unsigned fText,
+ unsigned fQuiet, unsigned fProgress)
+{
+ unsigned char Digest[16];
+ int rc;
+
+ rc = string_to_digest(pszDigest, Digest);
+ if (!rc)
+ {
+ void *pvFile;
+
+ pvFile = open_file(pszFilename, fText);
+ if (pvFile)
+ {
+ if (!fQuiet)
+ kmk_builtin_ctx_printf(pCtx, 0, "%s: ", pszFilename);
+ rc = check_md5sum(pvFile, Digest, fProgress);
+ close_file(pvFile);
+ if (!fQuiet)
+ {
+ kmk_builtin_ctx_printf(pCtx, 0, "%s\n", !rc ? "OK" : rc < 0 ? "FAILURE" : "ERROR");
+ if (rc > 0)
+ errx(pCtx, 1, "Error reading '%s': %s", pszFilename, strerror(rc));
+ }
+ if (rc)
+ rc = 1;
+ }
+ else
+ {
+ if (!fQuiet)
+ errx(pCtx, 1, "Failed to open '%s': %s", pszFilename, strerror(errno));
+ rc = 1;
+ }
+ }
+ else
+ {
+ errx(pCtx, 1, "Malformed MD5 digest '%s'!", pszDigest);
+ errx(pCtx, 1, " %*s^", rc - 1, "");
+ rc = 1;
+ }
+
+ return rc;
+}
+
+
+/**
+ * Checks the specified md5.lst file.
+ *
+ * @returns 0 if all checks out file, 1 if one or more fails or there are read errors.
+ * @param pCtx The command execution context.
+ * @param pszFilename The name of the file.
+ * @param fText The default mode, text or binary. Only used when fBinaryTextOpt is true.
+ * @param fBinaryTextOpt Whether a -b or -t option was specified and should be used.
+ * @param fQuiet Whether to be quiet.
+ * @param fProgress Whether to show an progress indicator on large files.
+ */
+static int check_files(PKMKBUILTINCTX pCtx, const char *pszFilename, int fText, int fBinaryTextOpt,
+ int fQuiet, unsigned fProgress)
+{
+ int rc = 0;
+ FILE *pFile;
+
+ /*
+ * Try open the md5.lst file and process it line by line.
+ */
+ pFile = fopen(pszFilename, "r" KMK_FOPEN_NO_INHERIT_MODE);
+ if (pFile)
+ {
+ int iLine = 0;
+ char szLine[8192];
+ while (fgets(szLine, sizeof(szLine), pFile))
+ {
+ const char *pszDigest;
+ int fLineText;
+ char *psz;
+ int rc2;
+
+ iLine++;
+ psz = szLine;
+
+ /* leading blanks */
+ while (*psz == ' ' || *psz == '\t' || *psz == '\n')
+ psz++;
+
+ /* skip blank or comment lines. */
+ if (!*psz || *psz == '#' || *psz == ';' || *psz == '/')
+ continue;
+
+ /* remove the trailing newline. */
+ rc2 = (int)strlen(psz);
+ if (psz[rc2 - 1] == '\n')
+ psz[rc2 - (rc2 >= 2 && psz[rc2 - 2] == '\r' ? 2 : 1)] = '\0';
+
+ /* skip to the end of the digest and terminate it. */
+ pszDigest = psz;
+ while (*psz != ' ' && *psz != '\t' && *psz)
+ psz++;
+ if (*psz)
+ {
+ *psz++ = '\0';
+
+ /* blanks */
+ while (*psz == ' ' || *psz == '\t' || *psz == '\n')
+ psz++;
+
+ /* check for binary asterix */
+ if (*psz != '*')
+ fLineText = fBinaryTextOpt ? fText : 0;
+ else
+ {
+ fLineText = 0;
+ psz++;
+ }
+ if (*psz)
+ {
+ unsigned char Digest[16];
+
+ /* the rest is filename. */
+ pszFilename = psz;
+
+ /*
+ * Do the job.
+ */
+ rc2 = string_to_digest(pszDigest, Digest);
+ if (!rc2)
+ {
+ void *pvFile = open_file(pszFilename, fLineText);
+ if (pvFile)
+ {
+ if (!fQuiet)
+ kmk_builtin_ctx_printf(pCtx, 0, "%s: ", pszFilename);
+ rc2 = check_md5sum(pvFile, Digest, fProgress);
+ close_file(pvFile);
+ if (!fQuiet)
+ {
+ kmk_builtin_ctx_printf(pCtx, 0, "%s\n", !rc2 ? "OK" : rc2 < 0 ? "FAILURE" : "ERROR");
+ if (rc2 > 0)
+ errx(pCtx, 1, "Error reading '%s': %s", pszFilename, strerror(rc2));
+ }
+ if (rc2)
+ rc = 1;
+ }
+ else
+ {
+ if (!fQuiet)
+ errx(pCtx, 1, "Failed to open '%s': %s", pszFilename, strerror(errno));
+ rc = 1;
+ }
+ }
+ else if (!fQuiet)
+ {
+ errx(pCtx, 1, "%s (%d): Ignoring malformed digest '%s' (digest)", pszFilename, iLine, pszDigest);
+ errx(pCtx, 1, "%s (%d): %*s^", pszFilename, iLine, rc2 - 1, "");
+ }
+ }
+ else if (!fQuiet)
+ errx(pCtx, 1, "%s (%d): Ignoring malformed line!", pszFilename, iLine);
+ }
+ else if (!fQuiet)
+ errx(pCtx, 1, "%s (%d): Ignoring malformed line!", pszFilename, iLine);
+ } /* while more lines */
+
+ fclose(pFile);
+ }
+ else
+ {
+ errx(pCtx, 1, "Failed to open '%s': %s", pszFilename, strerror(errno));
+ rc = 1;
+ }
+
+ return rc;
+}
+
+
+/**
+ * Calculates the MD5 sum for one file and prints it.
+ *
+ * @returns 0 on success, 1 on any kind of failure.
+ * @param pCtx Command context.
+ * @param pszFilename The file to process.
+ * @param fText The mode to open the file in.
+ * @param fQuiet Whether to be quiet or verbose about errors.
+ * @param fManifest Whether to format the output like a fetch manifest.
+ * @param fProgress Whether to show an progress indicator on large files.
+ * @param pOutput Where to write the list. Progress is always written to stdout.
+ */
+static int md5sum_file(PKMKBUILTINCTX pCtx, const char *pszFilename, unsigned fText, unsigned fQuiet, unsigned fProgress,
+ unsigned fManifest, FILE *pOutput)
+{
+ int rc;
+ void *pvFile;
+
+ /*
+ * Calculate and print the MD5 sum for one file.
+ */
+ pvFile = open_file(pszFilename, fText);
+ if (pvFile)
+ {
+ unsigned char Digest[16];
+ KU64 cbFile = 0;
+
+ if (fProgress && pOutput)
+ fprintf(stdout, "%s: ", pszFilename);
+
+ rc = calc_md5sum(pvFile, Digest, fProgress, &cbFile);
+ close_file(pvFile);
+
+ if (fProgress && pOutput)
+ {
+ size_t cch = strlen(pszFilename) + 2;
+ while (cch-- > 0)
+ fputc('\b', stdout);
+ }
+
+ if (!rc)
+ {
+ char szDigest[36];
+ digest_to_string(Digest, szDigest);
+ if (!fManifest)
+ {
+ if (pOutput)
+ fprintf(pOutput, "%s %s%s\n", szDigest, fText ? "" : "*", pszFilename);
+ kmk_builtin_ctx_printf(pCtx, 0, "%s %s%s\n", szDigest, fText ? "" : "*", pszFilename);
+ }
+ else
+ {
+ if (pOutput)
+ fprintf(pOutput, "%s_SIZE := %" KU64_PRI "\n%s_MD5 := %s\n", pszFilename, cbFile, pszFilename, szDigest);
+ kmk_builtin_ctx_printf(pCtx, 0, "%s_SIZE := %" KU64_PRI "\n%s_MD5 := %s\n",
+ pszFilename, cbFile, pszFilename, szDigest);
+ }
+ if (pOutput)
+ fflush(pOutput);
+ }
+ else
+ {
+ if (!fQuiet)
+ errx(pCtx, 1, "Failed to open '%s': %s", pszFilename, strerror(rc));
+ rc = 1;
+ }
+ }
+ else
+ {
+ if (!fQuiet)
+ errx(pCtx, 1, "Failed to open '%s': %s", pszFilename, strerror(errno));
+ rc = 1;
+ }
+ return rc;
+}
+
+
+
+/**
+ * md5sum, calculates and checks the md5sum of files.
+ * Somewhat similar to the GNU coreutil md5sum command.
+ */
+int kmk_builtin_md5sum(int argc, char **argv, char **envp, PKMKBUILTINCTX pCtx)
+{
+ int i;
+ int rc = 0;
+ int fText = 0;
+ int fBinaryTextOpt = 0;
+ int fQuiet = 0;
+ int fChecking = 0;
+ int fManifest = 0;
+ int fProgress = 0;
+ int fNoMoreOptions = 0;
+ const char *pszOutput = NULL;
+ FILE *pOutput = NULL;
+
+ /*
+ * Print usage if no arguments.
+ */
+ if (argc <= 1)
+ return usage(pCtx, 1);
+
+ /*
+ * Process the arguments, FIFO style.
+ */
+ i = 1;
+ while (i < argc)
+ {
+ char *psz = argv[i];
+ if (!fNoMoreOptions && psz[0] == '-' && psz[1] == '-' && !psz[2])
+ fNoMoreOptions = 1;
+ else if (*psz == '-' && !fNoMoreOptions)
+ {
+ psz++;
+
+ /* convert long options for gnu just for fun */
+ if (*psz == '-')
+ {
+ if (!strcmp(psz, "-binary"))
+ psz = "b";
+ else if (!strcmp(psz, "-text"))
+ psz = "t";
+ else if (!strcmp(psz, "-check"))
+ psz = "c";
+ else if (!strcmp(psz, "-check-file"))
+ psz = "C";
+ else if (!strcmp(psz, "-manifest"))
+ psz = "m";
+ else if (!strcmp(psz, "-output"))
+ psz = "o";
+ else if (!strcmp(psz, "-progress"))
+ psz = "p";
+ else if (!strcmp(psz, "-status"))
+ psz = "q";
+ else if (!strcmp(psz, "-warn"))
+ psz = "w";
+ else if (!strcmp(psz, "-help"))
+ psz = "h";
+ else if (!strcmp(psz, "-version"))
+ psz = "v";
+ }
+
+ /* short options */
+ do
+ {
+ switch (*psz)
+ {
+ case 'c':
+ fChecking = 1;
+ break;
+
+ case 'b':
+ fText = 0;
+ fBinaryTextOpt = 1;
+ break;
+
+ case 't':
+ fText = 1;
+ fBinaryTextOpt = 1;
+ break;
+
+ case 'm':
+ fManifest = 1;
+ break;
+
+ case 'p':
+ fProgress = 1 && isatty(fileno(stdout))
+#ifndef KMK_BUILTIN_STANDALONE
+ && (!pCtx->pOut || !pCtx->pOut->syncout)
+#endif
+ ;
+ break;
+
+ case 'q':
+ fQuiet = 1;
+ break;
+
+ case 'w':
+ /* ignored */
+ break;
+
+ case 'h':
+ usage(pCtx, 0);
+ return 0;
+
+ case 'v':
+ return kbuild_version(argv[0]);
+
+ /*
+ * -C md5 file
+ */
+ case 'C':
+ {
+ const char *pszFilename;
+ const char *pszDigest;
+
+ if (psz[1])
+ pszDigest = &psz[1];
+ else if (i + 1 < argc)
+ pszDigest = argv[++i];
+ else
+ {
+ errx(pCtx, 1, "'-C' is missing the MD5 sum!");
+ return 1;
+ }
+ if (i + 1 < argc)
+ pszFilename = argv[++i];
+ else
+ {
+ errx(pCtx, 1, "'-C' is missing the filename!");
+ return 1;
+ }
+
+ rc |= check_one_file(pCtx, pszFilename, pszDigest, fText, fQuiet, fProgress && !fQuiet);
+ psz = "\0";
+ break;
+ }
+
+ /*
+ * Output file.
+ */
+ case 'o':
+ {
+ if (fChecking)
+ {
+ errx(pCtx, 1, "'-o' cannot be used with -c or -C!");
+ return 1;
+ }
+
+ if (psz[1])
+ pszOutput = &psz[1];
+ else if (i + 1 < argc)
+ pszOutput = argv[++i];
+ else
+ {
+ errx(pCtx, 1, "'-o' is missing the file name!");
+ return 1;
+ }
+
+ psz = "\0";
+ break;
+ }
+
+ default:
+ errx(pCtx, 1, "Invalid option '%c'! (%s)", *psz, argv[i]);
+ return usage(pCtx, 1);
+ }
+ } while (*++psz);
+ }
+ else if (fChecking)
+ rc |= check_files(pCtx, argv[i], fText, fBinaryTextOpt, fQuiet, fProgress && !fQuiet);
+ else
+ {
+ /* lazily open the output if specified. */
+ if (pszOutput)
+ {
+ if (pOutput)
+ fclose(pOutput);
+ pOutput = fopen(pszOutput, "w" KMK_FOPEN_NO_INHERIT_MODE);
+ if (!pOutput)
+ {
+ rc = err(pCtx, 1, "fopen(\"%s\", \"w" KMK_FOPEN_NO_INHERIT_MODE "\") failed", pszOutput);
+ break;
+ }
+ pszOutput = NULL;
+ }
+
+ rc |= md5sum_file(pCtx, argv[i], fText, fQuiet, fProgress && !fQuiet && !fManifest, fManifest, pOutput);
+ }
+ i++;
+ }
+
+ if (pOutput)
+ fclose(pOutput);
+ return rc;
+}
+
+
+#ifdef KMK_BUILTIN_STANDALONE
+int main(int argc, char **argv, char **envp)
+{
+ KMKBUILTINCTX Ctx = { "kmk_md5sum", NULL };
+ return kmk_builtin_md5sum(argc, argv, envp, &Ctx);
+}
+#endif
+
+