doc: use semicolon instead of period in option descriptions
[coreutils.git] / src / base64.c
blob0a4006802ba474a63db80dceab3c979a8e3a4825
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 /* 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"
100 #endif
102 static void
103 wrap_write (const char *buffer, size_t len,
104 uintmax_t wrap_column, size_t *current_column, FILE *out)
106 size_t written;
108 if (wrap_column == 0)
110 /* Simple write. */
111 if (fwrite (buffer, 1, len, stdout) < len)
112 error (EXIT_FAILURE, errno, _("write error"));
114 else
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);
121 if (to_write == 0)
123 if (fputs ("\n", out) < 0)
124 error (EXIT_FAILURE, errno, _("write error"));
125 *current_column = 0;
127 else
129 if (fwrite (buffer + written, 1, to_write, stdout) < to_write)
130 error (EXIT_FAILURE, errno, _("write error"));
131 *current_column += to_write;
132 written += to_write;
137 static void
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];
143 size_t sum;
147 size_t n;
149 sum = 0;
152 n = fread (inbuf + sum, 1, BLOCKSIZE - sum, in);
153 sum += n;
155 while (!feof (in) && !ferror (in) && sum < BLOCKSIZE);
157 if (sum > 0)
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 &current_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"));
173 if (ferror (in))
174 error (EXIT_FAILURE, errno, _("read error"));
177 static void
178 do_decode (FILE *in, FILE *out, bool ignore_garbage)
180 char inbuf[B64BLOCKSIZE];
181 char outbuf[BLOCKSIZE];
182 size_t sum;
183 struct base64_decode_context ctx;
185 base64_decode_ctx_init (&ctx);
189 bool ok;
190 size_t n;
191 unsigned int k;
193 sum = 0;
196 n = fread (inbuf + sum, 1, B64BLOCKSIZE - sum, in);
198 if (ignore_garbage)
200 size_t i;
201 for (i = 0; n > 0 && i < n;)
202 if (isbase64 (inbuf[sum + i]) || inbuf[sum + i] == '=')
203 i++;
204 else
205 memmove (inbuf + sum + i, inbuf + sum + i + 1, --n - i);
208 sum += n;
210 if (ferror (in))
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)
222 break;
223 n = BLOCKSIZE;
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"));
229 if (!ok)
230 error (EXIT_FAILURE, 0, _("invalid input"));
233 while (!feof (in));
237 main (int argc, char **argv)
239 int opt;
240 FILE *input_fh;
241 const char *infile;
243 /* True if --decode has been given and we should decode data. */
244 bool decode = false;
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)
259 switch (opt)
261 case 'd':
262 decode = true;
263 break;
265 case 'w':
266 if (xstrtoumax (optarg, NULL, 0, &wrap_column, NULL) != LONGINT_OK)
267 error (EXIT_FAILURE, 0, _("invalid wrap size: %s"),
268 quotearg (optarg));
269 break;
271 case 'i':
272 ignore_garbage = true;
273 break;
275 case_GETOPT_HELP_CHAR;
277 case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
279 default:
280 usage (EXIT_FAILURE);
281 break;
284 if (argc - optind > 1)
286 error (0, 0, _("extra operand %s"), quote (argv[optind]));
287 usage (EXIT_FAILURE);
290 if (optind < argc)
291 infile = argv[optind];
292 else
293 infile = "-";
295 if (STREQ (infile, "-"))
297 if (O_BINARY)
298 xfreopen (NULL, "rb", stdin);
299 input_fh = stdin;
301 else
303 input_fh = fopen (infile, "rb");
304 if (input_fh == NULL)
305 error (EXIT_FAILURE, errno, "%s", infile);
308 fadvise (input_fh, FADVISE_SEQUENTIAL);
310 if (decode)
311 do_decode (input_fh, stdout, ignore_garbage);
312 else
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"));
319 else
320 error (EXIT_FAILURE, errno, "%s", infile);
323 exit (EXIT_SUCCESS);