/* Base64 encode/decode strings or files.                                       This is the base64 utility
   Copyright (C) 2004-2018 Free Software Foundation, Inc.                       
                                                                                
   This file is part of Base64.                                                 
                                                                                
   This program is free software: you can redistribute it and/or modify         
   it under the terms of the GNU General Public License as published by         
   the Free Software Foundation, either version 3 of the License, or            
   (at your option) any later version.                                          
                                                                                
   This program is distributed in the hope that it will be useful,              
   but WITHOUT ANY WARRANTY; without even the implied warranty of               
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the                
   GNU General Public License for more details.                                 
                                                                                
   You should have received a copy of the GNU General Public License            
   along with this program.  If not, see <https://www.gnu.org/licenses/>. */    The GNUv3 license
                                                                                
/* Written by Simon Josefsson <simon@josefsson.org>.  */                        
                                                                                
#include <config.h>                                                             Provides system specific information
                                                                                
#include <stdio.h>                                                              Provides standard I/O capability
#include <getopt.h>                                                             ...!includes auto-comment...
#include <sys/types.h>                                                          Provides system data types
                                                                                
#include "system.h"                                                             ...!includes auto-comment...
#include "die.h"                                                                ...!includes auto-comment...
#include "error.h"                                                              ...!includes auto-comment...
#include "fadvise.h"                                                            ...!includes auto-comment...
#include "quote.h"                                                              ...!includes auto-comment...
#include "xstrtol.h"                                                            ...!includes auto-comment...
#include "xdectoint.h"                                                          ...!includes auto-comment...
#include "xbinary-io.h"                                                         ...!includes auto-comment...
                                                                                
#define AUTHORS proper_name ("Simon Josefsson")                                 Line 36
                                                                                
#if BASE_TYPE == 32                                                             Line 38
# include "base32.h"                                                            Line 39
# define PROGRAM_NAME "base32"                                                  Line 40
#else                                                                           Line 41
# include "base64.h"                                                            Line 42
# define PROGRAM_NAME "base64"                                                  Line 43
#endif                                                                          Line 44
                                                                                
                                                                                
static struct option const long_options[] =                                     Line 47
{                                                                               
  {"decode", no_argument, 0, 'd'},                                              Line 49
  {"wrap", required_argument, 0, 'w'},                                          Line 50
  {"ignore-garbage", no_argument, 0, 'i'},                                      Line 51
                                                                                
  {GETOPT_HELP_OPTION_DECL},                                                    Line 53
  {GETOPT_VERSION_OPTION_DECL},                                                 Line 54
  {NULL, 0, NULL, 0}                                                            Line 55
};                                                                              Block 1
                                                                                
