tests: fix false failure due to gdb inline function handling
[coreutils.git] / src / base64.c
blobee07dd41f772ebac0b176b76964ac9d36dc709bb
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>. */
21 #include <config.h>
23 #include <stdio.h>
24 #include <getopt.h>
25 #include <sys/types.h>
27 #include "system.h"
28 #include "error.h"
29 #include "fadvise.h"
30 #include "xstrtol.h"
31 #include "quote.h"
32 #include "quotearg.h"
33 #include "xfreopen.h"
35 #include "base64.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},
50 {NULL, 0, NULL, 0}
53 void
54 usage (int status)
56 if (status != EXIT_SUCCESS)
57 emit_try_help ();
58 else
60 printf (_("\
61 Usage: %s [OPTION]... [FILE]\n\
62 Base64 encode or decode FILE, or standard input, to standard output.\n\
63 "), program_name);
65 emit_mandatory_arg_note ();
67 fputs (_("\
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\
72 \n\
73 "), stdout);
74 fputs (HELP_OPTION_DESCRIPTION, stdout);
75 fputs (VERSION_OPTION_DESCRIPTION, stdout);
76 fputs (_("\
77 \n\
78 With no FILE, or when FILE is -, read standard input.\n"), stdout);
79 fputs (_("\
80 \n\
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"),
85 stdout);
86 emit_ancillary_info ();
89 exit (status);
92 #define ENC_BLOCKSIZE (1024*3*10)
93 #define ENC_B64BLOCKSIZE BASE64_LENGTH (ENC_BLOCKSIZE)
94 /* Note that increasing this may decrease performance if --ignore-garbage
95 is used, because of the memmove operation below. */
96 #define DEC_BLOCKSIZE (1024*3)
97 #define DEC_B64BLOCKSIZE BASE64_LENGTH (DEC_BLOCKSIZE)
99 /* Ensure that BLOCKSIZE is a multiple of 3 and 4. */
100 verify (ENC_BLOCKSIZE % 12 == 0);
101 verify (DEC_BLOCKSIZE % 12 == 0);
103 static void
104 wrap_write (const char *buffer, size_t len,
105 uintmax_t wrap_column, size_t *current_column, FILE *out)
107 size_t written;
109 if (wrap_column == 0)
111 /* Simple write. */
112 if (fwrite (buffer, 1, len, stdout) < len)
113 error (EXIT_FAILURE, errno, _("write error"));
115 else
116 for (written = 0; written < len;)
118 uintmax_t cols_remaining = wrap_column - *current_column;
119 size_t to_write = MIN (cols_remaining, SIZE_MAX);
120 to_write = MIN (to_write, len - written);
122 if (to_write == 0)
124 if (fputc ('\n', out) == EOF)
125 error (EXIT_FAILURE, errno, _("write error"));
126 *current_column = 0;
128 else
130 if (fwrite (buffer + written, 1, to_write, stdout) < to_write)
131 error (EXIT_FAILURE, errno, _("write error"));
132 *current_column += to_write;
133 written += to_write;
138 static void
139 do_encode (FILE *in, FILE *out, uintmax_t wrap_column)
141 size_t current_column = 0;
142 char inbuf[ENC_BLOCKSIZE];
143 char outbuf[ENC_B64BLOCKSIZE];
144 size_t sum;
148 size_t n;
150 sum = 0;
153 n = fread (inbuf + sum, 1, ENC_BLOCKSIZE - sum, in);
154 sum += n;
156 while (!feof (in) && !ferror (in) && sum < ENC_BLOCKSIZE);
158 if (sum > 0)
160 /* Process input one block at a time. Note that ENC_BLOCKSIZE %
161 3 == 0, so that no base64 pads will appear in output. */
162 base64_encode (inbuf, sum, outbuf, BASE64_LENGTH (sum));
164 wrap_write (outbuf, BASE64_LENGTH (sum), wrap_column,
165 &current_column, out);
168 while (!feof (in) && !ferror (in) && sum == ENC_BLOCKSIZE);
170 /* When wrapping, terminate last line. */
171 if (wrap_column && current_column > 0 && fputc ('\n', out) == EOF)
172 error (EXIT_FAILURE, errno, _("write error"));
174 if (ferror (in))
175 error (EXIT_FAILURE, errno, _("read error"));
178 static void
179 do_decode (FILE *in, FILE *out, bool ignore_garbage)
181 char inbuf[DEC_B64BLOCKSIZE];
182 char outbuf[DEC_BLOCKSIZE];
183 size_t sum;
184 struct base64_decode_context ctx;
186 base64_decode_ctx_init (&ctx);
190 bool ok;
191 size_t n;
192 unsigned int k;
194 sum = 0;
197 n = fread (inbuf + sum, 1, DEC_B64BLOCKSIZE - sum, in);
199 if (ignore_garbage)
201 size_t i;
202 for (i = 0; n > 0 && i < n;)
203 if (isbase64 (inbuf[sum + i]) || inbuf[sum + i] == '=')
204 i++;
205 else
206 memmove (inbuf + sum + i, inbuf + sum + i + 1, --n - i);
209 sum += n;
211 if (ferror (in))
212 error (EXIT_FAILURE, errno, _("read error"));
214 while (sum < DEC_B64BLOCKSIZE && !feof (in));
216 /* The following "loop" is usually iterated just once.
217 However, when it processes the final input buffer, we want
218 to iterate it one additional time, but with an indicator
219 telling it to flush what is in CTX. */
220 for (k = 0; k < 1 + !!feof (in); k++)
222 if (k == 1 && ctx.i == 0)
223 break;
224 n = DEC_BLOCKSIZE;
225 ok = base64_decode_ctx (&ctx, inbuf, (k == 0 ? sum : 0), outbuf, &n);
227 if (fwrite (outbuf, 1, n, out) < n)
228 error (EXIT_FAILURE, errno, _("write error"));
230 if (!ok)
231 error (EXIT_FAILURE, 0, _("invalid input"));
234 while (!feof (in));
238 main (int argc, char **argv)
240 int opt;
241 FILE *input_fh;
242 const char *infile;
244 /* True if --decode has been given and we should decode data. */
245 bool decode = false;
246 /* True if we should ignore non-base64-alphabetic characters. */
247 bool ignore_garbage = false;
248 /* Wrap encoded base64 data around the 76:th column, by default. */
249 uintmax_t wrap_column = 76;
251 initialize_main (&argc, &argv);
252 set_program_name (argv[0]);
253 setlocale (LC_ALL, "");
254 bindtextdomain (PACKAGE, LOCALEDIR);
255 textdomain (PACKAGE);
257 atexit (close_stdout);
259 while ((opt = getopt_long (argc, argv, "diw:", long_options, NULL)) != -1)
260 switch (opt)
262 case 'd':
263 decode = true;
264 break;
266 case 'w':
267 if (xstrtoumax (optarg, NULL, 0, &wrap_column, NULL) != LONGINT_OK)
268 error (EXIT_FAILURE, 0, _("invalid wrap size: %s"),
269 quotearg (optarg));
270 break;
272 case 'i':
273 ignore_garbage = true;
274 break;
276 case_GETOPT_HELP_CHAR;
278 case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
280 default:
281 usage (EXIT_FAILURE);
282 break;
285 if (argc - optind > 1)
287 error (0, 0, _("extra operand %s"), quote (argv[optind]));
288 usage (EXIT_FAILURE);
291 if (optind < argc)
292 infile = argv[optind];
293 else
294 infile = "-";
296 if (STREQ (infile, "-"))
298 if (O_BINARY)
299 xfreopen (NULL, "rb", stdin);
300 input_fh = stdin;
302 else
304 input_fh = fopen (infile, "rb");
305 if (input_fh == NULL)
306 error (EXIT_FAILURE, errno, "%s", infile);
309 fadvise (input_fh, FADVISE_SEQUENTIAL);
311 if (decode)
312 do_decode (input_fh, stdout, ignore_garbage);
313 else
314 do_encode (input_fh, stdout, wrap_column);
316 if (fclose (input_fh) == EOF)
318 if (STREQ (infile, "-"))
319 error (EXIT_FAILURE, errno, _("closing standard input"));
320 else
321 error (EXIT_FAILURE, errno, "%s", infile);
324 exit (EXIT_SUCCESS);