Commit | Line | Data |
---|---|---|
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 | ||
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 }, | |
09fbf4d0 | 68 | { "ignspc", CDCF_IGNSPC }, |
236f657b MW |
69 | { "igninvch", CDCF_IGNINVCH }, |
70 | { "ignjunk", CDCF_IGNJUNK }, | |
71 | { 0, 0, } | |
72 | }; | |
73 | ||
74 | /*----- Main code ---------------------------------------------------------*/ | |
75 | ||
76 | static void usage(FILE *fp) | |
77 | { | |
78 | pquis(fp, | |
79 | "Usage: $ [-de] [-f FLAGS] [-i INDENT] [-m MAXLINE] [-o OUTPUT]\n" | |
80 | "\tCODEC [FILE ...]\n"); | |
81 | } | |
82 | ||
83 | static void version(FILE *fp) | |
84 | { pquis(fp, "$, mLib version " VERSION "\n"); } | |
85 | ||
86 | static void help(FILE *fp) | |
87 | { | |
88 | int i; | |
89 | ||
90 | version(fp); | |
91 | fputc('\n', fp); | |
92 | usage(fp); | |
93 | fputs("\n\ | |
94 | Encodes and decodes binary files. Options provided:\n\ | |
95 | \n\ | |
96 | -h, --help Show this help text.\n\ | |
97 | -v, --version Show the program's version number.\n\ | |
98 | -u, --usage Show a terse usage message.\n\ | |
99 | \n\ | |
100 | -d, --decode Decode binary FILEs.\n\ | |
101 | -e, --encode Encode binary FILEs (default).\n\ | |
102 | -f, --flags=FLAGS Set encoding/decoding FLAGS.\n\ | |
103 | -i, --indent=INDENT Indent each line with INDENT.\n\ | |
104 | -m, --maxline=MAXLINE Limit line length to (about) MAXLINE.\n\ | |
105 | -o, --output=OUTPUT Write encoded/decoded data to OUTPUT.\n\ | |
106 | \n", fp); | |
107 | #define ENUM(label, tab, end, getname) do { \ | |
108 | fputs(label ":", fp); \ | |
109 | for (i = 0; tab[i]end; i++) fprintf(fp, " %s", tab[i]getname); \ | |
110 | fputc('\n', fp); \ | |
111 | } while (0) | |
112 | ENUM("Codecs", cctab, != 0, ->name); | |
113 | ENUM("Flags", flagtab, .name, .name); | |
114 | #undef ENUM | |
115 | } | |
116 | ||
117 | static void code(codec *c, const char *ifile, FILE *ifp, FILE *ofp) | |
118 | { | |
119 | dstr d = DSTR_INIT; | |
120 | char buf[4096]; | |
121 | size_t n; | |
122 | int err; | |
123 | ||
124 | do { | |
125 | n = fread(buf, 1, sizeof(buf), ifp); | |
126 | DRESET(&d); | |
127 | if ((err = c->ops->code(c, buf, n, &d)) != 0) | |
128 | die(EXIT_FAILURE, "decoding error: %s", codec_strerror(err)); | |
129 | } while (fwrite(d.buf, 1, d.len, ofp) == d.len && n == sizeof(buf)); | |
130 | if (ferror(ifp)) | |
131 | die(EXIT_FAILURE, "error reading `%s': %s", ifile, strerror(errno)); | |
132 | } | |
133 | ||
134 | int main(int argc, char *argv[]) | |
135 | { | |
136 | enum { encode, decode }; | |
137 | int mode = encode; | |
138 | const codec_class **cc; | |
139 | codec *c; | |
140 | const char *indent = ""; | |
141 | const char *imode, *omode, *ofile = 0; | |
142 | unsigned maxline = 64; | |
09fbf4d0 | 143 | unsigned f = CDCF_IGNSPC | CDCF_IGNNEWL; |
236f657b MW |
144 | const char *p; |
145 | char *q; | |
146 | FILE *ifp, *ofp = stdout; | |
147 | dstr d = DSTR_INIT; | |
148 | int i; | |
149 | int sense; | |
150 | size_t n; | |
151 | int err; | |
152 | ||
153 | #define f_bogus 32768u | |
154 | ||
155 | ego(argv[0]); | |
156 | ||
157 | for (;;) { | |
158 | static const struct option opts[] = { | |
159 | { "help", 0, 0, 'h' }, | |
160 | { "version", 0, 0, 'v' }, | |
161 | { "usage", 0, 0, 'u' }, | |
162 | { "encode", 0, 0, 'e' }, | |
163 | { "decode", 0, 0, 'd' }, | |
164 | { "indent", OPTF_ARGREQ, 0, 'i' }, | |
165 | { "maxline", OPTF_ARGREQ, 0, 'm' }, | |
166 | { "output", OPTF_ARGREQ, 0, 'o' }, | |
167 | { "flags", OPTF_ARGREQ, 0, 'f' }, | |
168 | { 0, 0, 0, 0 } | |
169 | }; | |
170 | ||
171 | if ((i = mdwopt(argc, argv, "hvu" "edf:i:m:o:", opts, 0, 0, 0)) < 0) | |
172 | break; | |
173 | switch (i) { | |
174 | case 'h': help(stdout); exit(0); | |
175 | case 'v': version(stdout); exit(0); | |
176 | case 'u': usage(stdout); exit(0); | |
177 | ||
178 | case 'e': mode = encode; break; | |
179 | case 'd': mode = decode; break; | |
180 | case 'i': indent = optarg; break; | |
181 | case 'o': ofile = optarg; break; | |
182 | ||
183 | case 'm': | |
184 | errno = 0; | |
185 | maxline = strtoul(optarg, &q, 0); | |
186 | if (*q || errno) die(EXIT_FAILURE, "bad integer `%s'", optarg); | |
187 | break; | |
188 | ||
189 | case 'f': | |
190 | p = optarg; | |
191 | while (*p) { | |
192 | if (*p == '-') { sense = 0; p++; } | |
193 | else if (*p == '+') { sense = 1; p++; } | |
194 | else sense = 1; | |
195 | n = strcspn(p, ","); | |
196 | for (i = 0; flagtab[i].name; i++) { | |
197 | if (strlen(flagtab[i].name) == n && | |
198 | strncmp(flagtab[i].name, p, n) == 0) | |
199 | goto found; | |
200 | } | |
201 | die(EXIT_FAILURE, "unknown flag `%.*s'", (int)n, p); | |
202 | found: | |
203 | if (sense) f |= flagtab[i].f; | |
204 | else f &= ~flagtab[i].f; | |
205 | p += n; | |
206 | if (*p == ',') p++; | |
207 | } | |
208 | break; | |
209 | ||
210 | default: f |= f_bogus; break; | |
211 | } | |
212 | } | |
213 | argv += optind; argc -= optind; | |
214 | if ((f & f_bogus) || !argc) { usage(stderr); exit(EXIT_FAILURE); } | |
215 | ||
216 | for (cc = cctab;; cc++) { | |
217 | if (!*cc) die(EXIT_FAILURE, "unknown codec `%s'", *argv); | |
218 | else if (strcmp(*argv, (*cc)->name) == 0) break; | |
219 | } | |
220 | argv++; argc--; | |
221 | ||
222 | switch (mode) { | |
223 | case encode: | |
224 | DPUTC(&d, '\n'); | |
225 | for (p = indent;; p++) { | |
226 | switch (*p) { | |
227 | case '\\': | |
228 | p++; | |
229 | switch (*p) { | |
230 | case 'a': DPUTC(&d, '\n'); break; | |
231 | case 'b': DPUTC(&d, '\b'); break; | |
232 | case 'f': DPUTC(&d, '\f'); break; | |
233 | case 'n': DPUTC(&d, '\n'); break; | |
234 | case 'r': DPUTC(&d, '\r'); break; | |
235 | case 't': DPUTC(&d, '\t'); break; | |
236 | case 'v': DPUTC(&d, '\v'); break; | |
237 | case 0: goto done_indent; break; | |
238 | default: DPUTC(&d, *p); break; | |
239 | } | |
240 | break; | |
241 | case 0: | |
242 | goto done_indent; | |
243 | default: | |
244 | DPUTC(&d, *p); | |
245 | break; | |
246 | } | |
247 | } | |
248 | done_indent: | |
249 | DPUTZ(&d); | |
250 | c = (*cc)->encoder(f, d.buf, maxline); | |
251 | imode = "rb"; omode = "w"; | |
252 | break; | |
253 | case decode: | |
254 | c = (*cc)->decoder(f); | |
255 | imode = "r"; omode = "wb"; | |
256 | break; | |
257 | default: | |
258 | abort(); | |
259 | } | |
260 | ||
261 | if (ofile && (ofp = fopen(ofile, omode)) == 0) { | |
262 | die(EXIT_FAILURE, "couldn't open `%s' for writing: %s", | |
263 | ofile, strerror(errno)); | |
264 | } | |
265 | ||
266 | if (mode == encode) fwrite(d.buf + 1, 1, d.len - 1, ofp); | |
267 | if (!argc) | |
268 | code(c, "<stdin>", stdin, ofp); | |
269 | else for (i = 0; i < argc; i++) { | |
270 | if (strcmp(argv[i], "-") == 0) | |
271 | code(c, "<stdin>", stdin, ofp); | |
272 | else if ((ifp = fopen(argv[i], imode)) == 0) { | |
273 | die(EXIT_FAILURE, "couldn't open `%s' for reading: %s", | |
274 | argv[i], strerror(errno)); | |
275 | } else { | |
276 | code(c, argv[i], ifp, ofp); | |
277 | fclose(ifp); | |
278 | } | |
279 | } | |
280 | DRESET(&d); | |
281 | if ((err = c->ops->code(c, 0, 0, &d)) != 0) | |
282 | die(EXIT_FAILURE, "decoding error: %s", codec_strerror(err)); | |
283 | if (mode == encode) DPUTC(&d, '\n'); | |
284 | fwrite(d.buf, 1, d.len, ofp); | |
285 | c->ops->destroy(c); DDESTROY(&d); | |
286 | ||
287 | if (ferror(ofp) || fflush(ofp) || fclose(ofp)) { | |
288 | die(EXIT_FAILURE, "error writing to `%s': %s", | |
289 | ofile ? ofile : "<stdout>", strerror(errno)); | |
290 | } | |
291 | return (0); | |
292 | } | |
293 | ||
294 | /*----- That's all, folks -------------------------------------------------*/ |