diff options
Diffstat (limited to 'src/base64.c')
-rw-r--r-- | src/base64.c | 329 |
1 files changed, 178 insertions, 151 deletions
diff --git a/src/base64.c b/src/base64.c index 9f8ff41..83f0e9d 100644 --- a/src/base64.c +++ b/src/base64.c @@ -1,22 +1,20 @@ /* Base64 encode/decode strings or files. - Copyright (C) 2004, 2005, 2006, 2007 Free Software Foundation, Inc. + Copyright (C) 2004-2016 Free Software Foundation, Inc. This file is part of Base64. - Base64 is free software; you can redistribute it and/or modify it - under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2, or (at your option) - any later version. + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. - Base64 is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. You should have received a copy of the GNU General Public License - along with Base64; see the file COPYING. If not, write to the Free - Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, - MA 02110-1301, USA. */ + along with this program. If not, see <http://www.gnu.org/licenses/>. */ /* Written by Simon Josefsson <simon@josefsson.org>. */ @@ -28,84 +26,108 @@ #include "system.h" #include "error.h" -#include "xstrtol.h" +#include "fadvise.h" #include "quote.h" -#include "quotearg.h" - -#include "base64.h" +#include "xstrtol.h" +#include "xdectoint.h" +#include "xfreopen.h" -/* The official name of this program (e.g., no `g' prefix). */ -#define PROGRAM_NAME "base64" +#define AUTHORS proper_name ("Simon Josefsson") -#define AUTHOR "Simon Josefsson" +#if BASE_TYPE == 32 +# include "base32.h" +# define PROGRAM_NAME "base32" +#else +# include "base64.h" +# define PROGRAM_NAME "base64" +#endif -/* The invocation name of this program. */ -char *program_name; -static const struct option long_options[] = { +static struct option const long_options[] = +{ {"decode", no_argument, 0, 'd'}, {"wrap", required_argument, 0, 'w'}, {"ignore-garbage", no_argument, 0, 'i'}, - {"help", no_argument, 0, GETOPT_HELP_CHAR}, - {"version", no_argument, 0, GETOPT_VERSION_CHAR}, {GETOPT_HELP_OPTION_DECL}, {GETOPT_VERSION_OPTION_DECL}, {NULL, 0, NULL, 0} }; -static void +void usage (int status) { if (status != EXIT_SUCCESS) - fprintf (stderr, _("Try `%s --help' for more information.\n"), - program_name); + emit_try_help (); else { printf (_("\ -Usage: %s [OPTION] [FILE]\n\ -Base64 encode or decode FILE, or standard input, to standard output.\n\ -\n"), program_name); +Usage: %s [OPTION]... [FILE]\n\ +Base%d encode or decode FILE, or standard input, to standard output.\n\ +"), program_name, BASE_TYPE); + + emit_stdin_note (); + emit_mandatory_arg_note (); + fputs (_("\ - -w, --wrap=COLS Wrap encoded lines after COLS character (default 76).\n\ - Use 0 to disable line wrapping.\n\ -\n\ - -d, --decode Decode data.\n\ - -i, --ignore-garbage When decoding, ignore non-alphabet characters.\n\ + -d, --decode decode data\n\ + -i, --ignore-garbage when decoding, ignore non-alphabet characters\n\ + -w, --wrap=COLS wrap encoded lines after COLS character (default 76).\n\ + Use 0 to disable line wrapping\n\ \n\ "), stdout); - fputs (_("\ - --help Display this help and exit.\n\ - --version Output version information and exit.\n"), stdout); - fputs (_("\ -\n\ -With no FILE, or when FILE is -, read standard input.\n"), stdout); - fputs (_("\ + fputs (HELP_OPTION_DESCRIPTION, stdout); + fputs (VERSION_OPTION_DESCRIPTION, stdout); + printf (_("\ \n\ -The data are encoded as described for the base64 alphabet in RFC 3548.\n\ +The data are encoded as described for the %s alphabet in RFC 4648.\n\ When decoding, the input may contain newlines in addition to the bytes of\n\ -the formal base64 alphabet. Use --ignore-garbage to attempt to recover\n\ +the formal %s alphabet. Use --ignore-garbage to attempt to recover\n\ from any other non-alphabet bytes in the encoded stream.\n"), - stdout); - printf (_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT); + PROGRAM_NAME, PROGRAM_NAME); + emit_ancillary_info (PROGRAM_NAME); } exit (status); } +#define ENC_BLOCKSIZE (1024*3*10) + +#if BASE_TYPE == 32 +# define BASE_LENGTH BASE32_LENGTH /* Note that increasing this may decrease performance if --ignore-garbage - is used, because of the memmove operation below. */ -#define BLOCKSIZE 3072 -#define B64BLOCKSIZE BASE64_LENGTH (BLOCKSIZE) + is used, because of the memmove operation below. */ +# define DEC_BLOCKSIZE (1024*5) + +/* Ensure that BLOCKSIZE is a multiple of 5 and 8. */ +verify (ENC_BLOCKSIZE % 40 == 0); /* So padding chars only on last block. */ +verify (DEC_BLOCKSIZE % 40 == 0); /* So complete encoded blocks are used. */ + +# define base_encode base32_encode +# define base_decode_context base32_decode_context +# define base_decode_ctx_init base32_decode_ctx_init +# define base_decode_ctx base32_decode_ctx +# define isbase isbase32 +#else +# define BASE_LENGTH BASE64_LENGTH +/* Note that increasing this may decrease performance if --ignore-garbage + is used, because of the memmove operation below. */ +# define DEC_BLOCKSIZE (1024*3) /* Ensure that BLOCKSIZE is a multiple of 3 and 4. */ -#if BLOCKSIZE % 12 != 0 -# error "invalid BLOCKSIZE" +verify (ENC_BLOCKSIZE % 12 == 0); /* So padding chars only on last block. */ +verify (DEC_BLOCKSIZE % 12 == 0); /* So complete encoded blocks are used. */ + +# define base_encode base64_encode +# define base_decode_context base64_decode_context +# define base_decode_ctx_init base64_decode_ctx_init +# define base_decode_ctx base64_decode_ctx +# define isbase isbase64 #endif static void wrap_write (const char *buffer, size_t len, - uintmax_t wrap_column, size_t *current_column, FILE *out) + uintmax_t wrap_column, size_t *current_column, FILE *out) { size_t written; @@ -113,28 +135,28 @@ wrap_write (const char *buffer, size_t len, { /* Simple write. */ if (fwrite (buffer, 1, len, stdout) < len) - error (EXIT_FAILURE, errno, _("write error")); + error (EXIT_FAILURE, errno, _("write error")); } else for (written = 0; written < len;) { - uintmax_t cols_remaining = wrap_column - *current_column; - size_t to_write = MIN (cols_remaining, SIZE_MAX); - to_write = MIN (to_write, len - written); - - if (to_write == 0) - { - if (fputs ("\n", out) < 0) - error (EXIT_FAILURE, errno, _("write error")); - *current_column = 0; - } - else - { - if (fwrite (buffer + written, 1, to_write, stdout) < to_write) - error (EXIT_FAILURE, errno, _("write error")); - *current_column += to_write; - written += to_write; - } + uintmax_t cols_remaining = wrap_column - *current_column; + size_t to_write = MIN (cols_remaining, SIZE_MAX); + to_write = MIN (to_write, len - written); + + if (to_write == 0) + { + if (fputc ('\n', out) == EOF) + error (EXIT_FAILURE, errno, _("write error")); + *current_column = 0; + } + else + { + if (fwrite (buffer + written, 1, to_write, stdout) < to_write) + error (EXIT_FAILURE, errno, _("write error")); + *current_column += to_write; + written += to_write; + } } } @@ -142,8 +164,8 @@ static void do_encode (FILE *in, FILE *out, uintmax_t wrap_column) { size_t current_column = 0; - char inbuf[BLOCKSIZE]; - char outbuf[B64BLOCKSIZE]; + char inbuf[ENC_BLOCKSIZE]; + char outbuf[BASE_LENGTH (ENC_BLOCKSIZE)]; size_t sum; do @@ -152,26 +174,26 @@ do_encode (FILE *in, FILE *out, uintmax_t wrap_column) sum = 0; do - { - n = fread (inbuf + sum, 1, BLOCKSIZE - sum, in); - sum += n; - } - while (!feof (in) && !ferror (in) && sum < BLOCKSIZE); + { + n = fread (inbuf + sum, 1, ENC_BLOCKSIZE - sum, in); + sum += n; + } + while (!feof (in) && !ferror (in) && sum < ENC_BLOCKSIZE); if (sum > 0) - { - /* Process input one block at a time. Note that BLOCKSIZE % - 3 == 0, so that no base64 pads will appear in output. */ - base64_encode (inbuf, sum, outbuf, BASE64_LENGTH (sum)); - - wrap_write (outbuf, BASE64_LENGTH (sum), wrap_column, - ¤t_column, out); - } + { + /* Process input one block at a time. Note that ENC_BLOCKSIZE + is sized so that no pad chars will appear in output. */ + base_encode (inbuf, sum, outbuf, BASE_LENGTH (sum)); + + wrap_write (outbuf, BASE_LENGTH (sum), wrap_column, + ¤t_column, out); + } } - while (!feof (in) && !ferror (in) && sum == BLOCKSIZE); + while (!feof (in) && !ferror (in) && sum == ENC_BLOCKSIZE); /* When wrapping, terminate last line. */ - if (wrap_column && current_column > 0 && fputs ("\n", out) < 0) + if (wrap_column && current_column > 0 && fputc ('\n', out) == EOF) error (EXIT_FAILURE, errno, _("write error")); if (ferror (in)) @@ -181,12 +203,12 @@ do_encode (FILE *in, FILE *out, uintmax_t wrap_column) static void do_decode (FILE *in, FILE *out, bool ignore_garbage) { - char inbuf[B64BLOCKSIZE]; - char outbuf[BLOCKSIZE]; + char inbuf[BASE_LENGTH (DEC_BLOCKSIZE)]; + char outbuf[DEC_BLOCKSIZE]; size_t sum; - struct base64_decode_context ctx; + struct base_decode_context ctx; - base64_decode_ctx_init (&ctx); + base_decode_ctx_init (&ctx); do { @@ -196,43 +218,43 @@ do_decode (FILE *in, FILE *out, bool ignore_garbage) sum = 0; do - { - n = fread (inbuf + sum, 1, B64BLOCKSIZE - sum, in); - - if (ignore_garbage) - { - size_t i; - for (i = 0; n > 0 && i < n;) - if (isbase64 (inbuf[sum + i]) || inbuf[sum + i] == '=') - i++; - else - memmove (inbuf + sum + i, inbuf + sum + i + 1, --n - i); - } - - sum += n; - - if (ferror (in)) - error (EXIT_FAILURE, errno, _("read error")); - } - while (sum < B64BLOCKSIZE && !feof (in)); + { + n = fread (inbuf + sum, 1, BASE_LENGTH (DEC_BLOCKSIZE) - sum, in); + + if (ignore_garbage) + { + size_t i; + for (i = 0; n > 0 && i < n;) + if (isbase (inbuf[sum + i]) || inbuf[sum + i] == '=') + i++; + else + memmove (inbuf + sum + i, inbuf + sum + i + 1, --n - i); + } + + sum += n; + + if (ferror (in)) + error (EXIT_FAILURE, errno, _("read error")); + } + while (sum < BASE_LENGTH (DEC_BLOCKSIZE) && !feof (in)); /* The following "loop" is usually iterated just once. - However, when it processes the final input buffer, we want - to iterate it one additional time, but with an indicator - telling it to flush what is in CTX. */ - for (k = 0; k < 1 + feof (in); k++) - { - if (k == 1 && ctx.i == 0) - break; - n = BLOCKSIZE; - ok = base64_decode (&ctx, inbuf, (k == 0 ? sum : 0), outbuf, &n); - - if (fwrite (outbuf, 1, n, out) < n) - error (EXIT_FAILURE, errno, _("write error")); - - if (!ok) - error (EXIT_FAILURE, 0, _("invalid input")); - } + However, when it processes the final input buffer, we want + to iterate it one additional time, but with an indicator + telling it to flush what is in CTX. */ + for (k = 0; k < 1 + !!feof (in); k++) + { + if (k == 1 && ctx.i == 0) + break; + n = DEC_BLOCKSIZE; + ok = base_decode_ctx (&ctx, inbuf, (k == 0 ? sum : 0), outbuf, &n); + + if (fwrite (outbuf, 1, n, out) < n) + error (EXIT_FAILURE, errno, _("write error")); + + if (!ok) + error (EXIT_FAILURE, 0, _("invalid input")); + } } while (!feof (in)); } @@ -244,45 +266,44 @@ main (int argc, char **argv) FILE *input_fh; const char *infile; - /* True if --decode has bene given and we should decode data. */ + /* True if --decode has been given and we should decode data. */ bool decode = false; - /* True if we should ignore non-alphabetic characters. */ + /* True if we should ignore non-base-alphabetic characters. */ bool ignore_garbage = false; - /* Wrap encoded base64 data around the 76:th column, by default. */ + /* Wrap encoded data around the 76:th column, by default. */ uintmax_t wrap_column = 76; initialize_main (&argc, &argv); - program_name = argv[0]; + set_program_name (argv[0]); setlocale (LC_ALL, ""); bindtextdomain (PACKAGE, LOCALEDIR); textdomain (PACKAGE); atexit (close_stdout); - while ((opt = getopt_long (argc, argv, "dqiw:", long_options, NULL)) != -1) + while ((opt = getopt_long (argc, argv, "diw:", long_options, NULL)) != -1) switch (opt) { case 'd': - decode = true; - break; + decode = true; + break; case 'w': - if (xstrtoumax (optarg, NULL, 0, &wrap_column, NULL) != LONGINT_OK) - error (EXIT_FAILURE, 0, _("invalid wrap size: %s"), - quotearg (optarg)); - break; + wrap_column = xdectoumax (optarg, 0, UINTMAX_MAX, "", + _("invalid wrap size"), 0); + break; case 'i': - ignore_garbage = true; - break; + ignore_garbage = true; + break; - case_GETOPT_HELP_CHAR; + case_GETOPT_HELP_CHAR; - case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHOR); + case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS); default: - usage (EXIT_FAILURE); - break; + usage (EXIT_FAILURE); + break; } if (argc - optind > 1) @@ -296,15 +317,21 @@ main (int argc, char **argv) else infile = "-"; - if (strcmp (infile, "-") == 0) - input_fh = stdin; + if (STREQ (infile, "-")) + { + if (O_BINARY) + xfreopen (NULL, "rb", stdin); + input_fh = stdin; + } else { - input_fh = fopen (infile, "r"); + input_fh = fopen (infile, "rb"); if (input_fh == NULL) - error (EXIT_FAILURE, errno, "%s", infile); + error (EXIT_FAILURE, errno, "%s", quotef (infile)); } + fadvise (input_fh, FADVISE_SEQUENTIAL); + if (decode) do_decode (input_fh, stdout, ignore_garbage); else @@ -312,11 +339,11 @@ main (int argc, char **argv) if (fclose (input_fh) == EOF) { - if (strcmp (infile, "-") == 0) - error (EXIT_FAILURE, errno, _("closing standard input")); + if (STREQ (infile, "-")) + error (EXIT_FAILURE, errno, _("closing standard input")); else - error (EXIT_FAILURE, errno, "%s", infile); + error (EXIT_FAILURE, errno, "%s", quotef (infile)); } - exit (EXIT_SUCCESS); + return EXIT_SUCCESS; } |