void                                                                            Line 58
usage (int status)                                                              Line 59
{                                                                               
  if (status != EXIT_SUCCESS)                                                   Line 61
    emit_try_help ();                                                           ...!common auto-comment...
  else                                                                          Line 63
    {                                                                           
      printf (_("\                                                              Line 65
Usage: %s [OPTION]... [FILE]\n\                                                 Line 66
Base%d encode or decode FILE, or standard input, to standard output.\n\         Line 67
"), program_name, BASE_TYPE);                                                   Line 68
                                                                                
      emit_stdin_note ();                                                       ...!common auto-comment...
      emit_mandatory_arg_note ();                                               ...!common auto-comment...
                                                                                
      fputs (_("\                                                               Line 73
  -d, --decode          decode data\n\                                          Line 74
  -i, --ignore-garbage  when decoding, ignore non-alphabet characters\n\        Line 75
  -w, --wrap=COLS       wrap encoded lines after COLS character (default 76).\n\Line 76
                          Use 0 to disable line wrapping\n\                     Line 77
\n\                                                                             
"), stdout);                                                                    Line 79
      fputs (HELP_OPTION_DESCRIPTION, stdout);                                  Line 80
      fputs (VERSION_OPTION_DESCRIPTION, stdout);                               Line 81
      printf (_("\                                                              Line 82
\n\                                                                             
The data are encoded as described for the %s alphabet in RFC 4648.\n\           Line 84
When decoding, the input may contain newlines in addition to the bytes of\n\    Line 85
the formal %s alphabet.  Use --ignore-garbage to attempt to recover\n\          Line 86
from any other non-alphabet bytes in the encoded stream.\n"),                   Line 87
              PROGRAM_NAME, PROGRAM_NAME);                                      Line 88
      emit_ancillary_info (PROGRAM_NAME);                                       Line 89
    }                                                                           
                                                                                
  exit (status);                                                                Line 92
}                                                                               Block 2
                                                                                
#define ENC_BLOCKSIZE (1024*3*10)                                               Line 95
                                                                                
#if BASE_TYPE == 32                                                             Line 97
# define BASE_LENGTH BASE32_LENGTH                                              Line 98
/* Note that increasing this may decrease performance if --ignore-garbage       
   is used, because of the memmove operation below.  */                         
# define DEC_BLOCKSIZE (1024*5)                                                 Line 101
                                                                                
/* Ensure that BLOCKSIZE is a multiple of 5 and 8.  */                          
verify (ENC_BLOCKSIZE % 40 == 0);  /* So padding chars only on last block.  */  Line 104
verify (DEC_BLOCKSIZE % 40 == 0);  /* So complete encoded blocks are used.  */  Line 105
                                                                                
# define base_encode base32_encode                                              Line 107
# define base_decode_context base32_decode_context                              Line 108
# define base_decode_ctx_init base32_decode_ctx_init                            Line 109
# define base_decode_ctx base32_decode_ctx                                      Line 110
# define isbase isbase32                                                        Line 111
#else                                                                           Line 112
# define BASE_LENGTH BASE64_LENGTH                                              Line 113
/* Note that increasing this may decrease performance if --ignore-garbage       
   is used, because of the memmove operation below.  */                         
# define DEC_BLOCKSIZE (1024*3)                                                 Line 116
                                                                                
/* Ensure that BLOCKSIZE is a multiple of 3 and 4.  */                          
verify (ENC_BLOCKSIZE % 12 == 0);  /* So padding chars only on last block.  */  Line 119
verify (DEC_BLOCKSIZE % 12 == 0);  /* So complete encoded blocks are used.  */  Line 120
                                                                                
# define base_encode base64_encode                                              Line 122
# define base_decode_context base64_decode_context                              Line 123
# define base_decode_ctx_init base64_decode_ctx_init                            Line 124
# define base_decode_ctx base64_decode_ctx                                      Line 125
# define isbase isbase64                                                        Line 126
#endif                                                                          Line 127
                                                                                
static void                                                                     Line 129
wrap_write (const char *buffer, size_t len,                                     Line 130...!syscalls auto-comment...
            uintmax_t wrap_column, size_t *current_column, FILE *out)           Line 131
{                                                                               
  size_t written;                                                               Line 133
                                                                                
  if (wrap_column == 0)                                                         Line 135
    {                                                                           
      /* Simple write. */                                                       
      if (fwrite (buffer, 1, len, stdout) < len)                                Line 138...!syscalls auto-comment...
        die (EXIT_FAILURE, errno, _("write error"));                            Line 139
    }                                                                           
  else                                                                          Line 141
    for (written = 0; written < len;)                                           Line 142
      {                                                                         
        uintmax_t cols_remaining = wrap_column - *current_column;               Line 144
        size_t to_write = MIN (cols_remaining, SIZE_MAX);                       Line 145
        to_write = MIN (to_write, len - written);                               Line 146
                                                                                
        if (to_write == 0)                                                      Line 148
          {                                                                     
            if (fputc ('\n', out) == EOF)                                       Line 150
              die (EXIT_FAILURE, errno, _("write error"));                      Line 151
            *current_column = 0;                                                Line 152
          }                                                                     
        else                                                                    Line 154
          {                                                                     
            if (fwrite (buffer + written, 1, to_write, stdout) < to_write)      Line 156...!syscalls auto-comment...
              die (EXIT_FAILURE, errno, _("write error"));                      Line 157
            *current_column += to_write;                                        Line 158
            written += to_write;                                                Line 159
          }                                                                     
      }                                                                         
}                                                                               Block 3
                                                                                
static void                                                                     Line 164
do_encode (FILE *in, FILE *out, uintmax_t wrap_column)                          Line 165
{                                                                               
  size_t current_column = 0;                                                    Line 167
  char inbuf[ENC_BLOCKSIZE];                                                    Line 168
  char outbuf[BASE_LENGTH (ENC_BLOCKSIZE)];                                     Line 169
  size_t sum;                                                                   Line 170
                                                                                
  do                                                                            
    {                                                                           
      size_t n;                                                                 Line 174
                                                                                
      sum = 0;                                                                  Line 176
      do                                                                        
        {                                                                       
          n = fread (inbuf + sum, 1, ENC_BLOCKSIZE - sum, in);                  Line 179...!syscalls auto-comment...
          sum += n;                                                             Line 180
        }                                                                       
      while (!feof (in) && !ferror (in) && sum < ENC_BLOCKSIZE);                Line 182
                                                                                
      if (sum > 0)                                                              Line 184
        {                                                                       
          /* Process input one block at a time.  Note that ENC_BLOCKSIZE        
             is sized so that no pad chars will appear in output. */            
          base_encode (inbuf, sum, outbuf, BASE_LENGTH (sum));                  Line 188
                                                                                
          wrap_write (outbuf, BASE_LENGTH (sum), wrap_column,                   Line 190...!syscalls auto-comment...
                      ¤t_column, out);                                    Line 191
        }                                                                       
    }                                                                           
  while (!feof (in) && !ferror (in) && sum == ENC_BLOCKSIZE);                   Line 194
                                                                                
  /* When wrapping, terminate last line. */                                     
  if (wrap_column && current_column > 0 && fputc ('\n', out) == EOF)            Line 197
    die (EXIT_FAILURE, errno, _("write error"));                                Line 198
                                                                                
  if (ferror (in))                                                              Line 200
    die (EXIT_FAILURE, errno, _("read error"));                                 Line 201
}                                                                               Block 4
                                                                                
static void                                                                     Line 204
do_decode (FILE *in, FILE *out, bool ignore_garbage)                            Line 205
{                                                                               
  char inbuf[BASE_LENGTH (DEC_BLOCKSIZE)];                                      Line 207
  char outbuf[DEC_BLOCKSIZE];                                                   Line 208
  size_t sum;                                                                   Line 209
  struct base_decode_context ctx;                                               Line 210
                                                                                
  base_decode_ctx_init (&ctx);                                                  Line 212
                                                                                
  do                                                                            
    {                                                                           
      bool ok;                                                                  Line 216
      size_t n;                                                                 Line 217
      unsigned int k;                                                           Line 218
                                                                                
      sum = 0;                                                                  Line 220
      do                                                                        
        {                                                                       
          n = fread (inbuf + sum, 1, BASE_LENGTH (DEC_BLOCKSIZE) - sum, in);    Line 223...!syscalls auto-comment...
                                                                                
          if (ignore_garbage)                                                   Line 225
            {                                                                   
              for (size_t i = 0; n > 0 && i < n;)                               Line 227
                {                                                               
                  if (isbase (inbuf[sum + i]) || inbuf[sum + i] == '=')         Line 229
                    i++;                                                        Line 230
                  else                                                          Line 231
                    memmove (inbuf + sum + i, inbuf + sum + i + 1, --n - i);    Line 232
                }                                                               
            }                                                                   
                                                                                
          sum += n;                                                             Line 236
                                                                                
          if (ferror (in))                                                      Line 238
            die (EXIT_FAILURE, errno, _("read error"));                         Line 239
        }                                                                       
      while (sum < BASE_LENGTH (DEC_BLOCKSIZE) && !feof (in));                  Line 241
                                                                                
      /* The following "loop" is usually iterated just once.                    
         However, when it processes the final input buffer, we want             
         to iterate it one additional time, but with an indicator               
         telling it to flush what is in CTX.  */                                
      for (k = 0; k < 1 + !!feof (in); k++)                                     Line 247
        {                                                                       
          if (k == 1 && ctx.i == 0)                                             Line 249
            break;                                                              Line 250
          n = DEC_BLOCKSIZE;                                                    Line 251
          ok = base_decode_ctx (&ctx, inbuf, (k == 0 ? sum : 0), outbuf, &n);   Line 252
                                                                                
          if (fwrite (outbuf, 1, n, out) < n)                                   Line 254...!syscalls auto-comment...
            die (EXIT_FAILURE, errno, _("write error"));                        Line 255
                                                                                
          if (!ok)                                                              Line 257
            die (EXIT_FAILURE, 0, _("invalid input"));                          Line 258
        }                                                                       
    }                                                                           
  while (!feof (in));                                                           Line 261
}                                                                               Block 5
                                                                                
int                                                                             
main (int argc, char **argv)                                                    Line 265
{                                                                               
  int opt;                                                                      Line 267
  FILE *input_fh;                                                               Line 268
  const char *infile;                                                           Line 269
                                                                                
  /* True if --decode has been given and we should decode data. */              
  bool decode = false;                                                          Line 272
  /* True if we should ignore non-base-alphabetic characters. */                
  bool ignore_garbage = false;                                                  Line 274
  /* Wrap encoded data around the 76:th column, by default. */                  
  uintmax_t wrap_column = 76;                                                   Line 276
                                                                                
  initialize_main (&argc, &argv);                                               VMS-specific entry point handling wildcard expansion
  set_program_name (argv[0]);                                                   Retains program name and discards path
  setlocale (LC_ALL, "");                                                       Sets up internationalization (i18n)
  bindtextdomain (PACKAGE, LOCALEDIR);                                          Assigns i18n directorySets text domain for _() [gettext()] function
  textdomain (PACKAGE);                                                         Sets text domain for _() [gettext()] function
                                                                                
  atexit (close_stdout);                                                        Close stdout on exit (see gnulib)
                                                                                
  while ((opt = getopt_long (argc, argv, "diw:", long_options, NULL)) != -1)    Line 286
    switch (opt)                                                                Line 287
      {                                                                         
      case 'd':                                                                 Line 289
        decode = true;                                                          Line 290
        break;                                                                  Line 291
                                                                                
      case 'w':                                                                 Line 293
        wrap_column = xdectoumax (optarg, 0, UINTMAX_MAX, "",                   Line 294
                                  _("invalid wrap size"), 0);                   Line 295
        break;                                                                  Line 296
                                                                                
      case 'i':                                                                 Line 298
        ignore_garbage = true;                                                  Line 299
        break;                                                                  Line 300
                                                                                
      case_GETOPT_HELP_CHAR;                                                    Line 302
                                                                                
      case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);                         Line 304
                                                                                
      default:                                                                  Line 306
        usage (EXIT_FAILURE);                                                   Line 307
        break;                                                                  Line 308
      }                                                                         
                                                                                
  if (argc - optind > 1)                                                        Line 311
    {                                                                           
      error (0, 0, _("extra operand %s"), quote (argv[optind]));                Line 313
      usage (EXIT_FAILURE);                                                     Line 314
    }                                                                           
                                                                                
  if (optind < argc)                                                            Line 317
    infile = argv[optind];                                                      Line 318
  else                                                                          Line 319
    infile = "-";                                                               Line 320
                                                                                
  if (STREQ (infile, "-"))                                                      Line 322
    {                                                                           
      xset_binary_mode (STDIN_FILENO, O_BINARY);                                Line 324
      input_fh = stdin;                                                         Line 325
    }                                                                           
  else                                                                          Line 327
    {                                                                           
      input_fh = fopen (infile, "rb");                                          Line 329...!syscalls auto-comment...
      if (input_fh == NULL)                                                     Line 330
        die (EXIT_FAILURE, errno, "%s", quotef (infile));                       Line 331
    }                                                                           
                                                                                
  fadvise (input_fh, FADVISE_SEQUENTIAL);                                       Line 334...!syscalls auto-comment...
                                                                                
  if (decode)                                                                   Line 336
    do_decode (input_fh, stdout, ignore_garbage);                               Line 337
  else                                                                          Line 338
    do_encode (input_fh, stdout, wrap_column);                                  Line 339
                                                                                
  if (fclose (input_fh) == EOF)                                                 Line 341...!syscalls auto-comment...
    {                                                                           
      if (STREQ (infile, "-"))                                                  Line 343
        die (EXIT_FAILURE, errno, _("closing standard input"));                 Line 344
      else                                                                      Line 345
        die (EXIT_FAILURE, errno, "%s", quotef (infile));                       Line 346
    }                                                                           
                                                                                
  return EXIT_SUCCESS;                                                          Line 349
}                                                                               Block 6