chiark / gitweb /
Provide test cases for maxlen functions
[base91.git] / cli.c
1 /*
2  * basE91 command line front-end
3  *
4  * Copyright (c) 2000-2006 Joachim Henke
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions are met:
9  *
10  *  - Redistributions of source code must retain the above copyright notice,
11  *    this list of conditions and the following disclaimer.
12  *  - Redistributions in binary form must reproduce the above copyright notice,
13  *    this list of conditions and the following disclaimer in the documentation
14  *    and/or other materials provided with the distribution.
15  *  - Neither the name of Joachim Henke nor the names of his contributors may
16  *    be used to endorse or promote products derived from this software without
17  *    specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
23  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29  * POSSIBILITY OF SUCH DAMAGE.
30  */
31
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <string.h>
35 #ifdef _WIN32
36 #include <fcntl.h>
37 #include <unistd.h>
38 #endif
39 #include <getopt.h>
40 #include "base91.h"
41
42 #define FLG_D 1
43 #define FLG_V 2
44 #define FLG_VV 4
45
46 static char status[32];
47 static const char *progname;
48 static char *ibuf, *obuf;
49 static size_t ibuf_size, llen;
50 static struct basE91 b91;
51
52 static void stream_b91enc_p(void)
53 {
54         size_t itotal = 0;
55         size_t ototal = 0;
56         size_t s;
57
58         while ((s = fread(ibuf, 1, ibuf_size, stdin)) > 0) {
59                 itotal += s;
60                 s = basE91_encode(&b91, ibuf, s, obuf);
61                 ototal += s;
62                 fwrite(obuf, 1, s, stdout);
63         }
64         s = basE91_encode_end(&b91, obuf);      /* empty bit queue */
65         ototal += s;
66         fwrite(obuf, 1, s, stdout);
67
68         sprintf(status, "\t%.2f%%\n", itotal ? (float) ototal / itotal * 100.0 : 1.0);
69 }
70
71 static void stream_b91enc_w(void)
72 {
73         size_t l = llen;
74         size_t ltotal = 0;
75         size_t i, s;
76         char x;
77
78         while ((s = fread(ibuf, 1, ibuf_size, stdin)) > 0) {
79                 s = basE91_encode(&b91, ibuf, s, obuf);
80                 for (i = 0; l <= s; l += llen) {
81                         x = obuf[l];
82                         obuf[l] = '\0';
83                         puts(obuf + i);
84                         ++ltotal;
85                         obuf[l] = x;
86                         i = l;
87                 }
88                 fwrite(obuf + i, 1, s - i, stdout);
89                 l -= s;
90         }
91         s = basE91_encode_end(&b91, obuf);
92         if (s || l < llen) {
93                 obuf[s] = '\0';
94                 if (s > l) {
95                         x = obuf[1];
96                         obuf[1] = '\0';
97                         puts(obuf);
98                         ++ltotal;
99                         obuf[0] = x;
100                 }
101                 puts(obuf);
102                 ++ltotal;
103         }
104
105         sprintf(status, "\t%lu lines\n", (unsigned long) ltotal);
106 }
107
108 static void stream_b91dec(void)
109 {
110         size_t s;
111
112         while ((s = fread(ibuf, 1, ibuf_size, stdin)) > 0) {
113                 s = basE91_decode(&b91, ibuf, s, obuf);
114                 fwrite(obuf, 1, s, stdout);
115         }
116         s = basE91_decode_end(&b91, obuf);      /* empty bit queue */
117         fwrite(obuf, 1, s, stdout);
118
119         sprintf(status, "done\n");
120 }
121
122 static int init_flags(const char *p)
123 {
124         size_t l = strlen(p);
125
126         if (l > 5) {
127                 progname = p + l - 6;
128                 if (!strcmp(progname, "b91enc"))
129                         return 0;
130                 if (!strcmp(progname, "b91dec"))
131                         return FLG_D;
132         }
133         llen = 76;
134         progname = "base91";
135
136         return 0;
137 }
138
139 int main(int argc, char **argv)
140 {
141         size_t buf_size = 65536;        /* buffer memory defaults to 64 KiB */
142         int flags = init_flags(*argv);
143         const char *ifile = "from standard input";
144         const char *ofile = NULL;
145         int opt;
146         struct option longopts[8] = {
147                 {"decode", no_argument, NULL, 'd'},
148                 {"output", required_argument, NULL, 'o'},
149                 {"verbose", no_argument, NULL, 'v'},
150                 {"wrap", required_argument, NULL, 'w'},
151                 {"help", no_argument, NULL, 'h'},
152                 {"version", no_argument, NULL, 'V'},
153                 {NULL, 0, NULL, 0}
154         };
155
156         while ((opt = getopt_long(argc, argv, "dem:o:vw:hV", longopts, NULL)) != -1)
157                 switch (opt) {
158                 case 'd':
159                         flags |= FLG_D;
160                         break;
161                 case 'e':
162                         flags &= ~FLG_D;
163                         break;
164                 case 'm':
165                         {
166                                 char *t;
167                                 long l = strtol(optarg, &t, 0);
168
169                                 if (t == optarg || strlen(t) > 1 || l < 0) {
170                                         fprintf(stderr, "invalid SIZE argument: `%s'\n", optarg);
171                                         return EXIT_FAILURE;
172                                 }
173                                 buf_size = l;
174                                 switch (*t | 32) {
175                                 case ' ':
176                                 case 'b':
177                                         break;
178                                 case 'k':
179                                         buf_size <<= 10;
180                                         break;
181                                 case 'm':
182                                         buf_size <<= 20;
183                                         break;
184                                 default:
185                                         fprintf(stderr, "invalid SIZE suffix: `%s'\n", t);
186                                         return EXIT_FAILURE;
187                                 }
188                         }
189                         break;
190                 case 'o':
191                         if (strcmp(optarg, "-"))
192                                 ofile = optarg;
193                         break;
194                 case 'v':
195                         flags |= (flags & FLG_V) ? FLG_VV : FLG_V;
196                         break;
197                 case 'w':
198                         {
199                                 char *t;
200                                 long l = strtol(optarg, &t, 0);
201
202                                 if (*t || l < 0) {
203                                         fprintf(stderr, "invalid number of columns: `%s'\n", optarg);
204                                         return EXIT_FAILURE;
205                                 }
206                                 llen = l;
207                         }
208                         break;
209                 case 'h':
210                         printf("Usage: %s [OPTION]... [FILE]\n"
211                                 "basE91 encode or decode FILE, or standard input, to standard output.\n", progname);
212                         puts("\n  -d, --decode\t\tdecode data\n"
213                                 "  -m SIZE\t\tuse SIZE bytes of memory for buffers (suffixes b, K, M)\n"
214                                 "  -o, --output=FILE\twrite to FILE instead of standard output\n"
215                                 "  -v, --verbose\t\tverbose mode\n"
216                                 "  -w, --wrap=COLS\twrap encoded lines after COLS characters (default 76)\n"
217                                 "  --help\t\tdisplay this help and exit\n"
218                                 "  --version\t\toutput version information and exit\n\n"
219                                 "With no FILE, or when FILE is -, read standard input.");
220                         return EXIT_SUCCESS;
221                 case 'V':
222                         printf("%s 0.6.0\nCopyright (c) 2000-2006 Joachim Henke\n", progname);
223                         return EXIT_SUCCESS;
224                 default:
225                         fprintf(stderr, "Try `%s --help' for more information.\n", *argv);
226                         return EXIT_FAILURE;
227                 }
228
229         if (flags & FLG_D) {
230                 ibuf_size = (buf_size - 1) << 3;
231                 if (ibuf_size < 15) {
232                         fputs("SIZE must be >= 3 for decoding\n", stderr);
233                         return EXIT_FAILURE;
234                 }
235                 ibuf_size /= 15;
236         } else {
237                 ibuf_size = (buf_size - 2) << 4;
238                 if (ibuf_size < 29) {
239                         fputs("SIZE must be >= 4 for encoding\n", stderr);
240                         return EXIT_FAILURE;
241                 }
242                 ibuf_size /= 29;
243         }
244
245         if (optind < argc && strcmp(argv[optind], "-")) {
246                 ifile = argv[optind];
247                 if (freopen(ifile, "r", stdin) != stdin) {
248                         perror(ifile);
249                         return EXIT_FAILURE;
250                 }
251         }
252         if (ofile)
253                 if (freopen(ofile, "w", stdout) != stdout) {
254                         perror(ofile);
255                         return EXIT_FAILURE;
256                 }
257
258         if (flags & FLG_VV)
259                 fprintf(stderr, "using %lu bytes for buffers; input buffer: %lu bytes\n", (unsigned long) buf_size, (unsigned long) ibuf_size);
260         obuf = malloc(buf_size);
261         if (!obuf) {
262                 fputs("failed to allocate buffer memory\n", stderr);
263                 return EXIT_FAILURE;
264         }
265
266         basE91_init(&b91);
267 #ifdef _WIN32
268         _setmode(_fileno(stdin), _O_BINARY);
269 #endif
270
271         if (flags & FLG_D) {
272 #ifdef _WIN32
273                 _setmode(_fileno(stdout), _O_BINARY);
274 #endif
275                 ibuf = obuf + 1;        /* create overlapping buffers to use memory efficiently */
276                 if (flags & FLG_V)
277                         fprintf(stderr, "decoding %s ...", ifile);
278                 stream_b91dec();
279         } else {
280                 ibuf = obuf + buf_size - ibuf_size;     /* partial overlap */
281                 if (flags & FLG_V)
282                         fprintf(stderr, "encoding %s ...", ifile);
283                 if (llen)
284                         stream_b91enc_w();
285                 else
286                         stream_b91enc_p();
287         }
288         free(obuf);
289
290         if (flags & FLG_V)
291                 fputs(status, stderr);
292
293         return EXIT_SUCCESS;
294 }