chiark / gitweb /
debian/control: Include `prlimit' and `x86-model' in the metapackage.
[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/base32.h>
11 #include <mLib/dstr.h>
12 #include <mLib/hex.h>
13 #include <mLib/mdwopt.h>
14 #include <mLib/quis.h>
15 #include <mLib/report.h>
16
17 #include <catacomb/rand.h>
18 #include <catacomb/noise.h>
19
20 struct format {
21   const char *name;
22   void (*out)(size_t, unsigned);
23 };
24
25 #define CHUNK 1024
26
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))
30 {
31   char buf[CHUNK];
32   dstr d = DSTR_INIT;
33
34   while (n) {
35     size_t nn = CHUNK;
36     if (nn > n) nn = n;
37     rand_get(RAND_GLOBAL, buf, nn);
38     encode(ctx, buf, nn, &d);
39     if (fix) fix(d.buf, d.len);
40     DWRITE(&d, stdout);
41     DRESET(&d);
42     n -= nn;
43   }
44   encode(ctx, 0, 0, &d);
45   if (!line) {
46     while (d.len && d.buf[d.len - 1] == '=')
47       d.len--;
48   }
49   if (fix) fix(d.buf, d.len);
50   DPUTC(&d, '\n');
51   DWRITE(&d, stdout);
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); }
68
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);
79 }
80
81 static void fix_file64(char *p, size_t n)
82 {
83   while (n) {
84     if (*p == '/') *p = '%';
85     p++; n--;
86   }
87 }
88 static void format_file64(size_t n, unsigned line)
89   { do_format_base64(n, line, fix_file64); }
90
91 static void fix_safe64(char *p, size_t n)
92 {
93   while (n) {
94     if (*p == '+') *p = '-';
95     else if (*p == '/') *p = '_';
96     p++; n--;
97   }
98 }
99 static void format_safe64(size_t n, unsigned line)
100   { do_format_base64(n, line, fix_safe64); }
101
102 static void do_hex(void *ctx, char *p, size_t sz, dstr *d)
103   { hex_encode(ctx, p, sz, d); }
104 static void format_hex(size_t n, unsigned line)
105 {
106   hex_ctx b;
107
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);
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 },
130   { "safe64",   format_safe64 },
131   { "base32",   format_base32 },
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\
173 -f, --format=FORMAT     Select output format:\n\
174                           base64, file64, safe64, base32, hex, raw.\n\
175 ", stdout);
176 }
177
178 int main(int argc, char *argv[])
179 {
180   size_t n;
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
187
188   ego(argv[0]);
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);
248     exit(EXIT_FAILURE);
249   }
250   n = argc == 1 ? uarg(argv[0], "bit count") : 128;
251   if (!n || n % mul) die(EXIT_FAILURE, "bad bit count");
252   n /= mul;
253   rand_noisesrc(RAND_GLOBAL, &noise_source);
254   rand_seed(RAND_GLOBAL, 160);
255   ff->out(n, len);
256   if (ferror(stdout))
257     die(EXIT_FAILURE, "output error: %s", strerror(errno));
258   return (0);
259 }