diff options
| author | Tatsuya Kinoshita <tats@debian.org> | 2013-04-08 13:10:49 +0000 | 
|---|---|---|
| committer | Tatsuya Kinoshita <tats@debian.org> | 2013-04-08 13:10:49 +0000 | 
| commit | 52265ea04897f1d362927e6607b3be8c4e8f5a47 (patch) | |
| tree | 1590a721f9adbb3ef31974adce9e4ec63a470a88 /debian | |
| parent | New patch 120_sgrmouse.patch to support SGR 1006 mouse reporting (diff) | |
| download | w3m-52265ea04897f1d362927e6607b3be8c4e8f5a47.tar.gz w3m-52265ea04897f1d362927e6607b3be8c4e8f5a47.zip | |
New patch 130_siteconf.patch to support the siteconf feature
Patch from [w3m-dev 04464] on 2012-06-27, provided by AIDA Shinra.
Diffstat (limited to 'debian')
| -rw-r--r-- | debian/patches/130_siteconf.patch | 2051 | ||||
| -rw-r--r-- | debian/patches/series | 1 | 
2 files changed, 2052 insertions, 0 deletions
| diff --git a/debian/patches/130_siteconf.patch b/debian/patches/130_siteconf.patch new file mode 100644 index 0000000..4b3c352 --- /dev/null +++ b/debian/patches/130_siteconf.patch @@ -0,0 +1,2051 @@ +Subject: New feature: siteconf +From: AIDA Shinra <shinra@j10n.org> +Date: Wed, 27 Jun 2012 20:43:46 +0900 +Origin: upstream, http://www.sic.med.tohoku.ac.jp/~satodai/w3m-dev/201206.month/4464.html + +Patch to support the siteconf feature, from [w3m-dev 04464] +on 2012-06-27, provided by AIDA Shinra. + +diff --git a/anchor.c b/anchor.c +index 27bbd56..39f221d 100644 +--- a/anchor.c ++++ b/anchor.c +@@ -200,10 +200,11 @@ _put_anchor_news(Buffer *buf, char *p1, char *p2, int line, int pos) + 	if (*(p2 - 1) == '>') + 	    p2--; +     } +-    tmp = wc_Str_conv_strict(Strnew_charp_n(p1, p2 - p1), InnerCharset, +-			     buf->document_charset); +-    tmp = Sprintf("news:%s", file_quote(tmp->ptr)); +-    return registerHref(buf, tmp->ptr, NULL, NO_REFERER, NULL, '\0', line, ++    tmp = Strnew_charp("news:"); ++    Strcat_charp_n(tmp, p1, p2 - p1); ++    return registerHref(buf, url_encode(tmp->ptr, baseURL(buf), ++					buf->document_charset), ++			NULL, NO_REFERER, NULL, '\0', line, + 			pos); + } + #endif				/* USE_NNTP */ +@@ -213,9 +214,10 @@ _put_anchor_all(Buffer *buf, char *p1, char *p2, int line, int pos) + { +     Str tmp; +  +-    tmp = wc_Str_conv_strict(Strnew_charp_n(p1, p2 - p1), InnerCharset, +-			     buf->document_charset); +-    return registerHref(buf, url_quote(tmp->ptr), NULL, NO_REFERER, NULL, ++    tmp = Strnew_charp_n(p1, p2 - p1); ++    return registerHref(buf, url_encode(tmp->ptr, baseURL(buf), ++					buf->document_charset), ++			NULL, NO_REFERER, NULL, + 			'\0', line, pos); + } +  +@@ -756,7 +758,7 @@ link_list_panel(Buffer *buf) + 		p = parsedURL2Str(&pu)->ptr; + 		u = html_quote(p); + 		if (DecodeURL) +-		    p = html_quote(url_unquote_conv(p, buf->document_charset)); ++		    p = html_quote(url_decode2(p, buf)); + 		else + 		    p = u; + 	    } +@@ -787,7 +789,7 @@ link_list_panel(Buffer *buf) + 	    p = parsedURL2Str(&pu)->ptr; + 	    u = html_quote(p); + 	    if (DecodeURL) +-		p = html_quote(url_unquote_conv(p, buf->document_charset)); ++		p = html_quote(url_decode2(p, buf)); + 	    else + 		p = u; + 	    t = getAnchorText(buf, al, a); +@@ -809,16 +811,13 @@ link_list_panel(Buffer *buf) + 	    p = parsedURL2Str(&pu)->ptr; + 	    u = html_quote(p); + 	    if (DecodeURL) +-		p = html_quote(url_unquote_conv(p, buf->document_charset)); ++		p = html_quote(url_decode2(p, buf)); + 	    else + 		p = u; + 	    if (a->title && *a->title) + 		t = html_quote(a->title); +-	    else if (DecodeURL) +-		t = html_quote(url_unquote_conv +-			       (a->url, buf->document_charset)); + 	    else +-		t = html_quote(a->url); ++		t = html_quote(url_decode2(a->url, buf)); + 	    Strcat_m_charp(tmp, "<li><a href=\"", u, "\">", t, "</a><br>", p, + 			   "\n", NULL); + 	    a = retrieveAnchor(buf->formitem, a->start.line, a->start.pos); +@@ -842,19 +841,13 @@ link_list_panel(Buffer *buf) + 		    p = parsedURL2Str(&pu)->ptr; + 		    u = html_quote(p); + 		    if (DecodeURL) +-			p = html_quote(url_unquote_conv(p, +-							buf-> +-							document_charset)); ++			p = html_quote(url_decode2(p, buf)); + 		    else + 			p = u; + 		    if (m->alt && *m->alt) + 			t = html_quote(m->alt); +-		    else if (DecodeURL) +-			t = html_quote(url_unquote_conv(m->url, +-							buf-> +-							document_charset)); + 		    else +-			t = html_quote(m->url); ++			t = html_quote(url_decode2(m->url, buf)); + 		    Strcat_m_charp(tmp, "<li><a href=\"", u, "\">", t, + 				   "</a><br>", p, "\n", NULL); + 		} +diff --git a/config.h.in b/config.h.in +index 2f41eed..59997b4 100644 +--- a/config.h.in ++++ b/config.h.in +@@ -25,6 +25,7 @@ +  + #define PASSWD_FILE	RC_DIR "/passwd" + #define PRE_FORM_FILE	RC_DIR "/pre_form" ++#define SITECONF_FILE	RC_DIR "/siteconf" + #define USER_MAILCAP	RC_DIR "/mailcap" + #define SYS_MAILCAP	CONF_DIR "/mailcap" + #define USER_MIMETYPES	"~/.mime.types" +diff --git a/display.c b/display.c +index e00eb0c..2fe1183 100644 +--- a/display.c ++++ b/display.c +@@ -257,7 +257,7 @@ make_lastline_link(Buffer *buf, char *title, char *url) +     parseURL2(url, &pu, baseURL(buf)); +     u = parsedURL2Str(&pu); +     if (DecodeURL) +-	u = Strnew_charp(url_unquote_conv(u->ptr, buf->document_charset)); ++	u = Strnew_charp(url_decode2(u->ptr, buf)); + #ifdef USE_M17N +     u = checkType(u, &pr, NULL); + #endif +diff --git a/doc-jp/README.siteconf b/doc-jp/README.siteconf +new file mode 100644 +index 0000000..58b51c7 +--- /dev/null ++++ b/doc-jp/README.siteconf +@@ -0,0 +1,60 @@ ++siteconf: サイト別カスタマイズ ++ ++siteconf は、 URL のパターンと、それに紐付けられた設定から成ります。 ++siteconf を使うと、サイト毎に文字コードを指定して "decode_url" ++の出力を改善したり、 Google のリダイレクタを迂回して性能や ++プライバシーを向上させたりすることができます。 ++ ++デフォルトでは siteconf は ~/.w3m/siteconf から読み込まれます。 ++ ++===== 構文 ===== ++ ++url <url>|/<re-url>/|m@<re-url>@i [exact] ++substitute_url "<destination-url>" ++url_charset <charset> ++no_referer_from on|off ++no_referer_to on|off ++ ++後ろの方に書かれたものが優先されます。 ++ ++===== 例 ===== ++ ++url "http://twitter.com/#!/" ++substitute_url "http://mobile.twitter.com/" ++ ++twitter.com をモバイルサイトに転送します。 ++ ++url "http://your.bookmark.net/" ++no_referer_from on ++ ++your.bookmark.net から張ったリンクを辿る際に、 HTTP referer を ++送らないようにします。 ++ ++url "http://www.google.com/url?" exact ++substitute_url "file:///cgi-bin/your-redirector.cgi?" ++ ++Google のリダイレクタを local CGI に転送します。 ++ ++url /^http:\/\/[a-z]*\.wikipedia\.org\// ++url_charset utf-8 ++ ++同時に "decode_url" オプションをオンにすると、 Wikipedia への ++リンクを UTF-8 としてデコードして表示します。 ++ ++===== 正規表現について ===== ++ ++次の正規表現はいずれも同じ意味を表します。 ++ ++/http:\/\/www\.example\.com\// ++m/http:\/\/www\.example\.com\// ++m@http://www\.example\.com/@ ++m!http://www\.example\.com/! ++ ++最後に 'i' 修飾子を付けると、大文字小文字を区別せずに照合を行います。 ++例えば、 m@^http://www\.example\.com/abc/@i は以下のいずれとも一致します。 ++ ++http://www.example.com/abc/ ++http://www.example.com/Abc/ ++http://www.example.com/ABC/ ++ ++ただし、ホスト名の部分は常に小文字に変換してから比較します。 +diff --git a/doc/README.siteconf b/doc/README.siteconf +new file mode 100644 +index 0000000..f173087 +--- /dev/null ++++ b/doc/README.siteconf +@@ -0,0 +1,60 @@ ++The siteconf: Site-specific preferences  ++ ++The siteconf consists of URL patterns and preferences associated to them. ++You can improve "decode_url" feature by giving charsets of URLs site by site, ++or bypass Google's redirector for performance and your privacy. ++ ++The siteconf is read from ~/.w3m/siteconf by default. ++ ++===== The syntax ===== ++ ++url <url>|/<re-url>/|m@<re-url>@i [exact] ++substitute_url "<destination-url>" ++url_charset <charset> ++no_referer_from on|off ++no_referer_to on|off ++ ++The last match wins. ++ ++===== Examples ===== ++ ++url "http://twitter.com/#!/" ++substitute_url "http://mobile.twitter.com/" ++ ++This forwards the twitter.com to its mobile site. ++ ++url "http://your.bookmark.net/" ++no_referer_from on ++ ++This prevents HTTP referers from being sent when you follow links ++at the your.bookmark.net. ++ ++url "http://www.google.com/url?" exact ++substitute_url "file:///cgi-bin/your-redirector.cgi?" ++ ++This forwards the Google's redirector to your local CGI. ++ ++url /^http:\/\/[a-z]*\.wikipedia\.org\// ++url_charset utf-8 ++ ++When combinated with "decode_url" option turned on, links to ++Wikipedia will be human-readable. ++ ++===== Regular expressions notes ===== ++ ++Following expressions are all equivalent: ++ ++/http:\/\/www\.example\.com\// ++m/http:\/\/www\.example\.com\// ++m@http://www\.example\.com/@ ++m!http://www\.example\.com/! ++ ++With a trailing 'i' modifier, you can specify a case-insensitive match. ++For example, m@^http://www\.example\.com/abc/@i matches to: ++ ++http://www.example.com/abc/ ++http://www.example.com/Abc/ ++http://www.example.com/ABC/ ++ ++Hostnames, however, are always converted to lowercases before compared. ++ +diff --git a/file.c b/file.c +index 567d41e..22b76d9 100644 +--- a/file.c ++++ b/file.c +@@ -47,11 +47,11 @@ static JMP_BUF AbortLoading; + static struct table *tables[MAX_TABLE]; + static struct table_mode table_mode[MAX_TABLE]; +  +-#ifdef USE_IMAGE ++#if defined(USE_M17N) || defined(USE_IMAGE) + static ParsedURL *cur_baseURL = NULL; +-#ifdef USE_M17N +-static char cur_document_charset; + #endif ++#ifdef USE_M17N ++static wc_ces cur_document_charset = 0; + #endif +  + static Str cur_title; +@@ -215,7 +215,6 @@ currentLn(Buffer *buf) +  + static Buffer * + loadSomething(URLFile *f, +-	      char *path, + 	      Buffer *(*loadproc) (URLFile *, Buffer *), Buffer *defaultbuf) + { +     Buffer *buf; +@@ -223,17 +222,23 @@ loadSomething(URLFile *f, +     if ((buf = loadproc(f, defaultbuf)) == NULL) + 	return NULL; +  +-    buf->filename = path; +     if (buf->buffername == NULL || buf->buffername[0] == '\0') { + 	buf->buffername = checkHeader(buf, "Subject:"); +-	if (buf->buffername == NULL) +-	    buf->buffername = conv_from_system(lastFileName(path)); ++	if (buf->buffername == NULL && buf->filename != NULL) ++	    buf->buffername = conv_from_system(lastFileName(buf->filename)); +     } +     if (buf->currentURL.scheme == SCM_UNKNOWN) + 	buf->currentURL.scheme = f->scheme; +-    buf->real_scheme = f->scheme; +     if (f->scheme == SCM_LOCAL && buf->sourcefile == NULL) +-	buf->sourcefile = path; ++	buf->sourcefile = buf->filename; ++    if (loadproc == loadHTMLBuffer ++#ifdef USE_IMAGE ++	|| loadproc == loadImageBuffer ++#endif ++       ) ++	buf->type = "text/html"; ++    else ++	buf->type = "text/plain"; +     return buf; + } +  +@@ -484,28 +489,6 @@ convertLine0(URLFile *uf, Str line, int mode) +     return line; + } +  +-/*  +- * loadFile: load file to buffer +- */ +-Buffer * +-loadFile(char *path) +-{ +-    Buffer *buf; +-    URLFile uf; +-    init_stream(&uf, SCM_LOCAL, NULL); +-    examineFile(path, &uf); +-    if (uf.stream == NULL) +-	return NULL; +-    buf = newBuffer(INIT_BUFFER_WIDTH); +-    current_content_length = 0; +-#ifdef USE_M17N +-    content_charset = 0; +-#endif +-    buf = loadSomething(&uf, path, loadBuffer, buf); +-    UFclose(&uf); +-    return buf; +-} +- + int + matchattr(char *p, char *attr, int len, Str *value) + { +@@ -1697,13 +1680,15 @@ getLinkNumberStr(int correction) + /*  +  * loadGeneralFile: load file to buffer +  */ ++#define DO_EXTERNAL ((Buffer *(*)(URLFile *, Buffer *))doExternal) + Buffer * + loadGeneralFile(char *path, ParsedURL *volatile current, char *referer, + 		int flag, FormList *volatile request) + { +     URLFile f, *volatile of = NULL; +     ParsedURL pu; +-    Buffer *b = NULL, *(*volatile proc)() = loadBuffer; ++    Buffer *b = NULL; ++    Buffer *(*volatile proc)(URLFile *, Buffer *) = loadBuffer; +     char *volatile tpath; +     char *volatile t = "text/plain", *p, *volatile real_type = NULL; +     Buffer *volatile t_buf = NULL; +@@ -1730,7 +1715,22 @@ loadGeneralFile(char *path, ParsedURL *volatile current, char *referer, +     add_auth_cookie_flag = 0; +  +     checkRedirection(NULL); ++ +   load_doc: ++    { ++	const char *sc_redirect; ++	parseURL2(tpath, &pu, current); ++	sc_redirect = query_SCONF_SUBSTITUTE_URL(&pu); ++	if (sc_redirect && *sc_redirect && checkRedirection(&pu)) { ++	    tpath = (char *)sc_redirect; ++	    request = NULL; ++	    add_auth_cookie_flag = 0; ++	    current = New(ParsedURL); ++	    *current = pu; ++	    status = HTST_NORMAL; ++	    goto load_doc; ++	} ++    } +     TRAP_OFF; +     url_option.referer = referer; +     url_option.flag = flag; +@@ -1863,7 +1863,7 @@ loadGeneralFile(char *path, ParsedURL *volatile current, char *referer, + 	    /* 302: Found */ + 	    /* 303: See Other */ + 	    /* 307: Temporary Redirect (HTTP/1.1) */ +-	    tpath = url_quote_conv(p, DocumentCharset); ++	    tpath = url_encode(p, NULL, 0); + 	    request = NULL; + 	    UFclose(&f); + 	    current = New(ParsedURL); +@@ -2022,7 +2022,7 @@ loadGeneralFile(char *path, ParsedURL *volatile current, char *referer, + 	if (f.is_cgi && (p = checkHeader(t_buf, "Location:")) != NULL && + 	    checkRedirection(&pu)) { + 	    /* document moved */ +-	    tpath = url_quote_conv(remove_space(p), DocumentCharset); ++	    tpath = url_encode(remove_space(p), NULL, 0); + 	    request = NULL; + 	    UFclose(&f); + 	    add_auth_cookie_flag = 0; +@@ -2123,10 +2123,6 @@ loadGeneralFile(char *path, ParsedURL *volatile current, char *referer, +     if (real_type == NULL) + 	real_type = t; +     proc = loadBuffer; +-#ifdef USE_IMAGE +-    cur_baseURL = New(ParsedURL); +-    copyParsedURL(cur_baseURL, &pu); +-#endif +  +     current_content_length = 0; +     if ((p = checkHeader(t_buf, "Content-Length:")) != NULL) +@@ -2197,18 +2193,8 @@ loadGeneralFile(char *path, ParsedURL *volatile current, char *referer, + #endif +     else if (w3m_backend) ; +     else if (!(w3m_dump & ~DUMP_FRAME) || is_dump_text_type(t)) { +-	if (!do_download && doExternal(f, +-				       pu.real_file ? pu.real_file : pu.file, +-				       t, &b, t_buf)) { +-	    if (b && b != NO_BUFFER) { +-		b->real_scheme = f.scheme; +-		b->real_type = real_type; +-		if (b->currentURL.host == NULL && b->currentURL.file == NULL) +-		    copyParsedURL(&b->currentURL, &pu); +-	    } +-	    UFclose(&f); +-	    TRAP_OFF; +-	    return b; ++	if (!do_download && searchExtViewer(t) != NULL) { ++	    proc = DO_EXTERNAL; + 	} + 	else { + 	    TRAP_OFF; +@@ -2232,36 +2218,30 @@ loadGeneralFile(char *path, ParsedURL *volatile current, char *referer, +     else if (w3m_dump & DUMP_FRAME) + 	return NULL; +  ++    if (t_buf == NULL) ++	t_buf = newBuffer(INIT_BUFFER_WIDTH); ++    copyParsedURL(&t_buf->currentURL, &pu); ++    t_buf->filename = pu.real_file ? pu.real_file : ++	pu.file ? conv_to_system(pu.file) : NULL; +     if (flag & RG_FRAME) { +-	if (t_buf == NULL) +-	    t_buf = newBuffer(INIT_BUFFER_WIDTH); + 	t_buf->bufferprop |= BP_FRAME; +     } + #ifdef USE_SSL +-    if (t_buf) +-	t_buf->ssl_certificate = f.ssl_certificate; ++    t_buf->ssl_certificate = f.ssl_certificate; + #endif +     frame_source = flag & RG_FRAME_SRC; +-    b = loadSomething(&f, pu.real_file ? pu.real_file : pu.file, proc, t_buf); ++    if (proc == DO_EXTERNAL) { ++	b = doExternal(f, t, t_buf); ++    } else { ++	b = loadSomething(&f, proc, t_buf); ++    } +     UFclose(&f); +     frame_source = 0; +-    if (b) { ++    if (b && b != NO_BUFFER) { + 	b->real_scheme = f.scheme; + 	b->real_type = real_type; +-	if (b->currentURL.host == NULL && b->currentURL.file == NULL) +-	    copyParsedURL(&b->currentURL, &pu); +-	if (is_html_type(t)) +-	    b->type = "text/html"; +-	else if (w3m_backend) { +-	    Str s = Strnew_charp(t); +-	    b->type = s->ptr; +-	} +-#ifdef USE_IMAGE +-	else if (proc == loadImageBuffer) +-	    b->type = "text/html"; +-#endif +-	else +-	    b->type = "text/plain"; ++	if (w3m_backend) ++	    b->type = allocStr(t, -1); + 	if (pu.label) { + 	    if (proc == loadHTMLBuffer) { + 		Anchor *a; +@@ -3228,7 +3208,7 @@ process_img(struct parsed_tag *tag, int width) +  +     if (!parsedtag_get_value(tag, ATTR_SRC, &p)) + 	return tmp; +-    p = remove_space(p); ++    p = url_encode(remove_space(p), cur_baseURL, cur_document_charset); +     q = NULL; +     parsedtag_get_value(tag, ATTR_ALT, &q); +     if (!pseudoInlines && (q == NULL || (*q == '\0' && ignore_null_img_alt))) +@@ -3322,12 +3302,7 @@ process_img(struct parsed_tag *tag, int width) + 	    Image image; + 	    ParsedURL u; +  +-#ifdef USE_M17N +-	    parseURL2(wc_conv(p, InnerCharset, cur_document_charset)->ptr, &u, +-		      cur_baseURL); +-#else + 	    parseURL2(p, &u, cur_baseURL); +-#endif + 	    image.url = parsedURL2Str(&u)->ptr; + 	    if (!uncompressed_file_type(u.file, &image.ext)) + 		image.ext = filename_extension(u.file, TRUE); +@@ -4084,6 +4059,7 @@ process_form_int(struct parsed_tag *tag, int fid) +     parsedtag_get_value(tag, ATTR_METHOD, &p); +     q = "!CURRENT_URL!"; +     parsedtag_get_value(tag, ATTR_ACTION, &q); ++    q = url_encode(remove_space(q), cur_baseURL, cur_document_charset); +     r = NULL; + #ifdef USE_M17N +     if (parsedtag_get_value(tag, ATTR_ACCEPT_CHARSET, &r)) +@@ -5067,11 +5043,10 @@ HTMLtagproc1(struct parsed_tag *tag, struct html_feed_environ *h_env) + 	} + 	return 1; +     case HTML_BASE: +-#ifdef USE_IMAGE ++#if defined(USE_M17N) || defined(USE_IMAGE) + 	p = NULL; + 	if (parsedtag_get_value(tag, ATTR_HREF, &p)) { +-	    if (!cur_baseURL) +-		cur_baseURL = New(ParsedURL); ++	    cur_baseURL = New(ParsedURL); + 	    parseURL(p, cur_baseURL, NULL); + 	} + #endif +@@ -5329,6 +5304,13 @@ HTMLlineproc2body(Buffer *buf, Str (*feed) (), int llimit) + #ifdef MENU_SELECT +     Anchor **a_select = NULL; + #endif ++#if defined(USE_M17N) || defined(USE_IMAGE) ++    ParsedURL *base = baseURL(buf); ++#endif ++#ifdef USE_M17N ++    wc_ces name_charset = url_to_charset(NULL, &buf->currentURL, ++					 buf->document_charset); ++#endif +  +     if (out_size == 0) { + 	out_size = LINELEN; +@@ -5523,16 +5505,17 @@ HTMLlineproc2body(Buffer *buf, Str (*feed) (), int llimit) + 		    hseq = 0; + 		    id = NULL; + 		    if (parsedtag_get_value(tag, ATTR_NAME, &id)) { +-			id = url_quote_conv(id, buf->document_charset); ++			id = url_quote_conv(id, name_charset); + 			registerName(buf, id, currentLn(buf), pos); + 		    } + 		    if (parsedtag_get_value(tag, ATTR_HREF, &p)) +-			p = url_quote_conv(remove_space(p), +-					   buf->document_charset); ++			p = url_encode(remove_space(p), base, ++				       buf->document_charset); + 		    if (parsedtag_get_value(tag, ATTR_TARGET, &q)) + 			q = url_quote_conv(q, buf->document_charset); + 		    if (parsedtag_get_value(tag, ATTR_REFERER, &r)) +-			r = url_quote_conv(r, buf->document_charset); ++			r = url_encode(r, base, ++				       buf->document_charset); + 		    parsedtag_get_value(tag, ATTR_TITLE, &s); + 		    parsedtag_get_value(tag, ATTR_ACCESSKEY, &t); + 		    parsedtag_get_value(tag, ATTR_HSEQ, &hseq); +@@ -5618,7 +5601,7 @@ HTMLlineproc2body(Buffer *buf, Str (*feed) (), int llimit) + 			    ParsedURL u; + 			    Image *image; +  +-			    parseURL2(a_img->url, &u, cur_baseURL); ++			    parseURL2(a_img->url, &u, base); + 			    a_img->image = image = New(Image); + 			    image->url = parsedURL2Str(&u)->ptr; + 			    if (!uncompressed_file_type(u.file, &image->ext)) +@@ -5639,7 +5622,7 @@ HTMLlineproc2body(Buffer *buf, Str (*feed) (), int llimit) + 			    image->map = q; + 			    image->ismap = ismap; + 			    image->touch = 0; +-			    image->cache = getImage(image, cur_baseURL, ++			    image->cache = getImage(image, base, + 						    IMG_FLAG_SKIP); + 			} + 			else if (iseq < 0) { +@@ -5761,8 +5744,8 @@ HTMLlineproc2body(Buffer *buf, Str (*feed) (), int llimit) + 			break; + 		    if (parsedtag_get_value(tag, ATTR_HREF, &p)) { + 			MapArea *a; +-			p = url_quote_conv(remove_space(p), +-					   buf->document_charset); ++			p = url_encode(remove_space(p), base, ++				       buf->document_charset); + 			t = NULL; + 			parsedtag_get_value(tag, ATTR_TARGET, &t); + 			q = ""; +@@ -5811,11 +5794,14 @@ HTMLlineproc2body(Buffer *buf, Str (*feed) (), int llimit) + 		    break; + 		case HTML_BASE: + 		    if (parsedtag_get_value(tag, ATTR_HREF, &p)) { +-			p = url_quote_conv(remove_space(p), +-					   buf->document_charset); ++			p = url_encode(remove_space(p), NULL, ++				       buf->document_charset); + 			if (!buf->baseURL) + 			    buf->baseURL = New(ParsedURL); + 			parseURL(p, buf->baseURL, NULL); ++#if defined(USE_M17N) || defined(USE_IMAGE) ++			base = buf->baseURL; ++#endif + 		    } + 		    if (parsedtag_get_value(tag, ATTR_TARGET, &p)) + 			buf->baseTarget = +@@ -5830,8 +5816,8 @@ HTMLlineproc2body(Buffer *buf, Str (*feed) (), int llimit) + 			int refresh_interval = getMetaRefreshParam(q, &tmp); + #ifdef USE_ALARM + 			if (tmp) { +-			    p = url_quote_conv(remove_space(tmp->ptr), +-					       buf->document_charset); ++			    p = url_encode(remove_space(tmp->ptr), base, ++					   buf->document_charset); + 			    buf->event = setAlarmEvent(buf->event, + 						       refresh_interval, + 						       AL_IMPLICIT_ONCE, +@@ -5844,8 +5830,8 @@ HTMLlineproc2body(Buffer *buf, Str (*feed) (), int llimit) + 						       FUNCNAME_reload, NULL); + #else + 			if (tmp && refresh_interval == 0) { +-			    p = url_quote_conv(remove_space(tmp->ptr), +-					       buf->document_charset); ++			    p = url_encode(remove_space(tmp->ptr), base, ++					   buf->document_charset); + 			    pushEvent(FUNCNAME_gorURL, p); + 			} + #endif +@@ -5929,7 +5915,7 @@ HTMLlineproc2body(Buffer *buf, Str (*feed) (), int llimit) + #ifdef	ID_EXT + 		id = NULL; + 		if (parsedtag_get_value(tag, ATTR_ID, &id)) { +-		    id = url_quote_conv(id, buf->document_charset); ++		    id = url_quote_conv(id, name_charset); + 		    registerName(buf, id, currentLn(buf), pos); + 		} + 		if (renderFrameSet && +@@ -5982,7 +5968,8 @@ addLink(Buffer *buf, struct parsed_tag *tag) +  +     parsedtag_get_value(tag, ATTR_HREF, &href); +     if (href) +-	href = url_quote_conv(remove_space(href), buf->document_charset); ++	href = url_encode(remove_space(href), baseURL(buf), ++			  buf->document_charset); +     parsedtag_get_value(tag, ATTR_TITLE, &title); +     parsedtag_get_value(tag, ATTR_TYPE, &ctype); +     parsedtag_get_value(tag, ATTR_REL, &rel); +@@ -6963,8 +6950,6 @@ loadHTMLstream(URLFile *f, Buffer *newBuf, FILE * src, int internal) + 	image_flag = IMG_FLAG_AUTO; +     else + 	image_flag = IMG_FLAG_SKIP; +-    if (newBuf->currentURL.file) +-	cur_baseURL = baseURL(newBuf); + #endif +  +     if (w3m_halfload) { +@@ -6987,6 +6972,9 @@ loadHTMLstream(URLFile *f, Buffer *newBuf, FILE * src, int internal) + 	htmlenv1.f = stdout; +     else + 	htmlenv1.buf = newTextLineList(); ++#if defined(USE_M17N) || defined(USE_IMAGE) ++    cur_baseURL = baseURL(newBuf); ++#endif +  +     if (SETJMP(AbortLoading) != 0) { + 	HTMLlineproc1("<br>Transfer Interrupted!<br>", &htmlenv1); +@@ -7048,7 +7036,7 @@ loadHTMLstream(URLFile *f, Buffer *newBuf, FILE * src, int internal) + 	} + #endif + 	lineBuf2 = convertLine(f, lineBuf2, HTML_MODE, &charset, doc_charset); +-#if defined(USE_M17N) && defined(USE_IMAGE) ++#ifdef USE_M17N + 	cur_document_charset = charset; + #endif + 	HTMLlineproc0(lineBuf2->ptr, &htmlenv1, internal); +@@ -7060,6 +7048,12 @@ loadHTMLstream(URLFile *f, Buffer *newBuf, FILE * src, int internal) +     obuf.status = R_ST_NORMAL; +     completeHTMLstream(&htmlenv1, &obuf); +     flushline(&htmlenv1, &obuf, 0, 2, htmlenv1.limit); ++#if defined(USE_M17N) || defined(USE_IMAGE) ++    cur_baseURL = NULL; ++#endif ++#ifdef USE_M17N ++    cur_document_charset = 0; ++#endif +     if (htmlenv1.title) + 	newBuf->buffername = htmlenv1.title; +     if (w3m_halfdump) { +@@ -7207,7 +7201,7 @@ loadGopherDir(URLFile *uf, ParsedURL *pu, wc_ces * charset) + 	q = Strnew_m_charp("gopher://", host->ptr, ":", port->ptr, + 			   "/", file->ptr, NULL)->ptr; + 	Strcat_m_charp(tmp, "<a href=\"", +-		       html_quote(url_quote_conv(q, *charset)), ++		       html_quote(url_encode(q, NULL, *charset)), + 		       "\">", p, html_quote(name->ptr + 1), "</a>\n", NULL); +     } +  +@@ -7331,6 +7325,7 @@ loadImageBuffer(URLFile *uf, Buffer *newBuf) +     URLFile f; +     MySignalHandler(*volatile prevtrap) (SIGNAL_ARG) = NULL; +     struct stat st; ++    const ParsedURL *pu = newBuf ? &newBuf->currentURL : NULL; +  +     loadImage(newBuf, IMG_FLAG_STOP); +     image.url = uf->url; +@@ -7338,8 +7333,8 @@ loadImageBuffer(URLFile *uf, Buffer *newBuf) +     image.width = -1; +     image.height = -1; +     image.cache = NULL; +-    cache = getImage(&image, cur_baseURL, IMG_FLAG_AUTO); +-    if (!cur_baseURL->is_nocache && cache->loaded & IMG_FLAG_LOADED && ++    cache = getImage(&image, (ParsedURL *)pu, IMG_FLAG_AUTO); ++    if (!(pu && pu->is_nocache) && cache->loaded & IMG_FLAG_LOADED && + 	!stat(cache->file, &st)) + 	goto image_buffer; +  +@@ -7580,8 +7575,11 @@ openGeneralPagerBuffer(InputStream stream) + #ifdef USE_M17N +     content_charset = 0; + #endif ++    t_buf = newBuffer(INIT_BUFFER_WIDTH); ++    copyParsedURL(&t_buf->currentURL, NULL); ++    t_buf->currentURL.scheme = SCM_LOCAL; ++    t_buf->currentURL.file = "-"; +     if (SearchHeader) { +-	t_buf = newBuffer(INIT_BUFFER_WIDTH); + 	readHeader(&uf, t_buf, TRUE, NULL); + 	t = checkContentType(t_buf); + 	if (t == NULL) +@@ -7609,14 +7607,13 @@ openGeneralPagerBuffer(InputStream stream) + #ifdef USE_IMAGE +     else if (activeImage && displayImage && !useExtImageViewer && + 	     !(w3m_dump & ~DUMP_FRAME) && !strncasecmp(t, "image/", 6)) { +-	cur_baseURL = New(ParsedURL); +-	parseURL("-", cur_baseURL, NULL); + 	buf = loadImageBuffer(&uf, t_buf); + 	buf->type = "text/html"; +     } + #endif +     else { +-	if (doExternal(uf, "-", t, &buf, t_buf)) { ++	if (searchExtViewer(t)) { ++	    buf = doExternal(uf, t, t_buf); + 	    UFclose(&uf); + 	    if (buf == NULL || buf == NO_BUFFER) + 		return buf; +@@ -7629,8 +7626,6 @@ openGeneralPagerBuffer(InputStream stream) + 	} +     } +     buf->real_type = t; +-    buf->currentURL.scheme = SCM_LOCAL; +-    buf->currentURL.file = "-"; +     return buf; + } +  +@@ -7823,9 +7818,8 @@ save2tmp(URLFile uf, char *tmpf) +     return 0; + } +  +-int +-doExternal(URLFile uf, char *path, char *type, Buffer **bufp, +-	   Buffer *defaultbuf) ++Buffer * ++doExternal(URLFile uf, char *type, Buffer *defaultbuf) + { +     Str tmpf, command; +     struct mailcap *mcap; +@@ -7834,7 +7828,7 @@ doExternal(URLFile uf, char *path, char *type, Buffer **bufp, +     char *header, *src = NULL, *ext = uf.ext; +  +     if (!(mcap = searchExtViewer(type))) +-	return 0; ++	return NULL; +  +     if (mcap->nametemplate) { + 	tmpf = unquote_mailcap(mcap->nametemplate, NULL, "", NULL, NULL); +@@ -7867,15 +7861,13 @@ doExternal(URLFile uf, char *path, char *type, Buffer **bufp, + 	    UFclose(&uf); + 	    myExec(command->ptr); + 	} +-	*bufp = NO_BUFFER; +-	return 1; ++	return NO_BUFFER; +     } +     else + #endif +     { + 	if (save2tmp(uf, tmpf->ptr) < 0) { +-	    *bufp = NULL; +-	    return 1; ++	    return NULL; + 	} +     } +     if (mcap->flags & (MAILCAP_HTMLOUTPUT | MAILCAP_COPIOUSOUTPUT)) { +@@ -7918,14 +7910,13 @@ doExternal(URLFile uf, char *path, char *type, Buffer **bufp, + 	buf = NO_BUFFER; +     } +     if (buf && buf != NO_BUFFER) { +-	buf->filename = path; +-	if (buf->buffername == NULL || buf->buffername[0] == '\0') +-	    buf->buffername = conv_from_system(lastFileName(path)); ++	if ((buf->buffername == NULL || buf->buffername[0] == '\0') && ++	    buf->filename) ++	    buf->buffername = conv_from_system(lastFileName(buf->filename)); + 	buf->edit = mcap->edit; + 	buf->mailcap = mcap; +     } +-    *bufp = buf; +-    return 1; ++    return buf; + } +  + static int +diff --git a/fm.h b/fm.h +index 8378939..0f56c31 100644 +--- a/fm.h ++++ b/fm.h +@@ -264,6 +264,18 @@ extern int REV_LB[]; + #define IMG_FLAG_ERROR		2 + #define IMG_FLAG_DONT_REMOVE	4 +  ++#define IS_EMPTY_PARSED_URL(pu) ((pu)->scheme == SCM_UNKNOWN && !(pu)->file) ++#define SCONF_RESERVED		0 ++#define SCONF_SUBSTITUTE_URL	1 ++#define SCONF_URL_CHARSET	2 ++#define SCONF_NO_REFERER_FROM	3 ++#define SCONF_NO_REFERER_TO	4 ++#define SCONF_N_FIELD		5 ++#define query_SCONF_SUBSTITUTE_URL(pu) ((const char *)querySiteconf(pu, SCONF_SUBSTITUTE_URL)) ++#define query_SCONF_URL_CHARSET(pu) ((const wc_ces *)querySiteconf(pu, SCONF_URL_CHARSET)) ++#define query_SCONF_NO_REFERER_FROM(pu) ((const int *)querySiteconf(pu, SCONF_NO_REFERER_FROM)) ++#define query_SCONF_NO_REFERER_TO(pu) ((const int *)querySiteconf(pu, SCONF_NO_REFERER_TO)) ++ + /*  +  * Macros. +  */ +@@ -972,6 +984,7 @@ global int BackgroundExtViewer init(TRUE); + global int disable_secret_security_check init(FALSE); + global char *passwd_file init(PASSWD_FILE); + global char *pre_form_file init(PRE_FORM_FILE); ++global char *siteconf_file init(SITECONF_FILE); + global char *ftppasswd init(NULL); + global int ftppass_hostnamegen init(TRUE); + global int do_download init(FALSE); +diff --git a/form.c b/form.c +index b7556ca..fa17be4 100644 +--- a/form.c ++++ b/form.c +@@ -787,7 +787,7 @@ struct pre_form { + static struct pre_form *PreForm = NULL; +  + static struct pre_form * +-add_pre_form(struct pre_form *prev, char *url, char *name, char *action) ++add_pre_form(struct pre_form *prev, char *url, Regex *re_url, char *name, char *action) + { +     ParsedURL pu; +     struct pre_form *new; +@@ -796,21 +796,13 @@ add_pre_form(struct pre_form *prev, char *url, char *name, char *action) + 	new = prev->next = New(struct pre_form); +     else + 	new = PreForm = New(struct pre_form); +-    if (url && *url == '/') { +-	int l = strlen(url); +-	if (l > 1 && url[l - 1] == '/') +-	    new->url = allocStr(url + 1, l - 2); +-	else +-	    new->url = url + 1; +-	new->re_url = newRegex(new->url, FALSE, NULL, NULL); +-	if (!new->re_url) +-	    new->url = NULL; +-    } +-    else if (url) { ++    if (url && !re_url) { + 	parseURL2(url, &pu, NULL); + 	new->url = parsedURL2Str(&pu)->ptr; +-	new->re_url = NULL; +     } ++    else ++	new->url = url; ++    new->re_url = re_url; +     new->name = (name && *name) ? name : NULL; +     new->action = (action && *action) ? action : NULL; +     new->item = NULL; +@@ -834,7 +826,7 @@ add_pre_form_item(struct pre_form *pf, struct pre_form_item *prev, int type, +     new->name = name; +     new->value = value; +     if (checked && *checked && (!strcmp(checked, "0") || +-				strcasecmp(checked, "off") ++				!strcasecmp(checked, "off") + 				|| !strcasecmp(checked, "no"))) + 	new->checked = 0; +     else +@@ -875,6 +867,7 @@ loadPreForm(void) + 	return; +     while (1) { + 	char *p, *s, *arg; ++	Regex *re_arg; +  + 	line = Strfgets(fp); + 	if (line->length == 0) +@@ -890,18 +883,20 @@ loadPreForm(void) + 	if (*p == '#' || *p == '\0') + 	    continue;		/* comment or empty line */ + 	s = getWord(&p); +-	arg = getWord(&p); +  + 	if (!strcmp(s, "url")) { ++	    arg = getRegexWord((const char **)&p, &re_arg); + 	    if (!arg || !*arg) + 		continue; + 	    p = getQWord(&p); +-	    pf = add_pre_form(pf, arg, NULL, p); ++	    pf = add_pre_form(pf, arg, re_arg, NULL, p); + 	    pi = pf->item; + 	    continue; + 	} + 	if (!pf) + 	    continue; ++ ++	arg = getWord(&p); + 	if (!strcmp(s, "form")) { + 	    if (!arg || !*arg) + 		continue; +@@ -913,7 +908,7 @@ loadPreForm(void) + 	    } + 	    if (pf->item) { + 		struct pre_form *prev = pf; +-		pf = add_pre_form(prev, "", s, p); ++		pf = add_pre_form(prev, "", NULL, s, p); + 		/* copy previous URL */ + 		pf->url = prev->url; + 		pf->re_url = prev->re_url; +diff --git a/frame.c b/frame.c +index b431437..48c2d72 100644 +--- a/frame.c ++++ b/frame.c +@@ -91,7 +91,8 @@ newFrame(struct parsed_tag *tag, Buffer *buf) +     body->baseURL = baseURL(buf); +     if (tag) { + 	if (parsedtag_get_value(tag, ATTR_SRC, &p)) +-	    body->url = url_quote_conv(remove_space(p), buf->document_charset); ++	    body->url = url_encode(remove_space(p), body->baseURL, ++				   buf->document_charset); + 	if (parsedtag_get_value(tag, ATTR_NAME, &p) && *p != '_') + 	    body->name = url_quote_conv(p, buf->document_charset); +     } +@@ -639,7 +640,7 @@ createFrameFile(struct frameset *f, FILE * f1, Buffer *current, int level, + 			case HTML_BASE: + 			    /* "BASE" is prohibit tag */ + 			    if (parsedtag_get_value(tag, ATTR_HREF, &q)) { +-				q = url_quote_conv(remove_space(q), charset); ++				q = url_encode(remove_space(q), NULL, charset); + 				parseURL(q, &base, NULL); + 			    } + 			    if (parsedtag_get_value(tag, ATTR_TARGET, &q)) { +@@ -768,8 +769,8 @@ createFrameFile(struct frameset *f, FILE * f1, Buffer *current, int level, + 				if (!tag->value[j]) + 				    break; + 				tag->value[j] = +-				    url_quote_conv(remove_space(tag->value[j]), +-						   charset); ++				    url_encode(remove_space(tag->value[j]), ++					       &base, charset); + 				tag->need_reconstruct = TRUE; + 				parseURL2(tag->value[j], &url, &base); + 				if (url.scheme == SCM_UNKNOWN || +diff --git a/func.c b/func.c +index f389e00..8b5deac 100644 +--- a/func.c ++++ b/func.c +@@ -8,6 +8,7 @@ + #include "fm.h" + #include "func.h" + #include "myctype.h" ++#include "regex.h" +  + #include "funcname.c" + #include "functable.c" +@@ -434,6 +435,93 @@ getQWord(char **str) +     return tmp->ptr; + } +  ++/* This extracts /regex/i or m@regex@i from the given string. ++ * Then advances *str to the end of regex. ++ * If the input does not seems to be a regex, this falls back to getQWord(). ++ *  ++ * Returns a word (no matter whether regex or not) in the give string. ++ * If regex_ret is non-NULL, compiles the regex and stores there. ++ * ++ * XXX: Actually this is unrelated to func.c. ++ */ ++char * ++getRegexWord(const char **str, Regex **regex_ret) ++{ ++    char *word = NULL; ++    const char *p, *headp, *bodyp, *tailp; ++    char delimiter; ++    int esc; ++    int igncase = 0; ++ ++    p = *str; ++    SKIP_BLANKS(p); ++    headp = p; ++ ++    /* Get the opening delimiter */ ++    if (p[0] == 'm' && IS_PRINT(p[1]) && !IS_ALNUM(p[1]) && p[1] != '\\') { ++	delimiter = p[1]; ++	p += 2; ++    } ++    else if (p[0] == '/') { ++	delimiter = '/'; ++	p += 1; ++    } ++    else { ++	goto not_regex; ++    } ++    bodyp = p; ++ ++    /* Scan the end of the expression */ ++    for (esc = 0; *p; ++p) { ++	if (esc) { ++	    esc = 0; ++	} else { ++	    if (*p == delimiter) ++		break; ++	    else if (*p == '\\') ++		esc = 1; ++	} ++    } ++    if (!*p && *headp == '/') ++	goto not_regex; ++    tailp = p; ++ ++    /* Check the modifiers */ ++    if (*p == delimiter) { ++	while (*++p && !IS_SPACE(*p)) { ++	    switch (*p) { ++	    case 'i': ++		igncase = 1; ++		break; ++	    } ++	    /* ignore unknown modifiers */ ++	} ++    } ++ ++    /* Save the expression */ ++    word = allocStr(headp, p - headp); ++ ++    /* Compile */ ++    if (regex_ret) { ++	if (*tailp == delimiter) ++	    word[tailp - headp] = 0; ++	*regex_ret = newRegex(word + (bodyp - headp), igncase, NULL, NULL); ++	if (*tailp == delimiter) ++	    word[tailp - headp] = delimiter; ++    } ++    goto last; ++ ++not_regex: ++    p = headp; ++    word = getQWord((char **)&p); ++    if (regex_ret) ++	*regex_ret = NULL; ++ ++last: ++    *str = p; ++    return word; ++} ++ + #ifdef USE_MOUSE + static MouseAction default_mouse_action = { +     NULL, +diff --git a/history.c b/history.c +index 951ef83..e9be09b 100644 +--- a/history.c ++++ b/history.c +@@ -17,7 +17,7 @@ historyBuffer(Hist *hist) + 	for (item = hist->list->last; item; item = item->prev) { + 	    q = html_quote((char *)item->ptr); + 	    if (DecodeURL) +-		p = html_quote(url_unquote_conv((char *)item->ptr, 0)); ++		p = html_quote(url_decode2((char *)item->ptr, NULL)); + 	    else + 		p = q; + 	    Strcat_charp(src, "<li><a href=\""); +diff --git a/indep.c b/indep.c +index 65b04aa..89e86c1 100644 +--- a/indep.c ++++ b/indep.c +@@ -357,6 +357,20 @@ strcasemstr(char *str, char *srch[], char **ret_ptr) +     return -1; + } +  ++int ++strmatchlen(const char *s1, const char *s2, int maxlen) ++{ ++    int i; ++ ++    /* To allow the maxlen to be negatie (infinity), ++     * compare by "!=" instead of "<=". */ ++    for (i = 0; i != maxlen; ++i) { ++	if (!s1[i] || !s2[i] || s1[i] != s2[i]) ++	    break; ++    } ++    return i; ++} ++ + char * + remove_space(char *str) + { +diff --git a/indep.h b/indep.h +index b3819a3..cf566fe 100644 +--- a/indep.h ++++ b/indep.h +@@ -52,6 +52,7 @@ extern int strncasecmp(const char *s1, const char *s2, size_t n); + extern char *strcasestr(const char *s1, const char *s2); + #endif + extern int strcasemstr(char *str, char *srch[], char **ret_ptr); ++int strmatchlen(const char *s1, const char *s2, int maxlen); + extern char *remove_space(char *str); + extern int non_null(char *s); + extern void cleanup_line(Str s, int mode); +diff --git a/linein.c b/linein.c +index b7e81b6..572b015 100644 +--- a/linein.c ++++ b/linein.c +@@ -1026,7 +1026,7 @@ _prev(void) + 	strCurrentBuf = strBuf; +     } +     if (DecodeURL && (cm_mode & CPL_URL) ) +-	p = url_unquote_conv(p, 0); ++	p = url_decode2(p, NULL); +     strBuf = Strnew_charp(p); +     CLen = CPos = setStrType(strBuf, strProp); +     offset = 0; +@@ -1045,7 +1045,7 @@ _next(void) +     p = nextHist(hist); +     if (p) { + 	if (DecodeURL && (cm_mode & CPL_URL) ) +-	    p = url_unquote_conv(p, 0); ++	    p = url_decode2(p, NULL); + 	strBuf = Strnew_charp(p); +     } +     else { +diff --git a/main.c b/main.c +index b421943..a414391 100644 +--- a/main.c ++++ b/main.c +@@ -894,12 +894,17 @@ main(int argc, char **argv, char **envp) + 	if (i >= 0) { + 	    SearchHeader = search_header; + 	    DefaultType = default_type; ++	    char *url; ++	     ++	    url = load_argv[i]; ++	    if (getURLScheme(&url) == SCM_MISSING && !ArgvIsURL) ++		url = file_to_url(load_argv[i]); ++	    else ++		url = url_encode(conv_from_system(load_argv[i]), NULL, 0); + 	    if (w3m_dump == DUMP_HEAD) { + 		request = New(FormList); + 		request->method = FORM_METHOD_HEAD; +-		newbuf = +-		    loadGeneralFile(load_argv[i], NULL, NO_REFERER, 0, +-				    request); ++		newbuf = loadGeneralFile(url, NULL, NO_REFERER, 0, request); + 	    } + 	    else { + 		if (post_file && i == 0) { +@@ -928,9 +933,7 @@ main(int argc, char **argv, char **envp) + 		else { + 		    request = NULL; + 		} +-		newbuf = +-		    loadGeneralFile(load_argv[i], NULL, NO_REFERER, 0, +-				    request); ++		newbuf = loadGeneralFile(url, NULL, NO_REFERER, 0, request); + 	    } + 	    if (newbuf == NULL) { + 		/* FIXME: gettextize? */ +@@ -945,7 +948,7 @@ main(int argc, char **argv, char **envp) + 		break; + 	    case SCM_LOCAL: + 	    case SCM_LOCAL_CGI: +-		unshiftHist(LoadHist, conv_from_system(load_argv[i])); ++		unshiftHist(LoadHist, url); + 	    default: + 		pushHashHist(URLHist, parsedURL2Str(&newbuf->currentURL)->ptr); + 		break; +@@ -1269,15 +1272,12 @@ do_dump(Buffer *buf) + 	    printf("\nReferences:\n\n"); + 	    for (i = 0; i < buf->href->nanchor; i++) { + 	        ParsedURL pu; +-	        static Str s = NULL; ++		char *url; + 		if (buf->href->anchors[i].slave) + 		    continue; + 	        parseURL2(buf->href->anchors[i].url, &pu, baseURL(buf)); +-	        s = parsedURL2Str(&pu); +-    	        if (DecodeURL) +-		    s = Strnew_charp(url_unquote_conv +-				     (s->ptr, Currentbuf->document_charset)); +-	        printf("[%d] %s\n", buf->href->anchors[i].hseq + 1, s->ptr); ++		url = url_decode2(parsedURL2Str(&pu)->ptr, Currentbuf); ++	        printf("[%d] %s\n", buf->href->anchors[i].hseq + 1, url); + 	    } + 	} +     } +@@ -2261,7 +2261,7 @@ DEFUN(movR1, MOVE_RIGHT1, + static wc_uint32 + getChar(char *p) + { +-    return wc_any_to_ucs(wtf_parse1(&p)); ++    return wc_any_to_ucs(wtf_parse1((wc_uchar **)&p)); + } +  + static int +@@ -2804,12 +2804,15 @@ loadLink(char *url, char *target, char *referer, FormList *request) +     union frameset_element *f_element = NULL; +     int flag = 0; +     ParsedURL *base, pu; ++    const int *no_referer_ptr; +  +     message(Sprintf("loading %s", url)->ptr, 0, 0); +     refresh(); +  ++    no_referer_ptr = query_SCONF_NO_REFERER_FROM(&Currentbuf->currentURL); +     base = baseURL(Currentbuf); +-    if (base == NULL || ++    if ((no_referer_ptr && *no_referer_ptr) || ++	base == NULL || + 	base->scheme == SCM_LOCAL || base->scheme == SCM_LOCAL_CGI) + 	referer = NO_REFERER; +     if (referer == NULL) +@@ -4055,6 +4058,7 @@ goURL0(char *prompt, int relative) +     char *url, *referer; +     ParsedURL p_url, *current; +     Buffer *cur_buf = Currentbuf; ++    const int *no_referer_ptr; +  +     url = searchKeyData(); +     if (url == NULL) { +@@ -4064,11 +4068,8 @@ goURL0(char *prompt, int relative) + 	current = baseURL(Currentbuf); + 	if (current) { + 	    char *c_url = parsedURL2Str(current)->ptr; +-	    if (DefaultURLString == DEFAULT_URL_CURRENT) { +-		url = c_url; +-		if (DecodeURL) +-		    url = url_unquote_conv(url, 0); +-	    } ++	    if (DefaultURLString == DEFAULT_URL_CURRENT) ++		url = url_decode2(c_url, NULL); + 	    else + 		pushHist(hist, c_url); + 	} +@@ -4077,11 +4078,8 @@ goURL0(char *prompt, int relative) + 	    char *a_url; + 	    parseURL2(a->url, &p_url, current); + 	    a_url = parsedURL2Str(&p_url)->ptr; +-	    if (DefaultURLString == DEFAULT_URL_LINK) { +-		url = a_url; +-		if (DecodeURL) +-		    url = url_unquote_conv(url, Currentbuf->document_charset); +-	    } ++	    if (DefaultURLString == DEFAULT_URL_LINK) ++		url = url_decode2(a_url, Currentbuf); + 	    else + 		pushHist(hist, a_url); + 	} +@@ -4089,15 +4087,22 @@ goURL0(char *prompt, int relative) + 	if (url != NULL) + 	    SKIP_BLANKS(url); +     } +-#ifdef USE_M17N +-    if (url != NULL) { +-	if ((relative || *url == '#') && Currentbuf->document_charset) +-	    url = wc_conv_strict(url, InnerCharset, +-				 Currentbuf->document_charset)->ptr; ++    if (relative) { ++	no_referer_ptr = query_SCONF_NO_REFERER_FROM(&Currentbuf->currentURL); ++	current = baseURL(Currentbuf); ++	if ((no_referer_ptr && *no_referer_ptr) || ++	    current == NULL || ++	    current->scheme == SCM_LOCAL || current->scheme == SCM_LOCAL_CGI) ++	    referer = NO_REFERER; + 	else +-	    url = conv_to_system(url); ++	    referer = parsedURL2Str(&Currentbuf->currentURL)->ptr; ++	url = url_encode(url, current, Currentbuf->document_charset); ++    } ++    else { ++	current = NULL; ++	referer = NULL; ++	url = url_encode(url, NULL, 0); +     } +-#endif +     if (url == NULL || *url == '\0') { + 	displayBuffer(Currentbuf, B_FORCE_REDRAW); + 	return; +@@ -4106,14 +4111,6 @@ goURL0(char *prompt, int relative) + 	gotoLabel(url + 1); + 	return; +     } +-    if (relative) { +-	current = baseURL(Currentbuf); +-	referer = parsedURL2Str(&Currentbuf->currentURL)->ptr; +-    } +-    else { +-	current = NULL; +-	referer = NULL; +-    } +     parseURL2(url, &p_url, current); +     pushHashHist(URLHist, parsedURL2Str(&p_url)->ptr); +     cmd_loadURL(url, current, referer, NULL); +@@ -4510,8 +4507,7 @@ _peekURL(int only_img) + 	s = parsedURL2Str(&pu); +     } +     if (DecodeURL) +-	s = Strnew_charp(url_unquote_conv +-			 (s->ptr, Currentbuf->document_charset)); ++	s = Strnew_charp(url_decode2(s->ptr, Currentbuf)); + #ifdef USE_M17N +     s = checkType(s, &pp, NULL); +     p = NewAtom_N(Lineprop, s->length); +@@ -4570,7 +4566,7 @@ DEFUN(curURL, PEEK, "Peek current URL") + 	offset = 0; + 	s = currentURL(); + 	if (DecodeURL) +-	    s = Strnew_charp(url_unquote_conv(s->ptr, 0)); ++	    s = Strnew_charp(url_decode2(s->ptr, NULL)); + #ifdef USE_M17N + 	s = checkType(s, &pp, NULL); + 	p = NewAtom_N(Lineprop, s->length); +diff --git a/map.c b/map.c +index 90aa35a..12701e7 100644 +--- a/map.c ++++ b/map.c +@@ -279,7 +279,7 @@ follow_map_panel(Buffer *buf, char *name) + 	p = parsedURL2Str(&pu)->ptr; + 	q = html_quote(p); + 	if (DecodeURL) +-	    p = html_quote(url_unquote_conv(p, buf->document_charset)); ++	    p = html_quote(url_decode2(p, buf)); + 	else + 	    p = q; + 	Strcat_m_charp(mappage, "<tr valign=top><td><a href=\"", q, "\">", +@@ -417,10 +417,7 @@ append_map_info(Buffer *buf, Str tmp, FormItemList *fi) + 	    continue; + 	parseURL2(a->url, &pu, baseURL(buf)); + 	q = html_quote(parsedURL2Str(&pu)->ptr); +-	if (DecodeURL) +-	    p = html_quote(url_unquote_conv(a->url, buf->document_charset)); +-	else +-	    p = html_quote(a->url); ++	p = html_quote(url_decode2(a->url, buf)); + 	Strcat_m_charp(tmp, "<tr valign=top><td>  <td><a href=\"", + 		       q, "\">", + 		       html_quote(*a->alt ? a->alt : mybasename(a->url)), +@@ -457,10 +454,8 @@ append_link_info(Buffer *buf, Str html, LinkList * link) + 	    Strcat_charp(html, "[Rev]"); + 	if (!l->url) + 	    url = "(empty)"; +-	else if (DecodeURL) +-	    url = html_quote(url_unquote_conv(l->url, buf->document_charset)); + 	else +-	    url = html_quote(l->url); ++	    url = html_quote(url_decode2(l->url, buf)); + 	Strcat_m_charp(html, "<td>", url, NULL); + 	if (l->ctype) + 	    Strcat_m_charp(html, " (", html_quote(l->ctype), ")", NULL); +@@ -498,8 +493,7 @@ append_frame_info(Buffer *buf, Str html, struct frameset *set, int level) + 		    Strcat_charp(html, p); + 		} + 		if (DecodeURL) +-		    p = html_quote(url_unquote_conv(frame.body->url, +-						    buf->document_charset)); ++		    p = html_quote(url_decode2(frame.body->url, buf)); + 		else + 		    p = q; + 		Strcat_m_charp(html, " ", p, "</a></pre_int><br>\n", NULL); +@@ -550,9 +544,7 @@ page_info_panel(Buffer *buf) + #ifdef USE_M17N +     Strcat_charp(tmp, "<form method=internal action=charset>"); + #endif +-    p = parsedURL2Str(&buf->currentURL)->ptr; +-    if (DecodeURL) +-	p = url_unquote_conv(p, 0); ++    p = url_decode2(parsedURL2Str(&buf->currentURL)->ptr, NULL); +     Strcat_m_charp(tmp, "<table cellpadding=0>", + 		   "<tr valign=top><td nowrap>Title<td>", + 		   html_quote(buf->buffername), +@@ -589,7 +581,7 @@ page_info_panel(Buffer *buf) + 	p = parsedURL2Str(&pu)->ptr; + 	q = html_quote(p); + 	if (DecodeURL) +-	    p = html_quote(url_unquote_conv(p, buf->document_charset)); ++	    p = html_quote(url_decode2(p, buf)); + 	else + 	    p = q; + 	Strcat_m_charp(tmp, +@@ -602,7 +594,7 @@ page_info_panel(Buffer *buf) + 	p = parsedURL2Str(&pu)->ptr; + 	q = html_quote(p); + 	if (DecodeURL) +-	    p = html_quote(url_unquote_conv(p, buf->document_charset)); ++	    p = html_quote(url_decode2(p, buf)); + 	else + 	    p = q; + 	Strcat_m_charp(tmp, +@@ -613,10 +605,7 @@ page_info_panel(Buffer *buf) +     if (a != NULL) { + 	FormItemList *fi = (FormItemList *)a->url; + 	p = form2str(fi); +-	if (DecodeURL) +-	    p = html_quote(url_unquote_conv(p, buf->document_charset)); +-	else +-	    p = html_quote(p); ++	p = html_quote(url_decode2(p, buf)); + 	Strcat_m_charp(tmp, + 		       "<tr valign=top><td nowrap>Method/type of current form <td>", + 		       p, NULL); +diff --git a/menu.c b/menu.c +index 774b1bd..0f66583 100644 +--- a/menu.c ++++ b/menu.c +@@ -1365,9 +1365,7 @@ initSelectMenu(void) + 		break; + 	    default: + 		Strcat_char(str, ' '); +-		p = parsedURL2Str(&buf->currentURL)->ptr; +-		if (DecodeURL) +-		    p = url_unquote_conv(p, 0); ++		p = url_decode2(parsedURL2Str(&buf->currentURL)->ptr, NULL); + 		Strcat_charp(str, p); + 		break; + 	    } +@@ -1513,9 +1511,7 @@ initSelTabMenu(void) + 	    case SCM_MISSING: + 		break; + 	    default: +-		p = parsedURL2Str(&buf->currentURL)->ptr; +-		if (DecodeURL) +-		    p = url_unquote_conv(p, 0); ++		p = url_decode2(parsedURL2Str(&buf->currentURL)->ptr, NULL); + 		Strcat_charp(str, p); + 		break; + 	    } +@@ -1845,10 +1841,8 @@ link_menu(Buffer *buf) + 	    Strcat_charp(str, " "); + 	if (!l->url) + 	    p = ""; +-	else if (DecodeURL) +-	    p = url_unquote_conv(l->url, buf->document_charset); + 	else +-	    p = l->url; ++	    p = url_decode2(l->url, buf); + 	Strcat_charp(str, p); + 	label[i] = str->ptr; + 	if (len < str->length) +diff --git a/po/ja.po b/po/ja.po +index d67c695..947191c 100644 +--- a/po/ja.po ++++ b/po/ja.po +@@ -407,6 +407,10 @@ msgid "File for setting form on loading" + msgstr "梧莨惹若荐絎<ゃ" +  + #: rc.c:149 ++msgid "File for preferences for each site" ++msgstr "泣ゃヨ┃絎<ゃ" ++ ++#: rc.c:149 + msgid "Password for anonymous FTP (your mail address)" + msgstr "FTP鴻若(mail address篏帥)" +  +diff --git a/proto.h b/proto.h +index f8a7345..7b1a7a6 100644 +--- a/proto.h ++++ b/proto.h +@@ -162,6 +162,24 @@ extern Str searchURIMethods(ParsedURL *pu); + extern void chkExternalURIBuffer(Buffer *buf); + #endif + extern ParsedURL *schemeToProxy(int scheme); ++#ifdef USE_M17N ++extern wc_ces url_to_charset(const char *url, const ParsedURL *base, ++			     wc_ces doc_charset); ++extern char *url_encode(const char *url, const ParsedURL *base, ++			wc_ces doc_charset); ++#if 0 ++extern char *url_decode(const char *url, const ParsedURL *base, ++			wc_ces doc_charset); ++#endif ++extern char *url_decode2(const char *url, const Buffer *buf); ++#else /* !defined(USE_M17N) */ ++#define url_encode(url, base, cs) url_quote(url) ++extern char *url_decode0(const char *url); ++#if 0 ++#define url_decode(url, base, cs) url_decode0(url) ++#endif ++#define url_decode2(url, buf) url_decode0(url) ++#endif /* !defined(USE_M17N) */ + extern void examineFile(char *path, URLFile *uf); + extern char *acceptableEncoding(); + extern int dir_exist(char *path); +@@ -180,7 +198,6 @@ extern void push_symbol(Str str, char symbol, int width, int n); + #ifdef USE_UNICODE + extern void update_utf8_symbol(void); + #endif +-extern Buffer *loadFile(char *path); + extern Buffer *loadGeneralFile(char *path, ParsedURL *current, char *referer, + 			       int flag, FormList *request); + extern int is_boundary(unsigned char *, unsigned char *); +@@ -249,8 +266,7 @@ extern Buffer *openPagerBuffer(InputStream stream, Buffer *buf); + extern Buffer *openGeneralPagerBuffer(InputStream stream); + extern Line *getNextPage(Buffer *buf, int plen); + extern int save2tmp(URLFile uf, char *tmpf); +-extern int doExternal(URLFile uf, char *path, char *type, Buffer **bufp, +-		      Buffer *defaultbuf); ++extern Buffer *doExternal(URLFile uf, char *type, Buffer *defaultbuf); + extern int _doFileCopy(char *tmpf, char *defstr, int download); + #define doFileCopy(tmpf, defstr) _doFileCopy(tmpf, defstr, FALSE); + extern int doFileMove(char *tmpf, char *defstr); +@@ -507,7 +523,7 @@ extern ParsedURL *baseURL(Buffer *buf); + extern int openSocket(char *hostname, char *remoteport_name, + 		      unsigned short remoteport_num); + extern void parseURL(char *url, ParsedURL *p_url, ParsedURL *current); +-extern void copyParsedURL(ParsedURL *p, ParsedURL *q); ++extern void copyParsedURL(ParsedURL *p, const ParsedURL *q); + extern void parseURL2(char *url, ParsedURL *pu, ParsedURL *current); + extern Str parsedURL2Str(ParsedURL *pu); + extern int getURLScheme(char **url); +@@ -611,6 +627,7 @@ extern char *confFile(char *base); + extern char *auxbinFile(char *base); + extern char *libFile(char *base); + extern char *helpFile(char *base); ++extern const void *querySiteconf(const ParsedURL *query_pu, int field); + extern Str localCookie(void); + extern Str loadLocalDir(char *dirname); + extern void set_environ(char *var, char *value); +@@ -723,6 +740,8 @@ extern int getKey(char *s); + extern char *getKeyData(int key); + extern char *getWord(char **str); + extern char *getQWord(char **str); ++struct regex; ++extern char *getRegexWord(const char **str, struct regex **regex_ret); + #ifdef USE_MOUSE + extern void initMouseAction(void); + #endif +diff --git a/rc.c b/rc.c +index 8441a39..3bf6cea 100644 +--- a/rc.c ++++ b/rc.c +@@ -9,7 +9,9 @@ + #include <errno.h> + #include "parsetag.h" + #include "local.h" ++#include "regex.h" + #include <stdlib.h> ++#include <stddef.h> +  + struct param_ptr { +     char *name; +@@ -146,6 +148,7 @@ static int OptionEncode = FALSE; + #define CMT_DISABLE_SECRET_SECURITY_CHECK	N_("Disable secret file security check") + #define CMT_PASSWDFILE	 N_("Password file") + #define CMT_PRE_FORM_FILE	N_("File for setting form on loading") ++#define CMT_SITECONF_FILE	N_("File for preferences for each site") + #define CMT_FTPPASS      N_("Password for anonymous FTP (your mail address)") + #define CMT_FTPPASS_HOSTNAMEGEN N_("Generate domain part of password for FTP") + #define CMT_USERAGENT    N_("User-Agent identification string") +@@ -619,6 +622,8 @@ struct param_ptr params9[] = { +      CMT_FTPPASS_HOSTNAMEGEN, NULL}, +     {"pre_form_file", P_STRING, PI_TEXT, (void *)&pre_form_file, +      CMT_PRE_FORM_FILE, NULL}, ++    {"siteconf_file", P_STRING, PI_TEXT, (void *)&siteconf_file, ++     CMT_SITECONF_FILE, NULL}, +     {"user_agent", P_STRING, PI_TEXT, (void *)&UserAgent, CMT_USERAGENT, NULL}, +     {"no_referer", P_INT, PI_ONOFF, (void *)&NoSendReferer, CMT_NOSENDREFERER, +      NULL}, +@@ -1173,6 +1178,8 @@ do_mkdir(const char *dir, long mode) + #endif				/* not __MINW32_VERSION */ + #endif				/* not __EMX__ */ +  ++static void loadSiteconf(void); ++ + void + sync_with_option(void) + { +@@ -1199,6 +1206,7 @@ sync_with_option(void) + #endif +     loadPasswd(); +     loadPreForm(); ++    loadSiteconf(); +  +     if (AcceptLang == NULL || *AcceptLang == '\0') { + 	/* TRANSLATORS:  +@@ -1556,3 +1564,217 @@ helpFile(char *base) +     return expandPath(Strnew_m_charp(w3m_help_dir(), "/", base, NULL)->ptr); + } + #endif ++ ++/* siteconf */ ++/* ++ * url "<url>"|/<re-url>/|m@<re-url>@i [exact] ++ * substitute_url "<destination-url>" ++ * url_charset <charset> ++ * no_referer_from on|off ++ * no_referer_to on|off ++ *  ++ * The last match wins. ++ */ ++ ++struct siteconf_rec { ++    struct siteconf_rec *next; ++    char *url; ++    Regex *re_url; ++    int url_exact; ++    unsigned char mask[(SCONF_N_FIELD + 7) >> 3]; ++ ++    char *substitute_url; ++#ifdef USE_M17N ++    wc_ces url_charset; ++#endif ++    int no_referer_from; ++    int no_referer_to; ++}; ++#define SCONF_TEST(ent, f) ((ent)->mask[(f)>>3] & (1U<<((f)&7))) ++#define SCONF_SET(ent, f) ((ent)->mask[(f)>>3] |= (1U<<((f)&7))) ++#define SCONF_CLEAR(ent, f) ((ent)->mask[(f)>>3] &= ~(1U<<((f)&7))) ++ ++static struct siteconf_rec *siteconf_head = NULL; ++static struct siteconf_rec *newSiteconfRec(void); ++ ++static struct siteconf_rec * ++newSiteconfRec(void) ++{ ++    struct siteconf_rec *ent; ++ ++    ent = New(struct siteconf_rec); ++    ent->next = NULL; ++    ent->url = NULL; ++    ent->re_url = NULL; ++    ent->url_exact = FALSE; ++    memset(ent->mask, 0, sizeof(ent->mask)); ++ ++    ent->substitute_url = NULL; ++#ifdef USE_M17N ++    ent->url_charset = 0; ++#endif ++    return ent; ++} ++ ++static void ++loadSiteconf(void) ++{ ++    char *efname; ++    FILE *fp; ++    Str line; ++    struct siteconf_rec *ent = NULL; ++ ++    siteconf_head = NULL; ++    if (!siteconf_file) ++	return; ++    if ((efname = expandPath(siteconf_file)) == NULL) ++	return; ++    fp = fopen(efname, "r"); ++    if (fp == NULL) ++	return; ++    while (line = Strfgets(fp), line->length > 0) { ++	char *p, *s; ++ ++	Strchop(line); ++	p = line->ptr; ++	SKIP_BLANKS(p); ++	if (*p == '#' || *p == '\0') ++	    continue; ++	s = getWord(&p); ++ ++	/* The "url" begins a new record. */ ++	if (strcmp(s, "url") == 0) { ++	    char *url, *opt; ++	    struct siteconf_rec *newent; ++ ++	    /* First, register the current record. */ ++	    if (ent) { ++		ent->next = siteconf_head; ++		siteconf_head = ent; ++		ent = NULL; ++	    } ++ ++	    /* Second, create a new record. */ ++	    newent = newSiteconfRec(); ++	    url = getRegexWord((const char **)&p, &newent->re_url); ++	    opt = getWord(&p); ++	    SKIP_BLANKS(p); ++	    if (!newent->re_url) { ++		ParsedURL pu; ++		if (!url || !*url) ++		    continue; ++		parseURL2(url, &pu, NULL); ++		newent->url = parsedURL2Str(&pu)->ptr; ++	    } ++	    /* If we have an extra or unknown option, ignore this record ++	     * for future extensions. */ ++	    if (strcmp(opt, "exact") == 0) { ++		newent->url_exact = TRUE; ++	    } ++	    else if (*opt != 0) ++		    continue; ++	    if (*p) ++		continue; ++	    ent = newent; ++	    continue; ++	} ++ ++	/* If the current record is broken, skip to the next "url". */ ++	if (!ent) ++	    continue; ++ ++	/* Fill the new record. */ ++	if (strcmp(s, "substitute_url") == 0) { ++	    ent->substitute_url = getQWord(&p); ++	    SCONF_SET(ent, SCONF_SUBSTITUTE_URL); ++	} ++#ifdef USE_M17N ++	else if (strcmp(s, "url_charset") == 0) { ++	    char *charset = getWord(&p); ++	    ent->url_charset = (charset && *charset) ? ++		wc_charset_to_ces(charset) : 0; ++	    SCONF_SET(ent, SCONF_URL_CHARSET); ++	} ++#endif /* USE_M17N */ ++	else if (strcmp(s, "no_referer_from") == 0) { ++	    ent->no_referer_from = str_to_bool(getWord(&p), 0); ++	    SCONF_SET(ent, SCONF_NO_REFERER_FROM); ++	} ++	else if (strcmp(s, "no_referer_to") == 0) { ++	    ent->no_referer_to = str_to_bool(getWord(&p), 0); ++	    SCONF_SET(ent, SCONF_NO_REFERER_TO); ++	} ++    } ++    if (ent) { ++	ent->next = siteconf_head; ++	siteconf_head = ent; ++	ent = NULL; ++    } ++    fclose(fp); ++} ++ ++const void * ++querySiteconf(const ParsedURL *query_pu, int field) ++{ ++    const struct siteconf_rec *ent; ++    Str u; ++    char *firstp, *lastp; ++ ++    if (field < 0 || field >= SCONF_N_FIELD) ++	return NULL; ++    if (!query_pu || IS_EMPTY_PARSED_URL(query_pu)) ++	return NULL; ++    u = parsedURL2Str((ParsedURL *)query_pu); ++    if (u->length == 0) ++	return NULL; ++ ++    for (ent = siteconf_head; ent; ent = ent->next) { ++	if (!SCONF_TEST(ent, field)) ++	    continue; ++	if (ent->re_url) { ++	    if (RegexMatch(ent->re_url, u->ptr, u->length, 1)) { ++		MatchedPosition(ent->re_url, &firstp, &lastp); ++		if (!ent->url_exact) ++		    goto url_found; ++		if (firstp != u->ptr || lastp == firstp) ++		    continue; ++		if (*lastp == 0 || *lastp == '?' || *(lastp - 1) == '?' || ++		    *lastp == '#' || *(lastp - 1) == '#') ++		    goto url_found; ++	    } ++	} else { ++	    int matchlen = strmatchlen(ent->url, u->ptr, u->length); ++	    if (matchlen == 0 || ent->url[matchlen] != 0) ++		continue; ++	    firstp = u->ptr; ++	    lastp = u->ptr + matchlen; ++	    if (*lastp == 0 || *lastp == '?' || *(lastp - 1) == '?' || ++		*lastp == '#' || *(lastp - 1) == '#') ++		goto url_found; ++	    if (!ent->url_exact && (*lastp == '/' || *(lastp - 1) == '/')) ++		goto url_found; ++	} ++    } ++    return NULL; ++ ++url_found: ++    switch (field) { ++    case SCONF_SUBSTITUTE_URL: ++	if (ent->substitute_url && *ent->substitute_url) { ++	    Str tmp = Strnew_charp_n(u->ptr, firstp - u->ptr); ++	    Strcat_charp(tmp, ent->substitute_url); ++	    Strcat_charp(tmp, lastp); ++	    return tmp->ptr; ++	} ++	return NULL; ++#ifdef USE_M17N ++    case SCONF_URL_CHARSET: ++	return &ent->url_charset; ++#endif ++    case SCONF_NO_REFERER_FROM: ++	return &ent->no_referer_from; ++    case SCONF_NO_REFERER_TO: ++	return &ent->no_referer_to; ++    } ++    return NULL; ++} +diff --git a/url.c b/url.c +index ed6062e..cbb4aab 100644 +--- a/url.c ++++ b/url.c +@@ -444,6 +444,8 @@ baseURL(Buffer *buf) + 	/* <BASE> tag is defined in the document */ + 	return buf->baseURL; +     } ++    else if (IS_EMPTY_PARSED_URL(&buf->currentURL)) ++	return NULL; +     else + 	return &buf->currentURL; + } +@@ -638,16 +640,21 @@ openSocket(char *const hostname, + #define COPYPATH_SPC_ALLOW 0 + #define COPYPATH_SPC_IGNORE 1 + #define COPYPATH_SPC_REPLACE 2 ++#define COPYPATH_SPC_MASK 3 ++#define COPYPATH_LOWERCASE 4 +  + static char * + copyPath(char *orgpath, int length, int option) + { +     Str tmp = Strnew(); +-    while (*orgpath && length != 0) { +-	if (IS_SPACE(*orgpath)) { +-	    switch (option) { ++    char ch; ++    while ((ch = *orgpath) != 0 && length != 0) { ++	if (option & COPYPATH_LOWERCASE) ++	    ch = TOLOWER(ch); ++	if (IS_SPACE(ch)) { ++	    switch (option & COPYPATH_SPC_MASK) { + 	    case COPYPATH_SPC_ALLOW: +-		Strcat_char(tmp, *orgpath); ++		Strcat_char(tmp, ch); + 		break; + 	    case COPYPATH_SPC_IGNORE: + 		/* do nothing */ +@@ -658,7 +665,7 @@ copyPath(char *orgpath, int length, int option) + 	    } + 	} + 	else +-	    Strcat_char(tmp, *orgpath); ++	    Strcat_char(tmp, ch); + 	orgpath++; + 	length--; +     } +@@ -668,22 +675,14 @@ copyPath(char *orgpath, int length, int option) + void + parseURL(char *url, ParsedURL *p_url, ParsedURL *current) + { +-    char *p, *q; ++    char *p, *q, *qq; +     Str tmp; +  +     url = url_quote(url);	/* quote 0x01-0x20, 0x7F-0xFF */ +  +     p = url; ++    copyParsedURL(p_url, NULL); +     p_url->scheme = SCM_MISSING; +-    p_url->port = 0; +-    p_url->user = NULL; +-    p_url->pass = NULL; +-    p_url->host = NULL; +-    p_url->is_nocache = 0; +-    p_url->file = NULL; +-    p_url->real_file = NULL; +-    p_url->query = NULL; +-    p_url->label = NULL; +  +     /* RFC1808: Relative Uniform Resource Locators +      * 4.  Resolving Relative URLs +@@ -694,7 +693,7 @@ parseURL(char *url, ParsedURL *p_url, ParsedURL *current) + 	goto do_label; +     } + #if defined( __EMX__ ) || defined( __CYGWIN__ ) +-    if (!strncmp(url, "file://localhost/", 17)) { ++    if (!strncasecmp(url, "file://localhost/", 17)) { + 	p_url->scheme = SCM_LOCAL; + 	p += 17 - 1; + 	url += 17 - 1; +@@ -802,19 +801,20 @@ parseURL(char *url, ParsedURL *p_url, ParsedURL *current) + 	/* scheme://user:pass@host or + 	 * scheme://host:port + 	 */ +-	p_url->host = copyPath(q, p - q, COPYPATH_SPC_IGNORE); ++	qq = q; + 	q = ++p; + 	while (*p && strchr("@/?#", *p) == NULL) + 	    p++; + 	if (*p == '@') { + 	    /* scheme://user:pass@...       */ ++	    p_url->user = copyPath(qq, q - 1 - qq, COPYPATH_SPC_IGNORE); + 	    p_url->pass = copyPath(q, p - q, COPYPATH_SPC_ALLOW); + 	    q = ++p; +-	    p_url->user = p_url->host; +-	    p_url->host = NULL; + 	    goto analyze_url; + 	} + 	/* scheme://host:port/ */ ++	p_url->host = copyPath(qq, q - 1 - qq, ++			       COPYPATH_SPC_IGNORE | COPYPATH_LOWERCASE); + 	tmp = Strnew_charp_n(q, p - q); + 	p_url->port = atoi(tmp->ptr); + 	/* *p is one of ['\0', '/', '?', '#'] */ +@@ -829,7 +829,8 @@ parseURL(char *url, ParsedURL *p_url, ParsedURL *current) +     case '/': +     case '?': +     case '#': +-	p_url->host = copyPath(q, p - q, COPYPATH_SPC_IGNORE); ++	p_url->host = copyPath(q, p - q, ++			       COPYPATH_SPC_IGNORE | COPYPATH_LOWERCASE); + 	p_url->port = DefaultPort[p_url->scheme]; + 	break; +     } +@@ -956,12 +957,16 @@ parseURL(char *url, ParsedURL *p_url, ParsedURL *current) + 	p_url->label = NULL; + } +  +-#define initParsedURL(p) bzero(p,sizeof(ParsedURL)) + #define ALLOC_STR(s) ((s)==NULL?NULL:allocStr(s,-1)) +  + void +-copyParsedURL(ParsedURL *p, ParsedURL *q) ++copyParsedURL(ParsedURL *p, const ParsedURL *q) + { ++    if (q == NULL) { ++	memset(p, 0, sizeof(ParsedURL)); ++	p->scheme = SCM_UNKNOWN; ++	return; ++    } +     p->scheme = q->scheme; +     p->port = q->port; +     p->is_nocache = q->is_nocache; +@@ -1283,6 +1288,8 @@ static char * + otherinfo(ParsedURL *target, ParsedURL *current, char *referer) + { +     Str s = Strnew(); ++    const int *no_referer_ptr; ++    int no_referer; +  +     Strcat_charp(s, "User-Agent: "); +     if (UserAgent == NULL || *UserAgent == '\0') +@@ -1306,7 +1313,12 @@ otherinfo(ParsedURL *target, ParsedURL *current, char *referer) + 	Strcat_charp(s, "Pragma: no-cache\r\n"); + 	Strcat_charp(s, "Cache-control: no-cache\r\n"); +     } +-    if (!NoSendReferer) { ++    no_referer = NoSendReferer; ++    no_referer_ptr = query_SCONF_NO_REFERER_FROM(current); ++    no_referer = NoSendReferer || (no_referer_ptr && *no_referer_ptr); ++    no_referer_ptr = query_SCONF_NO_REFERER_TO(target); ++    no_referer = no_referer || (no_referer_ptr && *no_referer_ptr); ++    if (!no_referer) { + #ifdef USE_SSL +         if (current && current->scheme == SCM_HTTPS && target->scheme != SCM_HTTPS) { + 	  /* Don't send Referer: if https:// -> http:// */ +@@ -1314,6 +1326,7 @@ otherinfo(ParsedURL *target, ParsedURL *current, char *referer) + 	else + #endif + 	if (referer == NULL && current && current->scheme != SCM_LOCAL && ++	    current->scheme != SCM_LOCAL_CGI && + 	    (current->scheme != SCM_FTP || + 	     (current->user == NULL && current->pass == NULL))) { + 	    char *p = current->label; +@@ -2234,3 +2247,66 @@ schemeToProxy(int scheme) +     } +     return pu; + } ++ ++#ifdef USE_M17N ++wc_ces ++url_to_charset(const char *url, const ParsedURL *base, wc_ces doc_charset) ++{ ++    const ParsedURL *pu; ++    ParsedURL pu_buf; ++    const wc_ces *csptr; ++ ++    if (url && *url && *url != '#') { ++	parseURL2((char *)url, &pu_buf, (ParsedURL *)base); ++	pu = &pu_buf; ++    } else { ++	pu = base; ++    } ++    if (pu && (pu->scheme == SCM_LOCAL || pu->scheme == SCM_LOCAL_CGI)) ++	return SystemCharset; ++    csptr = query_SCONF_URL_CHARSET(pu); ++    return (csptr && *csptr) ? *csptr : ++	doc_charset ? doc_charset : DocumentCharset; ++} ++ ++char * ++url_encode(const char *url, const ParsedURL *base, wc_ces doc_charset) ++{ ++    return url_quote_conv((char *)url, ++			  url_to_charset(url, base, doc_charset)); ++} ++ ++#if 0 /* unused */ ++char * ++url_decode(const char *url, const ParsedURL *base, wc_ces doc_charset) ++{ ++    if (!DecodeURL) ++	return (char *)url; ++    return url_unquote_conv((char *)url, ++			    url_to_charset(url, base, doc_charset)); ++} ++#endif ++ ++char * ++url_decode2(const char *url, const Buffer *buf) ++{ ++    wc_ces url_charset; ++ ++    if (!DecodeURL) ++	return (char *)url; ++    url_charset = buf ? ++	url_to_charset(url, baseURL((Buffer *)buf), buf->document_charset) : ++	url_to_charset(url, NULL, 0); ++    return url_unquote_conv((char *)url, url_charset); ++} ++ ++#else /* !defined(USE_M17N) */ ++ ++char * ++url_decode0(const char *url) ++{ ++    if (!DecodeURL) ++	return (char *)url; ++    return url_unquote_conv((char *)url, 0); ++} ++#endif /* !defined(USE_M17N) */ diff --git a/debian/patches/series b/debian/patches/series index 5c08fb1..60b3386 100644 --- a/debian/patches/series +++ b/debian/patches/series @@ -9,3 +9,4 @@  100_use-cppflags.patch  110_form-input-text.patch  120_sgrmouse.patch +130_siteconf.patch | 
