¸1 BASE64 INTRODUCTION 1

1. Introduction.
BASE64
Encode or decode file as MIME base64 (RFC 1341)
by John Walker
http://www.fourmilab.ch/
This program is in the public domain.
EBCDIC support courtesy of Christian.Ferrari@fccrt.it, 2000-12-20.
#define REVDATE "10thJune2007"
2 PROGRAM GLOBAL CONTEXT BASE64 ¸2
2. Program global context.
#define TRUE 1
#define FALSE 0
#define LINELEN 72 /∗ Encoded line length (max 76) ∗/
#define MAXINLINE 256 /∗ Maximum input line length ∗/
#include "config.h" /∗ System-dependent configuration ∗/
¸ Preprocessor definitions )
¸ System include files 3 )
¸ Windows-specific include files 4 )
¸ Global variables 5 )
3. We include the following POSIX-standard C library files. Conditionals based on a probe of the system
by the configure program allow us to cope with the peculiarities of specific systems.
¸ System include files 3 ) ≡
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#ifdef HAVE_STRING_H
#include <string.h>
#else
#ifdef HAVE_STRINGS_H
#include <strings.h>
#endif
#endif
#ifdef HAVE_GETOPT
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#else
#include "getopt.h" /∗ No system getopt–use our own ∗/
#endif
This code is used in section 2.
4. The following include files are needed in WIN32 builds to permit setting already-open I/O streams to
binary mode.
¸ Windows-specific include files 4 ) ≡
#ifdef _WIN32
#define FORCE_BINARY_IO
#include <io.h>
#include <fcntl.h>
#endif
This code is used in section 2.
¸5 BASE64 PROGRAM GLOBAL CONTEXT 3
5. These variables are global to all procedures; many are used as “hidden arguments” to functions in order
to simplify calling sequences.
¸ Global variables 5 ) ≡
typedef unsigned char byte; /∗ Byte type ∗/
static FILE ∗fi; /∗ Input file ∗/
static FILE ∗fo; /∗ Output file ∗/
static byte iobuf [MAXINLINE]; /∗ I/O buffer ∗/
static int iolen = 0; /∗ Bytes left in I/O buffer ∗/
static int iocp = MAXINLINE; /∗ Character removal pointer ∗/
static int ateof = FALSE; /∗ EOF encountered ∗/
static byte dtable[256]; /∗ Encode / decode table ∗/
static int linelength = 0; /∗ Length of encoded output line ∗/
static char eol [ ] = /∗ End of line sequence ∗/
#ifdef FORCE_BINARY_IO
"\n"
#else
"\r\n"
#endif
;
static int errcheck = TRUE; /∗ Check decode input for errors ? ∗/
This code is used in section 2.
4 INPUT/OUTPUT FUNCTIONS BASE64 ¸6
6. Input/output functions.
7. Procedure inbuf fills the input buffer with data from the input stream fi.
static int inbuf (void)
¦
int l;
if (ateof ) ¦
return FALSE;
¦
l = fread (iobuf , 1, MAXINLINE, fi); /∗ Read input buffer ∗/
if (l ≤ 0) ¦
if (ferror (fi)) ¦
exit (1);
¦
ateof = TRUE;
return FALSE;
¦
iolen = l;
iocp = 0;
return TRUE;
¦
8. Procedure inchar returns the next character from the input line. At end of line, it calls inbuf to read
the next line, returning EOF at end of file.
static int inchar (void)
¦
if (iocp ≥ iolen) ¦
if (inbuf ( )) ¦
return EOF;
¦
¦
return iobuf [iocp
++
];
¦
9. Procedure insig returns the next significant input character, ignoring white space and control characters.
This procedure uses inchar to read the input stream and returns EOF when the end of the input file is reached.
static int insig (void)
¦
int c;
while (TRUE) ¦
c = inchar ( );
if (c ≡ EOF ∨ (c > ’’)) ¦
return c;
¦
¦
¦
¸10 BASE64 INPUT/OUTPUT FUNCTIONS 5
10. Procedure ochar outputs an encoded character, inserting line breaks as required so that no line exceeds
LINELEN characters.
static void ochar (int c)
¦
if (linelength ≥ LINELEN) ¦
if (fputs (eol , fo) ≡ EOF) ¦
exit (1);
¦
linelength = 0;
¦
if (putc(((byte) c), fo) ≡ EOF) ¦
exit (1);
¦
linelength
++
;
¦
6 ENCODING BASE64 ¸11
11. Encoding.
Procedure encode encodes the binary file opened as fi into base64, writing the output to fo.
static void encode(void)
¦
int i, hiteof = FALSE;
¸ initialise encoding table 12 );
while (hiteof ) ¦
byte igroup[3], ogroup[4];
int c, n;
igroup[0] = igroup[1] = igroup[2] = 0;
for (n = 0; n < 3; n
++
) ¦
c = inchar ( );
if (c ≡ EOF) ¦
hiteof = TRUE;
break;
¦
igroup[n] = (byte) c;
¦
if (n > 0) ¦
ogroup[0] = dtable[igroup[0] ¸ 2];
ogroup[1] = dtable[((igroup[0] & 3) ¸ 4) [ (igroup[1] ¸ 4)];
ogroup[2] = dtable[((igroup[1] &
#
F) ¸ 2) [ (igroup[2] ¸ 6)];
ogroup[3] = dtable[igroup[2] &
#
3F]; /∗ Replace characters in output stream with ”=” pad
characters if fewer than three characters were read from the end of the input stream. ∗/
if (n < 3) ¦
ogroup[3] = ’=’;
if (n < 2) ¦
ogroup[2] = ’=’;
¦
¦
for (i = 0; i < 4; i
++
) ¦
ochar (ogroup[i]);
¦
¦
¦
if (fputs (eol , fo) ≡ EOF) ¦
exit (1);
¦
¦
¸12 BASE64 ENCODING 7
12. Procedure initialise encoding table fills the binary encoding table with the characters the 6 bit values
are mapped into. The curious and disparate sequences used to fill this table permit this code to work both
on ASCII and EBCDIC systems, the latter thanks to Ch.F.
In EBCDIC systems character codes for letters are not consecutive; the initialisation must be split to
accommodate the EBCDIC consecutive letters:
A–I J–R S–Z a–i j–r s–z
This code works on ASCII as well as EBCDIC systems.
¸ initialise encoding table 12 ) ≡
for (i = 0; i < 9; i
++
) ¦
dtable[i] = ’A’ + i;
dtable[i + 9] = ’J’ + i;
dtable[26 + i] = ’a’ + i;
dtable[26 + i + 9] = ’j’ + i;
¦
for (i = 0; i < 8; i
++
) ¦
dtable[i + 18] = ’S’ + i;
dtable[26 + i + 18] = ’s’ + i;
¦
for (i = 0; i < 10; i
++
) ¦
dtable[52 + i] = ’0’ + i;
¦
dtable[62] = ’+’;
dtable[63] = ’/’;
This code is used in section 11.
8 DECODING BASE64 ¸13
13. Decoding.
Procedure decode decodes a base64 encoded stream from fi and emits the binary result on fo.
static void decode(void)
¦
int i;
¸ Initialise decode table 14 );
while (TRUE) ¦
byte a[4], b[4], o[3];
for (i = 0; i < 4; i
++
) ¦
int c = insig ( );
if (c ≡ EOF) ¦
if (errcheck ∧ (i > 0)) ¦
fprintf (stderr , "Inputfileincomplete.\n");
exit (1);
¦
return;
¦
if (dtable[c] &
#
80) ¦
if (errcheck ) ¦
fprintf (stderr , "Illegalcharacter’%c’ininputfile.\n", c);
exit (1);
¦ /∗ Ignoring errors: discard invalid character. ∗/
i
−−
;
continue;
¦
a[i] = (byte) c;
b[i] = (byte) dtable[c];
¦
o[0] = (b[0] ¸ 2) [ (b[1] ¸ 4);
o[1] = (b[1] ¸ 4) [ (b[2] ¸ 2);
o[2] = (b[2] ¸ 6) [ b[3];
i = a[2] ≡ ’=’ ? 1 : (a[3] ≡ ’=’ ? 2 : 3);
if (fwrite(o, i, 1, fo) ≡ EOF) ¦
exit (1);
¦
if (i < 3) ¦
return;
¦
¦
¦
¸14 BASE64 DECODING 9
14. Procedure initialisedecodetable creates the lookup table used to map base64 characters into their
binary values from 0 to 63. The table is built in this rather curious way in order to be properly initialised
for both ASCII-based systems and those using EBCDIC, where the letters are not contiguous. (EBCDIC
fixes courtesy of Ch.F.)
In EBCDIC systems character codes for letters are not consecutive; the initialisation must be split to
accommodate the EBCDIC consecutive letters:
A–I J–R S–Z a–i j–r s–z
This code works on ASCII as well as EBCDIC systems.
¸ Initialise decode table 14 ) ≡
for (i = 0; i < 255; i
++
) ¦
dtable[i] =
#
80;
¦
for (i = ’A’; i ≤ ’I’; i
++
) ¦
dtable[i] = 0 + (i −’A’);
¦
for (i = ’J’; i ≤ ’R’; i
++
) ¦
dtable[i] = 9 + (i −’J’);
¦
for (i = ’S’; i ≤ ’Z’; i
++
) ¦
dtable[i] = 18 + (i −’S’);
¦
for (i = ’a’; i ≤ ’i’; i
++
) ¦
dtable[i] = 26 + (i −’a’);
¦
for (i = ’j’; i ≤ ’r’; i
++
) ¦
dtable[i] = 35 + (i −’j’);
¦
for (i = ’s’; i ≤ ’z’; i
++
) ¦
dtable[i] = 44 + (i −’s’);
¦
for (i = ’0’; i ≤ ’9’; i
++
) ¦
dtable[i] = 52 + (i −’0’);
¦
dtable[’+’] = 62;
dtable[’/’] = 63;
dtable[’=’] = 0;
This code is used in section 13.
10 UTILITY FUNCTIONS BASE64 ¸15
15. Utility functions.
16. Procedure usage prints how-to-call information.
static void usage(void)
¦
printf ("%s−−Encode/decodefileasbase64.Call:\n", PRODUCT);
printf ("%s[−e/−d][options][infile][outfile]\n", PRODUCT);
printf ("\n");
printf ("Options:\n");
printf ("−−copyrightPrintcopyrightinformation\n");
printf ("−d,−−decodeDecodebase64encodedfile\n");
printf ("−e,−−encodeEncodefileintobase64\n");
printf ("−n,−−noerrcheckIgnoreerrorswhendecoding\n");
printf ("−u,−−helpPrintthismessage\n");
printf ("−−versionPrintversionnumber\n");
printf ("\n");
printf ("byJohnWalker\n");
printf ("http://www.fourmilab.ch/\n");
¦
¸17 BASE64 MAIN PROGRAM 11
17. Main program.
int main(int argc, char ∗argv [ ])
¦
extern char ∗optarg ; /∗ Imported from getopt ∗/
extern int optind ;
int f, decoding = FALSE, opt ;
#ifdef FORCE_BINARY_IO
int in std = TRUE, out std = TRUE;
#endif
char ∗cp; /∗ 2000-12-20 Ch.F. UNIX/390 C compiler (cc) does not allow initialisation of static
variables with non static right-value during variable declaration; it was moved from declaration to
main function start. ∗/
fi = stdin;
fo = stdout ;
¸ Process command-line options 18 );
¸ Process command-line arguments 19 );
¸ Force binary I/O where required 20 );
if (decoding ) ¦
decode( );
¦
else ¦
encode( );
¦
return 0;
¦
12 MAIN PROGRAM BASE64 ¸18
18. We use getopt to process command line options. This permits aggregation of options without
arguments and both −darg and −d arg syntax.
¸ Process command-line options 18 ) ≡
while ((opt = getopt (argc, argv , "denu−:")) ,= −1) ¦
switch (opt ) ¦
case ’d’: /∗ -d Decode ∗/
decoding = TRUE;
break;
case ’e’: /∗ -e Encode ∗/
decoding = FALSE;
break;
case ’n’: /∗ -n Suppress error checking ∗/
errcheck = FALSE;
break;
case ’u’: /∗ -u Print how-to-call information ∗/
case ’?’: usage( );
return 0;
case ’−’: /∗ – Extended options ∗/
switch (optarg [0]) ¦
case ’c’: /∗ –copyright ∗/
printf ("Thisprogramisinthepublicdomain.\n");
return 0;
case ’d’: /∗ –decode ∗/
decoding = TRUE;
break;
case ’e’: /∗ -encode ∗/
decoding = FALSE;
break;
case ’h’: /∗ –help ∗/
usage( );
return 0;
case ’n’: /∗ –noerrcheck ∗/
errcheck = FALSE;
break;
case ’v’: /∗ –version ∗/
printf ("%s%s\n", PRODUCT, VERSION);
printf ("Lastrevised:%s\n", REVDATE);
printf ("Thelatestversionisalwaysavailable\n");
printf ("athttp://www.fourmilab.ch/webtools/base64\n");
return 0;
¦
¦
¦
This code is used in section 17.
¸19 BASE64 MAIN PROGRAM 13
19. This code is executed after getopt has completed parsing command line options. At this point the
external variable optind in getopt contains the index of the first argument in the argv [ ] array.
¸ Process command-line arguments 19 ) ≡
f = 0;
for ( ; optind < argc; optind
++
) ¦
cp = argv [optind ];
switch (f) ¦ /∗ * Warning! On systems which distinguish text mode and binary I/O (MS-DOS,
Macintosh, etc.) the modes in these open statements will have to be made conditional based
upon whether an encode or decode is being done, which will have to be specified earlier. But it’s
worse: if input or output is from standard input or output, the mode will have to be changed on
the fly, which is generally system and compiler dependent. ’Twasn’t me who couldn’t conform
to Unix CR/LF convention, so don’t ask me to write the code to work around Apple and
Microsoft’s incompatible standards. * ∗/
case 0:
if (strcmp(cp, "−") ,= 0) ¦
if ((fi = fopen(cp,
#ifdef FORCE_BINARY_IO
decoding ? "r" : "rb"
#else
"r"
#endif
)) ≡ Λ) ¦
fprintf (stderr , "Cannotopeninputfile%s\n", cp);
return 2;
¦
#ifdef FORCE_BINARY_IO
in std = FALSE;
#endif
¦
f
++
;
break;
case 1:
if (strcmp(cp, "−") ,= 0) ¦
if ((fo = fopen(cp,
#ifdef FORCE_BINARY_IO
decoding ? "wb" : "w"
#else
"w"
#endif
)) ≡ Λ) ¦
fprintf (stderr , "Cannotopenoutputfile%s\n", cp);
return 2;
¦
#ifdef FORCE_BINARY_IO
out std = FALSE;
#endif
¦
f
++
;
break;
default: fprintf (stderr , "Toomanyfilenamesspecified.\n");
usage( );
return 2;
14 MAIN PROGRAM BASE64 ¸19
¦
¦
This code is used in section 17.
20. On WIN32, if the binary stream is the default of stdin/stdout, we must place this stream, opened in
text mode (translation of CR to CR/LF) by default, into binary mode (no EOL translation). If you port this
code to other platforms which distinguish between text and binary file I/O (for example, the Macintosh),
you’ll need to add equivalent code here.
The following code sets the already-open standard stream to binary mode on Microsoft Visual C 5.0
(Monkey C). If you’re using a different version or compiler, you may need some other incantation to cancel
the text translation spell.
¸ Force binary I/O where required 20 ) ≡
#ifdef FORCE_BINARY_IO
if ((decoding ∧ out std ) ∨ ((decoding ) ∧ in std )) ¦
#ifdef _WIN32
setmode( fileno(decoding ? fo : fi), O_BINARY);
#endif
¦
#endif
This code is used in section 17.
¸21 BASE64 INDEX 15
21. Index. The following is a cross-reference table for base64. Single-character identifiers are not
indexed, nor are reserved words. Underlined entries indicate where an identifier was declared.
fileno: 20.
setmode: 20.
_WIN32: 4, 20.
a: 13.
argc: 17, 18, 19.
argv : 17, 18, 19.
ateof : 5, 7.
b: 13.
byte: 5, 10, 11, 13.
c: 9, 10, 11, 13.
cp: 17, 19.
decode: 13, 14, 17.
decoding : 17, 18, 19, 20.
dtable: 5, 11, 12, 13, 14.
encode: 11, 17.
EOF: 8, 9, 10, 11, 13.
eol : 5, 10, 11.
errcheck : 5, 13, 18.
exit : 7, 10, 11, 13.
f: 17.
FALSE: 2, 5, 7, 11, 17, 18, 19.
ferror : 7.
fi: 5, 7, 11, 13, 17, 19, 20.
fo: 5, 10, 11, 13, 17, 19, 20.
fopen: 19.
FORCE_BINARY_IO: 4, 5, 17, 19, 20.
fprintf : 13, 19.
fputs : 10, 11.
fread : 7.
fwrite: 13.
getopt : 17, 18, 19.
HAVE_GETOPT: 3.
HAVE_STRING_H: 3.
HAVE_STRINGS_H: 3.
HAVE_UNISTD_H: 3.
hiteof : 11.
i: 11, 13.
igroup: 11.
in std : 17, 19, 20.
inbuf : 7, 8.
inchar : 8, 9, 11.
initialise: 14.
initialise encoding table: 12.
insig : 9, 13.
iobuf : 5, 7, 8.
iocp: 5, 7, 8.
iolen: 5, 7, 8.
l: 7.
LINELEN: 2, 10.
linelength: 5, 10.
main: 17.
MAXINLINE: 2, 5, 7.
n: 11.
o: 13.
O_BINARY: 20.
ochar : 10, 11.
ogroup: 11.
opt : 17, 18.
optarg : 17, 18.
optind : 17, 19.
out std : 17, 19, 20.
printf : 16, 18.
PRODUCT: 16, 18.
putc: 10.
REVDATE: 1, 18.
stderr : 13, 19.
stdin: 17.
stdout : 17.
strcmp: 19.
table: 14.
TRUE: 2, 5, 7, 9, 11, 13, 17, 18.
usage: 16, 18, 19.
VERSION: 18.
16 NAMES OF THE SECTIONS BASE64
¸ Force binary I/O where required 20 ) Used in section 17.
¸ Global variables 5 ) Used in section 2.
¸ Initialise decode table 14 ) Used in section 13.
¸ Process command-line arguments 19 ) Used in section 17.
¸ Process command-line options 18 ) Used in section 17.
¸ System include files 3 ) Used in section 2.
¸ Windows-specific include files 4 ) Used in section 2.
¸ initialise encoding table 12 ) Used in section 11.
BASE64
Section Page
Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1 1
Program global context . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2 2
Input/output functions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6 4
Encoding . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11 6
Decoding . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13 8
Utility functions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15 10
Main program . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17 11
Index . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21 15

