w3m

Unnamed repository; edit this file to name it for gitweb.
git clone https://logand.com/git/w3m.git/
Log | Files | Refs | README

file.c (203866B)


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