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 }, | |
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 -------------------------------------------------*/ |