build: ensure make-prime-list doesn't access out of bounds memory
[platform/upstream/coreutils.git] / src / base64.c
1 /* Base64 encode/decode strings or files.
2    Copyright (C) 2004-2013 Free Software Foundation, Inc.
3
4    This file is part of Base64.
5
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.
10
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.
15
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/>. */
18
19 /* Written by Simon Josefsson <simon@josefsson.org>.  */
20
21 #include <config.h>
22
23 #include <stdio.h>
24 #include <getopt.h>
25 #include <sys/types.h>
26
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"
34
35 #include "base64.h"
36
37 /* The official name of this program (e.g., no 'g' prefix).  */
38 #define PROGRAM_NAME "base64"
39
40 #define AUTHORS proper_name ("Simon Josefsson")
41
42 static struct option const long_options[] =
43 {
44   {"decode", no_argument, 0, 'd'},
45   {"wrap", required_argument, 0, 'w'},
46   {"ignore-garbage", no_argument, 0, 'i'},
47
48   {GETOPT_HELP_OPTION_DECL},
49   {GETOPT_VERSION_OPTION_DECL},
50   {NULL, 0, NULL, 0}
51 };
52
53 void
54 usage (int status)
55 {
56   if (status != EXIT_SUCCESS)
57     emit_try_help ();
58   else
59     {
60       printf (_("\
61 Usage: %s [OPTION]... [FILE]\n\
62 Base64 encode or decode FILE, or standard input, to standard output.\n\
63 "), program_name);
64
65       emit_mandatory_arg_note ();
66
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 ();
87     }
88
89   exit (status);
90 }
91
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)
96
97 /* Ensure that BLOCKSIZE is a multiple of 3 and 4.  */
98 #if BLOCKSIZE % 12 != 0
99 # error "invalid BLOCKSIZE"
100 #endif
101
102 static void
103 wrap_write (const char *buffer, size_t len,
104             uintmax_t wrap_column, size_t *current_column, FILE *out)
105 {
106   size_t written;
107
108   if (wrap_column == 0)
109     {
110       /* Simple write. */
111       if (fwrite (buffer, 1, len, stdout) < len)
112         error (EXIT_FAILURE, errno, _("write error"));
113     }
114   else
115     for (written = 0; written < len;)
116       {
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);
120
121         if (to_write == 0)
122           {
123             if (fputs ("\n", out) < 0)
124               error (EXIT_FAILURE, errno, _("write error"));
125             *current_column = 0;
126           }
127         else
128           {
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;
133           }
134       }
135 }
136
137 static void
138 do_encode (FILE *in, FILE *out, uintmax_t wrap_column)
139 {
140   size_t current_column = 0;
141   char inbuf[BLOCKSIZE];
142   char outbuf[B64BLOCKSIZE];
143   size_t sum;
144
145   do
146     {
147       size_t n;
148
149       sum = 0;
150       do
151         {
152           n = fread (inbuf + sum, 1, BLOCKSIZE - sum, in);
153           sum += n;
154         }
155       while (!feof (in) && !ferror (in) && sum < BLOCKSIZE);
156
157       if (sum > 0)
158         {
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));
162
163           wrap_write (outbuf, BASE64_LENGTH (sum), wrap_column,
164                       &current_column, out);
165         }
166     }
167   while (!feof (in) && !ferror (in) && sum == BLOCKSIZE);
168
169   /* When wrapping, terminate last line. */
170   if (wrap_column && current_column > 0 && fputs ("\n", out) < 0)
171     error (EXIT_FAILURE, errno, _("write error"));
172
173   if (ferror (in))
174     error (EXIT_FAILURE, errno, _("read error"));
175 }
176
177 static void
178 do_decode (FILE *in, FILE *out, bool ignore_garbage)
179 {
180   char inbuf[B64BLOCKSIZE];
181   char outbuf[BLOCKSIZE];
182   size_t sum;
183   struct base64_decode_context ctx;
184
185   base64_decode_ctx_init (&ctx);
186
187   do
188     {
189       bool ok;
190       size_t n;
191       unsigned int k;
192
193       sum = 0;
194       do
195         {
196           n = fread (inbuf + sum, 1, B64BLOCKSIZE - sum, in);
197
198           if (ignore_garbage)
199             {
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);
206             }
207
208           sum += n;
209
210           if (ferror (in))
211             error (EXIT_FAILURE, errno, _("read error"));
212         }
213       while (sum < B64BLOCKSIZE && !feof (in));
214
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++)
220         {
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);
225
226           if (fwrite (outbuf, 1, n, out) < n)
227             error (EXIT_FAILURE, errno, _("write error"));
228
229           if (!ok)
230             error (EXIT_FAILURE, 0, _("invalid input"));
231         }
232     }
233   while (!feof (in));
234 }
235
236 int
237 main (int argc, char **argv)
238 {
239   int opt;
240   FILE *input_fh;
241   const char *infile;
242
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;
249
250   initialize_main (&argc, &argv);
251   set_program_name (argv[0]);
252   setlocale (LC_ALL, "");
253   bindtextdomain (PACKAGE, LOCALEDIR);
254   textdomain (PACKAGE);
255
256   atexit (close_stdout);
257
258   while ((opt = getopt_long (argc, argv, "diw:", long_options, NULL)) != -1)
259     switch (opt)
260       {
261       case 'd':
262         decode = true;
263         break;
264
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;
270
271       case 'i':
272         ignore_garbage = true;
273         break;
274
275       case_GETOPT_HELP_CHAR;
276
277       case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
278
279       default:
280         usage (EXIT_FAILURE);
281         break;
282       }
283
284   if (argc - optind > 1)
285     {
286       error (0, 0, _("extra operand %s"), quote (argv[optind]));
287       usage (EXIT_FAILURE);
288     }
289
290   if (optind < argc)
291     infile = argv[optind];
292   else
293     infile = "-";
294
295   if (STREQ (infile, "-"))
296     {
297       if (O_BINARY)
298         xfreopen (NULL, "rb", stdin);
299       input_fh = stdin;
300     }
301   else
302     {
303       input_fh = fopen (infile, "rb");
304       if (input_fh == NULL)
305         error (EXIT_FAILURE, errno, "%s", infile);
306     }
307
308   fadvise (input_fh, FADVISE_SEQUENTIAL);
309
310   if (decode)
311     do_decode (input_fh, stdout, ignore_garbage);
312   else
313     do_encode (input_fh, stdout, wrap_column);
314
315   if (fclose (input_fh) == EOF)
316     {
317       if (STREQ (infile, "-"))
318         error (EXIT_FAILURE, errno, _("closing standard input"));
319       else
320         error (EXIT_FAILURE, errno, "%s", infile);
321     }
322
323   exit (EXIT_SUCCESS);
324 }