Tizen 2.0 Release
[external/tizen-coreutils.git] / src / base64.c
1 /* Base64 encode/decode strings or files.
2    Copyright (C) 2004, 2005, 2006, 2007 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 When decoding, the input may contain newlines in addition to the bytes of\n\
87 the formal base64 alphabet.  Use --ignore-garbage to attempt to recover\n\
88 from any other non-alphabet bytes in the encoded stream.\n"),
89              stdout);
90       printf (_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
91     }
92
93   exit (status);
94 }
95
96 /* Note that increasing this may decrease performance if --ignore-garbage
97    is used, because of the memmove operation below. */
98 #define BLOCKSIZE 3072
99 #define B64BLOCKSIZE BASE64_LENGTH (BLOCKSIZE)
100
101 /* Ensure that BLOCKSIZE is a multiple of 3 and 4.  */
102 #if BLOCKSIZE % 12 != 0
103 # error "invalid BLOCKSIZE"
104 #endif
105
106 static void
107 wrap_write (const char *buffer, size_t len,
108             uintmax_t wrap_column, size_t *current_column, FILE *out)
109 {
110   size_t written;
111
112   if (wrap_column == 0)
113     {
114       /* Simple write. */
115       if (fwrite (buffer, 1, len, stdout) < len)
116         error (EXIT_FAILURE, errno, _("write error"));
117     }
118   else
119     for (written = 0; written < len;)
120       {
121         uintmax_t cols_remaining = wrap_column - *current_column;
122         size_t to_write = MIN (cols_remaining, SIZE_MAX);
123         to_write = MIN (to_write, len - written);
124
125         if (to_write == 0)
126           {
127             if (fputs ("\n", out) < 0)
128               error (EXIT_FAILURE, errno, _("write error"));
129             *current_column = 0;
130           }
131         else
132           {
133             if (fwrite (buffer + written, 1, to_write, stdout) < to_write)
134               error (EXIT_FAILURE, errno, _("write error"));
135             *current_column += to_write;
136             written += to_write;
137           }
138       }
139 }
140
141 static void
142 do_encode (FILE *in, FILE *out, uintmax_t wrap_column)
143 {
144   size_t current_column = 0;
145   char inbuf[BLOCKSIZE];
146   char outbuf[B64BLOCKSIZE];
147   size_t sum;
148
149   do
150     {
151       size_t n;
152
153       sum = 0;
154       do
155         {
156           n = fread (inbuf + sum, 1, BLOCKSIZE - sum, in);
157           sum += n;
158         }
159       while (!feof (in) && !ferror (in) && sum < BLOCKSIZE);
160
161       if (sum > 0)
162         {
163           /* Process input one block at a time.  Note that BLOCKSIZE %
164              3 == 0, so that no base64 pads will appear in output. */
165           base64_encode (inbuf, sum, outbuf, BASE64_LENGTH (sum));
166
167           wrap_write (outbuf, BASE64_LENGTH (sum), wrap_column,
168                       &current_column, out);
169         }
170     }
171   while (!feof (in) && !ferror (in) && sum == BLOCKSIZE);
172
173   /* When wrapping, terminate last line. */
174   if (wrap_column && current_column > 0 && fputs ("\n", out) < 0)
175     error (EXIT_FAILURE, errno, _("write error"));
176
177   if (ferror (in))
178     error (EXIT_FAILURE, errno, _("read error"));
179 }
180
181 static void
182 do_decode (FILE *in, FILE *out, bool ignore_garbage)
183 {
184   char inbuf[B64BLOCKSIZE];
185   char outbuf[BLOCKSIZE];
186   size_t sum;
187   struct base64_decode_context ctx;
188
189   base64_decode_ctx_init (&ctx);
190
191   do
192     {
193       bool ok;
194       size_t n;
195       unsigned int k;
196
197       sum = 0;
198       do
199         {
200           n = fread (inbuf + sum, 1, B64BLOCKSIZE - sum, in);
201
202           if (ignore_garbage)
203             {
204               size_t i;
205               for (i = 0; n > 0 && i < n;)
206                 if (isbase64 (inbuf[sum + i]) || inbuf[sum + i] == '=')
207                   i++;
208                 else
209                   memmove (inbuf + sum + i, inbuf + sum + i + 1, --n - i);
210             }
211
212           sum += n;
213
214           if (ferror (in))
215             error (EXIT_FAILURE, errno, _("read error"));
216         }
217       while (sum < B64BLOCKSIZE && !feof (in));
218
219       /* The following "loop" is usually iterated just once.
220          However, when it processes the final input buffer, we want
221          to iterate it one additional time, but with an indicator
222          telling it to flush what is in CTX.  */
223       for (k = 0; k < 1 + feof (in); k++)
224         {
225           if (k == 1 && ctx.i == 0)
226             break;
227           n = BLOCKSIZE;
228           ok = base64_decode (&ctx, inbuf, (k == 0 ? sum : 0), outbuf, &n);
229
230           if (fwrite (outbuf, 1, n, out) < n)
231             error (EXIT_FAILURE, errno, _("write error"));
232
233           if (!ok)
234             error (EXIT_FAILURE, 0, _("invalid input"));
235         }
236     }
237   while (!feof (in));
238 }
239
240 int
241 main (int argc, char **argv)
242 {
243   int opt;
244   FILE *input_fh;
245   const char *infile;
246
247   /* True if --decode has bene given and we should decode data. */
248   bool decode = false;
249   /* True if we should ignore non-alphabetic characters. */
250   bool ignore_garbage = false;
251   /* Wrap encoded base64 data around the 76:th column, by default. */
252   uintmax_t wrap_column = 76;
253
254   initialize_main (&argc, &argv);
255   program_name = argv[0];
256   setlocale (LC_ALL, "");
257   bindtextdomain (PACKAGE, LOCALEDIR);
258   textdomain (PACKAGE);
259
260   atexit (close_stdout);
261
262   while ((opt = getopt_long (argc, argv, "dqiw:", long_options, NULL)) != -1)
263     switch (opt)
264       {
265       case 'd':
266         decode = true;
267         break;
268
269       case 'w':
270         if (xstrtoumax (optarg, NULL, 0, &wrap_column, NULL) != LONGINT_OK)
271           error (EXIT_FAILURE, 0, _("invalid wrap size: %s"),
272                  quotearg (optarg));
273         break;
274
275       case 'i':
276         ignore_garbage = true;
277         break;
278
279         case_GETOPT_HELP_CHAR;
280
281         case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHOR);
282
283       default:
284         usage (EXIT_FAILURE);
285         break;
286       }
287
288   if (argc - optind > 1)
289     {
290       error (0, 0, _("extra operand %s"), quote (argv[optind]));
291       usage (EXIT_FAILURE);
292     }
293
294   if (optind < argc)
295     infile = argv[optind];
296   else
297     infile = "-";
298
299   if (strcmp (infile, "-") == 0)
300     input_fh = stdin;
301   else
302     {
303       input_fh = fopen (infile, "r");
304       if (input_fh == NULL)
305         error (EXIT_FAILURE, errno, "%s", infile);
306     }
307
308   if (decode)
309     do_decode (input_fh, stdout, ignore_garbage);
310   else
311     do_encode (input_fh, stdout, wrap_column);
312
313   if (fclose (input_fh) == EOF)
314     {
315       if (strcmp (infile, "-") == 0)
316         error (EXIT_FAILURE, errno, _("closing standard input"));
317       else
318         error (EXIT_FAILURE, errno, "%s", infile);
319     }
320
321   exit (EXIT_SUCCESS);
322 }