Improve the check for departures from C89, and fix the departures
[platform/upstream/coreutils.git] / src / base64.c
1 /* Base64 encode/decode strings or files.
2    Copyright (C) 2004, 2005, 2006 Free Software Foundation, Inc.
3
4    This file is part of Base64.
5
6    Base64 is free software; you can redistribute it and/or modify it
7    under the terms of the GNU General Public License as published by
8    the Free Software Foundation; either version 2, or (at your option)
9    any later version.
10
11    Base64 is distributed in the hope that it will be useful, but
12    WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14    General Public License for more details.
15
16    You should have received a copy of the GNU General Public License
17    along with Base64; see the file COPYING.  If not, write to the Free
18    Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19    MA 02110-1301, USA. */
20
21 /* Written by Simon Josefsson <simon@josefsson.org>.  */
22
23 #include <config.h>
24
25 #include <stdio.h>
26 #include <getopt.h>
27 #include <sys/types.h>
28
29 #include "system.h"
30 #include "error.h"
31 #include "xstrtol.h"
32 #include "quote.h"
33 #include "quotearg.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 AUTHOR "Simon Josefsson"
41
42 /* The invocation name of this program.  */
43 char *program_name;
44
45 static const struct option long_options[] = {
46   {"decode", no_argument, 0, 'd'},
47   {"wrap", required_argument, 0, 'w'},
48   {"ignore-garbage", no_argument, 0, 'i'},
49   {"help", no_argument, 0, GETOPT_HELP_CHAR},
50   {"version", no_argument, 0, GETOPT_VERSION_CHAR},
51
52   {GETOPT_HELP_OPTION_DECL},
53   {GETOPT_VERSION_OPTION_DECL},
54   {NULL, 0, NULL, 0}
55 };
56
57 static void
58 usage (int status)
59 {
60   if (status != EXIT_SUCCESS)
61     fprintf (stderr, _("Try `%s --help' for more information.\n"),
62              program_name);
63   else
64     {
65       printf (_("\
66 Usage: %s [OPTION] [FILE]\n\
67 Base64 encode or decode FILE, or standard input, to standard output.\n\
68 \n"), program_name);
69       fputs (_("\
70   -w, --wrap=COLS       Wrap encoded lines after COLS character (default 76).\n\
71                         Use 0 to disable line wrapping.\n\
72 \n\
73   -d, --decode          Decode data.\n\
74   -i, --ignore-garbage  When decoding, ignore non-alphabet characters.\n\
75 \n\
76 "), stdout);
77       fputs (_("\
78       --help            Display this help and exit.\n\
79       --version         Output version information and exit.\n"), stdout);
80       fputs (_("\
81 \n\
82 With no FILE, or when FILE is -, read standard input.\n"), stdout);
83       fputs (_("\
84 \n\
85 The data are encoded as described for the base64 alphabet in RFC 3548.\n\
86 Decoding require compliant input by default, use --ignore-garbage to\n\
87 attempt to recover from non-alphabet characters (such as newlines) in\n\
88 the encoded stream.\n"), stdout);
89       printf (_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
90     }
91
92   exit (status);
93 }
94
95 /* Note that increasing this may decrease performance if --ignore-garbage
96    is used, because of the memmove operation below. */
97 #define BLOCKSIZE 3072
98 #define B64BLOCKSIZE BASE64_LENGTH (BLOCKSIZE)
99
100 /* Ensure that BLOCKSIZE is a multiple of 3 and 4.  */
101 #if BLOCKSIZE % 12 != 0
102 # error "invalid BLOCKSIZE"
103 #endif
104
105 static void
106 wrap_write (const char *buffer, size_t len,
107             uintmax_t wrap_column, size_t *current_column, FILE *out)
108 {
109   size_t written;
110
111   if (wrap_column == 0)
112     {
113       /* Simple write. */
114       if (fwrite (buffer, 1, len, stdout) < len)
115         error (EXIT_FAILURE, errno, _("write error"));
116     }
117   else
118     for (written = 0; written < len;)
119       {
120         uintmax_t cols_remaining = wrap_column - *current_column;
121         size_t to_write = MIN (cols_remaining, SIZE_MAX);
122         to_write = MIN (to_write, len - written);
123
124         if (to_write == 0)
125           {
126             if (fputs ("\n", out) < 0)
127               error (EXIT_FAILURE, errno, _("write error"));
128             *current_column = 0;
129           }
130         else
131           {
132             if (fwrite (buffer + written, 1, to_write, stdout) < to_write)
133               error (EXIT_FAILURE, errno, _("write error"));
134             *current_column += to_write;
135             written += to_write;
136           }
137       }
138 }
139
140 static void
141 do_encode (FILE *in, FILE *out, uintmax_t wrap_column)
142 {
143   size_t current_column = 0;
144   char inbuf[BLOCKSIZE];
145   char outbuf[B64BLOCKSIZE];
146   size_t sum;
147
148   do
149     {
150       size_t n;
151
152       sum = 0;
153       do
154         {
155           n = fread (inbuf + sum, 1, BLOCKSIZE - sum, in);
156           sum += n;
157         }
158       while (!feof (in) && !ferror (in) && sum < BLOCKSIZE);
159
160       if (sum > 0)
161         {
162           /* Process input one block at a time.  Note that BLOCKSIZE %
163              3 == 0, so that no base64 pads will appear in output. */
164           base64_encode (inbuf, sum, outbuf, BASE64_LENGTH (sum));
165
166           wrap_write (outbuf, BASE64_LENGTH (sum), wrap_column,
167                       &current_column, out);
168         }
169     }
170   while (!feof (in) && !ferror (in) && sum == BLOCKSIZE);
171
172   /* When wrapping, terminate last line. */
173   if (wrap_column && current_column > 0 && fputs ("\n", out) < 0)
174     error (EXIT_FAILURE, errno, _("write error"));
175
176   if (ferror (in))
177     error (EXIT_FAILURE, errno, _("read error"));
178 }
179
180 static void
181 do_decode (FILE *in, FILE *out, bool ignore_garbage)
182 {
183   char inbuf[B64BLOCKSIZE];
184   char outbuf[BLOCKSIZE];
185   size_t sum;
186
187   do
188     {
189       bool ok;
190       size_t n;
191
192       sum = 0;
193       do
194         {
195           n = fread (inbuf + sum, 1, B64BLOCKSIZE - sum, in);
196
197           if (ignore_garbage)
198             {
199               size_t i;
200               for (i = 0; n > 0 && i < n;)
201                 if (isbase64 (inbuf[sum + i]) || inbuf[sum + i] == '=')
202                   i++;
203                 else
204                   memmove (inbuf + sum + i, inbuf + sum + i + 1, --n - i);
205             }
206
207           sum += n;
208
209           if (ferror (in))
210             error (EXIT_FAILURE, errno, _("read error"));
211         }
212       while (sum < B64BLOCKSIZE && !feof (in));
213
214       n = BLOCKSIZE;
215       ok = base64_decode (inbuf, sum, outbuf, &n);
216
217       if (fwrite (outbuf, 1, n, out) < n)
218         error (EXIT_FAILURE, errno, _("write error"));
219
220       if (!ok)
221         error (EXIT_FAILURE, 0, _("invalid input"));
222     }
223   while (!feof (in));
224 }
225
226 int
227 main (int argc, char **argv)
228 {
229   int opt;
230   FILE *input_fh;
231   const char *infile;
232
233   /* True if --decode has bene given and we should decode data. */
234   bool decode = false;
235   /* True if we should ignore non-alphabetic characters. */
236   bool ignore_garbage = false;
237   /* Wrap encoded base64 data around the 76:th column, by default. */
238   uintmax_t wrap_column = 76;
239
240   initialize_main (&argc, &argv);
241   program_name = argv[0];
242   setlocale (LC_ALL, "");
243   bindtextdomain (PACKAGE, LOCALEDIR);
244   textdomain (PACKAGE);
245
246   atexit (close_stdout);
247
248   while ((opt = getopt_long (argc, argv, "dqiw:", long_options, NULL)) != -1)
249     switch (opt)
250       {
251       case 'd':
252         decode = true;
253         break;
254
255       case 'w':
256         if (xstrtoumax (optarg, NULL, 0, &wrap_column, NULL) != LONGINT_OK)
257           error (EXIT_FAILURE, 0, _("invalid wrap size: %s"),
258                  quotearg (optarg));
259         break;
260
261       case 'i':
262         ignore_garbage = true;
263         break;
264
265         case_GETOPT_HELP_CHAR;
266
267         case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHOR);
268
269       default:
270         usage (EXIT_FAILURE);
271         break;
272       }
273
274   if (argc - optind > 1)
275     {
276       error (0, 0, _("extra operand %s"), quote (argv[optind]));
277       usage (EXIT_FAILURE);
278     }
279
280   if (optind < argc)
281     infile = argv[optind];
282   else
283     infile = "-";
284
285   if (strcmp (infile, "-") == 0)
286     input_fh = stdin;
287   else
288     {
289       input_fh = fopen (infile, "r");
290       if (input_fh == NULL)
291         error (EXIT_FAILURE, errno, "%s", infile);
292     }
293
294   if (decode)
295     do_decode (input_fh, stdout, ignore_garbage);
296   else
297     do_encode (input_fh, stdout, wrap_column);
298
299   if (fclose (input_fh) == EOF)
300     {
301       if (strcmp (infile, "-") == 0)
302         error (EXIT_FAILURE, errno, _("closing standard input"));
303       else
304         error (EXIT_FAILURE, errno, "%s", infile);
305     }
306
307   exit (EXIT_SUCCESS);
308 }