Make gorp better. Deb fixes.
[misc] / gorp.c
1 #include <ctype.h>
2 #include <errno.h>
3 #include <limits.h>
4 #include <stdio.h>
5 #include <stdlib.h>
6 #include <string.h>
7
8 #include <mLib/alloc.h>
9 #include <mLib/base64.h>
10 #include <mLib/dstr.h>
11 #include <mLib/hex.h>
12 #include <mLib/mdwopt.h>
13 #include <mLib/quis.h>
14 #include <mLib/report.h>
15
16 #include <catacomb/rand.h>
17 #include <catacomb/noise.h>
18
19 struct format {
20 const char *name;
21 void (*out)(size_t, unsigned);
22 };
23
24 #define CHUNK 1024
25
26 static void format_base64(size_t n, unsigned line)
27 {
28 unsigned char buf[CHUNK];
29 dstr d = DSTR_INIT;
30 base64_ctx b;
31
32 base64_init(&b);
33 if (line) {
34 b.indent = "\n";
35 b.maxline = line;
36 } else {
37 b.indent = "";
38 b.maxline = 0;
39 }
40
41 while (n) {
42 size_t nn = CHUNK;
43 if (nn > n) nn = n;
44 rand_get(RAND_GLOBAL, buf, nn);
45 base64_encode(&b, buf, nn, &d);
46 DWRITE(&d, stdout);
47 DRESET(&d);
48 n -= nn;
49 }
50 base64_encode(&b, 0, 0, &d);
51 if (!line) {
52 while (d.len && d.buf[d.len - 1] == '=')
53 d.len--;
54 }
55 DPUTC(&d, '\n');
56 DWRITE(&d, stdout);
57 }
58
59 static void file_fix(char *p, size_t n)
60 {
61 while (n) {
62 if (*p == '/') *p = '%';
63 p++; n--;
64 }
65 }
66
67 static void format_file64(size_t n, unsigned line)
68 {
69 unsigned char buf[CHUNK];
70 dstr d = DSTR_INIT;
71 base64_ctx b;
72
73 base64_init(&b);
74 b.indent = "";
75 b.maxline = 0;
76
77 while (n) {
78 size_t nn = CHUNK;
79 if (nn > n) nn = n;
80 rand_get(RAND_GLOBAL, buf, nn);
81 base64_encode(&b, buf, nn, &d);
82 file_fix(d.buf, d.len);
83 DWRITE(&d, stdout);
84 DRESET(&d);
85 n -= nn;
86 }
87 base64_encode(&b, 0, 0, &d);
88 file_fix(d.buf, d.len);
89 while (d.len && d.buf[d.len - 1] == '=')
90 d.len--;
91 DPUTC(&d, '\n');
92 DWRITE(&d, stdout);
93 }
94
95 static void format_hex(size_t n, unsigned line)
96 {
97 unsigned char buf[CHUNK];
98 dstr d = DSTR_INIT;
99 hex_ctx h;
100
101 hex_init(&h);
102 if (line) {
103 h.indent = "\n";
104 h.maxline = line;
105 } else {
106 h.indent = "";
107 h.maxline = 0;
108 }
109
110 while (n) {
111 size_t nn = CHUNK;
112 if (nn > n) nn = n;
113 rand_get(RAND_GLOBAL, buf, nn);
114 hex_encode(&h, buf, nn, &d);
115 DWRITE(&d, stdout);
116 DRESET(&d);
117 n -= nn;
118 }
119 hex_encode(&h, 0, 0, &d);
120 DPUTC(&d, '\n');
121 DWRITE(&d, stdout);
122 }
123
124 static void format_raw(size_t n, unsigned line)
125 {
126 unsigned char buf[CHUNK];
127
128 while (n) {
129 size_t nn = CHUNK;
130 if (nn > n) nn = n;
131 rand_get(RAND_GLOBAL, buf, nn);
132 fwrite(buf, 1, nn, stdout);
133 n -= nn;
134 }
135 }
136
137 static const struct format fmt[] = {
138 { "base64", format_base64 },
139 { "file64", format_file64 },
140 { "hex", format_hex },
141 { "raw", format_raw },
142 { 0, 0 }
143 };
144
145 static int uarg(const char *p, const char *what)
146 {
147 unsigned long x;
148 char *q;
149 errno = 0;
150 x = strtoul(p, &q, 0);
151 if (*q || errno || x > INT_MAX) die(EXIT_FAILURE, "bad %s", what);
152 return (x);
153 }
154
155 static void version(FILE *fp)
156 {
157 pquis(stderr, "$, version " VERSION "\n");
158 }
159
160 static void usage(FILE *fp)
161 {
162 pquis(stderr, "Usage: $ [-y] [-l LEN] [-f FORMAT] [BITS]\n");
163 }
164
165 static void help(FILE *fp)
166 {
167 version(fp);
168 putc('\n', fp);
169 usage(fp);
170 fputs("\n\
171 Generates a random string, and prints it to standard output.\n\
172 \n\
173 Options:\n\
174 \n\
175 -h, --help Print this help message.\n\
176 -v, --version Print program version number.\n\
177 -u, --usage Print short usage summary.\n\
178 \n\
179 -y, --bytes Output length is bytes, not bits.\n\
180 -l, --line=LENGTH For textual output, limit line length to LENGTH.\n\
181 -f, --format=FORMAT Select output format: base64, file64, hex, raw.\n\
182 ", stdout);
183 }
184
185 int main(int argc, char *argv[])
186 {
187 size_t n;
188 unsigned f = 0;
189 unsigned len = 0;
190 unsigned mul = 8;
191 const struct format *ff = &fmt[0];
192
193 #define f_bogus 1u
194
195 ego(argv[0]);
196 for (;;) {
197 static const struct option opt[] = {
198 { "help", 0, 0, 'h' },
199 { "version", 0, 0, 'v' },
200 { "usage", 0, 0, 'u' },
201
202 { "format", OPTF_ARGREQ, 0, 'f' },
203 { "line", OPTF_ARGREQ, 0, 'l' },
204 { "bytes", 0, 0, 'y' },
205 { 0, 0, 0, 0 }
206 };
207 int i;
208
209 i = mdwopt(argc, argv, "hvuf:l:y", opt, 0, 0, 0);
210 if (i < 0)
211 break;
212 switch (i) {
213 case 'h':
214 help(stdout);
215 exit(0);
216 case 'v':
217 version(stdout);
218 exit(0);
219 case 'u':
220 usage(stdout);
221 exit(0);
222 case 'y':
223 mul = 1;
224 break;
225 case 'l':
226 len = uarg(optarg, "line length");
227 break;
228 case 'f':
229 ff = 0;
230 n = strlen(optarg);
231 for (i = 0; fmt[i].name; i++) {
232 if (strncmp(fmt[i].name, optarg, n) != 0)
233 continue;
234 if (!fmt[i].name[n]) {
235 ff = &fmt[i];
236 break;
237 }
238 if (ff)
239 die(EXIT_FAILURE, "ambiguous format name `%s'", optarg);
240 ff = &fmt[i];
241 }
242 if (!ff)
243 die(EXIT_FAILURE, "unknown format name `%s'", optarg);
244 break;
245 default:
246 f |= f_bogus;
247 break;
248 }
249 }
250
251 argc -= optind;
252 argv += optind;
253 if (f & f_bogus && argc > 1) {
254 usage(stderr);
255 exit(EXIT_FAILURE);
256 }
257 n = argc == 1 ? uarg(argv[0], "bit count") : 128;
258 if (!n || n % mul) die(EXIT_FAILURE, "bad bit count");
259 n /= mul;
260 rand_noisesrc(RAND_GLOBAL, &noise_source);
261 rand_seed(RAND_GLOBAL, 160);
262 ff->out(n, len);
263 if (ferror(stdout))
264 die(EXIT_FAILURE, "output error: %s", strerror(errno));
265 return (0);
266 }