X-Git-Url: https://git.distorted.org.uk/~mdw/mLib/blobdiff_plain/173ff44a439dc7cf51a3f7a433c4d0d444b59293..236f657b6dab66f31f4902cecfc03b4673f5bb98:/codec/bincode.c diff --git a/codec/bincode.c b/codec/bincode.c new file mode 100644 index 0000000..0a7d5c5 --- /dev/null +++ b/codec/bincode.c @@ -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 +#include +#include +#include + +#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, ofp); + else for (i = 0; i < argc; i++) { + if (strcmp(argv[i], "-") == 0) + code(c, "", 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 : "", strerror(errno)); + } + return (0); +} + +/*----- That's all, folks -------------------------------------------------*/