| /* |
| * Copyright 2019 The OpenSSL Project Authors. All Rights Reserved. |
| * |
| * Licensed under the Apache License 2.0 (the "License"). You may not use |
| * this file except in compliance with the License. You can obtain a copy |
| * in the file LICENSE in the source distribution or at |
| * https://www.openssl.org/source/license.html |
| */ |
| |
| #include <string.h> |
| #include <stdarg.h> |
| #include <openssl/bio.h> |
| #include <openssl/safestack.h> |
| #include "opt.h" |
| |
| static BIO *bio_in = NULL; |
| static BIO *bio_out = NULL; |
| static BIO *bio_err = NULL; |
| |
| /*- |
| * This program sets up a chain of BIO_f_filter() on top of bio_out, how |
| * many is governed by the user through -n. It allows the user to set the |
| * indentation for each individual filter using -i and -p. Then it reads |
| * text from bio_in and prints it out through the BIO chain. |
| * |
| * The filter index is counted from the source/sink, i.e. index 0 is closest |
| * to it. |
| * |
| * Example: |
| * |
| * $ echo foo | ./bio_prefix_text -n 2 -i 1:32 -p 1:FOO -i 0:3 |
| * FOO foo |
| * ^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ |
| * | | |
| * | +------ 32 spaces from filter 1 |
| * +-------------------------- 3 spaces from filter 0 |
| */ |
| |
| static size_t amount = 0; |
| static BIO **chain = NULL; |
| |
| typedef enum OPTION_choice { |
| OPT_ERR = -1, |
| OPT_EOF = 0, |
| OPT_AMOUNT, |
| OPT_INDENT, |
| OPT_PREFIX |
| } OPTION_CHOICE; |
| |
| static const OPTIONS options[] = { |
| { "n", OPT_AMOUNT, 'p', "Amount of BIO_f_prefix() filters" }, |
| /* |
| * idx is the index to the BIO_f_filter chain(), where 0 is closest |
| * to the source/sink BIO. If idx isn't given, 0 is assumed |
| */ |
| { "i", OPT_INDENT, 's', "Indentation in form '[idx:]indent'" }, |
| { "p", OPT_PREFIX, 's', "Prefix in form '[idx:]prefix'" }, |
| { NULL } |
| }; |
| |
| int opt_printf_stderr(const char *fmt, ...) |
| { |
| va_list ap; |
| int ret; |
| |
| va_start(ap, fmt); |
| ret = BIO_vprintf(bio_err, fmt, ap); |
| va_end(ap); |
| return ret; |
| } |
| |
| static int run_pipe(void) |
| { |
| char buf[4096]; |
| |
| while (!BIO_eof(bio_in)) { |
| size_t bytes_in; |
| size_t bytes_out; |
| |
| if (!BIO_read_ex(bio_in, buf, sizeof(buf), &bytes_in)) |
| return 0; |
| bytes_out = 0; |
| while (bytes_out < bytes_in) { |
| size_t bytes; |
| |
| if (!BIO_write_ex(chain[amount - 1], buf, bytes_in, &bytes)) |
| return 0; |
| bytes_out += bytes; |
| } |
| } |
| return 1; |
| } |
| |
| static int setup_bio_chain(const char *progname) |
| { |
| BIO *next = NULL; |
| size_t n = amount; |
| |
| chain = OPENSSL_zalloc(sizeof(*chain) * n); |
| |
| if (chain != NULL) { |
| size_t i; |
| |
| next = bio_out; |
| BIO_up_ref(next); /* Protection against freeing */ |
| |
| for (i = 0; n > 0; i++, n--) { |
| BIO *curr = BIO_new(BIO_f_prefix()); |
| |
| if (curr == NULL) |
| goto err; |
| chain[i] = BIO_push(curr, next); |
| if (chain[i] == NULL) |
| goto err; |
| next = chain[i]; |
| } |
| } |
| return chain != NULL; |
| err: |
| /* Free the chain we built up */ |
| BIO_free_all(next); |
| OPENSSL_free(chain); |
| return 0; |
| } |
| |
| static void cleanup(void) |
| { |
| if (chain != NULL) { |
| BIO_free_all(chain[amount - 1]); |
| OPENSSL_free(chain); |
| } |
| |
| BIO_free_all(bio_in); |
| BIO_free_all(bio_out); |
| BIO_free_all(bio_err); |
| } |
| |
| static int setup(void) |
| { |
| OPTION_CHOICE o; |
| char *arg; |
| char *colon; |
| char *endptr; |
| size_t idx, indent; |
| const char *progname = opt_getprog(); |
| |
| bio_in = BIO_new_fp(stdin, BIO_NOCLOSE | BIO_FP_TEXT); |
| bio_out = BIO_new_fp(stdout, BIO_NOCLOSE | BIO_FP_TEXT); |
| bio_err = BIO_new_fp(stderr, BIO_NOCLOSE | BIO_FP_TEXT); |
| #ifdef __VMS |
| bio_out = BIO_push(BIO_new(BIO_f_linebuffer()), bio_out); |
| bio_err = BIO_push(BIO_new(BIO_f_linebuffer()), bio_err); |
| #endif |
| |
| OPENSSL_assert(bio_in != NULL); |
| OPENSSL_assert(bio_out != NULL); |
| OPENSSL_assert(bio_err != NULL); |
| |
| |
| while ((o = opt_next()) != OPT_EOF) { |
| switch (o) { |
| case OPT_AMOUNT: |
| arg = opt_arg(); |
| amount = strtoul(arg, &endptr, 10); |
| if (endptr[0] != '\0') { |
| BIO_printf(bio_err, |
| "%s: -n argument isn't a decimal number: %s", |
| progname, arg); |
| return 0; |
| } |
| if (amount < 1) { |
| BIO_printf(bio_err, "%s: must set up at least one filter", |
| progname); |
| return 0; |
| } |
| if (!setup_bio_chain(progname)) { |
| BIO_printf(bio_err, "%s: failed setting up filter chain", |
| progname); |
| return 0; |
| } |
| break; |
| case OPT_INDENT: |
| if (chain == NULL) { |
| BIO_printf(bio_err, "%s: -i given before -n", progname); |
| return 0; |
| } |
| arg = opt_arg(); |
| colon = strchr(arg, ':'); |
| idx = 0; |
| if (colon != NULL) { |
| idx = strtoul(arg, &endptr, 10); |
| if (endptr[0] != ':') { |
| BIO_printf(bio_err, |
| "%s: -i index isn't a decimal number: %s", |
| progname, arg); |
| return 0; |
| } |
| colon++; |
| } else { |
| colon = arg; |
| } |
| indent = strtoul(colon, &endptr, 10); |
| if (endptr[0] != '\0') { |
| BIO_printf(bio_err, |
| "%s: -i value isn't a decimal number: %s", |
| progname, arg); |
| return 0; |
| } |
| if (idx >= amount) { |
| BIO_printf(bio_err, "%s: index (%zu) not within range 0..%zu", |
| progname, idx, amount - 1); |
| return 0; |
| } |
| if (BIO_set_indent(chain[idx], (long)indent) <= 0) { |
| BIO_printf(bio_err, "%s: failed setting indentation: %s", |
| progname, arg); |
| return 0; |
| } |
| break; |
| case OPT_PREFIX: |
| if (chain == NULL) { |
| BIO_printf(bio_err, "%s: -p given before -n", progname); |
| return 0; |
| } |
| arg = opt_arg(); |
| colon = strchr(arg, ':'); |
| idx = 0; |
| if (colon != NULL) { |
| idx = strtoul(arg, &endptr, 10); |
| if (endptr[0] != ':') { |
| BIO_printf(bio_err, |
| "%s: -p index isn't a decimal number: %s", |
| progname, arg); |
| return 0; |
| } |
| colon++; |
| } else { |
| colon = arg; |
| } |
| if (idx >= amount) { |
| BIO_printf(bio_err, "%s: index (%zu) not within range 0..%zu", |
| progname, idx, amount - 1); |
| return 0; |
| } |
| if (BIO_set_prefix(chain[idx], colon) <= 0) { |
| BIO_printf(bio_err, "%s: failed setting prefix: %s", |
| progname, arg); |
| return 0; |
| } |
| break; |
| default: |
| case OPT_ERR: |
| return 0; |
| } |
| } |
| return 1; |
| } |
| |
| int main(int argc, char **argv) |
| { |
| int rv = EXIT_SUCCESS; |
| |
| opt_init(argc, argv, options); |
| rv = (setup() && run_pipe()) ? EXIT_SUCCESS : EXIT_FAILURE; |
| cleanup(); |
| return rv; |
| } |