chiark / gitweb /
Add `pause' program.
[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 }