This commit was generated by cvs2svn to compensate for changes in r1365,
[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     unsigned 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 MSDOS
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 MSDOS
446 #include <fcntl.h>
447 #endif
448
449 #define BASE64 1
450 #define QP 2 /* quoted-printable */
451
452 int main(int argc, char *argv[])
453 {
454     int encode = 1, which = BASE64, i, portablenewlines = 0;
455     FILE *fp = stdin;
456     FILE *fpo = stdout;
457
458     for (i=1; i<argc; ++i) {
459         if (argv[i][0] == '-') {
460             switch (argv[i][1]) {
461                 case 'o':
462                     if (++i >= argc) {
463                         fprintf(stderr, "mimencode: -o requires a file name.\n");
464                         exit(-1);
465                     }
466                     fpo = fopen(argv[i], "w");
467                     if (!fpo) {
468                         perror(argv[i]);
469                         exit(-1);
470                     }
471                     break;
472                 case 'u':
473                     encode = 0;
474                     break;
475                 case 'q':
476                     which = QP;
477                     break;
478                 case 'p':
479                     portablenewlines = 1;
480                     break;
481                 case 'b':
482                     which = BASE64;
483                     break;
484                 default:
485                     fprintf(stderr,
486                        "Usage: mmencode [-u] [-q] [-b] [-p] [-o outputfile] [file name]\n");
487                     exit(-1);
488             }
489         } else {
490 #ifdef MSDOS
491             if (encode)
492                 fp = fopen(argv[i], "rb");
493             else
494             {
495                 fp = fopen(argv[i], "rt");
496                 setmode(fileno(fpo), O_BINARY);
497             } /* else */
498 #else
499             fp = fopen(argv[i], "r");
500 #endif /* MSDOS */
501             if (!fp) {
502                 perror(argv[i]);
503                 exit(-1);
504             }
505         }
506     }
507 #ifdef MSDOS
508     if (fp == stdin) setmode(fileno(fp), O_BINARY);
509 #endif /* MSDOS */
510     if (which == BASE64) {
511         if (encode) {
512             to64(fp, fpo, portablenewlines);
513         } else {
514             from64(fp,fpo, (char **) NULL, (int *) 0, portablenewlines);
515         }
516     } else {
517         if (encode) toqp(fp, fpo); else fromqp(fp, fpo, NULL, 0);
518     }
519     return(0);
520 }
521