commit 5873df545400dbe2bf6028e1fc7074076a781e03
parent ae1fc6dc6a3ddae0c32d9351399cf82c66209a90
Author: ukai <ukai>
Date:   Wed,  5 Feb 2003 16:43:56 +0000
[w3m-dev 03730] display decoded URL
* anchor.c (link_list_panel): support DecodeURL
* display.c (make_lastline_link): support DecodeURL
* etc.c (url_unquote_conv): added
* fm.h (DecodeURL): added
* history.c (historyBuffer): support DecodeURL
* indep.c (QUOTE_MAP): added
	(HTML_QUOTE_MAP): added
	(html_quote_char): deleted
	(url_quote): use is_url_quote
	(file_quote): use is_file_quote
	(is_url_safe): deleted
	(Str_form_quote): use is_url_unsafe
	(Str_url_unquote): add safe args
	(is_shell_safe): delete
	(shell_quote): use is_shell_unsafe
* indep.h (QUOTE_MAP): added
	(HTML_QUOTE_MAP): added
	(HTML_QUOTE_MASK): added
	(SHELL_UNSAFE_MASK): added
	(URL_QUOTE_MASK): added
	(FILE_QUOTE_MASK): added
	(URL_UNSAFE_MASK): added
	(GET_QUOTE_TYPE): added
	(is_html_quote): added
	(is_shell_unsafe): added
	(is_url_quote): added
	(is_file_quote): added
	(is_url_unsafe): added
	(html_quote_char): added
	(html_quote_char): deleted
	(Str_url_unquote): added safe
	(form_unquote): Str_url_unquote changes
* linein.c (_prev): support DecodeURL
	(_next): ditto
* main.c (goURL0): support DecodeURL
	(_peekURL): ditto
	(curURL): ditto
* map.c (follow_map_panel): support DecodeURL
	(append_map_info): ditto
	(append_link_info): ditto
	(append_frame_info): ditto
	(page_info_panel): ditto
* menu.c (initSelectMenu): delete SCM_LOCAL_CGI
		support DecodeURL
	(initSelTabMenu): delete SCM_LOCAL_CGI
		support DecodeURL
	(link_menu): support DecodeURL
* parsetagx.c (parse_tag): is_html_quote
* proto.h (url_unquote_conv): added
* rc.c (CMT_DECODE_URL): added
	(params1): add decode_url
* url.c (openURL): Str_url_unquote non safe
From: Hironori SAKAMOTO <hsaka@mth.biglobe.ne.jp>
Diffstat:
| M | ChangeLog | | | 56 | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ | 
| M | anchor.c | | | 58 | ++++++++++++++++++++++++++++++++++++++++++++-------------- | 
| M | display.c | | | 25 | +++++++++++++++++++++---- | 
| M | etc.c | | | 13 | +++++++++++++ | 
| M | fm.h | | | 1 | + | 
| M | history.c | | | 8 | ++++++-- | 
| M | indep.c | | | 99 | ++++++++++++++++++++++++++++++++++++------------------------------------------- | 
| M | indep.h | | | 20 | +++++++++++++++++--- | 
| M | linein.c | | | 4 | ++++ | 
| M | main.c | | | 65 | +++++++++++++++++++++++++++++++++++++++++++++++++---------------- | 
| M | map.c | | | 82 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++------------------------ | 
| M | menu.c | | | 24 | ++++++++++++++++++------ | 
| M | parsetagx.c | | | 6 | +++--- | 
| M | proto.h | | | 4 | ++++ | 
| M | rc.c | | | 3 | +++ | 
| M | url.c | | | 2 | +- | 
16 files changed, 342 insertions(+), 128 deletions(-)
diff --git a/ChangeLog b/ChangeLog
@@ -1,5 +1,61 @@
 2003-02-06  Hironori SAKAMOTO <hsaka@mth.biglobe.ne.jp>
 
+	* [w3m-dev 03730] display decoded URL
+	* anchor.c (link_list_panel): support DecodeURL
+	* display.c (make_lastline_link): support DecodeURL
+	* etc.c (url_unquote_conv): added
+	* fm.h (DecodeURL): added
+	* history.c (historyBuffer): support DecodeURL
+	* indep.c (QUOTE_MAP): added
+		(HTML_QUOTE_MAP): added
+		(html_quote_char): deleted
+		(url_quote): use is_url_quote
+		(file_quote): use is_file_quote
+		(is_url_safe): deleted
+		(Str_form_quote): use is_url_unsafe
+		(Str_url_unquote): add safe args
+		(is_shell_safe): delete
+		(shell_quote): use is_shell_unsafe
+	* indep.h (QUOTE_MAP): added
+		(HTML_QUOTE_MAP): added
+		(HTML_QUOTE_MASK): added
+		(SHELL_UNSAFE_MASK): added
+		(URL_QUOTE_MASK): added
+		(FILE_QUOTE_MASK): added
+		(URL_UNSAFE_MASK): added
+		(GET_QUOTE_TYPE): added
+		(is_html_quote): added
+		(is_shell_unsafe): added
+		(is_url_quote): added
+		(is_file_quote): added
+		(is_url_unsafe): added
+		(html_quote_char): added
+		(html_quote_char): deleted
+		(Str_url_unquote): added safe
+		(form_unquote): Str_url_unquote changes
+	* linein.c (_prev): support DecodeURL
+		(_next): ditto
+	* main.c (goURL0): support DecodeURL
+		(_peekURL): ditto
+		(curURL): ditto
+	* map.c (follow_map_panel): support DecodeURL
+		(append_map_info): ditto
+		(append_link_info): ditto
+		(append_frame_info): ditto
+		(page_info_panel): ditto
+	* menu.c (initSelectMenu): delete SCM_LOCAL_CGI
+			support DecodeURL
+		(initSelTabMenu): delete SCM_LOCAL_CGI
+			support DecodeURL
+		(link_menu): support DecodeURL
+	* parsetagx.c (parse_tag): is_html_quote
+	* proto.h (url_unquote_conv): added
+	* rc.c (CMT_DECODE_URL): added
+		(params1): add decode_url
+	* url.c (openURL): Str_url_unquote non safe
+
+2003-02-06  Hironori SAKAMOTO <hsaka@mth.biglobe.ne.jp>
+
 	* [w3m-dev 03729] buf fix of reAnchorNewsheader()
 	* anchor.c (reAnchorAny): check l->bpos
 		 (reAnchorNewsheader): check l->bpos
