2 * Encode or decode file as MIME base64 (RFC 1341)
3 * Public domain by John Walker, August 11 1997
4 * Modified slightly for the Citadel system, June 1999
6 * This program is open source software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License version 3.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
24 #define LINELEN 72 /* Encoded line length (max 76) */
26 typedef unsigned char byte; /* Byte type */
28 FILE *fi; /* Input file */
29 FILE *fo; /* Output file */
30 static byte iobuf[256]; /* I/O buffer */
31 static int iolen = 0; /* Bytes left in I/O buffer */
32 static int iocp = 256; /* Character removal pointer */
33 static int ateof = FALSE; /* EOF encountered */
34 static byte dtable[256]; /* Encode / decode table */
35 static int linelength = 0; /* Length of encoded output line */
36 static char eol[] = "\r\n"; /* End of line sequence */
37 static int errcheck = TRUE; /* Check decode input for errors ? */
39 /* INBUF -- Fill input buffer with data */
41 static int inbuf(void)
48 l = fread(iobuf, 1, sizeof iobuf, fi); /* Read input buffer */
61 /* INCHAR -- Return next character from input */
63 static int inchar(void)
74 /* OCHAR -- Output an encoded character, inserting line breaks
77 static void ochar(int c)
79 if (linelength >= LINELEN) {
80 if (fputs(eol, fo) == EOF) {
85 if (putc(((byte) c), fo) == EOF) {
91 /* ENCODE -- Encode binary file into base64. */
93 static void encode(void)
95 int i, hiteof = FALSE;
97 /* Fill dtable with character encodings. */
99 for (i = 0; i < 26; i++) {
101 dtable[26 + i] = 'a' + i;
103 for (i = 0; i < 10; i++) {
104 dtable[52 + i] = '0' + i;
110 byte igroup[3], ogroup[4];
113 igroup[0] = igroup[1] = igroup[2] = 0;
114 for (n = 0; n < 3; n++) {
120 igroup[n] = (byte) c;
123 ogroup[0] = dtable[igroup[0] >> 2];
124 ogroup[1] = dtable[((igroup[0] & 3) << 4) | (igroup[1] >> 4)];
125 ogroup[2] = dtable[((igroup[1] & 0xF) << 2) | (igroup[2] >> 6)];
126 ogroup[3] = dtable[igroup[2] & 0x3F];
128 /* Replace characters in output stream with "=" pad
129 characters if fewer than three characters were
130 read from the end of the input stream. */
138 for (i = 0; i < 4; i++) {
143 if (fputs(eol, fo) == EOF) {
148 /* INSIG -- Return next significant input */
150 static int insig(void)
154 /*CONSTANTCONDITION*/
157 if (c == EOF || (c > ' ')) {
164 /* DECODE -- Decode base64. */
166 static void decode(void)
170 for (i = 0; i < 255; i++) {
173 for (i = 'A'; i <= 'Z'; i++) {
174 dtable[i] = 0 + (i - 'A');
176 for (i = 'a'; i <= 'z'; i++) {
177 dtable[i] = 26 + (i - 'a');
179 for (i = '0'; i <= '9'; i++) {
180 dtable[i] = 52 + (i - '0');
186 /*CONSTANTCONDITION*/
188 byte a[4], b[4], o[3];
190 for (i = 0; i < 4; i++) {
194 if (errcheck && (i > 0)) {
195 fprintf(stderr, "Input file incomplete.\n");
200 if (dtable[c] & 0x80) {
202 fprintf(stderr, "Illegal character '%c' in input file.\n", c);
205 /* Ignoring errors: discard invalid character. */
210 b[i] = (byte) dtable[c];
212 o[0] = (b[0] << 2) | (b[1] >> 4);
213 o[1] = (b[1] << 4) | (b[2] >> 2);
214 o[2] = (b[2] << 6) | b[3];
215 i = a[2] == '=' ? 1 : (a[3] == '=' ? 2 : 3);
216 if (fwrite(o, i, 1, fo) == EOF) {
225 /* USAGE -- Print how-to-call information. */
227 static void usage(char *pname)
229 fprintf(stderr, "%s -- Encode/decode file as base64. Call:\n", pname);
231 " %s [-e[ncode] / -d[ecode]] [-n] [infile] [outfile]\n", pname);
232 fprintf(stderr, "\n");
233 fprintf(stderr, "Options:\n");
234 fprintf(stderr, " -D Decode base64 encoded file\n");
235 fprintf(stderr, " -E Encode file into base64\n");
236 fprintf(stderr, " -N Ignore errors when decoding\n");
237 fprintf(stderr, " -U Print this message\n");
238 fprintf(stderr, "\n");
239 fprintf(stderr, "by John Walker\n");
240 fprintf(stderr, " WWW: http://www.fourmilab.ch/\n");
245 int main(int argc, char *argv[])
247 int i, f = 0, decoding = FALSE;
253 for (i = 1; i < argc; i++) {
262 case 'D': /* -D Decode */
266 case 'E': /* -E Encode */
270 case 'N': /* -N Suppress error checking */
274 case 'U': /* -U Print how-to-call information */
282 /** Warning! On systems which distinguish text mode and
283 binary I/O (MS-DOS, Macintosh, etc.) the modes in these
284 open statements will have to be made conditional based
285 upon whether an encode or decode is being done, which
286 will have to be specified earlier. But it's worse: if
287 input or output is from standard input or output, the
288 mode will have to be changed on the fly, which is
289 generally system and compiler dependent. 'Twasn't me
290 who couldn't conform to Unix CR/LF convention, so
291 don't ask me to write the code to work around
292 Apple and Microsoft's incompatible standards. **/
295 if (strcmp(cp, "-") != 0) {
296 if ((fi = fopen(cp, "r")) == NULL) {
297 fprintf(stderr, "Cannot open input file %s\n", cp);
305 if (strcmp(cp, "-") != 0) {
306 if ((fo = fopen(cp, "w")) == NULL) {
307 fprintf(stderr, "Cannot open output file %s\n", cp);
315 fprintf(stderr, "Too many file names specified.\n");