about summary refs log tree commit diff stats
path: root/src
diff options
context:
space:
mode:
authorThomas E. Dickey <dickey@invisible-island.net>2000-06-23 16:13:42 -0400
committerThomas E. Dickey <dickey@invisible-island.net>2000-06-23 16:13:42 -0400
commitfe76940414337a9058df2d426bf5527154ca283a (patch)
treebdc0234689e8778492902fd6939d18a139f0b520 /src
parent03239fe7d0beedb4b52c96d56729d24fa0db0405 (diff)
downloadlynx-snapshots-fe76940414337a9058df2d426bf5527154ca283a.tar.gz
snapshot of project "lynx", label v2-8-4dev_4
Diffstat (limited to 'src')
-rw-r--r--src/GridText.c195
-rw-r--r--src/HTAlert.c19
-rw-r--r--src/HTFWriter.c6
-rw-r--r--src/HTML.c7
-rw-r--r--src/LYBookmark.c8
-rw-r--r--src/LYCgi.c5
-rw-r--r--src/LYCookie.c30
-rw-r--r--src/LYCurses.c3
-rw-r--r--src/LYCurses.h33
-rw-r--r--src/LYExtern.c15
-rw-r--r--src/LYExtern.h3
-rw-r--r--src/LYForms.c9
-rw-r--r--src/LYGetFile.c2
-rw-r--r--src/LYGlobalDefs.h6
-rw-r--r--src/LYKeymap.c242
-rw-r--r--src/LYKeymap.h9
-rw-r--r--src/LYLocal.c31
-rw-r--r--src/LYMail.c10
-rw-r--r--src/LYMain.c34
-rw-r--r--src/LYMainLoop.c114
-rw-r--r--src/LYMainLoop.h3
-rw-r--r--src/LYOptions.c81
-rw-r--r--src/LYPrint.c4
-rw-r--r--src/LYReadCFG.c16
-rw-r--r--src/LYStrings.c142
-rw-r--r--src/LYStrings.h19
-rw-r--r--src/LYStructs.h7
-rw-r--r--src/LYStyle.c4
-rw-r--r--src/LYUtils.c11
-rw-r--r--src/LYexit.c3
30 files changed, 813 insertions, 258 deletions
diff --git a/src/GridText.c b/src/GridText.c
index a1b91980..3c148609 100644
--- a/src/GridText.c
+++ b/src/GridText.c
@@ -1649,7 +1649,7 @@ PRIVATE void display_scrollbar ARGS1(
 	}
 #endif /* USE_COLOR_STYLE */
 	move(1, LYcols - 1);
-	addch(ACS_UARROW);
+	addch_raw(ACS_UARROW);
 #ifdef USE_COLOR_STYLE
 	LynxChangeStyle(s, STACK_OFF, 0);
 #endif /* USE_COLOR_STYLE */
@@ -1690,7 +1690,7 @@ PRIVATE void display_scrollbar ARGS1(
 	}
 #endif /* USE_COLOR_STYLE */
 	move(h + 2, LYcols - 1);
-	addch(ACS_DARROW);
+	addch_raw(ACS_DARROW);
 #ifdef USE_COLOR_STYLE
 	LynxChangeStyle(s, STACK_OFF, 0);
 #endif /* USE_COLOR_STYLE */
@@ -2285,9 +2285,11 @@ PRIVATE void display_page ARGS3(
 	/*
 	 *  For non-multibyte curses.
 	 *
-	 *  Is this repainting necessary??  Let's try without.
+	 *  Full repainting is necessary, otherwise only part of a multibyte 
+	 *  character sequence might be written because of curses output 
+	 *  optimizations. 
 	 */
-	/*clearok(curscr, TRUE);*/
+	clearok(curscr, TRUE); 
     }
     refresh();
 }
@@ -5098,6 +5100,191 @@ PUBLIC int HText_beginAnchor ARGS3(
     return(a->number);
 }
 
