aboutsummaryrefslogtreecommitdiffstats
path: root/debian/patches/130_siteconf.patch
diff options
context:
space:
mode:
Diffstat (limited to 'debian/patches/130_siteconf.patch')
-rw-r--r--debian/patches/130_siteconf.patch2051
1 files changed, 2051 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>&nbsp;&nbsp;<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&nbsp;<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) */