diff --git a/anchor.c b/anchor.c
@@ -685,7 +685,7 @@ link_list_panel(Buffer *buf)
     Anchor *a;
     FormItemList *fi;
     int i;
-    char *t, *u;
+    char *t, *u, *p;
     ParsedURL pu;
     Str tmp = Strnew_charp("<title>Link List</title>\
 <h1 align=center>Link List</h1>\n");
@@ -700,10 +700,15 @@ link_list_panel(Buffer *buf)
 	for (l = buf->linklist; l; l = l->next) {
 	    if (l->url) {
 		parseURL2(l->url, &pu, baseURL(buf));
-		u = html_quote(parsedURL2Str(&pu)->ptr);
+		p = parsedURL2Str(&pu)->ptr;
+		u = html_quote(p);
+		if (DecodeURL)
+		    p = html_quote(url_unquote_conv(p, buf->document_code));
+		else
+		    p = u;
 	    }
 	    else
-		u = "";
+		u = p = "";
 	    if (l->type == LINK_TYPE_REL)
 		t = " [Rel]";
 	    else if (l->type == LINK_TYPE_REV)
@@ -712,7 +717,7 @@ link_list_panel(Buffer *buf)
 		t = "";
 	    t = Sprintf("%s%s\n", l->title ? l->title : "", t)->ptr;
 	    t = html_quote(t);
-	    Strcat_m_charp(tmp, "<li><a href=\"", u, "\">", t, "</a><br>", u,
+	    Strcat_m_charp(tmp, "<li><a href=\"", u, "\">", t, "</a><br>", p,
 			   "\n", NULL);
 	}
 	Strcat_charp(tmp, "</ol>\n");
@@ -726,10 +731,15 @@ link_list_panel(Buffer *buf)
 	    if (a->slave)
 		continue;
 	    parseURL2(a->url, &pu, baseURL(buf));
-	    u = html_quote(parsedURL2Str(&pu)->ptr);
+	    p = parsedURL2Str(&pu)->ptr;
+	    u = html_quote(p);
+	    if (DecodeURL)
+		 p = html_quote(url_unquote_conv(p, buf->document_code));
+	    else
+		 p = u;
 	    t = getAnchorText(buf, al, a);
 	    t = t ? html_quote(t) : "";
-	    Strcat_m_charp(tmp, "<li><a href=\"", u, "\">", t, "</a><br>", u,
+	    Strcat_m_charp(tmp, "<li><a href=\"", u, "\">", t, "</a><br>", p,
 			   "\n", NULL);
 	}
 	Strcat_charp(tmp, "</ol>\n");
@@ -743,10 +753,19 @@ link_list_panel(Buffer *buf)
 	    if (a->slave)
 		continue;
 	    parseURL2(a->url, &pu, baseURL(buf));
-	    u = html_quote(parsedURL2Str(&pu)->ptr);
-	    t = (a->title && *a->title) ? html_quote(a->title) :
-		html_quote(a->url);
-	    Strcat_m_charp(tmp, "<li><a href=\"", u, "\">", t, "</a><br>", u,
+	    p = parsedURL2Str(&pu)->ptr;
+	    u = html_quote(p);
+	    if (DecodeURL)
+		 p = html_quote(url_unquote_conv(p, buf->document_code));
+	    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_code));
+	    else
+		t = html_quote(a->url);
+	    Strcat_m_charp(tmp, "<li><a href=\"", u, "\">", t, "</a><br>", p,
 			   "\n", NULL);
 	    a = retrieveAnchor(buf->formitem, a->start.line, a->start.pos);
 	    if (!a)
@@ -766,11 +785,22 @@ link_list_panel(Buffer *buf)
 		    if (!m)
 			continue;
 		    parseURL2(m->url, &pu, baseURL(buf));
-		    u = html_quote(parsedURL2Str(&pu)->ptr);
-		    t = (m->alt && *m->alt) ? html_quote(m->alt) :
-			html_quote(m->url);
+		    p = parsedURL2Str(&pu)->ptr;
+		    u = html_quote(p);
+		    if (DecodeURL)
+			 p = html_quote(url_unquote_conv(p,
+							 buf->document_code));
+		    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_code));
+		    else
+			t = html_quote(m->url);
 		    Strcat_m_charp(tmp, "<li><a href=\"", u, "\">", t,
-				   "</a><br>", u, "\n", NULL);
+				   "</a><br>", p, "\n", NULL);
 		}
 		Strcat_charp(tmp, "</ol>\n");
 	    }
