Intial commit
[profile/ivi/w3m.git] / file.c
1 /* $Id: file.c,v 1.254 2007/05/23 15:06:05 inu Exp $ */
2 #include "fm.h"
3 #include <sys/types.h>
4 #include "myctype.h"
5 #include <signal.h>
6 #include <setjmp.h>
7 #if defined(HAVE_WAITPID) || defined(HAVE_WAIT3)
8 #include <sys/wait.h>
9 #endif
10 #include <stdio.h>
11 #include <time.h>
12 #include <sys/stat.h>
13 #include <fcntl.h>
14 #include <utime.h>
15 /* foo */
16
17 #include "html.h"
18 #include "parsetagx.h"
19 #include "local.h"
20 #include "regex.h"
21
22 #ifndef max
23 #define max(a,b)        ((a) > (b) ? (a) : (b))
24 #endif                          /* not max */
25 #ifndef min
26 #define min(a,b)        ((a) > (b) ? (b) : (a))
27 #endif                          /* not min */
28
29 static int frame_source = 0;
30
31 static char *guess_filename(char *file);
32 static int _MoveFile(char *path1, char *path2);
33 static void uncompress_stream(URLFile *uf, char **src);
34 static FILE *lessopen_stream(char *path);
35 static Buffer *loadcmdout(char *cmd,
36                           Buffer *(*loadproc) (URLFile *, Buffer *),
37                           Buffer *defaultbuf);
38 #ifndef USE_ANSI_COLOR
39 #define addnewline(a,b,c,d,e,f,g) _addnewline(a,b,c,e,f,g)
40 #endif
41 static void addnewline(Buffer *buf, char *line, Lineprop *prop,
42                        Linecolor *color, int pos, int width, int nlines);
43 static void addLink(Buffer *buf, struct parsed_tag *tag);
44
45 static JMP_BUF AbortLoading;
46
47 static struct table *tables[MAX_TABLE];
48 static struct table_mode table_mode[MAX_TABLE];
49
50 #ifdef USE_IMAGE
51 static ParsedURL *cur_baseURL = NULL;
52 #ifdef USE_M17N
53 static char cur_document_charset;
54 #endif
55 #endif
56
57 static Str cur_title;
58 static Str cur_select;
59 static Str select_str;
60 static int select_is_multiple;
61 static int n_selectitem;
62 static Str cur_option;
63 static Str cur_option_value;
64 static Str cur_option_label;
65 static int cur_option_selected;
66 static int cur_status;
67 #ifdef MENU_SELECT
68 /* menu based <select>  */
69 FormSelectOption *select_option;
70 static int max_select = MAX_SELECT;
71 static int n_select;
72 static int cur_option_maxwidth;
73 #endif                          /* MENU_SELECT */
74
75 static Str cur_textarea;
76 Str *textarea_str;
77 static int cur_textarea_size;
78 static int cur_textarea_rows;
79 static int cur_textarea_readonly;
80 static int n_textarea;
81 static int ignore_nl_textarea;
82 static int max_textarea = MAX_TEXTAREA;
83
84 static int http_response_code;
85
86 #ifdef USE_M17N
87 static wc_ces content_charset = 0;
88 static wc_ces meta_charset = 0;
89 static char *check_charset(char *p);
90 static char *check_accept_charset(char *p);
91 #endif
92
93 #define set_prevchar(x,y,n) Strcopy_charp_n((x),(y),(n))
94 #define set_space_to_prevchar(x) Strcopy_charp_n((x)," ",1)
95
96 struct link_stack {
97     int cmd;
98     short offset;
99     short pos;
100     struct link_stack *next;
101 };
102
103 static struct link_stack *link_stack = NULL;
104
105 #define FORMSTACK_SIZE 10
106 #define FRAMESTACK_SIZE 10
107
108 #ifdef USE_NNTP
109 #define Str_news_endline(s) ((s)->ptr[0]=='.'&&((s)->ptr[1]=='\n'||(s)->ptr[1]=='\r'||(s)->ptr[1]=='\0'))
110 #endif                          /* USE_NNTP */
111
112 #define INITIAL_FORM_SIZE 10
113 static FormList **forms;
114 static int *form_stack;
115 static int form_max = -1;
116 static int forms_size = 0;
117 #define cur_form_id ((form_sp >= 0)? form_stack[form_sp] : -1)
118 static int form_sp = 0;
119
120 static clen_t current_content_length;
121
122 static int cur_hseq;
123 #ifdef USE_IMAGE
124 static int cur_iseq;
125 #endif
126
127 #define MAX_UL_LEVEL    9
128 #define UL_SYMBOL(x)    (N_GRAPH_SYMBOL + (x))
129 #define UL_SYMBOL_DISC          UL_SYMBOL(9)
130 #define UL_SYMBOL_CIRCLE        UL_SYMBOL(10)
131 #define UL_SYMBOL_SQUARE        UL_SYMBOL(11)
132 #define IMG_SYMBOL              UL_SYMBOL(12)
133 #define HR_SYMBOL       26
134
135 #ifdef USE_COOKIE
136 /* This array should be somewhere else */
137 /* FIXME: gettextize? */
138 char *violations[COO_EMAX] = {
139     "internal error",
140     "tail match failed",
141     "wrong number of dots",
142     "RFC 2109 4.3.2 rule 1",
143     "RFC 2109 4.3.2 rule 2.1",
144     "RFC 2109 4.3.2 rule 2.2",
145     "RFC 2109 4.3.2 rule 3",
146     "RFC 2109 4.3.2 rule 4",
147     "RFC XXXX 4.3.2 rule 5"
148 };
149 #endif
150
151 /* *INDENT-OFF* */
152 static struct compression_decoder {
153     int type;
154     char *ext;
155     char *mime_type;
156     int auxbin_p;
157     char *cmd;
158     char *name;
159     char *encoding;
160     char *encodings[4];
161 } compression_decoders[] = {
162     { CMP_COMPRESS, ".gz", "application/x-gzip",
163       0, GUNZIP_CMDNAME, GUNZIP_NAME, "gzip", 
164       {"gzip", "x-gzip", NULL} }, 
165     { CMP_COMPRESS, ".Z", "application/x-compress",
166       0, GUNZIP_CMDNAME, GUNZIP_NAME, "compress",
167       {"compress", "x-compress", NULL} }, 
168     { CMP_BZIP2, ".bz2", "application/x-bzip",
169       0, BUNZIP2_CMDNAME, BUNZIP2_NAME, "bzip, bzip2",
170       {"x-bzip", "bzip", "bzip2", NULL} }, 
171     { CMP_DEFLATE, ".deflate", "application/x-deflate",
172       1, INFLATE_CMDNAME, INFLATE_NAME, "deflate",
173       {"deflate", "x-deflate", NULL} }, 
174     { CMP_NOCOMPRESS, NULL, NULL, 0, NULL, NULL, NULL, {NULL}},
175 };
176 /* *INDENT-ON* */
177
178 #define SAVE_BUF_SIZE 1536
179
180 static MySignalHandler
181 KeyAbort(SIGNAL_ARG)
182 {
183     LONGJMP(AbortLoading, 1);
184     SIGNAL_RETURN;
185 }
186
187 static void
188 UFhalfclose(URLFile *f)
189 {
190     switch (f->scheme) {
191     case SCM_FTP:
192         closeFTP();
193         break;
194 #ifdef USE_NNTP
195     case SCM_NEWS:
196     case SCM_NNTP:
197         closeNews();
198         break;
199 #endif
200     default:
201         UFclose(f);
202         break;
203     }
204 }
205
206 int
207 currentLn(Buffer *buf)
208 {
209     if (buf->currentLine)
210         /*     return buf->currentLine->real_linenumber + 1;      */
211         return buf->currentLine->linenumber + 1;
212     else
213         return 1;
214 }
215
216 static Buffer *
217 loadSomething(URLFile *f,
218               char *path,
219               Buffer *(*loadproc) (URLFile *, Buffer *), Buffer *defaultbuf)
220 {
221     Buffer *buf;
222
223     if ((buf = loadproc(f, defaultbuf)) == NULL)
224         return NULL;
225
226     buf->filename = path;
227     if (buf->buffername == NULL || buf->buffername[0] == '\0') {
228         buf->buffername = checkHeader(buf, "Subject:");
229         if (buf->buffername == NULL)
230             buf->buffername = conv_from_system(lastFileName(path));
231     }
232     if (buf->currentURL.scheme == SCM_UNKNOWN)
233         buf->currentURL.scheme = f->scheme;
234     buf->real_scheme = f->scheme;
235     if (f->scheme == SCM_LOCAL && buf->sourcefile == NULL)
236         buf->sourcefile = path;
237     return buf;
238 }
239
240 int
241 dir_exist(char *path)
242 {
243     struct stat stbuf;
244
245     if (path == NULL || *path == '\0')
246         return 0;
247     if (stat(path, &stbuf) == -1)
248         return 0;
249     return IS_DIRECTORY(stbuf.st_mode);
250 }
251
252 static int
253 is_dump_text_type(char *type)
254 {
255     struct mailcap *mcap;
256     return (type && (mcap = searchExtViewer(type)) &&
257             (mcap->flags & (MAILCAP_HTMLOUTPUT | MAILCAP_COPIOUSOUTPUT)));
258 }
259
260 static int
261 is_text_type(char *type)
262 {
263     return (type == NULL || type[0] == '\0' ||
264             strncasecmp(type, "text/", 5) == 0 ||
265             strncasecmp(type, "message/", sizeof("message/") - 1) == 0);
266 }
267
268 static int
269 is_plain_text_type(char *type)
270 {
271     return ((type && strcasecmp(type, "text/plain") == 0) ||
272             (is_text_type(type) && !is_dump_text_type(type)));
273 }
274
275 static void
276 check_compression(char *path, URLFile *uf)
277 {
278     int len;
279     struct compression_decoder *d;
280
281     if (path == NULL)
282         return;
283
284     len = strlen(path);
285     uf->compression = CMP_NOCOMPRESS;
286     for (d = compression_decoders; d->type != CMP_NOCOMPRESS; d++) {
287         int elen;
288         if (d->ext == NULL)
289             continue;
290         elen = strlen(d->ext);
291         if (len > elen && strcasecmp(&path[len - elen], d->ext) == 0) {
292             uf->compression = d->type;
293             uf->guess_type = d->mime_type;
294             break;
295         }
296     }
297 }
298
299 static char *
300 compress_application_type(int compression)
301 {
302     struct compression_decoder *d;
303
304     for (d = compression_decoders; d->type != CMP_NOCOMPRESS; d++) {
305         if (d->type == compression)
306             return d->mime_type;
307     }
308     return NULL;
309 }
310
311 static char *
312 uncompressed_file_type(char *path, char **ext)
313 {
314     int len, slen;
315     Str fn;
316     char *t0;
317     struct compression_decoder *d;
318
319     if (path == NULL)
320         return NULL;
321
322     slen = 0;
323     len = strlen(path);
324     for (d = compression_decoders; d->type != CMP_NOCOMPRESS; d++) {
325         if (d->ext == NULL)
326             continue;
327         slen = strlen(d->ext);
328         if (len > slen && strcasecmp(&path[len - slen], d->ext) == 0)
329             break;
330     }
331     if (d->type == CMP_NOCOMPRESS)
332         return NULL;
333
334     fn = Strnew_charp(path);
335     Strshrink(fn, slen);
336     if (ext)
337         *ext = filename_extension(fn->ptr, 0);
338     t0 = guessContentType(fn->ptr);
339     if (t0 == NULL)
340         t0 = "text/plain";
341     return t0;
342 }
343
344 static int
345 setModtime(char *path, time_t modtime)
346 {
347     struct utimbuf t;
348     struct stat st;
349
350     if (stat(path, &st) == 0)
351         t.actime = st.st_atime;
352     else
353         t.actime = time(NULL);
354     t.modtime = modtime;
355     return utime(path, &t);
356 }
357
358 void
359 examineFile(char *path, URLFile *uf)
360 {
361     struct stat stbuf;
362
363     uf->guess_type = NULL;
364     if (path == NULL || *path == '\0' ||
365         stat(path, &stbuf) == -1 || NOT_REGULAR(stbuf.st_mode)) {
366         uf->stream = NULL;
367         return;
368     }
369     uf->stream = openIS(path);
370     if (!do_download) {
371         if (use_lessopen && getenv("LESSOPEN") != NULL) {
372             FILE *fp;
373             uf->guess_type = guessContentType(path);
374             if (uf->guess_type == NULL)
375                 uf->guess_type = "text/plain";
376             if (strcasecmp(uf->guess_type, "text/html") == 0)
377                 return;
378             if ((fp = lessopen_stream(path))) {
379                 UFclose(uf);
380                 uf->stream = newFileStream(fp, (void (*)())pclose);
381                 uf->guess_type = "text/plain";
382                 return;
383             }
384         }
385         check_compression(path, uf);
386         if (uf->compression != CMP_NOCOMPRESS) {
387             char *ext = uf->ext;
388             char *t0 = uncompressed_file_type(path, &ext);
389             uf->guess_type = t0;
390             uf->ext = ext;
391             uncompress_stream(uf, NULL);
392             return;
393         }
394     }
395 }
396
397 #define S_IXANY (S_IXUSR|S_IXGRP|S_IXOTH)
398
399 int
400 check_command(char *cmd, int auxbin_p)
401 {
402     static char *path = NULL;
403     Str dirs;
404     char *p, *np;
405     Str pathname;
406     struct stat st;
407
408     if (path == NULL)
409         path = getenv("PATH");
410     if (auxbin_p)
411         dirs = Strnew_charp(w3m_auxbin_dir());
412     else
413         dirs = Strnew_charp(path);
414     for (p = dirs->ptr; p != NULL; p = np) {
415         np = strchr(p, PATH_SEPARATOR);
416         if (np)
417             *np++ = '\0';
418         pathname = Strnew();
419         Strcat_charp(pathname, p);
420         Strcat_char(pathname, '/');
421         Strcat_charp(pathname, cmd);
422         if (stat(pathname->ptr, &st) == 0 && S_ISREG(st.st_mode)
423             && (st.st_mode & S_IXANY) != 0)
424             return 1;
425     }
426     return 0;
427 }
428
429 char *
430 acceptableEncoding()
431 {
432     static Str encodings = NULL;
433     struct compression_decoder *d;
434     TextList *l;
435     char *p;
436
437     if (encodings != NULL)
438         return encodings->ptr;
439     l = newTextList();
440     for (d = compression_decoders; d->type != CMP_NOCOMPRESS; d++) {
441         if (check_command(d->cmd, d->auxbin_p)) {
442             pushText(l, d->encoding);
443         }
444     }
445     encodings = Strnew();
446     while ((p = popText(l)) != NULL) {
447         if (encodings->length)
448             Strcat_charp(encodings, ", ");
449         Strcat_charp(encodings, p);
450     }
451     return encodings->ptr;
452 }
453
454 /* 
455  * convert line
456  */
457 #ifdef USE_M17N
458 Str
459 convertLine(URLFile *uf, Str line, int mode, wc_ces * charset,
460             wc_ces doc_charset)
461 #else
462 Str
463 convertLine0(URLFile *uf, Str line, int mode)
464 #endif
465 {
466 #ifdef USE_M17N
467     line = wc_Str_conv_with_detect(line, charset, doc_charset, InnerCharset);
468 #endif
469     if (mode != RAW_MODE)
470         cleanup_line(line, mode);
471 #ifdef USE_NNTP
472     if (uf && uf->scheme == SCM_NEWS)
473         Strchop(line);
474 #endif                          /* USE_NNTP */
475     return line;
476 }
477
478 /* 
479  * loadFile: load file to buffer
480  */
481 Buffer *
482 loadFile(char *path)
483 {
484     Buffer *buf;
485     URLFile uf;
486     init_stream(&uf, SCM_LOCAL, NULL);
487     examineFile(path, &uf);
488     if (uf.stream == NULL)
489         return NULL;
490     buf = newBuffer(INIT_BUFFER_WIDTH);
491     current_content_length = 0;
492 #ifdef USE_M17N
493     content_charset = 0;
494 #endif
495     buf = loadSomething(&uf, path, loadBuffer, buf);
496     UFclose(&uf);
497     return buf;
498 }
499
500 int
501 matchattr(char *p, char *attr, int len, Str *value)
502 {
503     int quoted;
504     char *q = NULL;
505
506     if (strncasecmp(p, attr, len) == 0) {
507         p += len;
508         SKIP_BLANKS(p);
509         if (value) {
510             *value = Strnew();
511             if (*p == '=') {
512                 p++;
513                 SKIP_BLANKS(p);
514                 quoted = 0;
515                 while (!IS_ENDL(*p) && (quoted || *p != ';')) {
516                     if (!IS_SPACE(*p))
517                         q = p;
518                     if (*p == '"')
519                         quoted = (quoted) ? 0 : 1;
520                     else
521                         Strcat_char(*value, *p);
522                     p++;
523                 }
524                 if (q)
525                     Strshrink(*value, p - q - 1);
526             }
527             return 1;
528         }
529         else {
530             if (IS_ENDT(*p)) {
531                 return 1;
532             }
533         }
534     }
535     return 0;
536 }
537
538 #ifdef USE_IMAGE
539 #ifdef USE_XFACE
540 static char *
541 xface2xpm(char *xface)
542 {
543     Image image;
544     ImageCache *cache;
545     FILE *f;
546     struct stat st;
547
548     SKIP_BLANKS(xface);
549     image.url = xface;
550     image.ext = ".xpm";
551     image.width = 48;
552     image.height = 48;
553     image.cache = NULL;
554     cache = getImage(&image, NULL, IMG_FLAG_AUTO);
555     if (cache->loaded & IMG_FLAG_LOADED && !stat(cache->file, &st))
556         return cache->file;
557     cache->loaded = IMG_FLAG_ERROR;
558
559     f = popen(Sprintf("%s > %s", shell_quote(auxbinFile(XFACE2XPM)),
560                       shell_quote(cache->file))->ptr, "w");
561     if (!f)
562         return NULL;
563     fputs(xface, f);
564     pclose(f);
565     if (stat(cache->file, &st) || !st.st_size)
566         return NULL;
567     cache->loaded = IMG_FLAG_LOADED | IMG_FLAG_DONT_REMOVE;
568     cache->index = 0;
569     return cache->file;
570 }
571 #endif
572 #endif
573
574 void
575 readHeader(URLFile *uf, Buffer *newBuf, int thru, ParsedURL *pu)
576 {
577     char *p, *q;
578 #ifdef USE_COOKIE
579     char *emsg;
580 #endif
581     char c;
582     Str lineBuf2 = NULL;
583     Str tmp;
584     TextList *headerlist;
585 #ifdef USE_M17N
586     wc_ces charset = WC_CES_US_ASCII, mime_charset;
587 #endif
588     char *tmpf;
589     FILE *src = NULL;
590     Lineprop *propBuffer;
591
592     headerlist = newBuf->document_header = newTextList();
593     if (uf->scheme == SCM_HTTP
594 #ifdef USE_SSL
595         || uf->scheme == SCM_HTTPS
596 #endif                          /* USE_SSL */
597         )
598         http_response_code = -1;
599     else
600         http_response_code = 0;
601
602     if (thru && !newBuf->header_source
603 #ifdef USE_IMAGE
604         && !image_source
605 #endif
606         ) {
607         tmpf = tmpfname(TMPF_DFL, NULL)->ptr;
608         src = fopen(tmpf, "w");
609         if (src)
610             newBuf->header_source = tmpf;
611     }
612     while ((tmp = StrmyUFgets(uf))->length) {
613 #ifdef USE_NNTP
614         if (uf->scheme == SCM_NEWS && tmp->ptr[0] == '.')
615             Strshrinkfirst(tmp, 1);
616 #endif
617         if(w3m_reqlog){
618             FILE *ff;
619             ff = fopen(w3m_reqlog, "a");
620             Strfputs(tmp, ff);
621             fclose(ff);
622         }
623         if (src)
624             Strfputs(tmp, src);
625         cleanup_line(tmp, HEADER_MODE);
626         if (tmp->ptr[0] == '\n' || tmp->ptr[0] == '\r' || tmp->ptr[0] == '\0') {
627             if (!lineBuf2)
628                 /* there is no header */
629                 break;
630             /* last header */
631         }
632         else if (!(w3m_dump & DUMP_HEAD)) {
633             if (lineBuf2) {
634                 Strcat(lineBuf2, tmp);
635             }
636             else {
637                 lineBuf2 = tmp;
638             }
639             c = UFgetc(uf);
640             UFundogetc(uf);
641             if (c == ' ' || c == '\t')
642                 /* header line is continued */
643                 continue;
644             lineBuf2 = decodeMIME(lineBuf2, &mime_charset);
645             lineBuf2 = convertLine(NULL, lineBuf2, RAW_MODE,
646                                    mime_charset ? &mime_charset : &charset,
647                                    mime_charset ? mime_charset
648                                    : DocumentCharset);
649             /* separated with line and stored */
650             tmp = Strnew_size(lineBuf2->length);
651             for (p = lineBuf2->ptr; *p; p = q) {
652                 for (q = p; *q && *q != '\r' && *q != '\n'; q++) ;
653                 lineBuf2 = checkType(Strnew_charp_n(p, q - p), &propBuffer,
654                                      NULL);
655                 Strcat(tmp, lineBuf2);
656                 if (thru)
657                     addnewline(newBuf, lineBuf2->ptr, propBuffer, NULL,
658                                lineBuf2->length, FOLD_BUFFER_WIDTH, -1);
659                 for (; *q && (*q == '\r' || *q == '\n'); q++) ;
660             }
661 #ifdef USE_IMAGE
662             if (thru && activeImage && displayImage) {
663                 Str src = NULL;
664                 if (!strncasecmp(tmp->ptr, "X-Image-URL:", 12)) {
665                     tmpf = &tmp->ptr[12];
666                     SKIP_BLANKS(tmpf);
667                     src = Strnew_m_charp("<img src=\"", html_quote(tmpf),
668                                          "\" alt=\"X-Image-URL\">", NULL);
669                 }
670 #ifdef USE_XFACE
671                 else if (!strncasecmp(tmp->ptr, "X-Face:", 7)) {
672                     tmpf = xface2xpm(&tmp->ptr[7]);
673                     if (tmpf)
674                         src = Strnew_m_charp("<img src=\"file:",
675                                              html_quote(tmpf),
676                                              "\" alt=\"X-Face\"",
677                                              " width=48 height=48>", NULL);
678                 }
679 #endif
680                 if (src) {
681                     URLFile f;
682                     Line *l;
683 #ifdef USE_M17N
684                     wc_ces old_charset = newBuf->document_charset;
685 #endif
686                     init_stream(&f, SCM_LOCAL, newStrStream(src));
687                     loadHTMLstream(&f, newBuf, NULL, TRUE);
688                     for (l = newBuf->lastLine; l && l->real_linenumber;
689                          l = l->prev)
690                         l->real_linenumber = 0;
691 #ifdef USE_M17N
692                     newBuf->document_charset = old_charset;
693 #endif
694                 }
695             }
696 #endif
697             lineBuf2 = tmp;
698         }
699         else {
700             lineBuf2 = tmp;
701         }
702         if ((uf->scheme == SCM_HTTP
703 #ifdef USE_SSL
704              || uf->scheme == SCM_HTTPS
705 #endif                          /* USE_SSL */
706             ) && http_response_code == -1) {
707             p = lineBuf2->ptr;
708             while (*p && !IS_SPACE(*p))
709                 p++;
710             while (*p && IS_SPACE(*p))
711                 p++;
712             http_response_code = atoi(p);
713             if (fmInitialized) {
714                 message(lineBuf2->ptr, 0, 0);
715                 refresh();
716             }
717         }
718         if (!strncasecmp(lineBuf2->ptr, "content-transfer-encoding:", 26)) {
719             p = lineBuf2->ptr + 26;
720             while (IS_SPACE(*p))
721                 p++;
722             if (!strncasecmp(p, "base64", 6))
723                 uf->encoding = ENC_BASE64;
724             else if (!strncasecmp(p, "quoted-printable", 16))
725                 uf->encoding = ENC_QUOTE;
726             else if (!strncasecmp(p, "uuencode", 8) ||
727                      !strncasecmp(p, "x-uuencode", 10))
728                 uf->encoding = ENC_UUENCODE;
729             else
730                 uf->encoding = ENC_7BIT;
731         }
732         else if (!strncasecmp(lineBuf2->ptr, "content-encoding:", 17)) {
733             struct compression_decoder *d;
734             p = lineBuf2->ptr + 17;
735             while (IS_SPACE(*p))
736                 p++;
737             uf->compression = CMP_NOCOMPRESS;
738             for (d = compression_decoders; d->type != CMP_NOCOMPRESS; d++) {
739                 char **e;
740                 for (e = d->encodings; *e != NULL; e++) {
741                     if (strncasecmp(p, *e, strlen(*e)) == 0) {
742                         uf->compression = d->type;
743                         break;
744                     }
745                 }
746                 if (uf->compression != CMP_NOCOMPRESS)
747                     break;
748             }
749             uf->content_encoding = uf->compression;
750         }
751 #ifdef USE_COOKIE
752         else if (use_cookie && accept_cookie &&
753                  pu && check_cookie_accept_domain(pu->host) &&
754                  (!strncasecmp(lineBuf2->ptr, "Set-Cookie:", 11) ||
755                   !strncasecmp(lineBuf2->ptr, "Set-Cookie2:", 12))) {
756             Str name = Strnew(), value = Strnew(), domain = NULL, path = NULL,
757                 comment = NULL, commentURL = NULL, port = NULL, tmp2;
758             int version, quoted, flag = 0;
759             time_t expires = (time_t) - 1;
760
761             q = NULL;
762             if (lineBuf2->ptr[10] == '2') {
763                 p = lineBuf2->ptr + 12;
764                 version = 1;
765             }
766             else {
767                 p = lineBuf2->ptr + 11;
768                 version = 0;
769             }
770 #ifdef DEBUG
771             fprintf(stderr, "Set-Cookie: [%s]\n", p);
772 #endif                          /* DEBUG */
773             SKIP_BLANKS(p);
774             while (*p != '=' && !IS_ENDT(*p))
775                 Strcat_char(name, *(p++));
776             Strremovetrailingspaces(name);
777             if (*p == '=') {
778                 p++;
779                 SKIP_BLANKS(p);
780                 quoted = 0;
781                 while (!IS_ENDL(*p) && (quoted || *p != ';')) {
782                     if (!IS_SPACE(*p))
783                         q = p;
784                     if (*p == '"')
785                         quoted = (quoted) ? 0 : 1;
786                     Strcat_char(value, *(p++));
787                 }
788                 if (q)
789                     Strshrink(value, p - q - 1);
790             }
791             while (*p == ';') {
792                 p++;
793                 SKIP_BLANKS(p);
794                 if (matchattr(p, "expires", 7, &tmp2)) {
795                     /* version 0 */
796                     expires = mymktime(tmp2->ptr);
797                 }
798                 else if (matchattr(p, "max-age", 7, &tmp2)) {
799                     /* XXX Is there any problem with max-age=0? (RFC 2109 ss. 4.2.1, 4.2.2 */
800                     expires = time(NULL) + atol(tmp2->ptr);
801                 }
802                 else if (matchattr(p, "domain", 6, &tmp2)) {
803                     domain = tmp2;
804                 }
805                 else if (matchattr(p, "path", 4, &tmp2)) {
806                     path = tmp2;
807                 }
808                 else if (matchattr(p, "secure", 6, NULL)) {
809                     flag |= COO_SECURE;
810                 }
811                 else if (matchattr(p, "comment", 7, &tmp2)) {
812                     comment = tmp2;
813                 }
814                 else if (matchattr(p, "version", 7, &tmp2)) {
815                     version = atoi(tmp2->ptr);
816                 }
817                 else if (matchattr(p, "port", 4, &tmp2)) {
818                     /* version 1, Set-Cookie2 */
819                     port = tmp2;
820                 }
821                 else if (matchattr(p, "commentURL", 10, &tmp2)) {
822                     /* version 1, Set-Cookie2 */
823                     commentURL = tmp2;
824                 }
825                 else if (matchattr(p, "discard", 7, NULL)) {
826                     /* version 1, Set-Cookie2 */
827                     flag |= COO_DISCARD;
828                 }
829                 quoted = 0;
830                 while (!IS_ENDL(*p) && (quoted || *p != ';')) {
831                     if (*p == '"')
832                         quoted = (quoted) ? 0 : 1;
833                     p++;
834                 }
835             }
836             if (pu && name->length > 0) {
837                 int err;
838                 if (show_cookie) {
839                     if (flag & COO_SECURE)
840                         disp_message_nsec("Received a secured cookie", FALSE, 1,
841                                       TRUE, FALSE);
842                     else
843                         disp_message_nsec(Sprintf("Received cookie: %s=%s",
844                                               name->ptr, value->ptr)->ptr,
845                                       FALSE, 1, TRUE, FALSE);
846                 }
847                 err =
848                     add_cookie(pu, name, value, expires, domain, path, flag,
849                                comment, version, port, commentURL);
850                 if (err) {
851                     char *ans = (accept_bad_cookie == ACCEPT_BAD_COOKIE_ACCEPT)
852                         ? "y" : NULL;
853                     if (fmInitialized && (err & COO_OVERRIDE_OK) &&
854                         accept_bad_cookie == ACCEPT_BAD_COOKIE_ASK) {
855                         Str msg = Sprintf("Accept bad cookie from %s for %s?",
856                                           pu->host,
857                                           ((domain && domain->ptr)
858                                            ? domain->ptr : "<localdomain>"));
859                         if (msg->length > COLS - 10)
860                             Strshrink(msg, msg->length - (COLS - 10));
861                         Strcat_charp(msg, " (y/n)");
862                         ans = inputAnswer(msg->ptr);
863                     }
864                     if (ans == NULL || TOLOWER(*ans) != 'y' ||
865                         (err =
866                          add_cookie(pu, name, value, expires, domain, path,
867                                     flag | COO_OVERRIDE, comment, version,
868                                     port, commentURL))) {
869                         err = (err & ~COO_OVERRIDE_OK) - 1;
870                         if (err >= 0 && err < COO_EMAX)
871                             emsg = Sprintf("This cookie was rejected "
872                                            "to prevent security violation. [%s]",
873                                            violations[err])->ptr;
874                         else
875                             emsg =
876                                 "This cookie was rejected to prevent security violation.";
877                         record_err_message(emsg);
878                         if (show_cookie)
879                             disp_message_nsec(emsg, FALSE, 1, TRUE, FALSE);
880                     }
881                     else
882                         if (show_cookie)
883                             disp_message_nsec(Sprintf
884                                           ("Accepting invalid cookie: %s=%s",
885                                            name->ptr, value->ptr)->ptr, FALSE,
886                                           1, TRUE, FALSE);
887                 }
888             }
889         }
890 #endif                          /* USE_COOKIE */
891         else if (!strncasecmp(lineBuf2->ptr, "w3m-control:", 12) &&
892                  uf->scheme == SCM_LOCAL_CGI) {
893             Str funcname = Strnew();
894             int f;
895
896             p = lineBuf2->ptr + 12;
897             SKIP_BLANKS(p);
898             while (*p && !IS_SPACE(*p))
899                 Strcat_char(funcname, *(p++));
900             SKIP_BLANKS(p);
901             f = getFuncList(funcname->ptr);
902             if (f >= 0) {
903                 tmp = Strnew_charp(p);
904                 Strchop(tmp);
905                 pushEvent(f, tmp->ptr);
906             }
907         }
908         if (headerlist)
909             pushText(headerlist, lineBuf2->ptr);
910         Strfree(lineBuf2);
911         lineBuf2 = NULL;
912     }
913     if (thru)
914         addnewline(newBuf, "", propBuffer, NULL, 0, -1, -1);
915     if (src)
916         fclose(src);
917 }
918
919 char *
920 checkHeader(Buffer *buf, char *field)
921 {
922     int len;
923     TextListItem *i;
924     char *p;
925
926     if (buf == NULL || field == NULL || buf->document_header == NULL)
927         return NULL;
928     len = strlen(field);
929     for (i = buf->document_header->first; i != NULL; i = i->next) {
930         if (!strncasecmp(i->ptr, field, len)) {
931             p = i->ptr + len;
932             return remove_space(p);
933         }
934     }
935     return NULL;
936 }
937
938 char *
939 checkContentType(Buffer *buf)
940 {
941     char *p;
942     Str r;
943     p = checkHeader(buf, "Content-Type:");
944     if (p == NULL)
945         return NULL;
946     r = Strnew();
947     while (*p && *p != ';' && !IS_SPACE(*p))
948         Strcat_char(r, *p++);
949 #ifdef USE_M17N
950     if ((p = strcasestr(p, "charset")) != NULL) {
951         p += 7;
952         SKIP_BLANKS(p);
953         if (*p == '=') {
954             p++;
955             SKIP_BLANKS(p);
956             if (*p == '"')
957                 p++;
958             content_charset = wc_guess_charset(p, 0);
959         }
960     }
961 #endif
962     return r->ptr;
963 }
964
965 struct auth_param {
966     char *name;
967     Str val;
968 };
969
970 struct http_auth {
971     int pri;
972     char *scheme;
973     struct auth_param *param;
974     Str (*cred) (struct http_auth * ha, Str uname, Str pw, ParsedURL *pu,
975                  HRequest *hr, FormList *request);
976 };
977
978 enum {
979     AUTHCHR_NUL,
980     AUTHCHR_SEP,
981     AUTHCHR_TOKEN,
982 };
983
984 static int
985 skip_auth_token(char **pp)
986 {
987     char *p;
988     int first = AUTHCHR_NUL, typ;
989
990     for (p = *pp ;; ++p) {
991         switch (*p) {
992         case '\0':
993             goto endoftoken;
994         default:
995             if ((unsigned char)*p > 037) {
996                 typ = AUTHCHR_TOKEN;
997                 break;
998             }
999             /* thru */
1000         case '\177':
1001         case '[':
1002         case ']':
1003         case '(':
1004         case ')':
1005         case '<':
1006         case '>':
1007         case '@':
1008         case ';':
1009         case ':':
1010         case '\\':
1011         case '"':
1012         case '/':
1013         case '?':
1014         case '=':
1015         case ' ':
1016         case '\t':
1017         case ',':
1018             typ = AUTHCHR_SEP;
1019             break;
1020         }
1021
1022         if (!first)
1023             first = typ;
1024         else if (first != typ)
1025             break;
1026     }
1027 endoftoken:
1028     *pp = p;
1029     return first;
1030 }
1031
1032 static Str
1033 extract_auth_val(char **q)
1034 {
1035     unsigned char *qq = *(unsigned char **)q;
1036     int quoted = 0;
1037     Str val = Strnew();
1038
1039     SKIP_BLANKS(qq);
1040     if (*qq == '"') {
1041         quoted = TRUE;
1042         Strcat_char(val, *qq++);
1043     }
1044     while (*qq != '\0') {
1045         if (quoted && *qq == '"') {
1046             Strcat_char(val, *qq++);
1047             break;
1048         }
1049         if (!quoted) {
1050             switch (*qq) {
1051             case '[':
1052             case ']':
1053             case '(':
1054             case ')':
1055             case '<':
1056             case '>':
1057             case '@':
1058             case ';':
1059             case ':':
1060             case '\\':
1061             case '"':
1062             case '/':
1063             case '?':
1064             case '=':
1065             case ' ':
1066             case '\t':
1067                 qq++;
1068             case ',':
1069                 goto end_token;
1070             default:
1071                 if (*qq <= 037 || *qq == 0177) {
1072                     qq++;
1073                     goto end_token;
1074                 }
1075             }
1076         }
1077         else if (quoted && *qq == '\\')
1078             Strcat_char(val, *qq++);
1079         Strcat_char(val, *qq++);
1080     }
1081   end_token:
1082     *q = (char *)qq;
1083     return val;
1084 }
1085
1086 static Str
1087 qstr_unquote(Str s)
1088 {
1089     char *p;
1090
1091     if (s == NULL)
1092         return NULL;
1093     p = s->ptr;
1094     if (*p == '"') {
1095         Str tmp = Strnew();
1096         for (p++; *p != '\0'; p++) {
1097             if (*p == '\\')
1098                 p++;
1099             Strcat_char(tmp, *p);
1100         }
1101         if (Strlastchar(tmp) == '"')
1102             Strshrink(tmp, 1);
1103         return tmp;
1104     }
1105     else
1106         return s;
1107 }
1108
1109 static char *
1110 extract_auth_param(char *q, struct auth_param *auth)
1111 {
1112     struct auth_param *ap;
1113     char *p;
1114
1115     for (ap = auth; ap->name != NULL; ap++) {
1116         ap->val = NULL;
1117     }
1118
1119     while (*q != '\0') {
1120         SKIP_BLANKS(q);
1121         for (ap = auth; ap->name != NULL; ap++) {
1122             size_t len;
1123
1124             len = strlen(ap->name);
1125             if (strncasecmp(q, ap->name, len) == 0 &&
1126                 (IS_SPACE(q[len]) || q[len] == '=')) {
1127                 p = q + len;
1128                 SKIP_BLANKS(p);
1129                 if (*p != '=')
1130                     return q;
1131                 q = p + 1;
1132                 ap->val = extract_auth_val(&q);
1133                 break;
1134             }
1135         }
1136         if (ap->name == NULL) {
1137             /* skip unknown param */
1138             int token_type;
1139             p = q;
1140             if ((token_type = skip_auth_token(&q)) == AUTHCHR_TOKEN &&
1141                 (IS_SPACE(*q) || *q == '=')) {
1142                 SKIP_BLANKS(q);
1143                 if (*q != '=')
1144                     return p;
1145                 q++;
1146                 extract_auth_val(&q);
1147             }
1148             else
1149                 return p;
1150         }
1151         if (*q != '\0') {
1152             SKIP_BLANKS(q);
1153             if (*q == ',')
1154                 q++;
1155             else
1156                 break;
1157         }
1158     }
1159     return q;
1160 }
1161
1162 static Str
1163 get_auth_param(struct auth_param *auth, char *name)
1164 {
1165     struct auth_param *ap;
1166     for (ap = auth; ap->name != NULL; ap++) {
1167         if (strcasecmp(name, ap->name) == 0)
1168             return ap->val;
1169     }
1170     return NULL;
1171 }
1172
1173 static Str
1174 AuthBasicCred(struct http_auth *ha, Str uname, Str pw, ParsedURL *pu,
1175               HRequest *hr, FormList *request)
1176 {
1177     Str s = Strdup(uname);
1178     Strcat_char(s, ':');
1179     Strcat(s, pw);
1180     return Strnew_m_charp("Basic ", encodeB(s->ptr)->ptr, NULL);
1181 }
1182
1183 #ifdef USE_DIGEST_AUTH
1184 #include <openssl/md5.h>
1185
1186 /* RFC2617: 3.2.2 The Authorization Request Header
1187  * 
1188  * credentials      = "Digest" digest-response
1189  * digest-response  = 1#( username | realm | nonce | digest-uri
1190  *                    | response | [ algorithm ] | [cnonce] |
1191  *                     [opaque] | [message-qop] |
1192  *                         [nonce-count]  | [auth-param] )
1193  *
1194  * username         = "username" "=" username-value
1195  * username-value   = quoted-string
1196  * digest-uri       = "uri" "=" digest-uri-value
1197  * digest-uri-value = request-uri   ; As specified by HTTP/1.1
1198  * message-qop      = "qop" "=" qop-value
1199  * cnonce           = "cnonce" "=" cnonce-value
1200  * cnonce-value     = nonce-value
1201  * nonce-count      = "nc" "=" nc-value
1202  * nc-value         = 8LHEX
1203  * response         = "response" "=" request-digest
1204  * request-digest = <"> 32LHEX <">
1205  * LHEX             =  "0" | "1" | "2" | "3" |
1206  *                     "4" | "5" | "6" | "7" |
1207  *                     "8" | "9" | "a" | "b" |
1208  *                     "c" | "d" | "e" | "f"
1209  */
1210
1211 static Str
1212 digest_hex(char *p)
1213 {
1214     char *h = "0123456789abcdef";
1215     Str tmp = Strnew_size(MD5_DIGEST_LENGTH * 2 + 1);
1216     int i;
1217     for (i = 0; i < MD5_DIGEST_LENGTH; i++, p++) {
1218         Strcat_char(tmp, h[(*p >> 4) & 0x0f]);
1219         Strcat_char(tmp, h[*p & 0x0f]);
1220     }
1221     return tmp;
1222 }
1223
1224 enum {
1225     QOP_NONE,
1226     QOP_AUTH,
1227     QOP_AUTH_INT,
1228 };
1229
1230 static Str
1231 AuthDigestCred(struct http_auth *ha, Str uname, Str pw, ParsedURL *pu,
1232                HRequest *hr, FormList *request)
1233 {
1234     Str tmp, a1buf, a2buf, rd, s;
1235     char md5[MD5_DIGEST_LENGTH + 1];
1236     Str uri = HTTPrequestURI(pu, hr);
1237     char nc[] = "00000001";
1238
1239     Str algorithm = qstr_unquote(get_auth_param(ha->param, "algorithm"));
1240     Str nonce = qstr_unquote(get_auth_param(ha->param, "nonce"));
1241     Str cnonce /* = qstr_unquote(get_auth_param(ha->param, "cnonce")) */;
1242     /* cnonce is what client should generate. */
1243     Str qop = qstr_unquote(get_auth_param(ha->param, "qop"));
1244
1245     static union {
1246         int r[4];
1247         char s[sizeof(int) * 4];
1248     } cnonce_seed;
1249     int qop_i = QOP_NONE;
1250
1251     cnonce_seed.r[0] = rand();
1252     cnonce_seed.r[1] = rand();
1253     cnonce_seed.r[2] = rand();
1254     MD5(cnonce_seed.s, sizeof(cnonce_seed.s), md5);
1255     cnonce = digest_hex(md5);
1256     cnonce_seed.r[3]++;
1257
1258     if (qop) {
1259         char *p;
1260         size_t i;
1261
1262         p = qop->ptr;
1263         SKIP_BLANKS(p);
1264
1265         for (;;) {
1266             if ((i = strcspn(p, " \t,")) > 0) {
1267                 if (i == sizeof("auth-int") - sizeof("") && !strncasecmp(p, "auth-int", i)) {
1268                     if (qop_i < QOP_AUTH_INT)
1269                         qop_i = QOP_AUTH_INT;
1270                 }
1271                 else if (i == sizeof("auth") - sizeof("") && !strncasecmp(p, "auth", i)) {
1272                     if (qop_i < QOP_AUTH)
1273                         qop_i = QOP_AUTH;
1274                 }
1275             }
1276
1277             if (p[i]) {
1278                 p += i + 1;
1279                 SKIP_BLANKS(p);
1280             }
1281             else
1282                 break;
1283         }
1284     }
1285
1286     /* A1 = unq(username-value) ":" unq(realm-value) ":" passwd */
1287     tmp = Strnew_m_charp(uname->ptr, ":",
1288                          qstr_unquote(get_auth_param(ha->param, "realm"))->ptr,
1289                          ":", pw->ptr, NULL);
1290     MD5(tmp->ptr, strlen(tmp->ptr), md5);
1291     a1buf = digest_hex(md5);
1292
1293     if (algorithm) {
1294         if (strcasecmp(algorithm->ptr, "MD5-sess") == 0) {
1295             /* A1 = H(unq(username-value) ":" unq(realm-value) ":" passwd)
1296              *      ":" unq(nonce-value) ":" unq(cnonce-value)
1297              */
1298             if (nonce == NULL)
1299                 return NULL;
1300             tmp = Strnew_m_charp(a1buf->ptr, ":",
1301                                  qstr_unquote(nonce)->ptr,
1302                                  ":", qstr_unquote(cnonce)->ptr, NULL);
1303             MD5(tmp->ptr, strlen(tmp->ptr), md5);
1304             a1buf = digest_hex(md5);
1305         }
1306         else if (strcasecmp(algorithm->ptr, "MD5") == 0)
1307             /* ok default */
1308             ;
1309         else
1310             /* unknown algorithm */
1311             return NULL;
1312     }
1313
1314     /* A2 = Method ":" digest-uri-value */
1315     tmp = Strnew_m_charp(HTTPrequestMethod(hr)->ptr, ":", uri->ptr, NULL);
1316     if (qop_i == QOP_AUTH_INT) {
1317         /*  A2 = Method ":" digest-uri-value ":" H(entity-body) */
1318         if (request && request->body) {
1319             if (request->method == FORM_METHOD_POST && request->enctype == FORM_ENCTYPE_MULTIPART) {
1320                 FILE *fp = fopen(request->body, "r");
1321                 if (fp != NULL) {
1322                     Str ebody;
1323                     ebody = Strfgetall(fp);
1324                     MD5(ebody->ptr, strlen(ebody->ptr), md5);
1325                 }
1326                 else {
1327                     MD5("", 0, md5);
1328                 }
1329             }
1330             else {
1331                 MD5(request->body, request->length, md5);
1332             }
1333         }
1334         else {
1335             MD5("", 0, md5);
1336         }
1337         Strcat_char(tmp, ':');
1338         Strcat(tmp, digest_hex(md5));
1339     }
1340     MD5(tmp->ptr, strlen(tmp->ptr), md5);
1341     a2buf = digest_hex(md5);
1342
1343     if (qop_i >= QOP_AUTH) {
1344         /* request-digest  = <"> < KD ( H(A1),     unq(nonce-value)
1345          *                      ":" nc-value
1346          *                      ":" unq(cnonce-value)
1347          *                      ":" unq(qop-value)
1348          *                      ":" H(A2)
1349          *                      ) <">
1350          */
1351         if (nonce == NULL)
1352             return NULL;
1353         tmp = Strnew_m_charp(a1buf->ptr, ":", qstr_unquote(nonce)->ptr,
1354                              ":", nc,
1355                              ":", qstr_unquote(cnonce)->ptr,
1356                              ":", qop_i == QOP_AUTH ? "auth" : "auth-int",
1357                              ":", a2buf->ptr, NULL);
1358         MD5(tmp->ptr, strlen(tmp->ptr), md5);
1359         rd = digest_hex(md5);
1360     }
1361     else {
1362         /* compatibility with RFC 2069
1363          * request_digest = KD(H(A1),  unq(nonce), H(A2))
1364          */
1365         tmp = Strnew_m_charp(a1buf->ptr, ":",
1366                              qstr_unquote(get_auth_param(ha->param, "nonce"))->
1367                              ptr, ":", a2buf->ptr, NULL);
1368         MD5(tmp->ptr, strlen(tmp->ptr), md5);
1369         rd = digest_hex(md5);
1370     }
1371
1372     /*
1373      * digest-response  = 1#( username | realm | nonce | digest-uri
1374      *                          | response | [ algorithm ] | [cnonce] |
1375      *                          [opaque] | [message-qop] |
1376      *                          [nonce-count]  | [auth-param] )
1377      */
1378
1379     tmp = Strnew_m_charp("Digest username=\"", uname->ptr, "\"", NULL);
1380     Strcat_m_charp(tmp, ", realm=",
1381                    get_auth_param(ha->param, "realm")->ptr, NULL);
1382     Strcat_m_charp(tmp, ", nonce=",
1383                    get_auth_param(ha->param, "nonce")->ptr, NULL);
1384     Strcat_m_charp(tmp, ", uri=\"", uri->ptr, "\"", NULL);
1385     Strcat_m_charp(tmp, ", response=\"", rd->ptr, "\"", NULL);
1386
1387     if (algorithm)
1388         Strcat_m_charp(tmp, ", algorithm=",
1389                        get_auth_param(ha->param, "algorithm")->ptr, NULL);
1390
1391     if (cnonce)
1392         Strcat_m_charp(tmp, ", cnonce=\"", cnonce->ptr, "\"", NULL);
1393
1394     if ((s = get_auth_param(ha->param, "opaque")) != NULL)
1395         Strcat_m_charp(tmp, ", opaque=", s->ptr, NULL);
1396
1397     if (qop_i >= QOP_AUTH) {
1398         Strcat_m_charp(tmp, ", qop=",
1399                        qop_i == QOP_AUTH ? "auth" : "auth-int",
1400                        NULL);
1401         /* XXX how to count? */
1402         /* Since nonce is unique up to each *-Authenticate and w3m does not re-use *-Authenticate: headers,
1403            nonce-count should be always "00000001". */
1404         Strcat_m_charp(tmp, ", nc=", nc, NULL);
1405     }
1406
1407     return tmp;
1408 }
1409 #endif
1410
1411 /* *INDENT-OFF* */
1412 struct auth_param none_auth_param[] = {
1413     {NULL, NULL}
1414 };
1415
1416 struct auth_param basic_auth_param[] = {
1417     {"realm", NULL},
1418     {NULL, NULL}
1419 };
1420
1421 #ifdef USE_DIGEST_AUTH
1422 /* RFC2617: 3.2.1 The WWW-Authenticate Response Header
1423  * challenge        =  "Digest" digest-challenge
1424  * 
1425  * digest-challenge  = 1#( realm | [ domain ] | nonce |
1426  *                       [ opaque ] |[ stale ] | [ algorithm ] |
1427  *                        [ qop-options ] | [auth-param] )
1428  *
1429  * domain            = "domain" "=" <"> URI ( 1*SP URI ) <">
1430  * URI               = absoluteURI | abs_path
1431  * nonce             = "nonce" "=" nonce-value
1432  * nonce-value       = quoted-string
1433  * opaque            = "opaque" "=" quoted-string
1434  * stale             = "stale" "=" ( "true" | "false" )
1435  * algorithm         = "algorithm" "=" ( "MD5" | "MD5-sess" |
1436  *                        token )
1437  * qop-options       = "qop" "=" <"> 1#qop-value <">
1438  * qop-value         = "auth" | "auth-int" | token
1439  */
1440 struct auth_param digest_auth_param[] = {
1441     {"realm", NULL},
1442     {"domain", NULL},
1443     {"nonce", NULL},
1444     {"opaque", NULL},
1445     {"stale", NULL},
1446     {"algorithm", NULL},
1447     {"qop", NULL},
1448     {NULL, NULL}
1449 };
1450 #endif
1451 /* for RFC2617: HTTP Authentication */
1452 struct http_auth www_auth[] = {
1453     { 1, "Basic ", basic_auth_param, AuthBasicCred },
1454 #ifdef USE_DIGEST_AUTH
1455     { 10, "Digest ", digest_auth_param, AuthDigestCred },
1456 #endif
1457     { 0, NULL, NULL, NULL,}
1458 };
1459 /* *INDENT-ON* */
1460
1461 static struct http_auth *
1462 findAuthentication(struct http_auth *hauth, Buffer *buf, char *auth_field)
1463 {
1464     struct http_auth *ha;
1465     int len = strlen(auth_field), slen;
1466     TextListItem *i;
1467     char *p0, *p;
1468
1469     bzero(hauth, sizeof(struct http_auth));
1470     for (i = buf->document_header->first; i != NULL; i = i->next) {
1471         if (strncasecmp(i->ptr, auth_field, len) == 0) {
1472             for (p = i->ptr + len; p != NULL && *p != '\0';) {
1473                 SKIP_BLANKS(p);
1474                 p0 = p;
1475                 for (ha = &www_auth[0]; ha->scheme != NULL; ha++) {
1476                     slen = strlen(ha->scheme);
1477                     if (strncasecmp(p, ha->scheme, slen) == 0) {
1478                         p += slen;
1479                         SKIP_BLANKS(p);
1480                         if (hauth->pri < ha->pri) {
1481                             *hauth = *ha;
1482                             p = extract_auth_param(p, hauth->param);
1483                             break;
1484                         }
1485                         else {
1486                             /* weak auth */
1487                             p = extract_auth_param(p, none_auth_param);
1488                         }
1489                     }
1490                 }
1491                 if (p0 == p) {
1492                     /* all unknown auth failed */
1493                     int token_type;
1494                     if ((token_type = skip_auth_token(&p)) == AUTHCHR_TOKEN && IS_SPACE(*p)) {
1495                         SKIP_BLANKS(p);
1496                         p = extract_auth_param(p, none_auth_param);
1497                     }
1498                     else
1499                         break;
1500                 }
1501             }
1502         }
1503     }
1504     return hauth->scheme ? hauth : NULL;
1505 }
1506
1507 static void
1508 getAuthCookie(struct http_auth *hauth, char *auth_header,
1509               TextList *extra_header, ParsedURL *pu, HRequest *hr,
1510               FormList *request,
1511               volatile Str *uname, volatile Str *pwd)
1512 {
1513     Str ss = NULL;
1514     Str tmp;
1515     TextListItem *i;
1516     int a_found;
1517     int auth_header_len = strlen(auth_header);
1518     char *realm = NULL;
1519     int proxy;
1520
1521     if (hauth)
1522         realm = qstr_unquote(get_auth_param(hauth->param, "realm"))->ptr;
1523
1524     if (!realm)
1525         return;
1526
1527     a_found = FALSE;
1528     for (i = extra_header->first; i != NULL; i = i->next) {
1529         if (!strncasecmp(i->ptr, auth_header, auth_header_len)) {
1530             a_found = TRUE;
1531             break;
1532         }
1533     }
1534     proxy = !strncasecmp("Proxy-Authorization:", auth_header,
1535                          auth_header_len);
1536     if (a_found) {
1537         /* This means that *-Authenticate: header is received after
1538          * Authorization: header is sent to the server. 
1539          */
1540         if (fmInitialized) {
1541             message("Wrong username or password", 0, 0);
1542             refresh();
1543         }
1544         else
1545             fprintf(stderr, "Wrong username or password\n");
1546         sleep(1);
1547         /* delete Authenticate: header from extra_header */
1548         delText(extra_header, i);
1549         invalidate_auth_user_passwd(pu, realm, *uname, *pwd, proxy);
1550     }
1551     *uname = NULL;
1552     *pwd = NULL;
1553
1554     if (!a_found && find_auth_user_passwd(pu, realm, (Str*)uname, (Str*)pwd, 
1555                                           proxy)) {
1556         /* found username & password in passwd file */ ;
1557     }
1558     else {
1559         if (QuietMessage)
1560             return;
1561         /* input username and password */
1562         sleep(2);
1563         if (fmInitialized) {
1564             char *pp;
1565             term_raw();
1566             /* FIXME: gettextize? */
1567             if ((pp = inputStr(Sprintf("Username for %s: ", realm)->ptr,
1568                                NULL)) == NULL)
1569                 return;
1570             *uname = Str_conv_to_system(Strnew_charp(pp));
1571             if ((pp = inputLine(Sprintf("Password for %s: ", realm)->ptr, NULL,
1572                                 IN_PASSWORD)) == NULL) {
1573                 *uname = NULL;
1574                 return;
1575             }
1576             *pwd = Str_conv_to_system(Strnew_charp(pp));
1577             term_cbreak();
1578         }
1579         else {
1580             /*
1581              * If post file is specified as '-', stdin is closed at this
1582              * point.
1583              * In this case, w3m cannot read username from stdin.
1584              * So exit with error message.
1585              * (This is same behavior as lwp-request.)
1586              */
1587             if (feof(stdin) || ferror(stdin)) {
1588                 /* FIXME: gettextize? */
1589                 fprintf(stderr, "w3m: Authorization required for %s\n",
1590                         realm);
1591                 exit(1);
1592             }
1593             
1594             /* FIXME: gettextize? */
1595             printf(proxy ? "Proxy Username for %s: " : "Username for %s: ",
1596                    realm);
1597             fflush(stdout);
1598             *uname = Strfgets(stdin);
1599             Strchop(*uname);
1600 #ifdef HAVE_GETPASSPHRASE
1601             *pwd = Strnew_charp((char *)
1602                                 getpassphrase(proxy ? "Proxy Password: " :
1603                                               "Password: "));
1604 #else
1605 #ifndef __MINGW32_VERSION
1606             *pwd = Strnew_charp((char *)
1607                                 getpass(proxy ? "Proxy Password: " :
1608                                         "Password: "));
1609 #else
1610             term_raw();
1611             *pwd = Strnew_charp((char *)
1612                                 inputLine(proxy ? "Proxy Password: " :
1613                                           "Password: ", NULL, IN_PASSWORD));
1614             term_cbreak();
1615 #endif /* __MINGW32_VERSION */
1616 #endif
1617         }
1618     }
1619     ss = hauth->cred(hauth, *uname, *pwd, pu, hr, request);
1620     if (ss) {
1621         tmp = Strnew_charp(auth_header);
1622         Strcat_m_charp(tmp, " ", ss->ptr, "\r\n", NULL);
1623         pushText(extra_header, tmp->ptr);
1624     }
1625     else {
1626         *uname = NULL;
1627         *pwd = NULL;
1628     }
1629     return;
1630 }
1631
1632 static int
1633 same_url_p(ParsedURL *pu1, ParsedURL *pu2)
1634 {
1635     return (pu1->scheme == pu2->scheme && pu1->port == pu2->port &&
1636             (pu1->host ? pu2->host ? !strcasecmp(pu1->host, pu2->host) : 0 : 1)
1637             && (pu1->file ? pu2->
1638                 file ? !strcmp(pu1->file, pu2->file) : 0 : 1));
1639 }
1640
1641 static int
1642 checkRedirection(ParsedURL *pu)
1643 {
1644     static ParsedURL *puv = NULL;
1645     static int nredir = 0;
1646     static int nredir_size = 0;
1647     Str tmp;
1648
1649     if (pu == NULL) {
1650         nredir = 0;
1651         nredir_size = 0;
1652         puv = NULL;
1653         return TRUE;
1654     }
1655     if (nredir >= FollowRedirection) {
1656         /* FIXME: gettextize? */
1657         tmp = Sprintf("Number of redirections exceeded %d at %s",
1658                       FollowRedirection, parsedURL2Str(pu)->ptr);
1659         disp_err_message(tmp->ptr, FALSE);
1660         return FALSE;
1661     }
1662     else if (nredir_size > 0 &&
1663              (same_url_p(pu, &puv[(nredir - 1) % nredir_size]) ||
1664               (!(nredir % 2)
1665                && same_url_p(pu, &puv[(nredir / 2) % nredir_size])))) {
1666         /* FIXME: gettextize? */
1667         tmp = Sprintf("Redirection loop detected (%s)",
1668                       parsedURL2Str(pu)->ptr);
1669         disp_err_message(tmp->ptr, FALSE);
1670         return FALSE;
1671     }
1672     if (!puv) {
1673         nredir_size = FollowRedirection / 2 + 1;
1674         puv = New_N(ParsedURL, nredir_size);
1675         memset(puv, 0, sizeof(ParsedURL) * nredir_size);
1676     }
1677     copyParsedURL(&puv[nredir % nredir_size], pu);
1678     nredir++;
1679     return TRUE;
1680 }
1681
1682 /* 
1683  * loadGeneralFile: load file to buffer
1684  */
1685 Buffer *
1686 loadGeneralFile(char *path, ParsedURL *volatile current, char *referer,
1687                 int flag, FormList *volatile request)
1688 {
1689     URLFile f, *volatile of = NULL;
1690     ParsedURL pu;
1691     Buffer *b = NULL, *(*volatile proc)() = loadBuffer;
1692     char *volatile tpath;
1693     char *volatile t = "text/plain", *p, *volatile real_type = NULL;
1694     Buffer *volatile t_buf = NULL;
1695     int volatile searchHeader = SearchHeader;
1696     int volatile searchHeader_through = TRUE;
1697     MySignalHandler(*volatile prevtrap) (SIGNAL_ARG) = NULL;
1698     TextList *extra_header = newTextList();
1699     volatile Str uname = NULL;
1700     volatile Str pwd = NULL;
1701     volatile Str realm = NULL;
1702     int volatile add_auth_cookie_flag;
1703     unsigned char status = HTST_NORMAL;
1704     URLOption url_option;
1705     Str tmp;
1706     Str volatile page = NULL;
1707 #ifdef USE_M17N
1708     wc_ces charset = WC_CES_US_ASCII;
1709 #endif
1710     HRequest hr;
1711     ParsedURL *volatile auth_pu;
1712
1713     tpath = path;
1714     prevtrap = NULL;
1715     add_auth_cookie_flag = 0;
1716
1717     checkRedirection(NULL);
1718   load_doc:
1719     TRAP_OFF;
1720     url_option.referer = referer;
1721     url_option.flag = flag;
1722     f = openURL(tpath, &pu, current, &url_option, request, extra_header, of,
1723                 &hr, &status);
1724     of = NULL;
1725 #ifdef USE_M17N
1726     content_charset = 0;
1727 #endif
1728     if (f.stream == NULL) {
1729         switch (f.scheme) {
1730         case SCM_LOCAL:
1731             {
1732                 struct stat st;
1733                 if (stat(pu.real_file, &st) < 0)
1734                     return NULL;
1735                 if (S_ISDIR(st.st_mode)) {
1736                     if (UseExternalDirBuffer) {
1737                         Str cmd = Sprintf("%s?dir=%s#current",
1738                                           DirBufferCommand, pu.file);
1739                         b = loadGeneralFile(cmd->ptr, NULL, NO_REFERER, 0,
1740                                             NULL);
1741                         if (b != NULL && b != NO_BUFFER) {
1742                             copyParsedURL(&b->currentURL, &pu);
1743                             b->filename = b->currentURL.real_file;
1744                         }
1745                         return b;
1746                     }
1747                     else {
1748                         page = loadLocalDir(pu.real_file);
1749                         t = "local:directory";
1750 #ifdef USE_M17N
1751                         charset = SystemCharset;
1752 #endif
1753                     }
1754                 }
1755             }
1756             break;
1757         case SCM_FTPDIR:
1758             page = loadFTPDir(&pu, &charset);
1759             t = "ftp:directory";
1760             break;
1761 #ifdef USE_NNTP
1762         case SCM_NEWS_GROUP:
1763             page = loadNewsgroup(&pu, &charset);
1764             t = "news:group";
1765             break;
1766 #endif
1767         case SCM_UNKNOWN:
1768 #ifdef USE_EXTERNAL_URI_LOADER
1769             tmp = searchURIMethods(&pu);
1770             if (tmp != NULL) {
1771                 b = loadGeneralFile(tmp->ptr, current, referer, flag, request);
1772                 if (b != NULL && b != NO_BUFFER)
1773                     copyParsedURL(&b->currentURL, &pu);
1774                 return b;
1775             }
1776 #endif
1777             /* FIXME: gettextize? */
1778             disp_err_message(Sprintf("Unknown URI: %s",
1779                                      parsedURL2Str(&pu)->ptr)->ptr, FALSE);
1780             break;
1781         }
1782         if (page && page->length > 0)
1783             goto page_loaded;
1784         return NULL;
1785     }
1786
1787     if (status == HTST_MISSING) {
1788         TRAP_OFF;
1789         UFclose(&f);
1790         return NULL;
1791     }
1792
1793     /* openURL() succeeded */
1794     if (SETJMP(AbortLoading) != 0) {
1795         /* transfer interrupted */
1796         TRAP_OFF;
1797         if (b)
1798             discardBuffer(b);
1799         UFclose(&f);
1800         return NULL;
1801     }
1802
1803     b = NULL;
1804     if (f.is_cgi) {
1805         /* local CGI */
1806         searchHeader = TRUE;
1807         searchHeader_through = FALSE;
1808     }
1809     if (header_string)
1810         header_string = NULL;
1811     TRAP_ON;
1812     if (pu.scheme == SCM_HTTP ||
1813 #ifdef USE_SSL
1814         pu.scheme == SCM_HTTPS ||
1815 #endif                          /* USE_SSL */
1816         ((
1817 #ifdef USE_GOPHER
1818              (pu.scheme == SCM_GOPHER && non_null(GOPHER_proxy)) ||
1819 #endif                          /* USE_GOPHER */
1820              (pu.scheme == SCM_FTP && non_null(FTP_proxy))
1821          ) && !Do_not_use_proxy && !check_no_proxy(pu.host))) {
1822
1823         if (fmInitialized) {
1824             term_cbreak();
1825             /* FIXME: gettextize? */
1826             message(Sprintf("%s contacted. Waiting for reply...", pu.host)->
1827                     ptr, 0, 0);
1828             refresh();
1829         }
1830         if (t_buf == NULL)
1831             t_buf = newBuffer(INIT_BUFFER_WIDTH);
1832 #if 0                           /* USE_SSL */
1833         if (IStype(f.stream) == IST_SSL) {
1834             Str s = ssl_get_certificate(f.stream, pu.host);
1835             if (s == NULL)
1836                 return NULL;
1837             else
1838                 t_buf->ssl_certificate = s->ptr;
1839         }
1840 #endif
1841         readHeader(&f, t_buf, FALSE, &pu);
1842         if (((http_response_code >= 301 && http_response_code <= 303)
1843              || http_response_code == 307)
1844             && (p = checkHeader(t_buf, "Location:")) != NULL
1845             && checkRedirection(&pu)) {
1846             /* document moved */
1847             /* 301: Moved Permanently */
1848             /* 302: Found */
1849             /* 303: See Other */
1850             /* 307: Temporary Redirect (HTTP/1.1) */
1851             tpath = url_quote_conv(p, DocumentCharset);
1852             request = NULL;
1853             UFclose(&f);
1854             current = New(ParsedURL);
1855             copyParsedURL(current, &pu);
1856             t_buf = newBuffer(INIT_BUFFER_WIDTH);
1857             t_buf->bufferprop |= BP_REDIRECTED;
1858             status = HTST_NORMAL;
1859             goto load_doc;
1860         }
1861         t = checkContentType(t_buf);
1862         if (t == NULL && pu.file != NULL) {
1863             if (!((http_response_code >= 400 && http_response_code <= 407) ||
1864                   (http_response_code >= 500 && http_response_code <= 505)))
1865                 t = guessContentType(pu.file);
1866         }
1867         if (t == NULL)
1868             t = "text/plain";
1869         if (add_auth_cookie_flag && realm && uname && pwd) {
1870             /* If authorization is required and passed */
1871             add_auth_user_passwd(&pu, qstr_unquote(realm)->ptr, uname, pwd, 
1872                                   0);
1873             add_auth_cookie_flag = 0;
1874         }
1875         if ((p = checkHeader(t_buf, "WWW-Authenticate:")) != NULL &&
1876             http_response_code == 401) {
1877             /* Authentication needed */
1878             struct http_auth hauth;
1879             if (findAuthentication(&hauth, t_buf, "WWW-Authenticate:") != NULL
1880                 && (realm = get_auth_param(hauth.param, "realm")) != NULL) {
1881                 auth_pu = &pu;
1882                 getAuthCookie(&hauth, "Authorization:", extra_header,
1883                               auth_pu, &hr, request, &uname, &pwd);
1884                 if (uname == NULL) {
1885                     /* abort */
1886                     TRAP_OFF;
1887                     goto page_loaded;
1888                 }
1889                 UFclose(&f);
1890                 add_auth_cookie_flag = 1;
1891                 status = HTST_NORMAL;
1892                 goto load_doc;
1893             }
1894         }
1895         if ((p = checkHeader(t_buf, "Proxy-Authenticate:")) != NULL &&
1896             http_response_code == 407) {
1897             /* Authentication needed */
1898             struct http_auth hauth;
1899             if (findAuthentication(&hauth, t_buf, "Proxy-Authenticate:")
1900                 != NULL
1901                 && (realm = get_auth_param(hauth.param, "realm")) != NULL) {
1902                 auth_pu = schemeToProxy(pu.scheme);
1903                 getAuthCookie(&hauth, "Proxy-Authorization:",
1904                               extra_header, auth_pu, &hr, request, 
1905                               &uname, &pwd);
1906                 if (uname == NULL) {
1907                     /* abort */
1908                     TRAP_OFF;
1909                     goto page_loaded;
1910                 }
1911                 UFclose(&f);
1912                 add_auth_cookie_flag = 1;
1913                 status = HTST_NORMAL;
1914                 goto load_doc;
1915             }
1916         }
1917         /* XXX: RFC2617 3.2.3 Authentication-Info: ? */
1918
1919         if (status == HTST_CONNECT) {
1920             of = &f;
1921             goto load_doc;
1922         }
1923
1924         f.modtime = mymktime(checkHeader(t_buf, "Last-Modified:"));
1925     }
1926 #ifdef USE_NNTP
1927     else if (pu.scheme == SCM_NEWS || pu.scheme == SCM_NNTP) {
1928         if (t_buf == NULL)
1929             t_buf = newBuffer(INIT_BUFFER_WIDTH);
1930         readHeader(&f, t_buf, TRUE, &pu);
1931         t = checkContentType(t_buf);
1932         if (t == NULL)
1933             t = "text/plain";
1934     }
1935 #endif                          /* USE_NNTP */
1936 #ifdef USE_GOPHER
1937     else if (pu.scheme == SCM_GOPHER) {
1938         switch (*pu.file) {
1939         case '0':
1940             t = "text/plain";
1941             break;
1942         case '1':
1943         case 'm':
1944             page = loadGopherDir(&f, &pu, &charset);
1945             t = "gopher:directory";
1946             TRAP_OFF;
1947             goto page_loaded;
1948         case 's':
1949             t = "audio/basic";
1950             break;
1951         case 'g':
1952             t = "image/gif";
1953             break;
1954         case 'h':
1955             t = "text/html";
1956             break;
1957         }
1958     }
1959 #endif                          /* USE_GOPHER */
1960     else if (pu.scheme == SCM_FTP) {
1961         check_compression(path, &f);
1962         if (f.compression != CMP_NOCOMPRESS) {
1963             char *t1 = uncompressed_file_type(pu.file, NULL);
1964             real_type = f.guess_type;
1965 #if 0
1966             if (t1 && strncasecmp(t1, "application/", 12) == 0) {
1967                 f.compression = CMP_NOCOMPRESS;
1968                 t = real_type;
1969             }
1970             else
1971 #endif
1972             if (t1)
1973                 t = t1;
1974             else
1975                 t = real_type;
1976         }
1977         else {
1978             real_type = guessContentType(pu.file);
1979             if (real_type == NULL)
1980                 real_type = "text/plain";
1981             t = real_type;
1982         }
1983 #if 0
1984         if (!strncasecmp(t, "application/", 12)) {
1985             char *tmpf = tmpfname(TMPF_DFL, NULL)->ptr;
1986             current_content_length = 0;
1987             if (save2tmp(f, tmpf) < 0)
1988                 UFclose(&f);
1989             else {
1990                 UFclose(&f);
1991                 TRAP_OFF;
1992                 doFileMove(tmpf, guess_save_name(t_buf, pu.file));
1993             }
1994             return NO_BUFFER;
1995         }
1996 #endif
1997     }
1998     else if (pu.scheme == SCM_DATA) {
1999         t = f.guess_type;
2000     }
2001     else if (searchHeader) {
2002         searchHeader = SearchHeader = FALSE;
2003         if (t_buf == NULL)
2004             t_buf = newBuffer(INIT_BUFFER_WIDTH);
2005         readHeader(&f, t_buf, searchHeader_through, &pu);
2006         if (f.is_cgi && (p = checkHeader(t_buf, "Location:")) != NULL &&
2007             checkRedirection(&pu)) {
2008             /* document moved */
2009             tpath = url_quote_conv(remove_space(p), DocumentCharset);
2010             request = NULL;
2011             UFclose(&f);
2012             add_auth_cookie_flag = 0;
2013             current = New(ParsedURL);
2014             copyParsedURL(current, &pu);
2015             t_buf = newBuffer(INIT_BUFFER_WIDTH);
2016             t_buf->bufferprop |= BP_REDIRECTED;
2017             status = HTST_NORMAL;
2018             goto load_doc;
2019         }
2020 #ifdef AUTH_DEBUG
2021         if ((p = checkHeader(t_buf, "WWW-Authenticate:")) != NULL) {
2022             /* Authentication needed */
2023             struct http_auth hauth;
2024             if (findAuthentication(&hauth, t_buf, "WWW-Authenticate:") != NULL
2025                 && (realm = get_auth_param(hauth.param, "realm")) != NULL) {
2026                 auth_pu = &pu;
2027                 getAuthCookie(&hauth, "Authorization:", extra_header,
2028                               auth_pu, &hr, request, &uname, &pwd);
2029                 if (uname == NULL) {
2030                     /* abort */
2031                     TRAP_OFF;
2032                     goto page_loaded;
2033                 }
2034                 UFclose(&f);
2035                 add_auth_cookie_flag = 1;
2036                 status = HTST_NORMAL;
2037                 goto load_doc;
2038             }
2039         }
2040 #endif /* defined(AUTH_DEBUG) */
2041         t = checkContentType(t_buf);
2042         if (t == NULL)
2043             t = "text/plain";
2044     }
2045     else if (DefaultType) {
2046         t = DefaultType;
2047         DefaultType = NULL;
2048     }
2049     else {
2050         t = guessContentType(pu.file);
2051         if (t == NULL)
2052             t = "text/plain";
2053         real_type = t;
2054         if (f.guess_type)
2055             t = f.guess_type;
2056     }
2057
2058   page_loaded:
2059     if (page) {
2060         FILE *src;
2061 #ifdef USE_IMAGE
2062         if (image_source)
2063             return NULL;
2064 #endif
2065         tmp = tmpfname(TMPF_SRC, ".html");
2066         src = fopen(tmp->ptr, "w");
2067         if (src) {
2068             Str s;
2069             s = wc_Str_conv_strict(page, InnerCharset, charset);
2070             Strfputs(s, src);
2071             fclose(src);
2072         }
2073         if (do_download) {
2074             char *file;
2075             if (!src)
2076                 return NULL;
2077             file = guess_filename(pu.file);
2078 #ifdef USE_GOPHER
2079             if (f.scheme == SCM_GOPHER)
2080                 file = Sprintf("%s.html", file)->ptr;
2081 #endif
2082 #ifdef USE_NNTP
2083             if (f.scheme == SCM_NEWS_GROUP)
2084                 file = Sprintf("%s.html", file)->ptr;
2085 #endif
2086             doFileMove(tmp->ptr, file);
2087             return NO_BUFFER;
2088         }
2089         b = loadHTMLString(page);
2090         if (b) {
2091             copyParsedURL(&b->currentURL, &pu);
2092             b->real_scheme = pu.scheme;
2093             b->real_type = t;
2094             if (src)
2095                 b->sourcefile = tmp->ptr;
2096 #ifdef USE_M17N
2097             b->document_charset = charset;
2098 #endif
2099         }
2100         return b;
2101     }
2102
2103     if (real_type == NULL)
2104         real_type = t;
2105     proc = loadBuffer;
2106 #ifdef USE_IMAGE
2107     cur_baseURL = New(ParsedURL);
2108     copyParsedURL(cur_baseURL, &pu);
2109 #endif
2110
2111     current_content_length = 0;
2112     if ((p = checkHeader(t_buf, "Content-Length:")) != NULL)
2113         current_content_length = strtoclen(p);
2114     if (do_download) {
2115         /* download only */
2116         char *file;
2117         TRAP_OFF;
2118         if (DecodeCTE && IStype(f.stream) != IST_ENCODED)
2119             f.stream = newEncodedStream(f.stream, f.encoding);
2120         if (pu.scheme == SCM_LOCAL) {
2121             struct stat st;
2122             if (PreserveTimestamp && !stat(pu.real_file, &st))
2123                 f.modtime = st.st_mtime;
2124             file = conv_from_system(guess_save_name(NULL, pu.real_file));
2125         }
2126         else
2127             file = guess_save_name(t_buf, pu.file);
2128         if (doFileSave(f, file) == 0)
2129             UFhalfclose(&f);
2130         else
2131             UFclose(&f);
2132         return NO_BUFFER;
2133     }
2134
2135     if ((f.content_encoding != CMP_NOCOMPRESS) && AutoUncompress
2136         && !(w3m_dump & DUMP_EXTRA)) {
2137         uncompress_stream(&f, &pu.real_file);
2138     }
2139     else if (f.compression != CMP_NOCOMPRESS) {
2140         if (!(w3m_dump & DUMP_SOURCE) &&
2141             (w3m_dump & ~DUMP_FRAME || is_text_type(t)
2142              || searchExtViewer(t))) {
2143             if (t_buf == NULL)
2144                 t_buf = newBuffer(INIT_BUFFER_WIDTH);
2145             uncompress_stream(&f, &t_buf->sourcefile);
2146             uncompressed_file_type(pu.file, &f.ext);
2147         }
2148         else {
2149             t = compress_application_type(f.compression);
2150             f.compression = CMP_NOCOMPRESS;
2151         }
2152     }
2153 #ifdef USE_IMAGE
2154     if (image_source) {
2155         Buffer *b = NULL;
2156         if (IStype(f.stream) != IST_ENCODED)
2157             f.stream = newEncodedStream(f.stream, f.encoding);
2158         if (save2tmp(f, image_source) == 0) {
2159             b = newBuffer(INIT_BUFFER_WIDTH);
2160             b->sourcefile = image_source;
2161             b->real_type = t;
2162         }
2163         UFclose(&f);
2164         TRAP_OFF;
2165         return b;
2166     }
2167 #endif
2168
2169     if (!strcasecmp(t, "text/html"))
2170         proc = loadHTMLBuffer;
2171     else if (is_plain_text_type(t))
2172         proc = loadBuffer;
2173 #ifdef USE_IMAGE
2174     else if (activeImage && displayImage && !useExtImageViewer &&
2175              !(w3m_dump & ~DUMP_FRAME) && !strncasecmp(t, "image/", 6))
2176         proc = loadImageBuffer;
2177 #endif
2178     else if (w3m_backend) ;
2179     else if (!(w3m_dump & ~DUMP_FRAME) || is_dump_text_type(t)) {
2180         if (!do_download && doExternal(f,
2181                                        pu.real_file ? pu.real_file : pu.file,
2182                                        t, &b, t_buf)) {
2183             if (b && b != NO_BUFFER) {
2184                 b->real_scheme = f.scheme;
2185                 b->real_type = real_type;
2186                 if (b->currentURL.host == NULL && b->currentURL.file == NULL)
2187                     copyParsedURL(&b->currentURL, &pu);
2188             }
2189             UFclose(&f);
2190             TRAP_OFF;
2191             return b;
2192         }
2193         else {
2194             TRAP_OFF;
2195             if (pu.scheme == SCM_LOCAL) {
2196                 UFclose(&f);
2197                 _doFileCopy(pu.real_file,
2198                             conv_from_system(guess_save_name
2199                                              (NULL, pu.real_file)), TRUE);
2200             }
2201             else {
2202                 if (DecodeCTE && IStype(f.stream) != IST_ENCODED)
2203                     f.stream = newEncodedStream(f.stream, f.encoding);
2204                 if (doFileSave(f, guess_save_name(t_buf, pu.file)) == 0)
2205                     UFhalfclose(&f);
2206                 else
2207                     UFclose(&f);
2208             }
2209             return NO_BUFFER;
2210         }
2211     }
2212     else if (w3m_dump & DUMP_FRAME)
2213         return NULL;
2214
2215     if (flag & RG_FRAME) {
2216         if (t_buf == NULL)
2217             t_buf = newBuffer(INIT_BUFFER_WIDTH);
2218         t_buf->bufferprop |= BP_FRAME;
2219     }
2220 #ifdef USE_SSL
2221     if (t_buf)
2222         t_buf->ssl_certificate = f.ssl_certificate;
2223 #endif
2224     frame_source = flag & RG_FRAME_SRC;
2225     b = loadSomething(&f, pu.real_file ? pu.real_file : pu.file, proc, t_buf);
2226     UFclose(&f);
2227     frame_source = 0;
2228     if (b) {
2229         b->real_scheme = f.scheme;
2230         b->real_type = real_type;
2231         if (b->currentURL.host == NULL && b->currentURL.file == NULL)
2232             copyParsedURL(&b->currentURL, &pu);
2233         if (!strcasecmp(t, "text/html"))
2234             b->type = "text/html";
2235         else if (w3m_backend) {
2236             Str s = Strnew_charp(t);
2237             b->type = s->ptr;
2238         }
2239 #ifdef USE_IMAGE
2240         else if (proc == loadImageBuffer)
2241             b->type = "text/html";
2242 #endif
2243         else
2244             b->type = "text/plain";
2245         if (pu.label) {
2246             if (proc == loadHTMLBuffer) {
2247                 Anchor *a;
2248                 a = searchURLLabel(b, pu.label);
2249                 if (a != NULL) {
2250                     gotoLine(b, a->start.line);
2251                     if (label_topline)
2252                         b->topLine = lineSkip(b, b->topLine,
2253                                               b->currentLine->linenumber
2254                                               - b->topLine->linenumber, FALSE);
2255                     b->pos = a->start.pos;
2256                     arrangeCursor(b);
2257                 }
2258             }
2259             else {              /* plain text */
2260                 int l = atoi(pu.label);
2261                 gotoRealLine(b, l);
2262                 b->pos = 0;
2263                 arrangeCursor(b);
2264             }
2265         }
2266     }
2267     if (header_string)
2268         header_string = NULL;
2269 #ifdef USE_NNTP
2270     if (f.scheme == SCM_NNTP || f.scheme == SCM_NEWS)
2271         reAnchorNewsheader(b);
2272 #endif
2273     preFormUpdateBuffer(b);
2274     TRAP_OFF;
2275     return b;
2276 }
2277
2278 #define TAG_IS(s,tag,len)\
2279   (strncasecmp(s,tag,len)==0&&(s[len] == '>' || IS_SPACE((int)s[len])))
2280
2281 static char *
2282 has_hidden_link(struct readbuffer *obuf, int cmd)
2283 {
2284     Str line = obuf->line;
2285     struct link_stack *p;
2286
2287     if (Strlastchar(line) != '>')
2288         return NULL;
2289
2290     for (p = link_stack; p; p = p->next)
2291         if (p->cmd == cmd)
2292             break;
2293     if (!p)
2294         return NULL;
2295
2296     if (obuf->pos == p->pos)
2297         return line->ptr + p->offset;
2298
2299     return NULL;
2300 }
2301
2302 static void
2303 push_link(int cmd, int offset, int pos)
2304 {
2305     struct link_stack *p;
2306     p = New(struct link_stack);
2307     p->cmd = cmd;
2308     p->offset = offset;
2309     p->pos = pos;
2310     p->next = link_stack;
2311     link_stack = p;
2312 }
2313
2314 static int
2315 is_period_char(unsigned char *ch)
2316 {
2317     switch (*ch) {
2318     case ',':
2319     case '.':
2320     case ':':
2321     case ';':
2322     case '?':
2323     case '!':
2324     case ')':
2325     case ']':
2326     case '}':
2327     case '>':
2328         return 1;
2329     default:
2330         return 0;
2331     }
2332 }
2333
2334 static int
2335 is_beginning_char(unsigned char *ch)
2336 {
2337     switch (*ch) {
2338     case '(':
2339     case '[':
2340     case '{':
2341     case '`':
2342     case '<':
2343         return 1;
2344     default:
2345         return 0;
2346     }
2347 }
2348
2349 static int
2350 is_word_char(unsigned char *ch)
2351 {
2352     Lineprop ctype = get_mctype(ch);
2353
2354 #ifdef USE_M17N
2355     if (ctype & (PC_CTRL | PC_KANJI | PC_UNKNOWN))
2356         return 0;
2357     if (ctype & (PC_WCHAR1 | PC_WCHAR2))
2358         return 1;
2359 #else
2360     if (ctype == PC_CTRL)
2361         return 0;
2362 #endif
2363
2364     if (IS_ALNUM(*ch))
2365         return 1;
2366
2367     switch (*ch) {
2368     case ',':
2369     case '.':
2370     case ':':
2371     case '\"':                  /* " */
2372     case '\'':
2373     case '$':
2374     case '%':
2375     case '*':
2376     case '+':
2377     case '-':
2378     case '@':
2379     case '~':
2380     case '_':
2381         return 1;
2382     }
2383 #ifdef USE_M17N
2384     if (*ch == NBSP_CODE)
2385         return 1;
2386 #else
2387     if (*ch == TIMES_CODE || *ch == DIVIDE_CODE || *ch == ANSP_CODE)
2388         return 0;
2389     if (*ch >= AGRAVE_CODE || *ch == NBSP_CODE)
2390         return 1;
2391 #endif
2392     return 0;
2393 }
2394
2395 #ifdef USE_M17N
2396 static int
2397 is_combining_char(unsigned char *ch)
2398 {
2399     Lineprop ctype = get_mctype(ch);
2400
2401     if (ctype & PC_WCHAR2)
2402         return 1;
2403     return 0;
2404 }
2405 #endif
2406
2407 int
2408 is_boundary(unsigned char *ch1, unsigned char *ch2)
2409 {
2410     if (!*ch1 || !*ch2)
2411         return 1;
2412
2413     if (*ch1 == ' ' && *ch2 == ' ')
2414         return 0;
2415
2416     if (*ch1 != ' ' && is_period_char(ch2))
2417         return 0;
2418
2419     if (*ch2 != ' ' && is_beginning_char(ch1))
2420         return 0;
2421
2422 #ifdef USE_M17N
2423     if (is_combining_char(ch2))
2424         return 0;
2425 #endif
2426     if (is_word_char(ch1) && is_word_char(ch2))
2427         return 0;
2428
2429     return 1;
2430 }
2431
2432
2433 static void
2434 set_breakpoint(struct readbuffer *obuf, int tag_length)
2435 {
2436     obuf->bp.len = obuf->line->length;
2437     obuf->bp.pos = obuf->pos;
2438     obuf->bp.tlen = tag_length;
2439     obuf->bp.flag = obuf->flag;
2440 #ifdef FORMAT_NICE
2441     obuf->bp.flag &= ~RB_FILL;
2442 #endif                          /* FORMAT_NICE */
2443     obuf->bp.top_margin = obuf->top_margin;
2444     obuf->bp.bottom_margin = obuf->bottom_margin;
2445
2446     if (!obuf->bp.init_flag)
2447         return;
2448
2449     bcopy((void *)&obuf->anchor, (void *)&obuf->bp.anchor,
2450           sizeof(obuf->anchor));
2451     obuf->bp.img_alt = obuf->img_alt;
2452     obuf->bp.in_bold = obuf->in_bold;
2453     obuf->bp.in_italic = obuf->in_italic;
2454     obuf->bp.in_under = obuf->in_under;
2455     obuf->bp.in_strike = obuf->in_strike;
2456     obuf->bp.in_ins = obuf->in_ins;
2457     obuf->bp.nobr_level = obuf->nobr_level;
2458     obuf->bp.prev_ctype = obuf->prev_ctype;
2459     obuf->bp.init_flag = 0;
2460 }
2461
2462 static void
2463 back_to_breakpoint(struct readbuffer *obuf)
2464 {
2465     obuf->flag = obuf->bp.flag;
2466     bcopy((void *)&obuf->bp.anchor, (void *)&obuf->anchor,
2467           sizeof(obuf->anchor));
2468     obuf->img_alt = obuf->bp.img_alt;
2469     obuf->in_bold = obuf->bp.in_bold;
2470     obuf->in_italic = obuf->bp.in_italic;
2471     obuf->in_under = obuf->bp.in_under;
2472     obuf->in_strike = obuf->bp.in_strike;
2473     obuf->in_ins = obuf->bp.in_ins;
2474     obuf->prev_ctype = obuf->bp.prev_ctype;
2475     obuf->pos = obuf->bp.pos;
2476     obuf->top_margin = obuf->bp.top_margin;
2477     obuf->bottom_margin = obuf->bp.bottom_margin;
2478     if (obuf->flag & RB_NOBR)
2479         obuf->nobr_level = obuf->bp.nobr_level;
2480 }
2481
2482 static void
2483 append_tags(struct readbuffer *obuf)
2484 {
2485     int i;
2486     int len = obuf->line->length;
2487     int set_bp = 0;
2488
2489     for (i = 0; i < obuf->tag_sp; i++) {
2490         switch (obuf->tag_stack[i]->cmd) {
2491         case HTML_A:
2492         case HTML_IMG_ALT:
2493         case HTML_B:
2494         case HTML_U:
2495         case HTML_I:
2496         case HTML_S:
2497             push_link(obuf->tag_stack[i]->cmd, obuf->line->length, obuf->pos);
2498             break;
2499         }
2500         Strcat_charp(obuf->line, obuf->tag_stack[i]->cmdname);
2501         switch (obuf->tag_stack[i]->cmd) {
2502         case HTML_NOBR:
2503             if (obuf->nobr_level > 1)
2504                 break;
2505         case HTML_WBR:
2506             set_bp = 1;
2507             break;
2508         }
2509     }
2510     obuf->tag_sp = 0;
2511     if (set_bp)
2512         set_breakpoint(obuf, obuf->line->length - len);
2513 }
2514
2515 static void
2516 push_tag(struct readbuffer *obuf, char *cmdname, int cmd)
2517 {
2518     obuf->tag_stack[obuf->tag_sp] = New(struct cmdtable);
2519     obuf->tag_stack[obuf->tag_sp]->cmdname = allocStr(cmdname, -1);
2520     obuf->tag_stack[obuf->tag_sp]->cmd = cmd;
2521     obuf->tag_sp++;
2522     if (obuf->tag_sp >= TAG_STACK_SIZE || obuf->flag & (RB_SPECIAL & ~RB_NOBR))
2523         append_tags(obuf);
2524 }
2525
2526 static void
2527 push_nchars(struct readbuffer *obuf, int width,
2528             char *str, int len, Lineprop mode)
2529 {
2530     append_tags(obuf);
2531     Strcat_charp_n(obuf->line, str, len);
2532     obuf->pos += width;
2533     if (width > 0) {
2534         set_prevchar(obuf->prevchar, str, len);
2535         obuf->prev_ctype = mode;
2536     }
2537     obuf->flag |= RB_NFLUSHED;
2538 }
2539
2540 #define push_charp(obuf, width, str, mode)\
2541 push_nchars(obuf, width, str, strlen(str), mode)
2542
2543 #define push_str(obuf, width, str, mode)\
2544 push_nchars(obuf, width, str->ptr, str->length, mode)
2545
2546 static void
2547 check_breakpoint(struct readbuffer *obuf, int pre_mode, char *ch)
2548 {
2549     int tlen, len = obuf->line->length;
2550
2551     append_tags(obuf);
2552     if (pre_mode)
2553         return;
2554     tlen = obuf->line->length - len;
2555     if (tlen > 0
2556         || is_boundary((unsigned char *)obuf->prevchar->ptr,
2557                        (unsigned char *)ch))
2558         set_breakpoint(obuf, tlen);
2559 }
2560
2561 static void
2562 push_char(struct readbuffer *obuf, int pre_mode, char ch)
2563 {
2564     check_breakpoint(obuf, pre_mode, &ch);
2565     Strcat_char(obuf->line, ch);
2566     obuf->pos++;
2567     set_prevchar(obuf->prevchar, &ch, 1);
2568     if (ch != ' ')
2569         obuf->prev_ctype = PC_ASCII;
2570     obuf->flag |= RB_NFLUSHED;
2571 }
2572
2573 #define PUSH(c) push_char(obuf, obuf->flag & RB_SPECIAL, c)
2574
2575 static void
2576 push_spaces(struct readbuffer *obuf, int pre_mode, int width)
2577 {
2578     int i;
2579
2580     if (width <= 0)
2581         return;
2582     check_breakpoint(obuf, pre_mode, " ");
2583     for (i = 0; i < width; i++)
2584         Strcat_char(obuf->line, ' ');
2585     obuf->pos += width;
2586     set_space_to_prevchar(obuf->prevchar);
2587     obuf->flag |= RB_NFLUSHED;
2588 }
2589
2590 static void
2591 proc_mchar(struct readbuffer *obuf, int pre_mode,
2592            int width, char **str, Lineprop mode)
2593 {
2594     check_breakpoint(obuf, pre_mode, *str);
2595     obuf->pos += width;
2596     Strcat_charp_n(obuf->line, *str, get_mclen(*str));
2597     if (width > 0) {
2598         set_prevchar(obuf->prevchar, *str, 1);
2599         if (**str != ' ')
2600             obuf->prev_ctype = mode;
2601     }
2602     (*str) += get_mclen(*str);
2603     obuf->flag |= RB_NFLUSHED;
2604 }
2605
2606 void
2607 push_render_image(Str str, int width, int limit,
2608                   struct html_feed_environ *h_env)
2609 {
2610     struct readbuffer *obuf = h_env->obuf;
2611     int indent = h_env->envs[h_env->envc].indent;
2612
2613     push_spaces(obuf, 1, (limit - width) / 2);
2614     push_str(obuf, width, str, PC_ASCII);
2615     push_spaces(obuf, 1, (limit - width + 1) / 2);
2616     if (width > 0)
2617         flushline(h_env, obuf, indent, 0, h_env->limit);
2618 }
2619
2620 static int
2621 sloppy_parse_line(char **str)
2622 {
2623     if (**str == '<') {
2624         while (**str && **str != '>')
2625             (*str)++;
2626         if (**str == '>')
2627             (*str)++;
2628         return 1;
2629     }
2630     else {
2631         while (**str && **str != '<')
2632             (*str)++;
2633         return 0;
2634     }
2635 }
2636
2637 static void
2638 passthrough(struct readbuffer *obuf, char *str, int back)
2639 {
2640     int cmd;
2641     Str tok = Strnew();
2642     char *str_bak;
2643
2644     if (back) {
2645         Str str_save = Strnew_charp(str);
2646         Strshrink(obuf->line, obuf->line->ptr + obuf->line->length - str);
2647         str = str_save->ptr;
2648     }
2649     while (*str) {
2650         str_bak = str;
2651         if (sloppy_parse_line(&str)) {
2652             char *q = str_bak;
2653             cmd = gethtmlcmd(&q);
2654             if (back) {
2655                 struct link_stack *p;
2656                 for (p = link_stack; p; p = p->next) {
2657                     if (p->cmd == cmd) {
2658                         link_stack = p->next;
2659                         break;
2660                     }
2661                 }
2662                 back = 0;
2663             }
2664             else {
2665                 Strcat_charp_n(tok, str_bak, str - str_bak);
2666                 push_tag(obuf, tok->ptr, cmd);
2667                 Strclear(tok);
2668             }
2669         }
2670         else {
2671             push_nchars(obuf, 0, str_bak, str - str_bak, obuf->prev_ctype);
2672         }
2673     }
2674 }
2675
2676 #if 0
2677 int
2678 is_blank_line(char *line, int indent)
2679 {
2680     int i, is_blank = 0;
2681
2682     for (i = 0; i < indent; i++) {
2683         if (line[i] == '\0') {
2684             is_blank = 1;
2685         }
2686         else if (line[i] != ' ') {
2687             break;
2688         }
2689     }
2690     if (i == indent && line[i] == '\0')
2691         is_blank = 1;
2692     return is_blank;
2693 }
2694 #endif
2695
2696 void
2697 fillline(struct readbuffer *obuf, int indent)
2698 {
2699     push_spaces(obuf, 1, indent - obuf->pos);
2700     obuf->flag &= ~RB_NFLUSHED;
2701 }
2702
2703 void
2704 flushline(struct html_feed_environ *h_env, struct readbuffer *obuf, int indent,
2705           int force, int width)
2706 {
2707     TextLineList *buf = h_env->buf;
2708     FILE *f = h_env->f;
2709     Str line = obuf->line, pass = NULL;
2710     char *hidden_anchor = NULL, *hidden_img = NULL, *hidden_bold = NULL,
2711         *hidden_under = NULL, *hidden_italic = NULL, *hidden_strike = NULL,
2712         *hidden_ins = NULL, *hidden = NULL;
2713
2714 #ifdef DEBUG
2715     if (w3m_debug) {
2716         FILE *df = fopen("zzzproc1", "a");
2717         fprintf(df, "flushline(%s,%d,%d,%d)\n", obuf->line->ptr, indent, force,
2718                 width);
2719         if (buf) {
2720             TextLineListItem *p;
2721             for (p = buf->first; p; p = p->next) {
2722                 fprintf(df, "buf=\"%s\"\n", p->ptr->line->ptr);
2723             }
2724         }
2725         fclose(df);
2726     }
2727 #endif
2728
2729     if (!(obuf->flag & (RB_SPECIAL & ~RB_NOBR)) && Strlastchar(line) == ' ') {
2730         Strshrink(line, 1);
2731         obuf->pos--;
2732     }
2733
2734     append_tags(obuf);
2735
2736     if (obuf->anchor.url)
2737         hidden = hidden_anchor = has_hidden_link(obuf, HTML_A);
2738     if (obuf->img_alt) {
2739         if ((hidden_img = has_hidden_link(obuf, HTML_IMG_ALT)) != NULL) {
2740             if (!hidden || hidden_img < hidden)
2741                 hidden = hidden_img;
2742         }
2743     }
2744     if (obuf->in_bold) {
2745         if ((hidden_bold = has_hidden_link(obuf, HTML_B)) != NULL) {
2746             if (!hidden || hidden_bold < hidden)
2747                 hidden = hidden_bold;
2748         }
2749     }
2750     if (obuf->in_italic) {
2751         if ((hidden_italic = has_hidden_link(obuf, HTML_I)) != NULL) {
2752             if (!hidden || hidden_italic < hidden)
2753                 hidden = hidden_italic;
2754         }
2755     }
2756     if (obuf->in_under) {
2757         if ((hidden_under = has_hidden_link(obuf, HTML_U)) != NULL) {
2758             if (!hidden || hidden_under < hidden)
2759                 hidden = hidden_under;
2760         }
2761     }
2762     if (obuf->in_strike) {
2763         if ((hidden_strike = has_hidden_link(obuf, HTML_S)) != NULL) {
2764             if (!hidden || hidden_strike < hidden)
2765                 hidden = hidden_strike;
2766         }
2767     }
2768     if (obuf->in_ins) {
2769         if ((hidden_ins = has_hidden_link(obuf, HTML_INS)) != NULL) {
2770             if (!hidden || hidden_ins < hidden)
2771                 hidden = hidden_ins;
2772         }
2773     }
2774     if (hidden) {
2775         pass = Strnew_charp(hidden);
2776         Strshrink(line, line->ptr + line->length - hidden);
2777     }
2778
2779     if (!(obuf->flag & (RB_SPECIAL & ~RB_NOBR)) && obuf->pos > width) {
2780         char *tp = &line->ptr[obuf->bp.len - obuf->bp.tlen];
2781         char *ep = &line->ptr[line->length];
2782
2783         if (obuf->bp.pos == obuf->pos && tp <= ep &&
2784             tp > line->ptr && tp[-1] == ' ') {
2785             bcopy(tp, tp - 1, ep - tp + 1);
2786             line->length--;
2787             obuf->pos--;
2788         }
2789     }
2790
2791     if (obuf->anchor.url && !hidden_anchor)
2792         Strcat_charp(line, "</a>");
2793     if (obuf->img_alt && !hidden_img)
2794         Strcat_charp(line, "</img_alt>");
2795     if (obuf->in_bold && !hidden_bold)
2796         Strcat_charp(line, "</b>");
2797     if (obuf->in_italic && !hidden_italic)
2798         Strcat_charp(line, "</i>");
2799     if (obuf->in_under && !hidden_under)
2800         Strcat_charp(line, "</u>");
2801     if (obuf->in_strike && !hidden_strike)
2802         Strcat_charp(line, "</s>");
2803     if (obuf->in_ins && !hidden_ins)
2804         Strcat_charp(line, "</ins>");
2805
2806     if (obuf->top_margin > 0) {
2807         int i;
2808         struct html_feed_environ h;
2809         struct readbuffer o;
2810         struct environment e[1];
2811
2812         init_henv(&h, &o, e, 1, NULL, width, indent);
2813         o.line = Strnew_size(width + 20);
2814         o.pos = obuf->pos;
2815         o.flag = obuf->flag;
2816         o.top_margin = -1;
2817         o.bottom_margin = -1;
2818         Strcat_charp(o.line, "<pre_int>");
2819         for (i = 0; i < o.pos; i++)
2820             Strcat_char(o.line, ' ');
2821         Strcat_charp(o.line, "</pre_int>");
2822         for (i = 0; i < obuf->top_margin; i++)
2823             flushline(h_env, &o, indent, force, width);
2824     }
2825
2826     if (force == 1 || obuf->flag & RB_NFLUSHED) {
2827         TextLine *lbuf = newTextLine(line, obuf->pos);
2828         if (RB_GET_ALIGN(obuf) == RB_CENTER) {
2829             align(lbuf, width, ALIGN_CENTER);
2830         }
2831         else if (RB_GET_ALIGN(obuf) == RB_RIGHT) {
2832             align(lbuf, width, ALIGN_RIGHT);
2833         }
2834         else if (RB_GET_ALIGN(obuf) == RB_LEFT && obuf->flag & RB_INTABLE) {
2835             align(lbuf, width, ALIGN_LEFT);
2836         }
2837 #ifdef FORMAT_NICE
2838         else if (obuf->flag & RB_FILL) {
2839             char *p;
2840             int rest, rrest;
2841             int nspace, d, i;
2842
2843             rest = width - get_Str_strwidth(line);
2844             if (rest > 1) {
2845                 nspace = 0;
2846                 for (p = line->ptr + indent; *p; p++) {
2847                     if (*p == ' ')
2848                         nspace++;
2849                 }
2850                 if (nspace > 0) {
2851                     int indent_here = 0;
2852                     d = rest / nspace;
2853                     p = line->ptr;
2854                     while (IS_SPACE(*p)) {
2855                         p++;
2856                         indent_here++;
2857                     }
2858                     rrest = rest - d * nspace;
2859                     line = Strnew_size(width + 1);
2860                     for (i = 0; i < indent_here; i++)
2861                         Strcat_char(line, ' ');
2862                     for (; *p; p++) {
2863                         Strcat_char(line, *p);
2864                         if (*p == ' ') {
2865                             for (i = 0; i < d; i++)
2866                                 Strcat_char(line, ' ');
2867                             if (rrest > 0) {
2868                                 Strcat_char(line, ' ');
2869                                 rrest--;
2870                             }
2871                         }
2872                     }
2873                     lbuf = newTextLine(line, width);
2874                 }
2875             }
2876         }
2877 #endif                          /* FORMAT_NICE */
2878 #ifdef TABLE_DEBUG
2879         if (w3m_debug) {
2880             FILE *f = fopen("zzzproc1", "a");
2881             fprintf(f, "pos=%d,%d, maxlimit=%d\n",
2882                     visible_length(lbuf->line->ptr), lbuf->pos,
2883                     h_env->maxlimit);
2884             fclose(f);
2885         }
2886 #endif
2887         if (lbuf->pos > h_env->maxlimit)
2888             h_env->maxlimit = lbuf->pos;
2889         if (buf)
2890             pushTextLine(buf, lbuf);
2891         else if (f) {
2892             Strfputs(Str_conv_to_halfdump(lbuf->line), f);
2893             fputc('\n', f);
2894         }
2895         if (obuf->flag & RB_SPECIAL || obuf->flag & RB_NFLUSHED)
2896             h_env->blank_lines = 0;
2897         else
2898             h_env->blank_lines++;
2899     }
2900     else {
2901         char *p = line->ptr, *q;
2902         Str tmp = Strnew(), tmp2 = Strnew();
2903
2904 #define APPEND(str) \
2905         if (buf) \
2906             appendTextLine(buf,(str),0); \
2907         else if (f) \
2908             Strfputs((str),f)
2909
2910         while (*p) {
2911             q = p;
2912             if (sloppy_parse_line(&p)) {
2913                 Strcat_charp_n(tmp, q, p - q);
2914                 if (force == 2) {
2915                     APPEND(tmp);
2916                 }
2917                 else
2918                     Strcat(tmp2, tmp);
2919                 Strclear(tmp);
2920             }
2921         }
2922         if (force == 2) {
2923             if (pass) {
2924                 APPEND(pass);
2925             }
2926             pass = NULL;
2927         }
2928         else {
2929             if (pass)
2930                 Strcat(tmp2, pass);
2931             pass = tmp2;
2932         }
2933     }
2934
2935     if (obuf->bottom_margin > 0) {
2936         int i;
2937         struct html_feed_environ h;
2938         struct readbuffer o;
2939         struct environment e[1];
2940
2941         init_henv(&h, &o, e, 1, NULL, width, indent);
2942         o.line = Strnew_size(width + 20);
2943         o.pos = obuf->pos;
2944         o.flag = obuf->flag;
2945         o.top_margin = -1;
2946         o.bottom_margin = -1;
2947         Strcat_charp(o.line, "<pre_int>");
2948         for (i = 0; i < o.pos; i++)
2949             Strcat_char(o.line, ' ');
2950         Strcat_charp(o.line, "</pre_int>");
2951         for (i = 0; i < obuf->bottom_margin; i++)
2952             flushline(h_env, &o, indent, force, width);
2953     }
2954     if (obuf->top_margin < 0 || obuf->bottom_margin < 0)
2955         return;
2956
2957     obuf->line = Strnew_size(256);
2958     obuf->pos = 0;
2959     obuf->top_margin = 0;
2960     obuf->bottom_margin = 0;
2961     set_space_to_prevchar(obuf->prevchar);
2962     obuf->bp.init_flag = 1;
2963     obuf->flag &= ~RB_NFLUSHED;
2964     set_breakpoint(obuf, 0);
2965     obuf->prev_ctype = PC_ASCII;
2966     link_stack = NULL;
2967     fillline(obuf, indent);
2968     if (pass)
2969         passthrough(obuf, pass->ptr, 0);
2970     if (!hidden_anchor && obuf->anchor.url) {
2971         Str tmp;
2972         if (obuf->anchor.hseq > 0)
2973             obuf->anchor.hseq = -obuf->anchor.hseq;
2974         tmp = Sprintf("<A HSEQ=\"%d\" HREF=\"", obuf->anchor.hseq);
2975         Strcat_charp(tmp, html_quote(obuf->anchor.url));
2976         if (obuf->anchor.target) {
2977             Strcat_charp(tmp, "\" TARGET=\"");
2978             Strcat_charp(tmp, html_quote(obuf->anchor.target));
2979         }
2980         if (obuf->anchor.referer) {
2981             Strcat_charp(tmp, "\" REFERER=\"");
2982             Strcat_charp(tmp, html_quote(obuf->anchor.referer));
2983         }
2984         if (obuf->anchor.title) {
2985             Strcat_charp(tmp, "\" TITLE=\"");
2986             Strcat_charp(tmp, html_quote(obuf->anchor.title));
2987         }
2988         if (obuf->anchor.accesskey) {
2989             char *c = html_quote_char(obuf->anchor.accesskey);
2990             Strcat_charp(tmp, "\" ACCESSKEY=\"");
2991             if (c)
2992                 Strcat_charp(tmp, c);
2993             else
2994                 Strcat_char(tmp, obuf->anchor.accesskey);
2995         }
2996         Strcat_charp(tmp, "\">");
2997         push_tag(obuf, tmp->ptr, HTML_A);
2998     }
2999     if (!hidden_img && obuf->img_alt) {
3000         Str tmp = Strnew_charp("<IMG_ALT SRC=\"");
3001         Strcat_charp(tmp, html_quote(obuf->img_alt->ptr));
3002         Strcat_charp(tmp, "\">");
3003         push_tag(obuf, tmp->ptr, HTML_IMG_ALT);
3004     }
3005     if (!hidden_bold && obuf->in_bold)
3006         push_tag(obuf, "<B>", HTML_B);
3007     if (!hidden_italic && obuf->in_italic)
3008         push_tag(obuf, "<I>", HTML_I);
3009     if (!hidden_under && obuf->in_under)
3010         push_tag(obuf, "<U>", HTML_U);
3011     if (!hidden_strike && obuf->in_strike)
3012         push_tag(obuf, "<S>", HTML_S);
3013     if (!hidden_ins && obuf->in_ins)
3014         push_tag(obuf, "<INS>", HTML_INS);
3015 }
3016
3017 void
3018 do_blankline(struct html_feed_environ *h_env, struct readbuffer *obuf,
3019              int indent, int indent_incr, int width)
3020 {
3021     if (h_env->blank_lines == 0)
3022         flushline(h_env, obuf, indent, 1, width);
3023 }
3024
3025 void
3026 purgeline(struct html_feed_environ *h_env)
3027 {
3028     char *p, *q;
3029     Str tmp;
3030
3031     if (h_env->buf == NULL || h_env->blank_lines == 0)
3032         return;
3033
3034     p = rpopTextLine(h_env->buf)->line->ptr;
3035     tmp = Strnew();
3036     while (*p) {
3037         q = p;
3038         if (sloppy_parse_line(&p)) {
3039             Strcat_charp_n(tmp, q, p - q);
3040         }
3041     }
3042     appendTextLine(h_env->buf, tmp, 0);
3043     h_env->blank_lines--;
3044 }
3045
3046 static int
3047 close_effect0(struct readbuffer *obuf, int cmd)
3048 {
3049     int i;
3050     char *p;
3051
3052     for (i = obuf->tag_sp - 1; i >= 0; i--) {
3053         if (obuf->tag_stack[i]->cmd == cmd)
3054             break;
3055     }
3056     if (i >= 0) {
3057         obuf->tag_sp--;
3058         bcopy(&obuf->tag_stack[i + 1], &obuf->tag_stack[i],
3059               (obuf->tag_sp - i) * sizeof(struct cmdtable *));
3060         return 1;
3061     }
3062     else if ((p = has_hidden_link(obuf, cmd)) != NULL) {
3063         passthrough(obuf, p, 1);
3064         return 1;
3065     }
3066     return 0;
3067 }
3068
3069 static void
3070 close_anchor(struct html_feed_environ *h_env, struct readbuffer *obuf)
3071 {
3072     if (obuf->anchor.url) {
3073         int i;
3074         char *p = NULL;
3075         int is_erased = 0;
3076
3077         for (i = obuf->tag_sp - 1; i >= 0; i--) {
3078             if (obuf->tag_stack[i]->cmd == HTML_A)
3079                 break;
3080         }
3081         if (i < 0 && obuf->anchor.hseq > 0 && Strlastchar(obuf->line) == ' ') {
3082             Strshrink(obuf->line, 1);
3083             obuf->pos--;
3084             is_erased = 1;
3085         }
3086
3087         if (i >= 0 || (p = has_hidden_link(obuf, HTML_A))) {
3088             if (obuf->anchor.hseq > 0) {
3089                 HTMLlineproc1(ANSP, h_env);
3090                 set_space_to_prevchar(obuf->prevchar);
3091             }
3092             else {
3093                 if (i >= 0) {
3094                     obuf->tag_sp--;
3095                     bcopy(&obuf->tag_stack[i + 1], &obuf->tag_stack[i],
3096                           (obuf->tag_sp - i) * sizeof(struct cmdtable *));
3097                 }
3098                 else {
3099                     passthrough(obuf, p, 1);
3100                 }
3101                 bzero((void *)&obuf->anchor, sizeof(obuf->anchor));
3102                 return;
3103             }
3104             is_erased = 0;
3105         }
3106         if (is_erased) {
3107             Strcat_char(obuf->line, ' ');
3108             obuf->pos++;
3109         }
3110
3111         push_tag(obuf, "</a>", HTML_N_A);
3112     }
3113     bzero((void *)&obuf->anchor, sizeof(obuf->anchor));
3114 }
3115
3116 void
3117 save_fonteffect(struct html_feed_environ *h_env, struct readbuffer *obuf)
3118 {
3119     if (obuf->fontstat_sp < FONT_STACK_SIZE)
3120         bcopy(obuf->fontstat, obuf->fontstat_stack[obuf->fontstat_sp],
3121               FONTSTAT_SIZE);
3122     obuf->fontstat_sp++;
3123     if (obuf->in_bold)
3124         push_tag(obuf, "</b>", HTML_N_B);
3125     if (obuf->in_italic)
3126         push_tag(obuf, "</i>", HTML_N_I);
3127     if (obuf->in_under)
3128         push_tag(obuf, "</u>", HTML_N_U);
3129     if (obuf->in_strike)
3130         push_tag(obuf, "</s>", HTML_N_S);
3131     if (obuf->in_ins)
3132         push_tag(obuf, "</ins>", HTML_N_INS);
3133     bzero(obuf->fontstat, FONTSTAT_SIZE);
3134 }
3135
3136 void
3137 restore_fonteffect(struct html_feed_environ *h_env, struct readbuffer *obuf)
3138 {
3139     if (obuf->fontstat_sp > 0)
3140         obuf->fontstat_sp--;
3141     if (obuf->fontstat_sp < FONT_STACK_SIZE)
3142         bcopy(obuf->fontstat_stack[obuf->fontstat_sp], obuf->fontstat,
3143               FONTSTAT_SIZE);
3144     if (obuf->in_bold)
3145         push_tag(obuf, "<b>", HTML_B);
3146     if (obuf->in_italic)
3147         push_tag(obuf, "<i>", HTML_I);
3148     if (obuf->in_under)
3149         push_tag(obuf, "<u>", HTML_U);
3150     if (obuf->in_strike)
3151         push_tag(obuf, "<s>", HTML_S);
3152     if (obuf->in_ins)
3153         push_tag(obuf, "<ins>", HTML_INS);
3154 }
3155
3156 static Str
3157 process_title(struct parsed_tag *tag)
3158 {
3159     cur_title = Strnew();
3160     return NULL;
3161 }
3162
3163 static Str
3164 process_n_title(struct parsed_tag *tag)
3165 {
3166     Str tmp;
3167
3168     if (!cur_title)
3169         return NULL;
3170     Strremovefirstspaces(cur_title);
3171     Strremovetrailingspaces(cur_title);
3172     tmp = Strnew_m_charp("<title_alt title=\"",
3173                          html_quote(cur_title->ptr), "\">", NULL);
3174     cur_title = NULL;
3175     return tmp;
3176 }
3177
3178 static void
3179 feed_title(char *str)
3180 {
3181     if (!cur_title)
3182         return;
3183     while (*str) {
3184         if (*str == '&')
3185             Strcat_charp(cur_title, getescapecmd(&str));
3186         else if (*str == '\n' || *str == '\r') {
3187             Strcat_char(cur_title, ' ');
3188             str++;
3189         }
3190         else
3191             Strcat_char(cur_title, *(str++));
3192     }
3193 }
3194
3195 Str
3196 process_img(struct parsed_tag *tag, int width)
3197 {
3198     char *p, *q, *r, *r2 = NULL, *s, *t;
3199 #ifdef USE_IMAGE
3200     int w, i, nw, ni = 1, n, w0 = -1, i0 = -1;
3201     int align, xoffset, yoffset, top, bottom, ismap = 0;
3202     int use_image = activeImage && displayImage;
3203 #else
3204     int w, i, nw, n;
3205 #endif
3206     int pre_int = FALSE, ext_pre_int = FALSE;
3207     Str tmp = Strnew();
3208
3209     if (!parsedtag_get_value(tag, ATTR_SRC, &p))
3210         return tmp;
3211     p = remove_space(p);
3212     q = NULL;
3213     parsedtag_get_value(tag, ATTR_ALT, &q);
3214     t = q;
3215     parsedtag_get_value(tag, ATTR_TITLE, &t);
3216     w = -1;
3217     if (parsedtag_get_value(tag, ATTR_WIDTH, &w)) {
3218         if (w < 0) {
3219             if (width > 0)
3220                 w = (int)(-width * pixel_per_char * w / 100 + 0.5);
3221             else
3222                 w = -1;
3223         }
3224 #ifdef USE_IMAGE
3225         if (use_image) {
3226             if (w > 0) {
3227                 w = (int)(w * image_scale / 100 + 0.5);
3228                 if (w == 0)
3229                     w = 1;
3230                 else if (w > MAX_IMAGE_SIZE)
3231                     w = MAX_IMAGE_SIZE;
3232             }
3233         }
3234 #endif
3235     }
3236 #ifdef USE_IMAGE
3237     if (use_image) {
3238         i = -1;
3239         if (parsedtag_get_value(tag, ATTR_HEIGHT, &i)) {
3240             if (i > 0) {
3241                 i = (int)(i * image_scale / 100 + 0.5);
3242                 if (i == 0)
3243                     i = 1;
3244                 else if (i > MAX_IMAGE_SIZE)
3245                     i = MAX_IMAGE_SIZE;
3246             }
3247             else {
3248                 i = -1;
3249             }
3250         }
3251         align = -1;
3252         parsedtag_get_value(tag, ATTR_ALIGN, &align);
3253         ismap = 0;
3254         if (parsedtag_exists(tag, ATTR_ISMAP))
3255             ismap = 1;
3256     }
3257     else
3258 #endif
3259         parsedtag_get_value(tag, ATTR_HEIGHT, &i);
3260     r = NULL;
3261     parsedtag_get_value(tag, ATTR_USEMAP, &r);
3262     if (parsedtag_exists(tag, ATTR_PRE_INT))
3263         ext_pre_int = TRUE;
3264
3265     tmp = Strnew_size(128);
3266 #ifdef USE_IMAGE
3267     if (use_image) {
3268         switch (align) {
3269         case ALIGN_LEFT:
3270             Strcat_charp(tmp, "<div_int align=left>");
3271             break;
3272         case ALIGN_CENTER:
3273             Strcat_charp(tmp, "<div_int align=center>");
3274             break;
3275         case ALIGN_RIGHT:
3276             Strcat_charp(tmp, "<div_int align=right>");
3277             break;
3278         }
3279     }
3280 #endif
3281     if (r) {
3282         Str tmp2;
3283         r2 = strchr(r, '#');
3284         s = "<form_int method=internal action=map>";
3285         tmp2 = process_form(parse_tag(&s, TRUE));
3286         if (tmp2)
3287             Strcat(tmp, tmp2);
3288         Strcat(tmp, Sprintf("<input_alt fid=\"%d\" "
3289                             "type=hidden name=link value=\"", cur_form_id));
3290         Strcat_charp(tmp, html_quote((r2) ? r2 + 1 : r));
3291         Strcat(tmp, Sprintf("\"><input_alt hseq=\"%d\" fid=\"%d\" "
3292                             "type=submit no_effect=true>",
3293                             cur_hseq++, cur_form_id));
3294     }
3295 #ifdef USE_IMAGE
3296     if (use_image) {
3297         w0 = w;
3298         i0 = i;
3299         if (w < 0 || i < 0) {
3300             Image image;
3301             ParsedURL u;
3302
3303 #ifdef USE_M17N
3304             parseURL2(wc_conv(p, InnerCharset, cur_document_charset)->ptr, &u,
3305                       cur_baseURL);
3306 #else
3307             parseURL2(p, &u, cur_baseURL);
3308 #endif
3309             image.url = parsedURL2Str(&u)->ptr;
3310             if (!uncompressed_file_type(u.file, &image.ext))
3311                 image.ext = filename_extension(u.file, TRUE);
3312             image.cache = NULL;
3313             image.width = w;
3314             image.height = i;
3315
3316             image.cache = getImage(&image, cur_baseURL, IMG_FLAG_SKIP);
3317             if (image.cache && image.cache->width > 0 &&
3318                 image.cache->height > 0) {
3319                 w = w0 = image.cache->width;
3320                 i = i0 = image.cache->height;
3321             }
3322             if (w < 0)
3323                 w = 8 * pixel_per_char;
3324             if (i < 0)
3325                 i = pixel_per_line;
3326         }
3327         nw = (w > 3) ? (int)((w - 3) / pixel_per_char + 1) : 1;
3328         ni = (i > 3) ? (int)((i - 3) / pixel_per_line + 1) : 1;
3329         Strcat(tmp,
3330                Sprintf("<pre_int><img_alt hseq=\"%d\" src=\"", cur_iseq++));
3331         pre_int = TRUE;
3332     }
3333     else
3334 #endif
3335     {
3336         if (w < 0)
3337             w = 12 * pixel_per_char;
3338         nw = w ? (int)((w - 1) / pixel_per_char + 1) : 1;
3339         if (r) {
3340             Strcat_charp(tmp, "<pre_int>");
3341             pre_int = TRUE;
3342         }
3343         Strcat_charp(tmp, "<img_alt src=\"");
3344     }
3345     Strcat_charp(tmp, html_quote(p));
3346     Strcat_charp(tmp, "\"");
3347     if (t) {
3348         Strcat_charp(tmp, " title=\"");
3349         Strcat_charp(tmp, html_quote(t));
3350         Strcat_charp(tmp, "\"");
3351     }
3352 #ifdef USE_IMAGE
3353     if (use_image) {
3354         if (w0 >= 0)
3355             Strcat(tmp, Sprintf(" width=%d", w0));
3356         if (i0 >= 0)
3357             Strcat(tmp, Sprintf(" height=%d", i0));
3358         switch (align) {
3359         case ALIGN_TOP:
3360             top = 0;
3361             bottom = ni - 1;
3362             yoffset = 0;
3363             break;
3364         case ALIGN_MIDDLE:
3365             top = ni / 2;
3366             bottom = top;
3367             if (top * 2 == ni)
3368                 yoffset = (int)(((ni + 1) * pixel_per_line - i) / 2);
3369             else
3370                 yoffset = (int)((ni * pixel_per_line - i) / 2);
3371             break;
3372         case ALIGN_BOTTOM:
3373             top = ni - 1;
3374             bottom = 0;
3375             yoffset = (int)(ni * pixel_per_line - i);
3376             break;
3377         default:
3378             top = ni - 1;
3379             bottom = 0;
3380             if (ni == 1 && ni * pixel_per_line > i)
3381                 yoffset = 0;
3382             else {
3383                 yoffset = (int)(ni * pixel_per_line - i);
3384                 if (yoffset <= -2)
3385                     yoffset++;
3386             }
3387             break;
3388         }
3389         xoffset = (int)((nw * pixel_per_char - w) / 2);
3390         if (xoffset)
3391             Strcat(tmp, Sprintf(" xoffset=%d", xoffset));
3392         if (yoffset)
3393             Strcat(tmp, Sprintf(" yoffset=%d", yoffset));
3394         if (top)
3395             Strcat(tmp, Sprintf(" top_margin=%d", top));
3396         if (bottom)
3397             Strcat(tmp, Sprintf(" bottom_margin=%d", bottom));
3398         if (r) {
3399             Strcat_charp(tmp, " usemap=\"");
3400             Strcat_charp(tmp, html_quote((r2) ? r2 + 1 : r));
3401             Strcat_charp(tmp, "\"");
3402         }
3403         if (ismap)
3404             Strcat_charp(tmp, " ismap");
3405     }
3406 #endif
3407     Strcat_charp(tmp, ">");
3408     if (q != NULL && *q == '\0' && ignore_null_img_alt)
3409         q = NULL;
3410     if (q != NULL) {
3411         n = get_strwidth(q);
3412 #ifdef USE_IMAGE
3413         if (use_image) {
3414             if (n > nw) {
3415                 char *r;
3416                 for (r = q, n = 0; r; r += get_mclen(r), n += get_mcwidth(r)) {
3417                     if (n + get_mcwidth(r) > nw)
3418                         break;
3419                 }
3420                 Strcat_charp(tmp, html_quote(Strnew_charp_n(q, r - q)->ptr));
3421             }
3422             else
3423                 Strcat_charp(tmp, html_quote(q));
3424         }
3425         else
3426 #endif
3427             Strcat_charp(tmp, html_quote(q));
3428         goto img_end;
3429     }
3430     if (w > 0 && i > 0) {
3431         /* guess what the image is! */
3432         if (w < 32 && i < 48) {
3433             /* must be an icon or space */
3434             n = 1;
3435             if (strcasestr(p, "space") || strcasestr(p, "blank"))
3436                 Strcat_charp(tmp, "_");
3437             else {
3438                 if (w * i < 8 * 16)
3439                     Strcat_charp(tmp, "*");
3440                 else {
3441                     if (!pre_int) {
3442                         Strcat_charp(tmp, "<pre_int>");
3443                         pre_int = TRUE;
3444                     }
3445                     push_symbol(tmp, IMG_SYMBOL, symbol_width, 1);
3446                     n = symbol_width;
3447                 }
3448             }
3449             goto img_end;
3450         }
3451         if (w > 200 && i < 13) {
3452             /* must be a horizontal line */
3453             if (!pre_int) {
3454                 Strcat_charp(tmp, "<pre_int>");
3455                 pre_int = TRUE;
3456             }
3457             w = w / pixel_per_char / symbol_width;
3458             if (w <= 0)
3459                 w = 1;
3460             push_symbol(tmp, HR_SYMBOL, symbol_width, w);
3461             n = w * symbol_width;
3462             goto img_end;
3463         }
3464     }
3465     for (q = p; *q; q++) ;
3466     while (q > p && *q != '/')
3467         q--;
3468     if (*q == '/')
3469         q++;
3470     Strcat_char(tmp, '[');
3471     n = 1;
3472     p = q;
3473     for (; *q; q++) {
3474         if (!IS_ALNUM(*q) && *q != '_' && *q != '-') {
3475             break;
3476         }
3477         Strcat_char(tmp, *q);
3478         n++;
3479         if (n + 1 >= nw)
3480             break;
3481     }
3482     Strcat_char(tmp, ']');
3483     n++;
3484   img_end:
3485 #ifdef USE_IMAGE
3486     if (use_image) {
3487         for (; n < nw; n++)
3488             Strcat_char(tmp, ' ');
3489     }
3490 #endif
3491     Strcat_charp(tmp, "</img_alt>");
3492     if (pre_int && !ext_pre_int)
3493         Strcat_charp(tmp, "</pre_int>");
3494     if (r) {
3495         Strcat_charp(tmp, "</input_alt>");
3496         process_n_form();
3497     }
3498 #ifdef USE_IMAGE
3499     if (use_image) {
3500         switch (align) {
3501         case ALIGN_RIGHT:
3502         case ALIGN_CENTER:
3503         case ALIGN_LEFT:
3504             Strcat_charp(tmp, "</div_int>");
3505             break;
3506         }
3507     }
3508 #endif
3509     return tmp;
3510 }
3511
3512 Str
3513 process_anchor(struct parsed_tag *tag, char *tagbuf)
3514 {
3515     if (parsedtag_need_reconstruct(tag)) {
3516         parsedtag_set_value(tag, ATTR_HSEQ, Sprintf("%d", cur_hseq++)->ptr);
3517         return parsedtag2str(tag);
3518     }
3519     else {
3520         Str tmp = Sprintf("<a hseq=\"%d\"", cur_hseq++);
3521         Strcat_charp(tmp, tagbuf + 2);
3522         return tmp;
3523     }
3524 }
3525
3526 Str
3527 process_input(struct parsed_tag *tag)
3528 {
3529     int i, w, v, x, y, z, iw, ih;
3530     char *q, *p, *r, *p2, *s;
3531     Str tmp = NULL;
3532     char *qq = "";
3533     int qlen = 0;
3534
3535     if (cur_form_id < 0) {
3536         char *s = "<form_int method=internal action=none>";
3537         tmp = process_form(parse_tag(&s, TRUE));
3538     }
3539     if (tmp == NULL)
3540         tmp = Strnew();
3541
3542     p = "text";
3543     parsedtag_get_value(tag, ATTR_TYPE, &p);
3544     q = NULL;
3545     parsedtag_get_value(tag, ATTR_VALUE, &q);
3546     r = "";
3547     parsedtag_get_value(tag, ATTR_NAME, &r);
3548     w = 20;
3549     parsedtag_get_value(tag, ATTR_SIZE, &w);
3550     i = 20;
3551     parsedtag_get_value(tag, ATTR_MAXLENGTH, &i);
3552     p2 = NULL;
3553     parsedtag_get_value(tag, ATTR_ALT, &p2);
3554     x = parsedtag_exists(tag, ATTR_CHECKED);
3555     y = parsedtag_exists(tag, ATTR_ACCEPT);
3556     z = parsedtag_exists(tag, ATTR_READONLY);
3557
3558     v = formtype(p);
3559     if (v == FORM_UNKNOWN)
3560         return NULL;
3561
3562     if (!q) {
3563         switch (v) {
3564         case FORM_INPUT_IMAGE:
3565         case FORM_INPUT_SUBMIT:
3566         case FORM_INPUT_BUTTON:
3567             q = "SUBMIT";
3568             break;
3569         case FORM_INPUT_RESET:
3570             q = "RESET";
3571             break;
3572             /* if no VALUE attribute is specified in 
3573              * <INPUT TYPE=CHECKBOX> tag, then the value "on" is used 
3574              * as a default value. It is not a part of HTML4.0 
3575              * specification, but an imitation of Netscape behaviour. 
3576              */
3577         case FORM_INPUT_CHECKBOX:
3578             q = "on";
3579         }
3580     }
3581     /* VALUE attribute is not allowed in <INPUT TYPE=FILE> tag. */
3582     if (v == FORM_INPUT_FILE)
3583         q = NULL;
3584     if (q) {
3585         qq = html_quote(q);
3586         qlen = get_strwidth(q);
3587     }
3588
3589     Strcat_charp(tmp, "<pre_int>");
3590     switch (v) {
3591     case FORM_INPUT_PASSWORD:
3592     case FORM_INPUT_TEXT:
3593     case FORM_INPUT_FILE:
3594     case FORM_INPUT_CHECKBOX:
3595         Strcat_char(tmp, '[');
3596         break;
3597     case FORM_INPUT_RADIO:
3598         Strcat_char(tmp, '(');
3599     }
3600     Strcat(tmp, Sprintf("<input_alt hseq=\"%d\" fid=\"%d\" type=%s "
3601                         "name=\"%s\" width=%d maxlength=%d value=\"%s\"",
3602                         cur_hseq++, cur_form_id, p, html_quote(r), w, i, qq));
3603     if (x)
3604         Strcat_charp(tmp, " checked");
3605     if (y)
3606         Strcat_charp(tmp, " accept");
3607     if (z)
3608         Strcat_charp(tmp, " readonly");
3609     Strcat_char(tmp, '>');
3610
3611     if (v == FORM_INPUT_HIDDEN)
3612         Strcat_charp(tmp, "</input_alt></pre_int>");
3613     else {
3614         switch (v) {
3615         case FORM_INPUT_PASSWORD:
3616         case FORM_INPUT_TEXT:
3617         case FORM_INPUT_FILE:
3618             Strcat_charp(tmp, "<u>");
3619             break;
3620         case FORM_INPUT_IMAGE:
3621             s = NULL;
3622             parsedtag_get_value(tag, ATTR_SRC, &s);
3623             if (s) {
3624                 Strcat(tmp, Sprintf("<img src=\"%s\"", html_quote(s)));
3625                 if (p2)
3626                     Strcat(tmp, Sprintf(" alt=\"%s\"", html_quote(p2)));
3627                 if (parsedtag_get_value(tag, ATTR_WIDTH, &iw))
3628                     Strcat(tmp, Sprintf(" width=\"%d\"", iw));
3629                 if (parsedtag_get_value(tag, ATTR_HEIGHT, &ih))
3630                     Strcat(tmp, Sprintf(" height=\"%d\"", ih));
3631                 Strcat_charp(tmp, " pre_int>");
3632                 Strcat_charp(tmp, "</input_alt></pre_int>");
3633                 return tmp;
3634             }
3635         case FORM_INPUT_SUBMIT:
3636         case FORM_INPUT_BUTTON:
3637         case FORM_INPUT_RESET:
3638             Strcat_charp(tmp, "[");
3639             break;
3640         }
3641         switch (v) {
3642         case FORM_INPUT_PASSWORD:
3643             i = 0;
3644             if (q) {
3645                 for (; i < qlen && i < w; i++)
3646                     Strcat_char(tmp, '*');
3647             }
3648             for (; i < w; i++)
3649                 Strcat_char(tmp, ' ');
3650             break;
3651         case FORM_INPUT_TEXT:
3652         case FORM_INPUT_FILE:
3653             if (q)
3654                 Strcat(tmp, textfieldrep(Strnew_charp(q), w));
3655             else {
3656                 for (i = 0; i < w; i++)
3657                     Strcat_char(tmp, ' ');
3658             }
3659             break;
3660         case FORM_INPUT_SUBMIT:
3661         case FORM_INPUT_BUTTON:
3662             if (p2)
3663                 Strcat_charp(tmp, html_quote(p2));
3664             else
3665                 Strcat_charp(tmp, qq);
3666             break;
3667         case FORM_INPUT_RESET:
3668             Strcat_charp(tmp, qq);
3669             break;
3670         case FORM_INPUT_RADIO:
3671         case FORM_INPUT_CHECKBOX:
3672             if (x)
3673                 Strcat_char(tmp, '*');
3674             else
3675                 Strcat_char(tmp, ' ');
3676             break;
3677         }
3678         switch (v) {
3679         case FORM_INPUT_PASSWORD:
3680         case FORM_INPUT_TEXT:
3681         case FORM_INPUT_FILE:
3682             Strcat_charp(tmp, "</u>");
3683             break;
3684         case FORM_INPUT_IMAGE:
3685         case FORM_INPUT_SUBMIT:
3686         case FORM_INPUT_BUTTON:
3687         case FORM_INPUT_RESET:
3688             Strcat_charp(tmp, "]");
3689         }
3690         Strcat_charp(tmp, "</input_alt>");
3691         switch (v) {
3692         case FORM_INPUT_PASSWORD:
3693         case FORM_INPUT_TEXT:
3694         case FORM_INPUT_FILE:
3695         case FORM_INPUT_CHECKBOX:
3696             Strcat_char(tmp, ']');
3697             break;
3698         case FORM_INPUT_RADIO:
3699             Strcat_char(tmp, ')');
3700         }
3701         Strcat_charp(tmp, "</pre_int>");
3702     }
3703     return tmp;
3704 }
3705
3706 Str
3707 process_select(struct parsed_tag *tag)
3708 {
3709     Str tmp = NULL;
3710     char *p;
3711
3712     if (cur_form_id < 0) {
3713         char *s = "<form_int method=internal action=none>";
3714         tmp = process_form(parse_tag(&s, TRUE));
3715     }
3716
3717     p = "";
3718     parsedtag_get_value(tag, ATTR_NAME, &p);
3719     cur_select = Strnew_charp(p);
3720     select_is_multiple = parsedtag_exists(tag, ATTR_MULTIPLE);
3721
3722 #ifdef MENU_SELECT
3723     if (!select_is_multiple) {
3724         select_str = Sprintf("<pre_int>[<input_alt hseq=\"%d\" "
3725                              "fid=\"%d\" type=select name=\"%s\" selectnumber=%d",
3726                              cur_hseq++, cur_form_id, html_quote(p), n_select);
3727         Strcat_charp(select_str, ">");
3728         if (n_select == max_select) {
3729             max_select *= 2;
3730             select_option =
3731                 New_Reuse(FormSelectOption, select_option, max_select);
3732         }
3733         select_option[n_select].first = NULL;
3734         select_option[n_select].last = NULL;
3735         cur_option_maxwidth = 0;
3736     }
3737     else
3738 #endif                          /* MENU_SELECT */
3739         select_str = Strnew();
3740     cur_option = NULL;
3741     cur_status = R_ST_NORMAL;
3742     n_selectitem = 0;
3743     return tmp;
3744 }
3745
3746 Str
3747 process_n_select(void)
3748 {
3749     if (cur_select == NULL)
3750         return NULL;
3751     process_option();
3752 #ifdef MENU_SELECT
3753     if (!select_is_multiple) {
3754         if (select_option[n_select].first) {
3755             FormItemList sitem;
3756             chooseSelectOption(&sitem, select_option[n_select].first);
3757             Strcat(select_str, textfieldrep(sitem.label, cur_option_maxwidth));
3758         }
3759         Strcat_charp(select_str, "</input_alt>]</pre_int>");
3760         n_select++;
3761     }
3762     else
3763 #endif                          /* MENU_SELECT */
3764         Strcat_charp(select_str, "<br>");
3765     cur_select = NULL;
3766     n_selectitem = 0;
3767     return select_str;
3768 }
3769
3770 void
3771 feed_select(char *str)
3772 {
3773     Str tmp = Strnew();
3774     int prev_status = cur_status;
3775     static int prev_spaces = -1;
3776     char *p;
3777
3778     if (cur_select == NULL)
3779         return;
3780     while (read_token(tmp, &str, &cur_status, 0, 0)) {
3781         if (cur_status != R_ST_NORMAL || prev_status != R_ST_NORMAL)
3782             continue;
3783         p = tmp->ptr;
3784         if (tmp->ptr[0] == '<' && Strlastchar(tmp) == '>') {
3785             struct parsed_tag *tag;
3786             char *q;
3787             if (!(tag = parse_tag(&p, FALSE)))
3788                 continue;
3789             switch (tag->tagid) {
3790             case HTML_OPTION:
3791                 process_option();
3792                 cur_option = Strnew();
3793                 if (parsedtag_get_value(tag, ATTR_VALUE, &q))
3794                     cur_option_value = Strnew_charp(q);
3795                 else
3796                     cur_option_value = NULL;
3797                 if (parsedtag_get_value(tag, ATTR_LABEL, &q))
3798                     cur_option_label = Strnew_charp(q);
3799                 else
3800                     cur_option_label = NULL;
3801                 cur_option_selected = parsedtag_exists(tag, ATTR_SELECTED);
3802                 prev_spaces = -1;
3803                 break;
3804             case HTML_N_OPTION:
3805                 /* do nothing */
3806                 break;
3807             default:
3808                 /* never happen */
3809                 break;
3810             }
3811         }
3812         else if (cur_option) {
3813             while (*p) {
3814                 if (IS_SPACE(*p) && prev_spaces != 0) {
3815                     p++;
3816                     if (prev_spaces > 0)
3817                         prev_spaces++;
3818                 }
3819                 else {
3820                     if (IS_SPACE(*p))
3821                         prev_spaces = 1;
3822                     else
3823                         prev_spaces = 0;
3824                     if (*p == '&')
3825                         Strcat_charp(cur_option, getescapecmd(&p));
3826                     else
3827                         Strcat_char(cur_option, *(p++));
3828                 }
3829             }
3830         }
3831     }
3832 }
3833
3834 void
3835 process_option(void)
3836 {
3837     char begin_char = '[', end_char = ']';
3838     int len;
3839
3840     if (cur_select == NULL || cur_option == NULL)
3841         return;
3842     while (cur_option->length > 0 && IS_SPACE(Strlastchar(cur_option)))
3843         Strshrink(cur_option, 1);
3844     if (cur_option_value == NULL)
3845         cur_option_value = cur_option;
3846     if (cur_option_label == NULL)
3847         cur_option_label = cur_option;
3848 #ifdef MENU_SELECT
3849     if (!select_is_multiple) {
3850         len = get_Str_strwidth(cur_option_label);
3851         if (len > cur_option_maxwidth)
3852             cur_option_maxwidth = len;
3853         addSelectOption(&select_option[n_select],
3854                         cur_option_value,
3855                         cur_option_label, cur_option_selected);
3856         return;
3857     }
3858 #endif                          /* MENU_SELECT */
3859     if (!select_is_multiple) {
3860         begin_char = '(';
3861         end_char = ')';
3862     }
3863     Strcat(select_str, Sprintf("<br><pre_int>%c<input_alt hseq=\"%d\" "
3864                                "fid=\"%d\" type=%s name=\"%s\" value=\"%s\"",
3865                                begin_char, cur_hseq++, cur_form_id,
3866                                select_is_multiple ? "checkbox" : "radio",
3867                                html_quote(cur_select->ptr),
3868                                html_quote(cur_option_value->ptr)));
3869     if (cur_option_selected)
3870         Strcat_charp(select_str, " checked>*</input_alt>");
3871     else
3872         Strcat_charp(select_str, "> </input_alt>");
3873     Strcat_char(select_str, end_char);
3874     Strcat_charp(select_str, html_quote(cur_option_label->ptr));
3875     Strcat_charp(select_str, "</pre_int>");
3876     n_selectitem++;
3877 }
3878
3879 Str
3880 process_textarea(struct parsed_tag *tag, int width)
3881 {
3882     Str tmp = NULL;
3883     char *p;
3884
3885     if (cur_form_id < 0) {
3886         char *s = "<form_int method=internal action=none>";
3887         tmp = process_form(parse_tag(&s, TRUE));
3888     }
3889
3890     p = "";
3891     parsedtag_get_value(tag, ATTR_NAME, &p);
3892     cur_textarea = Strnew_charp(p);
3893     cur_textarea_size = 20;
3894     if (parsedtag_get_value(tag, ATTR_COLS, &p)) {
3895         cur_textarea_size = atoi(p);
3896         if (p[strlen(p) - 1] == '%')
3897             cur_textarea_size = width * cur_textarea_size / 100 - 2;
3898         if (cur_textarea_size <= 0)
3899             cur_textarea_size = 20;
3900     }
3901     cur_textarea_rows = 1;
3902     if (parsedtag_get_value(tag, ATTR_ROWS, &p)) {
3903         cur_textarea_rows = atoi(p);
3904         if (cur_textarea_rows <= 0)
3905             cur_textarea_rows = 1;
3906     }
3907     cur_textarea_readonly = parsedtag_exists(tag, ATTR_READONLY);
3908     if (n_textarea >= max_textarea) {
3909         max_textarea *= 2;
3910         textarea_str = New_Reuse(Str, textarea_str, max_textarea);
3911     }
3912     textarea_str[n_textarea] = Strnew();
3913     ignore_nl_textarea = TRUE;
3914
3915     return tmp;
3916 }
3917
3918 Str
3919 process_n_textarea(void)
3920 {
3921     Str tmp;
3922     int i;
3923
3924     if (cur_textarea == NULL)
3925         return NULL;
3926
3927     tmp = Strnew();
3928     Strcat(tmp, Sprintf("<pre_int>[<input_alt hseq=\"%d\" fid=\"%d\" "
3929                         "type=textarea name=\"%s\" size=%d rows=%d "
3930                         "top_margin=%d textareanumber=%d",
3931                         cur_hseq, cur_form_id,
3932                         html_quote(cur_textarea->ptr),
3933                         cur_textarea_size, cur_textarea_rows,
3934                         cur_textarea_rows - 1, n_textarea));
3935     if (cur_textarea_readonly)
3936         Strcat_charp(tmp, " readonly");
3937     Strcat_charp(tmp, "><u>");
3938     for (i = 0; i < cur_textarea_size; i++)
3939         Strcat_char(tmp, ' ');
3940     Strcat_charp(tmp, "</u></input_alt>]</pre_int>\n");
3941     cur_hseq++;
3942     n_textarea++;
3943     cur_textarea = NULL;
3944
3945     return tmp;
3946 }
3947
3948 void
3949 feed_textarea(char *str)
3950 {
3951     if (cur_textarea == NULL)
3952         return;
3953     if (ignore_nl_textarea) {
3954         if (*str == '\r')
3955             str++;
3956         if (*str == '\n')
3957             str++;
3958     }
3959     ignore_nl_textarea = FALSE;
3960     while (*str) {
3961         if (*str == '&')
3962             Strcat_charp(textarea_str[n_textarea], getescapecmd(&str));
3963         else if (*str == '\n') {
3964             Strcat_charp(textarea_str[n_textarea], "\r\n");
3965             str++;
3966         }
3967         else if (*str != '\r')
3968             Strcat_char(textarea_str[n_textarea], *(str++));
3969     }
3970 }
3971
3972 Str
3973 process_hr(struct parsed_tag *tag, int width, int indent_width)
3974 {
3975     Str tmp = Strnew_charp("<nobr>");
3976     int w = 0;
3977     int x = ALIGN_CENTER;
3978
3979     if (width > indent_width)
3980         width -= indent_width;
3981     if (parsedtag_get_value(tag, ATTR_WIDTH, &w))
3982         w = REAL_WIDTH(w, width);
3983     else
3984         w = width;
3985
3986     parsedtag_get_value(tag, ATTR_ALIGN, &x);
3987     switch (x) {
3988     case ALIGN_CENTER:
3989         Strcat_charp(tmp, "<div_int align=center>");
3990         break;
3991     case ALIGN_RIGHT:
3992         Strcat_charp(tmp, "<div_int align=right>");
3993         break;
3994     case ALIGN_LEFT:
3995         Strcat_charp(tmp, "<div_int align=left>");
3996         break;
3997     }
3998     w /= symbol_width;
3999     if (w <= 0)
4000         w = 1;
4001     push_symbol(tmp, HR_SYMBOL, symbol_width, w);
4002     Strcat_charp(tmp, "</div_int></nobr>");
4003     return tmp;
4004 }
4005
4006 #ifdef USE_M17N
4007 static char *
4008 check_charset(char *p)
4009 {
4010     return wc_guess_charset(p, 0) ? p : NULL;
4011 }
4012
4013 static char *
4014 check_accept_charset(char *ac)
4015 {
4016     char *s = ac, *e;
4017
4018     while (*s) {
4019         while (*s && (IS_SPACE(*s) || *s == ','))
4020             s++;
4021         if (!*s)
4022             break;
4023         e = s;
4024         while (*e && !(IS_SPACE(*e) || *e == ','))
4025             e++;
4026         if (wc_guess_charset(Strnew_charp_n(s, e - s)->ptr, 0))
4027             return ac;
4028         s = e;
4029     }
4030     return NULL;
4031 }
4032 #endif
4033
4034 static Str
4035 process_form_int(struct parsed_tag *tag, int fid)
4036 {
4037     char *p, *q, *r, *s, *tg, *n;
4038
4039     p = "get";
4040     parsedtag_get_value(tag, ATTR_METHOD, &p);
4041     q = "!CURRENT_URL!";
4042     parsedtag_get_value(tag, ATTR_ACTION, &q);
4043     r = NULL;
4044 #ifdef USE_M17N
4045     if (parsedtag_get_value(tag, ATTR_ACCEPT_CHARSET, &r))
4046         r = check_accept_charset(r);
4047     if (!r && parsedtag_get_value(tag, ATTR_CHARSET, &r))
4048         r = check_charset(r);
4049 #endif
4050     s = NULL;
4051     parsedtag_get_value(tag, ATTR_ENCTYPE, &s);
4052     tg = NULL;
4053     parsedtag_get_value(tag, ATTR_TARGET, &tg);
4054     n = NULL;
4055     parsedtag_get_value(tag, ATTR_NAME, &n);
4056
4057     if (fid < 0) {
4058         form_max++;
4059         form_sp++;
4060         fid = form_max;
4061     }
4062     else {                      /* <form_int> */
4063         if (form_max < fid)
4064             form_max = fid;
4065         form_sp = fid;
4066     }
4067     if (forms_size == 0) {
4068         forms_size = INITIAL_FORM_SIZE;
4069         forms = New_N(FormList *, forms_size);
4070         form_stack = NewAtom_N(int, forms_size);
4071     }
4072     else if (forms_size <= form_max) {
4073         forms_size += form_max;
4074         forms = New_Reuse(FormList *, forms, forms_size);
4075         form_stack = New_Reuse(int, form_stack, forms_size);
4076     }
4077     form_stack[form_sp] = fid;
4078
4079     if (w3m_halfdump) {
4080         Str tmp = Sprintf("<form_int fid=\"%d\" action=\"%s\" method=\"%s\"",
4081                           fid, html_quote(q), html_quote(p));
4082         if (s)
4083             Strcat(tmp, Sprintf(" enctype=\"%s\"", html_quote(s)));
4084         if (tg)
4085             Strcat(tmp, Sprintf(" target=\"%s\"", html_quote(tg)));
4086         if (n)
4087             Strcat(tmp, Sprintf(" name=\"%s\"", html_quote(n)));
4088 #ifdef USE_M17N
4089         if (r)
4090             Strcat(tmp, Sprintf(" accept-charset=\"%s\"", html_quote(r)));
4091 #endif
4092         Strcat_charp(tmp, ">");
4093         return tmp;
4094     }
4095
4096     forms[fid] = newFormList(q, p, r, s, tg, n, NULL);
4097     return NULL;
4098 }
4099
4100 Str
4101 process_form(struct parsed_tag *tag)
4102 {
4103     return process_form_int(tag, -1);
4104 }
4105
4106 Str
4107 process_n_form(void)
4108 {
4109     if (form_sp >= 0)
4110         form_sp--;
4111     return NULL;
4112 }
4113
4114 static void
4115 clear_ignore_p_flag(int cmd, struct readbuffer *obuf)
4116 {
4117     static int clear_flag_cmd[] = {
4118         HTML_HR, HTML_UNKNOWN
4119     };
4120     int i;
4121
4122     for (i = 0; clear_flag_cmd[i] != HTML_UNKNOWN; i++) {
4123         if (cmd == clear_flag_cmd[i]) {
4124             obuf->flag &= ~RB_IGNORE_P;
4125             return;
4126         }
4127     }
4128 }
4129
4130 static void
4131 set_alignment(struct readbuffer *obuf, struct parsed_tag *tag)
4132 {
4133     long flag = -1;
4134     int align;
4135
4136     if (parsedtag_get_value(tag, ATTR_ALIGN, &align)) {
4137         switch (align) {
4138         case ALIGN_CENTER:
4139             flag = RB_CENTER;
4140             break;
4141         case ALIGN_RIGHT:
4142             flag = RB_RIGHT;
4143             break;
4144         case ALIGN_LEFT:
4145             flag = RB_LEFT;
4146         }
4147     }
4148     RB_SAVE_FLAG(obuf);
4149     if (flag != -1) {
4150         RB_SET_ALIGN(obuf, flag);
4151     }
4152 }
4153
4154 #ifdef ID_EXT
4155 static void
4156 process_idattr(struct readbuffer *obuf, int cmd, struct parsed_tag *tag)
4157 {
4158     char *id = NULL, *framename = NULL;
4159     Str idtag = NULL;
4160
4161     /* 
4162      * HTML_TABLE is handled by the other process.
4163      */
4164     if (cmd == HTML_TABLE)
4165         return;
4166
4167     parsedtag_get_value(tag, ATTR_ID, &id);
4168     parsedtag_get_value(tag, ATTR_FRAMENAME, &framename);
4169     if (id == NULL)
4170         return;
4171     if (framename)
4172         idtag = Sprintf("<_id id=\"%s\" framename=\"%s\">",
4173                         html_quote(id), html_quote(framename));
4174     else
4175         idtag = Sprintf("<_id id=\"%s\">", html_quote(id));
4176     push_tag(obuf, idtag->ptr, HTML_NOP);
4177 }
4178 #endif                          /* ID_EXT */
4179
4180 #define CLOSE_P if (obuf->flag & RB_P) { \
4181       flushline(h_env, obuf, envs[h_env->envc].indent,0,h_env->limit);\
4182       RB_RESTORE_FLAG(obuf);\
4183       obuf->flag &= ~RB_P;\
4184     }
4185
4186 #define CLOSE_A \
4187     CLOSE_P; \
4188     close_anchor(h_env, obuf);
4189
4190 #define CLOSE_DT \
4191     if (obuf->flag & RB_IN_DT) { \
4192       obuf->flag &= ~RB_IN_DT; \
4193       HTMLlineproc1("</b>", h_env); \
4194     }
4195
4196 #define PUSH_ENV(cmd) \
4197     if (++h_env->envc_real < h_env->nenv) { \
4198       ++h_env->envc; \
4199       envs[h_env->envc].env = cmd; \
4200       envs[h_env->envc].count = 0; \
4201       if (h_env->envc <= MAX_INDENT_LEVEL) \
4202         envs[h_env->envc].indent = envs[h_env->envc - 1].indent + INDENT_INCR; \
4203       else \
4204         envs[h_env->envc].indent = envs[h_env->envc - 1].indent; \
4205     }
4206
4207 #define POP_ENV \
4208     if (h_env->envc_real-- < h_env->nenv) \
4209       h_env->envc--;
4210
4211 static int
4212 ul_type(struct parsed_tag *tag, int default_type)
4213 {
4214     char *p;
4215     if (parsedtag_get_value(tag, ATTR_TYPE, &p)) {
4216         if (!strcasecmp(p, "disc"))
4217             return (int)'d';
4218         else if (!strcasecmp(p, "circle"))
4219             return (int)'c';
4220         else if (!strcasecmp(p, "square"))
4221             return (int)'s';
4222     }
4223     return default_type;
4224 }
4225
4226 int
4227 getMetaRefreshParam(char *q, Str *refresh_uri)
4228 {
4229     int refresh_interval;
4230     char *r;
4231     Str s_tmp = NULL;
4232
4233     if (q == NULL || refresh_uri == NULL)
4234         return 0;
4235
4236     refresh_interval = atoi(q);
4237     if (refresh_interval < 0)
4238         return 0;
4239
4240     while (*q) {
4241         if (!strncasecmp(q, "url=", 4)) {
4242             q += 4;
4243             if (*q == '\"')     /* " */
4244                 q++;
4245             r = q;
4246             while (*r && !IS_SPACE(*r) && *r != ';')
4247                 r++;
4248             s_tmp = Strnew_charp_n(q, r - q);
4249
4250             if (s_tmp->ptr[s_tmp->length - 1] == '\"') {        /* " 
4251                                                                  */
4252                 s_tmp->length--;
4253                 s_tmp->ptr[s_tmp->length] = '\0';
4254             }
4255             q = r;
4256         }
4257         while (*q && *q != ';')
4258             q++;
4259         if (*q == ';')
4260             q++;
4261         while (*q && *q == ' ')
4262             q++;
4263     }
4264     *refresh_uri = s_tmp;
4265     return refresh_interval;
4266 }
4267
4268 int
4269 HTMLtagproc1(struct parsed_tag *tag, struct html_feed_environ *h_env)
4270 {
4271     char *p, *q, *r;
4272     int i, w, x, y, z, count, width;
4273     struct readbuffer *obuf = h_env->obuf;
4274     struct environment *envs = h_env->envs;
4275     Str tmp;
4276     int hseq;
4277     int cmd;
4278 #ifdef ID_EXT
4279     char *id = NULL;
4280 #endif                          /* ID_EXT */
4281
4282     cmd = tag->tagid;
4283
4284     if (obuf->flag & RB_PRE) {
4285         switch (cmd) {
4286         case HTML_NOBR:
4287         case HTML_N_NOBR:
4288         case HTML_PRE_INT:
4289         case HTML_N_PRE_INT:
4290             return 1;
4291         }
4292     }
4293
4294     switch (cmd) {
4295     case HTML_B:
4296         obuf->in_bold++;
4297         if (obuf->in_bold > 1)
4298             return 1;
4299         return 0;
4300     case HTML_N_B:
4301         if (obuf->in_bold == 1 && close_effect0(obuf, HTML_B))
4302             obuf->in_bold = 0;
4303         if (obuf->in_bold > 0) {
4304             obuf->in_bold--;
4305             if (obuf->in_bold == 0)
4306                 return 0;
4307         }
4308         return 1;
4309     case HTML_I:
4310         obuf->in_italic++;
4311         if (obuf->in_italic > 1)
4312             return 1;
4313         return 0;
4314     case HTML_N_I:
4315         if (obuf->in_italic == 1 && close_effect0(obuf, HTML_I))
4316             obuf->in_italic = 0;
4317         if (obuf->in_italic > 0) {
4318             obuf->in_italic--;
4319             if (obuf->in_italic == 0)
4320                 return 0;
4321         }
4322         return 1;
4323     case HTML_U:
4324         obuf->in_under++;
4325         if (obuf->in_under > 1)
4326             return 1;
4327         return 0;
4328     case HTML_N_U:
4329         if (obuf->in_under == 1 && close_effect0(obuf, HTML_U))
4330             obuf->in_under = 0;
4331         if (obuf->in_under > 0) {
4332             obuf->in_under--;
4333             if (obuf->in_under == 0)
4334                 return 0;
4335         }
4336         return 1;
4337     case HTML_EM:
4338         HTMLlineproc1("<i>", h_env);
4339         return 1;
4340     case HTML_N_EM:
4341         HTMLlineproc1("</i>", h_env);
4342         return 1;
4343     case HTML_STRONG:
4344         HTMLlineproc1("<b>", h_env);
4345         return 1;
4346     case HTML_N_STRONG:
4347         HTMLlineproc1("</b>", h_env);
4348         return 1;
4349     case HTML_Q:
4350         HTMLlineproc1("`", h_env);
4351         return 1;
4352     case HTML_N_Q:
4353         HTMLlineproc1("'", h_env);
4354         return 1;
4355     case HTML_P:
4356     case HTML_N_P:
4357         CLOSE_A;
4358         if (!(obuf->flag & RB_IGNORE_P)) {
4359             flushline(h_env, obuf, envs[h_env->envc].indent, 1, h_env->limit);
4360             do_blankline(h_env, obuf, envs[h_env->envc].indent, 0,
4361                          h_env->limit);
4362         }
4363         obuf->flag |= RB_IGNORE_P;
4364         if (cmd == HTML_P) {
4365             set_alignment(obuf, tag);
4366             obuf->flag |= RB_P;
4367         }
4368         return 1;
4369     case HTML_BR:
4370         flushline(h_env, obuf, envs[h_env->envc].indent, 1, h_env->limit);
4371         h_env->blank_lines = 0;
4372         return 1;
4373     case HTML_H:
4374         if (!(obuf->flag & (RB_PREMODE | RB_IGNORE_P))) {
4375             flushline(h_env, obuf, envs[h_env->envc].indent, 0, h_env->limit);
4376             do_blankline(h_env, obuf, envs[h_env->envc].indent, 0,
4377                          h_env->limit);
4378         }
4379         HTMLlineproc1("<b>", h_env);
4380         set_alignment(obuf, tag);
4381         return 1;
4382     case HTML_N_H:
4383         HTMLlineproc1("</b>", h_env);
4384         if (!(obuf->flag & RB_PREMODE)) {
4385             flushline(h_env, obuf, envs[h_env->envc].indent, 0, h_env->limit);
4386         }
4387         do_blankline(h_env, obuf, envs[h_env->envc].indent, 0, h_env->limit);
4388         RB_RESTORE_FLAG(obuf);
4389         close_anchor(h_env, obuf);
4390         obuf->flag |= RB_IGNORE_P;
4391         return 1;
4392     case HTML_UL:
4393     case HTML_OL:
4394     case HTML_BLQ:
4395         CLOSE_A;
4396         if (!(obuf->flag & RB_IGNORE_P)) {
4397             flushline(h_env, obuf, envs[h_env->envc].indent, 0, h_env->limit);
4398             if (!(obuf->flag & RB_PREMODE) &&
4399                 (h_env->envc == 0 || cmd == HTML_BLQ))
4400                 do_blankline(h_env, obuf, envs[h_env->envc].indent, 0,
4401                              h_env->limit);
4402         }
4403         PUSH_ENV(cmd);
4404         if (cmd == HTML_UL || cmd == HTML_OL) {
4405             if (parsedtag_get_value(tag, ATTR_START, &count)) {
4406                 envs[h_env->envc].count = count - 1;
4407             }
4408         }
4409         if (cmd == HTML_OL) {
4410             envs[h_env->envc].type = '1';
4411             if (parsedtag_get_value(tag, ATTR_TYPE, &p)) {
4412                 envs[h_env->envc].type = (int)*p;
4413             }
4414         }
4415         if (cmd == HTML_UL)
4416             envs[h_env->envc].type = ul_type(tag, 0);
4417         flushline(h_env, obuf, envs[h_env->envc].indent, 0, h_env->limit);
4418         return 1;
4419     case HTML_N_UL:
4420     case HTML_N_OL:
4421     case HTML_N_DL:
4422     case HTML_N_BLQ:
4423         CLOSE_DT;
4424         CLOSE_A;
4425         if (h_env->envc > 0) {
4426             flushline(h_env, obuf, envs[h_env->envc - 1].indent, 0,
4427                       h_env->limit);
4428             POP_ENV;
4429             if (!(obuf->flag & RB_PREMODE) &&
4430                 (h_env->envc == 0 || cmd == HTML_N_DL || cmd == HTML_N_BLQ)) {
4431                 do_blankline(h_env, obuf,
4432                              envs[h_env->envc].indent,
4433                              INDENT_INCR, h_env->limit);
4434                 obuf->flag |= RB_IGNORE_P;
4435             }
4436         }
4437         close_anchor(h_env, obuf);
4438         return 1;
4439     case HTML_DL:
4440         CLOSE_A;
4441         if (!(obuf->flag & RB_IGNORE_P)) {
4442             flushline(h_env, obuf, envs[h_env->envc].indent, 0, h_env->limit);
4443             if (!(obuf->flag & RB_PREMODE))
4444                 do_blankline(h_env, obuf, envs[h_env->envc].indent, 0,
4445                              h_env->limit);
4446         }
4447         PUSH_ENV(cmd);
4448         if (parsedtag_exists(tag, ATTR_COMPACT))
4449             envs[h_env->envc].env = HTML_DL_COMPACT;
4450         obuf->flag |= RB_IGNORE_P;
4451         return 1;
4452     case HTML_LI:
4453         CLOSE_A;
4454         CLOSE_DT;
4455         if (h_env->envc > 0) {
4456             Str num;
4457             flushline(h_env, obuf,
4458                       envs[h_env->envc - 1].indent, 0, h_env->limit);
4459             envs[h_env->envc].count++;
4460             if (parsedtag_get_value(tag, ATTR_VALUE, &p)) {
4461                 count = atoi(p);
4462                 if (count > 0)
4463                     envs[h_env->envc].count = count;
4464                 else
4465                     envs[h_env->envc].count = 0;
4466             }
4467             switch (envs[h_env->envc].env) {
4468             case HTML_UL:
4469                 envs[h_env->envc].type = ul_type(tag, envs[h_env->envc].type);
4470                 for (i = 0; i < INDENT_INCR - 3; i++)
4471                     push_charp(obuf, 1, NBSP, PC_ASCII);
4472                 tmp = Strnew();
4473                 switch (envs[h_env->envc].type) {
4474                 case 'd':
4475                     push_symbol(tmp, UL_SYMBOL_DISC, symbol_width, 1);
4476                     break;
4477                 case 'c':
4478                     push_symbol(tmp, UL_SYMBOL_CIRCLE, symbol_width, 1);
4479                     break;
4480                 case 's':
4481                     push_symbol(tmp, UL_SYMBOL_SQUARE, symbol_width, 1);
4482                     break;
4483                 default:
4484                     push_symbol(tmp,
4485                                 UL_SYMBOL((h_env->envc_real -
4486                                            1) % MAX_UL_LEVEL), symbol_width,
4487                                 1);
4488                     break;
4489                 }
4490                 if (symbol_width == 1)
4491                     push_charp(obuf, 1, NBSP, PC_ASCII);
4492                 push_str(obuf, symbol_width, tmp, PC_ASCII);
4493                 push_charp(obuf, 1, NBSP, PC_ASCII);
4494                 set_space_to_prevchar(obuf->prevchar);
4495                 break;
4496             case HTML_OL:
4497                 if (parsedtag_get_value(tag, ATTR_TYPE, &p))
4498                     envs[h_env->envc].type = (int)*p;
4499                 switch ((envs[h_env->envc].count > 0)? envs[h_env->envc].type: '1') {
4500                 case 'i':
4501                     num = romanNumeral(envs[h_env->envc].count);
4502                     break;
4503                 case 'I':
4504                     num = romanNumeral(envs[h_env->envc].count);
4505                     Strupper(num);
4506                     break;
4507                 case 'a':
4508                     num = romanAlphabet(envs[h_env->envc].count);
4509                     break;
4510                 case 'A':
4511                     num = romanAlphabet(envs[h_env->envc].count);
4512                     Strupper(num);
4513                     break;
4514                 default:
4515                     num = Sprintf("%d", envs[h_env->envc].count);
4516                     break;
4517                 }
4518                 if (INDENT_INCR >= 4)
4519                     Strcat_charp(num, ". ");
4520                 else
4521                     Strcat_char(num, '.');
4522                 push_spaces(obuf, 1, INDENT_INCR - num->length);
4523                 push_str(obuf, num->length, num, PC_ASCII);
4524                 if (INDENT_INCR >= 4)
4525                     set_space_to_prevchar(obuf->prevchar);
4526                 break;
4527             default:
4528                 push_spaces(obuf, 1, INDENT_INCR);
4529                 break;
4530             }
4531         }
4532         else {
4533             flushline(h_env, obuf, 0, 0, h_env->limit);
4534         }
4535         obuf->flag |= RB_IGNORE_P;
4536         return 1;
4537     case HTML_DT:
4538         CLOSE_A;
4539         if (h_env->envc == 0 ||
4540             (h_env->envc_real < h_env->nenv &&
4541              envs[h_env->envc].env != HTML_DL &&
4542              envs[h_env->envc].env != HTML_DL_COMPACT)) {
4543             PUSH_ENV(HTML_DL);
4544         }
4545         if (h_env->envc > 0) {
4546             flushline(h_env, obuf,
4547                       envs[h_env->envc - 1].indent, 0, h_env->limit);
4548         }
4549         if (!(obuf->flag & RB_IN_DT)) {
4550             HTMLlineproc1("<b>", h_env);
4551             obuf->flag |= RB_IN_DT;
4552         }
4553         obuf->flag |= RB_IGNORE_P;
4554         return 1;
4555     case HTML_DD:
4556         CLOSE_A;
4557         CLOSE_DT;
4558         if (envs[h_env->envc].env == HTML_DL_COMPACT) {
4559             if (obuf->pos > envs[h_env->envc].indent)
4560                 flushline(h_env, obuf, envs[h_env->envc].indent, 0,
4561                           h_env->limit);
4562             else
4563                 push_spaces(obuf, 1, envs[h_env->envc].indent - obuf->pos);
4564         }
4565         else
4566             flushline(h_env, obuf, envs[h_env->envc].indent, 0, h_env->limit);
4567         /* obuf->flag |= RB_IGNORE_P; */
4568         return 1;
4569     case HTML_TITLE:
4570         close_anchor(h_env, obuf);
4571         process_title(tag);
4572         obuf->flag |= RB_TITLE;
4573         obuf->end_tag = HTML_N_TITLE;
4574         return 1;
4575     case HTML_N_TITLE:
4576         if (!(obuf->flag & RB_TITLE))
4577             return 1;
4578         obuf->flag &= ~RB_TITLE;
4579         obuf->end_tag = 0;
4580         tmp = process_n_title(tag);
4581         if (tmp)
4582             HTMLlineproc1(tmp->ptr, h_env);
4583         return 1;
4584     case HTML_TITLE_ALT:
4585         if (parsedtag_get_value(tag, ATTR_TITLE, &p))
4586             h_env->title = html_unquote(p);
4587         return 0;
4588     case HTML_FRAMESET:
4589         PUSH_ENV(cmd);
4590         push_charp(obuf, 9, "--FRAME--", PC_ASCII);
4591         flushline(h_env, obuf, envs[h_env->envc].indent, 0, h_env->limit);
4592         return 0;
4593     case HTML_N_FRAMESET:
4594         if (h_env->envc > 0) {
4595             POP_ENV;
4596             flushline(h_env, obuf, envs[h_env->envc].indent, 0, h_env->limit);
4597         }
4598         return 0;
4599     case HTML_NOFRAMES:
4600         CLOSE_A;
4601         flushline(h_env, obuf, envs[h_env->envc].indent, 0, h_env->limit);
4602         obuf->flag |= (RB_NOFRAMES | RB_IGNORE_P);
4603         /* istr = str; */
4604         return 1;
4605     case HTML_N_NOFRAMES:
4606         CLOSE_A;
4607         flushline(h_env, obuf, envs[h_env->envc].indent, 0, h_env->limit);
4608         obuf->flag &= ~RB_NOFRAMES;
4609         return 1;
4610     case HTML_FRAME:
4611         q = r = NULL;
4612         parsedtag_get_value(tag, ATTR_SRC, &q);
4613         parsedtag_get_value(tag, ATTR_NAME, &r);
4614         if (q) {
4615             q = html_quote(q);
4616             push_tag(obuf, Sprintf("<a hseq=\"%d\" href=\"%s\">",
4617                                    cur_hseq++, q)->ptr, HTML_A);
4618             if (r)
4619                 q = html_quote(r);
4620             push_charp(obuf, get_strwidth(q), q, PC_ASCII);
4621             push_tag(obuf, "</a>", HTML_N_A);
4622         }
4623         flushline(h_env, obuf, envs[h_env->envc].indent, 0, h_env->limit);
4624         return 0;
4625     case HTML_HR:
4626         close_anchor(h_env, obuf);
4627         tmp = process_hr(tag, h_env->limit, envs[h_env->envc].indent);
4628         HTMLlineproc1(tmp->ptr, h_env);
4629         set_space_to_prevchar(obuf->prevchar);
4630         return 1;
4631     case HTML_PRE:
4632         x = parsedtag_exists(tag, ATTR_FOR_TABLE);
4633         CLOSE_A;
4634         if (!(obuf->flag & RB_IGNORE_P)) {
4635             flushline(h_env, obuf, envs[h_env->envc].indent, 0, h_env->limit);
4636             if (!x)
4637                 do_blankline(h_env, obuf, envs[h_env->envc].indent, 0,
4638                              h_env->limit);
4639         }
4640         else
4641             fillline(obuf, envs[h_env->envc].indent);
4642         obuf->flag |= (RB_PRE | RB_IGNORE_P);
4643         /* istr = str; */
4644         return 1;
4645     case HTML_N_PRE:
4646         flushline(h_env, obuf, envs[h_env->envc].indent, 0, h_env->limit);
4647         if (!(obuf->flag & RB_IGNORE_P)) {
4648             do_blankline(h_env, obuf, envs[h_env->envc].indent, 0,
4649                          h_env->limit);
4650             obuf->flag |= RB_IGNORE_P;
4651             h_env->blank_lines++;
4652         }
4653         obuf->flag &= ~RB_PRE;
4654         close_anchor(h_env, obuf);
4655         return 1;
4656     case HTML_PRE_INT:
4657         i = obuf->line->length;
4658         append_tags(obuf);
4659         if (!(obuf->flag & RB_SPECIAL)) {
4660             set_breakpoint(obuf, obuf->line->length - i);
4661         }
4662         obuf->flag |= RB_PRE_INT;
4663         return 0;
4664     case HTML_N_PRE_INT:
4665         push_tag(obuf, "</pre_int>", HTML_N_PRE_INT);
4666         obuf->flag &= ~RB_PRE_INT;
4667         if (!(obuf->flag & RB_SPECIAL) && obuf->pos > obuf->bp.pos) {
4668             set_prevchar(obuf->prevchar, "", 0);
4669             obuf->prev_ctype = PC_CTRL;
4670         }
4671         return 1;
4672     case HTML_NOBR:
4673         obuf->flag |= RB_NOBR;
4674         obuf->nobr_level++;
4675         return 0;
4676     case HTML_N_NOBR:
4677         if (obuf->nobr_level > 0)
4678             obuf->nobr_level--;
4679         if (obuf->nobr_level == 0)
4680             obuf->flag &= ~RB_NOBR;
4681         return 0;
4682     case HTML_PRE_PLAIN:
4683         CLOSE_A;
4684         if (!(obuf->flag & RB_IGNORE_P)) {
4685             flushline(h_env, obuf, envs[h_env->envc].indent, 0, h_env->limit);
4686             do_blankline(h_env, obuf, envs[h_env->envc].indent, 0,
4687                          h_env->limit);
4688         }
4689         obuf->flag |= (RB_PRE | RB_IGNORE_P);
4690         return 1;
4691     case HTML_N_PRE_PLAIN:
4692         CLOSE_A;
4693         if (!(obuf->flag & RB_IGNORE_P)) {
4694             flushline(h_env, obuf, envs[h_env->envc].indent, 0, h_env->limit);
4695             do_blankline(h_env, obuf, envs[h_env->envc].indent, 0,
4696                          h_env->limit);
4697             obuf->flag |= RB_IGNORE_P;
4698         }
4699         obuf->flag &= ~RB_PRE;
4700         return 1;
4701     case HTML_LISTING:
4702     case HTML_XMP:
4703     case HTML_PLAINTEXT:
4704         CLOSE_A;
4705         if (!(obuf->flag & RB_IGNORE_P)) {
4706             flushline(h_env, obuf, envs[h_env->envc].indent, 0, h_env->limit);
4707             do_blankline(h_env, obuf, envs[h_env->envc].indent, 0,
4708                          h_env->limit);
4709         }
4710         obuf->flag |= (RB_PLAIN | RB_IGNORE_P);
4711         switch (cmd) {
4712         case HTML_LISTING:
4713             obuf->end_tag = HTML_N_LISTING;
4714             break;
4715         case HTML_XMP:
4716             obuf->end_tag = HTML_N_XMP;
4717             break;
4718         case HTML_PLAINTEXT:
4719             obuf->end_tag = MAX_HTMLTAG;
4720             break;
4721         }
4722         return 1;
4723     case HTML_N_LISTING:
4724     case HTML_N_XMP:
4725         CLOSE_A;
4726         if (!(obuf->flag & RB_IGNORE_P)) {
4727             flushline(h_env, obuf, envs[h_env->envc].indent, 0, h_env->limit);
4728             do_blankline(h_env, obuf, envs[h_env->envc].indent, 0,
4729                          h_env->limit);
4730             obuf->flag |= RB_IGNORE_P;
4731         }
4732         obuf->flag &= ~RB_PLAIN;
4733         obuf->end_tag = 0;
4734         return 1;
4735     case HTML_SCRIPT:
4736         obuf->flag |= RB_SCRIPT;
4737         obuf->end_tag = HTML_N_SCRIPT;
4738         return 1;
4739     case HTML_STYLE:
4740         obuf->flag |= RB_STYLE;
4741         obuf->end_tag = HTML_N_STYLE;
4742         return 1;
4743     case HTML_N_SCRIPT:
4744         obuf->flag &= ~RB_SCRIPT;
4745         obuf->end_tag = 0;
4746         return 1;
4747     case HTML_N_STYLE:
4748         obuf->flag &= ~RB_STYLE;
4749         obuf->end_tag = 0;
4750         return 1;
4751     case HTML_A:
4752         if (obuf->anchor.url)
4753             close_anchor(h_env, obuf);
4754
4755         hseq = 0;
4756
4757         if (parsedtag_get_value(tag, ATTR_HREF, &p))
4758             obuf->anchor.url = Strnew_charp(p)->ptr;
4759         if (parsedtag_get_value(tag, ATTR_TARGET, &p))
4760             obuf->anchor.target = Strnew_charp(p)->ptr;
4761         if (parsedtag_get_value(tag, ATTR_REFERER, &p))
4762             obuf->anchor.referer = Strnew_charp(p)->ptr;
4763         if (parsedtag_get_value(tag, ATTR_TITLE, &p))
4764             obuf->anchor.title = Strnew_charp(p)->ptr;
4765         if (parsedtag_get_value(tag, ATTR_ACCESSKEY, &p))
4766             obuf->anchor.accesskey = (unsigned char)*p;
4767         if (parsedtag_get_value(tag, ATTR_HSEQ, &hseq))
4768             obuf->anchor.hseq = hseq;
4769
4770         if (hseq == 0 && obuf->anchor.url) {
4771             obuf->anchor.hseq = cur_hseq;
4772             tmp = process_anchor(tag, h_env->tagbuf->ptr);
4773             push_tag(obuf, tmp->ptr, HTML_A);
4774             return 1;
4775         }
4776         return 0;
4777     case HTML_N_A:
4778         close_anchor(h_env, obuf);
4779         return 1;
4780     case HTML_IMG:
4781         tmp = process_img(tag, h_env->limit);
4782         HTMLlineproc1(tmp->ptr, h_env);
4783         return 1;
4784     case HTML_IMG_ALT:
4785         if (parsedtag_get_value(tag, ATTR_SRC, &p))
4786             obuf->img_alt = Strnew_charp(p);
4787 #ifdef USE_IMAGE
4788         i = 0;
4789         if (parsedtag_get_value(tag, ATTR_TOP_MARGIN, &i)) {
4790             if (i > obuf->top_margin)
4791                 obuf->top_margin = i;
4792         }
4793         i = 0;
4794         if (parsedtag_get_value(tag, ATTR_BOTTOM_MARGIN, &i)) {
4795             if (i > obuf->bottom_margin)
4796                 obuf->bottom_margin = i;
4797         }
4798 #endif
4799         return 0;
4800     case HTML_N_IMG_ALT:
4801         if (obuf->img_alt) {
4802             if (!close_effect0(obuf, HTML_IMG_ALT))
4803                 push_tag(obuf, "</img_alt>", HTML_N_IMG_ALT);
4804             obuf->img_alt = NULL;
4805         }
4806         return 1;
4807     case HTML_INPUT_ALT:
4808         i = 0;
4809         if (parsedtag_get_value(tag, ATTR_TOP_MARGIN, &i)) {
4810             if (i > obuf->top_margin)
4811                 obuf->top_margin = i;
4812         }
4813         i = 0;
4814         if (parsedtag_get_value(tag, ATTR_BOTTOM_MARGIN, &i)) {
4815             if (i > obuf->bottom_margin)
4816                 obuf->bottom_margin = i;
4817         }
4818         return 0;
4819     case HTML_TABLE:
4820         close_anchor(h_env, obuf);
4821         obuf->table_level++;
4822         if (obuf->table_level >= MAX_TABLE)
4823             break;
4824         w = BORDER_NONE;
4825         /* x: cellspacing, y: cellpadding */
4826         x = 2;
4827         y = 1;
4828         z = 0;
4829         width = 0;
4830         if (parsedtag_exists(tag, ATTR_BORDER)) {
4831             if (parsedtag_get_value(tag, ATTR_BORDER, &w)) {
4832                 if (w > 2)
4833                     w = BORDER_THICK;
4834                 else if (w < 0) {       /* weird */
4835                     w = BORDER_THIN;
4836                 }
4837             }
4838             else
4839                 w = BORDER_THIN;
4840         }
4841         if (parsedtag_get_value(tag, ATTR_WIDTH, &i)) {
4842             if (obuf->table_level == 0)
4843                 width = REAL_WIDTH(i, h_env->limit - envs[h_env->envc].indent);
4844             else
4845                 width = RELATIVE_WIDTH(i);
4846         }
4847         if (parsedtag_exists(tag, ATTR_HBORDER))
4848             w = BORDER_NOWIN;
4849         parsedtag_get_value(tag, ATTR_CELLSPACING, &x);
4850         parsedtag_get_value(tag, ATTR_CELLPADDING, &y);
4851         parsedtag_get_value(tag, ATTR_VSPACE, &z);
4852 #ifdef ID_EXT
4853         parsedtag_get_value(tag, ATTR_ID, &id);
4854 #endif                          /* ID_EXT */
4855         tables[obuf->table_level] = begin_table(w, x, y, z);
4856 #ifdef ID_EXT
4857         if (id != NULL)
4858             tables[obuf->table_level]->id = Strnew_charp(id);
4859 #endif                          /* ID_EXT */
4860         table_mode[obuf->table_level].pre_mode = 0;
4861         table_mode[obuf->table_level].indent_level = 0;
4862         table_mode[obuf->table_level].nobr_level = 0;
4863         table_mode[obuf->table_level].caption = 0;
4864         table_mode[obuf->table_level].end_tag = 0;      /* HTML_UNKNOWN */
4865 #ifndef TABLE_EXPAND
4866         tables[obuf->table_level]->total_width = width;
4867 #else
4868         tables[obuf->table_level]->real_width = width;
4869         tables[obuf->table_level]->total_width = 0;
4870 #endif
4871         return 1;
4872     case HTML_N_TABLE:
4873         /* should be processed in HTMLlineproc() */
4874         return 1;
4875     case HTML_CENTER:
4876         CLOSE_A;
4877         if (!(obuf->flag & (RB_PREMODE | RB_IGNORE_P)))
4878             flushline(h_env, obuf, envs[h_env->envc].indent, 0, h_env->limit);
4879         RB_SAVE_FLAG(obuf);
4880         RB_SET_ALIGN(obuf, RB_CENTER);
4881         return 1;
4882     case HTML_N_CENTER:
4883         CLOSE_A;
4884         if (!(obuf->flag & RB_PREMODE))
4885             flushline(h_env, obuf, envs[h_env->envc].indent, 0, h_env->limit);
4886         RB_RESTORE_FLAG(obuf);
4887         return 1;
4888     case HTML_DIV:
4889         CLOSE_A;
4890         if (!(obuf->flag & RB_IGNORE_P))
4891             flushline(h_env, obuf, envs[h_env->envc].indent, 0, h_env->limit);
4892         set_alignment(obuf, tag);
4893         return 1;
4894     case HTML_N_DIV:
4895         CLOSE_A;
4896         flushline(h_env, obuf, envs[h_env->envc].indent, 0, h_env->limit);
4897         RB_RESTORE_FLAG(obuf);
4898         return 1;
4899     case HTML_DIV_INT:
4900         CLOSE_P;
4901         if (!(obuf->flag & RB_IGNORE_P))
4902             flushline(h_env, obuf, envs[h_env->envc].indent, 0, h_env->limit);
4903         set_alignment(obuf, tag);
4904         return 1;
4905     case HTML_N_DIV_INT:
4906         CLOSE_P;
4907         flushline(h_env, obuf, envs[h_env->envc].indent, 0, h_env->limit);
4908         RB_RESTORE_FLAG(obuf);
4909         return 1;
4910     case HTML_FORM:
4911         CLOSE_A;
4912         if (!(obuf->flag & RB_IGNORE_P))
4913             flushline(h_env, obuf, envs[h_env->envc].indent, 0, h_env->limit);
4914         tmp = process_form(tag);
4915         if (tmp)
4916             HTMLlineproc1(tmp->ptr, h_env);
4917         return 1;
4918     case HTML_N_FORM:
4919         CLOSE_A;
4920         flushline(h_env, obuf, envs[h_env->envc].indent, 0, h_env->limit);
4921         obuf->flag |= RB_IGNORE_P;
4922         process_n_form();
4923         return 1;
4924     case HTML_INPUT:
4925         close_anchor(h_env, obuf);
4926         tmp = process_input(tag);
4927         if (tmp)
4928             HTMLlineproc1(tmp->ptr, h_env);
4929         return 1;
4930     case HTML_SELECT:
4931         close_anchor(h_env, obuf);
4932         tmp = process_select(tag);
4933         if (tmp)
4934             HTMLlineproc1(tmp->ptr, h_env);
4935         obuf->flag |= RB_INSELECT;
4936         obuf->end_tag = HTML_N_SELECT;
4937         return 1;
4938     case HTML_N_SELECT:
4939         obuf->flag &= ~RB_INSELECT;
4940         obuf->end_tag = 0;
4941         tmp = process_n_select();
4942         if (tmp)
4943             HTMLlineproc1(tmp->ptr, h_env);
4944         return 1;
4945     case HTML_OPTION:
4946         /* nothing */
4947         return 1;
4948     case HTML_TEXTAREA:
4949         close_anchor(h_env, obuf);
4950         tmp = process_textarea(tag, h_env->limit);
4951         if (tmp)
4952             HTMLlineproc1(tmp->ptr, h_env);
4953         obuf->flag |= RB_INTXTA;
4954         obuf->end_tag = HTML_N_TEXTAREA;
4955         return 1;
4956     case HTML_N_TEXTAREA:
4957         obuf->flag &= ~RB_INTXTA;
4958         obuf->end_tag = 0;
4959         tmp = process_n_textarea();
4960         if (tmp)
4961             HTMLlineproc1(tmp->ptr, h_env);
4962         return 1;
4963     case HTML_ISINDEX:
4964         p = "";
4965         q = "!CURRENT_URL!";
4966         parsedtag_get_value(tag, ATTR_PROMPT, &p);
4967         parsedtag_get_value(tag, ATTR_ACTION, &q);
4968         tmp = Strnew_m_charp("<form method=get action=\"",
4969                              html_quote(q),
4970                              "\">",
4971                              html_quote(p),
4972                              "<input type=text name=\"\" accept></form>",
4973                              NULL);
4974         HTMLlineproc1(tmp->ptr, h_env);
4975         return 1;
4976     case HTML_META:
4977         p = q = NULL;
4978         parsedtag_get_value(tag, ATTR_HTTP_EQUIV, &p);
4979         parsedtag_get_value(tag, ATTR_CONTENT, &q);
4980 #ifdef USE_M17N
4981         if (p && q && !strcasecmp(p, "Content-Type") &&
4982             (q = strcasestr(q, "charset")) != NULL) {
4983             q += 7;
4984             SKIP_BLANKS(q);
4985             if (*q == '=') {
4986                 q++;
4987                 SKIP_BLANKS(q);
4988                 meta_charset = wc_guess_charset(q, 0);
4989             }
4990         }
4991         else
4992 #endif
4993         if (p && q && !strcasecmp(p, "refresh")) {
4994             int refresh_interval;
4995             tmp = NULL;
4996             refresh_interval = getMetaRefreshParam(q, &tmp);
4997             if (tmp) {
4998                 q = html_quote(tmp->ptr);
4999                 tmp = Sprintf("Refresh (%d sec) <a href=\"%s\">%s</a>",
5000                               refresh_interval, q, q);
5001             }
5002             else if (refresh_interval > 0)
5003                 tmp = Sprintf("Refresh (%d sec)", refresh_interval);
5004             if (tmp) {
5005                 HTMLlineproc1(tmp->ptr, h_env);
5006                 do_blankline(h_env, obuf, envs[h_env->envc].indent, 0,
5007                              h_env->limit);
5008                 if (!is_redisplay &&
5009                     !((obuf->flag & RB_NOFRAMES) && RenderFrame)) {
5010                     tag->need_reconstruct = TRUE;
5011                     return 0;
5012                 }
5013             }
5014         }
5015         return 1;
5016     case HTML_BASE:
5017 #ifdef USE_IMAGE
5018         p = NULL;
5019         if (parsedtag_get_value(tag, ATTR_HREF, &p)) {
5020             if (!cur_baseURL)
5021                 cur_baseURL = New(ParsedURL);
5022             parseURL(p, cur_baseURL, NULL);
5023         }
5024 #endif
5025     case HTML_MAP:
5026     case HTML_N_MAP:
5027     case HTML_AREA:
5028         return 0;
5029     case HTML_DEL:
5030         switch (displayInsDel) {
5031         case DISPLAY_INS_DEL_SIMPLE:
5032             obuf->flag |= RB_DEL;
5033             break;
5034         case DISPLAY_INS_DEL_NORMAL:
5035             HTMLlineproc1("<U>[DEL:</U>", h_env);
5036             break;
5037         case DISPLAY_INS_DEL_FONTIFY:
5038             obuf->in_strike++;
5039             if (obuf->in_strike == 1) {
5040                 push_tag(obuf, "<s>", HTML_S);
5041             }
5042             break;
5043         }
5044         return 1;
5045     case HTML_N_DEL:
5046         switch (displayInsDel) {
5047         case DISPLAY_INS_DEL_SIMPLE:
5048             obuf->flag &= ~RB_DEL;
5049             break;
5050         case DISPLAY_INS_DEL_NORMAL:
5051             HTMLlineproc1("<U>:DEL]</U>", h_env);
5052         case DISPLAY_INS_DEL_FONTIFY:
5053             if (obuf->in_strike == 0)
5054                 return 1;
5055             if (obuf->in_strike == 1 && close_effect0(obuf, HTML_S))
5056                 obuf->in_strike = 0;
5057             if (obuf->in_strike > 0) {
5058                 obuf->in_strike--;
5059                 if (obuf->in_strike == 0) {
5060                     push_tag(obuf, "</s>", HTML_N_S);
5061                 }
5062             }
5063             break;
5064         }
5065         return 1;
5066     case HTML_S:
5067         switch (displayInsDel) {
5068         case DISPLAY_INS_DEL_SIMPLE:
5069             obuf->flag |= RB_S;
5070             break;
5071         case DISPLAY_INS_DEL_NORMAL:
5072             HTMLlineproc1("<U>[S:</U>", h_env);
5073             break;
5074         case DISPLAY_INS_DEL_FONTIFY:
5075             obuf->in_strike++;
5076             if (obuf->in_strike == 1) {
5077                 push_tag(obuf, "<s>", HTML_S);
5078             }
5079             break;
5080         }
5081         return 1;
5082     case HTML_N_S:
5083         switch (displayInsDel) {
5084         case DISPLAY_INS_DEL_SIMPLE:
5085             obuf->flag &= ~RB_S;
5086             break;
5087         case DISPLAY_INS_DEL_NORMAL:
5088             HTMLlineproc1("<U>:S]</U>", h_env);
5089             break;
5090         case DISPLAY_INS_DEL_FONTIFY:
5091             if (obuf->in_strike == 0)
5092                 return 1;
5093             if (obuf->in_strike == 1 && close_effect0(obuf, HTML_S))
5094                 obuf->in_strike = 0;
5095             if (obuf->in_strike > 0) {
5096                 obuf->in_strike--;
5097                 if (obuf->in_strike == 0) {
5098                     push_tag(obuf, "</s>", HTML_N_S);
5099                 }
5100             }
5101         }
5102         return 1;
5103     case HTML_INS:
5104         switch (displayInsDel) {
5105         case DISPLAY_INS_DEL_SIMPLE:
5106             break;
5107         case DISPLAY_INS_DEL_NORMAL:
5108             HTMLlineproc1("<U>[INS:</U>", h_env);
5109             break;
5110         case DISPLAY_INS_DEL_FONTIFY:
5111             obuf->in_ins++;
5112             if (obuf->in_ins == 1) {
5113                 push_tag(obuf, "<ins>", HTML_INS);
5114             }
5115             break;
5116         }
5117         return 1;
5118     case HTML_N_INS:
5119         switch (displayInsDel) {
5120         case DISPLAY_INS_DEL_SIMPLE:
5121             break;
5122         case DISPLAY_INS_DEL_NORMAL:
5123             HTMLlineproc1("<U>:INS]</U>", h_env);
5124             break;
5125         case DISPLAY_INS_DEL_FONTIFY:
5126             if (obuf->in_ins == 0)
5127                 return 1;
5128             if (obuf->in_ins == 1 && close_effect0(obuf, HTML_INS))
5129                 obuf->in_ins = 0;
5130             if (obuf->in_ins > 0) {
5131                 obuf->in_ins--;
5132                 if (obuf->in_ins == 0) {
5133                     push_tag(obuf, "</ins>", HTML_N_INS);
5134                 }
5135             }
5136             break;
5137         }
5138         return 1;
5139     case HTML_SUP:
5140         if (!(obuf->flag & (RB_DEL | RB_S)))
5141             HTMLlineproc1("^", h_env);
5142         return 1;
5143     case HTML_N_SUP:
5144         return 1;
5145     case HTML_SUB:
5146         if (!(obuf->flag & (RB_DEL | RB_S)))
5147             HTMLlineproc1("[", h_env);
5148         return 1;
5149     case HTML_N_SUB:
5150         if (!(obuf->flag & (RB_DEL | RB_S)))
5151             HTMLlineproc1("]", h_env);
5152         return 1;
5153     case HTML_FONT:
5154     case HTML_N_FONT:
5155     case HTML_NOP:
5156         return 1;
5157     case HTML_BGSOUND:
5158         if (view_unseenobject) {
5159             if (parsedtag_get_value(tag, ATTR_SRC, &p)) {
5160                 Str s;
5161                 q = html_quote(p);
5162                 s = Sprintf("<A HREF=\"%s\">bgsound(%s)</A>", q, q);
5163                 HTMLlineproc1(s->ptr, h_env);
5164             }
5165         }
5166         return 1;
5167     case HTML_EMBED:
5168         if (view_unseenobject) {
5169             if (parsedtag_get_value(tag, ATTR_SRC, &p)) {
5170                 Str s;
5171                 q = html_quote(p);
5172                 s = Sprintf("<A HREF=\"%s\">embed(%s)</A>", q, q);
5173                 HTMLlineproc1(s->ptr, h_env);
5174             }
5175         }
5176         return 1;
5177     case HTML_APPLET:
5178         if (view_unseenobject) {
5179             if (parsedtag_get_value(tag, ATTR_ARCHIVE, &p)) {
5180                 Str s;
5181                 q = html_quote(p);
5182                 s = Sprintf("<A HREF=\"%s\">applet archive(%s)</A>", q, q);
5183                 HTMLlineproc1(s->ptr, h_env);
5184             }
5185         }
5186         return 1;
5187     case HTML_BODY:
5188         if (view_unseenobject) {
5189             if (parsedtag_get_value(tag, ATTR_BACKGROUND, &p)) {
5190                 Str s;
5191                 q = html_quote(p);
5192                 s = Sprintf("<IMG SRC=\"%s\" ALT=\"bg image(%s)\"><BR>", q, q);
5193                 HTMLlineproc1(s->ptr, h_env);
5194             }
5195         }
5196     case HTML_N_HEAD:
5197         if (obuf->flag & RB_TITLE)
5198             HTMLlineproc1("</title>", h_env);
5199     case HTML_HEAD:
5200     case HTML_N_BODY:
5201         return 1;
5202     default:
5203         /* obuf->prevchar = '\0'; */
5204         return 0;
5205     }
5206     /* not reached */
5207     return 0;
5208 }
5209
5210 #define PPUSH(p,c) {outp[pos]=(p);outc[pos]=(c);pos++;}
5211 #define PSIZE   \
5212     if (out_size <= pos + 1) {  \
5213         out_size = pos * 3 / 2; \
5214         outc = New_Reuse(char, outc, out_size); \
5215         outp = New_Reuse(Lineprop, outp, out_size);     \
5216     }
5217
5218 static TextLineListItem *_tl_lp2;
5219
5220 static Str
5221 textlist_feed()
5222 {
5223     TextLine *p;
5224     if (_tl_lp2 != NULL) {
5225         p = _tl_lp2->ptr;
5226         _tl_lp2 = _tl_lp2->next;
5227         return p->line;
5228     }
5229     return NULL;
5230 }
5231
5232 ex_efct(int ex)
5233 {
5234     int effect = 0;
5235
5236     if (! ex)
5237         return 0;
5238
5239     if (ex & PE_EX_ITALIC)
5240         effect |= PE_EX_ITALIC_E;
5241
5242     if (ex & PE_EX_INSERT)
5243         effect |= PE_EX_INSERT_E;
5244
5245     if (ex & PE_EX_STRIKE)
5246         effect |= PE_EX_STRIKE_E;
5247
5248     return effect;
5249 }
5250
5251 static void
5252 HTMLlineproc2body(Buffer *buf, Str (*feed) (), int llimit)
5253 {
5254     static char *outc = NULL;
5255     static Lineprop *outp = NULL;
5256     static int out_size = 0;
5257     Anchor *a_href = NULL, *a_img = NULL, *a_form = NULL;
5258     char *p, *q, *r, *s, *t, *str;
5259     Lineprop mode, effect, ex_effect;
5260     int pos;
5261     int nlines;
5262 #ifdef DEBUG
5263     FILE *debug = NULL;
5264 #endif
5265     struct frameset *frameset_s[FRAMESTACK_SIZE];
5266     int frameset_sp = -1;
5267     union frameset_element *idFrame = NULL;
5268     char *id = NULL;
5269     int hseq, form_id;
5270     Str line;
5271     char *endp;
5272     char symbol = '\0';
5273     int internal = 0;
5274     Anchor **a_textarea = NULL;
5275 #ifdef MENU_SELECT
5276     Anchor **a_select = NULL;
5277 #endif
5278
5279     if (out_size == 0) {
5280         out_size = LINELEN;
5281         outc = NewAtom_N(char, out_size);
5282         outp = NewAtom_N(Lineprop, out_size);
5283     }
5284
5285     n_textarea = -1;
5286     if (!max_textarea) {        /* halfload */
5287         max_textarea = MAX_TEXTAREA;
5288         textarea_str = New_N(Str, max_textarea);
5289         a_textarea = New_N(Anchor *, max_textarea);
5290     }
5291 #ifdef MENU_SELECT
5292     n_select = -1;
5293     if (!max_select) {          /* halfload */
5294         max_select = MAX_SELECT;
5295         select_option = New_N(FormSelectOption, max_select);
5296         a_select = New_N(Anchor *, max_select);
5297     }
5298 #endif
5299
5300 #ifdef DEBUG
5301     if (w3m_debug)
5302         debug = fopen("zzzerr", "a");
5303 #endif
5304
5305     effect = 0;
5306     ex_effect = 0;
5307     nlines = 0;
5308     while ((line = feed()) != NULL) {
5309 #ifdef DEBUG
5310         if (w3m_debug) {
5311             Strfputs(line, debug);
5312             fputc('\n', debug);
5313         }
5314 #endif
5315         if (n_textarea >= 0 && *(line->ptr) != '<') {   /* halfload */
5316             Strcat(textarea_str[n_textarea], line);
5317             continue;
5318         }
5319       proc_again:
5320         if (++nlines == llimit)
5321             break;
5322         pos = 0;
5323 #ifdef ENABLE_REMOVE_TRAILINGSPACES
5324         Strremovetrailingspaces(line);
5325 #endif
5326         str = line->ptr;
5327         endp = str + line->length;
5328         while (str < endp) {
5329             PSIZE;
5330             mode = get_mctype(str);
5331             if ((effect | ex_efct(ex_effect)) & PC_SYMBOL && *str != '<') {
5332 #ifdef USE_M17N
5333                 char **buf = set_symbol(symbol_width0);
5334                 int len;
5335
5336                 p = buf[(int)symbol];
5337                 len = get_mclen(p);
5338                 mode = get_mctype(p);
5339                 PPUSH(mode | effect | ex_efct(ex_effect), *(p++));
5340                 if (--len) {
5341                     mode = (mode & ~PC_WCHAR1) | PC_WCHAR2;
5342                     while (len--) {
5343                         PSIZE;
5344                         PPUSH(mode | effect | ex_efct(ex_effect), *(p++));
5345                     }
5346                 }
5347 #else
5348                 PPUSH(PC_ASCII | effect | ex_efct(ex_effect), SYMBOL_BASE + symbol);
5349 #endif
5350                 str += symbol_width;
5351             }
5352 #ifdef USE_M17N
5353             else if (mode == PC_CTRL || mode == PC_UNDEF) {
5354 #else
5355             else if (mode == PC_CTRL || IS_INTSPACE(*str)) {
5356 #endif
5357                 PPUSH(PC_ASCII | effect | ex_efct(ex_effect), ' ');
5358                 str++;
5359             }
5360 #ifdef USE_M17N
5361             else if (mode & PC_UNKNOWN) {
5362                 PPUSH(PC_ASCII | effect | ex_efct(ex_effect), ' ');
5363                 str += get_mclen(str);
5364             }
5365 #endif
5366             else if (*str != '<' && *str != '&') {
5367 #ifdef USE_M17N
5368                 int len = get_mclen(str);
5369 #endif
5370                 PPUSH(mode | effect | ex_efct(ex_effect), *(str++));
5371 #ifdef USE_M17N
5372                 if (--len) {
5373                     mode = (mode & ~PC_WCHAR1) | PC_WCHAR2;
5374                     while (len--) {
5375                         PSIZE;
5376                         PPUSH(mode | effect | ex_efct(ex_effect), *(str++));
5377                     }
5378                 }
5379 #endif
5380             }
5381             else if (*str == '&') {
5382                 /* 
5383                  * & escape processing
5384                  */
5385                 p = getescapecmd(&str);
5386                 while (*p) {
5387                     PSIZE;
5388                     mode = get_mctype((unsigned char *)p);
5389 #ifdef USE_M17N
5390                     if (mode == PC_CTRL || mode == PC_UNDEF) {
5391 #else
5392                     if (mode == PC_CTRL || IS_INTSPACE(*str)) {
5393 #endif
5394                         PPUSH(PC_ASCII | effect | ex_efct(ex_effect), ' ');
5395                         p++;
5396                     }
5397 #ifdef USE_M17N
5398                     else if (mode & PC_UNKNOWN) {
5399                         PPUSH(PC_ASCII | effect | ex_efct(ex_effect), ' ');
5400                         p += get_mclen(p);
5401                     }
5402 #endif
5403                     else {
5404 #ifdef USE_M17N
5405                         int len = get_mclen(p);
5406 #endif
5407                         PPUSH(mode | effect | ex_efct(ex_effect), *(p++));
5408 #ifdef USE_M17N
5409                         if (--len) {
5410                             mode = (mode & ~PC_WCHAR1) | PC_WCHAR2;
5411                             while (len--) {
5412                                 PSIZE;
5413                                 PPUSH(mode | effect | ex_efct(ex_effect), *(p++));
5414                             }
5415                         }
5416 #endif
5417                     }
5418                 }
5419             }
5420             else {
5421                 /* tag processing */
5422                 struct parsed_tag *tag;
5423                 if (!(tag = parse_tag(&str, TRUE)))
5424                     continue;
5425                 switch (tag->tagid) {
5426                 case HTML_B:
5427                     effect |= PE_BOLD;
5428                     break;
5429                 case HTML_N_B:
5430                     effect &= ~PE_BOLD;
5431                     break;
5432                 case HTML_I:
5433                     ex_effect |= PE_EX_ITALIC;
5434                     break;
5435                 case HTML_N_I:
5436                     ex_effect &= ~PE_EX_ITALIC;
5437                     break;
5438                 case HTML_INS:
5439                     ex_effect |= PE_EX_INSERT;
5440                     break;
5441                 case HTML_N_INS:
5442                     ex_effect &= ~PE_EX_INSERT;
5443                     break;
5444                 case HTML_U:
5445                     effect |= PE_UNDER;
5446                     break;
5447                 case HTML_N_U:
5448                     effect &= ~PE_UNDER;
5449                     break;
5450                 case HTML_S:
5451                     ex_effect |= PE_EX_STRIKE;
5452                     break;
5453                 case HTML_N_S:
5454                     ex_effect &= ~PE_EX_STRIKE;
5455                     break;
5456                 case HTML_A:
5457                     if (renderFrameSet &&
5458                         parsedtag_get_value(tag, ATTR_FRAMENAME, &p)) {
5459                         p = url_quote_conv(p, buf->document_charset);
5460                         if (!idFrame || strcmp(idFrame->body->name, p)) {
5461                             idFrame = search_frame(renderFrameSet, p);
5462                             if (idFrame && idFrame->body->attr != F_BODY)
5463                                 idFrame = NULL;
5464                         }
5465                     }
5466                     p = r = s = NULL;
5467                     q = buf->baseTarget;
5468                     t = "";
5469                     hseq = 0;
5470                     id = NULL;
5471                     if (parsedtag_get_value(tag, ATTR_NAME, &id)) {
5472                         id = url_quote_conv(id, buf->document_charset);
5473                         registerName(buf, id, currentLn(buf), pos);
5474                     }
5475                     if (parsedtag_get_value(tag, ATTR_HREF, &p))
5476                         p = url_quote_conv(remove_space(p),
5477                                            buf->document_charset);
5478                     if (parsedtag_get_value(tag, ATTR_TARGET, &q))
5479                         q = url_quote_conv(q, buf->document_charset);
5480                     if (parsedtag_get_value(tag, ATTR_REFERER, &r))
5481                         r = url_quote_conv(r, buf->document_charset);
5482                     parsedtag_get_value(tag, ATTR_TITLE, &s);
5483                     parsedtag_get_value(tag, ATTR_ACCESSKEY, &t);
5484                     parsedtag_get_value(tag, ATTR_HSEQ, &hseq);
5485                     if (hseq > 0)
5486                         buf->hmarklist =
5487                             putHmarker(buf->hmarklist, currentLn(buf),
5488                                        pos, hseq - 1);
5489                     else if (hseq < 0) {
5490                         int h = -hseq - 1;
5491                         if (buf->hmarklist &&
5492                             h < buf->hmarklist->nmark &&
5493                             buf->hmarklist->marks[h].invalid) {
5494                             buf->hmarklist->marks[h].pos = pos;
5495                             buf->hmarklist->marks[h].line = currentLn(buf);
5496                             buf->hmarklist->marks[h].invalid = 0;
5497                             hseq = -hseq;
5498                         }
5499                     }
5500                     if (id && idFrame)
5501                         idFrame->body->nameList =
5502                             putAnchor(idFrame->body->nameList, id, NULL,
5503                                       (Anchor **)NULL, NULL, NULL, '\0',
5504                                       currentLn(buf), pos);
5505                     if (p) {
5506                         effect |= PE_ANCHOR;
5507                         a_href = registerHref(buf, p, q, r, s,
5508                                               *t, currentLn(buf), pos);
5509                         a_href->hseq = ((hseq > 0) ? hseq : -hseq) - 1;
5510                         a_href->slave = (hseq > 0) ? FALSE : TRUE;
5511                     }
5512                     break;
5513                 case HTML_N_A:
5514                     effect &= ~PE_ANCHOR;
5515                     if (a_href) {
5516                         a_href->end.line = currentLn(buf);
5517                         a_href->end.pos = pos;
5518                         if (a_href->start.line == a_href->end.line &&
5519                             a_href->start.pos == a_href->end.pos) {
5520                             if (buf->hmarklist &&
5521                                 a_href->hseq < buf->hmarklist->nmark)
5522                                 buf->hmarklist->marks[a_href->hseq].invalid = 1;
5523                             a_href->hseq = -1;
5524                         }
5525                         a_href = NULL;
5526                     }
5527                     break;
5528
5529                 case HTML_LINK:
5530                     addLink(buf, tag);
5531                     break;
5532
5533                 case HTML_IMG_ALT:
5534                     if (parsedtag_get_value(tag, ATTR_SRC, &p)) {
5535 #ifdef USE_IMAGE
5536                         int w = -1, h = -1, iseq = 0, ismap = 0;
5537                         int xoffset = 0, yoffset = 0, top = 0, bottom = 0;
5538                         parsedtag_get_value(tag, ATTR_HSEQ, &iseq);
5539                         parsedtag_get_value(tag, ATTR_WIDTH, &w);
5540                         parsedtag_get_value(tag, ATTR_HEIGHT, &h);
5541                         parsedtag_get_value(tag, ATTR_XOFFSET, &xoffset);
5542                         parsedtag_get_value(tag, ATTR_YOFFSET, &yoffset);
5543                         parsedtag_get_value(tag, ATTR_TOP_MARGIN, &top);
5544                         parsedtag_get_value(tag, ATTR_BOTTOM_MARGIN, &bottom);
5545                         if (parsedtag_exists(tag, ATTR_ISMAP))
5546                             ismap = 1;
5547                         q = NULL;
5548                         parsedtag_get_value(tag, ATTR_USEMAP, &q);
5549                         if (iseq > 0) {
5550                             buf->imarklist = putHmarker(buf->imarklist,
5551                                                         currentLn(buf), pos,
5552                                                         iseq - 1);
5553                         }
5554 #endif
5555                         s = NULL;
5556                         parsedtag_get_value(tag, ATTR_TITLE, &s);
5557                         p = url_quote_conv(remove_space(p),
5558                                            buf->document_charset);
5559                         a_img = registerImg(buf, p, s, currentLn(buf), pos);
5560 #ifdef USE_IMAGE
5561                         a_img->hseq = iseq;
5562                         a_img->image = NULL;
5563                         if (iseq > 0) {
5564                             ParsedURL u;
5565                             Image *image;
5566
5567                             parseURL2(a_img->url, &u, cur_baseURL);
5568                             a_img->image = image = New(Image);
5569                             image->url = parsedURL2Str(&u)->ptr;
5570                             if (!uncompressed_file_type(u.file, &image->ext))
5571                                 image->ext = filename_extension(u.file, TRUE);
5572                             image->cache = NULL;
5573                             image->width =
5574                                 (w > MAX_IMAGE_SIZE) ? MAX_IMAGE_SIZE : w;
5575                             image->height =
5576                                 (h > MAX_IMAGE_SIZE) ? MAX_IMAGE_SIZE : h;
5577                             image->xoffset = xoffset;
5578                             image->yoffset = yoffset;
5579                             image->y = currentLn(buf) - top;
5580                             if (image->xoffset < 0 && pos == 0)
5581                                 image->xoffset = 0;
5582                             if (image->yoffset < 0 && image->y == 1)
5583                                 image->yoffset = 0;
5584                             image->rows = 1 + top + bottom;
5585                             image->map = q;
5586                             image->ismap = ismap;
5587                             image->touch = 0;
5588                             image->cache = getImage(image, cur_baseURL,
5589                                                     IMG_FLAG_SKIP);
5590                         }
5591                         else if (iseq < 0) {
5592                             BufferPoint *po = buf->imarklist->marks - iseq - 1;
5593                             Anchor *a = retrieveAnchor(buf->img,
5594                                                        po->line, po->pos);
5595                             if (a) {
5596                                 a_img->url = a->url;
5597                                 a_img->image = a->image;
5598                             }
5599                         }
5600 #endif
5601                     }
5602                     effect |= PE_IMAGE;
5603                     break;
5604                 case HTML_N_IMG_ALT:
5605                     effect &= ~PE_IMAGE;
5606                     if (a_img) {
5607                         a_img->end.line = currentLn(buf);
5608                         a_img->end.pos = pos;
5609                     }
5610                     a_img = NULL;
5611                     break;
5612                 case HTML_INPUT_ALT:
5613                     {
5614                         FormList *form;
5615                         int top = 0, bottom = 0;
5616                         int textareanumber = -1;
5617 #ifdef MENU_SELECT
5618                         int selectnumber = -1;
5619 #endif
5620                         hseq = 0;
5621                         form_id = -1;
5622
5623                         parsedtag_get_value(tag, ATTR_HSEQ, &hseq);
5624                         parsedtag_get_value(tag, ATTR_FID, &form_id);
5625                         parsedtag_get_value(tag, ATTR_TOP_MARGIN, &top);
5626                         parsedtag_get_value(tag, ATTR_BOTTOM_MARGIN, &bottom);
5627                         if (form_id < 0 || form_id > form_max || forms == NULL)
5628                             break;      /* outside of <form>..</form> */
5629                         form = forms[form_id];
5630                         if (hseq > 0) {
5631                             int hpos = pos;
5632                             if (*str == '[')
5633                                 hpos++;
5634                             buf->hmarklist =
5635                                 putHmarker(buf->hmarklist, currentLn(buf),
5636                                            hpos, hseq - 1);
5637                         }
5638                         if (!form->target)
5639                             form->target = buf->baseTarget;
5640                         if (a_textarea &&
5641                             parsedtag_get_value(tag, ATTR_TEXTAREANUMBER,
5642                                                 &textareanumber)) {
5643                             if (textareanumber >= max_textarea) {
5644                                 max_textarea = 2 * textareanumber;
5645                                 textarea_str = New_Reuse(Str, textarea_str,
5646                                                          max_textarea);
5647                                 a_textarea = New_Reuse(Anchor *, a_textarea,
5648                                                        max_textarea);
5649                             }
5650                         }
5651 #ifdef MENU_SELECT
5652                         if (a_select &&
5653                             parsedtag_get_value(tag, ATTR_SELECTNUMBER,
5654                                                 &selectnumber)) {
5655                             if (selectnumber >= max_select) {
5656                                 max_select = 2 * selectnumber;
5657                                 select_option = New_Reuse(FormSelectOption,
5658                                                           select_option,
5659                                                           max_select);
5660                                 a_select = New_Reuse(Anchor *, a_select,
5661                                                      max_select);
5662                             }
5663                         }
5664 #endif
5665                         a_form =
5666                             registerForm(buf, form, tag, currentLn(buf), pos);
5667                         if (a_textarea && textareanumber >= 0)
5668                             a_textarea[textareanumber] = a_form;
5669 #ifdef MENU_SELECT
5670                         if (a_select && selectnumber >= 0)
5671                             a_select[selectnumber] = a_form;
5672 #endif
5673                         if (a_form) {
5674                             a_form->hseq = hseq - 1;
5675                             a_form->y = currentLn(buf) - top;
5676                             a_form->rows = 1 + top + bottom;
5677                             if (!parsedtag_exists(tag, ATTR_NO_EFFECT))
5678                                 effect |= PE_FORM;
5679                             break;
5680                         }
5681                     }
5682                 case HTML_N_INPUT_ALT:
5683                     effect &= ~PE_FORM;
5684                     if (a_form) {
5685                         a_form->end.line = currentLn(buf);
5686                         a_form->end.pos = pos;
5687                         if (a_form->start.line == a_form->end.line &&
5688                             a_form->start.pos == a_form->end.pos)
5689                             a_form->hseq = -1;
5690                     }
5691                     a_form = NULL;
5692                     break;
5693                 case HTML_MAP:
5694                     if (parsedtag_get_value(tag, ATTR_NAME, &p)) {
5695                         MapList *m = New(MapList);
5696                         m->name = Strnew_charp(p);
5697                         m->area = newGeneralList();
5698                         m->next = buf->maplist;
5699                         buf->maplist = m;
5700                     }
5701                     break;
5702                 case HTML_N_MAP:
5703                     /* nothing to do */
5704                     break;
5705                 case HTML_AREA:
5706                     if (buf->maplist == NULL)   /* outside of <map>..</map> */
5707                         break;
5708                     if (parsedtag_get_value(tag, ATTR_HREF, &p)) {
5709                         MapArea *a;
5710                         p = url_quote_conv(remove_space(p),
5711                                            buf->document_charset);
5712                         t = NULL;
5713                         parsedtag_get_value(tag, ATTR_TARGET, &t);
5714                         q = "";
5715                         parsedtag_get_value(tag, ATTR_ALT, &q);
5716                         r = NULL;
5717                         s = NULL;
5718 #ifdef USE_IMAGE
5719                         parsedtag_get_value(tag, ATTR_SHAPE, &r);
5720                         parsedtag_get_value(tag, ATTR_COORDS, &s);
5721 #endif
5722                         a = newMapArea(p, t, q, r, s);
5723                         pushValue(buf->maplist->area, (void *)a);
5724                     }
5725                     break;
5726                 case HTML_FRAMESET:
5727                     frameset_sp++;
5728                     if (frameset_sp >= FRAMESTACK_SIZE)
5729                         break;
5730                     frameset_s[frameset_sp] = newFrameSet(tag);
5731                     if (frameset_s[frameset_sp] == NULL)
5732                         break;
5733                     if (frameset_sp == 0) {
5734                         if (buf->frameset == NULL) {
5735                             buf->frameset = frameset_s[frameset_sp];
5736                         }
5737                         else
5738                             pushFrameTree(&(buf->frameQ),
5739                                           frameset_s[frameset_sp], NULL);
5740                     }
5741                     else
5742                         addFrameSetElement(frameset_s[frameset_sp - 1],
5743                                            *(union frameset_element *)
5744                                            &frameset_s[frameset_sp]);
5745                     break;
5746                 case HTML_N_FRAMESET:
5747                     if (frameset_sp >= 0)
5748                         frameset_sp--;
5749                     break;
5750                 case HTML_FRAME:
5751                     if (frameset_sp >= 0 && frameset_sp < FRAMESTACK_SIZE) {
5752                         union frameset_element element;
5753
5754                         element.body = newFrame(tag, buf);
5755                         addFrameSetElement(frameset_s[frameset_sp], element);
5756                     }
5757                     break;
5758                 case HTML_BASE:
5759                     if (parsedtag_get_value(tag, ATTR_HREF, &p)) {
5760                         p = url_quote_conv(remove_space(p),
5761                                            buf->document_charset);
5762                         if (!buf->baseURL)
5763                             buf->baseURL = New(ParsedURL);
5764                         parseURL(p, buf->baseURL, NULL);
5765                     }
5766                     if (parsedtag_get_value(tag, ATTR_TARGET, &p))
5767                         buf->baseTarget =
5768                             url_quote_conv(p, buf->document_charset);
5769                     break;
5770                 case HTML_META:
5771                     p = q = NULL;
5772                     parsedtag_get_value(tag, ATTR_HTTP_EQUIV, &p);
5773                     parsedtag_get_value(tag, ATTR_CONTENT, &q);
5774                     if (p && q && !strcasecmp(p, "refresh") && MetaRefresh) {
5775                         Str tmp = NULL;
5776                         int refresh_interval = getMetaRefreshParam(q, &tmp);
5777 #ifdef USE_ALARM
5778                         if (tmp) {
5779                             p = url_quote_conv(remove_space(tmp->ptr),
5780                                                buf->document_charset);
5781                             buf->event = setAlarmEvent(buf->event,
5782                                                        refresh_interval,
5783                                                        AL_IMPLICIT_ONCE,
5784                                                        FUNCNAME_gorURL, p);
5785                         }
5786                         else if (refresh_interval > 0)
5787                             buf->event = setAlarmEvent(buf->event,
5788                                                        refresh_interval,
5789                                                        AL_IMPLICIT,
5790                                                        FUNCNAME_reload, NULL);
5791 #else
5792                         if (tmp && refresh_interval == 0) {
5793                             p = url_quote_conv(remove_space(tmp->ptr),
5794                                                buf->document_charset);
5795                             pushEvent(FUNCNAME_gorURL, p);
5796                         }
5797 #endif
5798                     }
5799                     break;
5800                 case HTML_INTERNAL:
5801                     internal = HTML_INTERNAL;
5802                     break;
5803                 case HTML_N_INTERNAL:
5804                     internal = HTML_N_INTERNAL;
5805                     break;
5806                 case HTML_FORM_INT:
5807                     if (parsedtag_get_value(tag, ATTR_FID, &form_id))
5808                         process_form_int(tag, form_id);
5809                     break;
5810                 case HTML_TEXTAREA_INT:
5811                     if (parsedtag_get_value(tag, ATTR_TEXTAREANUMBER,
5812                                             &n_textarea)
5813                         && n_textarea < max_textarea) {
5814                         textarea_str[n_textarea] = Strnew();
5815                     }
5816                     else
5817                         n_textarea = -1;
5818                     break;
5819                 case HTML_N_TEXTAREA_INT:
5820                     if (n_textarea >= 0) {
5821                         FormItemList *item =
5822                             (FormItemList *)a_textarea[n_textarea]->url;
5823                         item->init_value = item->value =
5824                             textarea_str[n_textarea];
5825                     }
5826                     break;
5827 #ifdef MENU_SELECT
5828                 case HTML_SELECT_INT:
5829                     if (parsedtag_get_value(tag, ATTR_SELECTNUMBER, &n_select)
5830                         && n_select < max_select) {
5831                         select_option[n_select].first = NULL;
5832                         select_option[n_select].last = NULL;
5833                     }
5834                     else
5835                         n_select = -1;
5836                     break;
5837                 case HTML_N_SELECT_INT:
5838                     if (n_select >= 0) {
5839                         FormItemList *item =
5840                             (FormItemList *)a_select[n_select]->url;
5841                         item->select_option = select_option[n_select].first;
5842                         chooseSelectOption(item, item->select_option);
5843                         item->init_selected = item->selected;
5844                         item->init_value = item->value;
5845                         item->init_label = item->label;
5846                     }
5847                     break;
5848                 case HTML_OPTION_INT:
5849                     if (n_select >= 0) {
5850                         int selected;
5851                         q = "";
5852                         parsedtag_get_value(tag, ATTR_LABEL, &q);
5853                         p = q;
5854                         parsedtag_get_value(tag, ATTR_VALUE, &p);
5855                         selected = parsedtag_exists(tag, ATTR_SELECTED);
5856                         addSelectOption(&select_option[n_select],
5857                                         Strnew_charp(p), Strnew_charp(q),
5858                                         selected);
5859                     }
5860                     break;
5861 #endif
5862                 case HTML_TITLE_ALT:
5863                     if (parsedtag_get_value(tag, ATTR_TITLE, &p))
5864                         buf->buffername = html_unquote(p);
5865                     break;
5866                 case HTML_SYMBOL:
5867                     effect |= PC_SYMBOL;
5868                     if (parsedtag_get_value(tag, ATTR_TYPE, &p))
5869                         symbol = (char)atoi(p);
5870                     break;
5871                 case HTML_N_SYMBOL:
5872                     effect &= ~PC_SYMBOL;
5873                     break;
5874                 }
5875 #ifdef  ID_EXT
5876                 id = NULL;
5877                 if (parsedtag_get_value(tag, ATTR_ID, &id)) {
5878                     id = url_quote_conv(id, buf->document_charset);
5879                     registerName(buf, id, currentLn(buf), pos);
5880                 }
5881                 if (renderFrameSet &&
5882                     parsedtag_get_value(tag, ATTR_FRAMENAME, &p)) {
5883                     p = url_quote_conv(p, buf->document_charset);
5884                     if (!idFrame || strcmp(idFrame->body->name, p)) {
5885                         idFrame = search_frame(renderFrameSet, p);
5886                         if (idFrame && idFrame->body->attr != F_BODY)
5887                             idFrame = NULL;
5888                     }
5889                 }
5890                 if (id && idFrame)
5891                     idFrame->body->nameList =
5892                         putAnchor(idFrame->body->nameList, id, NULL,
5893                                   (Anchor **)NULL, NULL, NULL, '\0',
5894                                   currentLn(buf), pos);
5895 #endif                          /* ID_EXT */
5896             }
5897         }
5898         /* end of processing for one line */
5899         if (!internal)
5900             addnewline(buf, outc, outp, NULL, pos, -1, nlines);
5901         if (internal == HTML_N_INTERNAL)
5902             internal = 0;
5903         if (str != endp) {
5904             line = Strsubstr(line, str - line->ptr, endp - str);
5905             goto proc_again;
5906         }
5907     }
5908 #ifdef DEBUG
5909     if (w3m_debug)
5910         fclose(debug);
5911 #endif
5912     for (form_id = 1; form_id <= form_max; form_id++)
5913         forms[form_id]->next = forms[form_id - 1];
5914     buf->formlist = (form_max >= 0) ? forms[form_max] : NULL;
5915     if (n_textarea)
5916         addMultirowsForm(buf, buf->formitem);
5917 #ifdef USE_IMAGE
5918     addMultirowsImg(buf, buf->img);
5919 #endif
5920 }
5921
5922 static void
5923 addLink(Buffer *buf, struct parsed_tag *tag)
5924 {
5925     char *href = NULL, *title = NULL, *ctype = NULL, *rel = NULL, *rev = NULL;
5926     char type = LINK_TYPE_NONE;
5927     LinkList *l;
5928
5929     parsedtag_get_value(tag, ATTR_HREF, &href);
5930     if (href)
5931         href = url_quote_conv(remove_space(href), buf->document_charset);
5932     parsedtag_get_value(tag, ATTR_TITLE, &title);
5933     parsedtag_get_value(tag, ATTR_TYPE, &ctype);
5934     parsedtag_get_value(tag, ATTR_REL, &rel);
5935     if (rel != NULL) {
5936         /* forward link type */
5937         type = LINK_TYPE_REL;
5938         if (title == NULL)
5939             title = rel;
5940     }
5941     parsedtag_get_value(tag, ATTR_REV, &rev);
5942     if (rev != NULL) {
5943         /* reverse link type */
5944         type = LINK_TYPE_REV;
5945         if (title == NULL)
5946             title = rev;
5947     }
5948
5949     l = New(LinkList);
5950     l->url = href;
5951     l->title = title;
5952     l->ctype = ctype;
5953     l->type = type;
5954     l->next = NULL;
5955     if (buf->linklist) {
5956         LinkList *i;
5957         for (i = buf->linklist; i->next; i = i->next) ;
5958         i->next = l;
5959     }
5960     else
5961         buf->linklist = l;
5962 }
5963
5964 void
5965 HTMLlineproc2(Buffer *buf, TextLineList *tl)
5966 {
5967     _tl_lp2 = tl->first;
5968     HTMLlineproc2body(buf, textlist_feed, -1);
5969 }
5970
5971 static InputStream _file_lp2;
5972
5973 static Str
5974 file_feed()
5975 {
5976     Str s;
5977     s = StrISgets(_file_lp2);
5978     if (s->length == 0) {
5979         ISclose(_file_lp2);
5980         return NULL;
5981     }
5982     return s;
5983 }
5984
5985 void
5986 HTMLlineproc3(Buffer *buf, InputStream stream)
5987 {
5988     _file_lp2 = stream;
5989     HTMLlineproc2body(buf, file_feed, -1);
5990 }
5991
5992 static void
5993 proc_escape(struct readbuffer *obuf, char **str_return)
5994 {
5995     char *str = *str_return, *estr;
5996     int ech = getescapechar(str_return);
5997     int width, n_add = *str_return - str;
5998     Lineprop mode = PC_ASCII;
5999
6000     if (ech < 0) {
6001         *str_return = str;
6002         proc_mchar(obuf, obuf->flag & RB_SPECIAL, 1, str_return, PC_ASCII);
6003         return;
6004     }
6005     mode = IS_CNTRL(ech) ? PC_CTRL : PC_ASCII;
6006
6007     estr = conv_entity(ech);
6008     check_breakpoint(obuf, obuf->flag & RB_SPECIAL, estr);
6009     width = get_strwidth(estr);
6010     if (width == 1 && ech == (unsigned char)*estr &&
6011         ech != '&' && ech != '<' && ech != '>') {
6012         if (IS_CNTRL(ech))
6013             mode = PC_CTRL;
6014         push_charp(obuf, width, estr, mode);
6015     }
6016     else
6017         push_nchars(obuf, width, str, n_add, mode);
6018     set_prevchar(obuf->prevchar, estr, strlen(estr));
6019     obuf->prev_ctype = mode;
6020 }
6021
6022
6023 static int
6024 need_flushline(struct html_feed_environ *h_env, struct readbuffer *obuf,
6025                Lineprop mode)
6026 {
6027     char ch;
6028
6029     if (obuf->flag & RB_PRE_INT) {
6030         if (obuf->pos > h_env->limit)
6031             return 1;
6032         else
6033             return 0;
6034     }
6035
6036     ch = Strlastchar(obuf->line);
6037     /* if (ch == ' ' && obuf->tag_sp > 0) */
6038     if (ch == ' ')
6039         return 0;
6040
6041     if (obuf->pos > h_env->limit)
6042         return 1;
6043
6044     return 0;
6045 }
6046
6047 static int
6048 table_width(struct html_feed_environ *h_env, int table_level)
6049 {
6050     int width;
6051     if (table_level < 0)
6052         return 0;
6053     width = tables[table_level]->total_width;
6054     if (table_level > 0 || width > 0)
6055         return width;
6056     return h_env->limit - h_env->envs[h_env->envc].indent;
6057 }
6058
6059 /* HTML processing first pass */
6060 void
6061 HTMLlineproc0(char *line, struct html_feed_environ *h_env, int internal)
6062 {
6063     Lineprop mode;
6064     int cmd;
6065     struct readbuffer *obuf = h_env->obuf;
6066     int indent, delta;
6067     struct parsed_tag *tag;
6068     Str tokbuf;
6069     struct table *tbl = NULL;
6070     struct table_mode *tbl_mode = NULL;
6071     int tbl_width = 0;
6072 #ifdef USE_M17N
6073     int is_hangul, prev_is_hangul = 0;
6074 #endif
6075
6076 #ifdef DEBUG
6077     if (w3m_debug) {
6078         FILE *f = fopen("zzzproc1", "a");
6079         fprintf(f, "%c%c%c%c",
6080                 (obuf->flag & RB_PREMODE) ? 'P' : ' ',
6081                 (obuf->table_level >= 0) ? 'T' : ' ',
6082                 (obuf->flag & RB_INTXTA) ? 'X' : ' ',
6083                 (obuf->flag & (RB_SCRIPT | RB_STYLE)) ? 'S' : ' ');
6084         fprintf(f, "HTMLlineproc1(\"%s\",%d,%lx)\n", line, h_env->limit,
6085                 (unsigned long)h_env);
6086         fclose(f);
6087     }
6088 #endif
6089
6090     tokbuf = Strnew();
6091
6092   table_start:
6093     if (obuf->table_level >= 0) {
6094         int level = min(obuf->table_level, MAX_TABLE - 1);
6095         tbl = tables[level];
6096         tbl_mode = &table_mode[level];
6097         tbl_width = table_width(h_env, level);
6098     }
6099
6100     while (*line != '\0') {
6101         char *str, *p;
6102         int is_tag = FALSE;
6103         int pre_mode = (obuf->table_level >= 0) ? tbl_mode->pre_mode :
6104             obuf->flag;
6105         int end_tag = (obuf->table_level >= 0) ? tbl_mode->end_tag :
6106             obuf->end_tag;
6107
6108         if (*line == '<' || obuf->status != R_ST_NORMAL) {
6109             /* 
6110              * Tag processing
6111              */
6112             if (obuf->status == R_ST_EOL)
6113                 obuf->status = R_ST_NORMAL;
6114             else {
6115                 read_token(h_env->tagbuf, &line, &obuf->status,
6116                            pre_mode & RB_PREMODE, obuf->status != R_ST_NORMAL);
6117                 if (obuf->status != R_ST_NORMAL)
6118                     return;
6119             }
6120             if (h_env->tagbuf->length == 0)
6121                 continue;
6122             str = h_env->tagbuf->ptr;
6123             if (*str == '<') {
6124                 if (str[1] && REALLY_THE_BEGINNING_OF_A_TAG(str))
6125                     is_tag = TRUE;
6126                 else if (!(pre_mode & (RB_PLAIN | RB_INTXTA | RB_INSELECT |
6127                                        RB_SCRIPT | RB_STYLE | RB_TITLE))) {
6128                     line = Strnew_m_charp(str + 1, line, NULL)->ptr;
6129                     str = "&lt;";
6130                 }
6131             }
6132         }
6133         else {
6134             read_token(tokbuf, &line, &obuf->status, pre_mode & RB_PREMODE, 0);
6135             if (obuf->status != R_ST_NORMAL)    /* R_ST_AMP ? */
6136                 obuf->status = R_ST_NORMAL;
6137             str = tokbuf->ptr;
6138         }
6139
6140         if (pre_mode & (RB_PLAIN | RB_INTXTA | RB_INSELECT | RB_SCRIPT |
6141                         RB_STYLE | RB_TITLE)) {
6142             if (is_tag) {
6143                 p = str;
6144                 if ((tag = parse_tag(&p, internal))) {
6145                     if (tag->tagid == end_tag ||
6146                         (pre_mode & RB_INSELECT && tag->tagid == HTML_N_FORM)
6147                         || (pre_mode & RB_TITLE
6148                             && (tag->tagid == HTML_N_HEAD
6149                                 || tag->tagid == HTML_BODY)))
6150                         goto proc_normal;
6151                 }
6152             }
6153             /* title */
6154             if (pre_mode & RB_TITLE) {
6155                 feed_title(str);
6156                 continue;
6157             }
6158             /* select */
6159             if (pre_mode & RB_INSELECT) {
6160                 if (obuf->table_level >= 0)
6161                     goto proc_normal;
6162                 feed_select(str);
6163                 continue;
6164             }
6165             if (is_tag) {
6166                 if (strncmp(str, "<!--", 4) && (p = strchr(str + 1, '<'))) {
6167                     str = Strnew_charp_n(str, p - str)->ptr;
6168                     line = Strnew_m_charp(p, line, NULL)->ptr;
6169                 }
6170                 is_tag = FALSE;
6171             }
6172             if (obuf->table_level >= 0)
6173                 goto proc_normal;
6174             /* textarea */
6175             if (pre_mode & RB_INTXTA) {
6176                 feed_textarea(str);
6177                 continue;
6178             }
6179             /* script */
6180             if (pre_mode & RB_SCRIPT)
6181                 continue;
6182             /* style */
6183             if (pre_mode & RB_STYLE)
6184                 continue;
6185         }
6186
6187       proc_normal:
6188         if (obuf->table_level >= 0) {
6189             /* 
6190              * within table: in <table>..</table>, all input tokens
6191              * are fed to the table renderer, and then the renderer
6192              * makes HTML output.
6193              */
6194             switch (feed_table(tbl, str, tbl_mode, tbl_width, internal)) {
6195             case 0:
6196                 /* </table> tag */
6197                 obuf->table_level--;
6198                 if (obuf->table_level >= MAX_TABLE - 1)
6199                     continue;
6200                 end_table(tbl);
6201                 if (obuf->table_level >= 0) {
6202                     struct table *tbl0 = tables[obuf->table_level];
6203                     str = Sprintf("<table_alt tid=%d>", tbl0->ntable)->ptr;
6204                     pushTable(tbl0, tbl);
6205                     tbl = tbl0;
6206                     tbl_mode = &table_mode[obuf->table_level];
6207                     tbl_width = table_width(h_env, obuf->table_level);
6208                     feed_table(tbl, str, tbl_mode, tbl_width, TRUE);
6209                     continue;
6210                     /* continue to the next */
6211                 }
6212                 if (obuf->flag & RB_DEL)
6213                     continue;
6214                 /* all tables have been read */
6215                 if (tbl->vspace > 0 && !(obuf->flag & RB_IGNORE_P)) {
6216                     int indent = h_env->envs[h_env->envc].indent;
6217                     flushline(h_env, obuf, indent, 0, h_env->limit);
6218                     do_blankline(h_env, obuf, indent, 0, h_env->limit);
6219                 }
6220                 save_fonteffect(h_env, obuf);
6221                 renderTable(tbl, tbl_width, h_env);
6222                 restore_fonteffect(h_env, obuf);
6223                 obuf->flag &= ~RB_IGNORE_P;
6224                 if (tbl->vspace > 0) {
6225                     int indent = h_env->envs[h_env->envc].indent;
6226                     do_blankline(h_env, obuf, indent, 0, h_env->limit);
6227                     obuf->flag |= RB_IGNORE_P;
6228                 }
6229                 set_space_to_prevchar(obuf->prevchar);
6230                 continue;
6231             case 1:
6232                 /* <table> tag */
6233                 break;
6234             default:
6235                 continue;
6236             }
6237         }
6238
6239         if (is_tag) {
6240 /*** Beginning of a new tag ***/
6241             if ((tag = parse_tag(&str, internal)))
6242                 cmd = tag->tagid;
6243             else
6244                 continue;
6245             /* process tags */
6246             if (HTMLtagproc1(tag, h_env) == 0) {
6247                 /* preserve the tag for second-stage processing */
6248                 if (parsedtag_need_reconstruct(tag))
6249                     h_env->tagbuf = parsedtag2str(tag);
6250                 push_tag(obuf, h_env->tagbuf->ptr, cmd);
6251             }
6252 #ifdef ID_EXT
6253             else {
6254                 process_idattr(obuf, cmd, tag);
6255             }
6256 #endif                          /* ID_EXT */
6257             obuf->bp.init_flag = 1;
6258             clear_ignore_p_flag(cmd, obuf);
6259             if (cmd == HTML_TABLE)
6260                 goto table_start;
6261             else
6262                 continue;
6263         }
6264
6265         if (obuf->flag & (RB_DEL | RB_S))
6266             continue;
6267         while (*str) {
6268             mode = get_mctype(str);
6269             delta = get_mcwidth(str);
6270             if (obuf->flag & (RB_SPECIAL & ~RB_NOBR)) {
6271                 char ch = *str;
6272                 if (!(obuf->flag & RB_PLAIN) && (*str == '&')) {
6273                     char *p = str;
6274                     int ech = getescapechar(&p);
6275                     if (ech == '\n' || ech == '\r') {
6276                         ch = '\n';
6277                         str = p - 1;
6278                     }
6279                     else if (ech == '\t') {
6280                         ch = '\t';
6281                         str = p - 1;
6282                     }
6283                 }
6284                 if (ch != '\n')
6285                     obuf->flag &= ~RB_IGNORE_P;
6286                 if (ch == '\n') {
6287                     str++;
6288                     if (obuf->flag & RB_IGNORE_P) {
6289                         obuf->flag &= ~RB_IGNORE_P;
6290                         continue;
6291                     }
6292                     if (obuf->flag & RB_PRE_INT)
6293                         PUSH(' ');
6294                     else
6295                         flushline(h_env, obuf, h_env->envs[h_env->envc].indent,
6296                                   1, h_env->limit);
6297                 }
6298                 else if (ch == '\t') {
6299                     do {
6300                         PUSH(' ');
6301                     } while ((h_env->envs[h_env->envc].indent + obuf->pos)
6302                              % Tabstop != 0);
6303                     str++;
6304                 }
6305                 else if (obuf->flag & RB_PLAIN) {
6306                     char *p = html_quote_char(*str);
6307                     if (p) {
6308                         push_charp(obuf, 1, p, PC_ASCII);
6309                         str++;
6310                     }
6311                     else {
6312                         proc_mchar(obuf, 1, delta, &str, mode);
6313                     }
6314                 }
6315                 else {
6316                     if (*str == '&')
6317                         proc_escape(obuf, &str);
6318                     else
6319                         proc_mchar(obuf, 1, delta, &str, mode);
6320                 }
6321                 if (obuf->flag & (RB_SPECIAL & ~RB_PRE_INT))
6322                     continue;
6323             }
6324             else {
6325                 if (!IS_SPACE(*str))
6326                     obuf->flag &= ~RB_IGNORE_P;
6327                 if ((mode == PC_ASCII || mode == PC_CTRL) && IS_SPACE(*str)) {
6328                     if (*obuf->prevchar->ptr != ' ') {
6329                         PUSH(' ');
6330                     }
6331                     str++;
6332                 }
6333                 else {
6334 #ifdef USE_M17N
6335                     if (mode == PC_KANJI1)
6336                         is_hangul = wtf_is_hangul((wc_uchar *) str);
6337                     else
6338                         is_hangul = 0;
6339                     if (mode == PC_KANJI1 &&
6340                         !is_hangul && !prev_is_hangul &&
6341                         obuf->pos > h_env->envs[h_env->envc].indent &&
6342                         Strlastchar(obuf->line) == ' ') {
6343                         while (obuf->line->length >= 2 &&
6344                                !strncmp(obuf->line->ptr + obuf->line->length -
6345                                         2, "  ", 2)
6346                                && obuf->pos >= h_env->envs[h_env->envc].indent) {
6347                             Strshrink(obuf->line, 1);
6348                             obuf->pos--;
6349                         }
6350                         if (obuf->line->length >= 3 &&
6351                             obuf->prev_ctype == PC_KANJI1 &&
6352                             Strlastchar(obuf->line) == ' ' &&
6353                             obuf->pos >= h_env->envs[h_env->envc].indent) {
6354                             Strshrink(obuf->line, 1);
6355                             obuf->pos--;
6356                         }
6357                     }
6358                     prev_is_hangul = is_hangul;
6359 #endif
6360                     if (*str == '&')
6361                         proc_escape(obuf, &str);
6362                     else
6363                         proc_mchar(obuf, obuf->flag & RB_SPECIAL, delta, &str,
6364                                    mode);
6365                 }
6366             }
6367             if (need_flushline(h_env, obuf, mode)) {
6368                 char *bp = obuf->line->ptr + obuf->bp.len;
6369                 char *tp = bp - obuf->bp.tlen;
6370                 int i = 0;
6371
6372                 if (tp > obuf->line->ptr && tp[-1] == ' ')
6373                     i = 1;
6374
6375                 indent = h_env->envs[h_env->envc].indent;
6376                 if (obuf->bp.pos - i > indent) {
6377                     Str line;
6378                     append_tags(obuf);
6379                     line = Strnew_charp(bp);
6380                     Strshrink(obuf->line, obuf->line->length - obuf->bp.len);
6381 #ifdef FORMAT_NICE
6382                     if (obuf->pos - i > h_env->limit)
6383                         obuf->flag |= RB_FILL;
6384 #endif                          /* FORMAT_NICE */
6385                     back_to_breakpoint(obuf);
6386                     flushline(h_env, obuf, indent, 0, h_env->limit);
6387 #ifdef FORMAT_NICE
6388                     obuf->flag &= ~RB_FILL;
6389 #endif                          /* FORMAT_NICE */
6390                     HTMLlineproc1(line->ptr, h_env);
6391                 }
6392             }
6393         }
6394     }
6395     if (!(obuf->flag & (RB_SPECIAL | RB_INTXTA | RB_INSELECT))) {
6396         char *tp;
6397         int i = 0;
6398
6399         if (obuf->bp.pos == obuf->pos) {
6400             tp = &obuf->line->ptr[obuf->bp.len - obuf->bp.tlen];
6401         }
6402         else {
6403             tp = &obuf->line->ptr[obuf->line->length];
6404         }
6405
6406         if (tp > obuf->line->ptr && tp[-1] == ' ')
6407             i = 1;
6408         indent = h_env->envs[h_env->envc].indent;
6409         if (obuf->pos - i > h_env->limit) {
6410 #ifdef FORMAT_NICE
6411             obuf->flag |= RB_FILL;
6412 #endif                          /* FORMAT_NICE */
6413             flushline(h_env, obuf, indent, 0, h_env->limit);
6414 #ifdef FORMAT_NICE
6415             obuf->flag &= ~RB_FILL;
6416 #endif                          /* FORMAT_NICE */
6417         }
6418     }
6419 }
6420
6421 extern char *NullLine;
6422 extern Lineprop NullProp[];
6423
6424 #ifndef USE_ANSI_COLOR
6425 #define addnewline2(a,b,c,d,e,f) _addnewline2(a,b,c,e,f)
6426 #endif
6427 static void
6428 addnewline2(Buffer *buf, char *line, Lineprop *prop, Linecolor *color, int pos,
6429             int nlines)
6430 {
6431     Line *l;
6432     l = New(Line);
6433     l->next = NULL;
6434     l->lineBuf = line;
6435     l->propBuf = prop;
6436 #ifdef USE_ANSI_COLOR
6437     l->colorBuf = color;
6438 #endif
6439     l->len = pos;
6440     l->width = -1;
6441     l->size = pos;
6442     l->bpos = 0;
6443     l->bwidth = 0;
6444     l->prev = buf->currentLine;
6445     if (buf->currentLine) {
6446         l->next = buf->currentLine->next;
6447         buf->currentLine->next = l;
6448     }
6449     else
6450         l->next = NULL;
6451     if (buf->lastLine == NULL || buf->lastLine == buf->currentLine)
6452         buf->lastLine = l;
6453     buf->currentLine = l;
6454     if (buf->firstLine == NULL)
6455         buf->firstLine = l;
6456     l->linenumber = ++buf->allLine;
6457     if (nlines < 0) {
6458         /*     l->real_linenumber = l->linenumber;     */
6459         l->real_linenumber = 0;
6460     }
6461     else {
6462         l->real_linenumber = nlines;
6463     }
6464     l = NULL;
6465 }
6466
6467 static void
6468 addnewline(Buffer *buf, char *line, Lineprop *prop, Linecolor *color, int pos,
6469            int width, int nlines)
6470 {
6471     char *s;
6472     Lineprop *p;
6473 #ifdef USE_ANSI_COLOR
6474     Linecolor *c;
6475 #endif
6476     Line *l;
6477     int i, bpos, bwidth;
6478
6479     if (pos > 0) {
6480         s = allocStr(line, pos);
6481         p = NewAtom_N(Lineprop, pos);
6482         bcopy((void *)prop, (void *)p, pos * sizeof(Lineprop));
6483     }
6484     else {
6485         s = NullLine;
6486         p = NullProp;
6487     }
6488 #ifdef USE_ANSI_COLOR
6489     if (pos > 0 && color) {
6490         c = NewAtom_N(Linecolor, pos);
6491         bcopy((void *)color, (void *)c, pos * sizeof(Linecolor));
6492     }
6493     else {
6494         c = NULL;
6495     }
6496 #endif
6497     addnewline2(buf, s, p, c, pos, nlines);
6498     if (pos <= 0 || width <= 0)
6499         return;
6500     bpos = 0;
6501     bwidth = 0;
6502     while (1) {
6503         l = buf->currentLine;
6504         l->bpos = bpos;
6505         l->bwidth = bwidth;
6506         i = columnLen(l, width);
6507         if (i == 0) {
6508             i++;
6509 #ifdef USE_M17N
6510             while (i < l->len && p[i] & PC_WCHAR2)
6511                 i++;
6512 #endif
6513         }
6514         l->len = i;
6515         l->width = COLPOS(l, l->len);
6516         if (pos <= i)
6517             return;
6518         bpos += l->len;
6519         bwidth += l->width;
6520         s += i;
6521         p += i;
6522 #ifdef USE_ANSI_COLOR
6523         if (c)
6524             c += i;
6525 #endif
6526         pos -= i;
6527         addnewline2(buf, s, p, c, pos, nlines);
6528     }
6529 }
6530
6531 /* 
6532  * loadHTMLBuffer: read file and make new buffer
6533  */
6534 Buffer *
6535 loadHTMLBuffer(URLFile *f, Buffer *newBuf)
6536 {
6537     FILE *src = NULL;
6538     Str tmp;
6539
6540     if (newBuf == NULL)
6541         newBuf = newBuffer(INIT_BUFFER_WIDTH);
6542     if (newBuf->sourcefile == NULL &&
6543         (f->scheme != SCM_LOCAL || newBuf->mailcap)) {
6544         tmp = tmpfname(TMPF_SRC, ".html");
6545         src = fopen(tmp->ptr, "w");
6546         if (src)
6547             newBuf->sourcefile = tmp->ptr;
6548     }
6549
6550     loadHTMLstream(f, newBuf, src, newBuf->bufferprop & BP_FRAME);
6551
6552     newBuf->topLine = newBuf->firstLine;
6553     newBuf->lastLine = newBuf->currentLine;
6554     newBuf->currentLine = newBuf->firstLine;
6555     if (n_textarea)
6556         formResetBuffer(newBuf, newBuf->formitem);
6557     if (src)
6558         fclose(src);
6559
6560     return newBuf;
6561 }
6562
6563 static char *_size_unit[] = { "b", "kb", "Mb", "Gb", "Tb",
6564     "Pb", "Eb", "Zb", "Bb", "Yb", NULL
6565 };
6566
6567 char *
6568 convert_size(clen_t size, int usefloat)
6569 {
6570     float csize;
6571     int sizepos = 0;
6572     char **sizes = _size_unit;
6573
6574     csize = (float)size;
6575     while (csize >= 999.495 && sizes[sizepos + 1]) {
6576         csize = csize / 1024.0;
6577         sizepos++;
6578     }
6579     return Sprintf(usefloat ? "%.3g%s" : "%.0f%s",
6580                    floor(csize * 100.0 + 0.5) / 100.0, sizes[sizepos])->ptr;
6581 }
6582
6583 char *
6584 convert_size2(clen_t size1, clen_t size2, int usefloat)
6585 {
6586     char **sizes = _size_unit;
6587     float csize, factor = 1;
6588     int sizepos = 0;
6589
6590     csize = (float)((size1 > size2) ? size1 : size2);
6591     while (csize / factor >= 999.495 && sizes[sizepos + 1]) {
6592         factor *= 1024.0;
6593         sizepos++;
6594     }
6595     return Sprintf(usefloat ? "%.3g/%.3g%s" : "%.0f/%.0f%s",
6596                    floor(size1 / factor * 100.0 + 0.5) / 100.0,
6597                    floor(size2 / factor * 100.0 + 0.5) / 100.0,
6598                    sizes[sizepos])->ptr;
6599 }
6600
6601 void
6602 showProgress(clen_t * linelen, clen_t * trbyte)
6603 {
6604     int i, j, rate, duration, eta, pos;
6605     static time_t last_time, start_time;
6606     time_t cur_time;
6607     Str messages;
6608     char *fmtrbyte, *fmrate;
6609
6610     if (!fmInitialized)
6611         return;
6612
6613     if (*linelen < 1024)
6614         return;
6615     if (current_content_length > 0) {
6616         double ratio;
6617         cur_time = time(0);
6618         if (*trbyte == 0) {
6619             move(LASTLINE, 0);
6620             clrtoeolx();
6621             start_time = cur_time;
6622         }
6623         *trbyte += *linelen;
6624         *linelen = 0;
6625         if (cur_time == last_time)
6626             return;
6627         last_time = cur_time;
6628         move(LASTLINE, 0);
6629         ratio = 100.0 * (*trbyte) / current_content_length;
6630         fmtrbyte = convert_size2(*trbyte, current_content_length, 1);
6631         duration = cur_time - start_time;
6632         if (duration) {
6633             rate = *trbyte / duration;
6634             fmrate = convert_size(rate, 1);
6635             eta = rate ? (current_content_length - *trbyte) / rate : -1;
6636             messages = Sprintf("%11s %3.0f%% "
6637                                "%7s/s "
6638                                "eta %02d:%02d:%02d     ",
6639                                fmtrbyte, ratio,
6640                                fmrate,
6641                                eta / (60 * 60), (eta / 60) % 60, eta % 60);
6642         }
6643         else {
6644             messages = Sprintf("%11s %3.0f%%                          ",
6645                                fmtrbyte, ratio);
6646         }
6647         addstr(messages->ptr);
6648         pos = 42;
6649         i = pos + (COLS - pos - 1) * (*trbyte) / current_content_length;
6650         move(LASTLINE, pos);
6651         standout();
6652         addch(' ');
6653         for (j = pos + 1; j <= i; j++)
6654             addch('|');
6655         standend();
6656         /* no_clrtoeol(); */
6657         refresh();
6658     }
6659     else {
6660         cur_time = time(0);
6661         if (*trbyte == 0) {
6662             move(LASTLINE, 0);
6663             clrtoeolx();
6664             start_time = cur_time;
6665         }
6666         *trbyte += *linelen;
6667         *linelen = 0;
6668         if (cur_time == last_time)
6669             return;
6670         last_time = cur_time;
6671         move(LASTLINE, 0);
6672         fmtrbyte = convert_size(*trbyte, 1);
6673         duration = cur_time - start_time;
6674         if (duration) {
6675             fmrate = convert_size(*trbyte / duration, 1);
6676             messages = Sprintf("%7s loaded %7s/s", fmtrbyte, fmrate);
6677         }
6678         else {
6679             messages = Sprintf("%7s loaded", fmtrbyte);
6680         }
6681         message(messages->ptr, 0, 0);
6682         refresh();
6683     }
6684 }
6685
6686 void
6687 init_henv(struct html_feed_environ *h_env, struct readbuffer *obuf,
6688           struct environment *envs, int nenv, TextLineList *buf,
6689           int limit, int indent)
6690 {
6691     envs[0].indent = indent;
6692
6693     obuf->line = Strnew();
6694     obuf->cprop = 0;
6695     obuf->pos = 0;
6696     obuf->prevchar = Strnew_size(8);
6697     set_space_to_prevchar(obuf->prevchar);
6698     obuf->flag = RB_IGNORE_P;
6699     obuf->flag_sp = 0;
6700     obuf->status = R_ST_NORMAL;
6701     obuf->table_level = -1;
6702     obuf->nobr_level = 0;
6703     bzero((void *)&obuf->anchor, sizeof(obuf->anchor));
6704     obuf->img_alt = 0;
6705     obuf->in_bold = 0;
6706     obuf->in_italic = 0;
6707     obuf->in_under = 0;
6708     obuf->in_strike = 0;
6709     obuf->in_ins = 0;
6710     obuf->prev_ctype = PC_ASCII;
6711     obuf->tag_sp = 0;
6712     obuf->fontstat_sp = 0;
6713     obuf->top_margin = 0;
6714     obuf->bottom_margin = 0;
6715     obuf->bp.init_flag = 1;
6716     set_breakpoint(obuf, 0);
6717
6718     h_env->buf = buf;
6719     h_env->f = NULL;
6720     h_env->obuf = obuf;
6721     h_env->tagbuf = Strnew();
6722     h_env->limit = limit;
6723     h_env->maxlimit = 0;
6724     h_env->envs = envs;
6725     h_env->nenv = nenv;
6726     h_env->envc = 0;
6727     h_env->envc_real = 0;
6728     h_env->title = NULL;
6729     h_env->blank_lines = 0;
6730 }
6731
6732 void
6733 completeHTMLstream(struct html_feed_environ *h_env, struct readbuffer *obuf)
6734 {
6735     close_anchor(h_env, obuf);
6736     if (obuf->img_alt) {
6737         push_tag(obuf, "</img_alt>", HTML_N_IMG_ALT);
6738         obuf->img_alt = NULL;
6739     }
6740     if (obuf->in_bold) {
6741         push_tag(obuf, "</b>", HTML_N_B);
6742         obuf->in_bold = 0;
6743     }
6744     if (obuf->in_italic) {
6745         push_tag(obuf, "</i>", HTML_N_I);
6746         obuf->in_italic = 0;
6747     }
6748     if (obuf->in_under) {
6749         push_tag(obuf, "</u>", HTML_N_U);
6750         obuf->in_under = 0;
6751     }
6752     if (obuf->in_strike) {
6753         push_tag(obuf, "</s>", HTML_N_S);
6754         obuf->in_strike = 0;
6755     }
6756     if (obuf->in_ins) {
6757         push_tag(obuf, "</ins>", HTML_N_INS);
6758         obuf->in_ins = 0;
6759     }
6760     if (obuf->flag & RB_INTXTA)
6761         HTMLlineproc1("</textarea>", h_env);
6762     /* for unbalanced select tag */
6763     if (obuf->flag & RB_INSELECT)
6764         HTMLlineproc1("</select>", h_env);
6765     if (obuf->flag & RB_TITLE)
6766         HTMLlineproc1("</title>", h_env);
6767
6768     /* for unbalanced table tag */
6769     if (obuf->table_level >= MAX_TABLE)
6770         obuf->table_level = MAX_TABLE - 1;
6771
6772     while (obuf->table_level >= 0) {
6773         table_mode[obuf->table_level].pre_mode
6774             &= ~(TBLM_SCRIPT | TBLM_STYLE | TBLM_PLAIN);
6775         HTMLlineproc1("</table>", h_env);
6776     }
6777 }
6778
6779 static void
6780 print_internal_information(struct html_feed_environ *henv)
6781 {
6782     int i;
6783     Str s;
6784     TextLineList *tl = newTextLineList();
6785
6786     s = Strnew_charp("<internal>");
6787     pushTextLine(tl, newTextLine(s, 0));
6788     if (henv->title) {
6789         s = Strnew_m_charp("<title_alt title=\"",
6790                            html_quote(henv->title), "\">", NULL);
6791         pushTextLine(tl, newTextLine(s, 0));
6792     }
6793 #if 0
6794     if (form_max >= 0) {
6795         FormList *fp;
6796         for (i = 0; i <= form_max; i++) {
6797             fp = forms[i];
6798             s = Sprintf("<form_int fid=\"%d\" action=\"%s\" method=\"%s\"",
6799                         i, html_quote(fp->action->ptr),
6800                         (fp->method == FORM_METHOD_POST) ? "post"
6801                         : ((fp->method ==
6802                             FORM_METHOD_INTERNAL) ? "internal" : "get"));
6803             if (fp->target)
6804                 Strcat(s, Sprintf(" target=\"%s\"", html_quote(fp->target)));
6805             if (fp->enctype == FORM_ENCTYPE_MULTIPART)
6806                 Strcat_charp(s, " enctype=\"multipart/form-data\"");
6807 #ifdef USE_M17N
6808             if (fp->charset)
6809                 Strcat(s, Sprintf(" accept-charset=\"%s\"",
6810                                   html_quote(fp->charset)));
6811 #endif
6812             Strcat_charp(s, ">");
6813             pushTextLine(tl, newTextLine(s, 0));
6814         }
6815     }
6816 #endif
6817 #ifdef MENU_SELECT
6818     if (n_select > 0) {
6819         FormSelectOptionItem *ip;
6820         for (i = 0; i < n_select; i++) {
6821             s = Sprintf("<select_int selectnumber=%d>", i);
6822             pushTextLine(tl, newTextLine(s, 0));
6823             for (ip = select_option[i].first; ip; ip = ip->next) {
6824                 s = Sprintf("<option_int value=\"%s\" label=\"%s\"%s>",
6825                             html_quote(ip->value ? ip->value->ptr :
6826                                        ip->label->ptr),
6827                             html_quote(ip->label->ptr),
6828                             ip->checked ? " selected" : "");
6829                 pushTextLine(tl, newTextLine(s, 0));
6830             }
6831             s = Strnew_charp("</select_int>");
6832             pushTextLine(tl, newTextLine(s, 0));
6833         }
6834     }
6835 #endif                          /* MENU_SELECT */
6836     if (n_textarea > 0) {
6837         for (i = 0; i < n_textarea; i++) {
6838             s = Sprintf("<textarea_int textareanumber=%d>", i);
6839             pushTextLine(tl, newTextLine(s, 0));
6840             s = Strnew_charp(html_quote(textarea_str[i]->ptr));
6841             Strcat_charp(s, "</textarea_int>");
6842             pushTextLine(tl, newTextLine(s, 0));
6843         }
6844     }
6845     s = Strnew_charp("</internal>");
6846     pushTextLine(tl, newTextLine(s, 0));
6847
6848     if (henv->buf)
6849         appendTextLineList(henv->buf, tl);
6850     else if (henv->f) {
6851         TextLineListItem *p;
6852         for (p = tl->first; p; p = p->next)
6853             fprintf(henv->f, "%s\n", Str_conv_to_halfdump(p->ptr->line)->ptr);
6854     }
6855 }
6856
6857 void
6858 loadHTMLstream(URLFile *f, Buffer *newBuf, FILE * src, int internal)
6859 {
6860     struct environment envs[MAX_ENV_LEVEL];
6861     clen_t linelen = 0;
6862     clen_t trbyte = 0;
6863     Str lineBuf2 = Strnew();
6864 #ifdef USE_M17N
6865     wc_ces charset = WC_CES_US_ASCII;
6866     wc_ces volatile doc_charset = DocumentCharset;
6867 #endif
6868     struct html_feed_environ htmlenv1;
6869     struct readbuffer obuf;
6870 #ifdef USE_IMAGE
6871     int volatile image_flag;
6872 #endif
6873     MySignalHandler(*volatile prevtrap) (SIGNAL_ARG) = NULL;
6874
6875 #ifdef USE_M17N
6876     if (fmInitialized && graph_ok()) {
6877         symbol_width = symbol_width0 = 1;
6878     }
6879     else {
6880         symbol_width0 = 0;
6881         get_symbol(DisplayCharset, &symbol_width0);
6882         symbol_width = WcOption.use_wide ? symbol_width0 : 1;
6883     }
6884 #else
6885     symbol_width = symbol_width0 = 1;
6886 #endif
6887
6888     cur_title = NULL;
6889     n_textarea = 0;
6890     cur_textarea = NULL;
6891     max_textarea = MAX_TEXTAREA;
6892     textarea_str = New_N(Str, max_textarea);
6893 #ifdef MENU_SELECT
6894     n_select = 0;
6895     max_select = MAX_SELECT;
6896     select_option = New_N(FormSelectOption, max_select);
6897 #endif                          /* MENU_SELECT */
6898     cur_select = NULL;
6899     form_sp = -1;
6900     form_max = -1;
6901     forms_size = 0;
6902     forms = NULL;
6903     cur_hseq = 1;
6904 #ifdef USE_IMAGE
6905     cur_iseq = 1;
6906     if (newBuf->image_flag)
6907         image_flag = newBuf->image_flag;
6908     else if (activeImage && displayImage && autoImage)
6909         image_flag = IMG_FLAG_AUTO;
6910     else
6911         image_flag = IMG_FLAG_SKIP;
6912     if (newBuf->currentURL.file)
6913         cur_baseURL = baseURL(newBuf);
6914 #endif
6915
6916     if (w3m_halfload) {
6917         newBuf->buffername = "---";
6918 #ifdef USE_M17N
6919         newBuf->document_charset = InnerCharset;
6920 #endif
6921         max_textarea = 0;
6922 #ifdef MENU_SELECT
6923         max_select = 0;
6924 #endif
6925         HTMLlineproc3(newBuf, f->stream);
6926         w3m_halfload = FALSE;
6927         return;
6928     }
6929
6930     init_henv(&htmlenv1, &obuf, envs, MAX_ENV_LEVEL, NULL, newBuf->width, 0);
6931
6932     if (w3m_halfdump)
6933         htmlenv1.f = stdout;
6934     else
6935         htmlenv1.buf = newTextLineList();
6936
6937     if (SETJMP(AbortLoading) != 0) {
6938         HTMLlineproc1("<br>Transfer Interrupted!<br>", &htmlenv1);
6939         goto phase2;
6940     }
6941     TRAP_ON;
6942
6943 #ifdef USE_M17N
6944     if (newBuf != NULL) {
6945         if (newBuf->bufferprop & BP_FRAME)
6946             charset = InnerCharset;
6947         else if (newBuf->document_charset)
6948             charset = doc_charset = newBuf->document_charset;
6949     }
6950     if (content_charset && UseContentCharset)
6951         doc_charset = content_charset;
6952     meta_charset = 0;
6953 #endif
6954 #if     0
6955     do_blankline(&htmlenv1, &obuf, 0, 0, htmlenv1.limit);
6956     obuf.flag = RB_IGNORE_P;
6957 #endif
6958     if (IStype(f->stream) != IST_ENCODED)
6959         f->stream = newEncodedStream(f->stream, f->encoding);
6960     while ((lineBuf2 = StrmyUFgets(f))->length) {
6961 #ifdef USE_NNTP
6962         if (f->scheme == SCM_NEWS && lineBuf2->ptr[0] == '.') {
6963             Strshrinkfirst(lineBuf2, 1);
6964             if (lineBuf2->ptr[0] == '\n' || lineBuf2->ptr[0] == '\r' ||
6965                 lineBuf2->ptr[0] == '\0') {
6966                 /*
6967                  * iseos(f->stream) = TRUE;
6968                  */
6969                 break;
6970             }
6971         }
6972 #endif                          /* USE_NNTP */
6973         if (src)
6974             Strfputs(lineBuf2, src);
6975         linelen += lineBuf2->length;
6976         if (w3m_dump & DUMP_EXTRA)
6977             printf("W3m-in-progress: %s\n", convert_size2(linelen, current_content_length, TRUE));
6978         if (w3m_dump & DUMP_SOURCE)
6979             continue;
6980         showProgress(&linelen, &trbyte);
6981         /*
6982          * if (frame_source)
6983          * continue;
6984          */
6985 #ifdef USE_M17N
6986         if (meta_charset) {     /* <META> */
6987             if (content_charset == 0 && UseContentCharset) {
6988                 doc_charset = meta_charset;
6989                 charset = WC_CES_US_ASCII;
6990             }
6991             meta_charset = 0;
6992         }
6993 #endif
6994         lineBuf2 = convertLine(f, lineBuf2, HTML_MODE, &charset, doc_charset);
6995 #if defined(USE_M17N) && defined(USE_IMAGE)
6996         cur_document_charset = charset;
6997 #endif
6998         HTMLlineproc0(lineBuf2->ptr, &htmlenv1, internal);
6999     }
7000     if (obuf.status != R_ST_NORMAL) {
7001         obuf.status = R_ST_EOL;
7002         HTMLlineproc0("\n", &htmlenv1, internal);
7003     }
7004     obuf.status = R_ST_NORMAL;
7005     completeHTMLstream(&htmlenv1, &obuf);
7006     flushline(&htmlenv1, &obuf, 0, 2, htmlenv1.limit);
7007     if (htmlenv1.title)
7008         newBuf->buffername = htmlenv1.title;
7009     if (w3m_halfdump) {
7010         TRAP_OFF;
7011         print_internal_information(&htmlenv1);
7012         return;
7013     }
7014     if (w3m_backend) {
7015         TRAP_OFF;
7016         print_internal_information(&htmlenv1);
7017         backend_halfdump_buf = htmlenv1.buf;
7018         return;
7019     }
7020   phase2:
7021     newBuf->trbyte = trbyte + linelen;
7022     TRAP_OFF;
7023 #ifdef USE_M17N
7024     if (!(newBuf->bufferprop & BP_FRAME))
7025         newBuf->document_charset = charset;
7026 #endif
7027 #ifdef USE_IMAGE
7028     newBuf->image_flag = image_flag;
7029 #endif
7030     HTMLlineproc2(newBuf, htmlenv1.buf);
7031 }
7032
7033 /* 
7034  * loadHTMLString: read string and make new buffer
7035  */
7036 Buffer *
7037 loadHTMLString(Str page)
7038 {
7039     URLFile f;
7040     MySignalHandler(*volatile prevtrap) (SIGNAL_ARG) = NULL;
7041     Buffer *newBuf;
7042
7043     newBuf = newBuffer(INIT_BUFFER_WIDTH);
7044     if (SETJMP(AbortLoading) != 0) {
7045         TRAP_OFF;
7046         discardBuffer(newBuf);
7047         return NULL;
7048     }
7049     TRAP_ON;
7050
7051     init_stream(&f, SCM_LOCAL, newStrStream(page));
7052
7053 #ifdef USE_M17N
7054     newBuf->document_charset = InnerCharset;
7055 #endif
7056     loadHTMLstream(&f, newBuf, NULL, TRUE);
7057 #ifdef USE_M17N
7058     newBuf->document_charset = WC_CES_US_ASCII;
7059 #endif
7060
7061     TRAP_OFF;
7062     newBuf->topLine = newBuf->firstLine;
7063     newBuf->lastLine = newBuf->currentLine;
7064     newBuf->currentLine = newBuf->firstLine;
7065     newBuf->type = "text/html";
7066     newBuf->real_type = newBuf->type;
7067     if (n_textarea)
7068         formResetBuffer(newBuf, newBuf->formitem);
7069     return newBuf;
7070 }
7071
7072 #ifdef USE_GOPHER
7073
7074 /* 
7075  * loadGopherDir: get gopher directory
7076  */
7077 Str
7078 loadGopherDir(URLFile *uf, ParsedURL *pu, wc_ces * charset)
7079 {
7080     Str volatile tmp;
7081     Str lbuf, name, file, host, port;
7082     char *volatile p, *volatile q;
7083     MySignalHandler(*volatile prevtrap) (SIGNAL_ARG) = NULL;
7084 #ifdef USE_M17N
7085     wc_ces doc_charset = DocumentCharset;
7086 #endif
7087
7088     tmp = parsedURL2Str(pu);
7089     p = html_quote(tmp->ptr);
7090     tmp =
7091         convertLine(NULL, Strnew_charp(file_unquote(tmp->ptr)), RAW_MODE,
7092                     charset, doc_charset);
7093     q = html_quote(tmp->ptr);
7094     tmp = Strnew_m_charp("<html>\n<head>\n<base href=\"", p, "\">\n<title>", q,
7095                          "</title>\n</head>\n<body>\n<h1>Index of ", q,
7096                          "</h1>\n<table>\n", NULL);
7097
7098     if (SETJMP(AbortLoading) != 0)
7099         goto gopher_end;
7100     TRAP_ON;
7101
7102     while (1) {
7103         if (lbuf = StrUFgets(uf), lbuf->length == 0)
7104             break;
7105         if (lbuf->ptr[0] == '.' &&
7106             (lbuf->ptr[1] == '\n' || lbuf->ptr[1] == '\r'))
7107             break;
7108         lbuf = convertLine(uf, lbuf, HTML_MODE, charset, doc_charset);
7109         p = lbuf->ptr;
7110         for (q = p; *q && *q != '\t'; q++) ;
7111         name = Strnew_charp_n(p, q - p);
7112         if (!*q)
7113             continue;
7114         p = q + 1;
7115         for (q = p; *q && *q != '\t'; q++) ;
7116         file = Strnew_charp_n(p, q - p);
7117         if (!*q)
7118             continue;
7119         p = q + 1;
7120         for (q = p; *q && *q != '\t'; q++) ;
7121         host = Strnew_charp_n(p, q - p);
7122         if (!*q)
7123             continue;
7124         p = q + 1;
7125         for (q = p; *q && *q != '\t' && *q != '\r' && *q != '\n'; q++) ;
7126         port = Strnew_charp_n(p, q - p);
7127
7128         switch (name->ptr[0]) {
7129         case '0':
7130             p = "[text file]";
7131             break;
7132         case '1':
7133             p = "[directory]";
7134             break;
7135         case 'm':
7136             p = "[message]";
7137             break;
7138         case 's':
7139             p = "[sound]";
7140             break;
7141         case 'g':
7142             p = "[gif]";
7143             break;
7144         case 'h':
7145             p = "[HTML]";
7146             break;
7147         default:
7148             p = "[unsupported]";
7149             break;
7150         }
7151         q = Strnew_m_charp("gopher://", host->ptr, ":", port->ptr,
7152                            "/", file->ptr, NULL)->ptr;
7153         Strcat_m_charp(tmp, "<a href=\"",
7154                        html_quote(url_quote_conv(q, *charset)),
7155                        "\">", p, html_quote(name->ptr + 1), "</a>\n", NULL);
7156     }
7157
7158   gopher_end:
7159     TRAP_OFF;
7160
7161     Strcat_charp(tmp, "</table>\n</body>\n</html>\n");
7162     return tmp;
7163 }
7164 #endif                          /* USE_GOPHER */
7165
7166 /* 
7167  * loadBuffer: read file and make new buffer
7168  */
7169 Buffer *
7170 loadBuffer(URLFile *uf, Buffer *volatile newBuf)
7171 {
7172     FILE *volatile src = NULL;
7173 #ifdef USE_M17N
7174     wc_ces charset = WC_CES_US_ASCII;
7175     wc_ces volatile doc_charset = DocumentCharset;
7176 #endif
7177     Str lineBuf2;
7178     volatile char pre_lbuf = '\0';
7179     int nlines;
7180     Str tmpf;
7181     clen_t linelen = 0, trbyte = 0;
7182     Lineprop *propBuffer = NULL;
7183 #ifdef USE_ANSI_COLOR
7184     Linecolor *colorBuffer = NULL;
7185 #endif
7186     MySignalHandler(*volatile prevtrap) (SIGNAL_ARG) = NULL;
7187
7188     if (newBuf == NULL)
7189         newBuf = newBuffer(INIT_BUFFER_WIDTH);
7190     lineBuf2 = Strnew();
7191
7192     if (SETJMP(AbortLoading) != 0) {
7193         goto _end;
7194     }
7195     TRAP_ON;
7196
7197     if (newBuf->sourcefile == NULL &&
7198         (uf->scheme != SCM_LOCAL || newBuf->mailcap)) {
7199         tmpf = tmpfname(TMPF_SRC, NULL);
7200         src = fopen(tmpf->ptr, "w");
7201         if (src)
7202             newBuf->sourcefile = tmpf->ptr;
7203     }
7204 #ifdef USE_M17N
7205     if (newBuf->document_charset)
7206         charset = doc_charset = newBuf->document_charset;
7207     if (content_charset && UseContentCharset)
7208         doc_charset = content_charset;
7209 #endif
7210
7211     nlines = 0;
7212     if (IStype(uf->stream) != IST_ENCODED)
7213         uf->stream = newEncodedStream(uf->stream, uf->encoding);
7214     while ((lineBuf2 = StrmyISgets(uf->stream))->length) {
7215 #ifdef USE_NNTP
7216         if (uf->scheme == SCM_NEWS && lineBuf2->ptr[0] == '.') {
7217             Strshrinkfirst(lineBuf2, 1);
7218             if (lineBuf2->ptr[0] == '\n' || lineBuf2->ptr[0] == '\r' ||
7219                 lineBuf2->ptr[0] == '\0') {
7220                 /*
7221                  * iseos(uf->stream) = TRUE;
7222                  */
7223                 break;
7224             }
7225         }
7226 #endif                          /* USE_NNTP */
7227         if (src)
7228             Strfputs(lineBuf2, src);
7229         linelen += lineBuf2->length;
7230         if (w3m_dump & DUMP_EXTRA)
7231             printf("W3m-in-progress: %s\n", convert_size2(linelen, current_content_length, TRUE));
7232         if (w3m_dump & DUMP_SOURCE)
7233             continue;
7234         showProgress(&linelen, &trbyte);
7235         if (frame_source)
7236             continue;
7237         lineBuf2 =
7238             convertLine(uf, lineBuf2, PAGER_MODE, &charset, doc_charset);
7239         if (squeezeBlankLine) {
7240             if (lineBuf2->ptr[0] == '\n' && pre_lbuf == '\n') {
7241                 ++nlines;
7242                 continue;
7243             }
7244             pre_lbuf = lineBuf2->ptr[0];
7245         }
7246         ++nlines;
7247         Strchop(lineBuf2);
7248         lineBuf2 = checkType(lineBuf2, &propBuffer, NULL);
7249         addnewline(newBuf, lineBuf2->ptr, propBuffer, colorBuffer,
7250                    lineBuf2->length, FOLD_BUFFER_WIDTH, nlines);
7251     }
7252   _end:
7253     TRAP_OFF;
7254     newBuf->topLine = newBuf->firstLine;
7255     newBuf->lastLine = newBuf->currentLine;
7256     newBuf->currentLine = newBuf->firstLine;
7257     newBuf->trbyte = trbyte + linelen;
7258 #ifdef USE_M17N
7259     newBuf->document_charset = charset;
7260 #endif
7261     if (src)
7262         fclose(src);
7263
7264     return newBuf;
7265 }
7266
7267 #ifdef USE_IMAGE
7268 Buffer *
7269 loadImageBuffer(URLFile *uf, Buffer *newBuf)
7270 {
7271     Image image;
7272     ImageCache *cache;
7273     Str tmp, tmpf;
7274     FILE *src = NULL;
7275     URLFile f;
7276     MySignalHandler(*volatile prevtrap) (SIGNAL_ARG) = NULL;
7277     struct stat st;
7278
7279     loadImage(newBuf, IMG_FLAG_STOP);
7280     image.url = uf->url;
7281     image.ext = uf->ext;
7282     image.width = -1;
7283     image.height = -1;
7284     image.cache = NULL;
7285     cache = getImage(&image, cur_baseURL, IMG_FLAG_AUTO);
7286     if (!cur_baseURL->is_nocache && cache->loaded & IMG_FLAG_LOADED &&
7287         !stat(cache->file, &st))
7288         goto image_buffer;
7289
7290     TRAP_ON;
7291     if (IStype(uf->stream) != IST_ENCODED)
7292         uf->stream = newEncodedStream(uf->stream, uf->encoding);
7293     if (save2tmp(*uf, cache->file) < 0) {
7294         UFclose(uf);
7295         TRAP_OFF;
7296         return NULL;
7297     }
7298     UFclose(uf);
7299     TRAP_OFF;
7300
7301     cache->loaded = IMG_FLAG_LOADED;
7302     cache->index = 0;
7303
7304   image_buffer:
7305     if (newBuf == NULL)
7306         newBuf = newBuffer(INIT_BUFFER_WIDTH);
7307     cache->loaded |= IMG_FLAG_DONT_REMOVE;
7308     if (newBuf->sourcefile == NULL && uf->scheme != SCM_LOCAL)
7309         newBuf->sourcefile = cache->file;
7310
7311     tmp = Sprintf("<img src=\"%s\"><br><br>", html_quote(image.url));
7312     tmpf = tmpfname(TMPF_SRC, ".html");
7313     src = fopen(tmpf->ptr, "w");
7314     newBuf->mailcap_source = tmpf->ptr;
7315
7316     init_stream(&f, SCM_LOCAL, newStrStream(tmp));
7317     loadHTMLstream(&f, newBuf, src, TRUE);
7318     if (src)
7319         fclose(src);
7320
7321     newBuf->topLine = newBuf->firstLine;
7322     newBuf->lastLine = newBuf->currentLine;
7323     newBuf->currentLine = newBuf->firstLine;
7324     newBuf->image_flag = IMG_FLAG_AUTO;
7325     return newBuf;
7326 }
7327 #endif
7328
7329 static Str
7330 conv_symbol(Line *l)
7331 {
7332     Str tmp = NULL;
7333     char *p = l->lineBuf, *ep = p + l->len;
7334     Lineprop *pr = l->propBuf;
7335 #ifdef USE_M17N
7336     int w;
7337     char **symbol = NULL;
7338 #else
7339     char **symbol = get_symbol();
7340 #endif
7341
7342     for (; p < ep; p++, pr++) {
7343         if (*pr & PC_SYMBOL) {
7344 #ifdef USE_M17N
7345             char c = ((char)wtf_get_code((wc_uchar *) p) & 0x7f) - SYMBOL_BASE;
7346             int len = get_mclen(p);
7347 #else
7348             char c = *p - SYMBOL_BASE;
7349 #endif
7350             if (tmp == NULL) {
7351                 tmp = Strnew_size(l->len);
7352                 Strcopy_charp_n(tmp, l->lineBuf, p - l->lineBuf);
7353 #ifdef USE_M17N
7354                 w = (*pr & PC_KANJI) ? 2 : 1;
7355                 symbol = get_symbol(DisplayCharset, &w);
7356 #endif
7357             }
7358             Strcat_charp(tmp, symbol[(int)c]);
7359 #ifdef USE_M17N
7360             p += len - 1;
7361             pr += len - 1;
7362 #endif
7363         }
7364         else if (tmp != NULL)
7365             Strcat_char(tmp, *p);
7366     }
7367     if (tmp)
7368         return tmp;
7369     else
7370         return Strnew_charp_n(l->lineBuf, l->len);
7371 }
7372
7373 /* 
7374  * saveBuffer: write buffer to file
7375  */
7376 static void
7377 _saveBuffer(Buffer *buf, Line *l, FILE * f, int cont)
7378 {
7379     Str tmp;
7380     int is_html = FALSE;
7381 #ifdef USE_M17N
7382     int set_charset = !DisplayCharset;
7383     wc_ces charset = DisplayCharset ? DisplayCharset : WC_CES_US_ASCII;
7384 #endif
7385
7386     if (buf->type && !strcasecmp(buf->type, "text/html"))
7387         is_html = TRUE;
7388
7389   pager_next:
7390     for (; l != NULL; l = l->next) {
7391         if (is_html)
7392             tmp = conv_symbol(l);
7393         else
7394             tmp = Strnew_charp_n(l->lineBuf, l->len);
7395         tmp = wc_Str_conv(tmp, InnerCharset, charset);
7396         Strfputs(tmp, f);
7397         if (Strlastchar(tmp) != '\n' && !(cont && l->next && l->next->bpos))
7398             putc('\n', f);
7399     }
7400     if (buf->pagerSource && !(buf->bufferprop & BP_CLOSE)) {
7401         l = getNextPage(buf, PagerMax);
7402 #ifdef USE_M17N
7403         if (set_charset)
7404             charset = buf->document_charset;
7405 #endif
7406         goto pager_next;
7407     }
7408 }
7409
7410 void
7411 saveBuffer(Buffer *buf, FILE * f, int cont)
7412 {
7413     _saveBuffer(buf, buf->firstLine, f, cont);
7414 }
7415
7416 void
7417 saveBufferBody(Buffer *buf, FILE * f, int cont)
7418 {
7419     Line *l = buf->firstLine;
7420
7421     while (l != NULL && l->real_linenumber == 0)
7422         l = l->next;
7423     _saveBuffer(buf, l, f, cont);
7424 }
7425
7426 static Buffer *
7427 loadcmdout(char *cmd,
7428            Buffer *(*loadproc) (URLFile *, Buffer *), Buffer *defaultbuf)
7429 {
7430     FILE *f, *popen(const char *, const char *);
7431     Buffer *buf;
7432     URLFile uf;
7433
7434     if (cmd == NULL || *cmd == '\0')
7435         return NULL;
7436     f = popen(cmd, "r");
7437     if (f == NULL)
7438         return NULL;
7439     init_stream(&uf, SCM_UNKNOWN, newFileStream(f, (void (*)())pclose));
7440     buf = loadproc(&uf, defaultbuf);
7441     UFclose(&uf);
7442     return buf;
7443 }
7444
7445 /* 
7446  * getshell: execute shell command and get the result into a buffer
7447  */
7448 Buffer *
7449 getshell(char *cmd)
7450 {
7451     Buffer *buf;
7452
7453     buf = loadcmdout(cmd, loadBuffer, NULL);
7454     if (buf == NULL)
7455         return NULL;
7456     buf->filename = cmd;
7457     buf->buffername = Sprintf("%s %s", SHELLBUFFERNAME,
7458                               conv_from_system(cmd))->ptr;
7459     return buf;
7460 }
7461
7462 /* 
7463  * getpipe: execute shell command and connect pipe to the buffer
7464  */
7465 Buffer *
7466 getpipe(char *cmd)
7467 {
7468     FILE *f, *popen(const char *, const char *);
7469     Buffer *buf;
7470
7471     if (cmd == NULL || *cmd == '\0')
7472         return NULL;
7473     f = popen(cmd, "r");
7474     if (f == NULL)
7475         return NULL;
7476     buf = newBuffer(INIT_BUFFER_WIDTH);
7477     buf->pagerSource = newFileStream(f, (void (*)())pclose);
7478     buf->filename = cmd;
7479     buf->buffername = Sprintf("%s %s", PIPEBUFFERNAME,
7480                               conv_from_system(cmd))->ptr;
7481     buf->bufferprop |= BP_PIPE;
7482 #ifdef USE_M17N
7483     buf->document_charset = WC_CES_US_ASCII;
7484 #endif
7485     return buf;
7486 }
7487
7488 /* 
7489  * Open pager buffer
7490  */
7491 Buffer *
7492 openPagerBuffer(InputStream stream, Buffer *buf)
7493 {
7494
7495     if (buf == NULL)
7496         buf = newBuffer(INIT_BUFFER_WIDTH);
7497     buf->pagerSource = stream;
7498     buf->buffername = getenv("MAN_PN");
7499     if (buf->buffername == NULL)
7500         buf->buffername = PIPEBUFFERNAME;
7501     else
7502         buf->buffername = conv_from_system(buf->buffername);
7503     buf->bufferprop |= BP_PIPE;
7504 #ifdef USE_M17N
7505     if (content_charset && UseContentCharset)
7506         buf->document_charset = content_charset;
7507     else
7508         buf->document_charset = WC_CES_US_ASCII;
7509 #endif
7510     buf->currentLine = buf->firstLine;
7511
7512     return buf;
7513 }
7514
7515 Buffer *
7516 openGeneralPagerBuffer(InputStream stream)
7517 {
7518     Buffer *buf;
7519     char *t = "text/plain";
7520     Buffer *t_buf = NULL;
7521     URLFile uf;
7522
7523     init_stream(&uf, SCM_UNKNOWN, stream);
7524
7525 #ifdef USE_M17N
7526     content_charset = 0;
7527 #endif
7528     if (SearchHeader) {
7529         t_buf = newBuffer(INIT_BUFFER_WIDTH);
7530         readHeader(&uf, t_buf, TRUE, NULL);
7531         t = checkContentType(t_buf);
7532         if (t == NULL)
7533             t = "text/plain";
7534         if (t_buf) {
7535             t_buf->topLine = t_buf->firstLine;
7536             t_buf->currentLine = t_buf->lastLine;
7537         }
7538         SearchHeader = FALSE;
7539     }
7540     else if (DefaultType) {
7541         t = DefaultType;
7542         DefaultType = NULL;
7543     }
7544     if (!strcasecmp(t, "text/html")) {
7545         buf = loadHTMLBuffer(&uf, t_buf);
7546         buf->type = "text/html";
7547     }
7548     else if (is_plain_text_type(t)) {
7549         if (IStype(stream) != IST_ENCODED)
7550             stream = newEncodedStream(stream, uf.encoding);
7551         buf = openPagerBuffer(stream, t_buf);
7552         buf->type = "text/plain";
7553     }
7554 #ifdef USE_IMAGE
7555     else if (activeImage && displayImage && !useExtImageViewer &&
7556              !(w3m_dump & ~DUMP_FRAME) && !strncasecmp(t, "image/", 6)) {
7557         cur_baseURL = New(ParsedURL);
7558         parseURL("-", cur_baseURL, NULL);
7559         buf = loadImageBuffer(&uf, t_buf);
7560         buf->type = "text/html";
7561     }
7562 #endif
7563     else {
7564         if (doExternal(uf, "-", t, &buf, t_buf)) {
7565             UFclose(&uf);
7566             if (buf == NULL || buf == NO_BUFFER)
7567                 return buf;
7568         }
7569         else {                  /* unknown type is regarded as text/plain */
7570             if (IStype(stream) != IST_ENCODED)
7571                 stream = newEncodedStream(stream, uf.encoding);
7572             buf = openPagerBuffer(stream, t_buf);
7573             buf->type = "text/plain";
7574         }
7575     }
7576     buf->real_type = t;
7577     buf->currentURL.scheme = SCM_LOCAL;
7578     buf->currentURL.file = "-";
7579     return buf;
7580 }
7581
7582 Line *
7583 getNextPage(Buffer *buf, int plen)
7584 {
7585     Line *volatile top = buf->topLine, *volatile last = buf->lastLine,
7586         *volatile cur = buf->currentLine;
7587     int i;
7588     int volatile nlines = 0;
7589     clen_t linelen = 0, trbyte = buf->trbyte;
7590     Str lineBuf2;
7591     char volatile pre_lbuf = '\0';
7592     URLFile uf;
7593 #ifdef USE_M17N
7594     wc_ces charset;
7595     wc_ces volatile doc_charset = DocumentCharset;
7596     wc_uint8 old_auto_detect = WcOption.auto_detect;
7597 #endif
7598     int volatile squeeze_flag = FALSE;
7599     Lineprop *propBuffer = NULL;
7600
7601 #ifdef USE_ANSI_COLOR
7602     Linecolor *colorBuffer = NULL;
7603 #endif
7604     MySignalHandler(*volatile prevtrap) (SIGNAL_ARG) = NULL;
7605
7606     if (buf->pagerSource == NULL)
7607         return NULL;
7608
7609     if (last != NULL) {
7610         nlines = last->real_linenumber;
7611         pre_lbuf = *(last->lineBuf);
7612         if (pre_lbuf == '\0')
7613             pre_lbuf = '\n';
7614         buf->currentLine = last;
7615     }
7616
7617 #ifdef USE_M17N
7618     charset = buf->document_charset;
7619     if (buf->document_charset != WC_CES_US_ASCII)
7620         doc_charset = buf->document_charset;
7621     else if (UseContentCharset) {
7622         content_charset = 0;
7623         checkContentType(buf);
7624         if (content_charset)
7625             doc_charset = content_charset;
7626     }
7627     WcOption.auto_detect = buf->auto_detect;
7628 #endif
7629
7630     if (SETJMP(AbortLoading) != 0) {
7631         goto pager_end;
7632     }
7633     TRAP_ON;
7634
7635     init_stream(&uf, SCM_UNKNOWN, NULL);
7636     for (i = 0; i < plen; i++) {
7637         lineBuf2 = StrmyISgets(buf->pagerSource);
7638         if (lineBuf2->length == 0) {
7639             /* Assume that `cmd == buf->filename' */
7640             if (buf->filename)
7641                 buf->buffername = Sprintf("%s %s",
7642                                           CPIPEBUFFERNAME,
7643                                           conv_from_system(buf->filename))->
7644                     ptr;
7645             else if (getenv("MAN_PN") == NULL)
7646                 buf->buffername = CPIPEBUFFERNAME;
7647             buf->bufferprop |= BP_CLOSE;
7648             break;
7649         }
7650         linelen += lineBuf2->length;
7651         showProgress(&linelen, &trbyte);
7652         lineBuf2 =
7653             convertLine(&uf, lineBuf2, PAGER_MODE, &charset, doc_charset);
7654         if (squeezeBlankLine) {
7655             squeeze_flag = FALSE;
7656             if (lineBuf2->ptr[0] == '\n' && pre_lbuf == '\n') {
7657                 ++nlines;
7658                 --i;
7659                 squeeze_flag = TRUE;
7660                 continue;
7661             }
7662             pre_lbuf = lineBuf2->ptr[0];
7663         }
7664         ++nlines;
7665         Strchop(lineBuf2);
7666         lineBuf2 = checkType(lineBuf2, &propBuffer, &colorBuffer);
7667         addnewline(buf, lineBuf2->ptr, propBuffer, colorBuffer,
7668                    lineBuf2->length, FOLD_BUFFER_WIDTH, nlines);
7669         if (!top) {
7670             top = buf->firstLine;
7671             cur = top;
7672         }
7673         if (buf->lastLine->real_linenumber - buf->firstLine->real_linenumber
7674             >= PagerMax) {
7675             Line *l = buf->firstLine;
7676             do {
7677                 if (top == l)
7678                     top = l->next;
7679                 if (cur == l)
7680                     cur = l->next;
7681                 if (last == l)
7682                     last = NULL;
7683                 l = l->next;
7684             } while (l && l->bpos);
7685             buf->firstLine = l;
7686             buf->firstLine->prev = NULL;
7687         }
7688     }
7689   pager_end:
7690     TRAP_OFF;
7691
7692     buf->trbyte = trbyte + linelen;
7693 #ifdef USE_M17N
7694     buf->document_charset = charset;
7695     WcOption.auto_detect = old_auto_detect;
7696 #endif
7697     buf->topLine = top;
7698     buf->currentLine = cur;
7699     if (!last)
7700         last = buf->firstLine;
7701     else if (last && (last->next || !squeeze_flag))
7702         last = last->next;
7703     return last;
7704 }
7705
7706 int
7707 save2tmp(URLFile uf, char *tmpf)
7708 {
7709     FILE *ff;
7710     int check;
7711     clen_t linelen = 0, trbyte = 0;
7712     MySignalHandler(*volatile prevtrap) (SIGNAL_ARG) = NULL;
7713     static JMP_BUF env_bak;
7714
7715     ff = fopen(tmpf, "wb");
7716     if (ff == NULL) {
7717         /* fclose(f); */
7718         return -1;
7719     }
7720     bcopy(AbortLoading, env_bak, sizeof(JMP_BUF));
7721     if (SETJMP(AbortLoading) != 0) {
7722         goto _end;
7723     }
7724     TRAP_ON;
7725     check = 0;
7726 #ifdef USE_NNTP
7727     if (uf.scheme == SCM_NEWS) {
7728         char c;
7729         while (c = UFgetc(&uf), !iseos(uf.stream)) {
7730             if (c == '\n') {
7731                 if (check == 0)
7732                     check++;
7733                 else if (check == 3)
7734                     break;
7735             }
7736             else if (c == '.' && check == 1)
7737                 check++;
7738             else if (c == '\r' && check == 2)
7739                 check++;
7740             else
7741                 check = 0;
7742             putc(c, ff);
7743             linelen += sizeof(c);
7744             showProgress(&linelen, &trbyte);
7745         }
7746     }
7747     else
7748 #endif                          /* USE_NNTP */
7749     {
7750         Str buf = Strnew_size(SAVE_BUF_SIZE);
7751         while (UFread(&uf, buf, SAVE_BUF_SIZE)) {
7752             Strfputs(buf, ff);
7753             linelen += buf->length;
7754             showProgress(&linelen, &trbyte);
7755         }
7756     }
7757   _end:
7758     bcopy(env_bak, AbortLoading, sizeof(JMP_BUF));
7759     TRAP_OFF;
7760     fclose(ff);
7761     current_content_length = 0;
7762     return 0;
7763 }
7764
7765 int
7766 doExternal(URLFile uf, char *path, char *type, Buffer **bufp,
7767            Buffer *defaultbuf)
7768 {
7769     Str tmpf, command;
7770     struct mailcap *mcap;
7771     int mc_stat;
7772     Buffer *buf = NULL;
7773     char *header, *src = NULL, *ext = uf.ext;
7774
7775     if (!(mcap = searchExtViewer(type)))
7776         return 0;
7777
7778     if (mcap->nametemplate) {
7779         tmpf = unquote_mailcap(mcap->nametemplate, NULL, "", NULL, NULL);
7780         if (tmpf->ptr[0] == '.')
7781             ext = tmpf->ptr;
7782     }
7783     tmpf = tmpfname(TMPF_DFL, (ext && *ext) ? ext : NULL);
7784
7785     if (IStype(uf.stream) != IST_ENCODED)
7786         uf.stream = newEncodedStream(uf.stream, uf.encoding);
7787     header = checkHeader(defaultbuf, "Content-Type:");
7788     if (header)
7789         header = conv_to_system(header);
7790     command = unquote_mailcap(mcap->viewer, type, tmpf->ptr, header, &mc_stat);
7791 #ifndef __EMX__
7792     if (!(mc_stat & MCSTAT_REPNAME)) {
7793         Str tmp = Sprintf("(%s) < %s", command->ptr, shell_quote(tmpf->ptr));
7794         command = tmp;
7795     }
7796 #endif
7797
7798 #ifdef HAVE_SETPGRP
7799     if (!(mcap->flags & (MAILCAP_HTMLOUTPUT | MAILCAP_COPIOUSOUTPUT)) &&
7800         !(mcap->flags & MAILCAP_NEEDSTERMINAL) && BackgroundExtViewer) {
7801         flush_tty();
7802         if (!fork()) {
7803             setup_child(FALSE, 0, UFfileno(&uf));
7804             if (save2tmp(uf, tmpf->ptr) < 0)
7805                 exit(1);
7806             UFclose(&uf);
7807             myExec(command->ptr);
7808         }
7809         *bufp = NO_BUFFER;
7810         return 1;
7811     }
7812     else
7813 #endif
7814     {
7815         if (save2tmp(uf, tmpf->ptr) < 0) {
7816             *bufp = NULL;
7817             return 1;
7818         }
7819     }
7820     if (mcap->flags & (MAILCAP_HTMLOUTPUT | MAILCAP_COPIOUSOUTPUT)) {
7821         if (defaultbuf == NULL)
7822             defaultbuf = newBuffer(INIT_BUFFER_WIDTH);
7823         if (defaultbuf->sourcefile)
7824             src = defaultbuf->sourcefile;
7825         else
7826             src = tmpf->ptr;
7827         defaultbuf->sourcefile = NULL;
7828         defaultbuf->mailcap = mcap;
7829     }
7830     if (mcap->flags & MAILCAP_HTMLOUTPUT) {
7831         buf = loadcmdout(command->ptr, loadHTMLBuffer, defaultbuf);
7832         if (buf && buf != NO_BUFFER) {
7833             buf->type = "text/html";
7834             buf->mailcap_source = buf->sourcefile;
7835             buf->sourcefile = src;
7836         }
7837     }
7838     else if (mcap->flags & MAILCAP_COPIOUSOUTPUT) {
7839         buf = loadcmdout(command->ptr, loadBuffer, defaultbuf);
7840         if (buf && buf != NO_BUFFER) {
7841             buf->type = "text/plain";
7842             buf->mailcap_source = buf->sourcefile;
7843             buf->sourcefile = src;
7844         }
7845     }
7846     else {
7847         if (mcap->flags & MAILCAP_NEEDSTERMINAL || !BackgroundExtViewer) {
7848             fmTerm();
7849             mySystem(command->ptr, 0);
7850             fmInit();
7851             if (CurrentTab && Currentbuf)
7852                 displayBuffer(Currentbuf, B_FORCE_REDRAW);
7853         }
7854         else {
7855             mySystem(command->ptr, 1);
7856         }
7857         buf = NO_BUFFER;
7858     }
7859     if (buf && buf != NO_BUFFER) {
7860         buf->filename = path;
7861         if (buf->buffername == NULL || buf->buffername[0] == '\0')
7862             buf->buffername = conv_from_system(lastFileName(path));
7863         buf->edit = mcap->edit;
7864         buf->mailcap = mcap;
7865     }
7866     *bufp = buf;
7867     return 1;
7868 }
7869
7870 static int
7871 _MoveFile(char *path1, char *path2)
7872 {
7873     InputStream f1;
7874     FILE *f2;
7875     int is_pipe;
7876     clen_t linelen = 0, trbyte = 0;
7877     Str buf;
7878
7879     f1 = openIS(path1);
7880     if (f1 == NULL)
7881         return -1;
7882     if (*path2 == '|' && PermitSaveToPipe) {
7883         is_pipe = TRUE;
7884         f2 = popen(path2 + 1, "w");
7885     }
7886     else {
7887         is_pipe = FALSE;
7888         f2 = fopen(path2, "wb");
7889     }
7890     if (f2 == NULL) {
7891         ISclose(f1);
7892         return -1;
7893     }
7894     current_content_length = 0;
7895     buf = Strnew_size(SAVE_BUF_SIZE);
7896     while (ISread(f1, buf, SAVE_BUF_SIZE)) {
7897         Strfputs(buf, f2);
7898         linelen += buf->length;
7899         showProgress(&linelen, &trbyte);
7900     }
7901     ISclose(f1);
7902     if (is_pipe)
7903         pclose(f2);
7904     else
7905         fclose(f2);
7906     return 0;
7907 }
7908
7909 int
7910 _doFileCopy(char *tmpf, char *defstr, int download)
7911 {
7912 #ifndef __MINGW32_VERSION
7913     Str msg;
7914     Str filen;
7915     char *p, *q = NULL;
7916     pid_t pid;
7917     char *lock;
7918 #if !(defined(HAVE_SYMLINK) && defined(HAVE_LSTAT))
7919     FILE *f;
7920 #endif
7921     struct stat st;
7922     clen_t size = 0;
7923     int is_pipe = FALSE;
7924
7925     if (fmInitialized) {
7926         p = searchKeyData();
7927         if (p == NULL || *p == '\0') {
7928             /* FIXME: gettextize? */
7929             q = inputLineHist("(Download)Save file to: ",
7930                               defstr, IN_COMMAND, SaveHist);
7931             if (q == NULL || *q == '\0')
7932                 return FALSE;
7933             p = conv_to_system(q);
7934         }
7935         if (*p == '|' && PermitSaveToPipe)
7936             is_pipe = TRUE;
7937         else {
7938             if (q) {
7939                 p = unescape_spaces(Strnew_charp(q))->ptr;
7940                 p = conv_to_system(q);
7941             }
7942             p = expandPath(p);
7943             if (checkOverWrite(p) < 0)
7944                 return -1;
7945         }
7946         if (checkCopyFile(tmpf, p) < 0) {
7947             /* FIXME: gettextize? */
7948             msg = Sprintf("Can't copy. %s and %s are identical.",
7949                           conv_from_system(tmpf), conv_from_system(p));
7950             disp_err_message(msg->ptr, FALSE);
7951             return -1;
7952         }
7953         if (!download) {
7954             if (_MoveFile(tmpf, p) < 0) {
7955                 /* FIXME: gettextize? */
7956                 msg = Sprintf("Can't save to %s", conv_from_system(p));
7957                 disp_err_message(msg->ptr, FALSE);
7958             }
7959             return -1;
7960         }
7961         lock = tmpfname(TMPF_DFL, ".lock")->ptr;
7962 #if defined(HAVE_SYMLINK) && defined(HAVE_LSTAT)
7963         symlink(p, lock);
7964 #else
7965         f = fopen(lock, "w");
7966         if (f)
7967             fclose(f);
7968 #endif
7969         flush_tty();
7970         pid = fork();
7971         if (!pid) {
7972             setup_child(FALSE, 0, -1);
7973             if (!_MoveFile(tmpf, p) && PreserveTimestamp && !is_pipe &&
7974                 !stat(tmpf, &st))
7975                 setModtime(p, st.st_mtime);
7976             unlink(lock);
7977             exit(0);
7978         }
7979         if (!stat(tmpf, &st))
7980             size = st.st_size;
7981         addDownloadList(pid, conv_from_system(tmpf), p, lock, size);
7982     }
7983     else {
7984         q = searchKeyData();
7985         if (q == NULL || *q == '\0') {
7986             /* FIXME: gettextize? */
7987             printf("(Download)Save file to: ");
7988             fflush(stdout);
7989             filen = Strfgets(stdin);
7990             if (filen->length == 0)
7991                 return -1;
7992             q = filen->ptr;
7993         }
7994         for (p = q + strlen(q) - 1; IS_SPACE(*p); p--) ;
7995         *(p + 1) = '\0';
7996         if (*q == '\0')
7997             return -1;
7998         p = q;
7999         if (*p == '|' && PermitSaveToPipe)
8000             is_pipe = TRUE;
8001         else {
8002             p = expandPath(p);
8003             if (checkOverWrite(p) < 0)
8004                 return -1;
8005         }
8006         if (checkCopyFile(tmpf, p) < 0) {
8007             /* FIXME: gettextize? */
8008             printf("Can't copy. %s and %s are identical.", tmpf, p);
8009             return -1;
8010         }
8011         if (_MoveFile(tmpf, p) < 0) {
8012             /* FIXME: gettextize? */
8013             printf("Can't save to %s\n", p);
8014             return -1;
8015         }
8016         if (PreserveTimestamp && !is_pipe && !stat(tmpf, &st))
8017             setModtime(p, st.st_mtime);
8018     }
8019 #endif /* __MINGW32_VERSION */
8020     return 0;
8021 }
8022
8023 int
8024 doFileMove(char *tmpf, char *defstr)
8025 {
8026     int ret = doFileCopy(tmpf, defstr);
8027     unlink(tmpf);
8028     return ret;
8029 }
8030
8031 int
8032 doFileSave(URLFile uf, char *defstr)
8033 {
8034 #ifndef __MINGW32_VERSION
8035     Str msg;
8036     Str filen;
8037     char *p, *q;
8038     pid_t pid;
8039     char *lock;
8040     char *tmpf = NULL; 
8041 #if !(defined(HAVE_SYMLINK) && defined(HAVE_LSTAT))
8042     FILE *f;
8043 #endif
8044
8045     if (fmInitialized) {
8046         p = searchKeyData();
8047         if (p == NULL || *p == '\0') {
8048             /* FIXME: gettextize? */
8049             p = inputLineHist("(Download)Save file to: ",
8050                               defstr, IN_FILENAME, SaveHist);
8051             if (p == NULL || *p == '\0')
8052                 return -1;
8053             p = conv_to_system(p);
8054         }
8055         if (checkOverWrite(p) < 0)
8056             return -1;
8057         if (checkSaveFile(uf.stream, p) < 0) {
8058             /* FIXME: gettextize? */
8059             msg = Sprintf("Can't save. Load file and %s are identical.",
8060                           conv_from_system(p));
8061             disp_err_message(msg->ptr, FALSE);
8062             return -1;
8063         }
8064         /*
8065          * if (save2tmp(uf, p) < 0) {
8066          * msg = Sprintf("Can't save to %s", conv_from_system(p));
8067          * disp_err_message(msg->ptr, FALSE);
8068          * }
8069          */
8070         lock = tmpfname(TMPF_DFL, ".lock")->ptr;
8071 #if defined(HAVE_SYMLINK) && defined(HAVE_LSTAT)
8072         symlink(p, lock);
8073 #else
8074         f = fopen(lock, "w");
8075         if (f)
8076             fclose(f);
8077 #endif
8078         flush_tty();
8079         pid = fork();
8080         if (!pid) {
8081             if ((uf.content_encoding != CMP_NOCOMPRESS) && AutoUncompress) {
8082                 uncompress_stream(&uf, &tmpf);
8083                 if (tmpf)
8084                     unlink(tmpf);
8085             }
8086             setup_child(FALSE, 0, UFfileno(&uf));
8087             if (!save2tmp(uf, p) && PreserveTimestamp && uf.modtime != -1)
8088                 setModtime(p, uf.modtime);
8089             UFclose(&uf);
8090             unlink(lock);
8091             exit(0);
8092         }
8093         addDownloadList(pid, uf.url, p, lock, current_content_length);
8094     }
8095     else {
8096         q = searchKeyData();
8097         if (q == NULL || *q == '\0') {
8098             /* FIXME: gettextize? */
8099             printf("(Download)Save file to: ");
8100             fflush(stdout);
8101             filen = Strfgets(stdin);
8102             if (filen->length == 0)
8103                 return -1;
8104             q = filen->ptr;
8105         }
8106         for (p = q + strlen(q) - 1; IS_SPACE(*p); p--) ;
8107         *(p + 1) = '\0';
8108         if (*q == '\0')
8109             return -1;
8110         p = expandPath(q);
8111         if (checkOverWrite(p) < 0)
8112             return -1;
8113         if (checkSaveFile(uf.stream, p) < 0) {
8114             /* FIXME: gettextize? */
8115             printf("Can't save. Load file and %s are identical.", p);
8116             return -1;
8117         }
8118         if (uf.content_encoding != CMP_NOCOMPRESS && AutoUncompress) {
8119             uncompress_stream(&uf, &tmpf);
8120             if (tmpf)
8121                 unlink(tmpf);
8122         }
8123         if (save2tmp(uf, p) < 0) {
8124             /* FIXME: gettextize? */
8125             printf("Can't save to %s\n", p);
8126             return -1;
8127         }
8128         if (PreserveTimestamp && uf.modtime != -1)
8129             setModtime(p, uf.modtime);
8130     }
8131 #endif /* __MINGW32_VERSION */
8132     return 0;
8133 }
8134
8135 int
8136 checkCopyFile(char *path1, char *path2)
8137 {
8138     struct stat st1, st2;
8139
8140     if (*path2 == '|' && PermitSaveToPipe)
8141         return 0;
8142     if ((stat(path1, &st1) == 0) && (stat(path2, &st2) == 0))
8143         if (st1.st_ino == st2.st_ino)
8144             return -1;
8145     return 0;
8146 }
8147
8148 int
8149 checkSaveFile(InputStream stream, char *path2)
8150 {
8151     struct stat st1, st2;
8152     int des = ISfileno(stream);
8153
8154     if (des < 0)
8155         return 0;
8156     if (*path2 == '|' && PermitSaveToPipe)
8157         return 0;
8158     if ((fstat(des, &st1) == 0) && (stat(path2, &st2) == 0))
8159         if (st1.st_ino == st2.st_ino)
8160             return -1;
8161     return 0;
8162 }
8163
8164 int
8165 checkOverWrite(char *path)
8166 {
8167     struct stat st;
8168     char *ans;
8169
8170     if (stat(path, &st) < 0)
8171         return 0;
8172     /* FIXME: gettextize? */
8173     ans = inputAnswer("File exists. Overwrite? (y/n)");
8174     if (ans && TOLOWER(*ans) == 'y')
8175         return 0;
8176     else
8177         return -1;
8178 }
8179
8180 char *
8181 inputAnswer(char *prompt)
8182 {
8183     char *ans;
8184
8185     if (QuietMessage)
8186         return "n";
8187     if (fmInitialized) {
8188         term_raw();
8189         ans = inputChar(prompt);
8190     }
8191     else {
8192         printf("%s", prompt);
8193         fflush(stdout);
8194         ans = Strfgets(stdin)->ptr;
8195     }
8196     return ans;
8197 }
8198
8199 static void
8200 uncompress_stream(URLFile *uf, char **src)
8201 {
8202 #ifndef __MINGW32_VERSION
8203     pid_t pid1;
8204     FILE *f1;
8205     char *expand_cmd = GUNZIP_CMDNAME;
8206     char *expand_name = GUNZIP_NAME;
8207     char *tmpf = NULL;
8208     char *ext = NULL;
8209     struct compression_decoder *d;
8210
8211     if (IStype(uf->stream) != IST_ENCODED) {
8212         uf->stream = newEncodedStream(uf->stream, uf->encoding);
8213         uf->encoding = ENC_7BIT;
8214     }
8215     for (d = compression_decoders; d->type != CMP_NOCOMPRESS; d++) {
8216         if (uf->compression == d->type) {
8217             if (d->auxbin_p)
8218                 expand_cmd = auxbinFile(d->cmd);
8219             else
8220                 expand_cmd = d->cmd;
8221             expand_name = d->name;
8222             ext = d->ext;
8223             break;
8224         }
8225     }
8226     uf->compression = CMP_NOCOMPRESS;
8227
8228     if (uf->scheme != SCM_LOCAL
8229 #ifdef USE_IMAGE
8230         && !image_source
8231 #endif
8232         ) {
8233         tmpf = tmpfname(TMPF_DFL, ext)->ptr;
8234     }
8235
8236     /* child1 -- stdout|f1=uf -> parent */
8237     pid1 = open_pipe_rw(&f1, NULL);
8238     if (pid1 < 0) {
8239         UFclose(uf);
8240         return;
8241     }
8242     if (pid1 == 0) {
8243         /* child */
8244         pid_t pid2;
8245         FILE *f2 = stdin;
8246
8247         /* uf -> child2 -- stdout|stdin -> child1 */
8248         pid2 = open_pipe_rw(&f2, NULL);
8249         if (pid2 < 0) {
8250             UFclose(uf);
8251             exit(1);
8252         }
8253         if (pid2 == 0) {
8254             /* child2 */
8255             Str buf = Strnew_size(SAVE_BUF_SIZE);
8256             FILE *f = NULL;
8257
8258             setup_child(TRUE, 2, UFfileno(uf));
8259             if (tmpf)
8260                 f = fopen(tmpf, "wb");
8261             while (UFread(uf, buf, SAVE_BUF_SIZE)) {
8262                 if (Strfputs(buf, stdout) < 0)
8263                     break;
8264                 if (f)
8265                     Strfputs(buf, f);
8266             }
8267             UFclose(uf);
8268             if (f)
8269                 fclose(f);
8270             exit(0);
8271         }
8272         /* child1 */
8273         dup2(1, 2);             /* stderr>&stdout */
8274         setup_child(TRUE, -1, -1);
8275         execlp(expand_cmd, expand_name, NULL);
8276         exit(1);
8277     }
8278     if (tmpf) {
8279         if (src)
8280             *src = tmpf;
8281         else
8282             uf->scheme = SCM_LOCAL;
8283     }
8284     UFhalfclose(uf);
8285     uf->stream = newFileStream(f1, (void (*)())fclose);
8286 #endif /* __MINGW32_VERSION */
8287 }
8288
8289 static FILE *
8290 lessopen_stream(char *path)
8291 {
8292     char *lessopen;
8293     FILE *fp;
8294
8295     lessopen = getenv("LESSOPEN");
8296     if (lessopen == NULL) {
8297         return NULL;
8298     }
8299     if (lessopen[0] == '\0') {
8300         return NULL;
8301     }
8302
8303     if (lessopen[0] == '|') {
8304         /* pipe mode */
8305         Str tmpf;
8306         int c;
8307
8308         ++lessopen;
8309         tmpf = Sprintf(lessopen, shell_quote(path));
8310         fp = popen(tmpf->ptr, "r");
8311         if (fp == NULL) {
8312             return NULL;
8313         }
8314         c = getc(fp);
8315         if (c == EOF) {
8316             fclose(fp);
8317             return NULL;
8318         }
8319         ungetc(c, fp);
8320     }
8321     else {
8322         /* filename mode */
8323         /* not supported m(__)m */
8324         fp = NULL;
8325     }
8326     return fp;
8327 }
8328
8329 #if 0
8330 void
8331 reloadBuffer(Buffer *buf)
8332 {
8333     URLFile uf;
8334
8335     if (buf->sourcefile == NULL || buf->pagerSource != NULL)
8336         return;
8337     init_stream(&uf, SCM_UNKNOWN, NULL);
8338     examineFile(buf->mailcap_source ? buf->mailcap_source : buf->sourcefile,
8339                 &uf);
8340     if (uf.stream == NULL)
8341         return;
8342     is_redisplay = TRUE;
8343     buf->allLine = 0;
8344     buf->href = NULL;
8345     buf->name = NULL;
8346     buf->img = NULL;
8347     buf->formitem = NULL;
8348     buf->linklist = NULL;
8349     buf->maplist = NULL;
8350     if (buf->hmarklist)
8351         buf->hmarklist->nmark = 0;
8352     if (buf->imarklist)
8353         buf->imarklist->nmark = 0;
8354     if (!strcasecmp(buf->type, "text/html"))
8355         loadHTMLBuffer(&uf, buf);
8356     else
8357         loadBuffer(&uf, buf);
8358     UFclose(&uf);
8359     is_redisplay = FALSE;
8360 }
8361 #endif
8362
8363 static char *
8364 guess_filename(char *file)
8365 {
8366     char *p = NULL, *s;
8367
8368     if (file != NULL)
8369         p = mybasename(file);
8370     if (p == NULL || *p == '\0')
8371         return DEF_SAVE_FILE;
8372     s = p;
8373     if (*p == '#')
8374         p++;
8375     while (*p != '\0') {
8376         if ((*p == '#' && *(p + 1) != '\0') || *p == '?') {
8377             *p = '\0';
8378             break;
8379         }
8380         p++;
8381     }
8382     return s;
8383 }
8384
8385 char *
8386 guess_save_name(Buffer *buf, char *path)
8387 {
8388     if (buf && buf->document_header) {
8389         Str name = NULL;
8390         char *p, *q;
8391         if ((p = checkHeader(buf, "Content-Disposition:")) != NULL &&
8392             (q = strcasestr(p, "filename")) != NULL &&
8393             (q == p || IS_SPACE(*(q - 1)) || *(q - 1) == ';') &&
8394             matchattr(q, "filename", 8, &name))
8395             path = name->ptr;
8396         else if ((p = checkHeader(buf, "Content-Type:")) != NULL &&
8397                  (q = strcasestr(p, "name")) != NULL &&
8398                  (q == p || IS_SPACE(*(q - 1)) || *(q - 1) == ';') &&
8399                  matchattr(q, "name", 4, &name))
8400             path = name->ptr;
8401     }
8402     return guess_filename(path);
8403 }
8404
8405 /* Local Variables:    */
8406 /* c-basic-offset: 4   */
8407 /* tab-width: 8        */
8408 /* End:                */