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