LDADD = libcatcrypt.a libcatacomb.la
define(`LIBCAT_SRC', `cc.h getdate.h dnl
- cc-sig.c cc-subcmd.c cc-enc.c cc-kem.c cc-list.c cc-progress.c')
+ cc-sig.c cc-subcmd.c cc-enc.c cc-kem.c cc-hash.c \
+ cc-list.c cc-progress.c')
libcatcrypt_a_SOURCES = LIBCAT_SRC getdate.y
patsubst(MP_BASE MP_SOURCES, `\.c\>', `.lo') dsig.o keyutil.o rspit.o \
--- /dev/null
+/* -*-c-*-
+ *
+ * Common functions for hashing utilities
+ *
+ * (c) 2011 Straylight/Edgeware
+ */
+
+/*----- Licensing notice --------------------------------------------------*
+ *
+ * This file is part of Catacomb.
+ *
+ * Catacomb is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Library General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * Catacomb 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 Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with Catacomb; if not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ * MA 02111-1307, USA.
+ */
+
+/*----- Header files ------------------------------------------------------*/
+
+#define _FILE_OFFSET_BITS 64
+
+#include "config.h"
+
+#include <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <mLib/dstr.h>
+
+#include <mLib/hex.h>
+#include <mLib/base32.h>
+#include <mLib/base64.h>
+
+#include "ghash.h"
+#include "cc.h"
+
+/*----- Encoding and decoding ---------------------------------------------*/
+
+/* --- Hex encoding --- */
+
+static void puthex(const octet *buf, size_t sz, FILE *fp)
+{
+ while (sz) {
+ fprintf(fp, "%02x", *buf++);
+ sz--;
+ }
+}
+
+static size_t gethex(const char *p, octet *q, size_t sz, char **pp)
+{
+ size_t i = 0;
+ while (sz > 0 &&
+ isxdigit((unsigned char)p[0]) &&
+ isxdigit((unsigned char)p[1])) {
+ char buf[3];
+ buf[0] = p[0];
+ buf[1] = p[1];
+ buf[2] = 0;
+ *q++ = strtoul(buf, 0, 16);
+ sz--;
+ p += 2;
+ i++;
+ }
+ if (pp)
+ *pp = (char *)p;
+ return (i);
+}
+
+/* --- Base64 encoding --- */
+
+static void putbase64(const octet *buf, size_t sz, FILE *fp)
+{
+ base64_ctx b;
+ dstr d = DSTR_INIT;
+
+ base64_init(&b);
+ b.indent = "";
+ b.maxline = 0;
+ base64_encode(&b, buf, sz, &d);
+ base64_encode(&b, 0, 0, &d);
+ dstr_write(&d, fp);
+ dstr_destroy(&d);
+}
+
+static size_t getbase64(const char *p, octet *q, size_t sz, char **pp)
+{
+ base64_ctx b;
+ dstr d = DSTR_INIT;
+ size_t n = strlen(p);
+
+ base64_init(&b);
+ base64_decode(&b, p, n, &d);
+ if (pp) *pp = (/*unconst*/ char *)p + n;
+ base64_decode(&b, 0, 0, &d);
+ assert(d.len <= sz);
+ memcpy(q, d.buf, sz);
+ n = d.len;
+ dstr_destroy(&d);
+ return (n);
+}
+
+/* --- Base32 encoding --- */
+
+static void putbase32(const octet *buf, size_t sz, FILE *fp)
+{
+ base32_ctx b;
+ dstr d = DSTR_INIT;
+
+ base32_init(&b);
+ b.indent = "";
+ b.maxline = 0;
+ base32_encode(&b, buf, sz, &d);
+ base32_encode(&b, 0, 0, &d);
+ dstr_write(&d, fp);
+ dstr_destroy(&d);
+}
+
+static size_t getbase32(const char *p, octet *q, size_t sz, char **pp)
+{
+ base32_ctx b;
+ dstr d = DSTR_INIT;
+ size_t n = strlen(p);
+
+ base32_init(&b);
+ base32_decode(&b, p, n, &d);
+ if (pp) *pp = (/*unconst*/ char *)p + n;
+ base32_decode(&b, 0, 0, &d);
+ assert(d.len <= sz);
+ memcpy(q, d.buf, sz);
+ n = d.len;
+ dstr_destroy(&d);
+ return (n);
+}
+
+/* --- Table --- */
+
+const encodeops encodingtab[] = {
+#define TAB(tag, name) { #name, put##name, get##name },
+ ENCODINGS(TAB)
+#undef TAB
+ { 0, 0, 0 }
+};
+
+const encodeops *getencoding(const char *ename)
+{
+ const encodeops *e;
+
+ for (e = encodingtab; e->name; e++) {
+ if (strcmp(ename, e->name) == 0)
+ return (e);
+ }
+ return (0);
+}
+
+/*----- File hashing ------------------------------------------------------*/
+
+/* --- @fhash@ --- *
+ *
+ * Arguments: @const gchash *gch@ = pointer to hash function to use
+ * @unsigned f@ = flags to set
+ * @const char *file@ = file name to be hashed (null for stdin)
+ * @void *buf@ = pointer to hash output buffer
+ *
+ * Returns: Zero if it worked, nonzero on error.
+ *
+ * Use: Hashes a file.
+ */
+
+int fhash(const gchash *gch, unsigned f, const char *file, void *buf)
+{
+ FILE *fp;
+ char fbuf[1024 * 128];
+ size_t sz;
+ ghash *h;
+ int rc = 0;
+ fprogress ff;
+
+ if (!file || strcmp(file, "-") == 0)
+ fp = stdin;
+ else if ((fp = fopen(file, f & FHF_BINARY ? "rb" : "r")) == 0)
+ return (-1);
+
+ if (f & FHF_PROGRESS) {
+ if (fprogress_init(&ff, file, fp)) return (-1);
+ }
+
+ h = GH_INIT(gch);
+ while ((sz = fread(fbuf, 1, sizeof(fbuf), fp)) > 0) {
+ GH_HASH(h, fbuf, sz);
+ if (f & FHF_PROGRESS) fprogress_update(&ff, sz);
+ }
+ if (ferror(fp)) rc = -1;
+ if (fp != stdin) fclose(fp);
+ if (f & FHF_PROGRESS) fprogress_done(&ff);
+ GH_DONE(h, buf);
+ GH_DESTROY(h);
+ return (rc);
+}
+
+/*----- String I/O --------------------------------------------------------*/
+
+/* --- @getstring@ --- *
+ *
+ * Arguments: @void *in@ = input source
+ * @dstr *d@ = destination string
+ * @unsigned f@ = input flags
+ *
+ * Returns: Zero if OK, nonzero on end-of-file.
+ *
+ * Use: Reads a filename (or something similar) from a stream.
+ */
+
+static int nextch_file(void *in)
+ { FILE *fp = in; return (getc(fp)); }
+
+static int nextch_string(void *in)
+ { const unsigned char **p = in; return (*(*p)++); }
+
+int getstring(void *in, dstr *d, unsigned f)
+{
+ int ch;
+ int eofch = (f & GSF_STRING) ? 0 : EOF;
+ int (*nextch)(void *) = (f & GSF_STRING) ? nextch_string : nextch_file;
+ int q = 0;
+
+ /* --- Raw: just read exactly what's written up to a null byte --- */
+
+ if (f & GSF_RAW) {
+ if ((ch = nextch(in)) == eofch)
+ return (EOF);
+ for (;;) {
+ if (!ch)
+ break;
+ DPUTC(d, ch);
+ if ((ch = nextch(in)) == eofch)
+ break;
+ }
+ DPUTZ(d);
+ return (0);
+ }
+
+ /* --- Skip as far as whitespace --- *
+ *
+ * Also skip past comments.
+ */
+
+again:
+ ch = nextch(in);
+ while (isspace(ch))
+ ch = nextch(in);
+ if (ch == '#') {
+ do ch = nextch(in); while (ch != '\n' && ch != eofch);
+ goto again;
+ }
+ if (ch == eofch)
+ return (EOF);
+
+ /* --- If the character is a quote then read a quoted string --- */
+
+ switch (ch) {
+ case '`':
+ ch = '\'';
+ case '\'':
+ case '\"':
+ q = ch;
+ ch = nextch(in);
+ break;
+ }
+
+ /* --- Now read all sorts of interesting things --- */
+
+ for (;;) {
+
+ /* --- Handle an escaped thing --- */
+
+ if (ch == '\\') {
+ ch = nextch(in);
+ if (ch == eofch)
+ break;
+ switch (ch) {
+ case 'a': ch = '\a'; break;
+ case 'b': ch = '\b'; break;
+ case 'f': ch = '\f'; break;
+ case 'n': ch = '\n'; break;
+ case 'r': ch = '\r'; break;
+ case 't': ch = '\t'; break;
+ case 'v': ch = '\v'; break;
+ }
+ DPUTC(d, ch);
+ ch = nextch(in);
+ continue;
+ }
+
+ /* --- If it's a quote or some other end marker then stop --- */
+
+ if (ch == q)
+ break;
+ if (!q && isspace(ch))
+ break;
+
+ /* --- Otherwise contribute and continue --- */
+
+ DPUTC(d, ch);
+ if ((ch = nextch(in)) == eofch)
+ break;
+ }
+
+ /* --- Done --- */
+
+ DPUTZ(d);
+ return (0);
+}
+
+/* --- @putstring@ --- *
+ *
+ * Arguments: @FILE *fp@ = stream to write on
+ * @const char *p@ = pointer to text
+ * @unsigned f@ = output flags
+ *
+ * Returns: ---
+ *
+ * Use: Emits a string to a stream.
+ */
+
+void putstring(FILE *fp, const char *p, unsigned f)
+{
+ size_t sz = strlen(p);
+ unsigned qq;
+ const char *q;
+
+ /* --- Just write the string null terminated if raw --- */
+
+ if (f & GSF_RAW) {
+ fwrite(p, 1, sz + 1, fp);
+ return;
+ }
+
+ /* --- Check for any dodgy characters --- */
+
+ qq = 0;
+ for (q = p; *q; q++) {
+ if (isspace((unsigned char)*q)) {
+ qq = '\"';
+ break;
+ }
+ }
+
+ if (qq)
+ putc(qq, fp);
+
+ /* --- Emit the string --- */
+
+ for (q = p; *q; q++) {
+ switch (*q) {
+ case '\a': fputc('\\', fp); fputc('a', fp); break;
+ case '\b': fputc('\\', fp); fputc('b', fp); break;
+ case '\f': fputc('\\', fp); fputc('f', fp); break;
+ case '\n': fputc('\\', fp); fputc('n', fp); break;
+ case '\r': fputc('\\', fp); fputc('r', fp); break;
+ case '\t': fputc('\\', fp); fputc('t', fp); break;
+ case '\v': fputc('\\', fp); fputc('v', fp); break;
+ case '`': fputc('\\', fp); fputc('`', fp); break;
+ case '\'': fputc('\\', fp); fputc('\'', fp); break;
+ case '\"': fputc('\\', fp); fputc('\"', fp); break;
+ default:
+ putc(*q, fp);
+ break;
+ }
+ }
+
+ /* --- Done --- */
+
+ if (qq)
+ putc(qq, fp);
+}
+
+/*----- That's all, folks -------------------------------------------------*/
extern int cmd_encode(int /*argc*/, char */*argv*/[]);
extern int cmd_decode(int /*argc*/, char */*argv*/[]);
+/*----- Hash encoding functions -------------------------------------------*/
+
+/* --- Table --- */
+
+#define ENCODINGS(_) \
+ _(HEX, hex) \
+ _(BASE64, base64) \
+ _(BASE32, base32)
+
+enum {
+#define ENUM(tag, name) ENC_##tag,
+ ENCODINGS(ENUM)
+#undef ENUM
+ ENC_LIMIT
+};
+
+typedef struct encodeops {
+ const char *name;
+ void (*put)(const octet *, size_t, FILE *);
+ size_t (*get)(const char *, octet *, size_t, char **);
+} encodeops;
+
+extern const encodeops encodingtab[];
+
+/* --- @getencoding@ --- *
+ *
+ * Arguments: @const char *ename@ = encoding name
+ *
+ * Returns: Pointer to encoding table entry, or null.
+ *
+ * Use: Finds an encoding entry given its name.
+ */
+
+extern const encodeops *getencoding(const char */*ename*/);
+
+/*----- File hashing ------------------------------------------------------*/
+
+/* --- @fhash@ --- *
+ *
+ * Arguments: @const gchash *gch@ = pointer to hash function to use
+ * @unsigned f@ = flags to set
+ * @const char *file@ = file name to be hashed (null for stdin)
+ * @void *buf@ = pointer to hash output buffer
+ *
+ * Returns: Zero if it worked, nonzero on error.
+ *
+ * Use: Hashes a file.
+ */
+
+#define FHF_BINARY 256u
+#define FHF_PROGRESS 512u
+
+#define FHF_MASK 3840
+
+extern int fhash(const gchash */*gch*/, unsigned /*f*/,
+ const char */*file*/, void */*buf*/);
+
+/*----- String I/O --------------------------------------------------------*/
+
+#define GSF_RAW 4096u
+#define GSF_FILE 0u
+#define GSF_STRING 8192u
+
+#define GSF_MASK 61440u
+
+/* --- @getstring@ --- *
+ *
+ * Arguments: @void *in@ = input source
+ * @dstr *d@ = destination string
+ * @unsigned f@ = input flags
+ *
+ * Returns: Zero if OK, nonzero on end-of-file.
+ *
+ * Use: Reads a filename (or something similar) from a stream.
+ */
+
+extern int getstring(void */*in*/, dstr */*d*/, unsigned /*f*/);
+
+/* --- @putstring@ --- *
+ *
+ * Arguments: @FILE *fp@ = stream to write on
+ * @const char *p@ = pointer to text
+ * @unsigned f@ = output flags
+ *
+ * Returns: ---
+ *
+ * Use: Emits a string to a stream.
+ */
+
+extern void putstring(FILE */*fp*/, const char */*p*/, unsigned /*f*/);
+
/*----- Lists of things ---------------------------------------------------*/
/* --- @LIST(STRING, FP, END-TEST, NAME-EXPR)@ --- *
uint32 k; /* Keyid */
} block;
-/* --- @getstring@ --- *
- *
- * Arguments: @FILE *fp@ = stream from which to read
- * @dstr *d@ = destination string
- * @unsigned raw@ = raw or cooked read
- *
- * Returns: Zero if OK, nonzero on end-of-file.
- *
- * Use: Reads a filename (or something similar) from a stream.
- */
-
-static int getstring(FILE *fp, dstr *d, unsigned raw)
-{
- int ch;
- int q = 0;
-
- /* --- Raw: just read exactly what's written up to a null byte --- */
-
- if (raw) {
- if ((ch = getc(fp)) == EOF)
- return (EOF);
- for (;;) {
- if (!ch)
- break;
- DPUTC(d, ch);
- if ((ch = getc(fp)) == EOF)
- break;
- }
- DPUTZ(d);
- return (0);
- }
-
- /* --- Skip as far as whitespace --- *
- *
- * Also skip past comments.
- */
-
-again:
- ch = getc(fp);
- while (isspace(ch))
- ch = getc(fp);
- if (ch == '#') {
- do ch = getc(fp); while (ch != '\n' && ch != EOF);
- goto again;
- }
- if (ch == EOF)
- return (EOF);
-
- /* --- If the character is a quote then read a quoted string --- */
-
- switch (ch) {
- case '`':
- ch = '\'';
- case '\'':
- case '\"':
- q = ch;
- ch = getc(fp);
- break;
- }
-
- /* --- Now read all sorts of interesting things --- */
-
- for (;;) {
-
- /* --- Handle an escaped thing --- */
-
- if (ch == '\\') {
- ch = getc(fp);
- if (ch == EOF)
- break;
- switch (ch) {
- case 'a': ch = '\a'; break;
- case 'b': ch = '\b'; break;
- case 'f': ch = '\f'; break;
- case 'n': ch = '\n'; break;
- case 'r': ch = '\r'; break;
- case 't': ch = '\t'; break;
- case 'v': ch = '\v'; break;
- }
- DPUTC(d, ch);
- ch = getc(fp);
- continue;
- }
-
- /* --- If it's a quote or some other end marker then stop --- */
-
- if (ch == q || (!q && isspace((unsigned char)ch)))
- break;
-
- /* --- Otherwise contribute and continue --- */
-
- DPUTC(d, ch);
- if ((ch = getc(fp)) == EOF)
- break;
- }
-
- /* --- Done --- */
-
- DPUTZ(d);
- return (0);
-}
-
-/* --- @putstring@ --- *
- *
- * Arguments: @FILE *fp@ = stream to write on
- * @const char *p@ = pointer to text
- * @unsigned raw@ = whether the string is to be written raw
- *
- * Returns: ---
- *
- * Use: Emits a string to a stream.
- */
-
-static void putstring(FILE *fp, const char *p, unsigned raw)
-{
- size_t sz = strlen(p);
- unsigned qq;
- const char *q;
-
- /* --- Just write the string null terminated if raw --- */
-
- if (raw) {
- fwrite(p, 1, sz + 1, fp);
- return;
- }
-
- /* --- Check for any dodgy characters --- */
-
- qq = 0;
- for (q = p; *q; q++) {
- if (isspace((unsigned char)*q)) {
- qq = '\"';
- break;
- }
- }
-
- if (qq)
- putc(qq, fp);
-
- /* --- Emit the string --- */
-
- for (q = p; *q; q++) {
- switch (*q) {
- case '\a': fputc('\\', fp); fputc('a', fp); break;
- case '\b': fputc('\\', fp); fputc('b', fp); break;
- case '\f': fputc('\\', fp); fputc('f', fp); break;
- case '\n': fputc('\\', fp); fputc('n', fp); break;
- case '\r': fputc('\\', fp); fputc('r', fp); break;
- case '\t': fputc('\\', fp); fputc('t', fp); break;
- case '\v': fputc('\\', fp); fputc('v', fp); break;
- case '`': fputc('\\', fp); fputc('`', fp); break;
- case '\'': fputc('\\', fp); fputc('\'', fp); break;
- case '\"': fputc('\\', fp); fputc('\"', fp); break;
- default:
- putc(*q, fp);
- break;
- }
- }
-
- /* --- Done --- */
-
- if (qq)
- putc(qq, fp);
-}
-
/* --- @timestring@ --- *
*
* Arguments: @time_t t@ = a timestamp
tag = getc(fp);
else {
dstr d = DSTR_INIT;
- if (getstring(fp, &d, 0))
+ if (getstring(fp, &d, GSF_FILE))
return (E_EOF);
for (tag = 0; tagtab[tag]; tag++) {
if (strcmp(tagtab[tag], d.buf) == 0)
case T_IDENT:
case T_COMMENT:
- if (getstring(fp, &b->d, bin))
+ if (getstring(fp, &b->d, GSF_FILE | (bin ? GSF_RAW : 0)))
return (E_EOF);
break;
b->t = ((time_t)(((LOAD32(buf + 0) << 16) << 16) & ~MASK32) |
(time_t)LOAD32(buf + 4));
} else {
- if (getstring(fp, &b->d, 0))
+ if (getstring(fp, &b->d, GSF_FILE))
return (E_EOF);
if (strcmp(b->d.buf, "forever") == 0)
b->t = KEXP_FOREVER;
return (E_EOF);
b->k = LOAD32(buf);
} else {
- if (getstring(fp, &b->d, 0))
+ if (getstring(fp, &b->d, GSF_FILE))
return (E_EOF);
b->k = strtoul(b->d.buf, 0, 16);
}
b->b.len += sz;
} else {
base64_ctx b64;
- if (getstring(fp, &b->d, 0))
+ if (getstring(fp, &b->d, GSF_FILE))
return (E_EOF);
base64_init(&b64);
base64_decode(&b64, b->d.buf, b->d.len, &b->b);
base64_decode(&b64, 0, 0, &b->b);
DRESET(&b->d);
}
- if (tag == T_FILE && getstring(fp, &b->d, bin))
+ if (tag == T_FILE &&
+ getstring(fp, &b->d, GSF_FILE | (bin ? GSF_RAW : 0)))
return (E_EOF);
break;
/*----- Other shared functions --------------------------------------------*/
-/* --- @fhash@ --- *
- *
- * Arguments: @const gchash *c@ = pointer to hash class
- * @unsigned f@ = flags
- * @const char *file@ = file to hash
- * @void *b@ = pointer to output buffer
- *
- * Returns: Zero if it worked, or nonzero for a system error.
- *
- * Use: Hashes a file.
- */
-
-#define FHF_PROGRESS 256u
-
-static int fhash(const gchash *c, unsigned f, const char *file, void *b)
-{
- FILE *fp = fopen(file, "rb");
- fprogress ff;
- ghash *h = GH_INIT(c);
- char buf[4096];
- size_t sz;
- int rc = 0;
-
- if (!fp)
- return (-1);
- if (f & FHF_PROGRESS) {
- if (fprogress_init(&ff, file, fp)) return (-1);
- }
- while ((sz = fread(buf, 1, sizeof(buf), fp)) > 0) {
- GH_HASH(h, buf, sz);
- if (f & FHF_PROGRESS) fprogress_update(&ff, sz);
- }
- if (ferror(fp))
- rc = -1;
- if (f & FHF_PROGRESS) fprogress_done(&ff);
- GH_DONE(h, b);
- GH_DESTROY(h);
- fclose(fp);
- return (rc);
-}
-
/* --- @fhex@ --- *
*
* Arguments: @FILE *fp@ = file to write on
static int sign(int argc, char *argv[])
{
-#define f_raw 1u
+#define f_bogus 1u
#define f_bin 2u
-#define f_bogus 4u
-#define f_nocheck 8u
+#define f_nocheck 4u
unsigned f = 0;
const char *ki = "dsig";
break;
switch (i) {
case '0':
- f |= f_raw;
+ f |= GSF_RAW;
break;
case 'b':
f |= f_bin;
if (!ifile || strcmp(ifile, "-") == 0)
ifp = stdin;
- else if ((ifp = fopen(ifile, (f & f_raw) ? "rb" : "r")) == 0) {
+ else if ((ifp = fopen(ifile, (f & f_bin) ? "rb" : "r")) == 0) {
die(EXIT_FAILURE, "couldn't open input file `%s': %s",
ifile, strerror(errno));
}
/* --- Read the next filename to hash --- */
breset(&b);
- if (getstring(ifp, &b.d, f & f_raw))
+ if (getstring(ifp, &b.d, GSF_FILE | f))
break;
b.tag = T_FILE;
DENSURE(&b.b, GH_CLASS(s->h)->hashsz);
- if (fhash(GH_CLASS(s->h), f, b.d.buf, b.b.buf)) {
+ if (fhash(GH_CLASS(s->h), f | FHF_BINARY, b.d.buf, b.b.buf)) {
moan("Error reading `%s': %s", b.d.buf, strerror(errno));
f |= f_bogus;
} else {
die(EXIT_FAILURE, "error(s) occurred while creating signature");
return (EXIT_SUCCESS);
-#undef f_raw
#undef f_bin
#undef f_bogus
#undef f_nocheck
case T_FILE:
DRESET(&d);
DENSURE(&d, GH_CLASS(s->h)->hashsz);
- if (fhash(GH_CLASS(s->h), f, b.d.buf, d.buf)) {
+ if (fhash(GH_CLASS(s->h), f | FHF_BINARY, b.d.buf, d.buf)) {
if (verb > 1) {
printf("BAD error reading file `%s': %s\n",
b.d.buf, strerror(errno));
#include "config.h"
#include <assert.h>
-#include <ctype.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <mLib/sub.h>
#include <mLib/str.h>
-#include <mLib/hex.h>
-#include <mLib/base32.h>
-#include <mLib/base64.h>
-
#include "ghash.h"
#include "cc.h"
/*----- Static variables --------------------------------------------------*/
-#define f_binary 1u
-#define f_bogus 2u
-#define f_verbose 4u
-#define f_check 8u
-#define f_files 16u
-#define f_raw 32u
-#define f_oddhash 64u
-#define f_escape 128u
-#define f_oddenc 256u
-#define f_progress 512u
-
-/*----- Encoding and decoding ---------------------------------------------*/
-
-/* --- Hex encoding --- */
-
-static void puthex(const octet *buf, size_t sz, FILE *fp)
-{
- while (sz) {
- fprintf(fp, "%02x", *buf++);
- sz--;
- }
-}
-
-static size_t gethex(const char *p, octet *q, size_t sz, char **pp)
-{
- size_t i = 0;
- while (sz > 0 &&
- isxdigit((unsigned char)p[0]) &&
- isxdigit((unsigned char)p[1])) {
- char buf[3];
- buf[0] = p[0];
- buf[1] = p[1];
- buf[2] = 0;
- *q++ = strtoul(buf, 0, 16);
- sz--;
- p += 2;
- i++;
- }
- if (pp)
- *pp = (char *)p;
- return (i);
-}
-
-/* --- Base64 encoding --- */
-
-static void putb64(const octet *buf, size_t sz, FILE *fp)
-{
- base64_ctx b;
- dstr d = DSTR_INIT;
-
- base64_init(&b);
- b.indent = "";
- b.maxline = 0;
- base64_encode(&b, buf, sz, &d);
- base64_encode(&b, 0, 0, &d);
- dstr_write(&d, fp);
- dstr_destroy(&d);
-}
-
-static size_t getb64(const char *p, octet *q, size_t sz, char **pp)
-{
- base64_ctx b;
- dstr d = DSTR_INIT;
- size_t n = strlen(p);
-
- base64_init(&b);
- base64_decode(&b, p, n, &d);
- if (pp) *pp = (/*unconst*/ char *)p + n;
- base64_decode(&b, 0, 0, &d);
- assert(d.len <= sz);
- memcpy(q, d.buf, sz);
- n = d.len;
- dstr_destroy(&d);
- return (n);
-}
-
-/* --- Base32 encoding --- */
-
-static void putb32(const octet *buf, size_t sz, FILE *fp)
-{
- base32_ctx b;
- dstr d = DSTR_INIT;
-
- base32_init(&b);
- b.indent = "";
- b.maxline = 0;
- base32_encode(&b, buf, sz, &d);
- base32_encode(&b, 0, 0, &d);
- dstr_write(&d, fp);
- dstr_destroy(&d);
-}
-
-static size_t getb32(const char *p, octet *q, size_t sz, char **pp)
-{
- base32_ctx b;
- dstr d = DSTR_INIT;
- size_t n = strlen(p);
-
- base32_init(&b);
- base32_decode(&b, p, n, &d);
- if (pp) *pp = (/*unconst*/ char *)p + n;
- base32_decode(&b, 0, 0, &d);
- assert(d.len <= sz);
- memcpy(q, d.buf, sz);
- n = d.len;
- dstr_destroy(&d);
- return (n);
-}
-
-/* --- Table --- */
-
-typedef struct encodeops {
- const char *name;
- void (*put)(const octet *, size_t, FILE *);
- size_t (*get)(const char *, octet *, size_t, char **);
-} encodeops;
-
-static const encodeops encodingtab[] = {
- { "hex", puthex, gethex },
- { "base64", putb64, getb64 },
- { "base32", putb32, getb32 },
- { 0, 0, 0 }
-};
-
-static const encodeops *getencoding(const char *ename)
-{
- const encodeops *e;
-
- for (e = encodingtab; e->name; e++) {
- if (strcmp(ename, e->name) == 0)
- return (e);
- }
- return (0);
-}
+#define f_bogus 1u
+#define f_verbose 2u
+#define f_check 4u
+#define f_files 8u
+#define f_oddhash 16u
+#define f_escape 32u
+#define f_oddenc 64u
/*----- Support functions -------------------------------------------------*/
-/* --- @fhash@ --- *
- *
- * Arguments: @const char *file@ = file name to be hashed (null for stdin)
- * @unsigned f@ = flags to set
- * @const gchash *gch@ = pointer to hash function to use
- * @void *buf@ = pointer to hash output buffer
- *
- * Returns: Zero if it worked, nonzero on error.
- *
- * Use: Hashes a file.
- */
-
-static int fhash(const char *file, unsigned f, const gchash *gch, void *buf)
-{
- FILE *fp;
- char fbuf[1024 * 128];
- size_t sz;
- ghash *h;
- int e;
- fprogress ff;
-
- if (!file || strcmp(file, "-") == 0)
- fp = stdin;
- else if ((fp = fopen(file, f & f_binary ? "rb" : "r")) == 0)
- return (-1);
-
- if (f & f_progress) {
- if (fprogress_init(&ff, file, fp)) return (-1);
- }
-
- h = GH_INIT(gch);
- while ((sz = fread(fbuf, 1, sizeof(fbuf), fp)) > 0) {
- GH_HASH(h, fbuf, sz);
- if (f & f_progress) fprogress_update(&ff, sz);
- }
- GH_DONE(h, buf);
- GH_DESTROY(h);
- if (f & f_progress) fprogress_done(&ff);
- e = ferror(fp);
- if (file)
- fclose(fp);
- return (e ? -1 : 0);
-}
-
/* --- @gethash@ --- *
*
* Arguments: @const char *name@ = pointer to name string
return (gg);
}
-/* --- @getstring@ --- *
- *
- * Arguments: @FILE *fp@ = stream from which to read
- * @const char *p@ = string to read from instead
- * @dstr *d@ = destination string
- * @unsigned raw@ = raw or cooked read
- *
- * Returns: Zero if OK, nonzero on end-of-file.
- *
- * Use: Reads a filename (or something similar) from a stream.
- */
-
-static int getstring(FILE *fp, const char *p, dstr *d, unsigned raw)
-{
- int ch;
- int q = 0;
-
- /* --- Raw: just read exactly what's written up to a null byte --- */
-
-#define NEXTCH (fp ? getc(fp) : (unsigned char)*p++)
-#define EOFCH (fp ? EOF : 0)
-
- if (raw) {
- if ((ch = NEXTCH) == EOFCH)
- return (EOF);
- for (;;) {
- if (!ch)
- break;
- DPUTC(d, ch);
- if ((ch = NEXTCH) == EOFCH)
- break;
- }
- DPUTZ(d);
- return (0);
- }
-
- /* --- Skip as far as whitespace --- *
- *
- * Also skip past comments.
- */
-
-again:
- ch = NEXTCH;
- while (isspace(ch))
- ch = NEXTCH;
- if (ch == '#') {
- do ch = NEXTCH; while (ch != '\n' && ch != EOFCH);
- goto again;
- }
- if (ch == EOFCH)
- return (EOF);
-
- /* --- If the character is a quote then read a quoted string --- */
-
- switch (ch) {
- case '`':
- ch = '\'';
- case '\'':
- case '\"':
- q = ch;
- ch = NEXTCH;
- break;
- }
-
- /* --- Now read all sorts of interesting things --- */
-
- for (;;) {
-
- /* --- Handle an escaped thing --- */
-
- if (ch == '\\') {
- ch = NEXTCH;
- if (ch == EOFCH)
- break;
- switch (ch) {
- case 'a': ch = '\a'; break;
- case 'b': ch = '\b'; break;
- case 'f': ch = '\f'; break;
- case 'n': ch = '\n'; break;
- case 'r': ch = '\r'; break;
- case 't': ch = '\t'; break;
- case 'v': ch = '\v'; break;
- }
- DPUTC(d, ch);
- ch = NEXTCH;
- continue;
- }
-
- /* --- If it's a quote or some other end marker then stop --- */
-
- if (ch == q)
- break;
- if (!q && isspace(ch))
- break;
-
- /* --- Otherwise contribute and continue --- */
-
- DPUTC(d, ch);
- if ((ch = NEXTCH) == EOFCH)
- break;
- }
-
- /* --- Done --- */
-
- DPUTZ(d);
- return (0);
-
-#undef NEXTCH
-#undef EOFCH
-}
-
-/* --- @putstring@ --- *
- *
- * Arguments: @FILE *fp@ = stream to write on
- * @const char *p@ = pointer to text
- * @unsigned raw@ = whether the string is to be written raw
- *
- * Returns: ---
- *
- * Use: Emits a string to a stream.
- */
-
-static void putstring(FILE *fp, const char *p, unsigned raw)
-{
- size_t sz = strlen(p);
- unsigned qq;
- const char *q;
-
- /* --- Just write the string null terminated if raw --- */
-
- if (raw) {
- fwrite(p, 1, sz + 1, fp);
- return;
- }
-
- /* --- Check for any dodgy characters --- */
-
- qq = 0;
- for (q = p; *q; q++) {
- if (isspace((unsigned char)*q)) {
- qq = '\"';
- break;
- }
- }
-
- if (qq)
- putc(qq, fp);
-
- /* --- Emit the string --- */
-
- for (q = p; *q; q++) {
- switch (*q) {
- case '\a': fputc('\\', fp); fputc('a', fp); break;
- case '\b': fputc('\\', fp); fputc('b', fp); break;
- case '\f': fputc('\\', fp); fputc('f', fp); break;
- case '\n': fputc('\\', fp); fputc('n', fp); break;
- case '\r': fputc('\\', fp); fputc('r', fp); break;
- case '\t': fputc('\\', fp); fputc('t', fp); break;
- case '\v': fputc('\\', fp); fputc('v', fp); break;
- case '`': fputc('\\', fp); fputc('`', fp); break;
- case '\'': fputc('\\', fp); fputc('\'', fp); break;
- case '\"': fputc('\\', fp); fputc('\"', fp); break;
- case '#': fputc('\\', fp); fputc('#', fp); break;
- default:
- putc(*q, fp);
- break;
- }
- }
-
- /* --- Done --- */
-
- if (qq)
- putc(qq, fp);
-}
-
/*----- Guts --------------------------------------------------------------*/
-static int checkhash(const char *file, unsigned f,
- const gchash *gch, const encodeops *e)
+static int checkhash(const gchash *gch, unsigned f,
+ const char *file, const encodeops *e)
{
int rc;
FILE *fp;
if (!file || strcmp(file, "-") == 0)
fp = stdin;
- else if ((fp = fopen(file, f & f_raw ? "r" : "rb")) == 0) {
+ else if ((fp = fopen(file, f & GSF_RAW ? "r" : "rb")) == 0) {
moan("couldn't open `%s': %s", file, strerror(errno));
return (EXIT_FAILURE);
}
if (e->get(q, buf, gch->hashsz, 0) < gch->hashsz)
continue;
if (*p == '*')
- ff |= f_binary;
+ ff |= FHF_BINARY;
else if (*p != ' ')
continue;
p++;
if (f & f_escape) {
DRESET(&dd);
- getstring(0, p, &dd, 0);
+ getstring(&p, &dd, GSF_STRING);
p = dd.buf;
}
- if (fhash(p, ff, gch, buf + gch->hashsz)) {
+ if (fhash(gch, ff, p, buf + gch->hashsz)) {
moan("couldn't read `%s': %s", p, strerror(errno));
rc = EXIT_FAILURE;
continue;
return (0);
}
-static int dohash(const char *file, unsigned f,
- const gchash *gch, const encodeops *e)
+static int dohash(const gchash *gch, unsigned f,
+ const char *file, const encodeops *e)
{
int rc = 0;
octet *p = xmalloc(gch->hashsz);
- if (fhash(file, f, gch, p)) {
+ if (fhash(gch, f, file, p)) {
moan("couldn't read `%s': %s", file ? file : "<stdin>", strerror(errno));
rc = EXIT_FAILURE;
} else {
e->put(p, gch->hashsz, stdout);
if (file) {
fputc(' ', stdout);
- fputc(f & f_binary ? '*' : ' ', stdout);
+ fputc(f & FHF_BINARY ? '*' : ' ', stdout);
if (f & f_escape)
putstring(stdout, file, 0);
else
return (rc);
}
-static int dofile(const char *file, unsigned f,
- const gchash *gch, const encodeops *e)
-{
- return (f & f_check ? checkhash : dohash)(file, f, gch, e);
-}
+static int dofile(const gchash *gch, unsigned f,
+ const char *file, const encodeops *e)
+ { return (f & f_check ? checkhash : dohash)(gch, f, file, e); }
-static int hashfiles(const char *file, unsigned f,
- const gchash *gch, const encodeops *e)
+static int hashfiles(const gchash *gch, unsigned f,
+ const char *file, const encodeops *e)
{
FILE *fp;
dstr d = DSTR_INIT;
if (!file || strcmp(file, "-") == 0)
fp = stdin;
- else if ((fp = fopen(file, f & f_raw ? "r" : "rb")) == 0) {
+ else if ((fp = fopen(file, f & GSF_RAW ? "r" : "rb")) == 0) {
moan("couldn't open `%s': %s", file, strerror(errno));
return (EXIT_FAILURE);
}
for (;;) {
DRESET(&d);
- if (getstring(fp, 0, &d, f & f_raw))
+ if (getstring(fp, &d, GSF_FILE | f))
break;
- if ((rrc = dofile(d.buf, f, gch, e)) != 0)
+ if ((rrc = dofile(gch, f, d.buf, e)) != 0)
rc = rrc;
}
return (rc);
}
-static int hashsum(const char *file, unsigned f,
- const gchash *gch, const encodeops *e)
-{
- return (f & f_files ? hashfiles : dofile)(file, f, gch, e);
-}
+static int hashsum(const gchash *gch, unsigned f,
+ const char *file, const encodeops *e)
+ { return (f & f_files ? hashfiles : dofile)(gch, f, file, e); }
/*----- Main driver -------------------------------------------------------*/
f |= f_files;
break;
case '0':
- f |= f_raw;
+ f |= GSF_RAW;
break;
case 'e':
f |= f_escape;
f |= f_check;
break;
case 'b':
- f |= f_binary;
+ f |= FHF_BINARY;
break;
case 'v':
f |= f_verbose;
break;
case 'p':
- f |= f_progress;
+ f |= FHF_PROGRESS;
break;
default:
f |= f_bogus;
if (f & f_escape) fputs("#escape\n", stdout);
}
if (!argc)
- rc = hashsum(0, f, gch, e);
+ rc = hashsum(gch, f, 0, e);
else {
int i;
int rrc;
rc = 0;
for (i = 0; i < argc; i++) {
- if ((rrc = hashsum(argv[i], f, gch, e)) != 0)
+ if ((rrc = hashsum(gch, f, argv[i], e)) != 0)
rc = rrc;
}
}