+/*
+    This returns whether the given anchor has blank content. Shamelessly copied
+    from HText_endAnchor. The values returned are meaningful only for "normal"
+    links - like ones produced by <a href=".">foo</a>, no inputs, etc. - VH
+*/
+#ifdef MARK_HIDDEN_LINKS
+PUBLIC BOOL HText_isAnchorBlank ARGS2(
+	HText *,	text,
+	int,		number)
+{
+    TextAnchor *a;
+
+    /*
+     *  The number argument is set to 0 in HTML.c and
+     *  LYCharUtils.c when we want to end the anchor
+     *  for the immediately preceding HText_beginAnchor()
+     *  call.  If it's greater than 0, we want to handle
+     *  a particular anchor.  This allows us to set links
+     *  for positions indicated by NAME or ID attributes,
+     *  without needing to close any anchor with an HREF
+     *  within which that link might be embedded. - FM
+     */
+    if (number <= 0 || number == text->last_anchor->number) {
+	a = text->last_anchor;
+    } else {
+	for (a = text->first_anchor; a; a = a->next) {
+	    if (a->number == number) {
+		break;
+	    }
+	}
+	if (a == NULL) {
+	    /*
+	     *  There's no anchor with that number,
+	     *  so we'll default to the last anchor,
+	     *  and cross our fingers. - FM
+	     */
+	    a = text->last_anchor;
+	}
+    }
+
+    CTRACE((tfp, "GridText:HText_isAnchorBlank: number:%d link_type:%d\n",
+			a->number, a->link_type));
+    if (a->link_type == INPUT_ANCHOR) {
+	/*
+	 *  Shouldn't happen, but put test here anyway to be safe. - LE
+	 */
+
+	CTRACE((tfp,
+	   "HText_isAnchorBlank: internal error: last anchor was input field!\n"));
+	return 0;
+    }
+    if (a->number) {
+	/*
+	 *  If it goes somewhere...
+	 */
+	int i, j, k;
+	HTLine *last = text->last_line;
+	HTLine *prev = text->last_line->prev;
+	HTLine *start = last;
+	int CurBlankExtent = 0;
+	int BlankExtent = 0;
+	
+	int extent_adjust = (text->chars + last->size) - a->start -
+		     (text->Lines - a->line_num);
+
+	/*
+	 *  Check if the anchor content has only
+	 *  white and special characters, starting
+	 *  with the content on the last line. - FM
+	 */
+	a->extent += extent_adjust;
+	if (a->extent > (int)last->size) {
+	    /*
+	     *  The anchor extends over more than one line,
+	     *  so set up to check the entire last line. - FM
+	     */
+	    i = last->size;
+	} else {
+	    /*
+	     *  The anchor is restricted to the last line,
+	     *  so check from the start of the anchor. - FM
+	     */
+	    i = a->extent;
+	}
+	k = j = (last->size - i);
+	while (j < (int)last->size) {
+	    if (!IsSpecialAttrChar(last->data[j]) &&
+		!isspace((unsigned char)last->data[j]) &&
+		last->data[j] != HT_NON_BREAK_SPACE &&
+		last->data[j] != HT_EN_SPACE)
+		break;
+	    i--;
+	    j++;
+	}
+	if (i == 0) {
+	    if (a->extent > (int)last->size) {
+		/*
+		 *  The anchor starts on a preceding line, and
+		 *  the last line has only white and special
+		 *  characters, so declare the entire extent
+		 *  of the last line as blank. - FM
+		 */
+		CurBlankExtent = BlankExtent = last->size;
+	    } else {
+		/*
+		 *  The anchor starts on the last line, and
+		 *  has only white or special characters, so
+		 *  declare the anchor's extent as blank. - FM
+		 */
+		CurBlankExtent = BlankExtent = a->extent;
+	    }
+	}
+	/*
+	 *  While the anchor starts on a line preceding
+	 *  the one we just checked, and the one we just
+	 *  checked has only white and special characters,
+	 *  check whether the anchor's content on the
+	 *  immediately preceding line also has only
+	 *  white and special characters. - FM
+	 */
+	while (i == 0 &&
+	       (a->extent > CurBlankExtent ||
+		(a->extent == CurBlankExtent &&
+		 k == 0 &&
+		 prev != text->last_line &&
+		 (prev->size == 0 ||
+		  prev->data[prev->size - 1] == ']')))) {
+	    start = prev;
+	    k = j = prev->size - a->extent + CurBlankExtent;
+	    if (j < 0) {
+		/*
+		 *  The anchor starts on a preceding line,
+		 *  so check all of this line. - FM
+		 */
+		j = 0;
+		i = prev->size;
+	    } else {
+		/*
+		 *  The anchor starts on this line. - FM
+		 */
+		i = a->extent - CurBlankExtent;
+	    }
+	    while (j < (int)prev->size) {
+		if (!IsSpecialAttrChar(prev->data[j]) &&
+		    !isspace((unsigned char)prev->data[j]) &&
+		    prev->data[j] != HT_NON_BREAK_SPACE &&
+		    prev->data[j] != HT_EN_SPACE)
+		    break;
+		i--;
+		j++;
+	    }
+	    if (i == 0) {
+		if (a->extent > (CurBlankExtent + (int)prev->size) ||
+		    (a->extent == CurBlankExtent + (int)prev->size &&
+		     k == 0 &&
+		     prev->prev != text->last_line &&
+		     (prev->prev->size == 0 ||
+		      prev->prev->data[prev->prev->size - 1] == ']'))) {
+		    /*
+		     *  This line has only white and special
+		     *  characters, so treat its entire extent
+		     *  as blank, and decrement the pointer for
+		     *  the line to be analyzed. - FM
+		     */
+		    CurBlankExtent += prev->size;
+		    BlankExtent = CurBlankExtent;
+		    prev = prev->prev;
+		} else {
+		    /*
+		     *  The anchor starts on this line, and it
+		     *  has only white or special characters, so
+		     *  declare the anchor's extent as blank. - FM
+		     */
+		    BlankExtent = a->extent;
+		    break;
+		}
+	    }
+	}
+	a->extent -= extent_adjust;
+	return i==0;
+    } else
+	return 0;
+}
+#endif /* MARK_HIDDEN_LINKS */
+
 
 PUBLIC void HText_endAnchor ARGS2(
 	HText *,	text,
diff --git a/src/HTAlert.c b/src/HTAlert.c
index 5e339488..b698481d 100644
--- a/src/HTAlert.c
+++ b/src/HTAlert.c
@@ -457,23 +457,23 @@ PUBLIC int HTConfirmDefault ARGS2(CONST char *, Msg, int, Dft)
 	FREE(msg);
 
 	while (result < 0) {
-	    int c = LYgetch_for(FOR_SINGLEKEY);
+	    int c = LYgetch_single();
 #ifdef VMS
 	    if (HadVMSInterrupt) {
 		HadVMSInterrupt = FALSE;
-		c = *msg_no;
+		c = TOUPPER(*msg_no);
 	    }
 #endif /* VMS */
 	    if (c == 7 || c == 3) { /* remember we had ^G or ^C */
 		conf_cancelled = YES;
 		result = NO;
-	    } else if (TOUPPER(c) == TOUPPER(*msg_yes)) {
+	    } else if (c == TOUPPER(*msg_yes)) {
 		result = YES;
-	    } else if (TOUPPER(c) == TOUPPER(*msg_no)) {
+	    } else if (c == TOUPPER(*msg_no)) {
 		result = NO;
-	    } else if (fallback_y && TOLOWER(c) == fallback_y) {
+	    } else if (fallback_y && c == fallback_y) {
 		result = YES;
-	    } else if (fallback_n && TOLOWER(c) == fallback_n) {
+	    } else if (fallback_n && c == fallback_n) {
 		result = NO;
 	    } else if (Dft != DFT_CONFIRM) {
 		result = Dft;
@@ -858,8 +858,7 @@ PUBLIC BOOL HTConfirmCookie ARGS4(
 	if(LYAcceptAllCookies) {
 	    ch = 'A';
 	} else {
-	    ch = LYgetch_for(FOR_SINGLEKEY);
-	    ch = TOUPPER(ch);
+	    ch = LYgetch_single();
 #if defined(LOCALE) && defined(HAVE_GETTEXT) && !defined(gettext)
 	    /*
 	     * Special-purpose workaround for gettext support (we should do
@@ -1033,8 +1032,8 @@ PUBLIC int HTConfirmPostRedirect ARGS2(
 	    case 1:
 		_statusline(show_POST_url);
 	}
-	c = LYgetch_for(FOR_SINGLEKEY);
-	switch (TOUPPER(c)) {
+	c = LYgetch_single();
+	switch (c) {
 	    case 'P':
 		/*
 		**  Proceed with 301 or 307 redirect of POST
diff --git a/src/HTFWriter.c b/src/HTFWriter.c
index 64172a7f..6ea0dea1 100644
--- a/src/HTFWriter.c
+++ b/src/HTFWriter.c
@@ -749,8 +749,8 @@ PUBLIC HTStream* HTSaveToFile ARGS3(
 	    _statusline(CANNOT_DISPLAY_FILE_D_OR_C);
 	}
 
-	while(TOUPPER(c)!='C' && TOUPPER(c)!='D' && c!=7) {
-	    c=LYgetch();
+	while(c != 'D') {
+	    c = LYgetch_single();
 #ifdef VMS
 	    /*
 	     *	'C'ancel on Control-C or Control-Y and
@@ -766,7 +766,7 @@ PUBLIC HTStream* HTSaveToFile ARGS3(
 	/*
 	 *  Cancel on 'C', 'c' or Control-G or Control-C.
 	 */
-	if (TOUPPER(c)=='C' || c==7 || c==3) {
+	if (c == 'C' || c == 7) {
 	    _statusline(CANCELLING_FILE);
 	    LYCancelDownload = TRUE;
 	    FREE(ret_obj);
diff --git a/src/HTML.c b/src/HTML.c
index e27ffd33..c0fcf621 100644
--- a/src/HTML.c
+++ b/src/HTML.c
@@ -6730,7 +6730,12 @@ PRIVATE int HTML_end_element ARGS3(
 	 *  Set to know that we are no longer in an anchor.
 	 */
 	me->inA = FALSE;
-
+#ifdef MARK_HIDDEN_LINKS
+	if (hidden_link_marker && *hidden_link_marker && 
+		HText_isAnchorBlank(me->text, me->CurrentANum) ) {	    
+	    HText_appendText(me->text,hidden_link_marker);	
+	}
+#endif	
 	UPDATE_STYLE;
 	if (me->inBoldA == TRUE && me->inBoldH == FALSE)
 	    HText_appendCharacter(me->text, LY_BOLD_END_CHAR);
diff --git a/src/LYBookmark.c b/src/LYBookmark.c
index dfe82894..eb26456b 100644
--- a/src/LYBookmark.c
+++ b/src/LYBookmark.c
@@ -265,8 +265,8 @@ PUBLIC void save_bookmark_link ARGS2(
 	       (*BookmarkPage == '.' ?
 		    (BookmarkPage+1) : BookmarkPage)) != NULL) {
 	LYMBM_statusline(MULTIBOOKMARKS_SELF);
-	c = LYgetch();
-	if (TOUPPER(c) != 'L') {
+	c = LYgetch_single();
+	if (c != 'L') {
 	    FREE(bookmark_URL);
 	    return;
 	}
@@ -354,14 +354,14 @@ PUBLIC void save_bookmark_link ARGS2(
      *  Once and forever...
      */
     if (first_time) {
-	fprintf(fp,"<head>\n");
+	fprintf(fp, "<head>\n");
 #if defined(SH_EX) && !defined(_WINDOWS)	/* 1997/12/11 (Thu) 19:13:40 */
 	if (HTCJK != JAPANESE)
 	    LYAddMETAcharsetToFD(fp, -1);
 	else
 	    fprintf(fp, "<META %s %s>\n",
 		    "http-equiv=\"content-type\"",
-		    "conetnt=\"text/html;charset=iso-2022-jp\""); 
+		    "content=\"text/html;charset=iso-2022-jp\""); 
 #else
 	LYAddMETAcharsetToFD(fp, -1);
 #endif	/* !_WINDOWS */
diff --git a/src/LYCgi.c b/src/LYCgi.c
index 7e747377..97e4a93e 100644
--- a/src/LYCgi.c
+++ b/src/LYCgi.c
@@ -650,7 +650,8 @@ PRIVATE int LYLoadCGI ARGS4(
 			   format_out,
 			   sink, anAnchor);
 
-    HTSprintf0(&buf, "<head>\n<title>%s</title>\n</head>\n<body>\n", gettext("Good Advice"));
+    HTSprintf0(&buf, "<html>\n<head>\n<title>%s</title>\n</head>\n<body>\n",
+	       gettext("Good Advice"));
     (*target->isa->put_block)(target, buf, strlen(buf));
 
     HTSprintf0(&buf, "<h1>%s</h1>\n", gettext("Good Advice"));
@@ -670,7 +671,7 @@ PRIVATE int LYLoadCGI ARGS4(
 	       gettext("It provides state of the art CGI script support.\n"));
     (*target->isa->put_block)(target, buf, strlen(buf));
 
-    HTSprintf0(&buf,"</body>\n");
+    HTSprintf0(&buf,"</body>\n</html>\n");
     (*target->isa->put_block)(target, buf, strlen(buf));
 
     (*target->isa->_free)(target);
diff --git a/src/LYCookie.c b/src/LYCookie.c
index b52f9907..edd492ed 100644
--- a/src/LYCookie.c
+++ b/src/LYCookie.c
@@ -2417,14 +2417,14 @@ PRIVATE int LYHandleCookies ARGS4 (
 		    }
 		    HTNoDataOK = 1;
 		    while (1) {
-			ch = LYgetch_for(FOR_SINGLEKEY);
+			ch = LYgetch_single();
 #ifdef VMS
 			if (HadVMSInterrupt) {
 			    HadVMSInterrupt = FALSE;
 			    ch = 'C';
 			}
 #endif /* VMS */
-			switch(TOUPPER(ch)) {
+			switch(ch) {
 			    case 'A':
 				/*
 				 *  Set to accept all cookies
@@ -2571,7 +2571,7 @@ Delete_all_cookies_in_domain:
 #define PUTS(buf)    (*target->isa->put_block)(target, buf, strlen(buf))
 
 
-    HTSprintf0(&buf, "<HEAD>\n<TITLE>%s</title>\n</HEAD>\n<BODY>\n",
+    HTSprintf0(&buf, "<html>\n<head>\n<title>%s</title>\n</head>\n<body>\n",
 		 COOKIE_JAR_TITLE);
     PUTS(buf);
     HTSprintf0(&buf, "<h1>%s (%s)%s<a href=\"%s%s\">%s</a></h1>\n",
@@ -2580,12 +2580,12 @@ Delete_all_cookies_in_domain:
 	helpfilepath, COOKIE_JAR_HELP, COOKIE_JAR_TITLE);
     PUTS(buf);
 
-    HTSprintf0(&buf, "<NOTE>%s\n", ACTIVATE_TO_GOBBLE);
+    HTSprintf0(&buf, "<note>%s\n", ACTIVATE_TO_GOBBLE);
     PUTS(buf);
-    HTSprintf0(&buf, "%s</NOTE>\n", OR_CHANGE_ALLOW);
+    HTSprintf0(&buf, "%s</note>\n", OR_CHANGE_ALLOW);
     PUTS(buf);
 
-    HTSprintf0(&buf, "<DL COMPACT>\n");
+    HTSprintf0(&buf, "<dl compact>\n");
     PUTS(buf);
     for (dl = domain_list; dl != NULL; dl = dl->next) {
 	de = dl->object;
@@ -2598,7 +2598,7 @@ Delete_all_cookies_in_domain:
 	/*
 	 *  Show the domain link and 'allow' setting. - FM
 	 */
-	HTSprintf0(&buf, "<DT>%s<DD><A HREF=\"LYNXCOOKIE://%s/\">Domain=%s</A>\n",
+	HTSprintf0(&buf, "<dt>%s<dd><a href=\"LYNXCOOKIE://%s/\">Domain=%s</a>\n",
 		      de->domain, de->domain, de->domain);
 	PUTS(buf);
 	switch (de->bv) {
@@ -2641,7 +2641,7 @@ Delete_all_cookies_in_domain:
 	    } else {
 		StrAllocCopy(value, NO_VALUE);
 	    }
-	    HTSprintf0(&buf, "<DD><A HREF=\"LYNXCOOKIE://%s/%s\">%s=%s</A>\n",
+	    HTSprintf0(&buf, "<dd><a href=\"LYNXCOOKIE://%s/%s\">%s=%s</a>\n",
 			 de->domain, co->lynxID, name, value);
 	    FREE(name);
 	    FREE(value);
@@ -2661,7 +2661,7 @@ Delete_all_cookies_in_domain:
 	    } else {
 		StrAllocCopy(path, "/");
 	    }
-	    HTSprintf0(&buf, "<DD>Path=%s\n<DD>Port: %d Secure: %s Discard: %s\n",
+	    HTSprintf0(&buf, "<dd>Path=%s\n<dd>Port: %d Secure: %s Discard: %s\n",
 			 path, co->port,
 			 ((co->flags & COOKIE_FLAG_SECURE) ? "YES" : "NO"),
 			 ((co->flags & COOKIE_FLAG_DISCARD) ? "YES" : "NO"));
@@ -2672,7 +2672,7 @@ Delete_all_cookies_in_domain:
 	     *	Show the list of acceptable ports, if present. - FM
 	     */
 	    if (co->PortList) {
-		HTSprintf0(&buf, "<DD>PortList=\"%s\"\n", co->PortList);
+		HTSprintf0(&buf, "<dD>PortList=\"%s\"\n", co->PortList);
 		PUTS(buf);
 	    }
 
@@ -2685,7 +2685,7 @@ Delete_all_cookies_in_domain:
 		StrAllocCopy(Title, co->commentURL);
 		LYEntify(&Title, TRUE);
 		HTSprintf0(&buf,
-			"<DD>CommentURL: <A href=\"%s\">%s</A>\n",
+			"<dd>CommentURL: <a href=\"%s\">%s</a>\n",
 			Address,
 			Title);
 		FREE(Address);
@@ -2699,7 +2699,7 @@ Delete_all_cookies_in_domain:
 	    if (co->comment) {
 		StrAllocCopy(comment, co->comment);
 		LYEntify(&comment, TRUE);
-		HTSprintf0(&buf, "<DD>Comment: %s\n", comment);
+		HTSprintf0(&buf, "<dd>Comment: %s\n", comment);
 		FREE(comment);
 		PUTS(buf);
 	    }
@@ -2707,7 +2707,7 @@ Delete_all_cookies_in_domain:
 	    /*
 	     *	Show the Maximum Gobble Date. - FM
 	     */
-	    HTSprintf0(&buf, "<DD><EM>%s</EM> %s%s",
+	    HTSprintf0(&buf, "<dd><em>%s</em> %s%s",
 	    		 gettext("Maximum Gobble Date:"),
 			 ((co->flags & COOKIE_FLAG_EXPIRES_SET)
 					    ?
@@ -2717,10 +2717,10 @@ Delete_all_cookies_in_domain:
 					 "" : "\n"));
 	    PUTS(buf);
 	}
-	HTSprintf0(&buf, "</DT>\n");
+	HTSprintf0(&buf, "</dt>\n");
 	PUTS(buf);
     }
-    HTSprintf0(&buf, "</DL>\n</BODY>\n");
+    HTSprintf0(&buf, "</dl>\n</body>\n</html>\n");
     PUTS(buf);
 
     /*
diff --git a/src/LYCurses.c b/src/LYCurses.c
index 99799f74..d25d10c3 100644
--- a/src/LYCurses.c
+++ b/src/LYCurses.c
@@ -56,8 +56,7 @@ PRIVATE int dumbterm PARAMS((char *terminal));
 BOOLEAN LYCursesON = FALSE;
 
 #if USE_COLOR_TABLE || defined(USE_SLANG)
-PRIVATE int Current_Attr;
-PRIVATE int Masked_Attr;
+PUBLIC int Current_Attr, Masked_Attr;
 #endif
 
 #define OMIT_SCN_KEEPING 0 /* whether to omit keeping of Style_className
diff --git a/src/LYCurses.h b/src/LYCurses.h
index 98ad35cb..21934a56 100644
--- a/src/LYCurses.h
+++ b/src/LYCurses.h
@@ -50,6 +50,22 @@
 #define WINDOW void
 #define waddstr(w,s) addstr(s)
 
+#ifndef ACS_UARROW  
+#define ACS_UARROW  SLSMG_UARROW_CHAR
+#endif
+
+#ifndef ACS_DARROW
+#define ACS_DARROW  SLSMG_DARROW_CHAR
+#endif
+
+#ifndef ACS_CKBOARD
+#define ACS_CKBOARD SLSMG_CKBRD_CHAR
+#endif
+
+#ifndef ACS_BLOCK
+#define ACS_BLOCK   SLSMG_BLOCK_CHAR
+#endif
+
 #else /* Using curses: */
 
 #ifdef VMS
@@ -218,6 +234,11 @@ extern void LYbox PARAMS((WINDOW *win, BOOLEAN formfield));
 extern int LYlines;  /* replaces LINES */
 extern int LYcols;   /* replaces COLS */
 
+#if defined(USE_COLOR_TABLE) || defined(USE_SLANG)
+extern int Current_Attr;
+extern int Masked_Attr;
+#endif
+
 extern void start_curses NOPARAMS;
 extern void stop_curses NOPARAMS;
 extern BOOLEAN setup PARAMS((char *terminal));
@@ -316,9 +337,14 @@ extern void LY_SLerase NOPARAMS;
 #define scrollok(a,b) SLsmg_Newline_Moves = ((b) ? 1 : -1)
 #endif
 
-#define addch SLsmg_write_char
+#define addch(ch)     SLsmg_write_char(ch)
+#define addch_raw(ch) do {                                \
+                        SLsmg_Char_Type buf;              \
+                        buf = (ch) | (Current_Attr << 4); \
+                        SLsmg_write_raw (&buf, 1);        \
+                      } while (0)
 #define echo()
-#define printw SLsmg_printf
+#define printw        SLsmg_printf
 
 extern int curscr;
 extern BOOLEAN FullRefresh;
@@ -453,6 +479,9 @@ FANCY_CURSES.  Check your config.log to see why the FANCY_CURSES test failed.
 #define wstop_reverse(a)	wstandend(a)
 
 #endif /* FANCY_CURSES */
+
+#define addch_raw(ch)           addch(ch)
+
 #endif /* USE_SLANG */
 
 #ifdef USE_SLANG
diff --git a/src/LYExtern.c b/src/LYExtern.c
index 826c30f0..338cc42b 100644
--- a/src/LYExtern.c
+++ b/src/LYExtern.c
@@ -170,7 +170,9 @@ PRIVATE void format ARGS3(
     HTEndParam(result, fmt, 1);
 }
 
-void run_external ARGS1(char *, c)
+BOOL run_external ARGS2(
+    char *, 	c,
+    BOOL,	only_overriders)
 {
 #ifdef WIN_EX
     HANDLE handle;
@@ -182,9 +184,10 @@ void run_external ARGS1(char *, c)
 #endif
     char *cmdbuf = NULL;
     lynx_html_item_type *externals2 = 0;
+    int found = 0;
 
     if (externals == NULL)
-	return;
+	return 0;
 
 #ifdef WIN_EX			/* 1998/01/26 (Mon) 09:16:13 */
     if (c == NULL) {
@@ -201,12 +204,14 @@ void run_external ARGS1(char *, c)
 	CTRACE((tfp, "EXTERNAL: '%s' <==> '%s'\n", externals2->name, c));
 #endif
 	if (externals2->command != 0
-	  && !strncasecomp(externals2->name, c, strlen(externals2->name)))
+	  && !strncasecomp(externals2->name, c, strlen(externals2->name))
+	  && (only_overriders ? externals2->override_primary_action : 1))
 	{
-	    if (no_externals && !externals2->always_enabled) {
+	    if (no_externals && !externals2->always_enabled && !only_overriders) {
 		HTUserMsg(EXTERNALS_DISABLED);
 		break;
 	    }
+	    ++found;
 	    /*  Too dangerous to leave any URL that may come along unquoted.
 	     *  They often contain '&', ';', and '?' chars, and who knows
 	     *  what else may occur.
@@ -371,6 +376,6 @@ void run_external ARGS1(char *, c)
     } /* end-for */
 
     FREE(cmdbuf);
-    return;
+    return found;
 }
 #endif	/* USE_EXTERNALS */
diff --git a/src/LYExtern.h b/src/LYExtern.h
index c172800a..909c41cd 100644
--- a/src/LYExtern.h
+++ b/src/LYExtern.h
@@ -5,7 +5,8 @@
 #include <LYStructs.h>
 #endif /* LYSTRUCTS_H */
 
-void run_external PARAMS((char * c));
+/*returns TRUE if something matching was executed*/
+BOOL run_external PARAMS((char * c, BOOL only_overriders));
 char *string_short PARAMS((char * str, int cut_pos));
 
 #ifdef WIN_EX
diff --git a/src/LYForms.c b/src/LYForms.c
index 17c65d5d..11eb5125 100644
--- a/src/LYForms.c
+++ b/src/LYForms.c
@@ -443,7 +443,7 @@ again:
 	repeat = -1;
 	get_mouse_link();	/* Reset mouse_link. */
 
-	ch = LYgetch_for(FOR_INPUT);
+	ch = LYgetch_input();
 #ifdef SUPPORT_MULTIBYTE_EDIT
 #ifdef WIN_EX
 	if (!refresh_mb && (EditBinding(ch) != LYE_CHAR))
@@ -558,7 +558,7 @@ again:
 	}
 	if (action == LYE_LKCMD) {
 	    _statusline(ENTER_LYNX_COMMAND);
-	    ch = LYgetch_for(FOR_PANEL);
+	    ch = LYgetch();
 #ifdef VMS
 	    if (HadVMSInterrupt) {
 		HadVMSInterrupt = FALSE;
@@ -1166,8 +1166,8 @@ redraw:
 	wrefresh(form_window);
 #endif /* USE_SLANG  */
 
-	c = LYgetch_for(FOR_CHOICE);
-	if (c == 3 || c == 7) {	/* Control-C or Control-G */
+	c = LYgetch_choice();
+	if (c == 7) {		/* Control-C or Control-G */
 	    cmd = LYK_QUIT;
 #ifndef USE_SLANG
 	} else if (c == MOUSE_KEY) {
@@ -1851,7 +1851,6 @@ restore_popup_statusline:
 		cmd = LYK_ACTIVATE; /* to exit */
 		break;
 	}
-
     }
 #ifndef USE_SLANG
     delwin(form_window);
diff --git a/src/LYGetFile.c b/src/LYGetFile.c
index a1289c36..9807e870 100644
--- a/src/LYGetFile.c
+++ b/src/LYGetFile.c
@@ -505,7 +505,7 @@ Try_Redirected_URL:
 #endif /* !VMS */
 			    printf("\n%s", RETURN_TO_LYNX);
 			    fflush(stdout);
-			    LYgetch();
+			    (void) LYgetch();
 #ifdef VMS
 			    HadVMSInterrupt = FALSE;
 #endif /* VMS */
diff --git a/src/LYGlobalDefs.h b/src/LYGlobalDefs.h
index 44d4fda2..faa41a86 100644
--- a/src/LYGlobalDefs.h
+++ b/src/LYGlobalDefs.h
@@ -196,6 +196,8 @@ extern char *helpfilepath;
 extern char *jumpprompt;	/* The default jump statusline prompt */
 extern char *language;
 extern char *lynx_cfg_file;	/* location of active lynx.cfg file */
+extern char *lynx_cmd_logfile;	/* file to write keystroke commands, if any */
+extern char *lynx_cmd_script;	/* file to read keystroke commands, if any */
 extern char *lynx_save_space;
 extern char *lynx_temp_space;
 extern char *lynxjumpfile;
@@ -509,4 +511,8 @@ extern int LYsb_begin;
 extern int LYsb_end;
 #endif
 
+#ifdef MARK_HIDDEN_LINKS
+extern char* hidden_link_marker;
+#endif
+
 #endif /* LYGLOBALDEFS_H */
diff --git a/src/LYKeymap.c b/src/LYKeymap.c
index 75914bd5..11c6156e 100644
--- a/src/LYKeymap.c
+++ b/src/LYKeymap.c
@@ -120,8 +120,13 @@ LYK_8,               LYK_9,             0,          LYK_TRACE_LOG,
 LYK_UP_LINK,         LYK_INFO,     LYK_DOWN_LINK,   LYK_HELP,
 /* < */              /* = */         /* > */        /* ? */
 
+#ifndef SUPPORT_CHDIR
 LYK_RAW_TOGGLE,      LYK_ADDRLIST, LYK_PREV_PAGE,   LYK_COMMENT,
 /* @ */              /* A */         /* B */        /* C */
+#else
+LYK_RAW_TOGGLE,      LYK_ADDRLIST, LYK_PREV_PAGE,   LYK_CHDIR,
+/* @ */              /* A */         /* B */        /* C */
+#endif
 
 LYK_DOWNLOAD,        LYK_ELGOTO,  LYK_DIRED_MENU,   LYK_ECGOTO,
 /* D */              /* E */         /* F */        /* G */
@@ -411,9 +416,13 @@ LYKeymap_t key_override[KEYMAP_SIZE] = {
 
    0,                  0,              0,             0,
 /* < */             /* = */         /* > */        /* ? */
-
+#ifndef SUPPORT_CHDIR
    0,                  0,              0,         LYK_CREATE,
 /* @ */             /* A */         /* B */        /* C */
+#else
+   0,                  0,              0,         LYK_CHDIR,
+/* @ */             /* A */         /* B */        /* C */
+#endif
 
    0,                  0,        LYK_DIRED_MENU,       0,
 /* D */             /* E */         /* F */        /* G */
@@ -734,32 +743,42 @@ PRIVATE struct rmap revmap[] = {
 { "CHANGE_KCODE",	"Change Kanji code" },
 #endif
 #endif /* VMS */
+#ifdef SUPPORT_CHDIR
+ { "CHDIR",		"change current directory" },
+#endif
 { NULL,			"" }
 };
 
-PRIVATE CONST char *funckey[] = {
-  "Up Arrow",
-  "Down Arrow",
-  "Right Arrow",
-  "Left Arrow",
-  "Page Down",
-  "Page Up",
-  "Home",
-  "End",
-  "F1",
-  "Do key",
-  "Find key",
-  "Select key",
-  "Insert key",
-  "Remove key",
-  "(DO_NOTHING)",		/* should normally not appear in list */
-  "Back Tab",
-  0, 0, 0, 0, 0, 0, 0, 0,
-  0, 0, 0, 0, 0,
-  "mouse pseudo key",		/* normally not mapped to keymap[] action */
+PRIVATE CONST struct {
+    int key;
+    CONST char *name;
+} named_keys[] = {
+    { '\t',		"<tab>" },
+    { '\r',		"<return>" },
+    { CH_ESC,		"ESC" },
+    { ' ',		"<space>" },
+    { '<',		"<" },
+    { '>',		">" },
+    { 0177,		"<delete>" },
+    { UPARROW,		"Up Arrow" },
+    { DNARROW,		"Down Arrow" },
+    { RTARROW,		"Right Arrow" },
+    { LTARROW,		"Left Arrow" },
+    { PGDOWN,		"Page Down" },
+    { PGUP,		"Page Up" },
+    { HOME,		"Home" },
+    { END_KEY,		"End" },
+    { F1,		"F1" },
+    { DO_KEY,		"Do key" },
+    { FIND_KEY,		"Find key" },
+    { SELECT_KEY,	"Select key" },
+    { INSERT_KEY,	"Insert key" },
+    { REMOVE_KEY,	"Remove key" },
+    { DO_NOTHING,	"(DO_NOTHING)" },
+    { BACKTAB_KEY,	"Back Tab" },
+    { MOUSE_KEY,	"mouse pseudo key" },
 };
 
-
 struct emap {
 	CONST char *name;
 	CONST int   code;
@@ -818,74 +837,115 @@ PRIVATE struct emap ekmap[] = {
 #endif
 };
 
-PRIVATE char *pretty ARGS1 (int, c)
+PUBLIC char *LYKeycodeToString ARGS2 (
+	int,		c,
+	BOOLEAN,	upper8)
 {
-	static char buf[30];
+    static char buf[30];
+    unsigned n;
+    BOOLEAN named = FALSE;
+
+    for (n = 0; n < TABLESIZE(named_keys); n++) {
+	if (named_keys[n].key == c) {
+	    named = TRUE;
+	    strcpy(buf, named_keys[n].name);
+	    break;
+	}
+    }
 
-	if (c == '\t')
-		sprintf(buf, "<tab>");
-	else if (c == '\r')
-		sprintf(buf, "<return>");
-	else if (c == CH_ESC)
-		sprintf(buf, "ESC");
-	else if (c == ' ')
-		sprintf(buf, "<space>");
-	else if (c == '<')
-		sprintf(buf, "<");
-	else if (c == '>')
-		sprintf(buf, ">");
-	else if (c == 0177)
-		sprintf(buf, "<delete>");
-	else if (c > ' ' && c < 0177)
-		sprintf(buf, "%c", c);
-	else if (c > ' ' && c <= 0377 &&
-		 c <= LYlowest_eightbit[current_char_set])
-		sprintf(buf, "%c", c);
+    if (!named) {
+	if (c > ' '
+	 && c < 0177)
+	    sprintf(buf, "%c", c);
+	else if (upper8
+	 && c > ' '
+	 && c <= 0377
+	 && c <= LYlowest_eightbit[current_char_set])
+	    sprintf(buf, "%c", c);
 	else if (c < ' ')
-		sprintf(buf, "^%c", c|0100);
-	else if (c >= 0400 && (c - 0400) < (int) TABLESIZE(funckey)
-		 && funckey[c-0400])
-		sprintf(buf, "%.*s", (int)(sizeof(buf) - 1), funckey[c-0400]);
+	    sprintf(buf, "^%c", c|0100);
 	else if (c >= 0400)
-		sprintf(buf, "key-%#x", c);
+	    sprintf(buf, "key-%#x", c);
 	else
-		return 0;
+	    return 0;
+    }
+    return buf;
+}
 
-	return buf;
+PUBLIC int LYStringToKeycode ARGS1 (
+	char *,		src)
+{
+    unsigned n;
+    int key = -1;
+    int len = strlen(src);
+
+    if (len == 1)
+	key = *src;
+    else if (len == 2 && *src == '^')
+	key = src[1] & 0x1f;
+    else if (len > 6 && !strncasecomp(src, "key-", 4)) {
+	char *dst = 0;
+	key = strtol(src + 4, &dst, 0);
+	if (dst == 0 || *dst != 0)
+	    key = -1;
+    }
+    if (key < 0) {
+	for (n = 0; n < TABLESIZE(named_keys); n++) {
+	    if (!strcasecomp(named_keys[n].name, src)) {
+		key = named_keys[n].key;
+		break;
+	    }
+	}
+    }
+    return key;
 }
 
+#define PRETTY_LEN 11
+
 PRIVATE char *pretty_html ARGS1 (int, c)
 {
-	static char buf[30];
-
-	if (c == '\t')
-		sprintf(buf, "&lt;tab&gt;      ");
-	else if (c == '\r')
-		sprintf(buf, "&lt;return&gt;   ");
-	else if (c == ' ')
-		sprintf(buf, "&lt;space&gt;    ");
-	else if (c == '<')
-		sprintf(buf, "&lt;          ");
-	else if (c == '>')
-		sprintf(buf, "&gt;          ");
-	else if (c == 0177)
-		sprintf(buf, "&lt;delete&gt;   ");
-	else if (c > ' ' && c < 0177)
-		sprintf(buf, "%c", c);
-	else if (c > ' ' && c <= 0377 &&
-		 c <= LYlowest_eightbit[current_char_set])
-		sprintf(buf, "%c", c);
-	else if (c < ' ')
-		sprintf(buf, "^%c", c|0100);
-	else if (c >= 0400 && (c - 0400) < (int) TABLESIZE(funckey)
-		 && funckey[c-0400])
-		sprintf(buf, "%.*s", (int)(sizeof(buf) - 1), funckey[c-0400]);
-	else if (c >= 0400)
-		sprintf(buf, "%#x", c);
-	else
-		return 0;
+    char *src = LYKeycodeToString(c, TRUE);
+
+    if (src != 0) {
+	static CONST struct {
+	    int	code;
+	    CONST char *name;
+	} table[] = {
+	    { '<',	"&lt;" },
+	    { '>',	"&gt;" },
+	    { '"',	"&quot;" },
+	    { '&',	"&amp;" }
+	};
 
+	static char buf[30];
+	char *dst = buf;
+	int adj = 0;
+	unsigned n;
+	BOOLEAN found;
+
+	while ((c = *src++) != 0) {
+	    found = FALSE;
+	    for (n = 0; n < TABLESIZE(table); n++) {
+		if (c == table[n].code) {
+		    found = TRUE;
+		    strcpy(dst, table[n].name);
+		    adj += strlen(dst) - 1;
+		    dst += strlen(dst);
+		    break;
+		}
+	    }
+	    if (!found) {
+		*dst++ = c;
+	    }
+	}
+	adj -= (dst - buf) - PRETTY_LEN;
+	while (adj-- > 0)
+	    *dst++ = ' ';
+	*dst = 0;
 	return buf;
+    }
+
+    return 0;
 }
 
 PRIVATE char * format_binding ARGS2(
@@ -901,9 +961,10 @@ PRIVATE char * format_binding ARGS2(
      && revmap[the_key].name != 0
      && revmap[the_key].doc != 0
      && (formatted = pretty_html(i-1)) != 0) {
-	HTSprintf0(&buf, "%-11s %-13s %s\n", formatted,
-		revmap[the_key].name,
-		revmap[the_key].doc);
+	HTSprintf0(&buf, "%-*s %-13s %s\n",
+		   PRETTY_LEN, formatted,
+		   revmap[the_key].name,
+		   revmap[the_key].doc);
 	return buf;
     }
     return 0;
@@ -1059,18 +1120,9 @@ PRIVATE int LYLoadKeymap ARGS4 (
 
 #define PUTS(buf)    (*target->isa->put_block)(target, buf, strlen(buf))
 
-    HTSprintf0(&buf, "<head>\n<title>%s</title>\n</head>\n<body>\n",
+    HTSprintf0(&buf, "<html>\n<head>\n<title>%s</title>\n</head>\n<body>\n",
 		     CURRENT_KEYMAP_TITLE);
     PUTS(buf);
-#if 0	/* There isn't really a help page *on* the current keymap.
-	   And we don't need a title and version info here, better
-	   make the page as compact as possible. - kw */
-    HTSprintf0(&buf, "<h1>%s (%s)%s<a href=\"%s%s\">%s</a></h1>\n",
-	LYNX_NAME, LYNX_VERSION,
-	HELP_ON_SEGMENT,
-	helpfilepath, CURRENT_KEYMAP_HELP, CURRENT_KEYMAP_TITLE);
-    PUTS(buf);
-#endif
     HTSprintf0(&buf, "<pre>\n");
     PUTS(buf);
 
@@ -1090,7 +1142,7 @@ PRIVATE int LYLoadKeymap ARGS4 (
 	}
     }
 
-    HTSprintf0(&buf,"</pre>\n</body>\n");
+    HTSprintf0(&buf,"</pre>\n</body>\n</html>\n");
     PUTS(buf);
 
     (*target->isa->_free)(target);
@@ -1301,7 +1353,7 @@ PUBLIC char *key_for_func ARGS1 (
     char *formatted;
 
     if ((i = LYReverseKeymap(func)) >= 0) {
-	formatted = pretty(i);
+	formatted = LYKeycodeToString(i, TRUE);
 	StrAllocCopy(buf, formatted != 0 ? formatted : "?");
     } else if (buf == 0) {
 	StrAllocCopy(buf, "");
@@ -1325,7 +1377,7 @@ PUBLIC char *fmt_keys ARGS2(
     char *fmt_second;
     if (lkc_first < 0)
 	return NULL;
-    fmt_first = pretty(lkc_first);
+    fmt_first = LYKeycodeToString(lkc_first, TRUE);
     if (fmt_first && strlen(fmt_first) == 1 && *fmt_first != '\'') {
 	quotes = TRUE;
     }
@@ -1340,7 +1392,7 @@ PUBLIC char *fmt_keys ARGS2(
 	StrAllocCopy(buf, fmt_first);
     }
     if (lkc_second >= 0) {
-	fmt_second = pretty(lkc_second);
+	fmt_second = LYKeycodeToString(lkc_second, TRUE);
 	if (!fmt_second) {
 	    FREE(buf);
 	    return NULL;
diff --git a/src/LYKeymap.h b/src/LYKeymap.h
index f58b54a9..c4f47672 100644
--- a/src/LYKeymap.h
+++ b/src/LYKeymap.h
@@ -6,14 +6,16 @@
 #endif
 
 extern BOOLEAN LYisNonAlnumKeyname PARAMS((int ch, int KeyName));
+extern char *LYKeycodeToString PARAMS((int c, BOOLEAN upper8));
+extern char *fmt_keys PARAMS((int lkc_first, int lkc_second));
 extern char *key_for_func PARAMS((int func));
 extern char *key_for_func_ext PARAMS((int lac, int context_code));
-extern char *fmt_keys PARAMS((int lkc_first, int lkc_second));
 extern int LYReverseKeymap PARAMS((int KeyName));
-extern int lookup_keymap PARAMS((int code));
+extern int LYStringToKeycode PARAMS((char *src));
 extern int lacname_to_lac PARAMS((CONST char *func));
 extern int lecname_to_lec PARAMS((CONST char *func));
 extern int lkcstring_to_lkc PARAMS((CONST char *src));
+extern int lookup_keymap PARAMS((int code));
 extern int remap PARAMS((char *key, char *func, BOOLEAN for_dired));
 extern void print_keymap PARAMS((char **newfile));
 extern void reset_emacs_keys NOPARAMS;
@@ -236,6 +238,9 @@ typedef enum {
 #ifdef KANJI_CODE_OVERRIDE
   , LYK_CHG_KCODE
 #endif
+#ifdef SUPPORT_CHDIR
+  , LYK_CHDIR
+#endif
 } LYKeymapCode;
 
 
diff --git a/src/LYLocal.c b/src/LYLocal.c
index b80c9a45..deb7f0be 100644
--- a/src/LYLocal.c
+++ b/src/LYLocal.c
@@ -68,6 +68,10 @@
 # endif
 #endif
 
+#ifdef SUPPORT_CHDIR
+#include <LYMainLoop.h>
+#endif
+
 #include <LYLeaks.h>
 
 #undef USE_COMPRESS
@@ -140,6 +144,10 @@ struct dired_menu {
  *  configuration file via DIRED_MENU lines, then these default entries
  *  are discarded entirely.
  */
+#ifdef SUPPORT_CHDIR
+{ 0,		      "", "Change directory",
+		      "", "LYNXDIRED://CHDIR",			NULL },
+#endif
 { 0,		      "", "New File",
 "(in current directory)", "LYNXDIRED://NEW_FILE%d",		NULL },
 
@@ -767,7 +775,7 @@ PUBLIC BOOLEAN local_modify ARGS2(
 	document *,	doc,
 	char **,	newpath)
 {
-    int c, ans;
+    int ans;
     char *cp;
     char testpath[DIRED_MAXBUF]; /* a bit ridiculous */
     int count;
@@ -800,8 +808,7 @@ PUBLIC BOOLEAN local_modify ARGS2(
 #else
     _statusline(gettext("Modify name or location (n or l): "));
 #endif /* OK_PERMIT */
-    c = LYgetch();
-    ans = TOUPPER(c);
+    ans = LYgetch_single();
 
     if (strchr("NLP", ans) != NULL) {
 	cp = HTfullURL_toFile(links[doc->link].lname);
@@ -935,7 +942,7 @@ PRIVATE BOOLEAN create_directory ARGS1(
 PUBLIC BOOLEAN local_create ARGS1(
 	document *,	doc)
 {
-    int c, ans;
+    int ans;
     char *cp;
     char testpath[DIRED_MAXBUF];
 
@@ -948,8 +955,7 @@ PUBLIC BOOLEAN local_create ARGS1(
     FREE(cp);
 
     _statusline(gettext("Create file or directory (f or d): "));
-    c = LYgetch();
-    ans = TOUPPER(c);
+    ans = LYgetch_single();
 
     if (ans == 'F') {
 	return(create_file(testpath));
@@ -1485,6 +1491,7 @@ PUBLIC int local_dired ARGS1(
     char *tmpbuf = NULL;
     char *buffer = NULL;
     char *dirname = NULL;
+    BOOL do_pop_doc = TRUE;
 
     line_url = doc->address;
     CTRACE((tfp, "local_dired: called for <%s>.\n",
@@ -1495,8 +1502,13 @@ PUBLIC int local_dired ARGS1(
     StrAllocCopy(line, line_url);
     HTUnEscape(line);	/* _file_ (not URL) syntax, for those functions
 			   that need it.  Don't forget to FREE it. */
-
-    if ((arg = match_op("NEW_FILE", line)) != 0) {
+    if ((arg = match_op("CHDIR", line)) != 0) {    
+#ifdef SUPPORT_CHDIR
+	handle_LYK_CHDIR();
+	do_pop_doc = FALSE;
+#endif
+	arg = "blah";	/* do something to avoid cc's complaints */
+    } else if ((arg = match_op("NEW_FILE", line)) != 0) {
 	if (create_file(arg) > 0)
 	    LYforce_no_cache = TRUE;
     } else if ((arg = match_op("NEW_FOLDER", line)) != 0) {
@@ -1713,7 +1725,8 @@ PUBLIC int local_dired ARGS1(
     FREE(buffer);
     FREE(line);
     FREE(tp);
-    LYpop(doc);
+    if (do_pop_doc)
+	LYpop(doc);
     return 0;
 }
 
diff --git a/src/LYMail.c b/src/LYMail.c
index 0b7d3b38..7dd55aef 100644
--- a/src/LYMail.c
+++ b/src/LYMail.c
@@ -836,7 +836,7 @@ PUBLIC void mailmsg ARGS4(
 	fprintf(fd, "Cc: %s\n", personal_mail_address);
     }
     fprintf(fd, "X-URL: %s\n", filename);
-    fprintf(fd, "X-Mailer: Lynx, Version %s\n\n", LYNX_VERSION);
+    fprintf(fd, "X-Mailer: %s, Version %s\n\n", LYNX_NAME, LYNX_VERSION);
 #else
     if ((fd = LYOpenTemp(my_tmpfile, ".txt", "w")) == NULL) {
 	CTRACE((tfp, "mailmsg: Could not fopen '%s'.\n", my_tmpfile));
@@ -855,7 +855,7 @@ PUBLIC void mailmsg ARGS4(
 	    fprintf(fd, "Cc: %s\n", personal_mail_address);
 	}
 	fprintf(fd, "X-URL: %s\n", filename);
-	fprintf(fd, "X-Mailer: Lynx, Version %s\n\n", LYNX_VERSION);
+	fprintf(fd, "X-Mailer: %s, Version %s\n\n", LYNX_NAME, LYNX_VERSION);
     }
 #else /* !VMS, e.g., DOSPATH */
     sprintf(subject, "Lynx Error in %.56s", filename);
@@ -871,7 +871,7 @@ PUBLIC void mailmsg ARGS4(
     fprintf(fd, "%s\n\n", gettext("Thought you might want to know."));
 
     fprintf(fd, "%s\n", gettext("This message was automatically generated by"));
-    fprintf(fd, gettext("Lynx ver. %s"), LYNX_VERSION);
+    fprintf(fd, "%s %s", LYNX_NAME, LYNX_VERSION);
     if ((LynxSigFile != NULL) &&
 	(fp = fopen(LynxSigFile, TXT_R)) != NULL) {
 	fputs("-- \n", fd);
@@ -1338,7 +1338,7 @@ PUBLIC void reply_by_mail ARGS4(
 		(filename && *filename) ? filename : "mailto:",
 		(filename && *filename) ? "" : address);
 	fprintf((isPMDF ? hfd : fd),
-		"X-Mailer: Lynx, Version %s\n",LYNX_VERSION);
+		"X-Mailer: %s, Version %s\n", LYNX_NAME, LYNX_VERSION);
 #ifdef NO_ANONYMOUS_EMAIL
 	if (!isPMDF) {
 	    fprintf(fd, "\n");
@@ -1390,7 +1390,7 @@ PUBLIC void reply_by_mail ARGS4(
 	StrAllocCat(header, address);
     }
     StrAllocCat(header, "\n");
-    HTSprintf(&header, "X-Mailer: Lynx, Version %s\n", LYNX_VERSION);
+    HTSprintf(&header, "X-Mailer: %s, Version %s\n", LYNX_NAME, LYNX_VERSION);
 
     if (refid && *refid) {
 	StrAllocCat(header, "In-Reply-To: <");
diff --git a/src/LYMain.c b/src/LYMain.c
index 1f3967f5..31179bf2 100644
--- a/src/LYMain.c
+++ b/src/LYMain.c
@@ -340,6 +340,8 @@ PUBLIC BOOLEAN LYNewsPosting = NEWS_POSTING; /* News posting supported? */
 PUBLIC char *LynxSigFile = NULL;    /* Signature file, in or off home */
 PUBLIC char *system_mail = NULL;	  /* The path for sending mail */
 PUBLIC char *system_mail_flags = NULL;	  /* Flags for sending mail */
+PUBLIC char *lynx_cmd_logfile;	/* file to write keystroke commands, if any */
+PUBLIC char *lynx_cmd_script;	/* file to read keystroke commands, if any */
 PUBLIC char *lynx_cfg_file = NULL;	  /* location of active lynx.cfg */
 PUBLIC char *lynx_temp_space = NULL; /* The prefix for temporary file paths */
 PUBLIC char *lynx_save_space = NULL; /* The prefix for save to disk paths */
@@ -461,6 +463,10 @@ PUBLIC BOOL textfields_need_activation = FALSE;
 
 PUBLIC BOOLEAN textfield_prompt_at_left_edge = FALSE;
 
+#ifdef MARK_HIDDEN_LINKS
+PUBLIC char* hidden_link_marker = NULL;
+#endif
+
 
 #ifdef DISP_PARTIAL
 PUBLIC BOOLEAN display_partial_flag = TRUE; /* Display document during download */
@@ -1211,6 +1217,23 @@ PUBLIC int main ARGS2(
 
     LYOpenTraceLog();
 
+#ifdef EXP_CMD_LOGGING
+    /*
+     *	Open command-script, if specified
+     */
+    if (lynx_cmd_script != 0) {
+	tildeExpand(&lynx_cmd_script, TRUE);
+	LYOpenCmdScript();
+    }
+    /*
+     *	Open command-logging, if specified
+     */
+    if (lynx_cmd_logfile != 0) {
+	tildeExpand(&lynx_cmd_logfile, TRUE);
+	LYOpenCmdLogfile(argc, argv);
+    }
+#endif
+
     /*
      *	Set up the default jump file stuff. - FM
      */
@@ -1445,6 +1468,7 @@ PUBLIC int main ARGS2(
 	    StrAllocCopy(startfile, result);
 	    while (GetStdin(&buf)) {
 		fputs(buf, fp);
+		fputc('\n', fp);
 	    }
 	    FREE(buf);
 	    LYCloseTempFP(fp);
@@ -2984,6 +3008,16 @@ PRIVATE Parse_Args_Type Arg_Table [] =
       "cfg",		2|NEED_LYSTRING_ARG,	&lynx_cfg_file,
       "=FILENAME\nspecifies a lynx.cfg file other than the default"
    ),
+#ifdef EXP_CMD_LOGGING
+   PARSE_STR(
+       "cmd_log",	2|NEED_LYSTRING_ARG,	&lynx_cmd_logfile,
+       "=FILENAME\nlog keystroke commands to the given file"
+   ),
+   PARSE_STR(
+       "cmd_script",	2|NEED_LYSTRING_ARG,	&lynx_cmd_script,
+       "=FILENAME\nread keystroke commands from the given file (see -cmd_log)"
+   ),
+#endif
    PARSE_FUN(
       "child",		4|FUNCTION_ARG,		child_fun,
       "exit on left-arrow in startfile, and disable save to disk"
diff --git a/src/LYMainLoop.c b/src/LYMainLoop.c
index 3572942f..b7eaf53a 100644
--- a/src/LYMainLoop.c
+++ b/src/LYMainLoop.c
@@ -1300,6 +1300,10 @@ gettext("Enctype multipart/form-data not yet supported!  Cannot submit."));
 		    reloading = FALSE;
 		    return 0;
 		}
+
+	    if (run_external(links[curdoc.link].lname, TRUE))
+		return 0;
+
 	    /*
 	     *	Follow a normal link or anchor.
 	     */
@@ -1530,8 +1534,7 @@ PRIVATE void handle_LYK_ADD_BOOKMARK ARGS3(
 		 *  save either that or the link. - FM
 		 */
 		_statusline(BOOK_D_L_OR_CANCEL);
-		c = LYgetch_for(FOR_SINGLEKEY);
-		if (TOUPPER(c) == 'D') {
+		if ((c = LYgetch_single()) == 'D') {
 		    save_bookmark_link(curdoc.address, curdoc.title);
 		    *refresh_screen = TRUE; /* MultiBookmark support */
 		    goto check_add_bookmark_to_self;
@@ -1566,9 +1569,9 @@ PRIVATE void handle_LYK_ADD_BOOKMARK ARGS3(
 		     */
 		    _statusline(BOOK_L_OR_CANCEL);
 		}
-		c = LYgetch_for(FOR_SINGLEKEY);
+		c = LYgetch_single();
 	    }
-	    if (TOUPPER(c) == 'L') {
+	    if (c == 'L') {
 		if (curdoc.post_data != NULL &&
 		    links[curdoc.link].type == WWW_INTERN_LINK_TYPE) {
 		    /*
@@ -1606,8 +1609,7 @@ PRIVATE void handle_LYK_ADD_BOOKMARK ARGS3(
 	    return;
 	} else {
 	    _statusline(BOOK_D_OR_CANCEL);
-	    c = LYgetch_for(FOR_SINGLEKEY);
-	    if (TOUPPER(c) == 'D') {
+	    if (LYgetch_single() == 'D') {
 		save_bookmark_link(curdoc.address, curdoc.title);
 		*refresh_screen = TRUE; /* MultiBookmark support */
 	    } else {
@@ -2647,7 +2649,7 @@ PRIVATE void handle_LYK_EXTERN ARGS1(
 {
     if ((nlinks > 0) && (links[curdoc.link].lname != NULL))
     {
-	run_external(links[curdoc.link].lname);
+	run_external(links[curdoc.link].lname, FALSE);
 	*refresh_screen = TRUE;
     }
 }
@@ -2928,8 +2930,8 @@ PRIVATE BOOLEAN handle_LYK_HEAD ARGS1(
 	 * submit button.  - FM
 	 */
 	_statusline(HEAD_D_L_OR_CANCEL);
-	c = LYgetch_for(FOR_SINGLEKEY);
-	if (TOUPPER(c) == 'D') {
+	c = LYgetch_single();
+	if (c == 'D') {
 	    char *scheme = strncmp(curdoc.address, "LYNXIMGMAP:", 11) ?
 		curdoc.address : curdoc.address + 11;
 	    if (LYCanDoHEAD(scheme) != TRUE) {
@@ -2955,7 +2957,7 @@ PRIVATE BOOLEAN handle_LYK_HEAD ARGS1(
 		    }
 		}
 	    }
-	} else if (TOUPPER(c) == 'L') {
+	} else if (c == 'L') {
 	    if (links[curdoc.link].type != WWW_FORM_LINK_TYPE &&
 		strncmp(links[curdoc.link].lname, "http", 4) &&
 		strncmp(links[curdoc.link].lname,
@@ -3004,7 +3006,7 @@ PRIVATE BOOLEAN handle_LYK_HEAD ARGS1(
 		 * the current document, not the form link.  - FM
 		 */
 		_statusline(HEAD_D_OR_CANCEL);
-		c = LYgetch_for(FOR_SINGLEKEY);
+		c = LYgetch_single();
 	    } else {
 		/*
 		 * No links, so we can just assume that the user wants a HEAD
@@ -3012,7 +3014,7 @@ PRIVATE BOOLEAN handle_LYK_HEAD ARGS1(
 		 */
 		c = 'D';
 	    }
-	    if (TOUPPER(c) == 'D') {
+	    if (c == 'D') {
 		char *scheme = strncmp(curdoc.address, "LYNXIMGMAP:", 11) ?
 		    curdoc.address : curdoc.address + 11;
 		/*
@@ -5045,6 +5047,86 @@ PRIVATE void handle_LYK_digit ARGS6(
     return;
 }
 
+#ifdef SUPPORT_CHDIR
+
+/* original implementation by VH */
+PUBLIC void handle_LYK_CHDIR NOARGS
+{
+    static char buf[LY_MAXPATH];
+    char *p = NULL;
+
+    _statusline(gettext("cd to:"));
+    /* some people may prefer automatic clearing of the previous user input,
+       here, to do this, just uncomment next line - VH */
+    /* buf[0]='\0'; */
+    if (LYgetstr(buf, VISIBLE, sizeof(buf)-1, 0) < 0 || !*buf) {
+	HTInfoMsg(CANCELLED);
+	return;
+    }
+
+    if (*buf == '~' && !buf[1]) {
+	StrAllocCopy(p, Home_Dir());
+    } else if (*buf == '~') {
+	HTSprintf0(&p, "%s%s", Home_Dir(), buf+1);
+    } else {
+	StrAllocCopy(p, buf);
+    }
+
+    CTRACE((tfp, "changing directory to '%s'\n", p));
+    if (chdir(p)) {
+	switch (errno) {
+	case EACCES:
+	    HTInfoMsg(COULD_NOT_ACCESS_DIR);
+	    break;
+	case ENOENT:
+	    HTInfoMsg(gettext("No such directory"));
+	    break;
+	case ENOTDIR:
+	    HTInfoMsg(gettext("A component of path is not a directory"));
+	    break;
+	default:
+	    HTInfoMsg(gettext("failed to change directory"));
+	    break;
+	}
+    } else {
+#ifdef DIRED_SUPPORT
+	/*if in dired, load content of other directory*/
+	if (!no_dired_support
+	 && (lynx_edit_mode || (LYIsUIPage(curdoc.address, UIP_DIRED_MENU)))) {
+	    char buf2[LY_MAXPATH];
+	    char* tmp;
+	    char* addr = NULL;
+
+	    strcpy(buf2, p);
+	    Current_Dir(buf2);
+	    tmp = wwwName(buf2);
+
+	    StrAllocCopy(addr, "file://localhost");
+	    StrAllocCat(addr, tmp);
+	    if (tmp != buf2)
+	    /*since wwwName is nop on unix and allocates something on VMS and DOS*/
+		FREE(tmp);
+
+	    newdoc.address = addr;
+	    newdoc.isHEAD = FALSE;
+            StrAllocCopy(newdoc.title, gettext("A URL specified by the user"));
+	    FREE(newdoc.post_data);
+	    FREE(newdoc.post_content_type);
+	    FREE(newdoc.bookmark);
+	    newdoc.safe = FALSE;
+	    newdoc.internal_link = FALSE;
+	    /**force_load = TRUE;*/
+	    if (lynx_edit_mode) {
+		DIRED_UNCACHE_2;
+	    }
+	} else
+#endif
+	    HTInfoMsg(OPERATION_DONE);
+    }
+    FREE(p);
+}
+#endif
+
 /*
  *  Here's where we do all the work.
  *  mainloop is basically just a big switch dependent on the users input.
@@ -5144,7 +5226,7 @@ initialize:
 	refresh();
     }
 #endif /* USE_SLANG */
-    CTRACE((tfp,"Entering mainloop, startfile=%s\n",startfile));
+    CTRACE((tfp, "Entering mainloop, startfile=%s\n", startfile));
 
     if (form_post_data) {
 	StrAllocCopy(newdoc.post_data, form_post_data);
@@ -7168,7 +7250,11 @@ new_cmd:  /*
 
 	case LYK_DO_NOTHING:	/* pretty self explanatory */
 	    break;
-
+#ifdef SUPPORT_CHDIR
+	case LYK_CHDIR:
+	    handle_LYK_CHDIR();
+	    break;
+#endif
 	} /* end of BIG switch */
     }
 }
diff --git a/src/LYMainLoop.h b/src/LYMainLoop.h
index bb7dcf47..31804315 100644
--- a/src/LYMainLoop.h
+++ b/src/LYMainLoop.h
@@ -14,6 +14,9 @@ extern void LYMainLoop_pageDisplay PARAMS((int line_num));
 extern void LYSetNewline PARAMS((int value));
 extern void handle_LYK_TRACE_TOGGLE NOPARAMS;
 extern void handle_LYK_WHEREIS PARAMS((int cmd, BOOLEAN *refresh_screen));
+#ifdef SUPPORT_CHDIR
+extern void handle_LYK_CHDIR NOPARAMS; 
+#endif
 extern void repaint_main_statusline PARAMS((int for_what));
 
 #endif /* LYMAINLOOP_H */
diff --git a/src/LYOptions.c b/src/LYOptions.c
index 745d7944..b126cd0f 100644
--- a/src/LYOptions.c
+++ b/src/LYOptions.c
@@ -520,7 +520,7 @@ draw_options:
     addstr("'");
     addstr(TO_RETURN_SEGMENT);
 
-    while (TOUPPER(response) != 'R' &&
+    while (response != 'R' &&
 	   !LYisNonAlnumKeyname(response, LYK_PREV_DOC) &&
 	   response != '>' && !term_options &&
 	   response != 7 &&  response != 3) {
@@ -534,7 +534,7 @@ draw_options:
 	lynx_stop_prompt_color ();
 
 	refresh();
-	response = LYgetch_for(FOR_SINGLEKEY);
+	response = LYgetch_single();
 	if (term_options || response == 7 || response == 3)
 	    response = 'R';
 	if (LYisNonAlnumKeyname(response, LYK_REFRESH)) {
@@ -542,8 +542,7 @@ draw_options:
 	    goto draw_options;
 	}
 	switch (response) {
-	    case 'e':	/* Change the editor. */
-	    case 'E':
+	    case 'E':	/* Change the editor. */
 		if (no_editor) {
 		    _statusline(EDIT_DISABLED);
 		} else if (system_editor ) {
@@ -584,8 +583,7 @@ draw_options:
 		response = ' ';
 		break;
 
-	    case 'd':	/* Change the display. */
-	    case 'D':
+	    case 'D':	/* Change the display. */
 		if (x_display && *x_display) {
 		    LYstrncpy(display_option, x_display, sizeof(display_option) - 1);
 		} else {  /* clear the NONE */
@@ -650,8 +648,7 @@ draw_options:
 		response = ' ';
 		break;
 
-	    case 'l':	/* Change multibookmarks option. */
-	    case 'L':
+	    case 'L':	/* Change multibookmarks option. */
 		if (LYMBMBlocked) {
 		    _statusline(MULTIBOOKMARKS_DISALLOWED);
 		    response = ' ';
@@ -712,8 +709,7 @@ draw_options:
 		}
 		break;
 
-	    case 'b':	/* Change the bookmark page location. */
-	    case 'B':
+	    case 'B':	/* Change the bookmark page location. */
 		/*
 		 *  Anonymous users should not be allowed to
 		 *  change the bookmark page.
@@ -769,8 +765,7 @@ draw_options:
 		response = ' ';
 		break;
 
-	    case 'f':	/* Change ftp directory sorting. */
-	    case 'F':	/*  (also local for non-DIRED)	 */
+	    case 'F':	/* Change ftp directory sorting. */
 		/*
 		 *  Copy strings into choice array.
 		 */
@@ -808,8 +803,7 @@ draw_options:
 		}
 		break;
 
-	    case 'p': /* Change personal mail address for From headers. */
-	    case 'P':
+	    case 'P': /* Change personal mail address for From headers. */
 		if (personal_mail_address && *personal_mail_address) {
 		    LYstrncpy(display_option, personal_mail_address, sizeof(display_option) - 1);
 		} else {  /* clear the NONE */
@@ -845,8 +839,7 @@ draw_options:
 		response = ' ';
 		break;
 
-	    case 's':	/* Change case sensitivity for searches. */
-	    case 'S':
+	    case 'S':	/* Change case sensitivity for searches. */
 		/*
 		 *  Copy strings into choice array.
 		 */
@@ -947,8 +940,7 @@ draw_options:
 		}
 		break;
 
-	    case 'c':	/* Change display charset setting. */
-	    case 'C':
+	    case 'C':	/* Change display charset setting. */
 		if (!LYSelectPopups) {
 #ifndef ALL_CHARSETS_IN_O_MENU_SCREEN
 		    displayed_display_charset_idx = LYChooseBoolean(displayed_display_charset_idx,
@@ -1004,8 +996,7 @@ draw_options:
 		}
 		break;
 
-	    case 'o':	/* Change raw mode setting. */
-	    case 'O':
+	    case 'O':	/* Change raw mode setting. */
 		/*
 		 *  Copy strings into choice array.
 		 */
@@ -1029,8 +1020,7 @@ draw_options:
 		response = ' ';
 		break;
 
-	    case 'g':	/* Change language preference. */
-	    case 'G':
+	    case 'G':	/* Change language preference. */
 		if (language && *language) {
 		    LYstrncpy(display_option, language, sizeof(display_option) - 1);
 		} else {  /* clear the NONE */
@@ -1065,8 +1055,7 @@ draw_options:
 		response = ' ';
 		break;
 
-	    case 'h':	/* Change charset preference. */
-	    case 'H':
+	    case 'H':	/* Change charset preference. */
 		if (pref_charset && *pref_charset) {
 		    LYstrncpy(display_option, pref_charset, sizeof(display_option) - 1);
 		} else {  /* clear the NONE */
@@ -1101,8 +1090,7 @@ draw_options:
 		response = ' ';
 		break;
 
-	    case 'v':	/* Change VI keys setting. */
-	    case 'V':
+	    case 'V':	/* Change VI keys setting. */
 		/*
 		 *  Copy strings into choice array.
 		 */
@@ -1125,7 +1113,6 @@ draw_options:
 		break;
 
 	    case 'M':	/* Change emacs keys setting. */
-	    case 'm':
 		/*
 		 *  Copy strings into choice array.
 		 */
@@ -1148,7 +1135,6 @@ draw_options:
 		break;
 
 	    case 'W':	/* Change show dotfiles setting. */
-	    case 'w':
 		if (no_dotfiles) {
 		    _statusline(DOTFILE_ACCESS_DISABLED);
 		} else {
@@ -1170,8 +1156,7 @@ draw_options:
 		response = ' ';
 		break;
 
-	    case 't':	/* Change select popups setting. */
-	    case 'T':
+	    case 'T':	/* Change select popups setting. */
 		/*
 		 *  Copy strings into choice array.
 		 */
@@ -1315,8 +1300,7 @@ draw_options:
 		response = ' ';
 		break;
 
-	    case 'k':	/* Change keypad mode. */
-	    case 'K':
+	    case 'K':	/* Change keypad mode. */
 		/*
 		 *  Copy strings into choice array.
 		 */
@@ -1359,8 +1343,7 @@ draw_options:
 		}
 		break;
 
-	    case 'n':	/* Change line editor key bindings. */
-	    case 'N':
+	    case 'N':	/* Change line editor key bindings. */
 		if (!LYSelectPopups) {
 		    current_lineedit = LYChooseBoolean(current_lineedit,
 						      L_Lineed, -1,
@@ -1383,8 +1366,7 @@ draw_options:
 		break;
 
 #ifdef EXP_KEYBOARD_LAYOUT
-	    case 'y':	/* Change keyboard layout */
-	    case 'Y':
+	    case 'Y':	/* Change keyboard layout */
 		if (!LYSelectPopups) {
 		    current_layout = LYChooseBoolean(current_layout,
 						      L_Layout, -1,
@@ -1408,8 +1390,7 @@ draw_options:
 #endif /* EXP_KEYBOARD_LAYOUT */
 
 #ifdef DIRED_SUPPORT
-	    case 'i':	/* Change local directory sorting. */
-	    case 'I':
+	    case 'I':	/* Change local directory sorting. */
 		/*
 		 *  Copy strings into choice array.
 		 */
@@ -1445,8 +1426,7 @@ draw_options:
 		break;
 #endif /* DIRED_SUPPORT */
 
-	    case 'u':	/* Change user mode. */
-	    case 'U':
+	    case 'U':	/* Change user mode. */
 		/*
 		 *  Copy strings into choice array.
 		 */
@@ -1519,8 +1499,7 @@ draw_options:
 		}
 		break;
 
-	    case 'a':	/* Change user agent string. */
-	    case 'A':
+	    case 'A':	/* Change user agent string. */
 		if (!no_useragent) {
 		    if (LYUserAgent && *LYUserAgent) {
 			LYstrncpy(display_option, LYUserAgent, sizeof(display_option) - 1);
@@ -1569,8 +1548,7 @@ draw_options:
 		break;
 
 #if defined(ENABLE_OPTS_CHANGE_EXEC) && (defined(EXEC_LINKS) || defined(EXEC_SCRIPTS))
-	    case 'x':	/* Change local exec restriction. */
-	    case 'X':
+	    case 'X':	/* Change local exec restriction. */
 		if (exec_frozen && !LYSelectPopups) {
 		    _statusline(CHANGE_OF_SETTING_DISALLOWED);
 		    response = ' ';
@@ -1665,8 +1643,7 @@ draw_options:
 		}
 		break;
 
-	    case 'r':	/* Return to document (quit options menu). */
-	    case 'R':
+	    case 'R':	/* Return to document (quit options menu). */
 		break;
 
 	    default:
@@ -1732,9 +1709,9 @@ PRIVATE int boolean_choice ARGS4(
     while (1) {
 	move(line, col);
 	if (term_options == FALSE) {
-	    response = LYgetch_for(FOR_SINGLEKEY);
+	    response = LYgetch_single();
 	}
-	if (term_options || response == 7 || response == 3) {
+	if (term_options || response == 7) {
 	     /*
 	      *  Control-C or Control-G.
 	      */
@@ -1972,7 +1949,7 @@ draw_bookmark_list:
 	lynx_stop_prompt_color ();
 
 	refresh();
-	response = (def_response ? def_response : LYgetch_for(FOR_SINGLEKEY));
+	response = (def_response ? def_response : LYgetch_single());
 	def_response = 0;
 
 	/*
@@ -2038,7 +2015,7 @@ draw_bookmark_list:
 	 *  that way.
 	 */
 	for (a = 0; a <= MBM_V_MAXFILES; a++) {
-	    if ((TOUPPER(response) - 'A') == a) {
+	    if ((response - 'A') == a) {
 		if (LYlines < (MBM_V_MAXFILES + MULTI_OFFSET)) {
 		    if (MBM_current == 1 && a > (MBM_V_MAXFILES/2)) {
 			MBM_current = 2;
@@ -2519,8 +2496,8 @@ redraw:
 #endif /* USE_SLANG  */
 
 	term_options = FALSE;
-	c = LYgetch_for(FOR_CHOICE);
-	if (term_options || c == 3 || c == 7) {
+	c = LYgetch_choice();
+	if (term_options || c == 7) {
 	    cmd = LYK_QUIT;
 #ifndef USE_SLANG
 	} else if (c == MOUSE_KEY) {
diff --git a/src/LYPrint.c b/src/LYPrint.c
index cd7242ae..a7024688 100644
--- a/src/LYPrint.c
+++ b/src/LYPrint.c
@@ -1037,8 +1037,8 @@ PRIVATE void send_file_to_screen ARGS3(
 	Lpansi = FALSE;
     } else {
 	fprintf(stdout,"\n\n%s", PRESS_RETURN_TO_FINISH);
-	fflush(stdout);  /* refresh to screen */
-	LYgetch();  /* grab some user input to pause */
+	fflush(stdout);		/* refresh to screen */
+	(void) LYgetch();	/* grab some user input to pause */
 #ifdef VMS
 	HadVMSInterrupt = FALSE;
 #endif /* VMS */
diff --git a/src/LYReadCFG.c b/src/LYReadCFG.c
index d72c04bb..b23f0a28 100644
--- a/src/LYReadCFG.c
+++ b/src/LYReadCFG.c
@@ -165,6 +165,7 @@ PRIVATE void add_item_to_list ARGS2(
     cur_item->name = NULL;
     cur_item->command = NULL;
     cur_item->always_enabled = FALSE;
+    cur_item->override_primary_action = FALSE;
 
     /*
      *	Find first unescaped colon and process fields
@@ -196,7 +197,13 @@ PRIVATE void add_item_to_list ARGS2(
 	    remove_backslashes(cur_item->command);
 	}
 	if (*next_colon++) {
-	    cur_item->always_enabled = is_true(next_colon);
+	    colon = next_colon;
+	    if ((next_colon = strchr(colon,':')) != 0)
+		*next_colon++ = '\0';		
+	    cur_item->always_enabled = is_true(colon);
+	    if (next_colon) {
+		cur_item->override_primary_action = is_true(next_colon);
+	    }
 	}
     }
 }
@@ -1436,6 +1443,9 @@ static Config_Type Config_Table [] =
      PARSE_ENV("gopher_proxy", CONF_ENV, 0 ),
      PARSE_SET("gotobuffer", CONF_BOOL, &goto_buffer),
      PARSE_STR("helpfile", CONF_STR, &helpfile),
+#ifdef MARK_HIDDEN_LINKS
+     PARSE_STR("hidden_link_marker", CONF_STR, &hidden_link_marker),
+#endif     
      PARSE_SET("historical_comments", CONF_BOOL, &historical_comments),
 #ifdef USE_PRETTYSRC
      PARSE_FUN("htmlsrc_attrname_xform", CONF_FUN, read_htmlsrc_attrname_xform),
@@ -1731,7 +1741,7 @@ PRIVATE void do_read_cfg ARGS5(
 	cfg_filename = mypath;
     }
     if ((fp = fopen(cfg_filename, TXT_R)) == 0) {
-	CTRACE((tfp,"lynx.cfg file not found as %s\n",cfg_filename));
+	CTRACE((tfp, "lynx.cfg file not found as '%s'\n", cfg_filename));
 	return;
     }
     have_read_cfg = TRUE;
@@ -1743,7 +1753,7 @@ PRIVATE void do_read_cfg ARGS5(
     if (show_cfg) {
 	time_t t;
 	time(&t);
-	printf("### Lynx %s, at %s", LYNX_VERSION, ctime(&t));
+	printf("### %s %s, at %s", LYNX_NAME, LYNX_VERSION, ctime(&t));
     }
 #endif
     while (LYSafeGets(&buffer, fp) != 0) {
diff --git a/src/LYStrings.c b/src/LYStrings.c
index ecc6effe..723345d7 100644
--- a/src/LYStrings.c
+++ b/src/LYStrings.c
@@ -33,9 +33,9 @@
 #include <LYMainLoop.h>
 #endif
 
+#include <LYShowInfo.h>
 #include <LYLeaks.h>
 
-extern unsigned short *LYKbLayout;
 extern BOOL HTPassHighCtrlRaw;
 
 #if defined(WIN_EX)
@@ -1418,7 +1418,7 @@ PRIVATE int myGetChar NOARGS
     return i;
 }
 
-PUBLIC int LYgetch_for ARGS1(
+PRIVATE int LYgetch_for ARGS1(
 	int,	code)
 {
    SLang_Key_Type *key;
@@ -1463,7 +1463,7 @@ PUBLIC int LYgetch_for ARGS1(
  */
 #define found_CSI(first,second) ((second) == '[' || (first) == 155)
 
-PUBLIC int LYgetch_for ARGS1(
+PRIVATE int LYgetch_for ARGS1(
 	int,	code)
 {
     int a, b, c, d = -1;
@@ -1472,11 +1472,7 @@ PUBLIC int LYgetch_for ARGS1(
 
     have_levent = 0;
 
-#if defined(IGNORE_CTRL_C) || defined(USE_GETCHAR) || !defined(NCURSES) || \
-    (HAVE_KEYPAD && defined(KEY_RESIZE)) || \
-    (defined(NCURSES) && defined(USE_MOUSE) && !defined(DOSPATH))
 re_read:
-#endif /* IGNORE_CTRL_C || USE_GETCHAR etc. */
 #if !defined(UCX) || !defined(VAXC) /* errno not modifiable ? */
     if (errno == EINTR)
 	set_errno(0);		/* reset - kw */
@@ -2074,8 +2070,8 @@ re_read:
 #define HIST_CMD_2	12
 #define V_CMD_AREA	1
 
-		int left,right;
-		extern BOOLEAN system_is_NT;
+		int left = H_CMD_AREA;
+		int right = (LYcols - H_CMD_AREA);
 		/* yes, I am assuming that my screen will be a certain width. */
 
 		int tick_count;
@@ -2083,8 +2079,6 @@ re_read:
 		char mouse_info[128];
 		static int old_click = 0;	/* [m Sec] */
 
-		left = H_CMD_AREA;
-		right = (LYcols - H_CMD_AREA);
 		c = -1;
 		mouse_link = -1;
 
@@ -2295,7 +2289,43 @@ re_read:
 
 PUBLIC int LYgetch NOARGS
 {
-    return LYgetch_for(FOR_PANEL);
+    return LYReadCmdKey(FOR_PANEL);
+}
+
+/*
+ * Read a single keystroke, allows mouse-selection.
+ */
+PUBLIC int LYgetch_choice NOARGS
+{
+    int ch = LYReadCmdKey(FOR_CHOICE);
+    if (ch == 3)
+	ch = 7;			/* treat ^C the same as ^G */
+    return ch;
+}
+
+/*
+ * Read a single keystroke, allows mouse events.
+ */
+PUBLIC int LYgetch_input NOARGS
+{
+    int ch = LYReadCmdKey(FOR_INPUT);
+    if (ch == 3)
+	ch = 7;			/* treat ^C the same as ^G */
+    return ch;
+}
+
+/*
+ * Read a single keystroke, ignoring case by translating it to uppercase.
+ * Ignore mouse events, if any.
+ */
+PUBLIC int LYgetch_single NOARGS
+{
+    int ch = LYReadCmdKey(FOR_SINGLEKEY);
+    if (ch == 3)
+	ch = 7;			/* treat ^C the same as ^G */
+    else if (ch > 0 && ch < 256)
+	ch = TOUPPER(ch);	/* will ignore case of result */
+    return ch;
 }
 
 /*
@@ -3220,7 +3250,7 @@ again:
 	if (refresh_mb)
 	    LYRefreshEdit(&MyEdit);
 #endif /* SUPPORT_MULTIBYTE_EDIT */
-	ch = LYgetch_for(FOR_PROMPT);
+	ch = LYReadCmdKey(FOR_PROMPT);
 #ifdef SUPPORT_MULTIBYTE_EDIT
 #ifdef CJK_EX	/* for SJIS code */
 	if (!refresh_mb
@@ -4260,3 +4290,89 @@ PUBLIC void base64_encode ARGS3(
 }
 
 #endif /* EXP_FILE_UPLOAD */
+
+#ifdef EXP_CMD_LOGGING
+PRIVATE FILE *cmd_logfile;
+PRIVATE FILE *cmd_script;
+
+PUBLIC void LYOpenCmdLogfile ARGS2(
+	int,		argc,
+	char **,	argv)
+{
+    int n;
+
+    if (lynx_cmd_logfile != 0) {
+	cmd_logfile = LYNewTxtFile(lynx_cmd_logfile);
+	if (cmd_logfile != 0) {
+	    fprintf(cmd_logfile, "# Command logfile created by %s %s (%s)\n",
+		LYNX_NAME, LYNX_VERSION, LYVersionDate());
+	    for (n = 0; n < argc; n++) {
+		fprintf(cmd_logfile, "# Arg%d = %s\n", n, argv[n]);
+	    }
+	}
+    }
+}
+
+PUBLIC void LYOpenCmdScript NOARGS
+{
+    if (lynx_cmd_script != 0) {
+	cmd_script = fopen(lynx_cmd_script, "r");
+    }
+}
+
+PUBLIC int LYReadCmdKey ARGS1(
+	int,	mode)
+{
+    int ch = -1;
+
+    if (cmd_script != 0) {
+	char *buffer = 0;
+	char *src;
+	char *tmp;
+
+	while (LYSafeGets(&buffer, cmd_script) != 0) {
+	    LYTrimTrailing(buffer);
+	    src = LYSkipBlanks(buffer);
+	    tmp = LYSkipNonBlanks(src);
+	    if (tmp - src != 3
+	     || strncasecomp(src, "key", 3))
+		continue;
+	    src = LYSkipBlanks(tmp);
+	    if ((ch = LYStringToKeycode(src)) >= 0) {
+		refresh();
+		break;
+	    }
+	}
+	FREE(buffer);
+    } else {
+	ch = LYgetch_for(mode);
+    }
+    LYWriteCmdKey(ch);
+    return ch;
+}
+
+/*
+ * Write a LYKeymapCode 'ch' to the logfile.
+ */
+PUBLIC void LYWriteCmdKey ARGS1(
+	int,	ch)
+{
+    if (cmd_logfile != 0) {
+	fprintf(cmd_logfile, "key %s\n", LYKeycodeToString(ch, FALSE));
+    }
+}
+
+PUBLIC void LYCloseCmdLogfile NOARGS
+{
+    if (cmd_logfile != 0) {
+	fclose(cmd_logfile);
+	cmd_logfile = 0;
+    }
+    if (cmd_script != 0) {
+	fclose(cmd_script);
+	cmd_script = 0;
+    }
+    FREE(lynx_cmd_logfile);
+    FREE(lynx_cmd_script);
+}
+#endif /* EXP_CMD_LOGGING */
diff --git a/src/LYStrings.h b/src/LYStrings.h
index 79db1a0c..32aeed8c 100644
--- a/src/LYStrings.h
+++ b/src/LYStrings.h
@@ -19,8 +19,9 @@ extern char * LYstrncpy PARAMS((
 	int		n));
 extern void ena_csi PARAMS((BOOLEAN flag));
 extern int LYgetch NOPARAMS;
-extern int LYgetch_for PARAMS((
-	int		code));
+extern int LYgetch_choice NOPARAMS;
+extern int LYgetch_input NOPARAMS;
+extern int LYgetch_single NOPARAMS;
 extern int LYgetstr PARAMS((
 	char *		inputline,
 	int		hidden,
@@ -80,6 +81,17 @@ extern char * SNACat PARAMS((
 
 extern char *LYSafeGets PARAMS((char ** src, FILE * fp));
 
+#ifdef EXP_CMD_LOGGING
+extern int LYReadCmdKey PARAMS((int mode));
+extern void LYCloseCmdLogfile NOPARAMS;
+extern void LYOpenCmdLogfile PARAMS((int argc, char **argv));
+extern void LYOpenCmdScript NOPARAMS;
+extern void LYWriteCmdKey PARAMS((int ch));
+#else
+#define LYReadCmdKey(mode) LYgetch_for(mode)
+#define LYCloseCmdLogfile() /* nothing */
+#endif
+
 #ifdef EXP_FILE_UPLOAD
 extern void base64_encode PARAMS((char * dest, char * src, int len));
 #endif
@@ -105,7 +117,7 @@ extern void base64_encode PARAMS((char * dest, char * src, int len));
 #define REMOVE_KEY	269	/* 0x10D */
 #define DO_NOTHING	270	/* 0x10E */
 #define BACKTAB_KEY	271	/* 0x10F */
-#define MOUSE_KEY	0x11d	/* 0x11D */
+#define MOUSE_KEY	285	/* 0x11D */
 /*  ***** NOTES: *****
     If you add definitions for new lynxkeycodes to the above list that
     need to be mapped to LYK_* lynxactioncodes -
@@ -136,7 +148,6 @@ extern void base64_encode PARAMS((char * dest, char * src, int len));
     to be less than LKC_ISLKC (even if KEYMAP_SIZE is increased).
 */
 
-
 #  define FOR_PANEL	0	/* normal screen, also LYgetch default */
 #  define FOR_CHOICE	1	/* mouse menu */
 #  define FOR_INPUT	2	/* form input and textarea field */
diff --git a/src/LYStructs.h b/src/LYStructs.h
index 6e16e456..31b8afe5 100644
--- a/src/LYStructs.h
+++ b/src/LYStructs.h
@@ -88,6 +88,13 @@ typedef struct _lynx_html_item_type {
 					* not to disable the printer
 					* when the no_print option is on
 					*/
+    BOOL override_primary_action;	/* whether primary action will be
+					* overridden by this - e.g. this allows
+					* invoking user's MUA when mailto: link
+					* is activated using normal "activate"
+					* command. This field is only examined
+					* by code that handles EXTERNAL command.
+					*/
 } lynx_html_item_type;
 
 /* for printer commands */
diff --git a/src/LYStyle.c b/src/LYStyle.c
index 9292af8c..517e1832 100644
--- a/src/LYStyle.c
+++ b/src/LYStyle.c
@@ -1,6 +1,6 @@
 /* character level styles for Lynx
  * (c) 1996 Rob Partington -- donated to the Lyncei (if they want it :-)
- * @Id: LYStyle.c 1.35 Tue, 30 Nov 1999 20:33:02 -0700 dickey @
+ * @Id: LYStyle.c 1.36 Fri, 23 Jun 2000 08:15:08 -0700 dickey @
  */
 #include <HTUtils.h>
 #include <HTML.h>
@@ -440,7 +440,7 @@ PRIVATE int style_readFromFileREC ARGS2(char*, file, int, toplevel)
     if (!fh)
     {
 	/* this should probably be an alert or something */
-	CTRACE((tfp, "CSS:Can't open style file %s, using defaults\n", file));
+	CTRACE((tfp, "CSS:Can't open style file '%s', using defaults\n", file));
 	return -1;
     }
 
diff --git a/src/LYUtils.c b/src/LYUtils.c
index 1c545c75..5a298057 100644
--- a/src/LYUtils.c
+++ b/src/LYUtils.c
@@ -7088,9 +7088,16 @@ PUBLIC BOOLEAN LYValidateFilename ARGS2(
 #if defined(__DJGPP__) || defined(_WINDOWS)
     if (strchr(result, ':') != NULL)
 	cp = NULL;
-    else
+    else 
 #endif /*  __DJGPP__ || _WINDOWS */
-	cp = original_dir;
+	{
+#ifdef SUPPORT_CHDIR
+	    static char buf[LY_MAXPATH];
+	    cp = Current_Dir(buf);	    
+#else
+	    cp = original_dir;
+#endif
+	}
     }
     else
 #endif /* __EMX__*/
diff --git a/src/LYexit.c b/src/LYexit.c
index 3a1adacf..9eafb3da 100644
--- a/src/LYexit.c
+++ b/src/LYexit.c
@@ -10,6 +10,7 @@
 #include <LYSignal.h>
 #include <LYMainLoop.h>
 #endif /* !VMS */
+#include <LYStrings.h>
 #include <LYClean.h>
 
 /*
@@ -147,6 +148,8 @@ PUBLIC void LYexit ARGS1(
     LYCloselog();
 #endif /* !VMS && SYSLOG_REQUESTED_URLS */
 
+    LYCloseCmdLogfile();
+
 #ifdef exit
 /*  Make sure we use stdlib exit and not LYexit. - GAB
 */