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 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.
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.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
30 #define LINELEN 72 /* Encoded line length (max 76) */
32 typedef unsigned char byte; /* Byte type */
34 FILE *fi; /* Input file */
35 FILE *fo; /* Output file */
36 static byte iobuf[256]; /* I/O buffer */
37 static int iolen = 0; /* Bytes left in I/O buffer */
38 static int iocp = 256; /* Character removal pointer */
39 static int ateof = FALSE; /* EOF encountered */
40 static byte dtable[256]; /* Encode / decode table */
41 static int linelength = 0; /* Length of encoded output line */
42 static char eol[] = "\r\n"; /* End of line sequence */
43 static int errcheck = TRUE; /* Check decode input for errors ? */
45 /* INBUF -- Fill input buffer with data */
47 static int inbuf(void)
54 l = fread(iobuf, 1, sizeof iobuf, fi); /* Read input buffer */
67 /* INCHAR -- Return next character from input */
69 static int inchar(void)
80 /* OCHAR -- Output an encoded character, inserting line breaks
83 static void ochar(int c)
85 if (linelength >= LINELEN) {
86 if (fputs(eol, fo) == EOF) {
91 if (putc(((byte) c), fo) == EOF) {
97 /* ENCODE -- Encode binary file into base64. */
99 static void encode(void)
101 int i, hiteof = FALSE;
103 /* Fill dtable with character encodings. */
105 for (i = 0; i < 26; i++) {
107 dtable[26 + i] = 'a' + i;
109 for (i = 0; i < 10; i++) {
110 dtable[52 + i] = '0' + i;
116 byte igroup[3], ogroup[4];
119 igroup[0] = igroup[1] = igroup[2] = 0;
120 for (n = 0; n < 3; n++) {
126 igroup[n] = (byte) c;
129 ogroup[0] = dtable[igroup[0] >> 2];
130 ogroup[1] = dtable[((igroup[0] & 3) << 4) | (igroup[1] >> 4)];
131 ogroup[2] = dtable[((igroup[1] & 0xF) << 2) | (igroup[2] >> 6)];
132 ogroup[3] = dtable[igroup[2] & 0x3F];
134 /* Replace characters in output stream with "=" pad
135 characters if fewer than three characters were
136 read from the end of the input stream. */
144 for (i = 0; i < 4; i++) {
149 if (fputs(eol, fo) == EOF) {
154 /* INSIG -- Return next significant input */
156 static int insig(void)
160 /*CONSTANTCONDITION*/
163 if (c == EOF || (c > ' ')) {
170 /* DECODE -- Decode base64. */
172 static void decode(void)
176 for (i = 0; i < 255; i++) {
179 for (i = 'A'; i <= 'Z'; i++) {
180 dtable[i] = 0 + (i - 'A');
182 for (i = 'a'; i <= 'z'; i++) {
183 dtable[i] = 26 + (i - 'a');
185 for (i = '0'; i <= '9'; i++) {
186 dtable[i] = 52 + (i - '0');
192 /*CONSTANTCONDITION*/
194 byte a[4], b[4], o[3];
196 for (i = 0; i < 4; i++) {
200 if (errcheck && (i > 0)) {
201 fprintf(stderr, "Input file incomplete.\n");
206 if (dtable[c] & 0x80) {
208 fprintf(stderr, "Illegal character '%c' in input file.\n", c);
211 /* Ignoring errors: discard invalid character. */
216 b[i] = (byte) dtable[c];
218 o[0] = (b[0] << 2) | (b[1] >> 4);
219 o[1] = (b[1] << 4) | (b[2] >> 2);
220 o[2] = (b[2] << 6) | b[3];
221 i = a[2] == '=' ? 1 : (a[3] == '=' ? 2 : 3);
222 if (fwrite(o, i, 1, fo) == EOF) {
231 /* USAGE -- Print how-to-call information. */
233 static void usage(char *pname)
235 fprintf(stderr, "%s -- Encode/decode file as base64. Call:\n", pname);
237 " %s [-e[ncode] / -d[ecode]] [-n] [infile] [outfile]\n", pname);
238 fprintf(stderr, "\n");
239 fprintf(stderr, "Options:\n");
240 fprintf(stderr, " -D Decode base64 encoded file\n");
241 fprintf(stderr, " -E Encode file into base64\n");
242 fprintf(stderr, " -N Ignore errors when decoding\n");
243 fprintf(stderr, " -U Print this message\n");
244 fprintf(stderr, "\n");
245 fprintf(stderr, "by John Walker\n");
246 fprintf(stderr, " WWW: http://www.fourmilab.ch/\n");
251 int main(int argc, char *argv[])
253 int i, f = 0, decoding = FALSE;
259 for (i = 1; i < argc; i++) {
268 case 'D': /* -D Decode */
272 case 'E': /* -E Encode */
276 case 'N': /* -N Suppress error checking */
280 case 'U': /* -U Print how-to-call information */
288 /** Warning! On systems which distinguish text mode and
289 binary I/O (MS-DOS, Macintosh, etc.) the modes in these
290 open statements will have to be made conditional based
291 upon whether an encode or decode is being done, which
292 will have to be specified earlier. But it's worse: if
293 input or output is from standard input or output, the
294 mode will have to be changed on the fly, which is
295 generally system and compiler dependent. 'Twasn't me
296 who couldn't conform to Unix CR/LF convention, so
297 don't ask me to write the code to work around
298 Apple and Microsoft's incompatible standards. **/
301 if (strcmp(cp, "-") != 0) {
302 if ((fi = fopen(cp, "r")) == NULL) {
303 fprintf(stderr, "Cannot open input file %s\n", cp);
311 if (strcmp(cp, "-") != 0) {
312 if ((fo = fopen(cp, "w")) == NULL) {
313 fprintf(stderr, "Cannot open output file %s\n", cp);
321 fprintf(stderr, "Too many file names specified.\n");