chiark / gitweb /
codec/{base32,hex}.h: Include `codec.h'.
[mLib] / codec / bincode.c
CommitLineData
236f657b
MW
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
49static 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
60static 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
75static 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
82static void version(FILE *fp)
83 { pquis(fp, "$, mLib version " VERSION "\n"); }
84
85static void help(FILE *fp)
86{
87 int i;
88
89 version(fp);
90 fputc('\n', fp);
91 usage(fp);
92 fputs("\n\
93Encodes 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
116static 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
133int 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 -------------------------------------------------*/