h" /∗ No system getopt–use our own ∗/ #endif This code is used in section 2.h" /∗ System-dependent configuration ∗/ Preprocessor definitions System include files 3 Windows-specific include files 4 Global variables 5 3.h> #else #ifdef HAVE_STRINGS_H #include <strings. . System include files 3 ≡ #include <stdio. PROGRAM GLOBAL CONTEXT BASE64 §2 Program global context. The following include files are needed in WIN32 builds to permit setting already-open I/O streams to binary mode. 4.h> #endif This code is used in section 2. We include the following POSIX-standard C library files.h> #include <ctype. Conditionals based on a probe of the system by the configure program allow us to cope with the peculiarities of specific systems.h> #include <fcntl. #define TRUE 1 #define FALSE 0 #define LINELEN 72 /∗ Encoded line length (max 76) ∗/ #define MAXINLINE 256 /∗ Maximum input line length ∗/ #include "config.h> #endif #else #include "getopt.2 2.h> #include <stdlib. Windows-specific include files 4 ≡ #ifdef _WIN32 #define FORCE_BINARY_IO #include <io.h> #ifdef HAVE_STRING_H #include <string.h> #endif #endif #ifdef HAVE_GETOPT #ifdef HAVE_UNISTD_H #include <unistd.

/∗ Character removal pointer ∗/ static int ateof = FALSE. many are used as “hidden arguments” to functions in order to simplify calling sequences. Global variables 5 ≡ typedef unsigned char byte. /∗ Input file ∗/ static FILE ∗fo . /∗ Encode / decode table ∗/ static int linelength = 0. .§5 BASE64 PROGRAM GLOBAL CONTEXT 3 5. /∗ Bytes left in I/O buffer ∗/ static int iocp = MAXINLINE. /∗ I/O buffer ∗/ static int iolen = 0. /∗ Output file ∗/ static byte iobuf [MAXINLINE]. /∗ EOF encountered ∗/ static byte dtable [256]. /∗ Check decode input for errors ? ∗/ This code is used in section 2. /∗ Byte type ∗/ static FILE ∗fi . /∗ Length of encoded output line ∗/ static char eol [ ] = /∗ End of line sequence ∗/ #ifdef FORCE_BINARY_IO "\n" #else "\r\n" #endif . static int errcheck = TRUE. These variables are global to all procedures.

