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