/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ │ vi: set et ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi │ ╞══════════════════════════════════════════════════════════════════════════════╡ │ Copyright 2022 Justine Alexandra Roberts Tunney │ │ │ │ Permission to use, copy, modify, and/or distribute this software for │ │ any purpose with or without fee is hereby granted, provided that the │ │ above copyright notice and this permission notice appear in all copies. │ │ │ │ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │ │ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │ │ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │ │ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │ │ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │ │ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/assert.h" #include "libc/calls/calls.h" #include "libc/errno.h" #include "libc/fmt/itoa.h" #include "libc/fmt/magnumstrs.internal.h" #include "libc/limits.h" #include "libc/runtime/runtime.h" #include "libc/stdio/stdio.h" #include "libc/str/str.h" #include "libc/str/tab.h" #include "third_party/getopt/getopt.internal.h" #include "third_party/mbedtls/sha256.h" #define USAGE \ "[-?hbctw] [PATH...]\n\ -h help\n\ -c check mode\n\ -b binary mode\n\ -t textual mode\n\ -w warning mode\n\ \n\ cosmopolitan sha256sum v1.1\n\ copyright 2022 justine alexandra roberts tunney\n\ notice licenses are embedded in the binary\n\ https://twitter.com/justinetunney\n\ https://github.com/jart\n\ \n\ This program is an Actually Portable Executable (APE) rewrite of the\n\ GNU coreutils sha256sum command. Our version is better because it goes\n\ 400% faster than coreutils if the Intel® SHA-NI™ ISA is available,\n\ otherwise this command goes 50% faster than coreutils. This executable\n\ will work consistently on Linux/Mac/Windows/FreeBSD/NetBSD/OpenBSD, so\n\ consider vendoring it in your repo to avoid platform portability toil.\n\ " static bool g_warn; static char g_mode; static bool g_check; static int g_mismatches; static const char *prog; static wontreturn void PrintUsage(int rc, int fd) { tinyprint(fd, "Usage: ", prog, USAGE, NULL); exit(rc); } static void GetOpts(int argc, char *argv[]) { int opt; g_mode = ' '; while ((opt = getopt(argc, argv, "hbctw")) != -1) { switch (opt) { case 'w': g_warn = true; break; case 'c': g_check = true; break; case 't': g_mode = ' '; break; case 'b': g_mode = '*'; break; case 'h': PrintUsage(0, 1); default: PrintUsage(1, 2); } } } static bool IsModeCharacter(char c) { switch (c) { case ' ': case '*': return true; default: return false; } } static bool IsSupportedPath(const char *path) { size_t i; for (i = 0;; ++i) { switch (path[i]) { case 0: if (i) return true; // fallthrough case '\r': case '\n': case '\\': tinyprint(2, prog, ": ", path, ": unsupported path\n", NULL); return false; default: break; } } } static bool GetDigest(const char *path, FILE *f, unsigned char digest[32]) { size_t got; unsigned char buf[512]; mbedtls_sha256_context ctx; mbedtls_sha256_init(&ctx); unassert(!mbedtls_sha256_starts_ret(&ctx, false)); while ((got = fread(buf, 1, sizeof(buf), f))) { unassert(!mbedtls_sha256_update_ret(&ctx, buf, got)); } if (ferror(f)) { tinyprint(2, prog, ": ", path, ": ", strerror(errno), "\n", NULL); return false; } unassert(!mbedtls_sha256_finish_ret(&ctx, digest)); mbedtls_sha256_free(&ctx); return true; } static bool ProduceDigest(const char *path, FILE *f) { char hexdigest[65]; char mode[2] = {g_mode}; unsigned char digest[32]; if (!IsSupportedPath(path)) return false; if (!GetDigest(path, f, digest)) return false; hexpcpy(hexdigest, digest, 32); tinyprint(1, hexdigest, " ", mode, path, "\n", NULL); return true; } static bool CheckDigests(const char *path, FILE *f) { FILE *f2; bool k = true; int a, b, i, line; const char *path2, *status; unsigned char wantdigest[32], gotdigest[32]; char buf[64 + 2 + PATH_MAX + 1 + 1], *p; for (line = 0; fgets(buf, sizeof(buf), f); ++line) { if (!*chomp(buf)) continue; for (p = buf, i = 0; i < 32; ++i) { if ((a = kHexToInt[*p++ & 255]) == -1) goto InvalidLine; if ((b = kHexToInt[*p++ & 255]) == -1) goto InvalidLine; wantdigest[i] = a << 4 | b; } if (*p++ != ' ') goto InvalidLine; if (!IsModeCharacter(*p++)) goto InvalidLine; path2 = p; if (!*path2) goto InvalidLine; if (!IsSupportedPath(path2)) continue; if ((f2 = fopen(path2, "rb"))) { if (GetDigest(path2, f2, gotdigest)) { if (!memcmp(wantdigest, gotdigest, 32)) { status = "OK"; } else { status = "FAILED"; ++g_mismatches; k = false; } tinyprint(1, path2, ": ", status, "\n", NULL); } else { k = false; } fclose(f2); } else { tinyprint(2, prog, ": ", path2, ": ", strerror(errno), "\n", NULL); k = false; } continue; InvalidLine: if (g_warn) { char linestr[12]; FormatInt32(linestr, line + 1); tinyprint(2, prog, ": ", path, ":", linestr, ": ", "improperly formatted checksum line", "\n", NULL); } } if (ferror(f)) { tinyprint(2, prog, ": ", path, ": ", strerror(errno), "\n", NULL); k = false; } return k; } static bool Process(const char *path, FILE *f) { if (g_check) { return CheckDigests(path, f); } else { return ProduceDigest(path, f); } } int main(int argc, char *argv[]) { int i; FILE *f; bool k = true; prog = argv[0]; if (!prog) prog = "sha256sum"; GetOpts(argc, argv); if (optind == argc) { f = stdin; k &= Process("-", f); } else { for (i = optind; i < argc; ++i) { if ((f = fopen(argv[i], "rb"))) { k &= Process(argv[i], f); fclose(f); } else { tinyprint(2, prog, ": ", argv[i], ": ", strerror(errno), "\n", NULL); k = false; } } } if (g_mismatches) { char ibuf[12]; FormatInt32(ibuf, g_mismatches); tinyprint(2, prog, ": WARNING: ", ibuf, " computed checksum did NOT match\n", NULL); } return !k; }