iocp = 0. if (ateof ) { return FALSE. } iolen = l. static int inbuf (void) { int l. if (c ≡ EOF ∨ (c > ’ ’)) { return c. This procedure uses inchar to read the input stream and returns EOF when the end of the input file is reached. } 9. Procedure inbuf fills the input buffer with data from the input stream fi . static int insig (void) { int c. static int inchar (void) { if (iocp ≥ iolen ) { if (¬inbuf ( )) { return EOF. At end of line. INPUT/OUTPUT FUNCTIONS BASE64 §6 Input/output functions. if (l ≤ 0) { if (ferror (fi )) { exit (1). Procedure inchar returns the next character from the input line. fi ). 7. return FALSE. return TRUE. } } return iobuf [iocp ++ ]. } ateof = TRUE. Procedure insig returns the next significant input character.4 6. while (TRUE) { c = inchar ( ). it calls inbuf to read the next line. 1. returning EOF at end of file. ignoring white space and control characters. } /∗ Read input buffer ∗/ 8. } l = fread (iobuf . MAXINLINE. } } } .

fo ) ≡ EOF) { exit (1). inserting line breaks as required so that no line exceeds LINELEN characters. } linelength ++ . fo ) ≡ EOF) { exit (1). Procedure ochar outputs an encoded character. } if (putc (((byte) c). static void ochar (int c) { if (linelength ≥ LINELEN) { if (fputs (eol .§10 BASE64 INPUT/OUTPUT FUNCTIONS 5 10. } . } linelength = 0.

ogroup [2] = dtable [((igroup [1] & # F) 2) | (igroup [2] 6)]. if (c ≡ EOF) { hiteof = TRUE. } igroup [n] = (byte) c. initialise encoding table 12 . i ++ ) { ochar (ogroup [i]). } } } if (fputs (eol . /∗ Replace characters in output stream with ”=” pad characters if fewer than three characters were read from the end of the input stream.6 ENCODING BASE64 §11 11. i < 4. ogroup [4]. n. } } . hiteof = FALSE. writing the output to fo . } } for (i = 0. } if (n > 0) { ogroup [0] = dtable [igroup [0] 2]. while (¬hiteof ) { byte igroup [3]. Procedure encode encodes the binary file opened as fi into base64. break. ∗/ if (n < 3) { ogroup [3] = ’=’. int c. if (n < 2) { ogroup [2] = ’=’. ogroup [3] = dtable [igroup [2] & # 3F]. n ++ ) { c = inchar ( ). static void encode (void) { int i. ogroup [1] = dtable [((igroup [0] & 3) 4) | (igroup [1] 4)]. igroup [0] = igroup [1] = igroup [2] = 0. n < 3. for (n = 0. fo ) ≡ EOF) { exit (1). Encoding.

i < 9. i ++ ) { dtable [i + 18] = ’S’ + i. initialise encoding table 12 ≡ for (i = 0. dtable [63] = ’/’. i ++ ) { dtable [52 + i] = ’0’ + i. } for (i = 0. } for (i = 0. i < 8.F. Procedure initialise encoding table fills the binary encoding table with the characters the 6 bit values are mapped into. . dtable [i + 9] = ’J’ + i.§12 BASE64 ENCODING 7 12. This code is used in section 11. i ++ ) { dtable [i] = ’A’ + i. i < 10. dtable [26 + i + 9] = ’j’ + i. the initialisation must be split to accommodate the EBCDIC consecutive letters: A–I J–R S–Z a–i j–r s–z This code works on ASCII as well as EBCDIC systems. the latter thanks to Ch. } dtable [62] = ’+’. In EBCDIC systems character codes for letters are not consecutive. The curious and disparate sequences used to fill this table permit this code to work both on ASCII and EBCDIC systems. dtable [26 + i] = ’a’ + i. dtable [26 + i + 18] = ’s’ + i.

∗/ i −− . if (fwrite (o. Initialise decode table 14 .8 DECODING BASE64 §13 13. } a[i] = (byte) c. } if (dtable [c] & # 80) { if (errcheck ) { fprintf (stderr . exit (1). b[4]. i.\n"). o[2] = (b[2] 6) | b[3]. } return. i = a[2] ≡ ’=’ ? 1 : (a[3] ≡ ’=’ ? 2 : 3). "Illegal character ’%c’ in input file. } } } . i ++ ) { int c = insig ( ). Procedure decode decodes a base64 encoded stream from fi and emits the binary result on fo .\n". "Input file incomplete. exit (1). } /∗ Ignoring errors: discard invalid character. c). o[1] = (b[1] 4) | (b[2] 2). static void decode (void) { int i. b[i] = (byte) dtable [c]. if (c ≡ EOF) { if (errcheck ∧ (i > 0)) { fprintf (stderr . while (TRUE) { byte a[4]. i < 4. fo ) ≡ EOF) { exit (1). Decoding. continue. o[3]. } o[0] = (b[0] 2) | (b[1] 4). 1. } if (i < 3) { return. for (i = 0.

i ≤ ’R’. i ++ ) { dtable [i] = 52 + (i − ’0’). } for (i = ’0’. i ++ ) { dtable [i] = 18 + (i − ’S’). dtable [’/’] = 63.) In EBCDIC systems character codes for letters are not consecutive.§14 BASE64 DECODING 9 14. dtable [’=’] = 0. } for (i = ’S’. } for (i = ’s’. the initialisation must be split to accommodate the EBCDIC consecutive letters: A–I J–R S–Z a–i j–r s–z This code works on ASCII as well as EBCDIC systems. . i < 255. i ++ ) { dtable [i] = 0 + (i − ’A’). The table is built in this rather curious way in order to be properly initialised for both ASCII-based systems and those using EBCDIC. (EBCDIC fixes courtesy of Ch. i ≤ ’z’. i ++ ) { dtable [i] = 26 + (i − ’a’). } for (i = ’A’. i ≤ ’I’. i ≤ ’9’. } for (i = ’j’. This code is used in section 13. i ++ ) { dtable [i] = 35 + (i − ’j’). i ≤ ’Z’. i ++ ) { dtable [i] = 44 + (i − ’s’). Initialise decode table 14 ≡ for (i = 0. where the letters are not contiguous. } for (i = ’a’. i ≤ ’i’. } for (i = ’J’.F. } dtable [’+’] = 62. i ++ ) { dtable [i] = # 80. i ≤ ’r’. Procedure initialise decode table creates the lookup table used to map base64 characters into their binary values from 0 to 63. i ++ ) { dtable [i] = 9 + (i − ’J’).

printf ("\n"). 16. −−help Print this message\n"). PRODUCT). UTILITY FUNCTIONS BASE64 §15 Utility functions. −−noerrcheck Ignore errors when decoding\n"). printf (" −−version Print version number\n"). printf (" %s [−e / −d] [options] [infile] [outfile]\n". printf (" −d. printf (" −u. printf (" −−copyright Print copyright information\n"). printf ("\n"). } .ch/\n").10 15. Call:\n". −−decode Decode base64 encoded file\n"). PRODUCT).fourmilab. printf (" −e. static void usage (void) { printf ("%s −− Encode/decode file as base64. printf ("http://www. −−encode Encode file into base64\n"). Procedure usage prints how-to-call information. printf ("Options:\n"). printf ("by John Walker\n"). printf (" −n.

int main (int argc . } . char ∗argv [ ]) { extern char ∗optarg .§17 17. Process command-line arguments 19 . if (decoding ) { decode ( ). #endif char ∗cp . ∗/ fi = stdin . } return 0. decoding = FALSE. fo = stdout . Process command-line options 18 .F. it was moved from declaration to main function start. int f . #ifdef FORCE_BINARY_IO int in std = TRUE. /∗ 2000-12-20 Ch. /∗ Imported from getopt ∗/ extern int optind . Force binary I/O where required 20 . out std = TRUE. } else { encode ( ). BASE64 MAIN PROGRAM 11 Main program. opt . UNIX/390 C compiler (cc) does not allow initialisation of static variables with non static right-value during variable declaration.

12 MAIN PROGRAM BASE64 §18 18. Process command-line options 18 ≡ while ((opt = getopt (argc . case ’n’: /∗ -n Suppress error checking ∗/ errcheck = FALSE. This permits aggregation of options without arguments and both −darg and −d arg syntax.ch/webtools/base64\n"). return 0. case ’v’: /∗ –version ∗/ printf ("%s %s\n".\n"). case ’−’: /∗ – Extended options ∗/ switch (optarg [0]) { case ’c’: /∗ –copyright ∗/ printf ("This program is in the public domain. case ’h’: /∗ –help ∗/ usage ( ). break. case ’e’: /∗ -e Encode ∗/ decoding = FALSE. break. printf ("The latest version is always available\n"). break. case ’d’: /∗ –decode ∗/ decoding = TRUE. return 0. REVDATE). VERSION). printf ("Last revised: %s\n". return 0. argv . printf ("at http://www. We use getopt to process command line options. case ’u’: /∗ -u Print how-to-call information ∗/ case ’?’: usage ( ). break. break. } } } This code is used in section 17. PRODUCT. "denu−:")) = −1) { switch (opt ) { case ’d’: /∗ -d Decode ∗/ decoding = TRUE. case ’n’: /∗ –noerrcheck ∗/ errcheck = FALSE. case ’e’: /∗ -encode ∗/ decoding = FALSE. return 0.fourmilab. . break.

return 2. usage ( ). ’Twasn’t me who couldn’t conform to Unix CR/LF convention. optind < argc . default: fprintf (stderr . At this point the external variable optind in getopt contains the index of the first argument in the argv [ ] array. } #ifdef FORCE_BINARY_IO in std = FALSE. But it’s worse: if input or output is from standard input or output. * ∗/ case 0: if (strcmp (cp . etc.\n"). return 2.) the modes in these open statements will have to be made conditional based upon whether an encode or decode is being done. . break. break. } #ifdef FORCE_BINARY_IO out std = FALSE. "Cannot open output file %s\n". Macintosh. return 2.§19 BASE64 MAIN PROGRAM 13 19. which is generally system and compiler dependent. "−") = 0) { if ((fi = fopen (cp . cp ). Process command-line arguments 19 ≡ f = 0. so don’t ask me to write the code to work around Apple and Microsoft’s incompatible standards. for ( . switch (f ) { /∗ * Warning! On systems which distinguish text mode and binary I/O (MS-DOS. case 1: if (strcmp (cp . "Cannot open input file %s\n". optind ++ ) { cp = argv [optind ]. #endif } f ++ . #ifdef FORCE_BINARY_IO decoding ? "r" : "rb" #else "r" #endif )) ≡ Λ) { fprintf (stderr . cp ). #ifdef FORCE_BINARY_IO decoding ? "wb" : "w" #else "w" #endif )) ≡ Λ) { fprintf (stderr . "Too many file names specified. #endif } f ++ . "−") = 0) { if ((fo = fopen (cp . the mode will have to be changed on the fly. which will have to be specified earlier. This code is executed after getopt has completed parsing command line options.

we must place this stream. #endif } #endif This code is used in section 17. Force binary I/O where required 20 ≡ #ifdef FORCE_BINARY_IO if ((decoding ∧ out std ) ∨ ((¬decoding ) ∧ in std )) { #ifdef _WIN32 setmode ( fileno (decoding ? fo : fi ). . If you’re using a different version or compiler. The following code sets the already-open standard stream to binary mode on Microsoft Visual C 5. you’ll need to add equivalent code here. the Macintosh). 20. into binary mode (no EOL translation). O_BINARY). if the binary stream is the default of stdin/stdout. On WIN32. opened in text mode (translation of CR to CR/LF) by default.0 (Monkey C). If you port this code to other platforms which distinguish between text and binary file I/O (for example. you may need some other incantation to cancel the text translation spell.14 } } MAIN PROGRAM BASE64 §19 This code is used in section 17.

17. igroup : 11. 19. opt : 17. 8. insig : 9. 19. 5. a: 13. 11. 9. 18.§21 BASE64 INDEX 15 21. ochar : 10. l: 7. setmode : 20. 5. 13. putc : 10. 7. LINELEN: 2. 17. 18. 10. 19. main : 17. 19. 19. 17. 19. 20. getopt : 17. 10. fread : 7. 10. 10. optind : 17. 7. 7. FALSE: 2. strcmp : 19. HAVE_STRING_H: 3. _WIN32: 4. 19. 7. b: 13. f : 17. table : 14. 19. 12. initialise encoding table : 12. Underlined entries indicate where an identifier was declared. n: 11. ferror : 7. 18. c: 9. 20. stdin : 17. HAVE_UNISTD_H: 3. VERSION: 18. 20. fputs : 10. 13. 13. decode : 13. 9. 18. eol : 5. 13. fwrite : 13. 13. inbuf : 7. fi : 5. 8. 13. stdout : 17. fopen : 19. 13. 11. FORCE_BINARY_IO: 4. Single-character identifiers are not indexed. HAVE_STRINGS_H: 3. 18. errcheck : 5. argv : 17. iocp : 5. 11. 18. TRUE: 2. dtable : 5. 11. decoding : 17. 20. 20. encode : 11. fo : 5. 18. linelength : 5. 10. The following is a cross-reference table for base64. HAVE_GETOPT: 3. 19. PRODUCT: 16. 18. fileno : 20. 17. 11. 17. argc : 17. 7. 17. nor are reserved words. inchar : 8. 11. printf : 16. 18. cp : 17. ogroup : 11. fprintf : 13. 11. 18. 19. 18. O_BINARY: 20. 13. 18. 14. 14. stderr : 13. in std : 17. 7. 10. 11. 20. iolen : 5. 17. ateof : 5. 11. exit : 7. o: 13. 19. out std : 17. 19. 13. 9. 19. . REVDATE: 1. iobuf : 5. 13. 10. 13. 7. 7. 19. 11. 8. optarg : 17. 19. 10. EOF: 8. 5. 5. usage : 16. Index. hiteof : 11. 18. 11. 8. i: 11. 20. MAXINLINE: 2. 11. initialise : 14. byte: 5. 11.

Used in section 2. . Windows-specific include files 4 initialise encoding table 12 Used in section 11. Process command-line arguments 19 Used in section 17. Used in section 2. Process command-line options 18 Used in section 17.16 NAMES OF THE SECTIONS BASE64 Force binary I/O where required 20 Used in section 17. Global variables 5 Initialise decode table 14 Used in section 13. System include files 3 Used in section 2.

. . . . . . . . . . . . . . . . . . . . . . . . 17 Index . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1 Program global context . . . . . . . . . . . . 6 Encoding . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2 Input/output functions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .BASE64 Section Page Introduction . . . . . . . . . . . . . . . . . . . . . . . . . 13 Utility functions . . . . . . . . . . . . . . . . . . . . . . . . 15 Main program . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21 1 2 4 6 8 10 11 15 . 11 Decoding . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .