gorp \- write a short random string
.SH SYNOPSIS
.B gorp
+.RB [ \-y ]
+.RB [ \-l
+.IR length ]
+.RB [ \-f
+.IR format ]
.RI [ bits ]
.SH DESCRIPTION
Generates
.I bits
random bits (must be a multiple of 8, default is 128) and writes the
-resulting string to standard output in base64 format.
+resulting string to standard output.
+.PP
+The following options are recognized.
+.TP
+.B "\-h, \-\-help"
+Prints a help message to standard output and exits successfully.
+.TP
+.B "\-v, \-\-version"
+Prints the program's version number to standard output and exits
+successfully.
+.TP
+.B "\-u, \-\-usage"
+Prints a really short usage summary to standard output and exits
+successfully.
+.TP
+.BI "\-f, \-\-format=" format
+Prints the random string using the chosen output
+.IR format ,
+which may be
+.B base64
+(standard Base64 encoding, as described in RFC2045; this is the default),
+.B file64
+(Base64 encoding, with
+.RB ` % '
+instead of
+.RB ` / ',
+so the output is suitable for use as a filename),
+.B hex
+(plain hexadecimal encoding), or
+.B raw
+(raw binary output, not printable).
+.TP
+.B "\-l, \-\-line=" length
+Breaks textual output into lines of at most
+.I length
+characters, and does all encoding in a strictly conforming way. By
+default, the textual output is not line-broken, and strange terminator
+characters are not printed.
+.TP
+.B "\-y, \-\-bytes"
+The output length is bits, not bytes. This doesn't affect the default
+value of 128.
.SH BUGS
None known.
.SH AUTHOR
+#include <ctype.h>
+#include <errno.h>
+#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <mLib/alloc.h>
#include <mLib/base64.h>
#include <mLib/dstr.h>
+#include <mLib/hex.h>
+#include <mLib/mdwopt.h>
#include <mLib/quis.h>
#include <mLib/report.h>
#include <catacomb/rand.h>
#include <catacomb/noise.h>
-int main(int argc, char *argv[])
+struct format {
+ const char *name;
+ void (*out)(size_t, unsigned);
+};
+
+#define CHUNK 1024
+
+static void format_base64(size_t n, unsigned line)
{
+ unsigned char buf[CHUNK];
dstr d = DSTR_INIT;
base64_ctx b;
- char *p;
+
+ base64_init(&b);
+ if (line) {
+ b.indent = "\n";
+ b.maxline = line;
+ } else {
+ b.indent = "";
+ b.maxline = 0;
+ }
+
+ while (n) {
+ size_t nn = CHUNK;
+ if (nn > n) nn = n;
+ rand_get(RAND_GLOBAL, buf, nn);
+ base64_encode(&b, buf, nn, &d);
+ DWRITE(&d, stdout);
+ DRESET(&d);
+ n -= nn;
+ }
+ base64_encode(&b, 0, 0, &d);
+ if (!line) {
+ while (d.len && d.buf[d.len - 1] == '=')
+ d.len--;
+ }
+ DPUTC(&d, '\n');
+ DWRITE(&d, stdout);
+}
+
+static void file_fix(char *p, size_t n)
+{
+ while (n) {
+ if (*p == '/') *p = '%';
+ p++; n--;
+ }
+}
+
+static void format_file64(size_t n, unsigned line)
+{
+ unsigned char buf[CHUNK];
+ dstr d = DSTR_INIT;
+ base64_ctx b;
+
+ base64_init(&b);
+ b.indent = "";
+ b.maxline = 0;
+
+ while (n) {
+ size_t nn = CHUNK;
+ if (nn > n) nn = n;
+ rand_get(RAND_GLOBAL, buf, nn);
+ base64_encode(&b, buf, nn, &d);
+ file_fix(d.buf, d.len);
+ DWRITE(&d, stdout);
+ DRESET(&d);
+ n -= nn;
+ }
+ base64_encode(&b, 0, 0, &d);
+ file_fix(d.buf, d.len);
+ while (d.len && d.buf[d.len - 1] == '=')
+ d.len--;
+ DPUTC(&d, '\n');
+ DWRITE(&d, stdout);
+}
+
+static void format_hex(size_t n, unsigned line)
+{
+ unsigned char buf[CHUNK];
+ dstr d = DSTR_INIT;
+ hex_ctx h;
+
+ hex_init(&h);
+ if (line) {
+ h.indent = "\n";
+ h.maxline = line;
+ } else {
+ h.indent = "";
+ h.maxline = 0;
+ }
+
+ while (n) {
+ size_t nn = CHUNK;
+ if (nn > n) nn = n;
+ rand_get(RAND_GLOBAL, buf, nn);
+ hex_encode(&h, buf, nn, &d);
+ DWRITE(&d, stdout);
+ DRESET(&d);
+ n -= nn;
+ }
+ hex_encode(&h, 0, 0, &d);
+ DPUTC(&d, '\n');
+ DWRITE(&d, stdout);
+}
+
+static void format_raw(size_t n, unsigned line)
+{
+ unsigned char buf[CHUNK];
+
+ while (n) {
+ size_t nn = CHUNK;
+ if (nn > n) nn = n;
+ rand_get(RAND_GLOBAL, buf, nn);
+ fwrite(buf, 1, nn, stdout);
+ n -= nn;
+ }
+}
+
+static const struct format fmt[] = {
+ { "base64", format_base64 },
+ { "file64", format_file64 },
+ { "hex", format_hex },
+ { "raw", format_raw },
+ { 0, 0 }
+};
+
+static int uarg(const char *p, const char *what)
+{
+ unsigned long x;
+ char *q;
+ errno = 0;
+ x = strtoul(p, &q, 0);
+ if (*q || errno || x > INT_MAX) die(EXIT_FAILURE, "bad %s", what);
+ return (x);
+}
+
+static void version(FILE *fp)
+{
+ pquis(stderr, "$, version " VERSION "\n");
+}
+
+static void usage(FILE *fp)
+{
+ pquis(stderr, "Usage: $ [-y] [-l LEN] [-f FORMAT] [BITS]\n");
+}
+
+static void help(FILE *fp)
+{
+ version(fp);
+ putc('\n', fp);
+ usage(fp);
+ fputs("\n\
+Generates a random string, and prints it to standard output.\n\
+\n\
+Options:\n\
+\n\
+-h, --help Print this help message.\n\
+-v, --version Print program version number.\n\
+-u, --usage Print short usage summary.\n\
+\n\
+-y, --bytes Output length is bytes, not bits.\n\
+-l, --line=LENGTH For textual output, limit line length to LENGTH.\n\
+-f, --format=FORMAT Select output format: base64, file64, hex, raw.\n\
+", stdout);
+}
+
+int main(int argc, char *argv[])
+{
size_t n;
+ unsigned f = 0;
+ unsigned len = 0;
+ unsigned mul = 8;
+ const struct format *ff = &fmt[0];
+
+#define f_bogus 1u
ego(argv[0]);
- if (argc > 2) {
- pquis(stderr, "Usage: $ [BITS]\n");
+ for (;;) {
+ static const struct option opt[] = {
+ { "help", 0, 0, 'h' },
+ { "version", 0, 0, 'v' },
+ { "usage", 0, 0, 'u' },
+
+ { "format", OPTF_ARGREQ, 0, 'f' },
+ { "line", OPTF_ARGREQ, 0, 'l' },
+ { "bytes", 0, 0, 'y' },
+ { 0, 0, 0, 0 }
+ };
+ int i;
+
+ i = mdwopt(argc, argv, "hvuf:l:y", opt, 0, 0, 0);
+ if (i < 0)
+ break;
+ switch (i) {
+ case 'h':
+ help(stdout);
+ exit(0);
+ case 'v':
+ version(stdout);
+ exit(0);
+ case 'u':
+ usage(stdout);
+ exit(0);
+ case 'y':
+ mul = 1;
+ break;
+ case 'l':
+ len = uarg(optarg, "line length");
+ break;
+ case 'f':
+ ff = 0;
+ n = strlen(optarg);
+ for (i = 0; fmt[i].name; i++) {
+ if (strncmp(fmt[i].name, optarg, n) != 0)
+ continue;
+ if (!fmt[i].name[n]) {
+ ff = &fmt[i];
+ break;
+ }
+ if (ff)
+ die(EXIT_FAILURE, "ambiguous format name `%s'", optarg);
+ ff = &fmt[i];
+ }
+ if (!ff)
+ die(EXIT_FAILURE, "unknown format name `%s'", optarg);
+ break;
+ default:
+ f |= f_bogus;
+ break;
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+ if (f & f_bogus && argc > 1) {
+ usage(stderr);
exit(EXIT_FAILURE);
}
- n = argc == 2 ? atoi(argv[1]) : 128;
- if (!n || n % 8) die(EXIT_FAILURE, "bad bit count");
- n >>= 3;
- p = xmalloc(n);
+ n = argc == 1 ? uarg(argv[0], "bit count") : 128;
+ if (!n || n % mul) die(EXIT_FAILURE, "bad bit count");
+ n /= mul;
rand_noisesrc(RAND_GLOBAL, &noise_source);
rand_seed(RAND_GLOBAL, 160);
- rand_get(RAND_GLOBAL, p, n);
- base64_init(&b);
- b.maxline = 0;
- b.indent = "";
- base64_encode(&b, p, n, &d);
- base64_encode(&b, 0, 0, &d);
- printf("%s\n", d.buf);
+ ff->out(n, len);
+ if (ferror(stdout))
+ die(EXIT_FAILURE, "output error: %s", strerror(errno));
return (0);
}