minor style cleanup
[citadel.git] / citadel / utils / base64.c
1 /*
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
5  *
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.
8  *
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.
13  */
14
15
16 #include <stdio.h>
17 #include <stdlib.h>
18 #include <ctype.h>
19 #include <string.h>
20
21 #define TRUE  1
22 #define FALSE 0
23
24 #define LINELEN 72                    /* Encoded line length (max 76) */
25
26 typedef unsigned char byte;           /* Byte type */
27
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 ? */
38
39 /*  INBUF  --  Fill input buffer with data  */
40
41 static int inbuf(void)
42 {
43     int l;
44
45     if (ateof) {
46         return FALSE;
47     }
48     l = fread(iobuf, 1, sizeof iobuf, fi);     /* Read input buffer */
49     if (l <= 0) {
50         if (ferror(fi)) {
51             exit(1);
52         }
53         ateof = TRUE;
54         return FALSE;
55     }
56     iolen = l;
57     iocp = 0;
58     return TRUE;
59 }
60
61 /*  INCHAR  --  Return next character from input  */
62
63 static int inchar(void)
64 {
65     if (iocp >= iolen) {
66        if (!inbuf()) {
67           return EOF;
68         }
69     }
70
71     return iobuf[iocp++];
72 }
73
74 /*  OCHAR  --  Output an encoded character, inserting line breaks
75                where required.  */
76
77 static void ochar(int c)
78 {
79     if (linelength >= LINELEN) {
80         if (fputs(eol, fo) == EOF) {
81             exit(1);
82         }
83         linelength = 0;
84     }
85     if (putc(((byte) c), fo) == EOF) {
86         exit(1);
87     }
88     linelength++;
89 }
90
91 /*  ENCODE  --  Encode binary file into base64.  */
92
93 static void encode(void)
94 {
95     int i, hiteof = FALSE;
96
97     /*  Fill dtable with character encodings.  */
98
99     for (i = 0; i < 26; i++) {
100         dtable[i] = 'A' + i;
101         dtable[26 + i] = 'a' + i;
102     }
103     for (i = 0; i < 10; i++) {
104         dtable[52 + i] = '0' + i;
105     }
106     dtable[62] = '+';
107     dtable[63] = '/';
108
109     while (!hiteof) {
110         byte igroup[3], ogroup[4];
111         int c, n;
112
113         igroup[0] = igroup[1] = igroup[2] = 0;
114         for (n = 0; n < 3; n++) {
115             c = inchar();
116             if (c == EOF) {
117                 hiteof = TRUE;
118                 break;
119             }
120             igroup[n] = (byte) c;
121         }
122         if (n > 0) {
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];
127
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. */
131
132             if (n < 3) {
133                 ogroup[3] = '=';
134                 if (n < 2) {
135                     ogroup[2] = '=';
136                 }
137             }
138             for (i = 0; i < 4; i++) {
139                 ochar(ogroup[i]);
140             }
141         }
142     }
143     if (fputs(eol, fo) == EOF) {
144         exit(1);
145     }
146 }
147
148 /*  INSIG  --  Return next significant input  */
149
150 static int insig(void)
151 {
152     int c;
153
154     /*CONSTANTCONDITION*/
155     while (TRUE) {
156         c = inchar();
157         if (c == EOF || (c > ' ')) {
158             return c;
159         }
160     }
161     /*NOTREACHED*/
162 }
163
164 /*  DECODE  --  Decode base64.  */
165
166 static void decode(void)
167 {
168     int i;
169
170     for (i = 0; i < 255; i++) {
171         dtable[i] = 0x80;
172     }
173     for (i = 'A'; i <= 'Z'; i++) {
174         dtable[i] = 0 + (i - 'A');
175     }
176     for (i = 'a'; i <= 'z'; i++) {
177         dtable[i] = 26 + (i - 'a');
178     }
179     for (i = '0'; i <= '9'; i++) {
180         dtable[i] = 52 + (i - '0');
181     }
182     dtable['+'] = 62;
183     dtable['/'] = 63;
184     dtable['='] = 0;
185
186     /*CONSTANTCONDITION*/
187     while (TRUE) {
188         byte a[4], b[4], o[3];
189
190         for (i = 0; i < 4; i++) {
191             int c = insig();
192
193             if (c == EOF) {
194                 if (errcheck && (i > 0)) {
195                     fprintf(stderr, "Input file incomplete.\n");
196                     exit(1);
197                 }
198                 return;
199             }
200             if (dtable[c] & 0x80) {
201                 if (errcheck) {
202                     fprintf(stderr, "Illegal character '%c' in input file.\n", c);
203                     exit(1);
204                 }
205                 /* Ignoring errors: discard invalid character. */
206                 i--;
207                 continue;
208             }
209             a[i] = (byte) c;
210             b[i] = (byte) dtable[c];
211         }
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) {
217             exit(1);
218         }
219         if (i < 3) {
220             return;
221         }
222     }
223 }
224
225 /*  USAGE  --  Print how-to-call information.  */
226
227 static void usage(char *pname)
228 {
229     fprintf(stderr, "%s  --  Encode/decode file as base64.  Call:\n", pname);
230     fprintf(stderr,
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");
241 }
242
243 /*  Main program  */
244
245 int main(int argc, char *argv[])
246 {
247     int i, f = 0, decoding = FALSE;
248     char *cp, opt;
249
250     fi = stdin;
251     fo = stdout;
252
253     for (i = 1; i < argc; i++) {
254         cp = argv[i];
255         if (*cp == '-') {
256             opt = *(++cp);
257             if (islower(opt)) {
258                 opt = toupper(opt);
259             }
260             switch (opt) {
261
262                 case 'D':             /* -D  Decode */
263                     decoding = TRUE;
264                     break;
265
266                 case 'E':             /* -E  Encode */
267                     decoding = FALSE;
268                     break;
269
270                 case 'N':             /* -N  Suppress error checking */
271                     errcheck = FALSE;
272                     break;
273
274                 case 'U':             /* -U  Print how-to-call information */
275                 case '?':
276                     usage(argv[0]);
277                     return 0;
278            }
279         } else {
280             switch (f) {
281
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. **/
293
294                 case 0:
295                     if (strcmp(cp, "-") != 0) {
296                         if ((fi = fopen(cp, "r")) == NULL) {
297                             fprintf(stderr, "Cannot open input file %s\n", cp);
298                             return 2;
299                         }
300                     }
301                     f++;
302                     break;
303
304                 case 1:
305                     if (strcmp(cp, "-") != 0) {
306                         if ((fo = fopen(cp, "w")) == NULL) {
307                             fprintf(stderr, "Cannot open output file %s\n", cp);
308                             return 2;
309                         }
310                     }
311                     f++;
312                     break;
313
314                 default:
315                     fprintf(stderr, "Too many file names specified.\n");
316                     usage(argv[0]);
317                     return 2;
318             }
319        }
320     }
321
322     if (decoding) {
323        decode();
324     } else {
325        encode();
326     }
327     return 0;
328 }