1 /* mime.c
2 */
3
4 #include "EXTERN.h"
5 #include "common.h"
6 #include "list.h"
7 #include "hash.h"
8 #include "cache.h"
9 #include "head.h"
10 #include "search.h"
11 #include "art.h"
12 #include "artio.h"
13 #include "artstate.h"
14 #include "ng.h"
15 #include "term.h"
16 #include "decode.h"
17 #include "respond.h"
18 #include "env.h"
19 #include "color.h"
20 #include "util.h"
21 #include "util2.h"
22 #include "backpage.h"
23 #include "charsubst.h"
24 #include "INTERN.h"
25 #include "mime.h"
26 #include "mime.ih"
27
28 static char text_plain[] = "text/plain";
29
30 void
mime_init()31 mime_init()
32 {
33 char* s;
34 char* t;
35 char* mcname;
36
37 mimecap_list = new_list(0,-1,sizeof(MIMECAP_ENTRY),40,LF_ZERO_MEM,NULL);
38
39 if ((mcname = getenv("MIMECAPS")) == NULL)
40 mcname = getval("MAILCAPS", MIMECAP);
41 mcname = s = savestr(mcname);
42 do {
43 if ((t = index(s, ':')) != NULL)
44 *t++ = '\0';
45 if (*s)
46 mime_ReadMimecap(s);
47 s = t;
48 } while (s && *s);
49 free(mcname);
50 }
51
52 void
mime_ReadMimecap(mcname)53 mime_ReadMimecap(mcname)
54 char* mcname;
55 {
56 FILE* fp;
57 char* bp;
58 char* s;
59 char* t;
60 char* arg;
61 int buflen = 2048;
62 int linelen;
63 MIMECAP_ENTRY* mcp;
64 int i;
65
66 if ((fp = fopen(filexp(mcname), "r")) == NULL)
67 return;
68 bp = safemalloc(buflen);
69 for (i = mimecap_list->high; !feof(fp); ) {
70 *(s = bp) = '\0';
71 linelen = 0;
72 while (fgets(s, buflen - linelen, fp)) {
73 if (*s == '#')
74 continue;
75 linelen += strlen(s);
76 if (linelen == 0)
77 continue;
78 if (bp[linelen-1] == '\n') {
79 if (--linelen == 0)
80 continue;
81 if (bp[linelen-1] != '\\') {
82 bp[linelen] = '\0';
83 break;
84 }
85 bp[--linelen] = '\0';
86 }
87
88 if (linelen+1024 > buflen) {
89 buflen *= 2;
90 bp = saferealloc(bp, buflen);
91 }
92
93 s = bp + linelen;
94 }
95 for (s = bp; isspace(*s); s++) ;
96 if (!*s)
97 continue;
98 t = mime_ParseEntryArg(&s);
99 if (!s) {
100 fprintf(stderr, "trn: Ignoring invalid mimecap entry: %s\n", bp);
101 continue;
102 }
103 mcp = mimecap_ptr(++i);
104 mcp->contenttype = savestr(t);
105 mcp->command = savestr(mime_ParseEntryArg(&s));
106 while (s) {
107 t = mime_ParseEntryArg(&s);
108 if ((arg = index(t, '=')) != NULL) {
109 char* f = arg+1;
110 while (arg != t && isspace(arg[-1])) arg--;
111 *arg++ = '\0';
112 while (isspace(*f)) f++;
113 if (*f == '"')
114 f = cpytill(arg,f+1,'"');
115 else
116 arg = f;
117 }
118 if (*t) {
119 if (strcaseEQ(t, "needsterminal"))
120 mcp->flags |= MCF_NEEDSTERMINAL;
121 else if (strcaseEQ(t, "copiousoutput"))
122 mcp->flags |= MCF_COPIOUSOUTPUT;
123 else if (arg && strcaseEQ(t, "test"))
124 mcp->testcommand = savestr(arg);
125 else if (arg && strcaseEQ(t, "description"))
126 mcp->label = savestr(arg);
127 else if (arg && strcaseEQ(t, "label"))
128 mcp->label = savestr(arg); /* bogus old name for description */
129 }
130 }
131 }
132 mimecap_list->high = i;
133 free(bp);
134 fclose(fp);
135 }
136
137 static char*
mime_ParseEntryArg(cpp)138 mime_ParseEntryArg(cpp)
139 char** cpp;
140 {
141 char* s = *cpp;
142 char* f;
143 char* t;
144
145 while (isspace(*s)) s++;
146
147 for (f = t = s; *f && *f != ';'; ) {
148 if (*f == '\\') {
149 if (*++f == '%')
150 *t++ = '%';
151 else if (!*f)
152 break;
153 }
154 *t++ = *f++;
155 }
156 while (isspace(*f) || *f == ';') f++;
157 if (!*f)
158 f = NULL;
159 while (t != s && isspace(t[-1])) t--;
160 *t = '\0';
161 *cpp = f;
162 return s;
163 }
164
165 MIMECAP_ENTRY*
mime_FindMimecapEntry(contenttype,skip_flags)166 mime_FindMimecapEntry(contenttype, skip_flags)
167 char* contenttype;
168 int skip_flags;
169 {
170 MIMECAP_ENTRY* mcp;
171 int i;
172
173 for (i = 0; i <= mimecap_list->high; i++) {
174 mcp = mimecap_ptr(i);
175 if (!(mcp->flags & skip_flags)
176 && mime_TypesMatch(contenttype, mcp->contenttype)) {
177 if (!mcp->testcommand)
178 return mcp;
179 if (mime_Exec(mcp->testcommand) == 0)
180 return mcp;
181 }
182 }
183 return NULL;
184 }
185
186 bool
mime_TypesMatch(ct,pat)187 mime_TypesMatch(ct,pat)
188 char* ct;
189 char* pat;
190 {
191 char* s = index(pat,'/');
192 int len = (s? s - pat : strlen(pat));
193 bool iswild = (!s || strEQ(s+1,"*"));
194
195 return strcaseEQ(ct,pat)
196 || (iswild && strncaseEQ(ct,pat,len) && ct[len] == '/');
197 }
198
199 int
mime_Exec(cmd)200 mime_Exec(cmd)
201 char* cmd;
202 {
203 char* f;
204 char* t;
205
206 for (f = cmd, t = cmd_buf; *f && t-cmd_buf < CBUFLEN-2; f++) {
207 if (*f == '%') {
208 switch (*++f) {
209 case 's':
210 safecpy(t, decode_filename, CBUFLEN-(t-cmd_buf));
211 t += strlen(t);
212 break;
213 case 't':
214 *t++ = '\'';
215 safecpy(t, mime_section->type_name, CBUFLEN-(t-cmd_buf)-1);
216 t += strlen(t);
217 *t++ = '\'';
218 break;
219 case '{': {
220 char* s = index(f, '}');
221 char* p;
222 if (!s)
223 return -1;
224 f++;
225 *s = '\0';
226 p = mime_FindParam(mime_section->type_params, f);
227 *s = '}'; /* restore */
228 f = s;
229 *t++ = '\'';
230 safecpy(t, p, CBUFLEN-(t-cmd_buf)-1);
231 t += strlen(t);
232 *t++ = '\'';
233 break;
234 }
235 case '%':
236 *t++ = '%';
237 break;
238 case 'n':
239 case 'F':
240 return -1;
241 }
242 }
243 else
244 *t++ = *f;
245 }
246 *t = '\0';
247
248 return doshell(sh, cmd_buf);
249 }
250
251 void
mime_InitSections()252 mime_InitSections()
253 {
254 while (mime_PopSection()) ;
255 mime_ClearStruct(mime_section);
256 mime_state = NOT_MIME;
257 }
258
259 void
mime_PushSection()260 mime_PushSection()
261 {
262 MIME_SECT* mp = (MIME_SECT*)safemalloc(sizeof (MIME_SECT));
263 bzero((char*)mp, sizeof (MIME_SECT));
264 mp->prev = mime_section;
265 mime_section = mp;
266 }
267
268 bool
mime_PopSection()269 mime_PopSection()
270 {
271 MIME_SECT* mp = mime_section->prev;
272 if (mp) {
273 mime_ClearStruct(mime_section);
274 free((char*)mime_section);
275 mime_section = mp;
276 mime_state = mp->type;
277 return TRUE;
278 }
279 mime_state = mime_article.type;
280 return FALSE;
281 }
282
283 /* Free up this mime structure's resources */
284 void
mime_ClearStruct(mp)285 mime_ClearStruct(mp)
286 MIME_SECT* mp;
287 {
288 safefree0(mp->filename);
289 safefree0(mp->type_name);
290 safefree0(mp->type_params);
291 safefree0(mp->boundary);
292 safefree0(mp->html_blks);
293 mp->type = NOT_MIME;
294 mp->encoding = MENCODE_NONE;
295 mp->part = mp->total = mp->boundary_len = mp->flags = mp->html
296 = mp->html_blkcnt = 0;
297 mp->html_line_start = 0;
298 }
299
300 /* Setup mime_article structure based on article's headers */
301 void
mime_SetArticle()302 mime_SetArticle()
303 {
304 char* s;
305
306 mime_InitSections();
307 /*$$ Check mime version #? */
308 multimedia_mime = FALSE;
309 is_mime = (htype[MIMEVER_LINE].flags & HT_MAGIC)
310 && htype[MIMEVER_LINE].minpos >= 0;
311 if (is_mime) {
312 s = fetchlines(art,CONTXFER_LINE);
313 mime_ParseEncoding(mime_section,s);
314 free(s);
315
316 s = fetchlines(art,CONTTYPE_LINE);
317 mime_ParseType(mime_section,s);
318 free(s);
319
320 s = fetchlines(art,CONTDISP_LINE);
321 mime_ParseDisposition(mime_section,s);
322 free(s);
323
324 mime_state = mime_section->type;
325 if (mime_state == NOT_MIME
326 || (mime_state == TEXT_MIME && mime_section->encoding == MENCODE_NONE))
327 is_mime = FALSE;
328 else if (!mime_section->type_name)
329 mime_section->type_name = savestr(text_plain);
330 }
331 }
332
333 /* Use the Content-Type to set values in the mime structure */
334 void
mime_ParseType(mp,s)335 mime_ParseType(mp, s)
336 MIME_SECT* mp;
337 char* s;
338 {
339 char* t;
340
341 safefree0(mp->type_name);
342 safefree0(mp->type_params);
343
344 mp->type_params = mime_ParseParams(s);
345 if (!*s) {
346 mp->type = NOT_MIME;
347 return;
348 }
349 mp->type_name = savestr(s);
350 t = mime_FindParam(mp->type_params,"name");
351 if (t) {
352 safefree(mp->filename);
353 mp->filename = savestr(t);
354 }
355
356 if (strncaseEQ(s, "text", 4)) {
357 mp->type = TEXT_MIME;
358 s += 4;
359 if (*s++ != '/')
360 return;
361 #if 0
362 t = mime_FindParam(mp->type_params,"charset");
363 if (t && strncaseNE(t, "us-ascii", 8))
364 mp->type = ISOTEXT_MIME;
365 #endif
366 if (strncaseEQ(s, "html", 4))
367 mp->type = HTMLTEXT_MIME;
368 else if (strncaseEQ(s, "x-vcard", 7))
369 mp->type = UNHANDLED_MIME;
370 return;
371 }
372
373 if (strncaseEQ(s, "message/", 8)) {
374 s += 8;
375 mp->type = MESSAGE_MIME;
376 if (strcaseEQ(s, "partial")) {
377 t = mime_FindParam(mp->type_params,"id");
378 if (!t)
379 return;
380 safefree(mp->filename);
381 mp->filename = savestr(t);
382 t = mime_FindParam(mp->type_params,"number");
383 if (t)
384 mp->part = (short)atoi(t);
385 t = mime_FindParam(mp->type_params,"total");
386 if (t)
387 mp->total = (short)atoi(t);
388 if (!mp->total) {
389 mp->part = 0;
390 return;
391 }
392 return;
393 }
394 return;
395 }
396
397 if (strncaseEQ(s, "multipart/", 10)) {
398 s += 10;
399 t = mime_FindParam(mp->type_params,"boundary");
400 if (!t) {
401 mp->type = UNHANDLED_MIME;
402 return;
403 }
404 if (strncaseEQ(s, "alternative", 11))
405 mp->flags |= MSF_ALTERNATIVE;
406 safefree(mp->boundary);
407 mp->boundary = savestr(t);
408 mp->boundary_len = (short)strlen(t);
409 mp->type = MULTIPART_MIME;
410 return;
411 }
412
413 if (strncaseEQ(s, "image/", 6)) {
414 mp->type = IMAGE_MIME;
415 return;
416 }
417
418 if (strncaseEQ(s, "audio/", 6)) {
419 mp->type = AUDIO_MIME;
420 return;
421 }
422
423 mp->type = UNHANDLED_MIME;
424 }
425
426 /* Use the Content-Disposition to set values in the mime structure */
427 void
mime_ParseDisposition(mp,s)428 mime_ParseDisposition(mp, s)
429 MIME_SECT* mp;
430 char* s;
431 {
432 char* params;
433
434 params = mime_ParseParams(s);
435 if (strcaseEQ(s,"inline"))
436 mp->flags |= MSF_INLINE;
437
438 s = mime_FindParam(params,"filename");
439 if (s) {
440 safefree(mp->filename);
441 mp->filename = savestr(s);
442 }
443 safefree(params);
444 }
445
446 /* Use the Content-Transfer-Encoding to set values in the mime structure */
447 void
mime_ParseEncoding(mp,s)448 mime_ParseEncoding(mp, s)
449 MIME_SECT* mp;
450 char* s;
451 {
452 s = mime_SkipWhitespace(s);
453 if (!*s) {
454 mp->encoding = MENCODE_NONE;
455 return;
456 }
457 if (*s == '7' || *s == '8') {
458 if (strncaseEQ(s+1, "bit", 3)) {
459 s += 4;
460 mp->encoding = MENCODE_NONE;
461 }
462 }
463 else if (strncaseEQ(s, "quoted-printable", 16)) {
464 s += 16;
465 mp->encoding = MENCODE_QPRINT;
466 }
467 else if (strncaseEQ(s, "binary", 6)) {
468 s += 6;
469 mp->encoding = MENCODE_NONE;
470 }
471 else if (strncaseEQ(s, "base64", 6)) {
472 s += 6;
473 mp->encoding = MENCODE_BASE64;
474 }
475 else if (strncaseEQ(s, "x-uue", 5)) {
476 s += 5;
477 mp->encoding = MENCODE_UUE;
478 if (strncaseEQ(s, "ncode", 5))
479 s += 5;
480 }
481 else {
482 mp->encoding = MENCODE_UNHANDLED;
483 return;
484 }
485 if (*s != '\0' && !isspace(*s) && *s != ';' && *s != '(')
486 mp->encoding = MENCODE_UNHANDLED;
487 }
488
489 /* Parse a multipart mime header and affect the *mime_section structure */
490
491 void
mime_ParseSubheader(ifp,next_line)492 mime_ParseSubheader(ifp, next_line)
493 FILE* ifp;
494 char* next_line;
495 {
496 static char* line = NULL;
497 static int line_size = 0;
498 char* s;
499 int pos, linetype, len;
500 mime_ClearStruct(mime_section);
501 mime_section->type = TEXT_MIME;
502 for (;;) {
503 for (pos = 0; ; pos += strlen(line+pos)) {
504 len = pos + (next_line? strlen(next_line) : 0) + LBUFLEN;
505 if (line_size < len) {
506 line_size = len + LBUFLEN;
507 line = saferealloc(line, line_size);
508 }
509 if (next_line) {
510 safecpy(line+pos, next_line, line_size - pos);
511 next_line = NULL;
512 }
513 else if (ifp) {
514 if (!fgets(line + pos, LBUFLEN, ifp))
515 break;
516 }
517 else if (!readart(line + pos, LBUFLEN))
518 break;
519 if (line[0] == '\n')
520 break;
521 if (pos && line[pos] != ' ' && line[pos] != '\t') {
522 next_line = line + pos;
523 line[pos-1] = '\0';
524 break;
525 }
526 }
527 s = index(line,':');
528 if (s == NULL)
529 break;
530
531 linetype = set_line_type(line,s);
532 switch (linetype) {
533 case CONTTYPE_LINE:
534 mime_ParseType(mime_section,s+1);
535 break;
536 case CONTXFER_LINE:
537 mime_ParseEncoding(mime_section,s+1);
538 break;
539 case CONTDISP_LINE:
540 mime_ParseDisposition(mime_section,s+1);
541 break;
542 case CONTNAME_LINE:
543 safefree(mime_section->filename);
544 s = mime_SkipWhitespace(s+1);
545 mime_section->filename = savestr(s);
546 break;
547 #if 0
548 case CONTLEN_LINE:
549 mime_section->content_len = atol(s+1);
550 break;
551 #endif
552 }
553 }
554 mime_state = mime_section->type;
555 if (!mime_section->type_name)
556 mime_section->type_name = savestr(text_plain);
557 }
558
559 void
mime_SetState(bp)560 mime_SetState(bp)
561 char* bp;
562 {
563 int ret;
564
565 if (mime_state == BETWEEN_MIME) {
566 mime_ParseSubheader((FILE*)NULL,bp);
567 *bp = '\0';
568 if (mime_section->prev->flags & MSF_ALTERNADONE)
569 mime_state = ALTERNATE_MIME;
570 else if (mime_section->prev->flags & MSF_ALTERNATIVE)
571 mime_section->prev->flags |= MSF_ALTERNADONE;
572 }
573
574 while (mime_state == MESSAGE_MIME) {
575 mime_PushSection();
576 mime_ParseSubheader((FILE*)NULL,*bp? bp : (char*)NULL);
577 *bp = '\0';
578 }
579
580 if (mime_state == MULTIPART_MIME) {
581 mime_PushSection();
582 mime_state = SKIP_MIME; /* Skip anything before 1st part */
583 }
584
585 ret = mime_EndOfSection(bp);
586 switch (ret) {
587 case 0:
588 break;
589 case 1:
590 while (!mime_section->prev->boundary_len)
591 mime_PopSection();
592 mime_state = BETWEEN_MIME;
593 break;
594 case 2:
595 while (!mime_section->prev->boundary_len)
596 mime_PopSection();
597 mime_PopSection();
598 mime_state = END_OF_MIME;
599 break;
600 }
601 }
602
603 int
mime_EndOfSection(bp)604 mime_EndOfSection(bp)
605 char* bp;
606 {
607 MIME_SECT* mp = mime_section->prev;
608 while (mp && !mp->boundary_len)
609 mp = mp->prev;
610 if (mp) {
611 /* have we read all the data in this part? */
612 if (bp[0] == '-' && bp[1] == '-'
613 && strnEQ(bp+2,mp->boundary,mp->boundary_len)) {
614 int len = 2 + mp->boundary_len;
615 /* have we found the last boundary? */
616 if (bp[len] == '-' && bp[len+1] == '-'
617 && (bp[len+2] == '\n' || bp[len+2] == '\0'))
618 return 2;
619 return bp[len] == '\n' || bp[len] == '\0';
620 }
621 }
622 return 0;
623 }
624
625 /* Return a saved string of all the extra parameters on this mime
626 * header line. The passed-in string is transformed into just the
627 * first word on the line.
628 */
629 char*
mime_ParseParams(str)630 mime_ParseParams(str)
631 char* str;
632 {
633 char* s;
634 char* t;
635 char* e;
636 s = e = mime_SkipWhitespace(str);
637 while (*e && *e != ';' && !isspace(*e) && *e != '(') e++;
638 t = savestr(mime_SkipWhitespace(e));
639 *e = '\0';
640 if (s != str)
641 safecpy(str, s, e - s + 1);
642 str = s = t;
643 while (*s == ';') {
644 s = mime_SkipWhitespace(s+1);
645 while (*s && *s != ';' && *s != '(' && *s != '=' && !isspace(*s))
646 *t++ = *s++;
647 s = mime_SkipWhitespace(s);
648 if (*s == '=') {
649 *t++ = *s;
650 s = mime_SkipWhitespace(s+1);
651 if (*s == '"') {
652 s = cpytill(t,s+1,'"');
653 if (*s == '"')
654 s++;
655 t += strlen(t);
656 }
657 else
658 while (*s && *s != ';' && !isspace(*s) && *s != '(')
659 *t++ = *s++;
660 }
661 *t++ = '\0';
662 }
663 *t = '\0';
664 return str;
665 }
666
667 char*
mime_FindParam(s,param)668 mime_FindParam(s, param)
669 char* s;
670 char* param;
671 {
672 int param_len = strlen(param);
673 while (s && *s) {
674 if (strncaseEQ(s, param, param_len) && s[param_len] == '=')
675 return s + param_len + 1;
676 s += strlen(s) + 1;
677 }
678 return NULL;
679 }
680
681 /* Skip whitespace and RFC-822 comments. */
682
683 char*
mime_SkipWhitespace(s)684 mime_SkipWhitespace(s)
685 char* s;
686 {
687 int comment_level = 0;
688
689 while (*s) {
690 if (*s == '(') {
691 s++;
692 comment_level++;
693 while (comment_level) {
694 switch (*s++) {
695 case '\0':
696 return s-1;
697 case '\\':
698 s++;
699 break;
700 case '(':
701 comment_level++;
702 break;
703 case ')':
704 comment_level--;
705 break;
706 }
707 }
708 }
709 else if (!isspace(*s))
710 break;
711 else
712 s++;
713 }
714 return s;
715 }
716
717 void
mime_DecodeArticle(view)718 mime_DecodeArticle(view)
719 bool_int view;
720 {
721 MIMECAP_ENTRY* mcp = NULL;
722
723 seekart(savefrom);
724 *art_line = '\0';
725
726 while (1) {
727 if (mime_state != MESSAGE_MIME || !mime_section->total) {
728 if (!readart(art_line,sizeof art_line))
729 break;
730 mime_SetState(art_line);
731 }
732 switch (mime_state) {
733 case BETWEEN_MIME:
734 case END_OF_MIME:
735 break;
736 case TEXT_MIME:
737 case HTMLTEXT_MIME:
738 case ISOTEXT_MIME:
739 case MESSAGE_MIME:
740 /* $$ Check for uuencoded file here? */
741 mime_state = SKIP_MIME;
742 /* FALL THROUGH */
743 case SKIP_MIME: {
744 MIME_SECT* mp = mime_section;
745 while ((mp = mp->prev) != NULL && !mp->boundary_len) ;
746 if (!mp)
747 return;
748 break;
749 }
750 default:
751 if (view) {
752 mcp = mime_FindMimecapEntry(mime_section->type_name,0);
753 if (!mcp) {
754 printf("No view method for %s -- skipping.\n",
755 mime_section->type_name);
756 mime_state = SKIP_MIME;
757 break;
758 }
759 }
760 mime_state = DECODE_MIME;
761 if (decode_piece(mcp, *art_line == '\n'? NULL : art_line) != 0) {
762 mime_SetState(art_line);
763 if (mime_state == DECODE_MIME)
764 mime_state = SKIP_MIME;
765 }
766 else {
767 if (*msg) {
768 newline();
769 fputs(msg,stdout);
770 }
771 mime_state = SKIP_MIME;
772 }
773 newline();
774 break;
775 }
776 }
777 }
778
779 void
mime_Description(mp,s,limit)780 mime_Description(mp, s, limit)
781 MIME_SECT* mp;
782 char* s;
783 int limit;
784 {
785 char* fn = decode_fix_fname(mp->filename);
786 int len, flen = strlen(fn);
787
788 limit -= 2; /* leave room for the trailing ']' and '\n' */
789 sprintf(s, "[Attachment type=%s, name=", mp->type_name);
790 len = strlen(s);
791 if (len + flen <= limit)
792 sprintf(s+len, "%s]\n", fn);
793 else if (len+3 >= limit)
794 strcpy(s+limit-3, "...]\n");
795 else {
796 #if 0
797 sprintf(s+len, "...%s]\n", fn + flen - (limit-(len+3)));
798 #else
799 safecpy(s+len, fn, limit - (len+3));
800 strcat(s, "...]\n");
801 #endif
802 }
803 }
804
805 #define XX 255
806 static Uchar index_hex[256] = {
807 XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
808 XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
809 XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
810 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,XX,XX, XX,XX,XX,XX,
811 XX,10,11,12, 13,14,15,XX, XX,XX,XX,XX, XX,XX,XX,XX,
812 XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
813 XX,10,11,12, 13,14,15,XX, XX,XX,XX,XX, XX,XX,XX,XX,
814 XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
815 XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
816 XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
817 XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
818 XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
819 XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
820 XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
821 XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
822 XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
823 };
824
825 int
qp_decodestring(t,f,in_header)826 qp_decodestring(t, f, in_header)
827 char* t;
828 char* f;
829 bool_int in_header;
830 {
831 char* save_t = t;
832 while (*f) {
833 switch (*f) {
834 case '_':
835 if (in_header) {
836 *t++ = ' ';
837 f++;
838 }
839 else
840 *t++ = *f++;
841 break;
842 case '=': /* decode a hex-value */
843 if (f[1] == '\n') {
844 f += 2;
845 break;
846 }
847 if (index_hex[(Uchar)f[1]] != XX && index_hex[(Uchar)f[2]] != XX) {
848 *t = (index_hex[(Uchar)f[1]] << 4) + index_hex[(Uchar)f[2]];
849 f += 3;
850 if (*t != '\r')
851 t++;
852 break;
853 }
854 /* FALL THROUGH */
855 default:
856 *t++ = *f++;
857 break;
858 }
859 }
860 *t = '\0';
861 return t - save_t;
862 }
863
864 int
qp_decode(ifp,state)865 qp_decode(ifp,state)
866 FILE* ifp;
867 int state;
868 {
869 static FILE* ofp = NULL;
870 int c1, c2;
871
872 if (state == DECODE_DONE) {
873 if (ofp)
874 fclose(ofp);
875 ofp = NULL;
876 return state;
877 }
878
879 if (state == DECODE_START) {
880 char* filename = decode_fix_fname(mime_section->filename);
881 ofp = fopen(filename, FOPEN_WB);
882 if (!ofp)
883 return DECODE_ERROR;
884 erase_line(0);
885 printf("Decoding %s", filename);
886 if (nowait_fork)
887 fflush(stdout);
888 else
889 newline();
890 }
891
892 while ((c1 = mime_getc(ifp)) != EOF) {
893 check_c1:
894 if (c1 == '=') {
895 c1 = mime_getc(ifp);
896 if (c1 == '\n')
897 continue;
898 if (index_hex[(Uchar)c1] == XX) {
899 putc('=', ofp);
900 goto check_c1;
901 }
902 c2 = mime_getc(ifp);
903 if (index_hex[(Uchar)c2] == XX) {
904 putc('=', ofp);
905 putc(c1, ofp);
906 c1 = c2;
907 goto check_c1;
908 }
909 c1 = (index_hex[(Uchar)c1] << 4) | index_hex[(Uchar)c2];
910 if (c1 != '\r')
911 putc(c1, ofp);
912 }
913 else
914 putc(c1, ofp);
915 }
916
917 return DECODE_MAYBEDONE;
918 }
919
920 static Uchar index_b64[256] = {
921 XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
922 XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
923 XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,62, XX,XX,XX,63,
924 52,53,54,55, 56,57,58,59, 60,61,XX,XX, XX,XX,XX,XX,
925 XX, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10, 11,12,13,14,
926 15,16,17,18, 19,20,21,22, 23,24,25,XX, XX,XX,XX,XX,
927 XX,26,27,28, 29,30,31,32, 33,34,35,36, 37,38,39,40,
928 41,42,43,44, 45,46,47,48, 49,50,51,XX, XX,XX,XX,XX,
929 XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
930 XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
931 XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
932 XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
933 XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
934 XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
935 XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
936 XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
937 };
938
939 int
b64_decodestring(t,f)940 b64_decodestring(t, f)
941 char* t;
942 char* f;
943 {
944 char* save_t = t;
945 Uchar ch1, ch2;
946
947 while (*f && *f != '=') {
948 ch1 = index_b64[(Uchar)*f++];
949 if (ch1 == XX)
950 continue;
951 do {
952 if (!*f || *f == '=')
953 goto dbl_break;
954 ch2 = index_b64[(Uchar)*f++];
955 } while (ch2 == XX);
956 *t++ = (ch1 << 2) | (ch2 >> 4);
957 do {
958 if (!*f || *f == '=')
959 goto dbl_break;
960 ch1 = index_b64[(Uchar)*f++];
961 } while (ch1 == XX);
962 *t++ = ((ch2 & 0x0f) << 4) | (ch1 >> 2);
963 do {
964 if (!*f || *f == '=')
965 goto dbl_break;
966 ch2 = index_b64[(Uchar)*f++];
967 } while (ch2 == XX);
968 *t++ = ((ch1 & 0x03) << 6) | ch2;
969 }
970 dbl_break:
971 *t = '\0';
972 return t - save_t;
973 }
974
975 int
b64_decode(ifp,state)976 b64_decode(ifp, state)
977 FILE* ifp;
978 int state;
979 {
980 static FILE* ofp = NULL;
981 int c1, c2, c3, c4;
982
983 if (state == DECODE_DONE) {
984 all_done:
985 if (ofp)
986 fclose(ofp);
987 ofp = NULL;
988 return state;
989 }
990
991 if (state == DECODE_START) {
992 char* filename = decode_fix_fname(mime_section->filename);
993 ofp = fopen(filename, FOPEN_WB);
994 if (!ofp)
995 return DECODE_ERROR;
996 printf("Decoding %s", filename);
997 if (nowait_fork)
998 fflush(stdout);
999 else
1000 newline();
1001 state = DECODE_ACTIVE;
1002 }
1003
1004 while ((c1 = mime_getc(ifp)) != EOF) {
1005 if (c1 != '=' && index_b64[c1] == XX)
1006 continue;
1007 do {
1008 c2 = mime_getc(ifp);
1009 if (c2 == EOF)
1010 return state;
1011 } while (c2 != '=' && index_b64[c2] == XX);
1012 do {
1013 c3 = mime_getc(ifp);
1014 if (c3 == EOF)
1015 return state;
1016 } while (c3 != '=' && index_b64[c3] == XX);
1017 do {
1018 c4 = mime_getc(ifp);
1019 if (c4 == EOF)
1020 return state;
1021 } while (c4 != '=' && index_b64[c4] == XX);
1022 if (c1 == '=' || c2 == '=') {
1023 state = DECODE_DONE;
1024 break;
1025 }
1026 c1 = index_b64[c1];
1027 c2 = index_b64[c2];
1028 c1 = (c1 << 2) | (c2 >> 4);
1029 putc(c1, ofp);
1030 if (c3 == '=') {
1031 state = DECODE_DONE;
1032 break;
1033 }
1034 c3 = index_b64[c3];
1035 c2 = ((c2 & 0x0f) << 4) | (c3 >> 2);
1036 putc(c2, ofp);
1037 if (c4 == '=') {
1038 state = DECODE_DONE;
1039 break;
1040 }
1041 c4 = index_b64[c4];
1042 c3 = ((c3 & 0x03) << 6) | c4;
1043 putc(c3, ofp);
1044 }
1045
1046 if (state == DECODE_DONE)
1047 goto all_done;
1048
1049 return DECODE_MAYBEDONE;
1050 }
1051 #undef XX
1052
1053 static int
mime_getc(fp)1054 mime_getc(fp)
1055 FILE* fp;
1056 {
1057 if (fp)
1058 return fgetc(fp);
1059
1060 if (!mime_getc_line || !*mime_getc_line) {
1061 mime_getc_line = readart(art_line,sizeof art_line);
1062 if (mime_EndOfSection(art_line))
1063 return EOF;
1064 if (!mime_getc_line)
1065 return EOF;
1066 }
1067 return *mime_getc_line++;
1068 }
1069
1070 int
cat_decode(ifp,state)1071 cat_decode(ifp, state)
1072 FILE* ifp;
1073 int state;
1074 {
1075 static FILE* ofp = NULL;
1076
1077 if (state == DECODE_DONE) {
1078 if (ofp)
1079 fclose(ofp);
1080 ofp = NULL;
1081 return state;
1082 }
1083
1084 if (state == DECODE_START) {
1085 char* filename = decode_fix_fname(mime_section->filename);
1086 ofp = fopen(filename, FOPEN_WB);
1087 if (!ofp)
1088 return DECODE_ERROR;
1089 printf("Decoding %s", filename);
1090 if (nowait_fork)
1091 fflush(stdout);
1092 else
1093 newline();
1094 }
1095
1096 if (ifp) {
1097 while (fgets(buf, sizeof buf, ifp))
1098 fputs(buf, ofp);
1099 }
1100 else {
1101 while (readart(buf, sizeof buf)) {
1102 if (mime_EndOfSection(buf))
1103 break;
1104 fputs(buf, ofp);
1105 }
1106 }
1107
1108 return DECODE_MAYBEDONE;
1109 }
1110
1111 static int word_wrap_in_pre, normal_word_wrap, word_wrap;
1112
1113 int
filter_html(t,f)1114 filter_html(t, f)
1115 char* t;
1116 char* f;
1117 {
1118 static char tagword[32];
1119 static int tagword_len;
1120 char* bp;
1121 char* cp;
1122
1123 if (word_wrap_offset < 0) {
1124 normal_word_wrap = tc_COLS - 8;
1125 word_wrap_in_pre = 0;
1126 }
1127 else
1128 word_wrap_in_pre = normal_word_wrap = tc_COLS - word_wrap_offset;
1129
1130 if (normal_word_wrap <= 20)
1131 normal_word_wrap = 0;
1132 if (word_wrap_in_pre <= 20)
1133 word_wrap_in_pre = 0;
1134 word_wrap = (mime_section->html & HF_IN_PRE)? word_wrap_in_pre
1135 : normal_word_wrap;
1136 if (!mime_section->html_line_start)
1137 mime_section->html_line_start = t - artbuf;
1138
1139 if (!mime_section->html_blks) {
1140 mime_section->html_blks = (HBLK*)safemalloc(HTML_MAX_BLOCKS
1141 * sizeof (HBLK));
1142 }
1143
1144 for (bp = t; *f; f++) {
1145 if (mime_section->html & HF_IN_DQUOTE) {
1146 if (*f == '"')
1147 mime_section->html &= ~HF_IN_DQUOTE;
1148 else if (tagword_len < (sizeof tagword) - 1)
1149 tagword[tagword_len++] = *f;
1150 }
1151 else if (mime_section->html & HF_IN_SQUOTE) {
1152 if (*f == '\'')
1153 mime_section->html &= ~HF_IN_SQUOTE;
1154 else if (tagword_len < (sizeof tagword) - 1)
1155 tagword[tagword_len++] = *f;
1156 }
1157 else if (mime_section->html & HF_IN_COMMENT) {
1158 if (*f == '-' && f[1] == '-') {
1159 f++;
1160 mime_section->html &= ~HF_IN_COMMENT;
1161 }
1162 }
1163 else if (mime_section->html & HF_IN_TAG) {
1164 if (*f == '>') {
1165 mime_section->html &= ~HF_IN_TAG;
1166 tagword[tagword_len] = '\0';
1167 if (*tagword == '/')
1168 t = tag_action(t, tagword+1, CLOSING_TAG);
1169 else
1170 t = tag_action(t, tagword, OPENING_TAG);
1171 }
1172 else if (*f == '-' && f[1] == '-') {
1173 f++;
1174 mime_section->html |= HF_IN_COMMENT;
1175 }
1176 else if (*f == '"')
1177 mime_section->html |= HF_IN_DQUOTE;
1178 else if (*f == '\'')
1179 mime_section->html |= HF_IN_SQUOTE;
1180 else if (tagword_len < (sizeof tagword) - 1) {
1181 tagword[tagword_len++] = AT_GREY_SPACE(f)? ' ' : *f;
1182 }
1183 }
1184 else if (*f == '<') {
1185 tagword_len = 0;
1186 mime_section->html |= HF_IN_TAG;
1187 }
1188 else if (mime_section->html & HF_IN_HIDING)
1189 ;
1190 else if (*f == '&') {
1191 t = output_prep(t);
1192 if (strncaseEQ(f+1,"lt;",3)) {
1193 *t++ = '<';
1194 f += 3;
1195 }
1196 else if (strncaseEQ(f+1,"gt;",3)) {
1197 *t++ = '>';
1198 f += 3;
1199 }
1200 else if (strncaseEQ(f+1,"amp;",4)) {
1201 *t++ = '&';
1202 f += 4;
1203 }
1204 else if (strncaseEQ(f+1,"nbsp;",5)) {
1205 *t++ = ' ';
1206 f += 5;
1207 }
1208 else if (strncaseEQ(f+1,"quot;",5)) {
1209 *t++ = '"';
1210 f += 5;
1211 }
1212 else
1213 *t++ = *f;
1214 mime_section->html |= HF_NL_OK|HF_P_OK|HF_SPACE_OK;
1215 }
1216 else if (AT_GREY_SPACE(f) && !(mime_section->html & HF_IN_PRE)) {
1217 /* We don't want to call output_prep() here. */
1218 if (mime_section->html & HF_SPACE_OK) {
1219 mime_section->html &= ~HF_SPACE_OK;
1220 *t++ = ' ';
1221 }
1222 }
1223 else if (*f == '\n') { /* Handle the HF_IN_PRE case */
1224 t = output_prep(t);
1225 mime_section->html |= HF_NL_OK;
1226 t = do_newline(t, HF_NL_OK);
1227 }
1228 else {
1229 t = output_prep(t);
1230 *t++ = *f;
1231 mime_section->html |= HF_NL_OK|HF_P_OK|HF_SPACE_OK;
1232 }
1233
1234 if (word_wrap && t - artbuf - mime_section->html_line_start > tc_COLS) {
1235 char* line_start = mime_section->html_line_start + artbuf;
1236 for (cp = line_start + word_wrap;
1237 cp > line_start && *cp != ' ' && *cp != '\t';
1238 cp--) ;
1239 if (cp == line_start) {
1240 for (cp = line_start + word_wrap;
1241 cp - line_start <= tc_COLS && *cp != ' ' && *cp != '\t';
1242 cp++) ;
1243 if (cp - line_start > tc_COLS) {
1244 mime_section->html_line_start += tc_COLS;
1245 cp = NULL;
1246 }
1247 }
1248 if (cp) {
1249 int flag_save = mime_section->html;
1250 int fudge;
1251 char* s;
1252 mime_section->html |= HF_NL_OK;
1253 cp = line_start = do_newline(cp, HF_NL_OK);
1254 fudge = do_indent((char*)NULL);
1255 while (*cp == ' ' || *cp == '\t') cp++;
1256 if ((fudge -= cp - line_start) != 0) {
1257 if (fudge < 0)
1258 bcopy(cp, cp + fudge, t - cp);
1259 else
1260 for (s = t; s-- != cp; ) s[fudge] = *s;
1261 (void) do_indent(line_start);
1262 t += fudge;
1263 }
1264 mime_section->html = flag_save;
1265 }
1266 }
1267 }
1268 *t = '\0';
1269
1270 return t - bp;
1271 }
1272
1273 static char bullets[3] = {'*', 'o', '+'};
1274 static char letters[2] = {'a', 'A'};
1275 static char roman_letters[] = { 'M', 'D', 'C', 'L', 'X', 'V', 'I'};
1276 static int roman_values[] = {1000, 500, 100, 50, 10, 5, 1 };
1277
1278 static char*
tag_action(t,word,opening_tag)1279 tag_action(t, word, opening_tag)
1280 char* t;
1281 char* word;
1282 bool_int opening_tag;
1283 {
1284 char* cp;
1285 int i, j, tnum, len, itype, ch, cnt, num;
1286 bool match = 0;
1287 HBLK* blks = mime_section->html_blks;
1288
1289 for (cp = word; *cp && *cp != ' '; cp++) ;
1290 len = cp - word;
1291
1292 if (!isalpha(*word))
1293 return t;
1294 ch = isupper(*word)? tolower(*word) : *word;
1295 for (tnum = 0; tnum < LAST_TAG && *tagattr[tnum].name != ch; tnum++) ;
1296 for ( ; tnum < LAST_TAG && *tagattr[tnum].name == ch; tnum++) {
1297 if (len == tagattr[tnum].length
1298 && strncaseEQ(word, tagattr[tnum].name, len)) {
1299 match = 1;
1300 break;
1301 }
1302 }
1303 if (!match)
1304 return t;
1305
1306 if (!opening_tag && !(tagattr[tnum].flags & (TF_BLOCK|TF_HAS_CLOSE)))
1307 return t;
1308
1309 if ((mime_section->html & HF_IN_HIDING)
1310 && (opening_tag || tnum != blks[mime_section->html_blkcnt-1].tnum))
1311 return t;
1312
1313 if (tagattr[tnum].flags & TF_BR)
1314 mime_section->html |= HF_NL_OK;
1315
1316 if (opening_tag) {
1317 if (tagattr[tnum].flags & TF_NL) {
1318 t = output_prep(t);
1319 t = do_newline(t, HF_NL_OK);
1320 }
1321 if ((num = tagattr[tnum].flags & (TF_P|TF_LIST)) == TF_P
1322 || (num == (TF_P|TF_LIST) && !(mime_section->html & HF_COMPACT))) {
1323 t = output_prep(t);
1324 t = do_newline(t, HF_P_OK);
1325 }
1326 if (tagattr[tnum].flags & TF_SPACE) {
1327 if (mime_section->html & HF_SPACE_OK) {
1328 mime_section->html &= ~HF_SPACE_OK;
1329 *t++ = ' ';
1330 }
1331 }
1332 if (tagattr[tnum].flags & TF_TAB) {
1333 if (mime_section->html & HF_NL_OK) {
1334 mime_section->html &= ~HF_SPACE_OK;
1335 *t++ = '\t';
1336 }
1337 }
1338
1339 if ((tagattr[tnum].flags & TF_BLOCK)
1340 && mime_section->html_blkcnt < HTML_MAX_BLOCKS) {
1341 j = mime_section->html_blkcnt++;
1342 blks[j].tnum = tnum;
1343 blks[j].indent = 0;
1344 blks[j].cnt = 0;
1345
1346 if (tagattr[tnum].flags & TF_LIST)
1347 mime_section->html |= HF_COMPACT;
1348 else
1349 mime_section->html &= ~HF_COMPACT;
1350 }
1351 else
1352 j = mime_section->html_blkcnt - 1;
1353
1354 if ((tagattr[tnum].flags & (TF_BLOCK|TF_HIDE)) == (TF_BLOCK|TF_HIDE))
1355 mime_section->html |= HF_IN_HIDING;
1356
1357 switch (tnum) {
1358 case TAG_BLOCKQUOTE:
1359 if (((cp = find_attr(word, "type")) != NULL
1360 && strncaseEQ(cp, "cite", 4))
1361 || ((cp = find_attr(word, "style")) != NULL
1362 && strncaseEQ(cp, "border-left:", 12)))
1363 blks[j].indent = '>';
1364 else
1365 blks[j].indent = ' ';
1366 break;
1367 case TAG_HR:
1368 t = output_prep(t);
1369 *t++ = '-'; *t++ = '-';
1370 mime_section->html |= HF_NL_OK;
1371 t = do_newline(t, HF_NL_OK);
1372 break;
1373 case TAG_IMG:
1374 t = output_prep(t);
1375 if (mime_section->html & HF_SPACE_OK)
1376 *t++ = ' ';
1377 strcpy(t, "[Image] ");
1378 t += 8;
1379 mime_section->html &= ~HF_SPACE_OK;
1380 break;
1381 case TAG_OL:
1382 itype = 4;
1383 if ((cp = find_attr(word, "type")) != NULL) {
1384 switch (*cp) {
1385 case '1': itype = 4; break;
1386 case 'a': itype = 5; break;
1387 case 'A': itype = 6; break;
1388 case 'i': itype = 7; break;
1389 case 'I': itype = 8; break;
1390 }
1391 }
1392 blks[j].indent = itype;
1393 break;
1394 case TAG_UL:
1395 itype = 1;
1396 if ((cp = find_attr(word, "type")) != NULL) {
1397 switch (*cp) {
1398 case 'd': case 'D': itype = 1; break;
1399 case 'c': case 'C': itype = 2; break;
1400 case 's': case 'S': itype = 3; break;
1401 }
1402 }
1403 else {
1404 for (i = 0; i < mime_section->html_blkcnt; i++) {
1405 if (blks[i].indent && blks[i].indent < ' ') {
1406 if (++itype == 3)
1407 break;
1408 }
1409 }
1410 }
1411 blks[j].indent = itype;
1412 break;
1413 case TAG_LI:
1414 t = output_prep(t);
1415 ch = j < 0? ' ' : blks[j].indent;
1416 switch (ch) {
1417 case 1: case 2: case 3:
1418 t[-2] = bullets[ch-1];
1419 break;
1420 case 4:
1421 sprintf(t-4, "%2d. ", ++blks[j].cnt);
1422 if (*t)
1423 t += strlen(t);
1424 break;
1425 case 5: case 6:
1426 cnt = blks[j].cnt++;
1427 if (cnt >= 26*26)
1428 cnt = blks[j].cnt = 0;
1429 if (cnt >= 26)
1430 t[-4] = letters[ch-5] + (cnt / 26) - 1;
1431 t[-3] = letters[ch-5] + (cnt % 26);
1432 t[-2] = '.';
1433 break;
1434 case 7:
1435 for (i = 0; i < 7; i++) {
1436 if (isupper(roman_letters[i]))
1437 roman_letters[i] = tolower(roman_letters[i]);
1438 }
1439 goto roman_numerals;
1440 case 8:
1441 for (i = 0; i < 7; i++) {
1442 if (islower(roman_letters[i]))
1443 roman_letters[i] = toupper(roman_letters[i]);
1444 }
1445 roman_numerals:
1446 cp = t - 6;
1447 cnt = ++blks[j].cnt;
1448 for (i = 0; cnt && i < 7; i++) {
1449 num = roman_values[i];
1450 while (cnt >= num) {
1451 *cp++ = roman_letters[i];
1452 cnt -= num;
1453 }
1454 j = (i | 1) + 1;
1455 if (j < 7) {
1456 num -= roman_values[j];
1457 if (cnt >= num) {
1458 *cp++ = roman_letters[j];
1459 *cp++ = roman_letters[i];
1460 cnt -= num;
1461 }
1462 }
1463 }
1464 if (cp < t - 2) {
1465 t -= 2;
1466 for (cnt = t - cp; cp-- != t - 4; ) cp[cnt] = *cp;
1467 while (cnt--) *++cp = ' ';
1468 }
1469 else
1470 t = cp;
1471 *t++ = '.';
1472 *t++ = ' ';
1473 break;
1474 default:
1475 *t++ = '*';
1476 *t++ = ' ';
1477 break;
1478 }
1479 mime_section->html |= HF_NL_OK|HF_P_OK;
1480 break;
1481 case TAG_PRE:
1482 mime_section->html |= HF_IN_PRE;
1483 word_wrap = word_wrap_in_pre;
1484 break;
1485 }
1486 }
1487 else {
1488 if ((tagattr[tnum].flags & TF_BLOCK)) {
1489 for (j = mime_section->html_blkcnt; j--; ) {
1490 if (blks[j].tnum == tnum) {
1491 for (i = mime_section->html_blkcnt; --i > j; ) {
1492 t = tag_action(t, tagattr[blks[i].tnum].name,
1493 CLOSING_TAG);
1494 }
1495 mime_section->html_blkcnt = j;
1496 break;
1497 }
1498 }
1499 mime_section->html &= ~HF_IN_HIDING;
1500 while (j-- > 0) {
1501 if (tagattr[blks[j].tnum].flags & TF_HIDE) {
1502 mime_section->html |= HF_IN_HIDING;
1503 break;
1504 }
1505 }
1506 }
1507
1508 j = mime_section->html_blkcnt - 1;
1509 if (j >= 0 && (tagattr[blks[j].tnum].flags & TF_LIST))
1510 mime_section->html |= HF_COMPACT;
1511 else
1512 mime_section->html &= ~HF_COMPACT;
1513
1514 if ((tagattr[tnum].flags & TF_NL) && (mime_section->html & HF_NL_OK)) {
1515 mime_section->html |= HF_QUEUED_NL;
1516 mime_section->html &= ~HF_SPACE_OK;
1517 }
1518 if ((num = tagattr[tnum].flags & (TF_P|TF_LIST)) == TF_P
1519 || (num == (TF_P|TF_LIST) && !(mime_section->html & HF_COMPACT))) {
1520 if (mime_section->html & HF_P_OK) {
1521 mime_section->html |= HF_QUEUED_P;
1522 mime_section->html &= ~HF_SPACE_OK;
1523 }
1524 }
1525
1526 switch (tnum) {
1527 case TAG_PRE:
1528 mime_section->html &= ~HF_IN_PRE;
1529 word_wrap = normal_word_wrap;
1530 break;
1531 }
1532 }
1533
1534 return t;
1535 }
1536
1537 static char*
output_prep(t)1538 output_prep(t)
1539 char* t;
1540 {
1541 if (mime_section->html & HF_QUEUED_P) {
1542 mime_section->html &= ~HF_QUEUED_P;
1543 t = do_newline(t, HF_P_OK);
1544 }
1545 if (mime_section->html & HF_QUEUED_NL) {
1546 mime_section->html &= ~HF_QUEUED_NL;
1547 t = do_newline(t, HF_NL_OK);
1548 }
1549 return t + do_indent(t);
1550 }
1551
1552 static char*
do_newline(t,flag)1553 do_newline(t, flag)
1554 char* t;
1555 int flag;
1556 {
1557 if (mime_section->html & flag) {
1558 mime_section->html &= ~(flag|HF_SPACE_OK);
1559 t += do_indent(t);
1560 *t++ = '\n';
1561 mime_section->html_line_start = t - artbuf;
1562 mime_section->html |= HF_NEED_INDENT;
1563 }
1564 return t;
1565 }
1566
1567 static int
do_indent(t)1568 do_indent(t)
1569 char* t;
1570 {
1571 HBLK* blks;
1572 int j, ch, spaces, len = 0;
1573
1574 if (!(mime_section->html & HF_NEED_INDENT))
1575 return len;
1576
1577 if (t)
1578 mime_section->html &= ~HF_NEED_INDENT;
1579
1580 if ((blks = mime_section->html_blks) != NULL) {
1581 for (j = 0; j < mime_section->html_blkcnt; j++) {
1582 if ((ch = blks[j].indent) != 0) {
1583 switch (ch) {
1584 case '>':
1585 spaces = 1;
1586 break;
1587 case ' ':
1588 spaces = 3;
1589 break;
1590 case 7: case 8:
1591 ch = ' ';
1592 spaces = 5;
1593 break;
1594 default:
1595 ch = ' ';
1596 spaces = 3;
1597 break;
1598 }
1599 len += spaces + 1;
1600 if (len > 64) {
1601 len -= spaces + 1;
1602 break;
1603 }
1604 if (t) {
1605 *t++ = ch;
1606 while (spaces--)
1607 *t++ = ' ';
1608 }
1609 }
1610 }
1611 }
1612
1613 return len;
1614 }
1615
1616 static char*
find_attr(str,attr)1617 find_attr(str, attr)
1618 char* str;
1619 char* attr;
1620 {
1621 int len = strlen(attr);
1622 char* cp = str;
1623 char* s;
1624
1625 while ((cp = index(cp+1, '=')) != NULL) {
1626 for (s = cp; s[-1] == ' '; s--) ;
1627 while (cp[1] == ' ') cp++;
1628 if (s - str > len && s[-len-1] == ' ' && strncaseEQ(s-len,attr,len))
1629 return cp+1;
1630 }
1631 return NULL;
1632 }
1633