codec, baseconv: Cleanup of the various binary encoding functions.
[mLib] / codec / bincode.c
diff --git a/codec/bincode.c b/codec/bincode.c
new file mode 100644 (file)
index 0000000..0a7d5c5
--- /dev/null
@@ -0,0 +1,293 @@
+/* -*-c-*-
+ *
+ * Common test driver for encoding and decoding
+ *
+ * (c) 2009 Straylight/Edgeware
+ */
+
+/*----- Licensing notice --------------------------------------------------*
+ *
+ * This file is part of the mLib utilities library.
+ *
+ * mLib 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.
+ *
+ * mLib 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 mLib; if not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ * MA 02111-1307, USA.
+ */
+
+/*----- Header files ------------------------------------------------------*/
+
+#include "config.h"
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "codec.h"
+#include "dstr.h"
+#include "mdwopt.h"
+#include "quis.h"
+#include "report.h"
+
+#include "base64.h"
+#include "base32.h"
+#include "hex.h"
+
+/*----- Static variables --------------------------------------------------*/
+
+static const codec_class *cctab[] = {
+  &base64_class,
+  &base64url_class,
+  &file64_class,
+  &base32_class,
+  &base32hex_class,
+  &hex_class,
+  &null_codec_class,
+  0,
+};
+
+static const struct { const char *name; unsigned f; } flagtab[] = {
+  { "lowerc",          CDCF_LOWERC },
+  { "igncase",         CDCF_IGNCASE },
+  { "noeqpad",         CDCF_NOEQPAD },
+  { "igneqpad",                CDCF_IGNEQPAD },
+  { "igneqmid",                CDCF_IGNEQMID },
+  { "ignzpad",         CDCF_IGNZPAD },
+  { "ignnewl",         CDCF_IGNNEWL },
+  { "igninvch",                CDCF_IGNINVCH },
+  { "ignjunk",         CDCF_IGNJUNK },
+  { 0,                 0, }
+};
+
+/*----- Main code ---------------------------------------------------------*/
+
+static void usage(FILE *fp)
+{
+  pquis(fp,
+       "Usage: $ [-de] [-f FLAGS] [-i INDENT] [-m MAXLINE] [-o OUTPUT]\n"
+       "\tCODEC [FILE ...]\n");
+}
+
+static void version(FILE *fp)
+  { pquis(fp, "$, mLib version " VERSION "\n"); }
+
+static void help(FILE *fp)
+{
+  int i;
+
+  version(fp);
+  fputc('\n', fp);
+  usage(fp);
+  fputs("\n\
+Encodes and decodes binary files.  Options provided:\n\
+\n\
+-h, --help             Show this help text.\n\
+-v, --version          Show the program's version number.\n\
+-u, --usage            Show a terse usage message.\n\
+\n\
+-d, --decode           Decode binary FILEs.\n\
+-e, --encode           Encode binary FILEs (default).\n\
+-f, --flags=FLAGS      Set encoding/decoding FLAGS.\n\
+-i, --indent=INDENT    Indent each line with INDENT.\n\
+-m, --maxline=MAXLINE  Limit line length to (about) MAXLINE.\n\
+-o, --output=OUTPUT    Write encoded/decoded data to OUTPUT.\n\
+\n", fp);
+#define ENUM(label, tab, end, getname) do {                            \
+  fputs(label ":", fp);                                                        \
+  for (i = 0; tab[i]end; i++) fprintf(fp, " %s", tab[i]getname);       \
+  fputc('\n', fp);                                                     \
+} while (0)
+  ENUM("Codecs", cctab, != 0, ->name);
+  ENUM("Flags", flagtab, .name, .name);
+#undef ENUM
+}
+
+static void code(codec *c, const char *ifile, FILE *ifp, FILE *ofp)
+{
+  dstr d = DSTR_INIT;
+  char buf[4096];
+  size_t n;
+  int err;
+
+  do {
+    n = fread(buf, 1, sizeof(buf), ifp);
+    DRESET(&d);
+    if ((err = c->ops->code(c, buf, n, &d)) != 0)
+      die(EXIT_FAILURE, "decoding error: %s", codec_strerror(err));
+  } while (fwrite(d.buf, 1, d.len, ofp) == d.len && n == sizeof(buf));
+  if (ferror(ifp))
+    die(EXIT_FAILURE, "error reading `%s': %s", ifile, strerror(errno));
+}
+
+int main(int argc, char *argv[])
+{
+  enum { encode, decode };
+  int mode = encode;
+  const codec_class **cc;
+  codec *c;
+  const char *indent = "";
+  const char *imode, *omode, *ofile = 0;
+  unsigned maxline = 64;
+  unsigned f = CDCF_IGNNEWL;
+  const char *p;
+  char *q;
+  FILE *ifp, *ofp = stdout;
+  dstr d = DSTR_INIT;
+  int i;
+  int sense;
+  size_t n;
+  int err;
+
+#define f_bogus 32768u
+
+  ego(argv[0]);
+
+  for (;;) {
+    static const struct option opts[] = {
+      { "help",                0,              0,      'h' },
+      { "version",     0,              0,      'v' },
+      { "usage",       0,              0,      'u' },
+      { "encode",      0,              0,      'e' },
+      { "decode",      0,              0,      'd' },
+      { "indent",      OPTF_ARGREQ,    0,      'i' },
+      { "maxline",     OPTF_ARGREQ,    0,      'm' },
+      { "output",      OPTF_ARGREQ,    0,      'o' },
+      { "flags",       OPTF_ARGREQ,    0,      'f' },
+      { 0,             0,              0,      0 }
+    };
+
+    if ((i = mdwopt(argc, argv, "hvu" "edf:i:m:o:", opts, 0, 0, 0)) < 0)
+      break;
+    switch (i) {
+      case 'h': help(stdout); exit(0);
+      case 'v': version(stdout); exit(0);
+      case 'u': usage(stdout); exit(0);
+
+      case 'e': mode = encode; break;
+      case 'd': mode = decode; break;
+      case 'i': indent = optarg; break;
+      case 'o': ofile = optarg; break;
+
+      case 'm':
+       errno = 0;
+       maxline = strtoul(optarg, &q, 0);
+       if (*q || errno) die(EXIT_FAILURE, "bad integer `%s'", optarg);
+       break;
+
+      case 'f':
+       p = optarg;
+       while (*p) {
+         if (*p == '-') { sense = 0; p++; }
+         else if (*p == '+') { sense = 1; p++; }
+         else sense = 1;
+         n = strcspn(p, ",");
+         for (i = 0; flagtab[i].name; i++) {
+           if (strlen(flagtab[i].name) == n &&
+               strncmp(flagtab[i].name, p, n) == 0)
+             goto found;
+         }
+         die(EXIT_FAILURE, "unknown flag `%.*s'", (int)n, p);
+       found:
+         if (sense) f |= flagtab[i].f;
+         else f &= ~flagtab[i].f;
+         p += n;
+         if (*p == ',') p++;
+       }
+       break;
+
+      default: f |= f_bogus; break;
+    }
+  }
+  argv += optind; argc -= optind;
+  if ((f & f_bogus) || !argc) { usage(stderr); exit(EXIT_FAILURE); }
+
+  for (cc = cctab;; cc++) {
+    if (!*cc) die(EXIT_FAILURE, "unknown codec `%s'", *argv);
+    else if (strcmp(*argv, (*cc)->name) == 0) break;
+  }
+  argv++; argc--;
+
+  switch (mode) {
+    case encode:
+      DPUTC(&d, '\n');
+      for (p = indent;; p++) {
+       switch (*p) {
+         case '\\':
+           p++;
+           switch (*p) {
+             case 'a': DPUTC(&d, '\n'); break;
+             case 'b': DPUTC(&d, '\b'); break;
+             case 'f': DPUTC(&d, '\f'); break;
+             case 'n': DPUTC(&d, '\n'); break;
+             case 'r': DPUTC(&d, '\r'); break;
+             case 't': DPUTC(&d, '\t'); break;
+             case 'v': DPUTC(&d, '\v'); break;
+             case 0: goto done_indent; break;
+             default: DPUTC(&d, *p); break;
+           }
+           break;
+         case 0:
+           goto done_indent;
+         default:
+           DPUTC(&d, *p);
+           break;
+       }
+      }
+    done_indent:
+      DPUTZ(&d);
+      c = (*cc)->encoder(f, d.buf, maxline);
+      imode = "rb"; omode = "w";
+      break;
+    case decode:
+      c = (*cc)->decoder(f);
+      imode = "r"; omode = "wb";
+      break;
+    default:
+      abort();
+  }
+
+  if (ofile && (ofp = fopen(ofile, omode)) == 0) {
+    die(EXIT_FAILURE, "couldn't open `%s' for writing: %s",
+       ofile, strerror(errno));
+  }
+
+  if (mode == encode) fwrite(d.buf + 1, 1, d.len - 1, ofp);
+  if (!argc)
+    code(c, "<stdin>", stdin, ofp);
+  else for (i = 0; i < argc; i++) {
+    if (strcmp(argv[i], "-") == 0)
+      code(c, "<stdin>", stdin, ofp);
+    else if ((ifp = fopen(argv[i], imode)) == 0) {
+      die(EXIT_FAILURE, "couldn't open `%s' for reading: %s",
+         argv[i], strerror(errno));
+    } else {
+      code(c, argv[i], ifp, ofp);
+      fclose(ifp);
+    }
+  }
+  DRESET(&d);
+  if ((err = c->ops->code(c, 0, 0, &d)) != 0)
+    die(EXIT_FAILURE, "decoding error: %s", codec_strerror(err));
+  if (mode == encode) DPUTC(&d, '\n');
+  fwrite(d.buf, 1, d.len, ofp);
+  c->ops->destroy(c); DDESTROY(&d);
+
+  if (ferror(ofp) || fflush(ofp) || fclose(ofp)) {
+    die(EXIT_FAILURE, "error writing to `%s': %s",
+       ofile ? ofile : "<stdout>", strerror(errno));
+  }
+  return (0);
+}
+
+/*----- That's all, folks -------------------------------------------------*/