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