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