diff --git a/display.c b/display.c
@@ -235,9 +235,12 @@ static Str
 make_lastline_link(Buffer *buf, char *title, char *url)
 {
     Str s = NULL, u;
+#ifdef JP_CHARSET
+    Lineprop *pr;
+#endif
     ParsedURL pu;
     char *p;
-    int l = COLS - 1;
+    int l = COLS - 1, i;
 
     if (title && *title) {
 	s = Strnew_m_charp("[", title, "]", NULL);
@@ -255,6 +258,11 @@ make_lastline_link(Buffer *buf, char *title, char *url)
 	return s;
     parseURL2(url, &pu, baseURL(buf));
     u = parsedURL2Str(&pu);
+    if (DecodeURL)
+	u = Strnew_charp(url_unquote_conv(u->ptr, Currentbuf->document_code));
+#ifdef JP_CHARSET
+    u = checkType(u, &pr, NULL);
+#endif
     if (l <= 4 || l >= u->length) {
 	if (!s)
 	    return u;
@@ -263,14 +271,23 @@ make_lastline_link(Buffer *buf, char *title, char *url)
     }
     if (!s)
 	s = Strnew_size(COLS);
-    Strcat_charp_n(s, u->ptr, (l - 2) / 2);
+    i = (l - 2) / 2;
+#ifdef JP_CHARSET
+    if (CharType(pr[i]) == PC_KANJI2)
+	i--;
+#endif
+    Strcat_charp_n(s, u->ptr, i);
 #if LANG == JA
     Strcat_charp(s, "…");
 #else				/* LANG != JA */
     Strcat_charp(s, "..");
 #endif				/* LANG != JA */
-    l = COLS - 1 - s->length;
-    Strcat_charp(s, &u->ptr[u->length - l]);
+    i = u->length - (COLS - 1 - s->length);
+#ifdef JP_CHARSET
+    if (CharType(pr[i]) == PC_KANJI2)
+	i++;
+#endif
+    Strcat_charp(s, &u->ptr[i]);
     return s;
 }
 
diff --git a/etc.c b/etc.c
@@ -1636,6 +1636,19 @@ file_to_url(char *file)
     return tmp->ptr;
 }
 
+char *
+url_unquote_conv(char *url, char code)
+{
+    Str tmp;
+    tmp = Str_url_unquote(Strnew_charp(url), FALSE, TRUE);
+#ifdef JP_CHARSET
+    if (code == CODE_INNER_EUC)
+	code = CODE_EUC;
+    tmp = convertLine(NULL, tmp, &code, RAW_MODE);
+#endif
+    return tmp->ptr;
+}
+
 static char *tmpf_base[MAX_TMPF_TYPE] = {
     "tmp", "src", "frame", "cache", "cookie",
 };
diff --git a/fm.h b/fm.h
@@ -915,6 +915,7 @@ global int nextpage_topline init(FALSE);
 global char *displayTitleTerm init(NULL);
 global int displayLink init(FALSE);
 global int displayLineInfo init(FALSE);
+global int DecodeURL init(FALSE);
 global int retryAsHttp init(TRUE);
 global int showLineNum init(FALSE);
 global int show_srch_str init(TRUE);
diff --git a/history.c b/history.c
@@ -7,7 +7,7 @@ historyBuffer(Hist *hist)
 {
     Str src = Strnew();
     HistItem *item;
-    char *q;
+    char *p, *q;
 
     Strcat_charp(src, "<html>\n<head><title>History Page</title></head>\n");
     Strcat_charp(src, "<body>\n<h1>History Page</h1>\n<hr>\n");
@@ -15,10 +15,14 @@ historyBuffer(Hist *hist)
     if (hist && hist->list) {
 	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));
+	    else
+		p = q;
 	    Strcat_charp(src, "<li><a href=\"");
 	    Strcat_charp(src, q);
 	    Strcat_charp(src, "\">");
-	    Strcat_charp(src, q);
+	    Strcat_charp(src, p);
 	    Strcat_charp(src, "</a>\n");
 	}
     }
diff --git a/indep.c b/indep.c
@@ -11,6 +11,45 @@
 #include "myctype.h"
 #include "entity.h"
 
