]> code.citadel.org Git - citadel.git/blob - citadel/base64.c
Added base64.c to the distribution
[citadel.git] / citadel / base64.c
1 /*
2
3            Encode or decode file as MIME base64 (RFC 1341)
4
5                             by John Walker
6                        http://www.fourmilab.ch/
7
8                 This program is in the public domain.
9
10 */
11
12 #define REVDATE "11th August 1997"
13
14 #include <stdio.h>
15 #include <stdlib.h>
16 #include <ctype.h>
17 #include <string.h>
18
19 #define TRUE  1
20 #define FALSE 0
21
22 #define LINELEN 72                    /* Encoded line length (max 76) */
23
24 typedef unsigned char byte;           /* Byte type */
25
26 static FILE *fi = stdin;              /* Input file */
27 static FILE *fo = stdout;             /* Output file */
28 static byte iobuf[256];               /* I/O buffer */
29 static int iolen = 0;                 /* Bytes left in I/O buffer */
30 static int iocp = 256;                /* Character removal pointer */
31 static int ateof = FALSE;             /* EOF encountered */
32 static byte dtable[256];              /* Encode / decode table */
33 static int linelength = 0;            /* Length of encoded output line */
34 static char eol[] = "\r\n";           /* End of line sequence */
35 static int errcheck = TRUE;           /* Check decode input for errors ? */
36
37 /*  INBUF  --  Fill input buffer with data  */
38
39 static int inbuf(void)
40 {
41     int l;
42
43     if (ateof) {
44         return FALSE;
45     }
46     l = fread(iobuf, 1, 256, fi);     /* Read input buffer */
47     if (l <= 0) {
48         if (ferror(fi)) {
49             exit(1);
50         }
51         ateof = TRUE;
52         return FALSE;
53     }
54     iolen = l;
55     iocp = 0;
56     return TRUE;
57 }
58
59 /*  INCHAR  --  Return next character from input  */
60
61 static int inchar(void)
62 {
63     if (iocp >= iolen) {
64        if (!inbuf()) {
65           return EOF;
66         }
67     }
68
69     return iobuf[iocp++];
70 }
71
72 /*  OCHAR  --  Output an encoded character, inserting line breaks
73                where required.  */
74
75 static void ochar(int c)
76 {
77     if (linelength >= LINELEN) {
78         if (fputs(eol, fo) == EOF) {
79             exit(1);
80         }
81         linelength = 0;
82     }
83     if (putc(((byte) c), fo) == EOF) {
84         exit(1);
85     }
86     linelength++;
87 }
88
89 /*  ENCODE  --  Encode binary file into base64.  */
90
91 static void encode(void)
92 {
93     int i, hiteof = FALSE;
94
95     /*  Fill dtable with character encodings.  */
96
97     for (i = 0; i < 26; i++) {
98         dtable[i] = 'A' + i;
99         dtable[26 + i] = 'a' + i;
100     }
101     for (i = 0; i < 10; i++) {
102         dtable[52 + i] = '0' + i;
103     }
104     dtable[62] = '+';
105     dtable[63] = '/';
106
107     while (!hiteof) {
108         byte igroup[3], ogroup[4];
109         int c, n;
110
111         igroup[0] = igroup[1] = igroup[2] = 0;
112         for (n = 0; n < 3; n++) {
113             c = inchar();
114             if (c == EOF) {
115                 hiteof = TRUE;
116                 break;
117             }
118             igroup[n] = (byte) c;
119         }
120         if (n > 0) {
121             ogroup[0] = dtable[igroup[0] >> 2];
122             ogroup[1] = dtable[((igroup[0] & 3) << 4) | (igroup[1] >> 4)];
123             ogroup[2] = dtable[((igroup[1] & 0xF) << 2) | (igroup[2] >> 6)];
124             ogroup[3] = dtable[igroup[2] & 0x3F];
125
126             /* Replace characters in output stream with "=" pad
127                characters if fewer than three characters were
128                read from the end of the input stream. */
129
130             if (n < 3) {
131                 ogroup[3] = '=';
132                 if (n < 2) {
133                     ogroup[2] = '=';
134                 }
135             }
136             for (i = 0; i < 4; i++) {
137                 ochar(ogroup[i]);
138             }
139         }
140     }
141     if (fputs(eol, fo) == EOF) {
142         exit(1);
143     }
144 }
145
146 /*  INSIG  --  Return next significant input  */
147
148 static int insig(void)
149 {
150     int c;
151
152     /*CONSTANTCONDITION*/
153     while (TRUE) {
154         c = inchar();
155         if (c == EOF || (c > ' ')) {
156             return c;
157         }
158     }
159     /*NOTREACHED*/
160 }
161
162 /*  DECODE  --  Decode base64.  */
163
164 static void decode(void)
165 {
166     int i;
167
168     for (i = 0; i < 255; i++) {
169         dtable[i] = 0x80;
170     }
171     for (i = 'A'; i <= 'Z'; i++) {
172         dtable[i] = 0 + (i - 'A');
173     }
174     for (i = 'a'; i <= 'z'; i++) {
175         dtable[i] = 26 + (i - 'a');
176     }
177     for (i = '0'; i <= '9'; i++) {
178         dtable[i] = 52 + (i - '0');
179     }
180     dtable['+'] = 62;
181     dtable['/'] = 63;
182     dtable['='] = 0;
183
184     /*CONSTANTCONDITION*/
185     while (TRUE) {
186         byte a[4], b[4], o[3];
187
188         for (i = 0; i < 4; i++) {
189             int c = insig();
190
191             if (c == EOF) {
192                 if (errcheck && (i > 0)) {
193                     fprintf(stderr, "Input file incomplete.\n");
194                     exit(1);
195                 }
196                 return;
197             }
198             if (dtable[c] & 0x80) {
199                 if (errcheck) {
200                     fprintf(stderr, "Illegal character '%c' in input file.\n", c);
201                     exit(1);
202                 }
203                 /* Ignoring errors: discard invalid character. */
204                 i--;
205                 continue;
206             }
207             a[i] = (byte) c;
208             b[i] = (byte) dtable[c];
209         }
210         o[0] = (b[0] << 2) | (b[1] >> 4);
211         o[1] = (b[1] << 4) | (b[2] >> 2);
212         o[2] = (b[2] << 6) | b[3];
213         i = a[2] == '=' ? 1 : (a[3] == '=' ? 2 : 3);
214         if (fwrite(o, i, 1, fo) == EOF) {
215             exit(1);
216         }
217         if (i < 3) {
218             return;
219         }
220     }
221 }
222
223 /*  USAGE  --  Print how-to-call information.  */
224
225 static void usage(char *pname)
226 {
227     fprintf(stderr, "%s  --  Encode/decode file as base64.  Call:\n", pname);
228     fprintf(stderr,
229     "            %s [-e[ncode] / -d[ecode]] [-n] [infile] [outfile]\n", pname);
230     fprintf(stderr, "\n");
231     fprintf(stderr, "Options:\n");
232     fprintf(stderr, "           -D         Decode base64 encoded file\n");
233     fprintf(stderr, "           -E         Encode file into base64\n");
234     fprintf(stderr, "           -N         Ignore errors when decoding\n");
235     fprintf(stderr, "           -U         Print this message\n");
236     fprintf(stderr, "\n");
237     fprintf(stderr, "by John Walker\n");
238     fprintf(stderr, "   WWW:    http://www.fourmilab.ch/\n");
239 }
240
241 /*  Main program  */
242
243 int main(int argc, char *argv[])
244 {
245     int i, f = 0, decoding = FALSE;
246     char *cp, opt;
247
248     for (i = 1; i < argc; i++) {
249         cp = argv[i];
250         if (*cp == '-') {
251             opt = *(++cp);
252             if (islower(opt)) {
253                 opt = toupper(opt);
254             }
255             switch (opt) {
256
257                 case 'D':             /* -D  Decode */
258                     decoding = TRUE;
259                     break;
260
261                 case 'E':             /* -E  Encode */
262                     decoding = FALSE;
263                     break;
264
265                 case 'N':             /* -N  Suppress error checking */
266                     errcheck = FALSE;
267                     break;
268
269                 case 'U':             /* -U  Print how-to-call information */
270                 case '?':
271                     usage(argv[0]);
272                     return 0;
273            }
274         } else {
275             switch (f) {
276
277                 /** Warning!  On systems which distinguish text mode and
278                     binary I/O (MS-DOS, Macintosh, etc.) the modes in these
279                     open statements will have to be made conditional based
280                     upon whether an encode or decode is being done, which
281                     will have to be specified earlier.  But it's worse: if
282                     input or output is from standard input or output, the 
283                     mode will have to be changed on the fly, which is
284                     generally system and compiler dependent.  'Twasn't me
285                     who couldn't conform to Unix CR/LF convention, so 
286                     don't ask me to write the code to work around
287                     Apple and Microsoft's incompatible standards. **/
288
289                 case 0:
290                     if (strcmp(cp, "-") != 0) {
291                         if ((fi = fopen(cp, "r")) == NULL) {
292                             fprintf(stderr, "Cannot open input file %s\n", cp);
293                             return 2;
294                         }
295                     }
296                     f++;
297                     break;
298
299                 case 1:
300                     if (strcmp(cp, "-") != 0) {
301                         if ((fo = fopen(cp, "w")) == NULL) {
302                             fprintf(stderr, "Cannot open output file %s\n", cp);
303                             return 2;
304                         }
305                     }
306                     f++;
307                     break;
308
309                 default:
310                     fprintf(stderr, "Too many file names specified.\n");
311                     usage(argv[0]);
312                     return 2;
313             }
314        }
315     }
316
317     if (decoding) {
318        decode();
319     } else {
320        encode();
321     }
322     return 0;
323 }