X-Git-Url: https://git.distorted.org.uk/~mdw/misc/blobdiff_plain/f342fce2e614f9d34503082b165bf70c0a211631..e2590393156ec984aafcd143e12a95f2711ace86:/gorp.c diff --git a/gorp.c b/gorp.c index 933460c..d5ce481 100644 --- a/gorp.c +++ b/gorp.c @@ -1,40 +1,259 @@ +#include +#include +#include #include #include #include #include #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 do_format(size_t n, unsigned line, void *ctx, + void (*encode)(void *, char *, size_t, dstr *), + void (*fix)(char *, size_t)) { + char buf[CHUNK]; dstr d = DSTR_INIT; + + while (n) { + size_t nn = CHUNK; + if (nn > n) nn = n; + rand_get(RAND_GLOBAL, buf, nn); + encode(ctx, buf, nn, &d); + if (fix) fix(d.buf, d.len); + DWRITE(&d, stdout); + DRESET(&d); + n -= nn; + } + encode(ctx, 0, 0, &d); + if (!line) { + while (d.len && d.buf[d.len - 1] == '=') + d.len--; + } + if (fix) fix(d.buf, d.len); + DPUTC(&d, '\n'); + DWRITE(&d, stdout); +} + +static void do_base64(void *ctx, char *p, size_t sz, dstr *d) + { base64_encode(ctx, p, sz, d); } +static void do_format_base64(size_t n, unsigned line, + void (*fix)(char *, size_t)) +{ base64_ctx b; - char *p; + + base64_init(&b); + if (line) { b.indent = "\n"; b.maxline = line; } + else { b.indent = ""; b.maxline = 0; } + do_format(n, line, &b, do_base64, fix); +} +static void format_base64(size_t n, unsigned line) + { do_format_base64(n, line, 0); } + +static void do_base32(void *ctx, char *p, size_t sz, dstr *d) + { base32_encode(ctx, p, sz, d); } +static void format_base32(size_t n, unsigned line) +{ + base32_ctx b; + + base32_init(&b); + if (line) { b.indent = "\n"; b.maxline = line; } + else { b.indent = ""; b.maxline = 0; } + do_format(n, line, &b, do_base32, 0); +} + +static void fix_file64(char *p, size_t n) +{ + while (n) { + if (*p == '/') *p = '%'; + p++; n--; + } +} +static void format_file64(size_t n, unsigned line) + { do_format_base64(n, line, fix_file64); } + +static void fix_safe64(char *p, size_t n) +{ + while (n) { + if (*p == '+') *p = '-'; + else if (*p == '/') *p = '_'; + p++; n--; + } +} +static void format_safe64(size_t n, unsigned line) + { do_format_base64(n, line, fix_safe64); } + +static void do_hex(void *ctx, char *p, size_t sz, dstr *d) + { hex_encode(ctx, p, sz, d); } +static void format_hex(size_t n, unsigned line) +{ + hex_ctx b; + + hex_init(&b); + if (line) { b.indent = "\n"; b.maxline = line; } + else { b.indent = ""; b.maxline = 0; } + do_format(n, line, &b, do_hex, 0); +} + +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 }, + { "safe64", format_safe64 }, + { "base32", format_base32 }, + { "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:\n\ + base64, file64, safe64, base32, 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); }