+unsigned char QUOTE_MAP[0x100] = {
+/* NUL SOH STX ETX EOT ENQ ACK BEL  BS  HT  LF  VT  FF  CR  SO  SI */
+    24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
+/* DLE DC1 DC2 DC3 DC4 NAK SYN ETB CAN  EM SUB ESC  FS  GS  RS  US */
+    24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
+/* SPC   !   "   #   $   %   &   '   (   )   *   +   ,   -   .   / */
+    24, 72, 76, 40,  8, 40, 41, 72, 72, 72, 72, 40, 72,  8,  0, 64,
+/*   0   1   2   3   4   5   6   7   8   9   :   ;   <   =   >   ? */
+     0,  0,  0,  0,  0,  0,  0,  0,  0,  0, 32, 72, 74, 72, 75, 40,
+/*   @   A   B   C   D   E   F   G   H   I   J   K   L   M   N   O */
+    72,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
+/*   P   Q   R   S   T   U   V   W   X   Y   Z   [   \   ]   ^   _ */
+     0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0, 72, 72, 72, 72,  0,
+/*   `   a   b   c   d   e   f   g   h   i   j   k   l   m   n   o */
+    72,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
+/*   p   q   r   s   t   u   v   w   x   y   z   {   |   }   ~ DEL */
+     0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0, 72, 72, 72, 72, 24,
+
+    16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
+    16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
+    16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
+    16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
+    16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
+    16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
+    16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
+    16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
+};
+
+char *HTML_QUOTE_MAP[] = {
+    NULL,
+    "&",
+    "<",
+    ">",
+    """,
+    NULL,
+    NULL,
+    NULL,
+};
+
 clen_t
 strtoclen(const char *s)
 {
@@ -445,22 +484,6 @@ getescapecmd(char **s)
 }
 
 char *
-html_quote_char(char c)
-{
-    switch (c) {
-    case '&':
-	return "&";
-    case '<':
-	return "<";
-    case '>':
-	return ">";
-    case '"':
-	return """;
-    }
-    return NULL;
-}
-
-char *
 html_quote(char *str)
 {
     Str tmp = NULL;
@@ -522,7 +545,7 @@ url_quote(char *str)
     char *p;
 
     for (p = str; *p; p++) {
-	if (IS_CNTRL(*p) || *p == ' ' || !IS_ASCII(*p)) {
+	if (is_url_quote(*p)) {
 	    if (tmp == NULL)
 		tmp = Strnew_charp_n(str, (int)(p - str));
 	    Strcat_char(tmp, '%');
@@ -547,8 +570,7 @@ file_quote(char *str)
     char buf[4];
 
     for (p = str; *p; p++) {
-	if (IS_CNTRL(*p) || *p == ' ' || !IS_ASCII(*p) || *p == '+' ||
-	    *p == ':' || *p == '#' || *p == '?' || *p == '&' || *p == '%') {
+	if (is_file_quote(*p)) {
 	    if (tmp == NULL)
 		tmp = Strnew_charp_n(str, (int)(p - str));
 	    sprintf(buf, "%%%02X", (unsigned char)*p);
@@ -593,22 +615,6 @@ file_unquote(char *str)
     return str;
 }
 
-/* rfc1808 safe */
-static int
-is_url_safe(char c)
-{
-    switch (c) {
-	/* safe */
-    case '$':
-    case '-':
-    case '_':
-    case '.':
-	return 1;
-    default:
-	return IS_ALNUM(c);
-    }
-}
-
 Str
 Str_form_quote(Str x)
 {
@@ -622,7 +628,7 @@ Str_form_quote(Str x)
 		tmp = Strnew_charp_n(x->ptr, (int)(p - x->ptr));
 	    Strcat_char(tmp, '+');
 	}
-	else if (!is_url_safe(*p)) {
+	else if (is_url_unsafe(*p)) {
 	    if (tmp == NULL)
 		tmp = Strnew_charp_n(x->ptr, (int)(p - x->ptr));
 	    sprintf(buf, "%%%02X", (unsigned char)*p);
@@ -640,7 +646,7 @@ Str_form_quote(Str x)
 
 
 Str
-Str_url_unquote(Str x, int is_form)
+Str_url_unquote(Str x, int is_form, int safe)
 {
     Str tmp = NULL;
     char *p = x->ptr, *ep = x->ptr + x->length, *q;
@@ -657,7 +663,7 @@ Str_url_unquote(Str x, int is_form)
 	else if (*p == '%') {
 	    q = p;
 	    c = url_unquote_char(&q);
-	    if (c >= 0) {
+	    if (c >= 0 && (!safe || !IS_ASCII(c) || !is_file_quote(c))) {
 		if (tmp == NULL)
 		    tmp = Strnew_charp_n(x->ptr, (int)(p - x->ptr));
 		Strcat_char(tmp, (char)c);
@@ -674,21 +680,6 @@ Str_url_unquote(Str x, int is_form)
     return x;
 }
 
-static int
-is_shell_safe(char c)
-{
-    switch (c) {
-	/* safe */
-    case '/':
-    case '.':
-    case '_':
-    case ':':
-	return 1;
-    default:
-	return IS_ALNUM(c) || (c & 0x80);
-    }
-}
-
 char *
 shell_quote(char *str)
 {
@@ -696,7 +687,7 @@ shell_quote(char *str)
     char *p;
 
     for (p = str; *p; p++) {
-	if (!is_shell_safe(*p)) {
+	if (is_shell_unsafe(*p)) {
 	    if (tmp == NULL)
 		tmp = Strnew_charp_n(str, (int)(p - str));
 	    Strcat_char(tmp, '\\');
diff --git a/indep.h b/indep.h
@@ -17,6 +17,21 @@
 #define HTML_MODE	2
 #define HEADER_MODE	3
 
+extern unsigned char QUOTE_MAP[];
+extern char *HTML_QUOTE_MAP[];
+#define HTML_QUOTE_MASK   0x07	/* &, <, >, " */
+#define SHELL_UNSAFE_MASK 0x08	/* [^A-Za-z0-9_./:\200-\377] */
+#define URL_QUOTE_MASK    0x10	/* [\0- \177-\377] */
+#define FILE_QUOTE_MASK   0x30	/* [\0- #%&+:?\177-\377] */
+#define URL_UNSAFE_MASK   0x70	/* [^A-Za-z0-9_$\-.] */
+#define GET_QUOTE_TYPE(c) QUOTE_MAP[(int)(unsigned char)(c)]
+#define is_html_quote(c)   (GET_QUOTE_TYPE(c) & HTML_QUOTE_MASK)
+#define is_shell_unsafe(c) (GET_QUOTE_TYPE(c) & SHELL_UNSAFE_MASK)
+#define is_url_quote(c)    (GET_QUOTE_TYPE(c) & URL_QUOTE_MASK)
+#define is_file_quote(c)   (GET_QUOTE_TYPE(c) & FILE_QUOTE_MASK)
+#define is_url_unsafe(c)   (GET_QUOTE_TYPE(c) & URL_UNSAFE_MASK)
+#define html_quote_char(c) HTML_QUOTE_MAP[(int)is_html_quote(c)]
+
 extern clen_t strtoclen(const char *s);
 extern char *conv_entity(int ch);
 extern int getescapechar(char **s);
@@ -40,15 +55,14 @@ extern int strcasemstr(char *str, char *srch[], char **ret_ptr);
 extern char *remove_space(char *str);
 extern int non_null(char *s);
 extern void cleanup_line(Str s, int mode);
-extern char *html_quote_char(char c);
 extern char *html_quote(char *str);
 extern char *html_unquote(char *str);
 extern char *file_quote(char *str);
 extern char *file_unquote(char *str);
 extern char *url_quote(char *str);
-extern Str Str_url_unquote(Str x, int is_form);
+extern Str Str_url_unquote(Str x, int is_form, int safe);
 extern Str Str_form_quote(Str x);
-#define Str_form_unquote(x) Str_url_unquote((x), TRUE)
+#define Str_form_unquote(x) Str_url_unquote((x), TRUE, FALSE)
 extern char *shell_quote(char *str);
 
 extern char *w3m_auxbin_dir();
diff --git a/linein.c b/linein.c
@@ -1058,6 +1058,8 @@ _prev(void)
 	    return;
 	strCurrentBuf = strBuf;
     }
+    if (DecodeURL)
+	p = url_unquote_conv(p, 0);
     strBuf = Strnew_charp(p);
     CLen = CPos = setStrType(strBuf, strProp);
     offset = 0;
@@ -1075,6 +1077,8 @@ _next(void)
 	return;
     p = nextHist(hist);
     if (p) {
+	if (DecodeURL)
+	    p = url_unquote_conv(p, 0);
 	strBuf = Strnew_charp(p);
     }
     else {
diff --git a/main.c b/main.c
@@ -3878,8 +3878,11 @@ goURL0(char *prompt, int relative)
 	current = baseURL(Currentbuf);
 	if (current) {
 	    char *c_url = parsedURL2Str(current)->ptr;
-	    if (DefaultURLString == DEFAULT_URL_CURRENT)
+	    if (DefaultURLString == DEFAULT_URL_CURRENT) {
 		url = c_url;
+	        if (DecodeURL)
+		    url = url_unquote_conv(url, 0);
+	    }
 	    else
 		pushHist(hist, c_url);
 	}
@@ -3888,8 +3891,11 @@ 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)
+	    if (DefaultURLString == DEFAULT_URL_LINK) {
 		url = a_url;
+	        if (DecodeURL)
+		    url = url_unquote_conv(url, Currentbuf->document_code);
+	    }
 	    else
 		pushHist(hist, a_url);
 	}
@@ -4295,6 +4301,10 @@ _peekURL(int only_img)
     Anchor *a;
     ParsedURL pu;
     static Str s = NULL;
+#ifdef JP_CHARSET
+    static Lineprop *p = NULL;
+    Lineprop *pp;
+#endif
     static int offset = 0, n;
 
     if (Currentbuf->firstLine == NULL)
@@ -4309,29 +4319,38 @@ _peekURL(int only_img)
     else {
 	offset = 0;
     }
+    s = NULL;
     a = (only_img ? NULL : retrieveCurrentAnchor(Currentbuf));
     if (a == NULL) {
 	a = (only_img ? NULL : retrieveCurrentForm(Currentbuf));
 	if (a == NULL) {
 	    a = retrieveCurrentImg(Currentbuf);
-	    if (a == NULL) {
-		s = NULL;
+	    if (a == NULL)
 		return;
-	    }
 	}
-	else {
+	else
 	    s = Strnew_charp(form2str((FormItemList *)a->url));
-	    goto disp;
-	}
     }
-    parseURL2(a->url, &pu, baseURL(Currentbuf));
-    s = parsedURL2Str(&pu);
+    if (s == NULL) {
+	parseURL2(a->url, &pu, baseURL(Currentbuf));
+	s = parsedURL2Str(&pu);
+    }
+    if (DecodeURL)
+	s = Strnew_charp(url_unquote_conv(s->ptr, Currentbuf->document_code));
+#ifdef JP_CHARSET
+    s = checkType(s, &pp, NULL);
+    p = NewAtom_N(Lineprop, s->length);
+    bcopy((void *)pp, (void *)p, s->length * sizeof(Lineprop));
+#endif
   disp:
     n = searchKeyNum();
     if (n > 1 && s->length > (n - 1) * (COLS - 1))
-	disp_message_nomouse(&s->ptr[(n - 1) * (COLS - 1)], TRUE);
-    else
-	disp_message_nomouse(&s->ptr[offset], TRUE);
+	offset = (n - 1) * (COLS - 1);
+#ifdef JP_CHARSET
+    if (CharType(p[offset]) == PC_KANJI2)
+	offset++;
+#endif
+    disp_message_nomouse(&s->ptr[offset], TRUE);
 }
 
 /* peek URL */
@@ -4361,6 +4380,10 @@ void
 curURL(void)
 {
     static Str s = NULL;
+#ifdef JP_CHARSET
+    static Lineprop *p = NULL;
+    Lineprop *pp;
+#endif
     static int offset = 0, n;
 
     if (Currentbuf->bufferprop & BP_INTERNAL)
@@ -4374,12 +4397,22 @@ curURL(void)
     else {
 	offset = 0;
 	s = currentURL();
+	if (DecodeURL)
+	    s = Strnew_charp(url_unquote_conv(s->ptr, 0));
+#ifdef JP_CHARSET
+	s = checkType(s, &pp, NULL);
+	p = NewAtom_N(Lineprop, s->length);
+	bcopy((void *)pp, (void *)p, s->length * sizeof(Lineprop));
+#endif
     }
     n = searchKeyNum();
     if (n > 1 && s->length > (n - 1) * (COLS - 1))
-	disp_message_nomouse(&s->ptr[(n - 1) * (COLS - 1)], TRUE);
-    else
-	disp_message_nomouse(&s->ptr[offset], TRUE);
+	offset = (n - 1) * (COLS - 1);
+#ifdef JP_CHARSET
+    if (CharType(p[offset]) == PC_KANJI2)
+	offset++;
+#endif
+    disp_message_nomouse(&s->ptr[offset], TRUE);
 }
 
 /* view HTML source */
diff --git a/map.c b/map.c
@@ -263,7 +263,7 @@ follow_map_panel(Buffer *buf, char *name)
     ListItem *al;
     MapArea *a;
     ParsedURL pu;
-    char *url;
+    char *p, *q;
 
     ml = searchMapList(buf, name);
     if (ml == NULL)
@@ -275,10 +275,15 @@ follow_map_panel(Buffer *buf, char *name)
 	if (!a)
 	    continue;
 	parseURL2(a->url, &pu, baseURL(buf));
-	url = html_quote(parsedURL2Str(&pu)->ptr);
-	Strcat_m_charp(mappage, "<tr valign=top><td><a href=\"", url, "\">",
+	p = parsedURL2Str(&pu)->ptr;
+	q = html_quote(p);
+        if (DecodeURL)
+	    p = html_quote(url_unquote_conv(p, buf->document_code));
+        else
+	    p = q;
+	Strcat_m_charp(mappage, "<tr valign=top><td><a href=\"", q, "\">",
 		       html_quote(*a->alt ? a->alt : mybasename(a->url)),
-		       "</a><td>", url, NULL);
+		       "</a><td>", p, NULL);
     }
     Strcat_charp(mappage, "</table></body></html>");
 
@@ -391,7 +396,7 @@ append_map_info(Buffer *buf, Str tmp, FormItemList *fi)
     ListItem *al;
     MapArea *a;
     ParsedURL pu;
-    char *url;
+    char *p, *q;
 
     ml = searchMapList(buf, fi->value ? fi->value->ptr : NULL);
     if (ml == NULL)
@@ -405,11 +410,15 @@ append_map_info(Buffer *buf, Str tmp, FormItemList *fi)
 	if (!a)
 	    continue;
 	parseURL2(a->url, &pu, baseURL(buf));
-	url = html_quote(parsedURL2Str(&pu)->ptr);
+	q = html_quote(parsedURL2Str(&pu)->ptr);
+        if (DecodeURL)
+	    p = html_quote(url_unquote_conv(a->url, buf->document_code));
+        else
+	    p = html_quote(a->url);
 	Strcat_m_charp(tmp, "<tr valign=top><td>  <td><a href=\"",
-		       url, "\">",
+		       q, "\">",
 		       html_quote(*a->alt ? a->alt : mybasename(a->url)),
-		       "</a><td>", html_quote(a->url), "\n", NULL);
+		       "</a><td>", p, "\n", NULL);
     }
     Strcat_charp(tmp, "</table>");
 }
@@ -440,8 +449,13 @@ append_link_info(Buffer *buf, Str html, LinkList * link)
 	    Strcat_charp(html, "[Rel]");
 	else if (l->type == LINK_TYPE_REV)
 	    Strcat_charp(html, "[Rev]");
-	Strcat_m_charp(html, "<td>", l->url ? html_quote(l->url) : "(empty)",
-		       NULL);
+	if (!l->url)
+	    url = "(empty)";
+        else if (DecodeURL)
+	    url = html_quote(url_unquote_conv(l->url, buf->document_code));
+	else
+	    url = html_quote(l->url);
+	Strcat_m_charp(html, "<td>", url, NULL);
 	if (l->ctype)
 	    Strcat_m_charp(html, " (", html_quote(l->ctype), ")", NULL);
 	Strcat_charp(html, "\n");
@@ -473,14 +487,16 @@ append_frame_info(Buffer *buf, Str html, struct frameset *set, int level)
 		q = html_quote(frame.body->url);
 		Strcat_m_charp(html, "<a href=\"", q, "\">", NULL);
 		if (frame.body->name) {
-		    p = file_unquote(frame.body->name);
-#ifdef JP_CHARSET
-		    p = conv(p, buf->document_code, InnerCode)->ptr;
-#endif
-		    p = html_quote(p);
+		    p = html_quote(url_unquote_conv(frame.body->name,
+						    buf->document_code));
 		    Strcat_charp(html, p);
 		}
-		Strcat_m_charp(html, " ", q, "</a></pre_int><br>\n", NULL);
+		if (DecodeURL)
+		    p = html_quote(url_unquote_conv(frame.body->url,
+						    buf->document_code));
+		else
+		    p = q;
+		Strcat_m_charp(html, " ", p, "</a></pre_int><br>\n", NULL);
 #ifdef USE_SSL
 		if (frame.body->ssl_certificate)
 		    Strcat_m_charp(html,
@@ -505,11 +521,11 @@ page_info_panel(Buffer *buf)
 {
     Str tmp = Strnew_size(1024);
     Anchor *a;
-    Str s;
     ParsedURL pu;
     TextListItem *ti;
     struct frameset *f_set = NULL;
     int all;
+    char *p, *q;
 
     Strcat_charp(tmp, "<html><head>\
 <title>Information about current page</title>\
@@ -520,11 +536,14 @@ page_info_panel(Buffer *buf)
     all = buf->allLine;
     if (all == 0 && buf->lastLine)
 	all = buf->lastLine->linenumber;
+    p = parsedURL2Str(&buf->currentURL)->ptr;
+    if (DecodeURL)
+	p = url_unquote_conv(p, 0);
     Strcat_m_charp(tmp, "<table cellpadding=0>",
 		   "<tr valign=top><td nowrap>Title<td>",
 		   html_quote(buf->buffername),
 		   "<tr valign=top><td nowrap>Current URL<td>",
-		   html_quote(parsedURL2Str(&buf->currentURL)->ptr),
+		   html_quote(p),
 		   "<tr valign=top><td nowrap>Document Type<td>",
 		   buf->real_type ? html_quote(buf->real_type) : "unknown",
 		   "<tr valign=top><td nowrap>Last Modified<td>",
@@ -540,29 +559,42 @@ page_info_panel(Buffer *buf)
 
     a = retrieveCurrentAnchor(buf);
     if (a != NULL) {
-	char *aurl;
 	parseURL2(a->url, &pu, baseURL(buf));
-	s = parsedURL2Str(&pu);
-	aurl = html_quote(s->ptr);
+	p = parsedURL2Str(&pu)->ptr;
+	q = html_quote(p);
+	if (DecodeURL)
+	    p = html_quote(url_unquote_conv(p, buf->document_code));
+	else
+	    p = q;
 	Strcat_m_charp(tmp,
 		       "<tr valign=top><td nowrap>URL of current anchor<td><a href=\"",
-		       aurl, "\">", aurl, "</a>", NULL);
+		       q, "\">", p, "</a>", NULL);
     }
     a = retrieveCurrentImg(buf);
     if (a != NULL) {
 	parseURL2(a->url, &pu, baseURL(buf));
-	s = parsedURL2Str(&pu);
+	p = parsedURL2Str(&pu)->ptr;
+	q = html_quote(p);
+	if (DecodeURL)
+	    p = html_quote(url_unquote_conv(p, buf->document_code));
+	else
+	    p = q;
 	Strcat_m_charp(tmp,
 		       "<tr valign=top><td nowrap>URL of current image<td><a href=\"",
-		       html_quote(s->ptr), "\">", html_quote(s->ptr), "</a>",
+		       q, "\">", p, "</a>",
 		       NULL);
     }
     a = retrieveCurrentForm(buf);
     if (a != NULL) {
 	FormItemList *fi = (FormItemList *)a->url;
+	p = form2str(fi);
+	if (DecodeURL)
+	    p = html_quote(url_unquote_conv(p, buf->document_code));
+	else
+	    p = html_quote(p);
 	Strcat_m_charp(tmp,
 		       "<tr valign=top><td nowrap>Method/type of current form <td>",
-		       html_quote(form2str(fi)), NULL);
+		       p, NULL);
 	if (fi->parent->method == FORM_METHOD_INTERNAL
 	    && !Strcmp_charp(fi->parent->action, "map"))
 	    append_map_info(buf, tmp, fi->parent->item);
diff --git a/menu.c b/menu.c
@@ -1379,6 +1379,7 @@ initSelectMenu(void)
     Buffer *buf;
     Str str;
     char **label;
+    char *p;
     static char *comment = " SPC for select / D for delete buffer ";
 
     SelectV = -1;
@@ -1394,7 +1395,6 @@ initSelectMenu(void)
 	if (buf->filename != NULL) {
 	    switch (buf->currentURL.scheme) {
 	    case SCM_LOCAL:
-	    case SCM_LOCAL_CGI:
 		if (strcmp(buf->currentURL.file, "-")) {
 		    Strcat_char(str, ' ');
 		    Strcat_charp(str,
@@ -1406,7 +1406,10 @@ initSelectMenu(void)
 		break;
 	    default:
 		Strcat_char(str, ' ');
-		Strcat(str, parsedURL2Str(&buf->currentURL));
+		p = parsedURL2Str(&buf->currentURL)->ptr;
+		if (DecodeURL)
+		    p = url_unquote_conv(p, 0);
+		Strcat_charp(str, p);
 		break;
 	    }
 	}
@@ -1525,6 +1528,7 @@ initSelTabMenu(void)
     Buffer *buf;
     Str str;
     char **label;
+    char *p;
     static char *comment = " SPC for select / D for delete tab ";
 
     SelTabV = -1;
@@ -1541,7 +1545,6 @@ initSelTabMenu(void)
 	if (buf->filename != NULL) {
 	    switch (buf->currentURL.scheme) {
 	    case SCM_LOCAL:
-	    case SCM_LOCAL_CGI:
 		if (strcmp(buf->currentURL.file, "-")) {
 		    Strcat_char(str, ' ');
 		    Strcat_charp(str,
@@ -1552,8 +1555,10 @@ initSelTabMenu(void)
 	    case SCM_MISSING:
 		break;
 	    default:
-		Strcat_char(str, ' ');
-		Strcat(str, parsedURL2Str(&buf->currentURL));
+		p = parsedURL2Str(&buf->currentURL)->ptr;
+		if (DecodeURL)
+		    p = url_unquote_conv(p, 0);
+		Strcat_charp(str, p);
 		break;
 	    }
 	}
@@ -1827,6 +1832,7 @@ link_menu(Buffer *buf)
     int i, nitem, len = 0, linkV = -1;
     char **label;
     Str str;
+    char *p;
 
     if (!buf->linklist)
 	return NULL;
@@ -1843,7 +1849,13 @@ link_menu(Buffer *buf)
 	    Strcat_charp(str, " [Rev] ");
 	else
 	    Strcat_charp(str, " ");
-	Strcat_charp(str, l->url ? l->url : "");
+	if (!l->url)
+	    p = "";
+	else if (DecodeURL)
+	    p = url_unquote_conv(l->url, buf->document_code);
+	else
+	    p = l->url;
+	Strcat_charp(str, p);
 	label[i] = str->ptr;
 	if (len < str->length)
 	    len = str->length;
diff --git a/parsetagx.c b/parsetagx.c
@@ -182,7 +182,7 @@ parse_tag(char **s, int internal)
 		while (*q && *q != '"') {
 		    if (*q != '\n')
 			Strcat_char(value, *q);
-		    if (!tag->need_reconstruct && html_quote_char(*q))
+		    if (!tag->need_reconstruct && is_html_quote(*q))
 			tag->need_reconstruct = TRUE;
 		    q++;
 		}
@@ -194,7 +194,7 @@ parse_tag(char **s, int internal)
 		while (*q && *q != '\'') {
 		    if (*q != '\n')
 			Strcat_char(value, *q);
-		    if (!tag->need_reconstruct && html_quote_char(*q))
+		    if (!tag->need_reconstruct && is_html_quote(*q))
 			tag->need_reconstruct = TRUE;
 		    q++;
 		}
@@ -204,7 +204,7 @@ parse_tag(char **s, int internal)
 	    else if (*q) {
 		while (*q && !IS_SPACE(*q) && *q != '>') {
 		    Strcat_char(value, *q);
-		    if (!tag->need_reconstruct && html_quote_char(*q))
+		    if (!tag->need_reconstruct && is_html_quote(*q))
 			tag->need_reconstruct = TRUE;
 		    q++;
 		}
diff --git a/proto.h b/proto.h
@@ -600,6 +600,10 @@ extern void mySystem(char *command, int background);
 extern Str myExtCommand(char *cmd, char *arg, int redirect);
 extern Str myEditor(char *cmd, char *file, int line);
 extern char *file_to_url(char *file);
+#ifndef JP_CHARSET
+#define url_unquote_conv(x,y) _url_unquote_conv(x)
+#endif
+extern char *url_unquote_conv(char *url, char code);
 extern char *expandName(char *name);
 extern Str tmpfname(int type, char *ext);
 extern time_t mymktime(char *timestr);
diff --git a/rc.c b/rc.c
@@ -66,6 +66,7 @@ static int RC_table_size;
 #define CMT_OPEN_TAB_BLANK "targetが_blankか_newの場合は新しいタブで開く"
 #define CMT_OPEN_TAB_DL_LIST "Download list panel を新しいタブで開く"
 #define CMT_DISPLINK     "リンク先の自動表示"
+#define CMT_DECODE_URL   "URLをデコードして表示"
 #define CMT_DISPLINEINFO "現在の行番号の表示"
 #define CMT_DISP_IMAGE   "インライン画像を表示"
 #ifdef USE_IMAGE
@@ -230,6 +231,7 @@ static int RC_table_size;
 #define CMT_OPEN_TAB_BLANK "Open link on new tab if target is _blank or _new"
 #define CMT_OPEN_TAB_DL_LIST "Open download list panel on new tab"
 #define CMT_DISPLINK     "Display link URL automatically"
+#define CMT_DECODE_URL   "Display decoded URL"
 #define CMT_DISPLINEINFO "Display current line number"
 #define CMT_DISP_IMAGE   "Display inline images"
 #ifdef USE_IMAGE
@@ -525,6 +527,7 @@ struct param_ptr params1[] = {
      CMT_OPEN_TAB_DL_LIST, NULL},
     {"display_link", P_INT, PI_ONOFF, (void *)&displayLink, CMT_DISPLINK,
      NULL},
+    {"decode_url", P_INT, PI_ONOFF, (void *)&DecodeURL, CMT_DECODE_URL, NULL},
     {"display_lineinfo", P_INT, PI_ONOFF, (void *)&displayLineInfo,
      CMT_DISPLINEINFO, NULL},
     {"ext_dirlist", P_INT, PI_ONOFF, (void *)&UseExternalDirBuffer,
diff --git a/url.c b/url.c
@@ -1776,7 +1776,7 @@ openURL(char *url, ParsedURL *pu, ParsedURL *current,
 	    uf.encoding = ENC_BASE64;
 	}
 	else
-	    tmp = Str_url_unquote(tmp, FALSE);
+	    tmp = Str_url_unquote(tmp, FALSE, FALSE);
 	uf.stream = newStrStream(tmp);
 	uf.guess_type = (*p != '\0') ? p : "text/plain";
 	return uf;