* When translating the input from a REGI command into a vCard, set the telephone...
[citadel.git] / citadel / base64.c
blobc9717d145bb788d9456981c25160f23364311835
1 /*
2 * $Id$
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 */
11 #include <stdio.h>
12 #include <stdlib.h>
13 #include <ctype.h>
14 #include <string.h>
16 #define TRUE 1
17 #define FALSE 0
19 #define LINELEN 72 /* Encoded line length (max 76) */
21 typedef unsigned char byte; /* Byte type */
23 FILE *fi; /* Input file */
24 FILE *fo; /* Output file */
25 static byte iobuf[256]; /* I/O buffer */
26 static int iolen = 0; /* Bytes left in I/O buffer */
27 static int iocp = 256; /* Character removal pointer */
28 static int ateof = FALSE; /* EOF encountered */
29 static byte dtable[256]; /* Encode / decode table */
30 static int linelength = 0; /* Length of encoded output line */
31 static char eol[] = "\r\n"; /* End of line sequence */
32 static int errcheck = TRUE; /* Check decode input for errors ? */
34 /* INBUF -- Fill input buffer with data */
36 static int inbuf(void)
38 int l;
40 if (ateof) {
41 return FALSE;
43 l = fread(iobuf, 1, sizeof iobuf, fi); /* Read input buffer */
44 if (l <= 0) {
45 if (ferror(fi)) {
46 exit(1);
48 ateof = TRUE;
49 return FALSE;
51 iolen = l;
52 iocp = 0;
53 return TRUE;
56 /* INCHAR -- Return next character from input */
58 static int inchar(void)
60 if (iocp >= iolen) {
61 if (!inbuf()) {
62 return EOF;
66 return iobuf[iocp++];
69 /* OCHAR -- Output an encoded character, inserting line breaks
70 where required. */
72 static void ochar(int c)
74 if (linelength >= LINELEN) {
75 if (fputs(eol, fo) == EOF) {
76 exit(1);
78 linelength = 0;
80 if (putc(((byte) c), fo) == EOF) {
81 exit(1);
83 linelength++;
86 /* ENCODE -- Encode binary file into base64. */
88 static void encode(void)
90 int i, hiteof = FALSE;
92 /* Fill dtable with character encodings. */
94 for (i = 0; i < 26; i++) {
95 dtable[i] = 'A' + i;
96 dtable[26 + i] = 'a' + i;
98 for (i = 0; i < 10; i++) {
99 dtable[52 + i] = '0' + i;
101 dtable[62] = '+';
102 dtable[63] = '/';
104 while (!hiteof) {
105 byte igroup[3], ogroup[4];
106 int c, n;
108 igroup[0] = igroup[1] = igroup[2] = 0;
109 for (n = 0; n < 3; n++) {
110 c = inchar();
111 if (c == EOF) {
112 hiteof = TRUE;
113 break;
115 igroup[n] = (byte) c;
117 if (n > 0) {
118 ogroup[0] = dtable[igroup[0] >> 2];
119 ogroup[1] = dtable[((igroup[0] & 3) << 4) | (igroup[1] >> 4)];
120 ogroup[2] = dtable[((igroup[1] & 0xF) << 2) | (igroup[2] >> 6)];
121 ogroup[3] = dtable[igroup[2] & 0x3F];
123 /* Replace characters in output stream with "=" pad
124 characters if fewer than three characters were
125 read from the end of the input stream. */
127 if (n < 3) {
128 ogroup[3] = '=';
129 if (n < 2) {
130 ogroup[2] = '=';
133 for (i = 0; i < 4; i++) {
134 ochar(ogroup[i]);
138 if (fputs(eol, fo) == EOF) {
139 exit(1);
143 /* INSIG -- Return next significant input */
145 static int insig(void)
147 int c;
149 /*CONSTANTCONDITION*/
150 while (TRUE) {
151 c = inchar();
152 if (c == EOF || (c > ' ')) {
153 return c;
156 /*NOTREACHED*/
159 /* DECODE -- Decode base64. */
161 static void decode(void)
163 int i;
165 for (i = 0; i < 255; i++) {
166 dtable[i] = 0x80;
168 for (i = 'A'; i <= 'Z'; i++) {
169 dtable[i] = 0 + (i - 'A');
171 for (i = 'a'; i <= 'z'; i++) {
172 dtable[i] = 26 + (i - 'a');
174 for (i = '0'; i <= '9'; i++) {
175 dtable[i] = 52 + (i - '0');
177 dtable['+'] = 62;
178 dtable['/'] = 63;
179 dtable['='] = 0;
181 /*CONSTANTCONDITION*/
182 while (TRUE) {
183 byte a[4], b[4], o[3];
185 for (i = 0; i < 4; i++) {
186 int c = insig();
188 if (c == EOF) {
189 if (errcheck && (i > 0)) {
190 fprintf(stderr, "Input file incomplete.\n");
191 exit(1);
193 return;
195 if (dtable[c] & 0x80) {
196 if (errcheck) {
197 fprintf(stderr, "Illegal character '%c' in input file.\n", c);
198 exit(1);
200 /* Ignoring errors: discard invalid character. */
201 i--;
202 continue;
204 a[i] = (byte) c;
205 b[i] = (byte) dtable[c];
207 o[0] = (b[0] << 2) | (b[1] >> 4);
208 o[1] = (b[1] << 4) | (b[2] >> 2);
209 o[2] = (b[2] << 6) | b[3];
210 i = a[2] == '=' ? 1 : (a[3] == '=' ? 2 : 3);
211 if (fwrite(o, i, 1, fo) == EOF) {
212 exit(1);
214 if (i < 3) {
215 return;
220 /* USAGE -- Print how-to-call information. */
222 static void usage(char *pname)
224 fprintf(stderr, "%s -- Encode/decode file as base64. Call:\n", pname);
225 fprintf(stderr,
226 " %s [-e[ncode] / -d[ecode]] [-n] [infile] [outfile]\n", pname);
227 fprintf(stderr, "\n");
228 fprintf(stderr, "Options:\n");
229 fprintf(stderr, " -D Decode base64 encoded file\n");
230 fprintf(stderr, " -E Encode file into base64\n");
231 fprintf(stderr, " -N Ignore errors when decoding\n");
232 fprintf(stderr, " -U Print this message\n");
233 fprintf(stderr, "\n");
234 fprintf(stderr, "by John Walker\n");
235 fprintf(stderr, " WWW: http://www.fourmilab.ch/\n");
238 /* Main program */
240 int main(int argc, char *argv[])
242 int i, f = 0, decoding = FALSE;
243 char *cp, opt;
245 fi = stdin;
246 fo = stdout;
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);
255 switch (opt) {
257 case 'D': /* -D Decode */
258 decoding = TRUE;
259 break;
261 case 'E': /* -E Encode */
262 decoding = FALSE;
263 break;
265 case 'N': /* -N Suppress error checking */
266 errcheck = FALSE;
267 break;
269 case 'U': /* -U Print how-to-call information */
270 case '?':
271 usage(argv[0]);
272 return 0;
274 } else {
275 switch (f) {
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. **/
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;
296 f++;
297 break;
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;
306 f++;
307 break;
309 default:
310 fprintf(stderr, "Too many file names specified.\n");
311 usage(argv[0]);
312 return 2;
317 if (decoding) {
318 decode();
319 } else {
320 encode();
322 return 0;