XEmacs 21.4.10 "Military Intelligence".
[chise/xemacs-chise.git.1] / lib-src / mmencode.c
1 /*
2 Copyright (c) 1991 Bell Communications Research, Inc. (Bellcore)
3
4 Permission to use, copy, modify, and distribute this material 
5 for any purpose and without fee is hereby granted, provided 
6 that the above copyright notice and this permission notice 
7 appear in all copies, and that the name of Bellcore not be 
8 used in advertising or publicity pertaining to this 
9 material without the specific, prior written permission 
10 of an authorized representative of Bellcore.  BELLCORE 
11 MAKES NO REPRESENTATIONS ABOUT THE ACCURACY OR SUITABILITY 
12 OF THIS MATERIAL FOR ANY PURPOSE.  IT IS PROVIDED "AS IS", 
13 WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES.
14 */
15
16 #define NEWLINE_CHAR '\n'
17 #include <stdlib.h>
18 #include <stdio.h>
19 #include <ctype.h>
20 #include <string.h>
21 #include <errno.h>
22
23 static void
24 output64chunk(int c1, int c2, int c3, int pads, FILE *outfile);
25
26 static signed char basis_64[] =
27    "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
28
29 static signed char index_64[128] = {
30     -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
31     -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
32     -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,62, -1,-1,-1,63,
33     52,53,54,55, 56,57,58,59, 60,61,-1,-1, -1,-1,-1,-1,
34     -1, 0, 1, 2,  3, 4, 5, 6,  7, 8, 9,10, 11,12,13,14,
35     15,16,17,18, 19,20,21,22, 23,24,25,-1, -1,-1,-1,-1,
36     -1,26,27,28, 29,30,31,32, 33,34,35,36, 37,38,39,40,
37     41,42,43,44, 45,46,47,48, 49,50,51,-1, -1,-1,-1,-1
38 };
39
40 #define char64(c)  (((c) < 0 || (c) > 127) ? -1 : index_64[(c)])
41
42 /*
43 char64(c)
44 char c;
45 {
46     char *s = (char *) strchr(basis_64, c);
47     if (s) return(s-basis_64);
48     return(-1);
49 }
50 */
51
52 /* the following gets a character, but fakes it properly into two chars if there's a newline character */
53 static int InNewline=0;
54
55 static int
56 nextcharin (FILE *infile, int PortableNewlines)
57 {
58     int c;
59
60 #ifndef NEWLINE_CHAR
61     return(getc(infile));
62 #else
63     if (!PortableNewlines) return(getc(infile));
64     if (InNewline) {
65         InNewline = 0;
66         return(10); /* LF */
67     }
68     c = getc(infile);
69     if (c == NEWLINE_CHAR) {
70         InNewline = 1;
71         return(13); /* CR */
72     }
73     return(c);
74 #endif
75 }
76
77 static void
78 to64(FILE *infile, FILE *outfile, int PortableNewlines) 
79 {
80     int c1, c2, c3, ct=0;
81     InNewline = 0; /* always reset it */
82     while ((c1 = nextcharin(infile, PortableNewlines)) != EOF) {
83         c2 = nextcharin(infile, PortableNewlines);
84         if (c2 == EOF) {
85             output64chunk(c1, 0, 0, 2, outfile);
86         } else {
87             c3 = nextcharin(infile, PortableNewlines);
88             if (c3 == EOF) {
89                 output64chunk(c1, c2, 0, 1, outfile);
90             } else {
91                 output64chunk(c1, c2, c3, 0, outfile);
92             }
93         }
94         ct += 4;
95         if (ct > 71) {
96             putc('\n', outfile);
97             ct = 0;
98         }
99     }
100     if (ct) putc('\n', outfile);
101     fflush(outfile);
102 }
103
104 static void
105 output64chunk(int c1, int c2, int c3, int pads, FILE *outfile)
106 {
107     putc(basis_64[c1>>2], outfile);
108     putc(basis_64[((c1 & 0x3)<< 4) | ((c2 & 0xF0) >> 4)], outfile);
109     if (pads == 2) {
110         putc('=', outfile);
111         putc('=', outfile);
112     } else if (pads) {
113         putc(basis_64[((c2 & 0xF) << 2) | ((c3 & 0xC0) >>6)], outfile);
114         putc('=', outfile);
115     } else {
116         putc(basis_64[((c2 & 0xF) << 2) | ((c3 & 0xC0) >>6)], outfile);
117         putc(basis_64[c3 & 0x3F], outfile);
118     }
119 }
120
121 static int
122 PendingBoundary(char *s, char **Boundaries, int *BoundaryCt)
123 {
124     int i, len;
125
126     if (s[0] != '-' || s[1] != '-') return(0);
127
128
129     for (i=0; i < *BoundaryCt; ++i) {
130         len = strlen(Boundaries[i]);
131         if (!strncmp(s, Boundaries[i], len)) {
132             if (s[len] == '-' && s[len+1] == '-') *BoundaryCt = i;
133             return(1);
134         }
135     }
136     return(0);
137 }
138
139 /* If we're in portable newline mode, we have to convert CRLF to the 
140     local newline convention on output */
141
142 static int CRpending = 0;
143
144 #ifdef NEWLINE_CHAR
145 static void
146 almostputc(int c, FILE *outfile, int PortableNewlines)
147 {
148     if (CRpending) {
149         if (c == 10) {
150             putc(NEWLINE_CHAR, outfile);
151             CRpending = 0;
152         } else {
153             putc(13, outfile);
154             if (c != 13) {
155                 putc(c, outfile);
156                 CRpending = 0;
157             }
158         }
159     } else {
160         if (PortableNewlines && c == 13) {
161             CRpending = 1;
162         } else {
163             putc(c, outfile);
164         }
165     }
166 }
167 #else
168 static void
169 almostputc(int c, FILE *outfile, int PortableNewlines)
170 {
171     putc(c, outfile);
172 }
173 #endif
174
175 static void
176 from64(FILE *infile, FILE *outfile,
177        char **boundaries, int *boundaryct, int PortableNewlines) 
178 {
179     int c1, c2, c3, c4;
180     int newline = 1, DataDone = 0;
181
182     /* always reinitialize */
183     CRpending = 0;
184     while ((c1 = getc(infile)) != EOF) {
185         if (isspace(c1)) {
186             if (c1 == '\n') {
187                 newline = 1;
188             } else {
189                 newline = 0;
190             }
191             continue;
192         }
193         if (newline && boundaries && c1 == '-') {
194             char Buf[200];
195             /* a dash is NOT base 64, so all bets are off if NOT a boundary */
196             ungetc(c1, infile);
197             fgets(Buf, sizeof(Buf), infile);
198             if (boundaries
199                  && (Buf[0] == '-')
200                  && (Buf[1] == '-')
201                  && PendingBoundary(Buf, boundaries, boundaryct)) {
202                 return;
203             }
204             fprintf(stderr, "Ignoring unrecognized boundary line: %s\n", Buf);
205             continue;
206         }
207         if (DataDone) continue;
208         newline = 0;
209         do {
210             c2 = getc(infile);
211         } while (c2 != EOF && isspace(c2));
212         do {
213             c3 = getc(infile);
214         } while (c3 != EOF && isspace(c3));
215         do {
216             c4 = getc(infile);
217         } while (c4 != EOF && isspace(c4));
218         if (c2 == EOF || c3 == EOF || c4 == EOF) {
219             fprintf(stderr, "Warning: base64 decoder saw premature EOF!\n");
220             return;
221         }
222         if (c1 == '=' || c2 == '=') {
223             DataDone=1;
224             continue;
225         }
226         c1 = char64(c1);
227         c2 = char64(c2);
228         almostputc(((c1<<2) | ((c2&0x30)>>4)), outfile, PortableNewlines);
229         if (c3 == '=') {
230             DataDone = 1;
231         } else {
232             c3 = char64(c3);
233             almostputc((((c2&0XF) << 4) | ((c3&0x3C) >> 2)), outfile, PortableNewlines);
234             if (c4 == '=') {
235                 DataDone = 1;
236             } else {
237                 c4 = char64(c4);
238                 almostputc((((c3&0x03) <<6) | c4), outfile, PortableNewlines);
239             }
240         }
241     }
242     if (CRpending) putc(13, outfile); /* Don't drop a lone trailing char 13 */
243 }
244
245 static signed char basis_hex[] = "0123456789ABCDEF";
246 static signed char index_hex[128] = {
247     -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
248     -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
249     -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
250      0, 1, 2, 3,  4, 5, 6, 7,  8, 9,-1,-1, -1,-1,-1,-1,
251     -1,10,11,12, 13,14,15,-1, -1,-1,-1,-1, -1,-1,-1,-1,
252     -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
253     -1,10,11,12, 13,14,15,-1, -1,-1,-1,-1, -1,-1,-1,-1,
254     -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1
255 };
256
257 /* The following version generated complaints on Solaris. */
258 /* #define hexchar(c)  (((c) < 0 || (c) > 127) ? -1 : index_hex[(c)])  */
259 /*  Since we're no longer ever calling it with anything signed, this should work: */
260 #define hexchar(c)  (((c) > 127) ? -1 : index_hex[(c)])
261
262 /*
263 hexchar(c)
264 char c;
265 {
266     char *s;
267     if (islower(c)) c = toupper(c);
268     s = (char *) strchr(basis_hex, c);
269     if (s) return(s-basis_hex);
270     return(-1);
271 }
272 */
273
274 static void
275 toqp(FILE *infile, FILE *outfile) 
276 {
277     int c, ct=0, prevc=255;
278     while ((c = getc(infile)) != EOF) {
279         if ((c < 32 && (c != '\n' && c != '\t'))
280              || (c == '=')
281              || (c >= 127)
282              /* Following line is to avoid single periods alone on lines,
283                which messes up some dumb smtp implementations, sigh... */
284              || (ct == 0 && c == '.')) {
285             putc('=', outfile);
286             putc(basis_hex[c>>4], outfile);
287             putc(basis_hex[c&0xF], outfile);
288             ct += 3;
289             prevc = 'A'; /* close enough */
290         } else if (c == '\n') {
291             if (prevc == ' ' || prevc == '\t') {
292                 putc('=', outfile); /* soft & hard lines */
293                 putc(c, outfile);
294             }
295             putc(c, outfile);
296             ct = 0;
297             prevc = c;
298         } else {
299             if (c == 'F' && prevc == '\n') {
300                 /* HORRIBLE but clever hack suggested by MTR for sendmail-avoidance */
301                 c = getc(infile);
302                 if (c == 'r') {
303                     c = getc(infile);
304                     if (c == 'o') {
305                         c = getc(infile);
306                         if (c == 'm') {
307                             c = getc(infile);
308                             if (c == ' ') {
309                                 /* This is the case we are looking for */
310                                 fputs("=46rom", outfile);
311                                 ct += 6;
312                             } else {
313                                 fputs("From", outfile);
314                                 ct += 4;
315                             }
316                         } else {
317                             fputs("Fro", outfile);
318                             ct += 3;
319                         }
320                     } else {
321                         fputs("Fr", outfile);
322                         ct += 2;
323                     }
324                 } else {
325                     putc('F', outfile);
326                     ++ct;
327                 }
328                 ungetc(c, infile);
329                 prevc = 'x'; /* close enough -- printable */
330             } else { /* END horrible hack */
331                 putc(c, outfile);
332                 ++ct;
333                 prevc = c;
334             }
335         }
336         if (ct > 72) {
337             putc('=', outfile);
338             putc('\n', outfile);
339             ct = 0;
340             prevc = '\n';
341         }
342     }
343     if (ct) {
344         putc('=', outfile);
345         putc('\n', outfile);
346     }
347 }
348
349 static void
350 fromqp(FILE *infile, FILE *outfile, char **boundaries, int *boundaryct) 
351 {
352     int c1, c2;
353     int sawnewline = 1, neednewline = 0;
354     /* The neednewline hack is necessary because the newline leading into 
355       a multipart boundary is part of the boundary, not the data */
356
357     while ((c1 = getc(infile)) != EOF) {
358         if (sawnewline && boundaries && (c1 == '-')) {
359             char Buf[200];
360             unsigned char *s;
361
362             ungetc(c1, infile);
363             fgets(Buf, sizeof(Buf), infile);
364             if (boundaries
365                  && (Buf[0] == '-')
366                  && (Buf[1] == '-')
367                  && PendingBoundary(Buf, boundaries, boundaryct)) {
368                 return;
369             }
370             /* Not a boundary, now we must treat THIS line as q-p, sigh */
371             if (neednewline) {
372                 putc('\n', outfile);
373                 neednewline = 0;
374             }
375             for (s=(unsigned char *) Buf; *s; ++s) {
376                 if (*s == '=') {
377                     if (!*++s) break;
378                     if (*s == '\n') {
379                         /* ignore it */
380                         sawnewline = 1;
381                     } else {
382                         c1 = hexchar(*s);
383                         if (!*++s) break;
384                         c2 = hexchar(*s);
385                         putc(c1<<4 | c2, outfile);
386                     }
387                 } else {
388 #ifdef WIN32_NATIVE
389                     if (*s == '\n')
390                         putc('\r', outfile);    /* insert CR for binary-mode write */
391 #endif
392                     putc(*s, outfile);
393                 }
394             }
395         } else {
396             if (neednewline) {
397                 putc('\n', outfile);
398                 neednewline = 0;
399             }
400             if (c1 == '=') {
401                 sawnewline = 0;
402                 c1 = getc(infile);
403                 if (c1 == '\n') {
404                     /* ignore it */
405                     sawnewline = 1;
406                 } else {
407                     c2 = getc(infile);
408                     c1 = hexchar(c1);
409                     c2 = hexchar(c2);
410                     putc(c1<<4 | c2, outfile);
411                     if (c2 == '\n') sawnewline = 1;
412                 }
413             } else {
414                 if (c1 == '\n') {
415                     sawnewline = 1;
416                     neednewline = 1;
417                 } else {
418                     sawnewline = 0;
419                     putc(c1, outfile);
420                 }
421             }
422         }
423     }
424     if (neednewline) {
425         putc('\n', outfile);
426         neednewline = 0;
427     }    
428 }
429
430
431 /*
432 Copyright (c) 1991 Bell Communications Research, Inc. (Bellcore)
433
434 Permission to use, copy, modify, and distribute this material 
435 for any purpose and without fee is hereby granted, provided 
436 that the above copyright notice and this permission notice 
437 appear in all copies, and that the name of Bellcore not be 
438 used in advertising or publicity pertaining to this 
439 material without the specific, prior written permission 
440 of an authorized representative of Bellcore.  BELLCORE 
441 MAKES NO REPRESENTATIONS ABOUT THE ACCURACY OR SUITABILITY 
442 OF THIS MATERIAL FOR ANY PURPOSE.  IT IS PROVIDED "AS IS", 
443 WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES.
444 */
445 #ifdef WIN32_NATIVE
446 #include <io.h>
447 #include <fcntl.h>
448 #endif
449
450 #define BASE64 1
451 #define QP 2 /* quoted-printable */
452
453 int main(int argc, char *argv[])
454 {
455     int encode = 1, which = BASE64, i, portablenewlines = 0;
456     FILE *fp = stdin;
457     FILE *fpo = stdout;
458
459     for (i=1; i<argc; ++i) {
460         if (argv[i][0] == '-') {
461             switch (argv[i][1]) {
462                 case 'o':
463                     if (++i >= argc) {
464                         fprintf(stderr, "mimencode: -o requires a file name.\n");
465                         exit(-1);
466                     }
467                     fpo = fopen(argv[i], "w");
468                     if (!fpo) {
469                         perror(argv[i]);
470                         exit(-1);
471                     }
472                     break;
473                 case 'u':
474                     encode = 0;
475                     break;
476                 case 'q':
477                     which = QP;
478                     break;
479                 case 'p':
480                     portablenewlines = 1;
481                     break;
482                 case 'b':
483                     which = BASE64;
484                     break;
485                 default:
486                     fprintf(stderr,
487                        "Usage: mmencode [-u] [-q] [-b] [-p] [-o outputfile] [file name]\n");
488                     exit(-1);
489             }
490         } else {
491 #ifdef WIN32_NATIVE
492             if (encode)
493                 fp = fopen(argv[i], "rb");
494             else
495             {
496                 fp = fopen(argv[i], "rt");
497                 setmode(fileno(fpo), O_BINARY);
498             } /* else */
499 #else
500             fp = fopen(argv[i], "r");
501 #endif /* WIN32_NATIVE */
502             if (!fp) {
503                 perror(argv[i]);
504                 exit(-1);
505             }
506         }
507     }
508 #ifdef WIN32_NATIVE
509     if (fp == stdin) setmode(fileno(fp), O_BINARY);
510 #endif /* WIN32_NATIVE */
511     if (which == BASE64) {
512         if (encode) {
513             to64(fp, fpo, portablenewlines);
514         } else {
515             from64(fp,fpo, (char **) NULL, (int *) 0, portablenewlines);
516         }
517     } else {
518         if (encode) toqp(fp, fpo); else fromqp(fp, fpo, NULL, 0);
519     }
520     return(0);
521 }
522