4 * Encode or decode file as MIME base64 (RFC 1341)
5 * Public domain by John Walker, August 11 1997
6 * Modified slightly for the Citadel system, June 1999
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 3 of the License, or
11 * (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
32 #define LINELEN 72 /* Encoded line length (max 76) */
34 typedef unsigned char byte; /* Byte type */
36 FILE *fi; /* Input file */
37 FILE *fo; /* Output file */
38 static byte iobuf[256]; /* I/O buffer */
39 static int iolen = 0; /* Bytes left in I/O buffer */
40 static int iocp = 256; /* Character removal pointer */
41 static int ateof = FALSE; /* EOF encountered */
42 static byte dtable[256]; /* Encode / decode table */
43 static int linelength = 0; /* Length of encoded output line */
44 static char eol[] = "\r\n"; /* End of line sequence */
45 static int errcheck = TRUE; /* Check decode input for errors ? */
47 /* INBUF -- Fill input buffer with data */
49 static int inbuf(void)
56 l = fread(iobuf, 1, sizeof iobuf, fi); /* Read input buffer */
69 /* INCHAR -- Return next character from input */
71 static int inchar(void)
82 /* OCHAR -- Output an encoded character, inserting line breaks
85 static void ochar(int c)
87 if (linelength >= LINELEN) {
88 if (fputs(eol, fo) == EOF) {
93 if (putc(((byte) c), fo) == EOF) {
99 /* ENCODE -- Encode binary file into base64. */
101 static void encode(void)
103 int i, hiteof = FALSE;
105 /* Fill dtable with character encodings. */
107 for (i = 0; i < 26; i++) {
109 dtable[26 + i] = 'a' + i;
111 for (i = 0; i < 10; i++) {
112 dtable[52 + i] = '0' + i;
118 byte igroup[3], ogroup[4];
121 igroup[0] = igroup[1] = igroup[2] = 0;
122 for (n = 0; n < 3; n++) {
128 igroup[n] = (byte) c;
131 ogroup[0] = dtable[igroup[0] >> 2];
132 ogroup[1] = dtable[((igroup[0] & 3) << 4) | (igroup[1] >> 4)];
133 ogroup[2] = dtable[((igroup[1] & 0xF) << 2) | (igroup[2] >> 6)];
134 ogroup[3] = dtable[igroup[2] & 0x3F];
136 /* Replace characters in output stream with "=" pad
137 characters if fewer than three characters were
138 read from the end of the input stream. */
146 for (i = 0; i < 4; i++) {
151 if (fputs(eol, fo) == EOF) {
156 /* INSIG -- Return next significant input */
158 static int insig(void)
162 /*CONSTANTCONDITION*/
165 if (c == EOF || (c > ' ')) {
172 /* DECODE -- Decode base64. */
174 static void decode(void)
178 for (i = 0; i < 255; i++) {
181 for (i = 'A'; i <= 'Z'; i++) {
182 dtable[i] = 0 + (i - 'A');
184 for (i = 'a'; i <= 'z'; i++) {
185 dtable[i] = 26 + (i - 'a');
187 for (i = '0'; i <= '9'; i++) {
188 dtable[i] = 52 + (i - '0');
194 /*CONSTANTCONDITION*/
196 byte a[4], b[4], o[3];
198 for (i = 0; i < 4; i++) {
202 if (errcheck && (i > 0)) {
203 fprintf(stderr, "Input file incomplete.\n");
208 if (dtable[c] & 0x80) {
210 fprintf(stderr, "Illegal character '%c' in input file.\n", c);
213 /* Ignoring errors: discard invalid character. */
218 b[i] = (byte) dtable[c];
220 o[0] = (b[0] << 2) | (b[1] >> 4);
221 o[1] = (b[1] << 4) | (b[2] >> 2);
222 o[2] = (b[2] << 6) | b[3];
223 i = a[2] == '=' ? 1 : (a[3] == '=' ? 2 : 3);
224 if (fwrite(o, i, 1, fo) == EOF) {
233 /* USAGE -- Print how-to-call information. */
235 static void usage(char *pname)
237 fprintf(stderr, "%s -- Encode/decode file as base64. Call:\n", pname);
239 " %s [-e[ncode] / -d[ecode]] [-n] [infile] [outfile]\n", pname);
240 fprintf(stderr, "\n");
241 fprintf(stderr, "Options:\n");
242 fprintf(stderr, " -D Decode base64 encoded file\n");
243 fprintf(stderr, " -E Encode file into base64\n");
244 fprintf(stderr, " -N Ignore errors when decoding\n");
245 fprintf(stderr, " -U Print this message\n");
246 fprintf(stderr, "\n");
247 fprintf(stderr, "by John Walker\n");
248 fprintf(stderr, " WWW: http://www.fourmilab.ch/\n");
253 int main(int argc, char *argv[])
255 int i, f = 0, decoding = FALSE;
261 for (i = 1; i < argc; i++) {
270 case 'D': /* -D Decode */
274 case 'E': /* -E Encode */
278 case 'N': /* -N Suppress error checking */
282 case 'U': /* -U Print how-to-call information */
290 /** Warning! On systems which distinguish text mode and
291 binary I/O (MS-DOS, Macintosh, etc.) the modes in these
292 open statements will have to be made conditional based
293 upon whether an encode or decode is being done, which
294 will have to be specified earlier. But it's worse: if
295 input or output is from standard input or output, the
296 mode will have to be changed on the fly, which is
297 generally system and compiler dependent. 'Twasn't me
298 who couldn't conform to Unix CR/LF convention, so
299 don't ask me to write the code to work around
300 Apple and Microsoft's incompatible standards. **/
303 if (strcmp(cp, "-") != 0) {
304 if ((fi = fopen(cp, "r")) == NULL) {
305 fprintf(stderr, "Cannot open input file %s\n", cp);
313 if (strcmp(cp, "-") != 0) {
314 if ((fo = fopen(cp, "w")) == NULL) {
315 fprintf(stderr, "Cannot open output file %s\n", cp);
323 fprintf(stderr, "Too many file names specified.\n");