LCOV - code coverage report
Current view: top level - src - base64.c (source / functions) Hit Total Coverage
Test: coreutils.info Lines: 100 109 91.7 %
Date: 2018-01-30 Functions: 5 5 100.0 %

          Line data    Source code
       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             :    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 "xstrtol.h"
      30             : #include "quote.h"
      31             : #include "quotearg.h"
      32             : 
      33             : #include "base64.h"
      34             : 
      35             : /* The official name of this program (e.g., no `g' prefix).  */
      36             : #define PROGRAM_NAME "base64"
      37             : 
      38             : #define AUTHOR "Simon Josefsson"
      39             : 
      40             : /* The invocation name of this program.  */
      41             : char *program_name;
      42             : 
      43             : static const struct option long_options[] = {
      44             :   {"decode", no_argument, 0, 'd'},
      45             :   {"wrap", required_argument, 0, 'w'},
      46             :   {"ignore-garbage", no_argument, 0, 'i'},
      47             :   {"help", no_argument, 0, GETOPT_HELP_CHAR},
      48             :   {"version", no_argument, 0, GETOPT_VERSION_CHAR},
      49             : 
      50             :   {GETOPT_HELP_OPTION_DECL},
      51             :   {GETOPT_VERSION_OPTION_DECL},
      52             :   {NULL, 0, NULL, 0}
      53             : };
      54             : 
      55             : static void
      56          33 : usage (int status)
      57             : {
      58          33 :   if (status != EXIT_SUCCESS)
      59          31 :     fprintf (stderr, _("Try `%s --help' for more information.\n"),
      60             :              program_name);
      61             :   else
      62             :     {
      63           2 :       printf (_("\
      64             : Usage: %s [OPTION] [FILE]\n\
      65             : Base64 encode or decode FILE, or standard input, to standard output.\n\
      66             : \n"), program_name);
      67           2 :       fputs (_("\
      68             :   -w, --wrap=COLS       Wrap encoded lines after COLS character (default 76).\n\
      69             :                         Use 0 to disable line wrapping.\n\
      70             : \n\
      71             :   -d, --decode          Decode data.\n\
      72             :   -i, --ignore-garbage  When decoding, ignore non-alphabet characters.\n\
      73             : \n\
      74             : "), stdout);
      75           2 :       fputs (_("\
      76             :       --help            Display this help and exit.\n\
      77             :       --version         Output version information and exit.\n"), stdout);
      78           2 :       fputs (_("\
      79             : \n\
      80             : With no FILE, or when FILE is -, read standard input.\n"), stdout);
      81           2 :       fputs (_("\
      82             : \n\
      83             : The data are encoded as described for the base64 alphabet in RFC 3548.\n\
      84             : When decoding, the input may contain newlines in addition to the bytes of\n\
      85             : the formal base64 alphabet.  Use --ignore-garbage to attempt to recover\n\
      86             : from any other non-alphabet bytes in the encoded stream.\n"),
      87             :              stdout);
      88           2 :       emit_bug_reporting_address ();
      89             :     }
      90             : 
      91          33 :   exit (status);
      92             : }
      93             : 
      94             : /* Note that increasing this may decrease performance if --ignore-garbage
      95             :    is used, because of the memmove operation below. */
      96             : #define BLOCKSIZE 3072
      97             : #define B64BLOCKSIZE BASE64_LENGTH (BLOCKSIZE)
      98             : 
      99             : /* Ensure that BLOCKSIZE is a multiple of 3 and 4.  */
     100             : #if BLOCKSIZE % 12 != 0
     101             : # error "invalid BLOCKSIZE"
     102             : #endif
     103             : 
     104             : static void
     105          10 : wrap_write (const char *buffer, size_t len,
     106             :             uintmax_t wrap_column, size_t *current_column, FILE *out)
     107             : {
     108             :   size_t written;
     109             : 
     110          10 :   if (wrap_column == 0)
     111             :     {
     112             :       /* Simple write. */
     113           3 :       if (fwrite (buffer, 1, len, stdout) < len)
     114           0 :         error (EXIT_FAILURE, errno, _("write error"));
     115             :     }
     116             :   else
     117          65 :     for (written = 0; written < len;)
     118             :       {
     119          51 :         uintmax_t cols_remaining = wrap_column - *current_column;
     120          51 :         size_t to_write = MIN (cols_remaining, SIZE_MAX);
     121          51 :         to_write = MIN (to_write, len - written);
     122             : 
     123          51 :         if (to_write == 0)
     124             :           {
     125          22 :             if (fputs ("\n", out) < 0)
     126           0 :               error (EXIT_FAILURE, errno, _("write error"));
     127          22 :             *current_column = 0;
     128             :           }
     129             :         else
     130             :           {
     131          29 :             if (fwrite (buffer + written, 1, to_write, stdout) < to_write)
     132           0 :               error (EXIT_FAILURE, errno, _("write error"));
     133          29 :             *current_column += to_write;
     134          29 :             written += to_write;
     135             :           }
     136             :       }
     137          10 : }
     138             : 
     139             : static void
     140          14 : do_encode (FILE *in, FILE *out, uintmax_t wrap_column)
     141             : {
     142          14 :   size_t current_column = 0;
     143             :   char inbuf[BLOCKSIZE];
     144             :   char outbuf[B64BLOCKSIZE];
     145             :   size_t sum;
     146             : 
     147             :   do
     148             :     {
     149             :       size_t n;
     150             : 
     151          14 :       sum = 0;
     152             :       do
     153             :         {
     154          14 :           n = fread (inbuf + sum, 1, BLOCKSIZE - sum, in);
     155          14 :           sum += n;
     156             :         }
     157          14 :       while (!feof (in) && !ferror (in) && sum < BLOCKSIZE);
     158             : 
     159          14 :       if (sum > 0)
     160             :         {
     161             :           /* Process input one block at a time.  Note that BLOCKSIZE %
     162             :              3 == 0, so that no base64 pads will appear in output. */
     163          10 :           base64_encode (inbuf, sum, outbuf, BASE64_LENGTH (sum));
     164             : 
     165          10 :           wrap_write (outbuf, BASE64_LENGTH (sum), wrap_column,
     166             :                       &current_column, out);
     167             :         }
     168             :     }
     169          14 :   while (!feof (in) && !ferror (in) && sum == BLOCKSIZE);
     170             : 
     171             :   /* When wrapping, terminate last line. */
     172          14 :   if (wrap_column && current_column > 0 && fputs ("\n", out) < 0)
     173           0 :     error (EXIT_FAILURE, errno, _("write error"));
     174             : 
     175          14 :   if (ferror (in))
     176           5 :     error (EXIT_FAILURE, errno, _("read error"));
     177           9 : }
     178             : 
     179             : static void
     180          25 : do_decode (FILE *in, FILE *out, bool ignore_garbage)
     181             : {
     182             :   char inbuf[B64BLOCKSIZE];
     183             :   char outbuf[BLOCKSIZE];
     184             :   size_t sum;
     185             :   struct base64_decode_context ctx;
     186             : 
     187          25 :   base64_decode_ctx_init (&ctx);
     188             : 
     189             :   do
     190             :     {
     191             :       bool ok;
     192             :       size_t n;
     193             :       unsigned int k;
     194             : 
     195          25 :       sum = 0;
     196             :       do
     197             :         {
     198          25 :           n = fread (inbuf + sum, 1, B64BLOCKSIZE - sum, in);
     199             : 
     200          25 :           if (ignore_garbage)
     201             :             {
     202             :               size_t i;
     203          52 :               for (i = 0; n > 0 && i < n;)
     204          40 :                 if (isbase64 (inbuf[sum + i]) || inbuf[sum + i] == '=')
     205          38 :                   i++;
     206             :                 else
     207           2 :                   memmove (inbuf + sum + i, inbuf + sum + i + 1, --n - i);
     208             :             }
     209             : 
     210          25 :           sum += n;
     211             : 
     212          25 :           if (ferror (in))
     213           3 :             error (EXIT_FAILURE, errno, _("read error"));
     214             :         }
     215          22 :       while (sum < B64BLOCKSIZE && !feof (in));
     216             : 
     217             :       /* The following "loop" is usually iterated just once.
     218             :          However, when it processes the final input buffer, we want
     219             :          to iterate it one additional time, but with an indicator
     220             :          telling it to flush what is in CTX.  */
     221          43 :       for (k = 0; k < 1 + feof (in); k++)
     222             :         {
     223          42 :           if (k == 1 && ctx.i == 0)
     224           8 :             break;
     225          34 :           n = BLOCKSIZE;
     226          34 :           ok = base64_decode (&ctx, inbuf, (k == 0 ? sum : 0), outbuf, &n);
     227             : 
     228          34 :           if (fwrite (outbuf, 1, n, out) < n)
     229           0 :             error (EXIT_FAILURE, errno, _("write error"));
     230             : 
     231          34 :           if (!ok)
     232          13 :             error (EXIT_FAILURE, 0, _("invalid input"));
     233             :         }
     234             :     }
     235           9 :   while (!feof (in));
     236           9 : }
     237             : 
     238             : int
     239          83 : main (int argc, char **argv)
     240             : {
     241             :   int opt;
     242             :   FILE *input_fh;
     243             :   const char *infile;
     244             : 
     245             :   /* True if --decode has bene given and we should decode data. */
     246          83 :   bool decode = false;
     247             :   /* True if we should ignore non-alphabetic characters. */
     248          83 :   bool ignore_garbage = false;
     249             :   /* Wrap encoded base64 data around the 76:th column, by default. */
     250          83 :   uintmax_t wrap_column = 76;
     251             : 
     252             :   initialize_main (&argc, &argv);
     253          83 :   program_name = argv[0];
     254          83 :   setlocale (LC_ALL, "");
     255             :   bindtextdomain (PACKAGE, LOCALEDIR);
     256             :   textdomain (PACKAGE);
     257             : 
     258          83 :   atexit (close_stdout);
     259             : 
     260         206 :   while ((opt = getopt_long (argc, argv, "dqiw:", long_options, NULL)) != -1)
     261          61 :     switch (opt)
     262             :       {
     263          29 :       case 'd':
     264          29 :         decode = true;
     265          29 :         break;
     266             : 
     267          12 :       case 'w':
     268          12 :         if (xstrtoumax (optarg, NULL, 0, &wrap_column, NULL) != LONGINT_OK)
     269           7 :           error (EXIT_FAILURE, 0, _("invalid wrap size: %s"),
     270             :                  quotearg (optarg));
     271           5 :         break;
     272             : 
     273           6 :       case 'i':
     274           6 :         ignore_garbage = true;
     275           6 :         break;
     276             : 
     277           2 :         case_GETOPT_HELP_CHAR;
     278             : 
     279           2 :         case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHOR);
     280             : 
     281          10 :       default:
     282          10 :         usage (EXIT_FAILURE);
     283           0 :         break;
     284             :       }
     285             : 
     286          62 :   if (argc - optind > 1)
     287             :     {
     288          21 :       error (0, 0, _("extra operand %s"), quote (argv[optind]));
     289          21 :       usage (EXIT_FAILURE);
     290             :     }
     291             : 
     292          41 :   if (optind < argc)
     293          28 :     infile = argv[optind];
     294             :   else
     295          13 :     infile = "-";
     296             : 
     297          41 :   if (STREQ (infile, "-"))
     298          29 :     input_fh = stdin;
     299             :   else
     300             :     {
     301          12 :       input_fh = fopen (infile, "r");
     302          12 :       if (input_fh == NULL)
     303           2 :         error (EXIT_FAILURE, errno, "%s", infile);
     304             :     }
     305             : 
     306          39 :   if (decode)
     307          25 :     do_decode (input_fh, stdout, ignore_garbage);
     308             :   else
     309          14 :     do_encode (input_fh, stdout, wrap_column);
     310             : 
     311          18 :   if (fclose (input_fh) == EOF)
     312             :     {
     313           0 :       if (STREQ (infile, "-"))
     314           0 :         error (EXIT_FAILURE, errno, _("closing standard input"));
     315             :       else
     316           0 :         error (EXIT_FAILURE, errno, "%s", infile);
     317             :     }
     318             : 
     319          18 :   exit (EXIT_SUCCESS);
     320             : }

Generated by: LCOV version 1.10