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