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