X-Git-Url: https://www.chiark.greenend.org.uk/ucgi/~mdw/git/misc/blobdiff_plain/de41f398e807540bf9eaee6dd4ca21f19c2af388..22fb157c9459f0d07eb46d09370c27d3e57a437c:/gorp.c diff --git a/gorp.c b/gorp.c index 933460c..398e852 100644 --- a/gorp.c +++ b/gorp.c @@ -1,3 +1,6 @@ +#include +#include +#include #include #include #include @@ -5,36 +8,259 @@ #include #include #include +#include +#include #include #include #include #include -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); }