chiark / gitweb /
codec/{base32,hex}.h: Include `codec.h'.
[mLib] / codec / bincode.c
1 /* -*-c-*-
2  *
3  * Common test driver for encoding and decoding
4  *
5  * (c) 2009 Straylight/Edgeware
6  */
7
8 /*----- Licensing notice --------------------------------------------------*
9  *
10  * This file is part of the mLib utilities library.
11  *
12  * mLib is free software; you can redistribute it and/or modify
13  * it under the terms of the GNU Library General Public License as
14  * published by the Free Software Foundation; either version 2 of the
15  * License, or (at your option) any later version.
16  *
17  * mLib is distributed in the hope that it will be useful,
18  * but WITHOUT ANY WARRANTY; without even the implied warranty of
19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20  * GNU Library General Public License for more details.
21  *
22  * You should have received a copy of the GNU Library General Public
23  * License along with mLib; if not, write to the Free
24  * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
25  * MA 02111-1307, USA.
26  */
27
28 /*----- Header files ------------------------------------------------------*/
29
30 #include "config.h"
31
32 #include <errno.h>
33 #include <stdio.h>
34 #include <stdlib.h>
35 #include <string.h>
36
37 #include "codec.h"
38 #include "dstr.h"
39 #include "mdwopt.h"
40 #include "quis.h"
41 #include "report.h"
42
43 #include "base64.h"
44 #include "base32.h"
45 #include "hex.h"
46
47 /*----- Static variables --------------------------------------------------*/
48
49 static const codec_class *cctab[] = {
50   &base64_class,
51   &base64url_class,
52   &file64_class,
53   &base32_class,
54   &base32hex_class,
55   &hex_class,
56   &null_codec_class,
57   0,
58 };
59
60 static const struct { const char *name; unsigned f; } flagtab[] = {
61   { "lowerc",           CDCF_LOWERC },
62   { "igncase",          CDCF_IGNCASE },
63   { "noeqpad",          CDCF_NOEQPAD },
64   { "igneqpad",         CDCF_IGNEQPAD },
65   { "igneqmid",         CDCF_IGNEQMID },
66   { "ignzpad",          CDCF_IGNZPAD },
67   { "ignnewl",          CDCF_IGNNEWL },
68   { "igninvch",         CDCF_IGNINVCH },
69   { "ignjunk",          CDCF_IGNJUNK },
70   { 0,                  0, }
71 };
72
73 /*----- Main code ---------------------------------------------------------*/
74
75 static void usage(FILE *fp)
76 {
77   pquis(fp,
78         "Usage: $ [-de] [-f FLAGS] [-i INDENT] [-m MAXLINE] [-o OUTPUT]\n"
79         "\tCODEC [FILE ...]\n");
80 }
81
82 static void version(FILE *fp)
83   { pquis(fp, "$, mLib version " VERSION "\n"); }
84
85 static void help(FILE *fp)
86 {
87   int i;
88
89   version(fp);
90   fputc('\n', fp);
91   usage(fp);
92   fputs("\n\
93 Encodes and decodes binary files.  Options provided:\n\
94 \n\
95 -h, --help              Show this help text.\n\
96 -v, --version           Show the program's version number.\n\
97 -u, --usage             Show a terse usage message.\n\
98 \n\
99 -d, --decode            Decode binary FILEs.\n\
100 -e, --encode            Encode binary FILEs (default).\n\
101 -f, --flags=FLAGS       Set encoding/decoding FLAGS.\n\
102 -i, --indent=INDENT     Indent each line with INDENT.\n\
103 -m, --maxline=MAXLINE   Limit line length to (about) MAXLINE.\n\
104 -o, --output=OUTPUT     Write encoded/decoded data to OUTPUT.\n\
105 \n", fp);
106 #define ENUM(label, tab, end, getname) do {                             \
107   fputs(label ":", fp);                                                 \
108   for (i = 0; tab[i]end; i++) fprintf(fp, " %s", tab[i]getname);        \
109   fputc('\n', fp);                                                      \
110 } while (0)
111   ENUM("Codecs", cctab, != 0, ->name);
112   ENUM("Flags", flagtab, .name, .name);
113 #undef ENUM
114 }
115
116 static void code(codec *c, const char *ifile, FILE *ifp, FILE *ofp)
117 {
118   dstr d = DSTR_INIT;
119   char buf[4096];
120   size_t n;
121   int err;
122
123   do {
124     n = fread(buf, 1, sizeof(buf), ifp);
125     DRESET(&d);
126     if ((err = c->ops->code(c, buf, n, &d)) != 0)
127       die(EXIT_FAILURE, "decoding error: %s", codec_strerror(err));
128   } while (fwrite(d.buf, 1, d.len, ofp) == d.len && n == sizeof(buf));
129   if (ferror(ifp))
130     die(EXIT_FAILURE, "error reading `%s': %s", ifile, strerror(errno));
131 }
132
133 int main(int argc, char *argv[])
134 {
135   enum { encode, decode };
136   int mode = encode;
137   const codec_class **cc;
138   codec *c;
139   const char *indent = "";
140   const char *imode, *omode, *ofile = 0;
141   unsigned maxline = 64;
142   unsigned f = CDCF_IGNNEWL;
143   const char *p;
144   char *q;
145   FILE *ifp, *ofp = stdout;
146   dstr d = DSTR_INIT;
147   int i;
148   int sense;
149   size_t n;
150   int err;
151
152 #define f_bogus 32768u
153
154   ego(argv[0]);
155
156   for (;;) {
157     static const struct option opts[] = {
158       { "help",         0,              0,      'h' },
159       { "version",      0,              0,      'v' },
160       { "usage",        0,              0,      'u' },
161       { "encode",       0,              0,      'e' },
162       { "decode",       0,              0,      'd' },
163       { "indent",       OPTF_ARGREQ,    0,      'i' },
164       { "maxline",      OPTF_ARGREQ,    0,      'm' },
165       { "output",       OPTF_ARGREQ,    0,      'o' },
166       { "flags",        OPTF_ARGREQ,    0,      'f' },
167       { 0,              0,              0,      0 }
168     };
169
170     if ((i = mdwopt(argc, argv, "hvu" "edf:i:m:o:", opts, 0, 0, 0)) < 0)
171       break;
172     switch (i) {
173       case 'h': help(stdout); exit(0);
174       case 'v': version(stdout); exit(0);
175       case 'u': usage(stdout); exit(0);
176
177       case 'e': mode = encode; break;
178       case 'd': mode = decode; break;
179       case 'i': indent = optarg; break;
180       case 'o': ofile = optarg; break;
181
182       case 'm':
183         errno = 0;
184         maxline = strtoul(optarg, &q, 0);
185         if (*q || errno) die(EXIT_FAILURE, "bad integer `%s'", optarg);
186         break;
187
188       case 'f':
189         p = optarg;
190         while (*p) {
191           if (*p == '-') { sense = 0; p++; }
192           else if (*p == '+') { sense = 1; p++; }
193           else sense = 1;
194           n = strcspn(p, ",");
195           for (i = 0; flagtab[i].name; i++) {
196             if (strlen(flagtab[i].name) == n &&
197                 strncmp(flagtab[i].name, p, n) == 0)
198               goto found;
199           }
200           die(EXIT_FAILURE, "unknown flag `%.*s'", (int)n, p);
201         found:
202           if (sense) f |= flagtab[i].f;
203           else f &= ~flagtab[i].f;
204           p += n;
205           if (*p == ',') p++;
206         }
207         break;
208
209       default: f |= f_bogus; break;
210     }
211   }
212   argv += optind; argc -= optind;
213   if ((f & f_bogus) || !argc) { usage(stderr); exit(EXIT_FAILURE); }
214
215   for (cc = cctab;; cc++) {
216     if (!*cc) die(EXIT_FAILURE, "unknown codec `%s'", *argv);
217     else if (strcmp(*argv, (*cc)->name) == 0) break;
218   }
219   argv++; argc--;
220
221   switch (mode) {
222     case encode:
223       DPUTC(&d, '\n');
224       for (p = indent;; p++) {
225         switch (*p) {
226           case '\\':
227             p++;
228             switch (*p) {
229               case 'a': DPUTC(&d, '\n'); break;
230               case 'b': DPUTC(&d, '\b'); break;
231               case 'f': DPUTC(&d, '\f'); break;
232               case 'n': DPUTC(&d, '\n'); break;
233               case 'r': DPUTC(&d, '\r'); break;
234               case 't': DPUTC(&d, '\t'); break;
235               case 'v': DPUTC(&d, '\v'); break;
236               case 0: goto done_indent; break;
237               default: DPUTC(&d, *p); break;
238             }
239             break;
240           case 0:
241             goto done_indent;
242           default:
243             DPUTC(&d, *p);
244             break;
245         }
246       }
247     done_indent:
248       DPUTZ(&d);
249       c = (*cc)->encoder(f, d.buf, maxline);
250       imode = "rb"; omode = "w";
251       break;
252     case decode:
253       c = (*cc)->decoder(f);
254       imode = "r"; omode = "wb";
255       break;
256     default:
257       abort();
258   }
259
260   if (ofile && (ofp = fopen(ofile, omode)) == 0) {
261     die(EXIT_FAILURE, "couldn't open `%s' for writing: %s",
262         ofile, strerror(errno));
263   }
264
265   if (mode == encode) fwrite(d.buf + 1, 1, d.len - 1, ofp);
266   if (!argc)
267     code(c, "<stdin>", stdin, ofp);
268   else for (i = 0; i < argc; i++) {
269     if (strcmp(argv[i], "-") == 0)
270       code(c, "<stdin>", stdin, ofp);
271     else if ((ifp = fopen(argv[i], imode)) == 0) {
272       die(EXIT_FAILURE, "couldn't open `%s' for reading: %s",
273           argv[i], strerror(errno));
274     } else {
275       code(c, argv[i], ifp, ofp);
276       fclose(ifp);
277     }
278   }
279   DRESET(&d);
280   if ((err = c->ops->code(c, 0, 0, &d)) != 0)
281     die(EXIT_FAILURE, "decoding error: %s", codec_strerror(err));
282   if (mode == encode) DPUTC(&d, '\n');
283   fwrite(d.buf, 1, d.len, ofp);
284   c->ops->destroy(c); DDESTROY(&d);
285
286   if (ferror(ofp) || fflush(ofp) || fclose(ofp)) {
287     die(EXIT_FAILURE, "error writing to `%s': %s",
288         ofile ? ofile : "<stdout>", strerror(errno));
289   }
290   return (0);
291 }
292
293 /*----- That's all, folks -------------------------------------------------*/