1 /* Base64 encode/decode strings or files.
2 Copyright (C) 2004-2013 Free Software Foundation, Inc.
4 This file is part of Base64.
6 This program is free software: you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation, either version 3 of the License, or
9 (at your option) any later version.
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with this program. If not, see <http://www.gnu.org/licenses/>. */
19 /* Written by Simon Josefsson <simon@josefsson.org>. */
25 #include <sys/types.h>
37 /* The official name of this program (e.g., no 'g' prefix). */
38 #define PROGRAM_NAME "base64"
40 #define AUTHORS proper_name ("Simon Josefsson")
42 static struct option const long_options[] =
44 {"decode", no_argument, 0, 'd'},
45 {"wrap", required_argument, 0, 'w'},
46 {"ignore-garbage", no_argument, 0, 'i'},
48 {GETOPT_HELP_OPTION_DECL},
49 {GETOPT_VERSION_OPTION_DECL},
56 if (status != EXIT_SUCCESS)
61 Usage: %s [OPTION]... [FILE]\n\
62 Base64 encode or decode FILE, or standard input, to standard output.\n\
65 emit_mandatory_arg_note ();
68 -d, --decode decode data\n\
69 -i, --ignore-garbage when decoding, ignore non-alphabet characters\n\
70 -w, --wrap=COLS wrap encoded lines after COLS character (default 76).\n\
71 Use 0 to disable line wrapping\n\
74 fputs (HELP_OPTION_DESCRIPTION, stdout);
75 fputs (VERSION_OPTION_DESCRIPTION, stdout);
78 With no FILE, or when FILE is -, read standard input.\n"), stdout);
81 The data are encoded as described for the base64 alphabet in RFC 3548.\n\
82 When decoding, the input may contain newlines in addition to the bytes of\n\
83 the formal base64 alphabet. Use --ignore-garbage to attempt to recover\n\
84 from any other non-alphabet bytes in the encoded stream.\n"),
86 emit_ancillary_info ();
92 /* Note that increasing this may decrease performance if --ignore-garbage
93 is used, because of the memmove operation below. */
94 #define BLOCKSIZE 3072
95 #define B64BLOCKSIZE BASE64_LENGTH (BLOCKSIZE)
97 /* Ensure that BLOCKSIZE is a multiple of 3 and 4. */
98 #if BLOCKSIZE % 12 != 0
99 # error "invalid BLOCKSIZE"
103 wrap_write (const char *buffer, size_t len,
104 uintmax_t wrap_column, size_t *current_column, FILE *out)
108 if (wrap_column == 0)
111 if (fwrite (buffer, 1, len, stdout) < len)
112 error (EXIT_FAILURE, errno, _("write error"));
115 for (written = 0; written < len;)
117 uintmax_t cols_remaining = wrap_column - *current_column;
118 size_t to_write = MIN (cols_remaining, SIZE_MAX);
119 to_write = MIN (to_write, len - written);
123 if (fputs ("\n", out) < 0)
124 error (EXIT_FAILURE, errno, _("write error"));
129 if (fwrite (buffer + written, 1, to_write, stdout) < to_write)
130 error (EXIT_FAILURE, errno, _("write error"));
131 *current_column += to_write;
138 do_encode (FILE *in, FILE *out, uintmax_t wrap_column)
140 size_t current_column = 0;
141 char inbuf[BLOCKSIZE];
142 char outbuf[B64BLOCKSIZE];
152 n = fread (inbuf + sum, 1, BLOCKSIZE - sum, in);
155 while (!feof (in) && !ferror (in) && sum < BLOCKSIZE);
159 /* Process input one block at a time. Note that BLOCKSIZE %
160 3 == 0, so that no base64 pads will appear in output. */
161 base64_encode (inbuf, sum, outbuf, BASE64_LENGTH (sum));
163 wrap_write (outbuf, BASE64_LENGTH (sum), wrap_column,
164 ¤t_column, out);
167 while (!feof (in) && !ferror (in) && sum == BLOCKSIZE);
169 /* When wrapping, terminate last line. */
170 if (wrap_column && current_column > 0 && fputs ("\n", out) < 0)
171 error (EXIT_FAILURE, errno, _("write error"));
174 error (EXIT_FAILURE, errno, _("read error"));
178 do_decode (FILE *in, FILE *out, bool ignore_garbage)
180 char inbuf[B64BLOCKSIZE];
181 char outbuf[BLOCKSIZE];
183 struct base64_decode_context ctx;
185 base64_decode_ctx_init (&ctx);
196 n = fread (inbuf + sum, 1, B64BLOCKSIZE - sum, in);
201 for (i = 0; n > 0 && i < n;)
202 if (isbase64 (inbuf[sum + i]) || inbuf[sum + i] == '=')
205 memmove (inbuf + sum + i, inbuf + sum + i + 1, --n - i);
211 error (EXIT_FAILURE, errno, _("read error"));
213 while (sum < B64BLOCKSIZE && !feof (in));
215 /* The following "loop" is usually iterated just once.
216 However, when it processes the final input buffer, we want
217 to iterate it one additional time, but with an indicator
218 telling it to flush what is in CTX. */
219 for (k = 0; k < 1 + !!feof (in); k++)
221 if (k == 1 && ctx.i == 0)
224 ok = base64_decode_ctx (&ctx, inbuf, (k == 0 ? sum : 0), outbuf, &n);
226 if (fwrite (outbuf, 1, n, out) < n)
227 error (EXIT_FAILURE, errno, _("write error"));
230 error (EXIT_FAILURE, 0, _("invalid input"));
237 main (int argc, char **argv)
243 /* True if --decode has been given and we should decode data. */
245 /* True if we should ignore non-base64-alphabetic characters. */
246 bool ignore_garbage = false;
247 /* Wrap encoded base64 data around the 76:th column, by default. */
248 uintmax_t wrap_column = 76;
250 initialize_main (&argc, &argv);
251 set_program_name (argv[0]);
252 setlocale (LC_ALL, "");
253 bindtextdomain (PACKAGE, LOCALEDIR);
254 textdomain (PACKAGE);
256 atexit (close_stdout);
258 while ((opt = getopt_long (argc, argv, "diw:", long_options, NULL)) != -1)
266 if (xstrtoumax (optarg, NULL, 0, &wrap_column, NULL) != LONGINT_OK)
267 error (EXIT_FAILURE, 0, _("invalid wrap size: %s"),
272 ignore_garbage = true;
275 case_GETOPT_HELP_CHAR;
277 case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
280 usage (EXIT_FAILURE);
284 if (argc - optind > 1)
286 error (0, 0, _("extra operand %s"), quote (argv[optind]));
287 usage (EXIT_FAILURE);
291 infile = argv[optind];
295 if (STREQ (infile, "-"))
298 xfreopen (NULL, "rb", stdin);
303 input_fh = fopen (infile, "rb");
304 if (input_fh == NULL)
305 error (EXIT_FAILURE, errno, "%s", infile);
308 fadvise (input_fh, FADVISE_SEQUENTIAL);
311 do_decode (input_fh, stdout, ignore_garbage);
313 do_encode (input_fh, stdout, wrap_column);
315 if (fclose (input_fh) == EOF)
317 if (STREQ (infile, "-"))
318 error (EXIT_FAILURE, errno, _("closing standard input"));
320 error (EXIT_FAILURE, errno, "%s", infile);