about summary refs log tree commit diff stats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/DefaultStyle.c13
-rw-r--r--src/GridText.c604
-rw-r--r--src/GridText.h9
-rw-r--r--src/HTAlert.c109
-rw-r--r--src/HTAlert.h14
-rw-r--r--src/HTFWriter.c44
-rw-r--r--src/HTInit.c6
-rw-r--r--src/HTML.c294
-rw-r--r--src/LYBookmark.c486
-rw-r--r--src/LYBookmark.h14
-rw-r--r--src/LYCgi.c6
-rw-r--r--src/LYCharUtils.c2
-rw-r--r--src/LYCurses.h23
-rw-r--r--src/LYForms.c597
-rw-r--r--src/LYGetFile.c140
-rw-r--r--src/LYGlobalDefs.h14
-rw-r--r--src/LYHistory.c14
-rw-r--r--src/LYKeymap.c16
-rw-r--r--src/LYKeymap.h1
-rw-r--r--src/LYLocal.c424
-rw-r--r--src/LYLocal.h7
-rw-r--r--src/LYMail.c157
-rw-r--r--src/LYMail.h23
-rw-r--r--src/LYMain.c139
-rw-r--r--src/LYMainLoop.c525
-rw-r--r--src/LYMap.c2
-rw-r--r--src/LYNews.c1
-rw-r--r--src/LYOptions.c668
-rw-r--r--src/LYOptions.h27
-rw-r--r--src/LYPrint.c123
-rw-r--r--src/LYReadCFG.c18
-rw-r--r--src/LYShowInfo.c88
-rw-r--r--src/LYStructs.h9
-rw-r--r--src/LYUtils.c133
-rw-r--r--src/LYUtils.h5
-rw-r--r--src/LYrcFile.c180
36 files changed, 3724 insertions, 1211 deletions
diff --git a/src/DefaultStyle.c b/src/DefaultStyle.c
index 1cbe4252..b4ee349a 100644
--- a/src/DefaultStyle.c
+++ b/src/DefaultStyle.c
@@ -277,18 +277,11 @@ PRIVATE HTStyle HTStyleExample = {
 	&HTStyleGlossaryCompact6,  "Example", "XMP",
 	HT_FONT, 1.0, HT_BLACK,		0, 0,
 	0, 0, 0, HT_LEFT,		1, 0,	tabs_8,
-	NO, NO, 1, 1,			0
-};	
-
-PRIVATE HTStyle HTStyleStyle = { /* HTML 3.0 STYLE - FM */
-	&HTStyleExample,  "Style", "STYLE",
-	HT_FONT, 1.0, HT_BLACK,		0, 0,
-	0, 0, 0, HT_LEFT,		1, 0,	tabs_8,
-	NO, NO, 1, 1,			0
+	NO, NO, 0, 0,			0
 };	
 
 PRIVATE HTStyle HTStylePreformatted = {
-	&HTStyleStyle,  	"Preformatted", "PRE",
+	&HTStyleExample,  	"Preformatted", "PRE",
 	HT_FONT, 1.0, HT_BLACK,		0, 0,
 	0, 0, 0, HT_LEFT,		1, 0,	tabs_8,
 	NO, YES, 0, 0,			0
@@ -298,7 +291,7 @@ PRIVATE HTStyle HTStyleListing = {
 	&HTStylePreformatted,  "Listing", "LISTING",
 	HT_FONT, 1.0, HT_BLACK,		0, 0,
 	0, 0, 0, HT_LEFT,		1, 0,	tabs_8,
-	NO, NO, 1, 1,			0 };	
+	NO, NO, 0, 0,			0 };	
 
 PRIVATE HTStyle HTStyleAddress = {
 	&HTStyleListing,  "Address", "ADDRESS",
diff --git a/src/GridText.c b/src/GridText.c
index 2c35304d..e988c7ba 100644
--- a/src/GridText.c
+++ b/src/GridText.c
@@ -68,10 +68,10 @@ PUBLIC char * HTAppVersion = LYNX_VERSION;        /* Application version */
 
 PUBLIC int HTFormNumber = 0;
 PUBLIC int HTFormFields = 0;
-PUBLIC char * HTCurSelectGroup = NULL;		/* form select group name */
-PUBLIC int HTCurSelectGroupType = F_RADIO_TYPE;	/* group type */
-PUBLIC char * HTCurSelectGroupSize = NULL;	/* length of select */
-PRIVATE char * HTCurSelectedOptionValue = NULL;	/* select choice */
+PUBLIC char * HTCurSelectGroup = NULL;		/* Form select group name */
+PUBLIC int HTCurSelectGroupType = F_RADIO_TYPE;	/* Group type */
+PUBLIC char * HTCurSelectGroupSize = NULL;	/* Length of select */
+PRIVATE char * HTCurSelectedOptionValue = NULL;	/* Select choice */
 
 PUBLIC char * checked_box = "[X]";
 PUBLIC char * unchecked_box = "[ ]";
@@ -157,7 +157,8 @@ struct _HText {
 /*
  *  Boring static variable used for moving cursor across
  */
-#define UNDERSCORES(n) (&underscore_string[(MAX_LINE-1) - (n)])
+#define UNDERSCORES(n) \
+ ((n) >= MAX_LINE ? underscore_string : &underscore_string[(MAX_LINE-1)] - (n))
 
 /*
  *	Memory leak fixed.
@@ -192,7 +193,7 @@ PUBLIC HText *	HText_new ARGS1(HTParentAnchor *,anchor)
     int status, VMType=3, VMTotal;
 #endif /* VMS && VAXC && !__DECC */
     HTLine * line = NULL;
-    HText * self = (HText *) calloc(sizeof(*self),1);
+    HText * self = (HText *) calloc(1, sizeof(*self));
     if (!self) return self;
     
 #if defined(VMS) && defined (VAXC) && !defined(__DECC)
@@ -239,7 +240,7 @@ PUBLIC HText *	HText_new ARGS1(HTParentAnchor *,anchor)
 #endif /* VMS && VAXC && !__DECC */
     }
     
-    line = self->last_line = (HTLine *)calloc(sizeof(char),LINE_SIZE(MAX_LINE));
+    line = self->last_line = (HTLine *)calloc(1, LINE_SIZE(MAX_LINE));
     if (line == NULL)
         outofmem(__FILE__, "HText_New");
     line->next = line->prev = line;
@@ -281,14 +282,20 @@ PUBLIC HText *	HText_new ARGS1(HTParentAnchor *,anchor)
      *	Check to see if our underline and star_string need initialization
      *		if the underline is not filled with dots.
      */ 
-    if (underscore_string[0] != '.') { /* Make a line */
+    if (underscore_string[0] != '.') {
         char *p;
-        for (p=underscore_string; p<underscore_string+(MAX_LINE-1); p++)
-            *p = '.';           /* Used for printfs later */
+	/*
+	 * Create and array of dots for the UNDERSCORES macro. - FM
+	 */
+	memset(underscore_string, '.', (MAX_LINE-1));
         underscore_string[(MAX_LINE-1)] = '\0';
-        for (p=star_string; p<star_string+(LINESIZE-1); p++)
-            *p = '_';           /* Used for printfs later */
-        star_string[(LINESIZE-1)] = '\0';
+        underscore_string[MAX_LINE] = '\0';
+	/*
+	 * Create and array of underscores for the STARS macro. - FM
+	 */
+	memset(star_string, '_', (MAX_LINE-1));
+        star_string[(MAX_LINE-1)] = '\0';
+        star_string[MAX_LINE] = '\0';
     }
 
     underline_on = FALSE; /* reset */
@@ -594,7 +601,8 @@ PRIVATE void display_title ARGS1(HText *,text)
     if (cp == NULL)
         outofmem(__FILE__, "display_title");
     if (HTCJK != NOCJK) {
-        if (*title && (tmp = (unsigned char *)calloc(1, strlen(title) + 1))) {
+        if (*title &&
+	    (tmp = (unsigned char *)calloc(1, (strlen(title) + 1)))) {
 	    if (kanji_code == EUC) {
 	        TO_EUC((unsigned char *)title, tmp);
 	    } else if (kanji_code == SJIS) {
@@ -878,7 +886,7 @@ PRIVATE void display_page ARGS3(HText *,text, int,line_number, char *, target)
 	    }
 	} 
 
-	if (Anchor_ptr == text->last_anchor)
+	if (Anchor_ptr == text->last_anchor || nlinks == MAXLINKS)
 	    break;
     }
 
@@ -953,7 +961,7 @@ PRIVATE void split_line ARGS2(HText *,text, int,split)
     HTLine * previous = text->last_line;
     int ctrl_chars_on_previous_line = 0;
     char * cp;
-    HTLine * line = (HTLine *)calloc(sizeof(char), LINE_SIZE(MAX_LINE));
+    HTLine * line = (HTLine *)calloc(1, LINE_SIZE(MAX_LINE));
 
     ctrl_chars_on_this_line = 0; /*reset since we are going to a new line*/
     HTML_Last_Char = ' ';
@@ -1126,7 +1134,7 @@ PRIVATE void split_line ARGS2(HText *,text, int,split)
      *  problem with Ultrix (4.2) : realloc() is not declared properly.
      *  So we'll use a substitute for realloc.
      */
-    temp = (HTLine *)calloc(sizeof(char), LINE_SIZE(previous->size));
+    temp = (HTLine *)calloc(1, LINE_SIZE(previous->size));
     if (temp == NULL)
         outofmem(__FILE__, "split_line");
     memcpy(temp, previous, LINE_SIZE(previous->size));
@@ -1639,7 +1647,7 @@ PUBLIC void HText_beginAnchor ARGS2(HText *,text, HTChildAnchor *,anc)
 {
     char marker[16];
 
-    TextAnchor * a = (TextAnchor *) calloc(sizeof(*a),1);
+    TextAnchor * a = (TextAnchor *) calloc(1, sizeof(*a));
     
     if (a == NULL)
         outofmem(__FILE__, "HText_beginAnchor");
@@ -1938,7 +1946,8 @@ PUBLIC HTChildAnchor * HText_childNumber ARGS1(int,number)
     return (HTChildAnchor *)0;	/* Fail */
 }
 
-/* HTGetLinkInfo returns some link info based on the number
+/*
+ *  HTGetLinkInfo returns some link info based on the number.
  */
 PUBLIC int HTGetLinkInfo ARGS3(int, number, char **, hightext, char **, lname)
 {
@@ -1969,16 +1978,18 @@ PUBLIC int HTGetLinkInfo ARGS3(int, number, char **, hightext, char **, lname)
     return(NO);
 }
 
-/* HText_getNumOfLines returns the number of lines in the
- * current document
+/*
+ *  HText_getNumOfLines returns the number of lines in the
+ *  current document.
  */
 PUBLIC int HText_getNumOfLines NOARGS
 {
      return(HTMainText->lines);
 }
 
-/* HText_getTitle returns the title of the
- * current document
+/*
+ *  HText_getTitle returns the title of the
+ *  current document.
  */
 PUBLIC char * HText_getTitle NOARGS
 {
@@ -1986,12 +1997,47 @@ PUBLIC char * HText_getTitle NOARGS
 }
 
 /*
- * HText_pageDisplay displays a screen of text
- * starting from the line 'line_num'-1
- * this is the primary call for lynx
+ *  HText_getSugFname returns the suggested filename of the current
+ *  document (normally derived from a Content-Disposition header with
+ *  file; filename=name.suffix). - FM
+ */
+PUBLIC char * HText_getSugFname NOARGS
+{
+   return((char *) HTAnchor_SugFname(HTMainText->node_anchor));
+}
+
+/*
+ *  HText_getLastModified returns the Last-Modified header
+ *  if available, for the current document. - FM
  */
-extern char is_www_index;
+PUBLIC char * HText_getLastModified NOARGS
+{
+   return((char *) HTAnchor_last_modified(HTMainText->node_anchor));
+}
 
+/*
+ *  HText_getDate returns the Date header
+ *  if available, for the current document. - FM
+ */
+PUBLIC char * HText_getDate NOARGS
+{
+   return((char *) HTAnchor_date(HTMainText->node_anchor));
+}
+
+/*
+ *  HText_getServer returns the Server header
+ *  if available, for the current document. - FM
+ */
+PUBLIC char * HText_getServer NOARGS
+{
+   return((char *) HTAnchor_server(HTMainText->node_anchor));
+}
+
+/*
+ *  HText_pageDisplay displays a screen of text
+ *  starting from the line 'line_num'-1
+ *  this is the primary call for lynx
+ */
 PUBLIC void HText_pageDisplay ARGS2(int,line_num, char *, target)
 {
     display_page(HTMainText, line_num-1, target);
@@ -2000,8 +2046,8 @@ PUBLIC void HText_pageDisplay ARGS2(int,line_num, char *, target)
 } 
 
 /*
- * HText_LinksInLines returns the number of links in the
- * 'lines' number of lines beginning with 'line_num'-1. - FM
+ *  HText_LinksInLines returns the number of links in the
+ *  'lines' number of lines beginning with 'line_num'-1. - FM
  */
 PUBLIC int HText_LinksInLines ARGS3(HText *,text, int,line_num, int,lines)
 {
@@ -2034,7 +2080,8 @@ PUBLIC void HText_setStale ARGS1(HText *,text)
 
 PUBLIC void HText_refresh ARGS1(HText *,text)
 {
-    if (text->stale) display_page(text, text->top_of_screen, "");
+    if (text->stale)
+        display_page(text, text->top_of_screen, "");
 }
 
 PUBLIC int HText_sourceAnchors ARGS1(HText *,text)
@@ -2679,8 +2726,7 @@ PUBLIC void www_user_search ARGS2(int,start_line, char *,target)
 	        www_search_result=count;
 		return;
 	    } else if (count > start_line) {  /* next line */
-    		_user_message("\"%s\" could not be found in this document",
-			      target);
+    		_user_message(STRING_NOT_FOUND, target);
     		sleep(MessageSecs);
 	        return;			/* end */
 	    } else {
@@ -2721,7 +2767,18 @@ PUBLIC  void  user_message ARGS2(char *,message, char *,argument)
  */
 PUBLIC char * HText_getOwner NOARGS
 {
-   return((char *)HTAnchor_owner(HTMainText->node_anchor));
+    return((char *)HTAnchor_owner(HTMainText->node_anchor));
+}
+
+/* HText_setMainTextOwner sets the owner for the
+ * current document
+ */
+PUBLIC void HText_setMainTextOwner ARGS1(CONST char *, owner)
+{
+    if (!HTMainText)
+        return;
+
+    HTAnchor_setOwner(HTMainText->node_anchor, owner);
 }
 
 /* HText_getRevTitle returns the RevTitle element of the
@@ -2730,7 +2787,7 @@ PUBLIC char * HText_getOwner NOARGS
  */
 PUBLIC char * HText_getRevTitle NOARGS
 {
-   return((char *)HTAnchor_RevTitle(HTMainText->node_anchor));
+    return((char *)HTAnchor_RevTitle(HTMainText->node_anchor));
 }
 
 PUBLIC void HTuncache_current_document NOARGS
@@ -2743,53 +2800,85 @@ PUBLIC void HTuncache_current_document NOARGS
 
 PUBLIC int HTisDocumentSource NOARGS
 {
-   return(HTMainText->source);
+    return(HTMainText->source);
 }
 
 PUBLIC char * HTLoadedDocumentURL NOARGS
 {
-   if (!HTMainText)
+    if (!HTMainText)
 	return ("");
 
-   if (HTMainText->node_anchor && HTMainText->node_anchor->address) 
+    if (HTMainText->node_anchor && HTMainText->node_anchor->address) 
        	return(HTMainText->node_anchor->address);
-   else
+    else
 	return ("");
 }
 
 PUBLIC char * HTLoadedDocumentPost_data NOARGS
 {
-   if (!HTMainText)
+    if (!HTMainText)
 	return ("");
 
-   if (HTMainText->node_anchor && HTMainText->node_anchor->post_data) 
+    if (HTMainText->node_anchor && HTMainText->node_anchor->post_data) 
        	return(HTMainText->node_anchor->post_data);
-   else
+    else
 	return ("");
 }
 
 PUBLIC char * HTLoadedDocumentTitle NOARGS
 {
-   if (!HTMainText)
+    if (!HTMainText)
 	return ("");
 
-   if (HTMainText->node_anchor && HTMainText->node_anchor->title) 
+    if (HTMainText->node_anchor && HTMainText->node_anchor->title) 
        	return(HTMainText->node_anchor->title);
-   else
+    else
 	return ("");
 }
 
 PUBLIC BOOLEAN HTLoadedDocumentIsHEAD NOARGS
 {
-   if (!HTMainText)
+    if (!HTMainText)
 	return (FALSE);
 
-   if (HTMainText->node_anchor && HTMainText->node_anchor->isHEAD) 
+    if (HTMainText->node_anchor && HTMainText->node_anchor->isHEAD) 
        	return(HTMainText->node_anchor->isHEAD);
-   else
+    else
 	return (FALSE);
 }
 
+PUBLIC char * HTLoadedDocumentCharset NOARGS
+{
+    if (!HTMainText)
+	return (NULL);
+
+    if (HTMainText->node_anchor && HTMainText->node_anchor->charset) 
+       	return(HTMainText->node_anchor->charset);
+    else
+	return (NULL);
+}
+
+PUBLIC void HText_setNodeAnchorBookmark ARGS1(
+	CONST char *,	bookmark)
+{
+    if (!HTMainText)
+	return;
+
+    if (HTMainText->node_anchor)
+	HTAnchor_setBookmark(HTMainText->node_anchor, bookmark);
+}
+
+PUBLIC char * HTLoadedDocumentBookmark NOARGS
+{
+    if (!HTMainText)
+	return (NULL);
+
+    if (HTMainText->node_anchor && HTMainText->node_anchor->bookmark) 
+       	return(HTMainText->node_anchor->bookmark);
+    else
+	return (NULL);
+}
+
 PUBLIC int HText_LastLineSize ARGS1(HText *,text)
 {
     if (!text || !text->last_line || !text->last_line->size)
@@ -2921,6 +3010,7 @@ PRIVATE int HTFormMethod;
 PRIVATE char * HTFormAction = NULL;
 PRIVATE char * HTFormEnctype = NULL;
 PRIVATE char * HTFormTitle = NULL;
+PRIVATE BOOLEAN HTFormDisabled = FALSE;
 
 PUBLIC void HText_beginForm ARGS4(
 	char *,		action,
@@ -2931,7 +3021,11 @@ PUBLIC void HText_beginForm ARGS4(
     HTFormMethod = URL_GET_METHOD;
     HTFormNumber++;
     HTFormFields = 0;
+    HTFormDisabled = FALSE;
 
+    /*
+     *  Check the ACTION. - FM
+     */
     if (action != NULL) {
 	if (!strncmp(action, "mailto:", 7)) {
 	    HTFormMethod = URL_MAIL_METHOD;
@@ -2941,15 +3035,28 @@ PUBLIC void HText_beginForm ARGS4(
     else
 	StrAllocCopy(HTFormAction, HTLoadedDocumentURL());
     
-    if (method != NULL)
-	if (!strcasecomp(method,"post") && HTFormMethod != URL_MAIL_METHOD)
-	   HTFormMethod = URL_POST_METHOD;
+    /*
+     *  Check the METHOD. - FM
+     */
+    if (method != NULL && HTFormMethod != URL_MAIL_METHOD)
+	if (!strcasecomp(method,"post") || !strcasecomp(method,"pget"))
+	    HTFormMethod = URL_POST_METHOD;
 
-    if ((enctype != NULL) && *enctype)
+    /*
+     *  Check the ENCTYPE. - FM
+     */
+    if ((enctype != NULL) && *enctype) {
         StrAllocCopy(HTFormEnctype, enctype);
-    else
+	if (HTFormMethod != URL_MAIL_METHOD &&
+	    !strncasecomp(enctype, "multipart/form-data", 19)) 
+	    HTFormMethod = URL_POST_METHOD;
+    } else {
         FREE(HTFormEnctype);
+    }
 
+    /*
+     *  Check the TITLE. - FM
+     */
     if ((title != NULL) && *title)
         StrAllocCopy(HTFormTitle, title);
     else
@@ -2992,6 +3099,8 @@ PUBLIC void HText_endForm ARGS1(HText *,text)
 		    StrAllocCopy(a->input_field->submit_title, HTFormTitle);
 		a->input_field->submit_method = HTFormMethod;
 		a->input_field->type = F_TEXT_SUBMIT_TYPE;
+		if (HTFormDisabled)
+		    a->input_field->disabled = TRUE;
 		break;
 	    }
 	    if (a == text->last_anchor)
@@ -3006,6 +3115,7 @@ PUBLIC void HText_endForm ARGS1(HText *,text)
     FREE(HTFormEnctype);
     FREE(HTFormTitle);
     HTFormFields = 0;
+    HTFormDisabled = FALSE;
 }
 
 PUBLIC void HText_beginSelect ARGS3(char *,name, BOOLEAN,multiple, char *, size)
@@ -3087,7 +3197,15 @@ PUBLIC char * HText_setLastOptionValue ARGS5(HText *, text, char *, value,
 	 *  Put the text on the screen as well.
 	 */
         HText_appendText(text, cp);
- 
+
+    } else if (LYSelectPopups == FALSE) {
+        StrAllocCopy(text->last_anchor->input_field->value,
+		     (submit_value ? submit_value : cp));
+        /*
+	 *  Put the text on the screen as well.
+	 */
+        HText_appendText(text, cp);
+
     } else {
 	/*
 	 *  Create a linked list of option values.
@@ -3106,7 +3224,7 @@ PUBLIC char * HText_setLastOptionValue ARGS5(HText *, text, char *, value,
 	     *  No option items yet.
 	     */
 	    new_ptr = text->last_anchor->input_field->select_list = 
-				(OptionType *) calloc(1,sizeof(OptionType));
+				(OptionType *) calloc(1, sizeof(OptionType));
 	    if (new_ptr == NULL)
 	        outofmem(__FILE__, "HText_setLastOptionValue");
 
@@ -3119,7 +3237,7 @@ PUBLIC char * HText_setLastOptionValue ARGS5(HText *, text, char *, value,
 	    number++;  /* add one more */
 
 	    op_ptr->next = new_ptr =
-	    			(OptionType *) calloc(1,sizeof(OptionType));
+	    			(OptionType *) calloc(1, sizeof(OptionType));
 	    if (new_ptr == NULL)
 	        outofmem(__FILE__, "HText_setLastOptionValue");
 	}
@@ -3248,11 +3366,17 @@ PUBLIC int HText_beginInput ARGS2(HText *,text, InputFieldData *,I)
 
 
     /*
-     *  If this is a radio button, and it's the first with this name,
-     *  make sure it's checked by default.  Otherwise, if it's checked,
-     *  uncheck the default or any preceding radio button with this name
-     *  that was checked. - FM
+     *  If this is a radio button, or an OPTION we're converting
+     *  to a radio button, and it's the first with this name, make
+     *  sure it's checked by default.  Otherwise, if it's checked,
+     *  uncheck the default or any preceding radio button with this
+     *  name that was checked. - FM
      */
+    if (I->type != NULL && !strcmp(I->type,"OPTION") &&
+ 	HTCurSelectGroupType == F_RADIO_TYPE && LYSelectPopups == FALSE) {
+	I->type = "RADIO";
+	I->name = HTCurSelectGroup;
+    }
     if (I->name && I->type && !strcasecomp(I->type, "radio")) {
         if (!text->last_anchor) {
 	    I->checked = TRUE;
@@ -3299,7 +3423,7 @@ PUBLIC int HText_beginInput ARGS2(HText *,text, InputFieldData *,I)
 
     f->select_list = 0;
     f->number = HTFormNumber;
-    f->disabled = I->disabled;
+    f->disabled = (HTFormDisabled ? TRUE : I->disabled);
     f->no_cache = NO;
 
     HTFormFields++;
@@ -3312,22 +3436,12 @@ PUBLIC int HText_beginInput ARGS2(HText *,text, InputFieldData *,I)
         f->no_cache = TRUE;
 
     /*
-     *  Disable if the ENCTYPE is multipart/form-data
-     *  until we add code to handle it. - FM
-     */
-    if (HTFormEnctype) {
-         if (!strcmp(HTFormEnctype, "multipart/form-data")) {
-	     f->disabled = YES;
-	 }
-    }
-
-    /*
      *  Set up VALUE.
      */
     if (I->value)
         StrAllocCopy(IValue, I->value);
     if (IValue && HTCJK != NOCJK) {
-	if ((tmp = (unsigned char *)calloc(1, strlen(IValue)+1))) {
+	if ((tmp = (unsigned char *)calloc(1, (strlen(IValue) + 1)))) {
 	    if (kanji_code == EUC) {
 		TO_EUC((unsigned char *)IValue, tmp);
 	    } else if (kanji_code == SJIS) {
@@ -3346,6 +3460,7 @@ PUBLIC int HText_beginInput ARGS2(HText *,text, InputFieldData *,I)
 
     /*
      *  Special case of OPTION.
+     *  Is handled above if radio type and LYSelectPopups is FALSE.
      */
     /* set the values and let the parsing below do the work */
     if (I->type != NULL && !strcmp(I->type,"OPTION")) {
@@ -3365,7 +3480,6 @@ PUBLIC int HText_beginInput ARGS2(HText *,text, InputFieldData *,I)
 	if (HTCurSelectGroupSize != NULL) {
 	    f->size_l = atoi(HTCurSelectGroupSize);
 	    FREE(HTCurSelectGroupSize);
-	    HTCurSelectGroupSize = NULL;
 	}
     }
 
@@ -3518,10 +3632,10 @@ PUBLIC int HText_beginInput ARGS2(HText *,text, InputFieldData *,I)
 	    StrAllocCopy(f->submit_title, HTFormTitle);
 	f->submit_method = HTFormMethod;
 
-    } else if (f->type == F_RADIO_TYPE || f->type == F_CHECKBOX_TYPE ) {
+    } else if (f->type == F_RADIO_TYPE || f->type == F_CHECKBOX_TYPE) {
 	f->size=3;
 	if (IValue == NULL)
-	   StrAllocCopy(f->value, "on");
+	   StrAllocCopy(f->value, (f->type == F_CHECKBOX_TYPE ? "on" : ""));
 
     }
     FREE(IValue); 
@@ -3578,9 +3692,20 @@ PUBLIC void HText_SubmitForm ARGS4(FormInfo *,submit_item, document *,doc,
     char *previous_blanks = NULL;
     BOOLEAN PlainText = FALSE;
     BOOLEAN SemiColon = FALSE;
+    char *Boundary = NULL;
+    char *MultipartContentType = NULL;
 
     if (submit_item->submit_action) {
         /*
+	 *  If we're mailing, make sure it's a mailto ACTION. - FM
+	 */
+        if ((submit_item->submit_method == URL_MAIL_METHOD) &&
+	    strncmp(submit_item->submit_action, "mailto:", 7)) {
+	    HTAlert(BAD_FORM_MAILTO);
+	    return;
+	}
+
+        /*
 	 *  Set length plus breathing room.
 	 */
         len = strlen(submit_item->submit_action) + 2048;
@@ -3589,6 +3714,36 @@ PUBLIC void HText_SubmitForm ARGS4(FormInfo *,submit_item, document *,doc,
     }
 
     /*
+     *  Check the ENCTYPE and set up the appropriate variables. - FM
+     */
+    if (submit_item->submit_enctype &&
+	!strncasecomp(submit_item->submit_enctype, "text/plain", 10)) {
+	/*
+	 *  Do not hex escape, and use physical newlines
+	 *  to separate name=value pairs. - FM
+	 */
+	PlainText = TRUE;
+    } else if (submit_item->submit_enctype &&
+	       !strncasecomp(submit_item->submit_enctype,
+			     "application/sgml-form-urlencoded", 32)) {
+	/*
+	 *  Use semicolons instead of ampersands as the
+	 *  separators for name=value pairs. - FM
+	 */
+	SemiColon = TRUE;
+    } else if (submit_item->submit_enctype &&
+	       !strncasecomp(submit_item->submit_enctype,
+			     "multipart/form-data", 19)) {
+	/*
+	 *  Use the multipart MIME format.  We should generate
+	 *  a boundary string which we are sure doesn't occur
+	 *  in the content, but for now we'll just assume that
+	 *  this string doesn't. - FM
+	 */
+	Boundary = "xnyLAaB03X";
+    }
+
+    /*
      *  Go through list of anchors and get size first.
      */
     while (1) {
@@ -3597,15 +3752,14 @@ PUBLIC void HText_SubmitForm ARGS4(FormInfo *,submit_item, document *,doc,
 
 	        form_ptr = anchor_ptr->input_field;
 	
-	        len += strlen(form_ptr->name)+10;
+	        len += (strlen(form_ptr->name) + (Boundary ? 100 : 10));
 		/*
 		 *	Calculate by the option submit value if present.
 		 */
-		if (form_ptr->cp_submit_value != NULL)	{
-			len += strlen(form_ptr->cp_submit_value) + 10;
-		}
-		else	{
-	        	len += strlen(form_ptr->value)+10;
+		if (form_ptr->cp_submit_value != NULL) {
+		    len += (strlen(form_ptr->cp_submit_value) + 10);
+		} else {
+	            len += (strlen(form_ptr->value) + 10);
 		}
 	        len += 32; /* plus and ampersand + safty net */
 
@@ -3623,28 +3777,11 @@ PUBLIC void HText_SubmitForm ARGS4(FormInfo *,submit_item, document *,doc,
     /*
      *  Get query ready.
      */
-    query = (char *)calloc (sizeof(char), len);
+    query = (char *)calloc(1, len);
     if (query == NULL)
         outofmem(__FILE__, "HText_SubmitForm");
 
-    if (submit_item->submit_enctype &&
-	!strncasecomp(submit_item->submit_enctype, "text/plain", 10)) {
-	/*
-	 *  Do not hex escape, and use physical newlines
-	 *  to separate name=value pairs. - FM
-	 */
-	PlainText = TRUE;
-    } else if (submit_item->submit_enctype &&
-	       !strncasecomp(submit_item->submit_enctype,
-			     "application/sgml-form-urlencoded", 32)) {
-	/*
-	 *  Use semicolons instead of ampersands as the
-	 *  separators for name=value pairs. - FM
-	 */
-	SemiColon = TRUE;
-    }
-
-    if (submit_item->submit_method == URL_GET_METHOD) {
+    if (submit_item->submit_method == URL_GET_METHOD && Boundary == NULL) {
        	strcpy (query, submit_item->submit_action);
        	/*
 	 *  Method is GET.  Clip out any anchor in the current URL.
@@ -3660,44 +3797,59 @@ PUBLIC void HText_SubmitForm ARGS4(FormInfo *,submit_item, document *,doc,
 	strcat(query,"?");
     } else {
         query[0] = '\0';
-	if (submit_item->submit_method != URL_MAIL_METHOD) {
-	    /*
-	     *  We are submitting POST content to a server,
-	     *  so load the post_content_type element. - FM
-	     */
-	    if (SemiColon == TRUE) {
-	        StrAllocCopy(doc->post_content_type,
-			     "application/sgml-form-urlencoded");
-	    } else if (PlainText == TRUE) {
-	        StrAllocCopy(doc->post_content_type,
-			     "text/plain");
-	    } else {
-	        StrAllocCopy(doc->post_content_type,
-	        	     "application/x-www-form-urlencoded");
-	    }
+	/*
+	 *  We are submitting POST content to a server,
+	 *  so load the post_content_type element. - FM
+	 */
+	if (SemiColon == TRUE) {
+	    StrAllocCopy(doc->post_content_type,
+			 "application/sgml-form-urlencoded");
+	} else if (PlainText == TRUE) {
+	    StrAllocCopy(doc->post_content_type,
+			 "text/plain");
+	} else if (Boundary != NULL) {
+	    StrAllocCopy(doc->post_content_type,
+			 "multipart/form-data; boundary=");
+	    StrAllocCat(doc->post_content_type, Boundary);
+	} else {
+	    StrAllocCopy(doc->post_content_type,
+			 "application/x-www-form-urlencoded");
+	}
 
-	    /*
-	     *  Append the exended charset info if known, and it is
-	     *  not ISO-8859-1 or US-ASCII.  We'll assume the user
-	     *  has the matching character set selected, or a
-	     *  download offer would have been forced and we would
-	     *  not be processing the form here.  We don't yet want
-	     *  to do this unless the server indicated the charset
-	     *  in the original transmission, because otherwise it
-	     *  might be an old server and CGI script which will
-	     *  not parse out the extended charset info, and reject
-	     *  the POST Content-Type as invalid. - FM
-	     */
-	    if (HTMainText->node_anchor->charset != NULL &&
-	        *HTMainText->node_anchor->charset != '\0') {
-		if (strcasecomp(HTMainText->node_anchor->charset,
-				"iso-8859-1") &&
-		    strcasecomp(HTMainText->node_anchor->charset,
-				"us-ascii")) {
-		    StrAllocCat(doc->post_content_type, ";charset=");
-		    StrAllocCat(doc->post_content_type,
-				HTMainText->node_anchor->charset);
-		}
+	/*
+	 *  Append the exended charset info if known, and it is not
+	 *  ISO-8859-1 or US-ASCII.  We'll assume the user has the
+	 *  matching character set selected, or a download offer would
+	 *  have been forced and we would not be processing the form
+	 *  here.  We don't yet want to do this unless the server
+	 *  indicated the charset in the original transmission, because
+	 *  otherwise it might be an old server and CGI script which
+	 *  will not parse out the extended charset info, and reject
+	 *  the POST Content-Type as invalid.  If the ENCTYPE is
+	 *  multipart/form-data and the charset is known, set up a
+	 *  Content-Type string for the text fields and append the
+	 *  charset even if it is ISO-8859-1 or US-ASCII, but don't
+	 *  append it to the post_content_type header.  Note that we do
+	 *  not yet have a way to vary the charset among multipart form
+	 *  fields, so this code assumes it is the same for all of the
+	 *  text fields. - FM
+	 */
+	if (HTMainText->node_anchor->charset != NULL &&
+	    *HTMainText->node_anchor->charset != '\0') {
+	    if (Boundary == NULL &&
+	        strcasecomp(HTMainText->node_anchor->charset, "iso-8859-1") &&
+		strcasecomp(HTMainText->node_anchor->charset, "us-ascii")) {
+		StrAllocCat(doc->post_content_type, "; charset=");
+		StrAllocCat(doc->post_content_type,
+			    HTMainText->node_anchor->charset);
+	    } else if (Boundary != NULL) {
+	        MultipartContentType = (char *)calloc(1,
+			     (40 + strlen(HTMainText->node_anchor->charset)));
+		if (query == NULL)
+		    outofmem(__FILE__, "HText_SubmitForm");
+		sprintf(MultipartContentType,
+			"\r\nContent-Type: text/plain; charset=%s",
+			HTMainText->node_anchor->charset);
 	    }
 	}
     }
@@ -3735,12 +3887,19 @@ PUBLIC void HText_SubmitForm ARGS4(FormInfo *,submit_item, document *,doc,
 		        (form_ptr->value && *form_ptr->value != '\0' &&
 		         !strcmp(form_ptr->value, link_value)))) {
 		        if (first_one) {
+			    if (Boundary) {
+			        sprintf(&query[strlen(query)],
+					"--%s\r\n", Boundary);
+			    }
                             first_one=FALSE;
                         } else {
 			    if (PlainText) {
 			        strcat(query, "\n");
 			    } else if (SemiColon) {
 			        strcat(query, ";");
+			    } else if (Boundary) {
+			        sprintf(&query[strlen(query)],
+					"\r\n--%s\r\n", Boundary);
 			    } else {
                                 strcat(query, "&");
 			    }
@@ -3749,6 +3908,14 @@ PUBLIC void HText_SubmitForm ARGS4(FormInfo *,submit_item, document *,doc,
 			if (PlainText) {
 			    StrAllocCopy(escaped1, (form_ptr->name ?
 			    			    form_ptr->name : ""));
+			} else if (Boundary) {
+			    StrAllocCopy(escaped1,
+			    	    "Content-Disposition: form-data; name=");
+			    StrAllocCat(escaped1, (form_ptr->name ?
+			    			    form_ptr->name : ""));
+			    if (MultipartContentType)
+			        StrAllocCat(escaped1, MultipartContentType);
+			    StrAllocCat(escaped1, "\r\n\r\n");
 			} else {
 		            escaped1 = HTEscape(form_ptr->name,URL_XALPHAS);
 			}
@@ -3772,7 +3939,7 @@ PUBLIC void HText_SubmitForm ARGS4(FormInfo *,submit_item, document *,doc,
 				    form_ptr->cp_submit_value[i] = 173;
 				}
 			    }
-			    if (PlainText) {
+			    if (PlainText || Boundary) {
 			        StrAllocCopy(escaped2,
 					     (form_ptr->cp_submit_value ?
 					      form_ptr->cp_submit_value : ""));
@@ -3796,7 +3963,7 @@ PUBLIC void HText_SubmitForm ARGS4(FormInfo *,submit_item, document *,doc,
 				    form_ptr->value[i] = 173;
 				}
 			    }
-			    if (PlainText) {
+			    if (PlainText || Boundary) {
 			        StrAllocCopy(escaped2, (form_ptr->value ?
 							form_ptr->value : ""));
 			    } else {
@@ -3805,34 +3972,46 @@ PUBLIC void HText_SubmitForm ARGS4(FormInfo *,submit_item, document *,doc,
 			    }
 		        }
 
-			if (!strcmp(form_ptr->value, "[IMAGE]-Submit"))
+			if (!strcmp(form_ptr->value, "[IMAGE]-Submit")) {
 			    /*
 			     * It's a clickable image submit button.
 			     * Fake a 0,0 coordinate pair, which
 			     * typically returns the image's default. - FM
 			     */
-			    sprintf(&query[strlen(query)],
-				    "%s.x=0%s%s.y=0%s",
-				    escaped1,
-				    (PlainText ?
-				          "\n" : (SemiColon ?
-					  		";" : "&")),
-				    escaped1,
-				    ((PlainText && *escaped1) ?
-				    			 "\n" : ""));
-			else
+			    if (Boundary) {
+			        escaped1[(strlen(escaped1) - 4)] = '\0';
+			        sprintf(&query[strlen(query)],
+				    "%s.x\r\n\r\n0\r\n--%s\r\n%s.y\r\n\r\n0",
+					escaped1,
+					Boundary,
+					escaped1);
+			    } else {
+			        sprintf(&query[strlen(query)],
+					"%s.x=0%s%s.y=0%s",
+					escaped1,
+					(PlainText ?
+					      "\n" : (SemiColon ?
+							    ";" : "&")),
+					escaped1,
+					((PlainText && *escaped1) ?
+				    			     "\n" : ""));
+			    }
+			} else {
 			    /*
 			     * It's a standard submit button.
 			     * Use the name=value pair. = FM
 			     */
 			    sprintf(&query[strlen(query)],
-				    "%s=%s%s%s",
+				    "%s%s%s%s%s",
 				    escaped1,
+				    (Boundary ?
+				    	   "" : "="),
 				    (PlainText ?
 				          "\n" : ""),
 				    escaped2,
 				    ((PlainText && *escaped2) ?
 				    			 "\n" : ""));
+			}
 		        FREE(escaped1);
 		        FREE(escaped2);
 		    }
@@ -3843,12 +4022,19 @@ PUBLIC void HText_SubmitForm ARGS4(FormInfo *,submit_item, document *,doc,
 		    /* only add if selected */
 		    if (form_ptr->num_value) {
 	                if (first_one) {
+			    if (Boundary) {
+			        sprintf(&query[strlen(query)],
+					"--%s\r\n", Boundary);
+			    }
 		            first_one=FALSE;
 	                } else {
 			    if (PlainText) {
 			        strcat(query, "\n");
 			    } else if (SemiColon) {
 			        strcat(query, ";");
+			    } else if (Boundary) {
+			        sprintf(&query[strlen(query)],
+					"\r\n--%s\r\n", Boundary);
 			    } else {
 		                strcat(query, "&");
 			    }
@@ -3857,6 +4043,15 @@ PUBLIC void HText_SubmitForm ARGS4(FormInfo *,submit_item, document *,doc,
 			if (PlainText) {
 			    StrAllocCopy(escaped1, (form_ptr->name ?
 			    			    form_ptr->name : ""));
+			} else if (Boundary) {
+			    StrAllocCopy(escaped1,
+			    	     "Content-Disposition: form-data; name=");
+			    StrAllocCat(escaped1,
+				        (form_ptr->name ?
+			    		 form_ptr->name : ""));
+			    if (MultipartContentType)
+			        StrAllocCat(escaped1, MultipartContentType);
+			    StrAllocCat(escaped1, "\r\n\r\n");
 			} else {
 		            escaped1 = HTEscape(form_ptr->name, URL_XALPHAS);
 			}
@@ -3879,7 +4074,7 @@ PUBLIC void HText_SubmitForm ARGS4(FormInfo *,submit_item, document *,doc,
 				    form_ptr->cp_submit_value[i] = 173;
 				 }
 			    }
-			    if (PlainText) {
+			    if (PlainText || Boundary) {
 			        StrAllocCopy(escaped2,
 					     (form_ptr->cp_submit_value ?
 					      form_ptr->cp_submit_value : ""));
@@ -3904,7 +4099,7 @@ PUBLIC void HText_SubmitForm ARGS4(FormInfo *,submit_item, document *,doc,
 
 				}
 			    }
-			    if (PlainText) {
+			    if (PlainText || Boundary) {
 			        StrAllocCopy(escaped2, (form_ptr->value ?
 							form_ptr->value : ""));
 			    } else {
@@ -3914,8 +4109,10 @@ PUBLIC void HText_SubmitForm ARGS4(FormInfo *,submit_item, document *,doc,
 			}
 
                         sprintf(&query[strlen(query)],
-				"%s=%s%s%s",
+				"%s%s%s%s%s",
 				escaped1,
+				(Boundary ?
+				       "" : "="),
 				(PlainText ?
 				      "\n" : ""),
 				escaped2,
@@ -3939,7 +4136,7 @@ PUBLIC void HText_SubmitForm ARGS4(FormInfo *,submit_item, document *,doc,
 			    form_ptr->value[i] = 173;
 			}
 		    }
-		    if (PlainText) {
+		    if (PlainText || Boundary) {
 		        StrAllocCopy(escaped2, (form_ptr->value ? 
 						form_ptr->value : ""));
 		    } else {
@@ -3955,12 +4152,19 @@ PUBLIC void HText_SubmitForm ARGS4(FormInfo *,submit_item, document *,doc,
 			 */
 			FREE(previous_blanks);
 		        if (first_one) {
+			    if (Boundary) {
+			        sprintf(&query[strlen(query)],
+					"--%s\r\n", Boundary);
+			    }
                             first_one=FALSE;
                         } else {
 			    if (PlainText) {
 			        strcat(query, "\n");
 			    } else if (SemiColon) {
 			        strcat(query, ";");
+			    } else if (Boundary) {
+			        sprintf(&query[strlen(query)],
+					"\r\n--%s\r\n", Boundary);
 			    } else {
                                 strcat(query, "&");
 			    }
@@ -3968,12 +4172,22 @@ PUBLIC void HText_SubmitForm ARGS4(FormInfo *,submit_item, document *,doc,
 			if (PlainText) {
 			    StrAllocCopy(escaped1, (form_ptr->name ?
 			    			    form_ptr->name : ""));
+			} else if (Boundary) {
+			    StrAllocCopy(escaped1,
+			    	    "Content-Disposition: form-data; name=");
+			    StrAllocCat(escaped1, (form_ptr->name ?
+			    			    form_ptr->name : ""));
+			    if (MultipartContentType)
+			        StrAllocCat(escaped1, MultipartContentType);
+			    StrAllocCat(escaped1, "\r\n\r\n");
 			} else {
                             escaped1 = HTEscape(form_ptr->name, URL_XALPHAS);
 			}
                         sprintf(&query[strlen(query)],
-				"%s=%s%s%s",
+				"%s%s%s%s%s",
 				escaped1,
+				(Boundary ?
+				       "" : "="),
 				(PlainText ?
 				      "\n" : ""),
 				escaped2,
@@ -3994,6 +4208,9 @@ PUBLIC void HText_SubmitForm ARGS4(FormInfo *,submit_item, document *,doc,
 			    if (PlainText) {
 			        sprintf(&query[strlen(query)], "%s\n",
 							       escaped2);
+			    } else if (Boundary) {
+			        sprintf(&query[strlen(query)], "%s\r\n",
+							       escaped2);
 			    } else {
 			        sprintf(&query[strlen(query)], "%%0a%s",
 							       escaped2);
@@ -4001,6 +4218,8 @@ PUBLIC void HText_SubmitForm ARGS4(FormInfo *,submit_item, document *,doc,
 			} else {
 			    if (PlainText) {
 			        StrAllocCat(previous_blanks, "\n");
+			    } else if (Boundary) {
+			        StrAllocCat(previous_blanks, "\r\n");
 			    } else {
 			        StrAllocCat(previous_blanks, "%0a");
 			    }
@@ -4014,12 +4233,19 @@ PUBLIC void HText_SubmitForm ARGS4(FormInfo *,submit_item, document *,doc,
 		case F_OPTION_LIST_TYPE:
 		case F_HIDDEN_TYPE:
 	            if (first_one) {
+			if (Boundary) {
+			    sprintf(&query[strlen(query)],
+				    "--%s\r\n", Boundary);
+			}
 		        first_one=FALSE;
 	            } else {
 		        if (PlainText) {
 			    strcat(query, "\n");
 			} else if (SemiColon) {
 			    strcat(query, ";");
+			} else if (Boundary) {
+			    sprintf(&query[strlen(query)],
+			    	    "\r\n--%s\r\n", Boundary);
 			} else {
 		            strcat(query, "&");
 			}
@@ -4028,6 +4254,14 @@ PUBLIC void HText_SubmitForm ARGS4(FormInfo *,submit_item, document *,doc,
 		    if (PlainText) {
 		       StrAllocCopy(escaped1, (form_ptr->name ?
 		       			       form_ptr->name : ""));
+		    } else if (Boundary) {
+			StrAllocCopy(escaped1,
+			    	    "Content-Disposition: form-data; name=");
+			StrAllocCat(escaped1, (form_ptr->name ?
+			    		       form_ptr->name : ""));
+			if (MultipartContentType)
+			    StrAllocCat(escaped1, MultipartContentType);
+			StrAllocCat(escaped1, "\r\n\r\n");
 		    } else {
 		        escaped1 = HTEscape(form_ptr->name, URL_XALPHAS);
 		    }
@@ -4051,7 +4285,7 @@ PUBLIC void HText_SubmitForm ARGS4(FormInfo *,submit_item, document *,doc,
 				form_ptr->cp_submit_value[i] = 173;
 			    }
 			}
-			if (PlainText) {
+			if (PlainText || Boundary) {
 			    StrAllocCopy(escaped2,
 			    		 (form_ptr->cp_submit_value ?
 					  form_ptr->cp_submit_value : ""));
@@ -4075,7 +4309,7 @@ PUBLIC void HText_SubmitForm ARGS4(FormInfo *,submit_item, document *,doc,
 				form_ptr->value[i] = 173;
 			    }
 			}
-			if (PlainText) {
+			if (PlainText || Boundary) {
 			    StrAllocCopy(escaped2, (form_ptr->value ?
 			    			    form_ptr->value : ""));
 			} else {
@@ -4085,8 +4319,10 @@ PUBLIC void HText_SubmitForm ARGS4(FormInfo *,submit_item, document *,doc,
 		    }
 
                     sprintf(&query[strlen(query)],
-		    	    "%s=%s%s%s",
+		    	    "%s%s%s%s%s",
 			    escaped1,
+			    (Boundary ?
+			    	   "" : "="),
 			    (PlainText ?
 				  "\n" : ""),
 			    escaped2,
@@ -4106,15 +4342,13 @@ PUBLIC void HText_SubmitForm ARGS4(FormInfo *,submit_item, document *,doc,
 
 	anchor_ptr = anchor_ptr->next;
     }
+    if (Boundary) {
+        sprintf(&query[strlen(query)], "\r\n--%s--\r\n", Boundary);
+    }
     FREE(previous_blanks);
 
     if (submit_item->submit_method == URL_MAIL_METHOD) {
-	if (strncmp(submit_item->submit_action, "mailto:", 7)) {
-	    HTAlert(BAD_FORM_MAILTO);
-	    FREE(query);
-	    return;
-	}
-	_user_message("submitting %s", submit_item->submit_action);
+	_user_message("Submitting %s", submit_item->submit_action);
 	if (TRACE) {
 	    fprintf(stderr, "\nGridText - mailto_address: %s\n",
 	    		    (submit_item->submit_action+7));
@@ -4133,14 +4367,16 @@ PUBLIC void HText_SubmitForm ARGS4(FormInfo *,submit_item, document *,doc,
 		   (submit_item->submit_title) :
 		   	     (HText_getTitle() ?
 			      HText_getTitle() : "")),
-		 query);
+		 query,
+		 doc->post_content_type);
 	FREE(query);
+        FREE(doc->post_content_type);
 	return;
     } else {
         _statusline(SUBMITTING_FORM);
     }
    
-    if (submit_item->submit_method == URL_POST_METHOD) {
+    if (submit_item->submit_method == URL_POST_METHOD || Boundary) {
         StrAllocCopy(doc->post_data, query);
         if (TRACE)
 	    fprintf(stderr,"GridText - post_data: %s\n",doc->post_data);
@@ -4156,14 +4392,42 @@ PUBLIC void HText_SubmitForm ARGS4(FormInfo *,submit_item, document *,doc,
     }
 }
 
+PUBLIC void HText_DisableCurrentForm NOARGS
+{
+    TextAnchor * anchor_ptr = HTMainText->first_anchor;
+
+    HTFormDisabled = TRUE;
+
+    /*
+     *  Go through list of anchors and set the disabled flag.
+     */
+    while (1) {
+        if (anchor_ptr->link_type == INPUT_ANCHOR &&
+            anchor_ptr->input_field->number == HTFormNumber) {
+
+            anchor_ptr->input_field->disabled = TRUE;
+        }
+
+        if (anchor_ptr == HTMainText->last_anchor)
+            break;
+
+
+        anchor_ptr = anchor_ptr->next;
+    }
+
+    return;
+}
+
 PUBLIC void HText_ResetForm ARGS1(FormInfo *,form)
 {
     TextAnchor * anchor_ptr = HTMainText->first_anchor;
 
     _statusline(RESETTING_FORM);
 
-   /* go through list of anchors and reset values */
-   while (1) {
+    /*
+     *  Go through list of anchors and reset values.
+     */
+    while (1) {
         if (anchor_ptr->link_type == INPUT_ANCHOR) {
             if (anchor_ptr->input_field->number == form->number) {
 
@@ -4198,9 +4462,7 @@ PUBLIC void HText_ResetForm ARGS1(FormInfo *,form)
 
 
         anchor_ptr = anchor_ptr->next;
-   }
-
-
+    }
 }
 
 PUBLIC void HText_activateRadioButton ARGS1(FormInfo *,form)
diff --git a/src/GridText.h b/src/GridText.h
index c20ae2ec..0ed5cad0 100644
--- a/src/GridText.h
+++ b/src/GridText.h
@@ -64,7 +64,12 @@ extern int HText_sourceAnchors PARAMS((HText * text));
 extern void HText_setStale PARAMS((HText * text));
 extern void HText_refresh PARAMS((HText * text));
 extern char * HText_getTitle NOPARAMS;
+extern char * HText_getSugFname NOPARAMS;
+extern char * HText_getLastModified NOPARAMS;
+extern char * HText_getDate NOPARAMS;
+extern char * HText_getServer NOPARAMS;
 extern char * HText_getOwner NOPARAMS;
+extern void HText_setMainTextOwner PARAMS((CONST char * owner));
 extern char * HText_getRevTitle NOPARAMS;
 extern void print_wwwfile_to_fd PARAMS((FILE * fp, int is_reply));
 extern BOOLEAN HTFindPoundSelector PARAMS((char *selector));
@@ -78,6 +83,9 @@ extern char * HTLoadedDocumentURL NOPARAMS;
 extern char * HTLoadedDocumentPost_data NOPARAMS;
 extern char * HTLoadedDocumentTitle NOPARAMS;
 extern BOOLEAN HTLoadedDocumentIsHEAD NOPARAMS;
+extern char * HTLoadedDocumentCharset NOPARAMS;
+extern void HText_setNodeAnchorBookmark PARAMS((CONST char *bookmark));
+extern char * HTLoadedDocumentBookmark NOPARAMS;
 extern int HText_LastLineSize PARAMS((HText *me));
 extern int HText_PreviousLineSize PARAMS((HText *me));
 extern void HText_NegateLineOne PARAMS((HText *text));
@@ -99,6 +107,7 @@ extern char * HText_setLastOptionValue PARAMS((HText *text, char *value,
 extern int HText_beginInput PARAMS((HText *text, InputFieldData *I));
 extern void HText_SubmitForm PARAMS((FormInfo *submit_item, document *doc,
 				     char *link_name, char *link_value));
+extern void HText_DisableCurrentForm NOPARAMS;
 extern void HText_ResetForm PARAMS((FormInfo *form));
 extern void HText_activateRadioButton PARAMS((FormInfo *form));
 
diff --git a/src/HTAlert.c b/src/HTAlert.c
index 79a4a7b0..5a3ddc1c 100644
--- a/src/HTAlert.c
+++ b/src/HTAlert.c
@@ -13,11 +13,12 @@
 #include "HTUtils.h"
 #include "tcp.h"
 #include "HTAlert.h"
+#include "LYGlobalDefs.h"
+#include "LYCurses.h"
 #include "LYStrings.h"
 #include "LYUtils.h"
 #include "LYSignal.h"
 #include "GridText.h"
-#include "LYGlobalDefs.h"
 
 #include "LYLeaks.h"
 
@@ -214,3 +215,109 @@ PUBLIC void HTPromptUsernameAndPassword ARGS3(CONST char *,     Msg,
     }
 }
 
+
+#define	SERVER_ASKED_FOR_REDIRECTION \
+ "Server asked for redirection of POST content to"
+#define	PROCEED_GET_CANCEL "P)roceed, use G)ET or C)ancel "
+#define	ADVANCED_POST_REDIRECT \
+ "Redirection of POST content. P)roceed, see U)RL, use G)ET or C)ancel"
+#define	LOCATION_HEADER "Location: "
+
+/*      Confirm redirection of POST		HTConfirmPostRedirect()
+**
+** On entry,
+**      redirecting_url             is the Location.
+**
+** On exit,
+**      Returns 0 on cancel,
+**	  1 for redirect of POST with content,
+**	303 for redirect as GET without content
+*/
+PUBLIC int HTConfirmPostRedirect ARGS1(
+	CONST char *,	redirecting_url)
+{
+    char *show_POST_url = NULL;
+    char url[256];
+    int on_screen = 0;	/* 0 - show menu
+   			 * 1 - show url
+			 * 2 - menu is already on screen */
+
+    if (dump_output_immediately)
+	/*
+	 *  Treat as 303 (GET without content) if not interactive.
+	 */
+        return 303;
+
+    if (user_mode == NOVICE_MODE) {
+        on_screen = 2;
+        move(LYlines-2, 0);
+        addstr(SERVER_ASKED_FOR_REDIRECTION);
+	clrtoeol();
+        move(LYlines-1, 0);
+	sprintf(url, "URL: %.*s",
+		    (LYcols < 250 ? LYcols-6 : 250), redirecting_url);
+        addstr(url);
+	clrtoeol();
+        _statusline(PROCEED_GET_CANCEL);
+    } else {
+	StrAllocCopy(show_POST_url, LOCATION_HEADER);
+	StrAllocCat(show_POST_url, redirecting_url);
+    }
+    while (1) {
+	int c;  
+
+	switch (on_screen) {
+	    case 0:
+	        _statusline(ADVANCED_POST_REDIRECT);
+		break;
+	    case 1:
+	        _statusline(show_POST_url);
+	}
+	c = LYgetch();
+	switch (TOUPPER(c)) {
+	    case 'P':
+		/*
+		 *  Proceed with 301 or 302 redirect of POST
+		 *  (we check only for 0 and 303 in HTTP.c).
+		 */
+	        FREE(show_POST_url);
+		return 1;	
+
+ 	    case 7:
+ 	    case 'C':
+	        /*
+		 * Cancel request.
+		 */
+	        FREE(show_POST_url);
+		return 0;
+
+	    case 'G':
+	        /*
+		 *  Treat as 303 (GET without content).
+		 */
+	        FREE(show_POST_url);
+		return 303;
+
+	    case 'U':
+	        /*
+		 *  Show URL for intermediate or advanced mode.
+		 */
+	        if (user_mode != NOVICE_MODE)
+		    if (on_screen == 1)
+			on_screen = 0;
+		    else
+			on_screen = 1;
+		break;
+
+	    default:
+	        /*
+		 *  Get another character.
+		 */
+		if (on_screen == 1)
+		    on_screen = 0;
+		else
+		    on_screen = 2;
+	}
+    }
+}
+
diff --git a/src/HTAlert.h b/src/HTAlert.h
index 8ce0be53..dc470cfd 100644
--- a/src/HTAlert.h
+++ b/src/HTAlert.h
@@ -81,6 +81,20 @@ extern void HTPromptUsernameAndPassword PARAMS((
 	char **		username,
 	char **		password));
 
+
+/*      Confirm redirection of POST		HTConfirmPostRedirect()
+**
+** On entry,
+**      redirecting_url             is the Location.
+**
+** On exit,
+**      Returns 0 on cancel,
+**	  1 for redirect of POST with content,
+**	303 for redirect as GET without content
+*/
+extern int HTConfirmPostRedirect PARAMS((
+	CONST char *	redirecting_url));
+
 /*
 
     */
diff --git a/src/HTFWriter.c b/src/HTFWriter.c
index a3b46f0c..e9f7d1ee 100644
--- a/src/HTFWriter.c
+++ b/src/HTFWriter.c
@@ -50,6 +50,11 @@ PUBLIC unsigned long LYVMS_FixedLengthRecords PARAMS((char *filename));
 #endif /* USE_COMMAND_FILE */
 #endif /* VMS */
 
+PUBLIC HTStream* HTSaveToFile PARAMS((
+        HTPresentation *       pres,
+        HTParentAnchor *       anchor,
+        HTStream *             sink));
+
 #define FREE(x) if (x) {free(x); x = NULL;}
 
 
@@ -201,13 +206,23 @@ PRIVATE void HTFWriter_free ARGS1(HTStream *, me)
 		    StrAllocCat(addr, path);
 #endif /* VMS */
 		    StrAllocCopy(me->anchor->FileCache, path);
+		    FREE(path);
 		    FREE(me->anchor->content_encoding);
 		    status = HTLoadFile(addr,
 			    		me->anchor,
 			    		me->pres->rep_out,
 					me->sink);
+		    if (dump_output_immediately &&
+		        me->pres->rep_out == HTAtom_for("www/present")) {
+			FREE(addr);
+			remove(me->anchor->FileCache);
+			FREE(me->anchor->FileCache);
+			FREE(me->remove_command);
+			FREE(me->end_command);
+			FREE(me);
+		        return;
+		    }
 		}
-		FREE(path);
 		FREE(addr);
 	    }
 
@@ -251,9 +266,10 @@ PRIVATE void HTFWriter_free ARGS1(HTStream *, me)
 	FREE(me->end_command);
     }
 
-    FREE(me);
-
     if (dump_output_immediately) {
+        if (me->anchor->FileCache)
+            remove(me->anchor->FileCache);
+	FREE(me);
         (void) signal(SIGHUP, SIG_DFL);
         (void) signal(SIGTERM, SIG_DFL);
 #ifndef VMS
@@ -265,6 +281,9 @@ PRIVATE void HTFWriter_free ARGS1(HTStream *, me)
 #endif /* SIGTSTP */
         exit(0);
     }
+
+    FREE(me);
+    return;
 }
 
 /*	Abort writing
@@ -368,6 +387,10 @@ PUBLIC HTStream* HTSaveAndExecute ARGS3(
 
 #if defined(EXEC_LINKS) || defined(EXEC_SCRIPTS)
     if (pres->quality == 999.0) { /* exec link */
+        if (dump_output_immediately) {
+	    LYCancelledFetch = TRUE;
+	    return(NULL);
+	}
         if (no_exec) {
             _statusline(EXECUTION_DISABLED);
             sleep(AlertSecs);
@@ -390,6 +413,10 @@ PUBLIC HTStream* HTSaveAndExecute ARGS3(
     }
 #endif /* EXEC_LINKS || EXEC_SCRIPTS */
     
+    if (dump_output_immediately) {
+        return(HTSaveToFile(pres, anchor, sink));
+    }
+
     me = (HTStream*)calloc(sizeof(*me),1);
     if (me == NULL)
         outofmem(__FILE__, "HTSaveAndExecute");
@@ -681,6 +708,17 @@ PUBLIC HTStream* HTSaveToFile ARGS3(
     _statusline(RETRIEVING_FILE);
 
     StrAllocCopy(anchor->FileCache, fnam);
+    if (!strncasecomp(pres->rep->name, "text/html", 9)) {
+        /*
+	 *  Add the document's URL as a BASE tag at the top of the file,
+	 *  so that any partial or relative URLs within it will be resolved
+	 *  relative to that if no BASE tag is present and replaces it.
+	 *  Note that the markup will be technically invalid if a DOCTYPE
+	 *  declaration, or HTML or HEAD tags, are present, and thus the
+	 *  file may need editing for perfection. - FM
+	 */
+        fprintf(ret_obj->fp, "<BASE HREF=\"%s\">\n\n", anchor->address);
+    }
     return ret_obj;
 }
 
diff --git a/src/HTInit.c b/src/HTInit.c
index 0038e605..63933236 100644
--- a/src/HTInit.c
+++ b/src/HTInit.c
@@ -141,6 +141,12 @@ PUBLIC void HTFormatInit NOARGS
 /*
  *  Now add our basic conversions.
  */
+ HTSetConversion("text/x-sgml",
+ 			      "www/source",  HTPlainPresent, 1.0, 0.0, 0.0, 0);
+ HTSetConversion("text/x-sgml",
+ 			      "www/present", HTMLPresent,    1.0, 0.0, 0.0, 0);
+ HTSetConversion("text/sgml", "www/source",  HTPlainPresent, 1.0, 0.0, 0.0, 0);
+ HTSetConversion("text/sgml", "www/present", HTMLPresent,    1.0, 0.0, 0.0, 0);
  HTSetConversion("text/plain","www/present", HTPlainPresent, 1.0, 0.0, 0.0, 0);
  HTSetConversion("text/html", "www/source",  HTPlainPresent, 1.0, 0.0, 0.0, 0);
  HTSetConversion("text/html", "text/x-c",    HTMLToC, 	     0.5, 0.0, 0.0, 0);
diff --git a/src/HTML.c b/src/HTML.c
index 885a5596..e96ab89d 100644
--- a/src/HTML.c
+++ b/src/HTML.c
@@ -1,10 +1,10 @@
 /*		Structured stream to Rich hypertext converter
 **		============================================
 **
-**	This generates of a hypertext object.  It converts from the
-**	structured stream interface fro HTMl events into the style-
-**	oriented iunterface of the HText.h interface.  This module is
-**	only used in clients and shouldnot be linked into servers.
+**	This generates a hypertext object.  It converts from the
+**	structured stream interface of HTML events into the style-
+**	oriented interface of the HText.h interface.  This module is
+**	only used in clients and should not be linked into servers.
 **
 **	Override this module if making a new GUI browser.
 **
@@ -13,43 +13,31 @@
 */
 #include "HTUtils.h"
 #include "tcp.h"
-
 #include "HTML.h"
-
-/* #define CAREFUL		 Check nesting here not really necessary */
-
-/*#include <ctype.h> included by HTUtils.h -- FM */
-/*#include <stdio.h> included by HTUtils.h -- FM */
-
 #include "HTCJK.h"
 #include "HTAtom.h"
 #include "HTChunk.h"
 #include "HText.h"
 #include "HTStyle.h"
-
 #include "HTAlert.h"
 #include "HTMLGen.h"
 #include "HTParse.h"
-
 #include "HTNestedList.h"
 #include "HTForms.h"
-
 #include "GridText.h"
-
 #include "HTFont.h"
-
-#ifdef VMS
-#include "LYCurses.h"
-#include "HTVMSUtils.h"
-#endif /* VMS */
-
-
 #include "LYGlobalDefs.h"
 #include "LYSignal.h"
 #include "LYUtils.h"
 #include "LYCharSets.h"
 #include "LYCharUtils.h"
 #include "LYMap.h"
+#include "LYBookmark.h"
+
+#ifdef VMS
+#include "LYCurses.h"
+#include "HTVMSUtils.h"
+#endif /* VMS */
 
 #include "LYexit.h"
 #include "LYLeaks.h"
@@ -305,7 +293,7 @@ PRIVATE void HTML_put_character ARGS2(HTStructured *, me, char, c)
     /*
      *  Ignore all non-MAP content when just
      *  scanning a document for MAPs. - FM
-     *  
+     */
     if (LYMapsOnly)
         return;
 
@@ -414,8 +402,7 @@ PRIVATE void HTML_put_character ARGS2(HTStructured *, me, char, c)
 	    B_inPRE = TRUE;
 
 	} else if (!strcmp(me->sp->style->name,"Listing") ||
-		   !strcmp(me->sp->style->name,"Example") ||
-		   !strcmp(me->sp->style->name,"Style")) {
+		   !strcmp(me->sp->style->name,"Example")) {
 	    if (c != '\r') {
 		B_inP = TRUE; 
 		B_inLABEL = FALSE; 
@@ -637,8 +624,6 @@ PRIVATE void HTML_start_element ARGS5(
 	    HText_appendCharacter(me->text,LY_UNDERLINE_END_CHAR);
 	    B_inUnderline = FALSE;
 	}
-	FREE(base_href);
-        B_inBASE = FALSE;
 	break;
 
     case HTML_HEAD:
@@ -652,8 +637,6 @@ PRIVATE void HTML_start_element ARGS5(
 	    HText_appendCharacter(me->text,LY_UNDERLINE_END_CHAR);
 	    B_inUnderline = FALSE;
 	}
-	FREE(base_href);
-        B_inBASE = FALSE;
 	break;
 
     case HTML_BASE:
@@ -1023,9 +1006,9 @@ PRIVATE void HTML_start_element ARGS5(
 			}
 			B_CurrentA = HTAnchor_findChildAndLink(
 				me->node_anchor,		/* Parent */
-				(id_string ? id_string : 0),	/* Tag */
+				id_string,			/* Tag */
 				href,				/* Addresss */
-				0);				/* Type */
+				(void *)0);			/* Type */
 			if (id_string)
 			    *cp = '#';
 			FREE(id_string);
@@ -1225,9 +1208,9 @@ PRIVATE void HTML_start_element ARGS5(
 		 */
 	        B_CurrentA = HTAnchor_findChildAndLink(
 				me->node_anchor,	/* Parent */
-		    		0,			/* Tag */
-		    		href ? href : 0,	/* Addresss */
-		    		0);			/* Type */
+		    		NULL,			/* Tag */
+		    		href,			/* Addresss */
+		    		(void *)0);		/* Type */
 		{
 		    if (dest = HTAnchor_parent(
 			    HTAnchor_followMainLink((HTAnchor*)B_CurrentA)
@@ -1240,8 +1223,8 @@ PRIVATE void HTML_start_element ARGS5(
 		        (B_ID_A = HTAnchor_findChildAndLink(
 					me->node_anchor,	/* Parent */
 					LYToolbarName,		/* Tag */
-					0,			/* Addresss */
-					0))) {			/* Type */
+					NULL,			/* Addresss */
+					(void *)0))) {		/* Type */
 			HText_beginAnchor(me->text, B_ID_A);
 			HText_endAnchor(me->text);
 			HText_setToolbar(me->text);
@@ -1439,9 +1422,9 @@ PRIVATE void HTML_start_element ARGS5(
 
 	    B_CurrentA = HTAnchor_findChildAndLink(
 				me->node_anchor,	/* Parent */
-				0,			/* Tag */
+				NULL,			/* Tag */
 				href,			/* Addresss */
-				0);			/* Type */
+				(void *)0);		/* Type */
 	    HTML_EnsureSingleSpace(me);
 	    if (B_inUnderline == FALSE)
 	        HText_appendCharacter(me->text,LY_UNDERLINE_START_CHAR);
@@ -1478,8 +1461,8 @@ PRIVATE void HTML_start_element ARGS5(
 	    (B_ID_A = HTAnchor_findChildAndLink(
 					me->node_anchor,	/* Parent */
 					LYToolbarName,		/* Tag */
-					0,			/* Addresss */
-					0))) {			/* Type */
+					NULL,			/* Addresss */
+					(void *)0))) {		/* Type */
 	    HText_beginAnchor(me->text, B_ID_A);
 	    HText_endAnchor(me->text);
 	    HText_setToolbar(me->text);
@@ -1938,7 +1921,7 @@ PRIVATE void HTML_start_element ARGS5(
 	 *  use chevrons, but for now we'll always use double-
 	 *  or single-quotes. - FM
 	 */
-	if (Quote_Level == ((Quote_Level/2)*2))
+	if (!(Quote_Level & 1))
 	    HText_appendCharacter(me->text, '"');
 	else
 	    HText_appendCharacter(me->text, '`');
@@ -2517,13 +2500,13 @@ PRIVATE void HTML_start_element ARGS5(
 	    }
 
 	    B_CurrentA = HTAnchor_findChildAndLink(
-			me->node_anchor,		/* Parent */
-			(id_string ? id_string : 0),	/* Tag */
-			(href ? href : 0),		/* Address */
+			me->node_anchor,			/* Parent */
+			id_string,				/* Tag */
+			href,					/* Address */
 			(present &&
 			 present[HTML_A_TYPE] &&
 			   value[HTML_A_TYPE]) ? 
-   (HTLinkType*)HTAtom_for(value[HTML_A_TYPE]) : 0);	/* Type */
+   (HTLinkType*)HTAtom_for(value[HTML_A_TYPE]) : (void *)0);	/* Type */
 
 	    /*
 	     *	Get rid of href since no longer needed.
@@ -2821,17 +2804,17 @@ PRIVATE void HTML_start_element ARGS5(
 		        if (B_ID_A = HTAnchor_findChildAndLink(
 				  me->node_anchor,	/* Parent */
 				  id_string,		/* Tag */
-				  0,			/* Addresss */
-				  0)) {			/* Type */
+				  NULL,			/* Addresss */
+				  (void *)0)) {		/* Type */
 		            HText_beginAnchor(me->text, B_ID_A);
 		            HText_endAnchor(me->text);
 		        }
 		    }
 		    B_CurrentA = HTAnchor_findChildAndLink(
 		    		me->node_anchor,	/* Parent */
-				0,			/* Tag */
+				NULL,			/* Tag */
 				map_href,		/* Addresss */
-				0);			/* Type */
+				(void *)0);		/* Type */
 		    if (B_CurrentA && title) {
 			if (dest = HTAnchor_parent(
 				HTAnchor_followMainLink((HTAnchor*)B_CurrentA)
@@ -2862,8 +2845,8 @@ PRIVATE void HTML_start_element ARGS5(
 		    if (B_ID_A = HTAnchor_findChildAndLink(
 				  me->node_anchor,	/* Parent */
 				  id_string,		/* Tag */
-				  0,			/* Addresss */
-				  0)) {			/* Type */
+				  NULL,			/* Addresss */
+				  (void *)0)) {		/* Type */
 		        HText_beginAnchor(me->text, B_ID_A);
 		        HText_endAnchor(me->text);
 		    }
@@ -2875,17 +2858,17 @@ PRIVATE void HTML_start_element ARGS5(
 		    if (B_ID_A = HTAnchor_findChildAndLink(
 				  me->node_anchor,	/* Parent */
 				  id_string,		/* Tag */
-				  0,			/* Addresss */
-				  0)) {			/* Type */
+				  NULL,			/* Addresss */
+				  (void *)0)) {		/* Type */
 		        HText_beginAnchor(me->text, B_ID_A);
 		        HText_endAnchor(me->text);
 		    }
 		}
 		B_CurrentA = HTAnchor_findChildAndLink(
 		    		me->node_anchor,	/* Parent */
-				0,			/* Tag */
+				NULL,			/* Tag */
 				map_href,		/* Addresss */
-				0);			/* Type */
+				(void *)0);		/* Type */
 		if (B_CurrentA && title) {
 		    if (dest = HTAnchor_parent(
 				HTAnchor_followMainLink((HTAnchor*)B_CurrentA)
@@ -2916,8 +2899,8 @@ PRIVATE void HTML_start_element ARGS5(
 		    if (B_ID_A = HTAnchor_findChildAndLink(
 				  me->node_anchor,	/* Parent */
 				  id_string,		/* Tag */
-				  0,			/* Addresss */
-				  0)) {			/* Type */
+				  NULL,			/* Addresss */
+				  (void *)0)) {		/* Type */
 		        HText_beginAnchor(me->text, B_ID_A);
 		        HText_endAnchor(me->text);
 		    }
@@ -2929,9 +2912,9 @@ PRIVATE void HTML_start_element ARGS5(
 	     */
 	    B_CurrentA = HTAnchor_findChildAndLink(
 			me->node_anchor,		/* Parent */
-			0,				/* Tag */
-			href ? href : 0,		/* Addresss */
-			0);				/* Type */
+			NULL,				/* Tag */
+			href,				/* Addresss */
+			(void *)0);			/* Type */
 	    FREE(href);
 	    HText_beginAnchor(me->text, B_CurrentA);
 	    if (B_inBoldH == FALSE)
@@ -2969,9 +2952,9 @@ PRIVATE void HTML_start_element ARGS5(
 	    }
 	    B_CurrentA = HTAnchor_findChildAndLink(
 		    		me->node_anchor,	/* Parent */
-				0,			/* Tag */
+				NULL,			/* Tag */
 				map_href,		/* Addresss */
-				0);			/* Type */
+				(void *)0);		/* Type */
 	    if (B_CurrentA && title) {
 		if (dest = HTAnchor_parent(
 				HTAnchor_followMainLink((HTAnchor*)B_CurrentA)
@@ -3007,8 +2990,8 @@ PRIVATE void HTML_start_element ARGS5(
 		if (B_ID_A = HTAnchor_findChildAndLink(
 				  me->node_anchor,	/* Parent */
 				  id_string,		/* Tag */
-				  0,			/* Addresss */
-				  0)) {			/* Type */
+				  NULL,			/* Addresss */
+				  (void *)0)) {		/* Type */
 		    HText_beginAnchor(me->text, B_ID_A);
 		    HText_endAnchor(me->text);
 		}
@@ -3238,9 +3221,9 @@ PRIVATE void HTML_start_element ARGS5(
 
 		if ((B_CurrentA = HTAnchor_findChildAndLink(
 		       			me->node_anchor,	/* Parent */
-		       			0,			/* Tag */
+		       			NULL,			/* Tag */
 		       			href,			/* Addresss */
-		       			0))) {			/* Type */
+		       			(void *)0))) {		/* Type */
 		    HText_beginAnchor(me->text, B_CurrentA);
 		    if (B_inBoldH == FALSE)
 			HText_appendCharacter(me->text,LY_BOLD_START_CHAR);
@@ -3415,9 +3398,9 @@ PRIVATE void HTML_start_element ARGS5(
 
 		if ((B_CurrentA = HTAnchor_findChildAndLink(
 					me->node_anchor,	/* Parent */
-					0,			/* Tag */
+					NULL,			/* Tag */
 					href,			/* Addresss */
-					0))) {			/* Type */
+					(void *)0))) {		/* Type */
 		    if (!me->text) {
 		        UPDATE_STYLE;
 		    } else {
@@ -3566,9 +3549,9 @@ PRIVATE void HTML_start_element ARGS5(
 	    if ((href && *href) &&
 	        (B_CurrentA = HTAnchor_findChildAndLink(
 					me->node_anchor,	/* Parent */
-					0,			/* Tag */
+					NULL,			/* Tag */
 					href,			/* Addresss */
-					0))) {			/* Type */
+					(void *)0))) {		/* Type */
 		HText_beginAnchor(me->text, B_CurrentA);
 		if (B_inBoldH == FALSE)
 		    HText_appendCharacter(me->text,LY_BOLD_START_CHAR);
@@ -3632,9 +3615,9 @@ PRIVATE void HTML_start_element ARGS5(
 	        UPDATE_STYLE;
 	    if ((B_CurrentA = HTAnchor_findChildAndLink(
 					me->node_anchor,	/* Parent */
-					0,			/* Tag */
+					NULL,			/* Tag */
 					href,			/* Addresss */
-					0))) {			/* Type */
+					(void *)0))) {		/* Type */
 		HTML_put_character(me, ' ');  /* space char may be ignored */
 		me->in_word = NO;
 		HText_beginAnchor(me->text, B_CurrentA);
@@ -3741,9 +3724,9 @@ PRIVATE void HTML_start_element ARGS5(
 
 		if ((B_CurrentA = HTAnchor_findChildAndLink(
 					me->node_anchor,	/* Parent */
-					0,			/* Tag */
+					NULL,			/* Tag */
 					href,			/* Addresss */
-					0))) {			/* Type */
+					(void *)0))) {		/* Type */
 		    HText_beginAnchor(me->text, B_CurrentA);
 		    if (B_inBoldH == FALSE)
 			HText_appendCharacter(me->text,LY_BOLD_START_CHAR);
@@ -3885,9 +3868,9 @@ PRIVATE void HTML_start_element ARGS5(
 	    }
 	    if (action) {
 	        source = HTAnchor_findChildAndLink(me->node_anchor, 
-						   0,
+						   NULL,
 						   action,
-						   0);
+						   (void *)0);
 		if (link_dest = HTAnchor_followMainLink((HTAnchor *)source)) {
 		    /*
 		     *  Memory leak fixed.
@@ -4033,6 +4016,7 @@ PRIVATE void HTML_start_element ARGS5(
 		     *  Not yet implemented.
 		     */
 		    HTML_put_string(me,"[RANGE Input] (Not yet implemented.)");
+		    HText_DisableCurrentForm();
 		    if (TRACE)
 		        fprintf(stderr, "HTML: Ignoring TYPE=\"range\"\n");
 		    break;
@@ -4052,6 +4036,7 @@ PRIVATE void HTML_start_element ARGS5(
 		        HText_appendCharacter(me->text,
 					      LY_UNDERLINE_END_CHAR);
 		    }
+		    HText_DisableCurrentForm();
 		    if (TRACE)
 		        fprintf(stderr, "HTML: Ignoring TYPE=\"file\"\n");
 		    break;
@@ -4232,8 +4217,8 @@ PRIVATE void HTML_start_element ARGS5(
 	        (B_ID_A = HTAnchor_findChildAndLink(
 				me->node_anchor,	 /* Parent */
 				id_string,		 /* Tag */
-				0,			 /* Addresss */
-				0))) {			 /* Type */
+				NULL,			 /* Addresss */
+				(void *)0))) {		 /* Type */
 		if (!me->text)
 		    UPDATE_STYLE;
 		HText_beginAnchor(me->text, B_ID_A);
@@ -4309,15 +4294,27 @@ PRIVATE void HTML_start_element ARGS5(
 		select_disabled=YES;
 	    if (present && present[HTML_SELECT_SIZE] &&
 	        value[HTML_SELECT_SIZE] && *value[HTML_SELECT_SIZE]) {
+#ifdef NOTDEFINED
 		StrAllocCopy(size, value[HTML_SELECT_SIZE]);
+#else
+		/*
+		 *  Let the size be determined by the number of OPTIONs. - FM
+		 */
+		if (TRACE)
+		    fprintf(stderr,
+		    	    "HTML: Ignoring SIZE=\"%s\" for SELECT.\n",
+		    	    (char *)value[HTML_SELECT_SIZE]);
+#endif /* NOTDEFINED */
 	    }
 
-	    if (B_inBoldH == TRUE && multiple == NO) {
+	    if (B_inBoldH == TRUE &&
+	        (multiple == NO || LYSelectPopups == FALSE)) {
 	        HText_appendCharacter(me->text,LY_BOLD_END_CHAR);
 		B_inBoldH = FALSE;
 		B_needBoldH = TRUE;
 	    }
-	    if (B_inUnderline == TRUE && multiple == NO) {
+	    if (B_inUnderline == TRUE &&
+	        (multiple == NO || LYSelectPopups == FALSE)) {
 	        HText_appendCharacter(me->text,LY_UNDERLINE_END_CHAR);
 		B_inUnderline = FALSE;
 	    }
@@ -4376,12 +4373,15 @@ PRIVATE void HTML_start_element ARGS5(
 	    }
 
 	    /*
-	     *  If its not a multiple option list then don't
-	     *  use the checkbox method, and don't put
-	     *  anything on the screen yet.
+	     *  If its not a multiple option list and select popups
+	     *  are enabled, then don't use the checkbox/button method,
+	     *  and don't put anything on the screen yet.
 	     */
-	    if (first_option || HTCurSelectGroupType == F_CHECKBOX_TYPE) {
-		if (HTCurSelectGroupType == F_CHECKBOX_TYPE) {
+	    if (first_option ||
+	        HTCurSelectGroupType == F_CHECKBOX_TYPE ||
+		LYSelectPopups == FALSE) {
+		if (HTCurSelectGroupType == F_CHECKBOX_TYPE ||
+		    LYSelectPopups == FALSE) {
 	            /*
 		     *  Start a newline before each option.
 		     */
@@ -4405,7 +4405,9 @@ PRIVATE void HTML_start_element ARGS5(
 
 	        I.type = "OPTION";
     
-	        if (present && present[HTML_OPTION_SELECTED])
+	        if ((present && present[HTML_OPTION_SELECTED]) ||
+		    (first_option && LYSelectPopups == FALSE &&
+		     HTCurSelectGroupType == F_RADIO_TYPE))
 		    I.checked=YES;
 
 		if (present && present[HTML_OPTION_VALUE] &&
@@ -4445,8 +4447,8 @@ PRIVATE void HTML_start_element ARGS5(
 		    if (B_ID_A = HTAnchor_findChildAndLink(
 				    me->node_anchor,	   /* Parent */
 				    value[HTML_OPTION_ID], /* Tag */
-				    0,			   /* Addresss */
-				    0)) {		   /* Type */
+				    NULL,		   /* Addresss */
+				    (void *)0)) {	   /* Type */
 			HText_beginAnchor(me->text, B_ID_A);
 			HText_endAnchor(me->text);
 		        I.id = (char *)value[HTML_OPTION_ID];
@@ -4455,9 +4457,8 @@ PRIVATE void HTML_start_element ARGS5(
 
 	        HText_beginInput(me->text, &I);
     
-	        first_option = FALSE;
-
-		if (HTCurSelectGroupType == F_CHECKBOX_TYPE) {
+		if (HTCurSelectGroupType == F_CHECKBOX_TYPE ||
+		    LYSelectPopups == FALSE) {
 	            /*
 		     *  Put 3 underscores and one space before each option.
 		     */
@@ -4472,10 +4473,14 @@ PRIVATE void HTML_start_element ARGS5(
 	     *  Get ready for the next value.
 	     */
             HTChunkClear(&me->option);
-	    if (present && present[HTML_OPTION_SELECTED])
-		LastOptionChecked=YES;
+	    if ((present && present[HTML_OPTION_SELECTED]) ||
+	        (first_option && LYSelectPopups == FALSE &&
+		 HTCurSelectGroupType == F_RADIO_TYPE))
+		LastOptionChecked = TRUE;
 	    else
-		LastOptionChecked=NO;
+		LastOptionChecked = FALSE;
+	    first_option = FALSE;
+
 
 	    if (present && present[HTML_OPTION_VALUE] &&
 	        value[HTML_OPTION_VALUE])
@@ -4596,6 +4601,9 @@ PRIVATE void HTML_end_element ARGS3(
 	int,			element_number,
 	char **,		include)
 {
+    int i;
+    char *temp = NULL;
+
 #ifdef CAREFUL			/* parser assumed to produce good nesting */
     if (element_number != me->sp[0].tag_number) {
         fprintf(stderr, 
@@ -4604,15 +4612,23 @@ PRIVATE void HTML_end_element ARGS3(
 		HTML_dtd.tags[me->sp->tag_number].name);
 		/* panic */
     }
-#endif
+#endif /* CAREFUL */
+
+    /*
+     *  If we're seeking MAPs, skip everything that's
+     *  not a MAP or AREA tag. - FM
+     */
     if (LYMapsOnly) {
         if (!(element_number == HTML_MAP || element_number == HTML_AREA)) {
 	    return;
 	}
     }
-    
+
+    /*
+     *  Pop state off stack.
+     */
     if (me->sp < me->stack + MAX_NESTING+1) {
-        (me->sp)++;				/* Pop state off stack */
+        (me->sp)++;
         if (TRACE)
 	    fprintf(stderr,
 	    	    "HTML:end_element: Popped style off stack - %s\n",
@@ -4623,7 +4639,9 @@ PRIVATE void HTML_end_element ARGS3(
   "Stack underflow error!  Tried to pop off more styles than exist in stack\n");
     }
     
-    /* Check for unclosed TEXTAREA */
+    /*
+     *  Check for unclosed TEXTAREA. - FM
+     */
     if (B_inTEXTAREA && element_number != HTML_TEXTAREA)
         if (TRACE) {
 	    fprintf(stderr, "HTML: Missing TEXTAREA end tag\n");
@@ -4633,6 +4651,9 @@ PRIVATE void HTML_end_element ARGS3(
 	    sleep(MessageSecs);
 	}
 
+    /*
+     *  Handle the end tag. - FM
+     */
     switch(element_number) {
 
     case HTML_HTML:
@@ -4647,8 +4668,6 @@ PRIVATE void HTML_end_element ARGS3(
 	    HText_appendCharacter(me->text,LY_UNDERLINE_END_CHAR);
 	    B_inUnderline = FALSE;
 	}
-	FREE(base_href);
-        B_inBASE = FALSE;
 	if (B_inA || B_inFORM || B_inSELECT || B_inTEXTAREA)
 	    if (TRACE)
 	        fprintf(stderr,
@@ -4683,6 +4702,36 @@ PRIVATE void HTML_end_element ARGS3(
         HTChunkTerminate(&me->title);
     	HTAnchor_setTitle(me->node_anchor, me->title.data);
         HTChunkClear(&me->title);
+	/*
+	 *  Check if it's a bookmark file, and if so, insert the
+	 *  current description string and filepath for it. - FM
+	 */
+	if (me->node_anchor->bookmark && *me->node_anchor->bookmark) {
+	    for (i = 0; i <= MBM_V_MAXFILES; i++) {
+	        if (MBM_A_subbookmark[i] &&
+		    !strcmp(MBM_A_subbookmark[i],
+		    	    me->node_anchor->bookmark)) {
+		    StrAllocCat(*include, "<H2><EM>Description:</EM> ");
+		    StrAllocCopy(temp,
+		    		 ((MBM_A_subdescript[i] &&
+				   *MBM_A_subdescript[i]) ?
+				     MBM_A_subdescript[i] : "(none)"));
+		    LYEntify((char **)&temp, TRUE);
+		    StrAllocCat(*include, temp);
+		    StrAllocCat(*include,
+		    		"<BR><EM>&nbsp;&nbsp;&nbsp;Filepath:</EM> ");
+		    StrAllocCopy(temp,
+		    		 ((MBM_A_subbookmark[i] &&
+				   *MBM_A_subbookmark[i]) ?
+				     MBM_A_subbookmark[i] : "(unknown)"));
+		    LYEntify((char **)&temp, TRUE);
+		    StrAllocCat(*include, temp);
+		    FREE(temp);
+		    StrAllocCat(*include, "</H2>");
+		    break;
+		}
+	    }
+	}
 	break;
 	
     case HTML_STYLE:
@@ -4886,7 +4935,7 @@ PRIVATE void HTML_end_element ARGS3(
 	 *  use chevrons, but for now we'll always use double-
 	 *  or single-quotes. - FM
 	 */
-	if (Quote_Level == ((Quote_Level/2)*2))
+	if (!(Quote_Level & 1))
 	    HText_appendCharacter(me->text, '"');
 	else
 	    HText_appendCharacter(me->text, '\'');
@@ -5508,9 +5557,10 @@ End_Object:
 
 	    LastOptionChecked = FALSE;
 
-	    if (HTCurSelectGroupType == F_CHECKBOX_TYPE) {
+	    if (HTCurSelectGroupType == F_CHECKBOX_TYPE ||
+	        LYSelectPopups == FALSE) {
 	            /*
-		     *  Start a newline after the last checkbox option.
+		     *  Start a newline after the last checkbox/button option.
 		     */
 		    HTML_EnsureSingleSpace(me);
 	    } else {
@@ -5648,11 +5698,22 @@ PUBLIC void HTML_free ARGS1(HTStructured *, me)
     UPDATE_STYLE;		/* Creates empty document here! */
     if (me->comment_end)
 		HTML_put_string(me,me->comment_end);
-    HText_endAppend(me->text);
+    if (me->text) {
+	if (B_inUnderline) {
+	    HText_appendCharacter(me->text,LY_UNDERLINE_END_CHAR);
+	    B_inUnderline = FALSE;
+	}
+	HText_endAppend(me->text);
+    }
 
     if (me->target) {
         (*me->targetClass._free)(me->target);
     }
+    List_Nesting_Level = -1;
+    HTML_zero_OL_Counter();
+    Division_Level = -1;
+    Underline_Level = 0;
+    Quote_Level = 0;
     if (me->sp && me->sp->style && me->sp->style->name) {
         if (!strcmp(me->sp->style->name, "DivCenter") ||
 	    !strcmp(me->sp->style->name, "HeadingCenter")) {
@@ -5666,6 +5727,7 @@ PUBLIC void HTML_free ARGS1(HTStructured *, me)
 	styles[HTML_PRE]->alignment = HT_LEFT;
     }
     FREE(base_href);
+    B_inBASE = FALSE;
     FREE(LYMapName);
     FREE(me);
 }
@@ -5689,6 +5751,21 @@ PRIVATE void HTML_abort ARGS2(HTStructured *, me, HTError, e)
     if (me->target) {
         (*me->targetClass._abort)(me->target, e);
     }
+    if (me->sp && me->sp->style && me->sp->style->name) {
+        if (!strcmp(me->sp->style->name, "DivCenter") ||
+	    !strcmp(me->sp->style->name, "HeadingCenter")) {
+	    me->sp->style->alignment = HT_CENTER;
+	} else if (!strcmp(me->sp->style->name, "DivRight") ||
+		   !strcmp(me->sp->style->name, "HeadingRight")) {
+	    me->sp->style->alignment = HT_RIGHT;
+	} else  {
+	    me->sp->style->alignment = HT_LEFT;
+	}
+	styles[HTML_PRE]->alignment = HT_LEFT;
+    }
+    FREE(base_href);
+    B_inBASE = FALSE;
+    FREE(LYMapName);
     FREE(me);
 }
 
@@ -5762,7 +5839,6 @@ PRIVATE void get_styles NOARGS
     styles[HTML_PLAINTEXT] =
     styles[HTML_XMP] =		HTStyleNamed(styleSheet, "Example");
     styles[HTML_PRE] =		HTStyleNamed(styleSheet, "Preformatted");
-    styles[HTML_STYLE] =	HTStyleNamed(styleSheet, "Style");
     styles[HTML_LISTING] =	HTStyleNamed(styleSheet, "Listing");
 }
 /*				P U B L I C
@@ -6206,8 +6282,8 @@ PRIVATE void HTML_CheckForID ARGS4(
 	    (B_ID_A = HTAnchor_findChildAndLink(
 				me->node_anchor,	/* Parent */
 				temp,			/* Tag */
-				0,			/* Addresss */
-				0))) {			/* Type */
+				NULL,			/* Addresss */
+				(void *)0))) {		/* Type */
 	    HText_beginAnchor(me->text, B_ID_A);
 	    HText_endAnchor(me->text);
 	}
@@ -6238,8 +6314,8 @@ PRIVATE void HTML_HandleID ARGS2(
     if (B_ID_A = HTAnchor_findChildAndLink(
 				me->node_anchor,	/* Parent */
 				id,			/* Tag */
-				0,			/* Addresss */
-				0)) {			/* Type */
+				NULL,			/* Addresss */
+				(void *)0)) {		/* Type */
 	HText_beginAnchor(me->text, B_ID_A);
 	HText_endAnchor(me->text);
     }
diff --git a/src/LYBookmark.c b/src/LYBookmark.c
index 88dca1ef..4ba29d0b 100644
--- a/src/LYBookmark.c
+++ b/src/LYBookmark.c
@@ -9,10 +9,12 @@
 #include "LYSystem.h"
 #include "LYKeymap.h"
 #include "LYCharUtils.h"
+#include "LYCurses.h"
 
 #ifdef VMS
 #include "HTVMSUtils.h"
 #include <nam.h>
+extern BOOLEAN HadVMSInterrupt;	/* Flag from cleanup_sig() AST */
 #endif /* VMS */
 
 #include "LYLeaks.h"
@@ -26,52 +28,70 @@ PRIVATE char * convert_mosaic_bookmark_file PARAMS((char *filename_buffer));
  *  Tries to open the bookmark file for reading.
  *  if successful the file is closed and the filename
  *  is returned and the URL is given in name.
+ *
+ *  Returns a zero-length pointer to flag a cancel,
+ *  a space to flag an undefined selection, and
+ *  NULL for a failure (processing error). - FM
  */
-PUBLIC char * get_bookmark_filename ARGS1(char **,URL)
+PUBLIC char * get_bookmark_filename ARGS1(
+	char **, URL)
 {
     char URL_buffer[256];
     static char filename_buffer[256];
     char string_buffer[256];
     FILE *fp;
+    int MBM_tmp;
 
-    if (!bookmark_page) {
+    /*
+     *  Multi_Bookmarks support. - FMG & FM
+     *  Let user select a bookmark file.
+     */
+    MBM_tmp = select_multi_bookmarks();
+    if (MBM_tmp == -2)
+        /*
+	 *  Zero-length pointer flags a cancel.
+	*/
+        return("");
+    if (MBM_tmp == -1) {
 	sprintf(string_buffer,
 		BOOKMARK_FILE_NOT_DEFINED,
 		key_for_func(LYK_OPTIONS));
 	_statusline(string_buffer);
 	sleep(AlertSecs);
-	return(NULL);
+	/*
+	 *  Space flags an undefined selection.
+	*/
+	return(" ");
+    } else {
+	StrAllocCopy(BookmarkPage, MBM_A_subbookmark[MBM_tmp]);
     }
 
     /*
-     *  See if it is in the home path.
+     *  Seek it in the home path.
      */
 #ifdef VMS
-    sprintf(filename_buffer,"%s%s", Home_Dir(), bookmark_page);
+    LYVMS_HomePathAndFilename(filename_buffer,
+			      sizeof(filename_buffer),
+			      BookmarkPage);
 #else
-    sprintf(filename_buffer,"%s/%s", Home_Dir(), bookmark_page);
+    sprintf(filename_buffer,"%s/%s", Home_Dir(), BookmarkPage);
 #endif /* VMS */
+    if (TRACE)
+        fprintf(stderr, "\nget_bookmark_filename: SEEKING %s\n   AS %s\n\n",
+		BookmarkPage, filename_buffer);
     if ((fp = fopen(filename_buffer,"r")) != NULL) {
 	goto success;
     }
 
     /*
-     *  See if we can open it raw.
-     */
-    if ((fp = fopen(bookmark_page,"r")) != NULL) {
-	strcpy(filename_buffer, bookmark_page);
-	goto success;
-    } 
-
-    /*
      *  Failure.
      */
     return(NULL);
 
 success:
     /*
-     *  We now have the file open.  Check if it is a mosaic
-     *  hotlist.
+     *  We now have the file open.
+     *  Check if it is a mosaic hotlist.
      */
     if (fgets(string_buffer, 255, fp) &&
 	!strncmp(string_buffer, "ncsa-xmosaic-hotlist-format-1", 29)) {
@@ -79,23 +99,23 @@ success:
 	/*
 	 *  It is a mosaic hotlist file.
 	 */
-	is_mosaic_hotlist=TRUE;
+	is_mosaic_hotlist = TRUE;
 	fclose(fp);
 	newname = convert_mosaic_bookmark_file(filename_buffer);
 #ifdef VMS
-    sprintf(URL_buffer,"file://localhost%s", HTVMS_wwwName((char *)newname));
+	sprintf(URL_buffer,"file://localhost%s",
+		HTVMS_wwwName((char *)newname));
 #else
-    sprintf(URL_buffer,"file://localhost%s", newname);
+	sprintf(URL_buffer,"file://localhost%s", newname);
 #endif /* VMS */
-
     } else {
 	fclose(fp);
-	is_mosaic_hotlist=FALSE;
+	is_mosaic_hotlist = FALSE;
 #ifdef VMS
-    sprintf(URL_buffer,"file://localhost%s",
-    			HTVMS_wwwName((char *)filename_buffer));
+	sprintf(URL_buffer,"file://localhost%s",
+    		HTVMS_wwwName((char *)filename_buffer));
 #else
-    sprintf(URL_buffer,"file://localhost%s", filename_buffer);
+	sprintf(URL_buffer,"file://localhost%s", filename_buffer);
 #endif /* VMS */
     }
 
@@ -104,7 +124,8 @@ success:
 
 } /* big end */
 
-PRIVATE char * convert_mosaic_bookmark_file ARGS1(char *,filename_buffer)
+PRIVATE char * convert_mosaic_bookmark_file ARGS1(
+	char *,	filename_buffer)
 {
     static char newfile[256];
     static BOOLEAN first = TRUE;
@@ -122,14 +143,13 @@ PRIVATE char * convert_mosaic_bookmark_file ARGS1(char *,filename_buffer)
 #endif /* VMS */
     }
 
-
-    if((nfp = fopen(newfile, "w")) == NULL) {
+    if ((nfp = fopen(newfile, "w")) == NULL) {
 	_statusline(NO_TEMP_FOR_HOTLIST);
 	sleep(AlertSecs);
 	return ("");
     }
 
-    if((fp = fopen(filename_buffer, "r")) == NULL)
+    if ((fp = fopen(filename_buffer, "r")) == NULL)
 	return ("");  /* should always open */
 
     fprintf(nfp,"<head>\n<title>%s</title>\n</head>\n",MOSAIC_BOOKMARK_TITLE);
@@ -161,7 +181,9 @@ PRIVATE char * convert_mosaic_bookmark_file ARGS1(char *,filename_buffer)
     return(newfile);
 }
 
-PUBLIC void save_bookmark_link ARGS2(char *,address, char *,title)
+PUBLIC void save_bookmark_link ARGS2(
+	char *,	address,
+	char *,	title)
 {
     FILE *fp;
     BOOLEAN first_time = FALSE;
@@ -177,10 +199,27 @@ PUBLIC void save_bookmark_link ARGS2(char *,address, char *,title)
     }
 
     filename = get_bookmark_filename(&bookmark_URL);
-    FREE(bookmark_URL); /* don't need it */
-    if (!bookmark_page)
+
+    /*
+     *  If filename is a space, invalid bookmark
+     *  file was selected.  If zero-length, user
+     *  cancelled.  Ignore request in both cases!
+     */
+    if (filename)
+      if (*filename == '\0' || !strcmp(filename," "))
+	return;
+    /*
+     *  If BookmarkPage didn't get loaded, something
+     *  went wrong, so ignore the request.
+     */
+    if (!BookmarkPage)
 	return;
 
+     /*
+      *  We don't need the full URL.
+      */
+    FREE(bookmark_URL);
+
     /*
      *  Allow user to change the title. - FM
      */
@@ -205,32 +244,25 @@ PUBLIC void save_bookmark_link ARGS2(char *,address, char *,title)
     /*
      *  Open the bookmark file. - FM
      */
-    if (filename == NULL) {
+    if (filename == NULL)
 	first_time = TRUE;
-	/*
-	 *  Try in the home directory first.
-	 */
+    /*
+     *  Try in the home directory.
+     */
 #ifdef VMS
-    	sprintf(filename_buffer, "sys$login:%s", bookmark_page);
+    LYVMS_HomePathAndFilename(filename_buffer,
+			      sizeof(filename_buffer),
+			      BookmarkPage);
 #else
-    	sprintf(filename_buffer, "%s/%s", Home_Dir(), bookmark_page);
+    sprintf(filename_buffer, "%s/%s", Home_Dir(), BookmarkPage);
 #endif /* VMS */
-    	if ((fp = fopen(filename_buffer,"w")) == NULL) {
-	   /*
-	    *  Try it raw.
-	    */
-    	    if ((fp = fopen(bookmark_page,"r")) == NULL) {
-	        _statusline(BOOKMARK_OPEN_FAILED);
-	        sleep(AlertSecs);
-	        return;
-	    }
-	}
-    } else {
-	if ((fp = fopen(filename,"a+")) == NULL) {
-	    _statusline(BOOKMARK_OPEN_FAILED);
-	    sleep(AlertSecs);
-	    return;
-	}
+    if (TRACE)
+        fprintf(stderr, "\nsave_bookmark_link: SEEKING %s\n   AS %s\n\n",
+		BookmarkPage, filename_buffer);
+    if ((fp = fopen(filename_buffer, (first_time ? "w" : "a+"))) == NULL) {
+	_statusline(BOOKMARK_OPEN_FAILED);
+	sleep(AlertSecs);
+	return;
     }
 
     /*
@@ -245,8 +277,8 @@ PUBLIC void save_bookmark_link ARGS2(char *,address, char *,title)
     if (first_time) {
 	fprintf(fp,"<head>\n<title>%s</title>\n</head>\n",BOOKMARK_TITLE);
 	fprintf(fp,"\
-     You can delete links using the new remove bookmark command.\n\
-     it is usually the 'R' key but may have been remapped by you or\n\
+     You can delete links using the remove bookmark command.  It\n\
+     is usually the 'R' key but may have been remapped by you or\n\
      your system administrator.<br>\n\
      This file may also be edited with a standard text editor.\n\
      Outdated or invalid links may be removed by simply deleting\n\
@@ -277,38 +309,47 @@ PUBLIC void save_bookmark_link ARGS2(char *,address, char *,title)
     sleep(MessageSecs);
 }
 	
-PUBLIC void remove_bookmark_link ARGS1(int,cur)
+PUBLIC void remove_bookmark_link ARGS2(
+	int,		cur,
+	char *,		cur_bookmark_page)
 {
     FILE *fp, *nfp;
     char buf[BUFSIZ];
     int n;
 #ifdef VMS
+    char filename_buffer[NAM$C_MAXRSS+12];
     char newfile[NAM$C_MAXRSS+12];
 #else
-    char newfile[128];
+    char filename_buffer[256];
+    char newfile[256];
     struct stat stat_buf;
     mode_t mode;
 #endif /* VMS */
-    char *filename;
-    char *URL = 0;
 
     if (TRACE)
 	fprintf(stderr, "remove_bookmark_link: deleting link number: %d\n",
 			cur);
 
-    filename = get_bookmark_filename(&URL);
-    FREE(URL); /* don't need it */
-    if (!bookmark_page)
+    if (!cur_bookmark_page)
 	return;
-
-    if ((!filename) || (fp=fopen(filename, "r")) == NULL) {
+#ifdef VMS
+    LYVMS_HomePathAndFilename(filename_buffer,
+			      sizeof(filename_buffer),
+			      cur_bookmark_page);
+#else
+    sprintf(filename_buffer,"%s/%s", Home_Dir(), cur_bookmark_page);
+#endif /* VMS */
+    if (TRACE)
+        fprintf(stderr, "\nremove_bookmark_link: SEEKING %s\n   AS %s\n\n",
+		cur_bookmark_page, filename_buffer);
+    if ((fp = fopen(filename_buffer, "r")) == NULL) {
 	_statusline(BOOKMARK_OPEN_FAILED_FOR_DEL);
 	sleep(AlertSecs);
 	return;
     }
 
 #ifdef VMS
-    sprintf(newfile, "%s-%d", filename, getpid());
+    sprintf(newfile, "%s-%d", filename_buffer, getpid());
 #else
     tempname(newfile, NEW_FILE);
 #endif /* VMS */
@@ -327,7 +368,7 @@ PUBLIC void remove_bookmark_link ARGS1(int,cur)
     /*
      *  Explicitly preserve bookmark file mode on Unix. - DSL
      */
-    if (stat(filename,&stat_buf) == 0) {
+    if (stat(filename_buffer, &stat_buf) == 0) {
 	mode = ((stat_buf.st_mode & 0777) | 0600);
 	(void) fclose(nfp);
 	nfp = NULL;
@@ -385,27 +426,27 @@ PUBLIC void remove_bookmark_link ARGS1(int,cur)
 
     if (TRACE)
 	fprintf(stderr, "remove_bookmark_link: files: %s %s\n",
-			newfile, filename);
+			newfile, filename_buffer);
 
     fclose(fp);
     fp = NULL;
     fclose(nfp);
     nfp = NULL;
  	
-    if (rename(newfile, filename) != -1) {
+    if (rename(newfile, filename_buffer) != -1) {
 #ifdef VMS
 	char VMSfilename[256];
 	/*
 	 *  Purge lower version of file.
 	 */
-	sprintf(VMSfilename, "%s;-1", filename);
+	sprintf(VMSfilename, "%s;-1", filename_buffer);
         while (remove(VMSfilename) == 0)
 	    ;
 	/*
 	 *  Reset version number.
 	 */
-	sprintf(VMSfilename, "%s;1", filename);
-	rename(filename, VMSfilename);
+	sprintf(VMSfilename, "%s;1", filename_buffer);
+	rename(filename_buffer, VMSfilename);
 #endif /* VMS */
         return;
     } else {
@@ -417,7 +458,7 @@ PUBLIC void remove_bookmark_link ARGS1(int,cur)
 	 */
 	if (errno == EXDEV) {
 	    char buffer[2048];
-	    sprintf(buffer, "%s %s %s", MV_PATH, newfile, filename);
+	    sprintf(buffer, "%s %s %s", MV_PATH, newfile, filename_buffer);
 	    system(buffer);
 	    return;
 	}
@@ -442,3 +483,304 @@ failure:
         fclose(fp);
     remove(newfile);
 }
+
+/*
+ *  Allows user to select sub-bookmarks files. - FMG & FM
+ */
+PUBLIC int select_multi_bookmarks NOARGS
+{
+    int c;
+
+    /*
+     *  If not enabled, pick the "default" (0).
+     */
+    if (LYMultiBookmarks == FALSE || LYHaveSubBookmarks() == FALSE) {
+	if (MBM_A_subbookmark[0]) /* If it exists! */
+            return(0);
+	else
+            return(-1);
+    }
+
+    /*
+     *  For ADVANCED users, we can just mess with the status line to save
+     *  the 2 redraws of the screen, if LYMBMAdvnced is TRUE.  '=' will
+     *  still show the screen and let them do it the "long" way.
+     */
+    if (LYMBMAdvanced && user_mode == ADVANCED_MODE) {
+	move(LYlines-1, 0);
+	clrtoeol();
+	start_reverse();
+	addstr("Select subbookmark, '=' for menu, or ^G to cancel: ");
+	stop_reverse();
+	refresh();
+
+get_advanced_choice:
+	c = LYgetch();
+#ifdef VMS
+	if (HadVMSInterrupt) {
+	    HadVMSInterrupt = FALSE;
+	    c = 7;
+        }
+#endif /* VMS */
+	if (LYisNonAlnumKeyname(c, LYK_PREV_DOC) ||
+	    c == 7 || c == 3) {
+	    /*
+	     *  Treat left-arrow, ^G, or ^C as cancel.
+	     */
+	    return(-2);
+	}
+	if (LYisNonAlnumKeyname(c, LYK_REFRESH)) {
+	    /*
+	     *  Refresh the screen.
+	     */
+	    clearok(curscr, TRUE);
+	    refresh();
+	    goto get_advanced_choice;
+	}
+	if (LYisNonAlnumKeyname(c, LYK_ACTIVATE)) {
+	    /*
+	     *  Assume default bookmark file on ENTER or right-arrow.
+	     */
+	    return (MBM_A_subbookmark[0] ? 0 : -1);
+	}
+	switch (c) {
+	    case '=':
+	        /*
+		 *  Get the choice via the menu.
+		 */
+		return(select_menu_multi_bookmarks());
+
+	    default:
+	        /*
+		 *  Convert to an array index, act on it if valid.
+		 *  Otherwise, get another keystroke.
+		 */
+		c = TOUPPER(c) - 'A';
+		if (c < 0 || c > MBM_V_MAXFILES) {
+		    goto get_advanced_choice;
+		}
+	}
+	/*
+	 *  See if we have a bookmark like that.
+	 */
+	return (MBM_A_subbookmark[c] ? c : -1);
+    } else {
+        /*
+	 *  Get the choice via the menu.
+	 */
+	return(select_menu_multi_bookmarks());
+    }
+}
+
+/*
+ *  Allows user to select sub-bookmarks files. - FMG & FM
+ */
+PUBLIC int select_menu_multi_bookmarks NOARGS
+{
+    FILE *fp;
+    int c, MBM_counter, MBM_tmp_count, MBM_allow;
+    int MBM_screens, MBM_from, MBM_to, MBM_current;
+    char string_buffer[256];
+    char *cp, *cp1;
+
+    /*
+     *  If not enabled, pick the "default" (0).
+     */
+    if (LYMultiBookmarks == FALSE)
+	return(0);
+
+    /*
+     *  Filip M. Gieszczykiewicz (filipg@paranoia.com) & FM
+     *  ---------------------------------------------------
+     *  LYMultiBookmarks - TRUE when multi_support enabled.
+     *
+     *  MBM_A_subbookmark[n] - Hold values of the respective
+     *  "multi_bookmarkn" in the lynxrc file.
+     *
+     *  MBM_A_subdescript[n] - Hold description entries in the
+     *  lynxrc file.
+     *
+     *  Note: MBM_A_subbookmark[0] is defined to be same value as
+     *        "bookmark_file" in the lynxrc file and/or the startup
+     *        "bookmark_page".
+     *
+     *  We make the display of bookmarks depend on rows we have
+     *  available.
+     *
+     *  We load BookmarkPage with the valid MBM_A_subbookmark[n]
+     *  via get_bookmark_filename().  Otherwise, that function
+     *  returns a zero-length string to indicate a cancel, a
+     *  single space to indicate an invalid choice, or NULL to
+     *  indicate an inaccessible file.
+     */
+    MBM_allow=(LYlines-7);	/* We need 7 for header and footer */
+    /*
+     *  Screen big enough?
+     */
+    if (MBM_allow <= 0) {
+        /*
+	 *  Too small.
+	 */
+	_statusline(MULTIBOOKMARKS_SMALL);
+	sleep(AlertSecs);
+	return (-2);
+    }
+    /*
+     *  Load the bad choice message.
+     */
+    sprintf(string_buffer,
+    	    BOOKMARK_FILE_NOT_DEFINED,
+	    key_for_func(LYK_OPTIONS));
+
+    MBM_screens = (MBM_V_MAXFILES/MBM_allow)+1; /* int rounds off low. */
+
+    MBM_current = 1; /* Gotta start somewhere :-) */
+
+draw_bookmark_choices:
+    MBM_from = MBM_allow * MBM_current - MBM_allow;
+    if (MBM_from < 0)
+	MBM_from = 0; /* 0 is default bookmark... */
+    if (MBM_current != 1)
+	MBM_from++;
+
+    MBM_to = (MBM_allow * MBM_current);
+    if (MBM_to > MBM_V_MAXFILES)
+	MBM_to = MBM_V_MAXFILES;
+
+    /*
+     * Display menu of bookmarks.
+     */
+    clear();
+    move(1, 5);
+    if (bold_H1 || bold_headers)
+	start_bold();
+    if (MBM_screens > 1)
+	printw(" Select Bookmark (screen %d of %d)", MBM_current, MBM_screens);
+    else
+	printw("       Select Bookmark");
+    if (bold_H1 || bold_headers)
+	stop_bold();
+
+    MBM_tmp_count = 0;
+    for (c = MBM_from; c <= MBM_to; c++) {
+	move(3+MBM_tmp_count, 5);
+	printw("%c : %s",(c+'A'),
+	       (!MBM_A_subdescript[c] ? "" : MBM_A_subdescript[c]));
+
+	move(3+MBM_tmp_count,36);
+	printw("(%s)",
+	       (!MBM_A_subbookmark[c] ? "" : MBM_A_subbookmark[c]));
+
+	MBM_tmp_count++;
+    }
+
+    /*
+     *  Don't need to show it if it all fits on one screen!
+     */
+    if (MBM_screens > 1) {
+	move(LYlines-2, 0);
+	addstr(MULTIBOOKMARKS_MOVE);
+    }
+
+    move(LYlines-1, 0);
+    clrtoeol();
+    start_reverse();
+    addstr(MULTIBOOKMARKS_SAVE);
+    stop_reverse();
+    refresh();
+
+get_bookmark_choice:
+    c = LYgetch();
+#ifdef VMS
+    if (HadVMSInterrupt) {
+	HadVMSInterrupt = FALSE;
+	c = 7;
+    }
+#endif /* VMS */
+
+    if (LYisNonAlnumKeyname(c, LYK_PREV_DOC) ||
+	c == 7 || c == 3) {
+	/*
+	 *  Treat left-arrow, ^G, or ^C as cancel.
+	 */
+	return(-2);
+    }
+
+    if (LYisNonAlnumKeyname(c, LYK_REFRESH)) {
+	/*
+	 *  Refresh the screen.
+	 */
+	clearok(curscr, TRUE);
+	refresh();
+	goto get_bookmark_choice;
+    }
+
+    if (LYisNonAlnumKeyname(c, LYK_ACTIVATE)) {
+	/*
+	 *  Assume default bookmark file on ENTER or right-arrow.
+	 */
+	return(MBM_A_subbookmark[0] ? 0 : -1);
+    }
+
+    /*
+     *  Next range, if available.
+     */
+    if ((c == ']' ||  LYisNonAlnumKeyname(c, LYK_NEXT_PAGE)) &&
+        MBM_screens > 1) {
+	if (++MBM_current > MBM_screens)
+	    MBM_current = 1;
+	goto draw_bookmark_choices;
+    }
+
+    /*
+     *  Previous range, if available.
+     */
+    if ((c == '[' ||  LYisNonAlnumKeyname(c, LYK_PREV_PAGE)) &&
+        MBM_screens > 1) {
+	if (--MBM_current <= 0)
+	    MBM_current = MBM_screens;
+	goto draw_bookmark_choices;
+    }
+
+    c = TOUPPER(c) - 'A';
+    /*
+     *  See if we have a bookmark like that.
+     */
+    if (c < 0 || c > MBM_V_MAXFILES) {
+	goto get_bookmark_choice;
+    } else if (!MBM_A_subbookmark[c]) {
+	move(LYlines-1, 0);
+	clrtoeol();
+	start_reverse();
+ 	addstr(string_buffer);
+	stop_reverse();
+	refresh();
+	sleep(AlertSecs);
+	move(LYlines-1, 0);
+	clrtoeol();
+	start_reverse();
+	addstr(MULTIBOOKMARKS_SAVE);
+	stop_reverse();
+	refresh();
+	goto get_bookmark_choice;
+    } else {
+	return(c);
+    }
+}
+
+/*
+ *  This function returns TRUE if we have sub-bookmarks defined.
+ *  Otherwise (i.e., only the default bookmark file is defined),
+ *  it returns FALSE. - FM
+ */
+PUBLIC BOOLEAN LYHaveSubBookmarks NOARGS
+{
+    int i;
+
+    for (i = 1; i < MBM_V_MAXFILES; i++) {
+        if (MBM_A_subbookmark[i] != NULL && *MBM_A_subbookmark[i] != '\0')
+	    return(TRUE);
+    }
+
+    return(FALSE);
+}
diff --git a/src/LYBookmark.h b/src/LYBookmark.h
index 838c025e..41f54c6b 100644
--- a/src/LYBookmark.h
+++ b/src/LYBookmark.h
@@ -8,10 +8,22 @@
 
 extern char * get_bookmark_filename PARAMS((char **name));
 extern void save_bookmark_link PARAMS((char *address, char *title));
-extern void remove_bookmark_link PARAMS((int cur));
+extern void remove_bookmark_link PARAMS((int cur, char *cur_bookmark_page));
+extern int select_multi_bookmarks NOPARAMS;
+extern int select_menu_multi_bookmarks NOPARAMS;
+extern BOOLEAN LYHaveSubBookmarks NOPARAMS;
+
+extern void edit_bookmarks NOPARAMS; /* in LYOptions.c */
 
 #define BOOKMARK_TITLE "Bookmark file"
 #define MOSAIC_BOOKMARK_TITLE "Converted Mosaic Hotlist"
+#define MBM_V_MAXFILES  25	/* Max number of sub-bookmark files */
+/*
+ *  Arrays that holds the names of sub-bookmark files
+ *  and their descriptions.
+ */
+char  *MBM_A_subbookmark[MBM_V_MAXFILES+1];
+char  *MBM_A_subdescript[MBM_V_MAXFILES+1];
 
 #endif /* LYBOOKMARK_H */
 
diff --git a/src/LYCgi.c b/src/LYCgi.c
index 77eefb77..9a81fadb 100644
--- a/src/LYCgi.c
+++ b/src/LYCgi.c
@@ -176,10 +176,8 @@ PUBLIC int LYLoadCGI ARGS4(
 	sleep(MessageSecs);
 	status = HT_NOT_LOADED;
 
-    } else if (!reloading && no_bookmark_exec && bookmark_page &&
-	       (strstr(HTLoadedDocumentURL(), bookmark_page) ||
-		!strcmp(HTLoadedDocumentTitle(),
-			MOSAIC_BOOKMARK_TITLE))) {
+    } else if (!reloading && no_bookmark_exec &&
+ 	       HTLoadedDocumentBookmark()) {
 	_statusline(BOOKMARK_EXEC_DISABLED);
 	sleep(MessageSecs);
 	status = HT_NOT_LOADED;
diff --git a/src/LYCharUtils.c b/src/LYCharUtils.c
index 75d50f8d..18aa65d0 100644
--- a/src/LYCharUtils.c
+++ b/src/LYCharUtils.c
@@ -1024,7 +1024,7 @@ PUBLIC char *LYFindEndOfComment ARGS1(
 	 */
         return NULL;
 
-    if (strcmp(str, "<!--"))
+    if (strncmp(str, "<!--", 4))
         /*
 	 *  We don't have the start of a comment, so
 	 *  return the beginning of the string. - FM
diff --git a/src/LYCurses.h b/src/LYCurses.h
index 065fdd1d..d18cd6e3 100644
--- a/src/LYCurses.h
+++ b/src/LYCurses.h
@@ -12,8 +12,7 @@
 #if defined(UNIX) && !defined(unix)
 #define unix
 #endif /* UNIX && !unix */
-#include "slang.h"
-#include "slcurses.h"
+#include <slang.h>
 
 #else /* Using curses: */
 
@@ -125,6 +124,21 @@ extern unsigned int Lynx_Color_Flags;
 #define NO_TTYTYPE
 #endif /* !NO_TTYTYPE */
 
+/* Map some curses functions to slang functions. */
+#define stdscr NULL
+#define COLS SLtt_Screen_Cols
+#define LINES SLtt_Screen_Rows
+#define move SLsmg_gotorc
+#define addstr SLsmg_write_string
+#define clear SLsmg_cls
+#define standout SLsmg_reverse_video
+#define standend  SLsmg_normal_video
+#define clrtoeol SLsmg_erase_eol
+#define scrollok(a,b) SLsmg_Newline_Moves = ((b) ? 1 : -1)
+#define addch SLsmg_write_char
+#define echo()
+#define printw SLsmg_printf
+ 
 extern int curscr;
 extern BOOLEAN FullRefresh;
 #ifdef clearok
@@ -139,10 +153,9 @@ extern void LY_SLrefresh NOPARAMS;
 
 #ifdef VMS
 extern void VTHome NOPARAMS;
-#ifdef endwin
-#undef endwin
-#endif /*endwin */
 #define endwin() clear(),refresh(),SLsmg_reset_smg(),VTHome()
+#else
+#define endwin SLsmg_reset_smg(),SLang_reset_tty
 #endif /* VMS */
 
 #else /* Define curses functions: */
diff --git a/src/LYForms.c b/src/LYForms.c
index 14132d58..b7cd64ee 100644
--- a/src/LYForms.c
+++ b/src/LYForms.c
@@ -25,8 +25,8 @@ extern HTCJKlang HTCJK;
 
 PRIVATE int form_getstr PARAMS((struct link * form_link));
 PRIVATE int popup_options PARAMS((int cur_selection, OptionType *list, 
-						   int ly, int lx, int width,
-						   int i_length));
+				  int ly, int lx, int width,
+				  int i_length, int disabled));
 
 PUBLIC int change_form_link ARGS6(struct link *, form_link, int, mode, 
 				  document *,newdoc, BOOLEAN *,refresh_screen,
@@ -36,10 +36,11 @@ PUBLIC int change_form_link ARGS6(struct link *, form_link, int, mode,
     FormInfo *form = form_link->form;
     int c=DO_NOTHING;
 
-	/*	If there is no form to perform action on, don't do anything.
+	/*
+	 *  If there is no form to perform action on, don't do anything.
 	 */
-	if(form == NULL)	{
-		return(c);
+	if (form == NULL) {
+	    return(c);
 	}
 
     /* move to the link position */
@@ -49,7 +50,7 @@ PUBLIC int change_form_link ARGS6(struct link *, form_link, int, mode,
 	case F_CHECKBOX_TYPE:
 	    if (form->disabled == YES)
 	        break;
-	    if(form->num_value) {
+	    if (form->num_value) {
 		form_link->hightext = unchecked_box;
 		form->num_value = 0;
 	    } else {
@@ -69,18 +70,18 @@ PUBLIC int change_form_link ARGS6(struct link *, form_link, int, mode,
 	        int dummy;
 		dummy = popup_options(form->num_value, form->select_list,
 				form_link->ly, form_link->lx, form->size,
-				form->size_l);
+				form->size_l, form->disabled);
 	        c = 12;  /* CTRL-R for repaint */
 	        break;
 	    }
 	    form->num_value = popup_options(form->num_value, form->select_list,
 				form_link->ly, form_link->lx, form->size,
-				form->size_l);
+				form->size_l, form->disabled);
 
 	    {
     	        OptionType * opt_ptr=form->select_list;
 		int i;
-    	        for(i=0; i<form->num_value; i++, opt_ptr = opt_ptr->next) 
+    	        for (i = 0; i < form->num_value; i++, opt_ptr = opt_ptr->next) 
 		    ; /* null body */
 		form->value = opt_ptr->name;   /* set the name */
 		form->cp_submit_value = opt_ptr->cp_submit_value; /* set the value */
@@ -94,7 +95,7 @@ PUBLIC int change_form_link ARGS6(struct link *, form_link, int, mode,
 		/* radio buttons must have one and
 		 * only one down at a time! 
 		 */
-	    if(form->num_value) {
+	    if (form->num_value) {
 		_statusline(NEED_CHECKED_RADIO_BUTTON);
 		sleep(MessageSecs);
 
@@ -104,8 +105,8 @@ PUBLIC int change_form_link ARGS6(struct link *, form_link, int, mode,
 		 * unselect any that are selected. :) 
 		 */
 		start_bold();
-		for(i=0; i < nlinks; i++)
-		   if(links[i].type == WWW_FORM_LINK_TYPE &&
+		for (i = 0; i < nlinks; i++)
+		   if (links[i].type == WWW_FORM_LINK_TYPE &&
 		       links[i].form->type == F_RADIO_TYPE &&
 		        links[i].form->number == form->number &&
    		         /* if it has the same name and its on */
@@ -128,7 +129,7 @@ PUBLIC int change_form_link ARGS6(struct link *, form_link, int, mode,
 	case F_TEXTAREA_TYPE:
 	case F_PASSWORD_TYPE:
 	    c = form_getstr(form_link);
-	    if(form->type == F_PASSWORD_TYPE) 
+	    if (form->type == F_PASSWORD_TYPE) 
         	form_link->hightext = STARS(strlen(form->value));
 	    else
 	    	form_link->hightext = form->value;
@@ -230,7 +231,7 @@ PRIVATE int form_getstr ARGS1(struct link *, form_link)
     /* get the initial position of the cursor */
     GetYX(startline, startcol);
 
-    if(startcol + form->size > LYcols-1)
+    if (startcol + form->size > LYcols-1)
 	far_col = LYcols-1;
     else
 	far_col = startcol + form->size;
@@ -291,11 +292,11 @@ again:
 	    case LTARROW:
 	        if (MyEdit.pos==0) {
 		    int c='Y';	/* Go back immediately if no changes */
-		    if(strcmp(MyEdit.buffer, form->value)) {
+		    if (strcmp(MyEdit.buffer, form->value)) {
 		        _statusline(PREV_DOC_QUERY);
 			c=LYgetch();
 		    }
-		    if(TOUPPER(c) == 'Y') {
+		    if (TOUPPER(c) == 'Y') {
 #ifdef NOTDEFINED
 			/*
 			 * Why not just keep what we have??
@@ -351,97 +352,124 @@ breakfor:
 }
 
 
-PRIVATE int popup_options ARGS6(int,cur_selection, OptionType *,list, 
-						   int, ly, int, lx, int,width,
-						   int, i_length)
+PRIVATE int popup_options ARGS7(
+	int,		cur_selection,
+	OptionType *,	list,
+	int,		ly,
+	int,		lx,
+	int,		width,
+	int,		i_length,
+	int,		disabled)
 {
     /*
-     * Revamped to handle within-tag VALUE's, if present,
-     * and to position the popup window appropriately,
-     * taking the user_mode setting into account. -- FM
+     *  Revamped to handle within-tag VALUE's, if present,
+     *  and to position the popup window appropriately,
+     *  taking the user_mode setting into account. -- FM
      */
-    int c=0, cmd=0, i=0;
+    int c = 0, cmd = 0, i = 0, j = 0;
     int orig_selection = cur_selection;
 #ifndef USE_SLANG
     WINDOW * form_window;
 #endif /* !USE_SLANG */
-    int num_options=0, top, bottom, length= -1;
-    OptionType * opt_ptr=list;
-    int window_offset=0;
+    int num_options = 0, top, bottom, length = -1;
+    OptionType * opt_ptr = list;
+    int window_offset = 0;
     int display_lines;
 #ifdef VMS
     extern BOOLEAN HadVMSInterrupt; /* Flag from cleanup_sig() AST */
-#endif
+#endif /* VMS */
+    static char prev_target[512]; 		/* Search string buffer */
+    static char prev_target_buffer[512];	/* Next search buffer */
+    static BOOL first = TRUE;
+    char *cp;
+    int ch = 0, recall;
+    int QueryTotal;
+    int QueryNum;
+    BOOLEAN FirstRecall = TRUE;
+    OptionType * tmp_ptr;
+    BOOLEAN ReDraw = FALSE;
+
+    /*
+     * Initialize the search string buffer. - FM
+     */
+    if (first) {
+	*prev_target_buffer = '\0';
+	first = FALSE;
+    }
+    *prev_target = '\0';
+    QueryTotal = (search_queries ? HTList_count(search_queries) : 0);
+    recall = ((QueryTotal >= 1) ? RECALL : NORECALL);
+    QueryNum = QueryTotal;
 
     /*
-     * Set display_lines based on the user_mode global.
+     *  Set display_lines based on the user_mode global.
      */
-    if(user_mode==NOVICE_MODE)
+    if (user_mode==NOVICE_MODE)
         display_lines = LYlines-4;
     else
         display_lines = LYlines-2;
 
     /*
-     * Counting the number of options to be displayed.
+     *  Counting the number of options to be displayed.
      *   num_options ranges 0...n
      */
-    for(; opt_ptr->next; num_options++, opt_ptr = opt_ptr->next)
+    for (; opt_ptr->next; num_options++, opt_ptr = opt_ptr->next)
          ; /* null body */
 
     /*
-     * Let's assume for the sake of sanity that ly is the number
+     *  Let's assume for the sake of sanity that ly is the number
      *   corresponding to the line the selection box is on.
-     * Let's also assume that cur_selection is the number of the
+     *  Let's also assume that cur_selection is the number of the
      *   item that should be initially selected, as 0 beign the
      *   first item.
-     * So what we have, is the top equal to the current screen line
+     *  So what we have, is the top equal to the current screen line
      *   subtracting the cur_selection + 1 (the one must be for the
      *   top line we will draw in a box).  If the top goes under 0,
      *   consider it 0.
      */
     top = ly - (cur_selection + 1);
-    if(top < 0)
+    if (top < 0)
 	top = 0;
 
     /*
-     * Check and see if we need to put the i_length parameter up to
-     *   the number of real options.
+     *  Check and see if we need to put the i_length parameter up to
+     *  the number of real options.
      */
-    if(!i_length) {
+    if (!i_length) {
         i_length = num_options;
     }
     else {
         /*
-	 * Otherwise, it is really one number too high.
+	 *  Otherwise, it is really one number too high.
 	 */
 	i_length--;
     }
 
     /*
-     * The bottom is the value of the top plus the number of options
-     *   to view plus 3 (one for the top line, one for the bottom line,
-     *   and one to offset the 0 counted in the num_options).
+     *  The bottom is the value of the top plus the number of options
+     *  to view plus 3 (one for the top line, one for the bottom line,
+     *  and one to offset the 0 counted in the num_options).
      */
     bottom = top + i_length + 3;
 
     /*
-     * Hmm...  If the bottom goes beyond the number of lines available
+     *  Hmm...  If the bottom goes beyond the number of lines available,
      */
-    if(bottom > display_lines) {
+    if (bottom > display_lines) {
         /*
-	 * Position the window at the top if we have more options
-	 *   than will fit in the window.
+	 *  Position the window at the top if we have more
+	 *  options than will fit in the window.
 	 */
-	if(i_length+3 > display_lines) {
+	if (i_length+3 > display_lines) {
 	    top = 0;
             bottom = top + i_length+3;
-	    if(bottom > display_lines)
+	    if (bottom > display_lines)
 	        bottom = display_lines + 1;
 	} else {
 	    /*
-	     * Try to position the window so that the selected option will
-	     *    appear where the selecton box currently is positioned.
-	     * It could end up too high, at this point, but we'll move it
+	     *  Try to position the window so that the selected option will
+	     *    appear where the selection box currently is positioned.
+	     *  It could end up too high, at this point, but we'll move it
 	     *    down latter, if that has happened.
 	     */
 	    top = (display_lines + 1) - (i_length + 3);
@@ -450,12 +478,12 @@ PRIVATE int popup_options ARGS6(int,cur_selection, OptionType *,list,
     }
 
     /*
-     * This is really fun, when the length is 4, it means 0-4, or 5.
+     *  This is really fun, when the length is 4, it means 0-4, or 5.
      */
     length = (bottom - top) - 2;
 
     /*
-     * Move the window down if it's too high.
+     *  Move the window down if it's too high.
      */
     if (bottom < ly + 2) {
         bottom = ly + 2;
@@ -465,8 +493,8 @@ PRIVATE int popup_options ARGS6(int,cur_selection, OptionType *,list,
     }
 
     /*
-     * Set up the overall window, including the boxing characters ('*'), if
-     * it all fits.  Otherwise, set up the widest window possible. - FM
+     *  Set up the overall window, including the boxing characters ('*'),
+     *  if it all fits.  Otherwise, set up the widest window possible. - FM
      */
 #ifndef USE_SLANG
     if (!(form_window = newwin(bottom - top, width+4, top, lx - 1)) &&
@@ -480,11 +508,11 @@ PRIVATE int popup_options ARGS6(int,cur_selection, OptionType *,list,
 #endif /* !USE_SLANG */
 
     /*
-     * Set up the window_offset for options.
+     *  Set up the window_offset for options.
      *   cur_selection ranges from 0...n
      *   length ranges from 0...m
      */
-    if(cur_selection >= length)	{
+    if (cur_selection >= length) {
         window_offset = cur_selection - length + 1;
     }
 
@@ -495,12 +523,13 @@ PRIVATE int popup_options ARGS6(int,cur_selection, OptionType *,list,
  *        09-05-94 FM
  */
 redraw:
+    opt_ptr = list;
 
-    opt_ptr=list;
-
-    /* display the boxed options */
-    for(i = 0; i <= num_options; i++, opt_ptr = opt_ptr->next) {
-        if(i >= window_offset && i - window_offset < length) {
+    /*
+     *  Display the boxed options.
+     */
+    for (i = 0; i <= num_options; i++, opt_ptr = opt_ptr->next) {
+        if (i >= window_offset && i - window_offset < length) {
 #ifndef USE_SLANG
 	    wmove(form_window,(i+1)-window_offset,2);
 	    wclrtoeol(form_window);
@@ -521,12 +550,16 @@ redraw:
 #else
     SLsmg_draw_box (top, lx - 1, bottom - top, width + 4);
 #endif /* !USE_SLANG */
-    opt_ptr=NULL;
+    opt_ptr = NULL;
 
-    /* loop on user input */
-    while(cmd != LYK_ACTIVATE) {
+    /*
+     *  Loop on user input.
+     */
+    while (cmd != LYK_ACTIVATE) {
 
-        /* unreverse cur selection */
+        /*
+	 *  Unreverse cur selection.
+	 */
 	if (opt_ptr != NULL) {
 #ifndef USE_SLANG
 	    wmove(form_window,(i+1)-window_offset,2);
@@ -537,9 +570,9 @@ redraw:
 #endif /* !USE_SLANG */
 	}
 
-        opt_ptr=list;
+        opt_ptr = list;
 
-        for(i=0; i<cur_selection; i++, opt_ptr = opt_ptr->next) 
+        for (i = 0; i < cur_selection; i++, opt_ptr = opt_ptr->next) 
 	    ; /* null body */
 
 #ifndef USE_SLANG
@@ -568,85 +601,67 @@ redraw:
         switch(cmd) {
             case LYK_PREV_LINK:
 	    case LYK_UP_LINK:
-	
-	    if( cur_selection)
-                cur_selection--;
 
-		/* scroll the window up if neccessary */
-		if (cur_selection-window_offset < 0) {
+		if (cur_selection > 0)
+		    cur_selection--;
+
+		/*
+		 *  Scroll the window up if neccessary.
+		 */
+		if ((cur_selection - window_offset) < 0) {
 		    window_offset--;
-#ifdef USE_SLANG
 		    goto redraw;
-#else
-		    wmove(form_window,1,2);
-		    winsertln(form_window);
-#ifdef VMS
-		    VMSbox(form_window, (bottom - top), (width + 4));
-#else
-    		    box(form_window, BOXVERT, BOXHORI);
-#endif /* VMS */
-#endif /* USE_SLANG */
 		}
                 break;
 
             case LYK_NEXT_LINK:
 	    case LYK_DOWN_LINK:
-		if(cur_selection < num_options)
+		if (cur_selection < num_options)
                     cur_selection++;
 
-		/* scroll the window down if neccessary */
-		if(cur_selection-window_offset >= length) {
-		    /* remove the bottom border befor scrolling */
+		/*
+		 *  Scroll the window down if neccessary
+		 */
+		if ((cur_selection - window_offset) >= length) {
 		    window_offset++;
-#ifdef USE_SLANG
 		    goto redraw;
-#else
-		    wmove(form_window,length+1,1);
-		    wclrtoeol(form_window);
-		    scroll(form_window);
-#ifdef VMS
-		    VMSbox(form_window, (bottom - top), (width + 4));
-#else
-    		    box(form_window, BOXVERT, BOXHORI);
-#endif /* VMS */
-#endif /* USE_SLANG */
 		}
                 break;
 
 	    case LYK_NEXT_PAGE:
 		/*
-		 * Okay, are we on the last page of the list?
-		 *   if not then,
+		 *  Okay, are we on the last page of the list?
+		 *  If not then,
 		 */
-		if(window_offset != num_options - length + 1) {
+		if (window_offset != (num_options - length + 1)) {
 		    /*
-		     * Modify the current selection to not be a
-		     *   coordinate in the list, but a coordinate
-		     *   on the item selected in the window.
+		     *  Modify the current selection to not be a
+		     *  coordinate in the list, but a coordinate
+		     *  on the item selected in the window.
 		     */
 		    cur_selection -= window_offset;
 
 		    /*
-		     * Page down the proper length for the list.
-		     * If simply to far, back up.
+		     *  Page down the proper length for the list.
+		     *  If simply to far, back up.
 		     */
 		    window_offset += length;
-		    if(window_offset > num_options - length) {
-		        window_offset = num_options - length + 1;
+		    if (window_offset > (num_options - length)) {
+		        window_offset = (num_options - length + 1);
 		    }
 
 		    /*
-		     * Readjust the current selection to be a list
-		     *   coordinate rather than window.
-		     * Redraw this thing.
+		     *  Readjust the current selection to be a
+		     *  list coordinate rather than window.
+		     *  Redraw this thing.
 		     */
 		    cur_selection += window_offset;
 		    goto redraw;
 		}
-		else if(cur_selection < num_options) {
+		else if (cur_selection < num_options) {
 		    /*
-		     * Already on last page of the list so just
-		     *   redraw it with the last item selected.
+		     *  Already on last page of the list so just
+		     *  redraw it with the last item selected.
 		     */
 		    cur_selection = num_options;
 		}
@@ -654,47 +669,372 @@ redraw:
 
 	    case LYK_PREV_PAGE:
 		/*
-		 * Are we on the first page of the list?
-		 *   if not then,
+		 *  Are we on the first page of the list?
+		 *  If not then,
 		 */
-		if(window_offset != 0) {
+		if (window_offset != 0) {
 		    /*
-		     * Modify the current selection to not be a list
-		     *   coordinate, but a window coordinate.
+		     *  Modify the current selection to not be a
+		     *  list coordinate, but a window coordinate.
 		     */
 		    cur_selection -= window_offset;
 
 		    /*
-		     * Page up the proper length.
-		     * If too far, back up.
+		     *  Page up the proper length.
+		     *  If too far, back up.
 		     */
 		    window_offset -= length;
-		    if(window_offset < 0) {
+		    if (window_offset < 0) {
 		        window_offset = 0;
 		    }
 
 		    /*
-		     * Readjust the current selection.
+		     *  Readjust the current selection.
 		     */
 		    cur_selection += window_offset;
 		    goto redraw;
-		}
-		else if(cur_selection > 0) {
+		} else if (cur_selection > 0) {
 		    /*
-		     * Already on the first page so just
-		     *   back up to the first item.
+		     *  Already on the first page so just
+		     *  back up to the first item.
 		     */
 		    cur_selection = 0;
 		}
 		break;
 
+	    case LYK_HOME:
+	        cur_selection = 0;
+		if (window_offset > 0) {
+		    window_offset = 0;
+		    goto redraw;
+		}
+		break;
+
+	    case LYK_END:
+	        cur_selection = num_options;
+		if (window_offset != (num_options - length + 1)) {
+		    window_offset = (num_options - length + 1);
+		    goto redraw;
+		}
+		break;
+
+            case LYK_DOWN_TWO:
+	        cur_selection += 2;
+		if (cur_selection > num_options)
+                    cur_selection = num_options;
+
+		/*
+		 *  Scroll the window down if neccessary.
+		 */
+		if ((cur_selection - window_offset) >= length) {
+		    window_offset += 2;
+		    if (window_offset > (num_options - length + 1))
+		        window_offset = (num_options - length + 1);
+		    goto redraw;
+		}
+                break;
+
+	    case LYK_UP_TWO:
+	        cur_selection -= 2;
+		if (cur_selection < 0)
+		    cur_selection = 0;
+
+		/*
+		 *  Scroll the window up if neccessary.
+		 */
+		if ((cur_selection - window_offset) < 0) {
+		    window_offset -= 2;
+		    if (window_offset < 0)
+		        window_offset = 0;
+		    goto redraw;
+		}
+                break;
+
+            case LYK_DOWN_HALF:
+	        cur_selection += (length/2);
+		if (cur_selection > num_options)
+                    cur_selection = num_options;
+
+		/*
+		 *  Scroll the window down if neccessary.
+		 */
+		if ((cur_selection - window_offset) >= length) {
+		    window_offset += (length/2);
+		    if (window_offset > (num_options - length + 1))
+		        window_offset = (num_options - length + 1);
+		    goto redraw;
+		}
+                break;
+
+	    case LYK_UP_HALF:
+	        cur_selection -= (length/2);
+		if (cur_selection < 0)
+		    cur_selection = 0;
+
+		/*
+		 *  Scroll the window up if neccessary.
+		 */
+		if ((cur_selection - window_offset) < 0) {
+		    window_offset -= (length/2);
+		    if (window_offset < 0)
+		        window_offset = 0;
+		    goto redraw;
+		}
+                break;
+
+	    case LYK_REFRESH:
+	        clearok(curscr, TRUE);
+	        refresh();
+		break;
+
+	    case LYK_NEXT:
+	        strcpy(prev_target, prev_target_buffer);
+	    case LYK_WHEREIS:
+	        if (*prev_target == '\0' ) {
+		    _statusline(ENTER_WHEREIS_QUERY);
+		    if ((ch = LYgetstr(prev_target, VISIBLE,
+	    		 	       sizeof(prev_target_buffer),
+				       recall)) < 0) {
+			/*
+			 *  User cancelled the search via ^G. - FM
+			 */
+			_statusline(CANCELLED);
+			sleep(InfoSecs);
+			goto restore_popup_statusline;
+		    }
+		}
+
+check_recall:
+		if (*prev_target == '\0' &&
+		    !(recall && (ch == UPARROW || ch == DNARROW))) {
+		    /*
+		     *  No entry.  Simply break.   - FM
+		     */
+	            _statusline(CANCELLED);
+		    sleep(InfoSecs);
+		    goto restore_popup_statusline;
+		}
+
+		if (recall && ch == UPARROW) {
+		    if (FirstRecall) {
+		        /*
+			 *  Use the current string or
+			 *  last query in the list. - FM
+			 */
+			FirstRecall = FALSE;
+			if (*prev_target_buffer) {
+			    for (QueryNum = (QueryTotal - 1);
+			         QueryNum > 0; QueryNum--) {
+				if ((cp=(char *)HTList_objectAt(search_queries,
+	    						QueryNum)) != NULL &&
+				    !strcmp(prev_target_buffer, cp)) {
+				    break;
+				}
+			    }
+			} else {
+			    QueryNum = 0;
+			}
+		    } else {
+			/*
+			 *  Go back to the previous query in the list. - FM
+			 */
+			QueryNum++;
+		    }
+		    if (QueryNum >= QueryTotal)
+			/*
+			 *  Roll around to the last query in the list. - FM
+			 */
+			QueryNum = 0;
+		    if ((cp=(char *)HTList_objectAt(search_queries,
+	    					    QueryNum)) != NULL) {
+			strcpy(prev_target, cp);
+			if (*prev_target_buffer &&
+			    !strcmp(prev_target_buffer, prev_target)) {
+			    _statusline(EDIT_CURRENT_QUERY);
+			} else if ((*prev_target_buffer && QueryTotal == 2) ||
+				   (!(*prev_target_buffer) &&
+				      QueryTotal == 1)) {
+			    _statusline(EDIT_THE_PREV_QUERY);
+			} else {
+			    _statusline(EDIT_A_PREV_QUERY);
+			}
+			if ((ch=LYgetstr(prev_target, VISIBLE,
+				sizeof(prev_target_buffer), recall)) < 0) {
+			    /*
+			     *  User cancelled the search via ^G. - FM
+			     */
+			    _statusline(CANCELLED);
+			    sleep(InfoSecs);
+			    goto restore_popup_statusline;
+			}
+			goto check_recall;
+		    }
+		} else if (recall && ch == DNARROW) {
+		    if (FirstRecall) {
+		    /*
+		     *  Use the current string or
+		     *  first query in the list. - FM
+		     */
+		    FirstRecall = FALSE;
+		    if (*prev_target_buffer) {
+			for (QueryNum = 0;
+			     QueryNum < (QueryTotal - 1); QueryNum++) {
+			    if ((cp=(char *)HTList_objectAt(search_queries,
+	    					    QueryNum)) != NULL &&
+				!strcmp(prev_target_buffer, cp)) {
+				    break;
+			    }
+			}
+		    } else {
+			QueryNum = QueryTotal - 1;
+		    }
+		} else {
+		    /*
+		     *  Advance to the next query in the list. - FM
+		     */
+		    QueryNum--;
+		}
+		if (QueryNum < 0)
+		    /*
+		     *  Roll around to the first query in the list. - FM
+		     */
+		    QueryNum = QueryTotal - 1;
+		    if ((cp=(char *)HTList_objectAt(search_queries,
+	    				QueryNum)) != NULL) {
+			strcpy(prev_target, cp);
+			if (*prev_target_buffer &&
+			    !strcmp(prev_target_buffer, prev_target)) {
+			    _statusline(EDIT_CURRENT_QUERY);
+			} else if ((*prev_target_buffer &&
+				    QueryTotal == 2) ||
+				   (!(*prev_target_buffer) &&
+				    QueryTotal == 1)) {
+			    _statusline(EDIT_THE_PREV_QUERY);
+			} else {
+			    _statusline(EDIT_A_PREV_QUERY);
+			}
+			if ((ch = LYgetstr(prev_target, VISIBLE,
+					   sizeof(prev_target_buffer),
+					   recall)) < 0) {
+			    /*
+			     * User cancelled the search via ^G. - FM
+			     */
+			    _statusline(CANCELLED);
+			    sleep(InfoSecs);
+			    goto restore_popup_statusline;
+	    		}
+			goto check_recall;
+		    }
+ 		}
+		/*
+		 *  Replace the search string buffer with the new target. - FM
+		 */
+		strcpy(prev_target_buffer, prev_target);
+		HTAddSearchQuery(prev_target_buffer);
+
+		/*
+		 *  Start search at the next option. - FM
+		 */
+		for (j = 1, tmp_ptr = opt_ptr->next;
+		     tmp_ptr != NULL; tmp_ptr = tmp_ptr->next, j++) {
+		    if (case_sensitive) {
+			if (strstr(tmp_ptr->name, prev_target_buffer) != NULL)
+			    break;
+		    } else {
+			if (LYstrstr(tmp_ptr->name, prev_target_buffer) != NULL)
+			    break;
+		    }
+		}
+		if (tmp_ptr != NULL) {
+		    /*
+		     *  We have a hit, so make that option the current. - FM
+		     */
+		    cur_selection += j;
+		    /*
+		     *  Scroll the window down if neccessary.
+		     */
+		    if ((cur_selection - window_offset) >= length) {
+		        window_offset += j;
+			if (window_offset > (num_options - length + 1))
+			    window_offset = (num_options - length + 1);
+			ReDraw = TRUE;
+		    }
+		    goto restore_popup_statusline;
+		}
+
+		/*
+		 *  If we started at the beginning, it can't be present. - FM
+		 */
+		if (cur_selection == 0) {
+		    _user_message(STRING_NOT_FOUND, prev_target_buffer);
+		    sleep(MessageSecs);
+		    goto restore_popup_statusline;
+		}
+
+		/*
+		 *  Search from the beginning to the current option. - FM
+		 */
+		for (j = 0, tmp_ptr = list;
+		     j < cur_selection; tmp_ptr = tmp_ptr->next, j++) {
+		    if (case_sensitive) {
+			if (strstr(tmp_ptr->name, prev_target_buffer) != NULL)
+			    break;
+		    } else {
+			if (LYstrstr(tmp_ptr->name, prev_target_buffer) != NULL)
+			    break;
+		    }
+		}
+		if (j < cur_selection) {
+		    /*
+		     *  We have a hit, so make that option the current. - FM
+		     */
+		    j = (cur_selection - j);
+		    cur_selection -= j;
+		    /*
+		     *  Scroll the window up if neccessary.
+		     */
+		    if ((cur_selection - window_offset) < 0) {
+		        window_offset -= j;
+			if (window_offset < 0)
+			    window_offset = 0;
+			ReDraw = TRUE;
+		    }
+		    goto restore_popup_statusline;
+		}
+
+		/*
+		 *  Didn't find it in the preceding options either. - FM
+		 */
+		_user_message(STRING_NOT_FOUND, prev_target_buffer);
+		sleep(MessageSecs);
+
+restore_popup_statusline:
+		/*
+		 *  Restore the popup statusline and
+		 *  reset the search variables. - FM
+		 */
+		if (disabled)
+		    _statusline(FORM_LINK_OPTION_LIST_UNM_MSG);
+   		else
+		    _statusline(FORM_LINK_OPTION_LIST_MESSAGE);
+		*prev_target = '\0';
+		QueryTotal = (search_queries ? HTList_count(search_queries)
+					     : 0);
+		recall = ((QueryTotal >= 1) ? RECALL : NORECALL);
+		QueryNum = QueryTotal;
+		if (ReDraw == TRUE) {
+		    ReDraw = FALSE;
+		    goto redraw;
+		}
+		break;
+
 	    case LYK_QUIT:
 	    case LYK_ABORT:
 	    case LYK_PREV_DOC:
 	    case 7:	/* Control-G */
 	    case 3:	/* Control-C */
 		cur_selection = orig_selection;
-		cmd=LYK_ACTIVATE; /* to exit */
+		cmd = LYK_ACTIVATE; /* to exit */
 		break;
         }
 
@@ -706,4 +1046,3 @@ redraw:
 
     return(cur_selection);
 }
-
diff --git a/src/LYGetFile.c b/src/LYGetFile.c
index 81d0d945..cd8ef577 100644
--- a/src/LYGetFile.c
+++ b/src/LYGetFile.c
@@ -53,28 +53,28 @@ extern BOOLEAN LYDidRename;
 PRIVATE char * LYSanctify ARGS1(char *, href) 
 {
     int i;
-    char *p,*cp,*tp;
+    char *p, *cp, *tp;
     char address_buffer[1024];
 
-    i = strlen(href) - 1;
+    i = (strlen(href) - 1);
     while (i && href[i] == '/') href[i--] = '\0';
 
-    if ((cp = (char *) strchr(href,'~')) != NULL) {
-       if (!strncmp(href,"file://localhost/",17))
-	 tp = href + 17;
+    if ((cp = (char *)strchr(href,'~')) != NULL) {
+       if (!strncmp(href, "file://localhost/", 17))
+	 tp = (href + 17);
        else 
-	 tp = href + 5;
-       if ((cp-tp) && *(cp-1) != '/')
+	 tp = (href + 5);
+       if ((cp - tp) && *(cp-1) != '/')
 	 return href;
-       LYstrncpy(address_buffer,href,cp-href);
-       if (address_buffer[strlen(address_buffer)-1] == '/')
-	 address_buffer[strlen(address_buffer)-1] = '\0';
+       LYstrncpy(address_buffer, href, (cp - href));
+       if (address_buffer[(strlen(address_buffer) - 1)] == '/')
+	 address_buffer[(strlen(address_buffer) - 1)] = '\0';
        p = (char *)Home_Dir();
-       strcat(address_buffer,p);
+       strcat(address_buffer, p);
        if (strlen(++cp))
-	 strcat(address_buffer,cp);
-       if (strcmp(href,address_buffer))
-	 StrAllocCopy(href,address_buffer);
+	 strcat(address_buffer, cp);
+       if (strcmp(href, address_buffer))
+	 StrAllocCopy(href, address_buffer);
     }
     return href;
 }
@@ -92,6 +92,7 @@ Try_Redirected_URL:
 	WWWDoc.address = doc->address;
         WWWDoc.post_data = doc->post_data;
         WWWDoc.post_content_type = doc->post_content_type;
+        WWWDoc.bookmark = doc->bookmark;
 	WWWDoc.isHEAD = doc->isHEAD;
 
 	/* reset WWW_Download_File just in case */
@@ -133,7 +134,8 @@ Try_Redirected_URL:
 		}
 		if (traversal) {
 		    /* only traverse http URLs */
-		    if (url_type != HTTP_URL_TYPE)
+		    if (url_type != HTTP_URL_TYPE &&
+		        url_type != LYNXIMGMAP_URL_TYPE)
 		        return(NULLFILE);
 		} else if (check_realm && !LYPermitURL && !LYJumpFileURL) {
 		    if (!(0==strncmp(startrealm, WWWDoc.address,
@@ -237,6 +239,7 @@ Try_Redirected_URL:
 		       WWWDoc.address = doc->address;
         	       WWWDoc.post_data = doc->post_data;
         	       WWWDoc.post_content_type = doc->post_content_type;
+		       WWWDoc.bookmark = doc->bookmark;
 		       WWWDoc.isHEAD = doc->isHEAD;
 
 		       if (!HTLoadAbsolute(&WWWDoc))
@@ -266,6 +269,7 @@ Try_Redirected_URL:
 		    WWWDoc.address = doc->address;
         	    WWWDoc.post_data = doc->post_data;
         	    WWWDoc.post_content_type = doc->post_content_type;
+		    WWWDoc.bookmark = doc->bookmark;
 		    WWWDoc.isHEAD = doc->isHEAD;
 
 		    if (!HTLoadAbsolute(&WWWDoc)) {
@@ -283,10 +287,8 @@ Try_Redirected_URL:
 				 doc->address+9, ALWAYS_EXEC_PATH)) {
             	        statusline(EXECUTION_DISABLED);
             		sleep(MessageSecs);
-		    } else if (no_bookmark_exec && bookmark_page &&
-		    	       (strstr(HTLoadedDocumentURL(), bookmark_page) ||
-			        !strcmp(HTLoadedDocumentTitle(),
-					MOSAIC_BOOKMARK_TITLE))) {
+		    } else if (no_bookmark_exec &&
+		    	       HTLoadedDocumentBookmark()) {
 			statusline(BOOKMARK_EXEC_DISABLED);
 			sleep(MessageSecs);
 		    } else if (local_exec || (local_exec_on_local_files &&
@@ -476,6 +478,7 @@ Try_Redirected_URL:
 			WWWDoc.address = doc->address;
 			WWWDoc.post_data = doc->post_data;
 			WWWDoc.post_content_type = doc->post_content_type;
+			WWWDoc.bookmark = doc->bookmark;
 			WWWDoc.isHEAD = doc->isHEAD;
 		        status = HTLoadAbsolute(&WWWDoc);
 		    }
@@ -639,51 +642,80 @@ Try_Redirected_URL:
 		     */
                     {
                         char *pound;
-                        /* check for #selector */
+                        /*
+			 *  Check for #selector.
+			 */
                         pound = (char *)strchr(doc->address, '#');
 
-			/* check to see if there is a temp
-			 * file waiting for us to download
+			/*
+			 *  Check to see if there is a temp
+			 *  file waiting for us to download.
 			 */
 			if (WWW_Download_File) {
-				HTMLSetCharacterHandling(current_char_set);
-				if (LYdownload_options(&doc->address,
-						       WWW_Download_File) < 0)
-				    return(NOT_FOUND);
-		    		WWWDoc.address = doc->address;
-		    		FREE(WWWDoc.post_data);
-		    		FREE(WWWDoc.post_content_type);
-				WWWDoc.isHEAD = FALSE;
-				HTOutputFormat = WWW_PRESENT;
-				if (!HTLoadAbsolute(&WWWDoc)) 
-                        	    return(NOT_FOUND);
-				else 
-				    return(NORMAL);
+			    HTParentAnchor *tmpanchor;
+			    char *fname = NULL;
+
+			    HTMLSetCharacterHandling(current_char_set);
+			    /*
+			     *  Check for a suggested filename from
+			     *  the Content-Dispostion header. - FM
+			     */
+			    if (((tmpanchor = HTAnchor_parent(
+						HTAnchor_findAddress(&WWWDoc)
+							     )) != NULL) &&
+				HTAnchor_SugFname(tmpanchor) != NULL) {
+				StrAllocCopy(fname,
+					     HTAnchor_SugFname(tmpanchor));
+			    } else {
+			        StrAllocCopy(fname, doc->address);
+			    }
+			    if (LYdownload_options(&fname,
+						   WWW_Download_File) < 0) {
+				FREE(fname);
+				return(NOT_FOUND);
+			    }
+			    StrAllocCopy(doc->address, fname);
+			    FREE(fname);
+		    	    WWWDoc.address = doc->address;
+		    	    FREE(WWWDoc.post_data);
+		    	    FREE(WWWDoc.post_content_type);
+			    WWWDoc.bookmark = doc->bookmark;
+			    WWWDoc.isHEAD = FALSE;
+			    HTOutputFormat = WWW_PRESENT;
+			    if (!HTLoadAbsolute(&WWWDoc)) 
+                        	return(NOT_FOUND);
+			    else 
+				return(NORMAL);
 
 			} else if (pound == NULL &&
-				  /*
-				   * HTAnchor hash-table searches are now
-				   * case-sensitive (hopefully, without
-				   * anchor deletion problems), so this
-				   * is too. - FM
-				   */
-                                  (strcmp(doc->address,
-						HTLoadedDocumentURL()) ||
-				  /*
-				   * Also check the post_data elements. - FM
-				   */
-				  strcmp(doc->post_data ? doc->post_data : "",
-				    	 HTLoadedDocumentPost_data()) ||
-				  /*
-				   * Also check the isHEAD element. - FM
-				   */
-				  doc->isHEAD != HTLoadedDocumentIsHEAD())) {
+				   /*
+				    * HTAnchor hash-table searches are now
+				    * case-sensitive (hopefully, without
+				    * anchor deletion problems), so this
+				    * is too. - FM
+				    */
+				   (strcmp(doc->address,
+				  	   HTLoadedDocumentURL()) ||
+				   /*
+				    * Also check the post_data elements. - FM
+				    */
+				   strcmp((doc->post_data ?
+				   	   doc->post_data : ""),
+				    	  HTLoadedDocumentPost_data()) ||
+				   /*
+				    * Also check the isHEAD element. - FM
+				    */
+				   doc->isHEAD != HTLoadedDocumentIsHEAD())) {
 			    HTMLSetCharacterHandling(current_char_set);
-			    /* nothing needed to be shown */
+			    /*
+			     *  Nothing needed to be shown.
+			     */
 			    return(NULLFILE);
 
                         } else {
-                        /* may set www_search_result */
+                        /*
+			 *  May set www_search_result.
+			 */
                             if (pound != NULL)
                                 HTFindPoundSelector(pound+1);
 			    HTMLSetCharacterHandling(current_char_set);
diff --git a/src/LYGlobalDefs.h b/src/LYGlobalDefs.h
index 88c84b82..5f2577c5 100644
--- a/src/LYGlobalDefs.h
+++ b/src/LYGlobalDefs.h
@@ -48,7 +48,7 @@ extern char *list_format;
 extern BOOLEAN lynx_edit_mode;
 extern BOOLEAN no_dired_support;
 extern BOOLEAN dir_list_style;
-extern taglink *tagged;
+extern HTList *tagged;
 #define FILES_FIRST 1
 #define MIXED_STYLE 2
 #ifdef OK_OVERRIDE
@@ -81,7 +81,8 @@ extern BOOLEAN LYUseNoviceLineTwo;  /* True if TOGGLE_HELP is not mapped */
 
 #define MAX_LINE 1024	/* Hope that no window is larger than this */
 extern char star_string[MAX_LINE + 1]; /* from GridText.c */
-#define STARS(n) (&star_string[(MAX_LINE-1) - (n)])
+#define STARS(n) \
+ ((n) >= MAX_LINE ? star_string : &star_string[(MAX_LINE-1)] - (n))
 #define DIRNAMESIZE 256
 
 extern BOOLEAN LYShowCursor;   /* show the cursor or hide it */
@@ -153,6 +154,8 @@ extern BOOLEAN no_suspend;
 extern BOOLEAN no_editor;
 extern BOOLEAN no_shell;
 extern BOOLEAN no_bookmark;
+extern BOOLEAN no_multibook;
+extern BOOLEAN no_bookmark_exec;
 extern BOOLEAN no_option_save;
 extern BOOLEAN no_print;
 extern BOOLEAN no_download;
@@ -160,7 +163,6 @@ extern BOOLEAN no_disk_save;
 extern BOOLEAN no_exec;
 extern BOOLEAN no_lynxcgi;
 extern BOOLEAN exec_frozen;
-extern BOOLEAN no_bookmark_exec;
 extern BOOLEAN no_goto;
 extern BOOLEAN no_goto_cso;
 extern BOOLEAN no_goto_file;
@@ -197,6 +199,7 @@ extern char *editor;          /* if non empty it enables edit mode with
 			       * the editor that is named */
 extern char *jumpfile;
 extern char *bookmark_page;
+extern char *BookmarkPage;
 extern char *personal_type_map;
 extern char *global_type_map;
 extern char *global_extension_map;
@@ -248,5 +251,10 @@ extern BOOLEAN LYisConfiguredForX;
 extern char *URLDomainPrefixes;
 extern char *URLDomainSuffixes;
 extern BOOLEAN startfile_ok;
+extern BOOLEAN LYSelectPopups;		/* Cast popups to radio buttons? */
+extern BOOLEAN LYUseDefSelPop;		/* Command line -popup toggle    */
+extern int LYMultiBookmarks;    	/* Multi bookmark support on?	 */
+extern BOOLEAN LYMBMBlocked;		/* Force MBM support off?	 */
+extern BOOLEAN LYMBMAdvanced;		/* MBM statusline for ADVANCED?	 */
 
 #endif /* LYGLOBALDEFS_H */
diff --git a/src/LYHistory.c b/src/LYHistory.c
index d947820d..8ad69cb3 100644
--- a/src/LYHistory.c
+++ b/src/LYHistory.c
@@ -66,14 +66,16 @@ PUBLIC void LYpush ARGS1(document *,doc)
     if (nhist<MAXHIST)  {
 	history[nhist].link = doc->link;
 	history[nhist].page = doc->line;
-	history[nhist].title = 0;
+	history[nhist].title = NULL;
 	StrAllocCopy(history[nhist].title, doc->title);
-	history[nhist].address = 0;
+	history[nhist].address = NULL;
 	StrAllocCopy(history[nhist].address, doc->address);
-	history[nhist].post_data = 0;
+	history[nhist].post_data = NULL;
 	StrAllocCopy(history[nhist].post_data, doc->post_data);
-	history[nhist].post_content_type = 0;
+	history[nhist].post_content_type = NULL;
 	StrAllocCopy(history[nhist].post_content_type, doc->post_content_type);
+	history[nhist].bookmark = NULL;
+	StrAllocCopy(history[nhist].bookmark, doc->bookmark);
 	history[nhist].isHEAD = doc->isHEAD;
 	nhist++;
 
@@ -101,6 +103,8 @@ PUBLIC void LYpop ARGS1(document *,doc)
 	doc->post_data = history[nhist].post_data;
 	FREE(doc->post_content_type);
 	doc->post_content_type = history[nhist].post_content_type;
+	FREE(doc->bookmark);
+	doc->bookmark = history[nhist].bookmark;
 	doc->isHEAD = history[nhist].isHEAD;
 
         if(TRACE)
@@ -124,6 +128,7 @@ PUBLIC void LYpop_num ARGS2(int,number, document *,doc)
 	StrAllocCopy(doc->address, history[number].address);
 	StrAllocCopy(doc->post_data, history[number].post_data);
 	StrAllocCopy(doc->post_content_type, history[number].post_content_type);
+	StrAllocCopy(doc->bookmark, history[number].bookmark);
 	doc->isHEAD = history[number].isHEAD;
     }
 }
@@ -236,6 +241,7 @@ PUBLIC void historytarget ARGS1(document *,newdoc)
 	    WWWDoc.address = newdoc->address;
             WWWDoc.post_data = newdoc->post_data;
             WWWDoc.post_content_type = newdoc->post_content_type;
+            WWWDoc.bookmark = newdoc->bookmark;
 	    WWWDoc.isHEAD = newdoc->isHEAD;
 	    if ((HText *)HTAnchor_document(
 				HTAnchor_parent(HTAnchor_findAddress(&WWWDoc))
diff --git a/src/LYKeymap.c b/src/LYKeymap.c
index a12a471b..5ba5f6a1 100644
--- a/src/LYKeymap.c
+++ b/src/LYKeymap.c
@@ -750,3 +750,19 @@ PUBLIC char *key_for_func ARGS1 (int,func)
 	}
 	return buf;
 }
+
+/*
+ *  This function returns TRUE if the ch is non-alphanumeric
+ *  and maps to keyname (LYK_foo in the keymap[] array). - FM
+ */ 
+PUBLIC BOOL LYisNonAlnumKeyname ARGS2(
+	int,	ch,
+	int,	keyname)
+{
+    if ((ch >= '0' && ch <= '9') ||
+        (ch >= 'A' && ch <= 'z') ||
+	ch < 0 || ch > 269)
+	return (FALSE);
+
+    return(keymap[ch+1] == keyname);
+}
diff --git a/src/LYKeymap.h b/src/LYKeymap.h
index af29e432..99c38fd2 100644
--- a/src/LYKeymap.h
+++ b/src/LYKeymap.h
@@ -12,6 +12,7 @@ extern void set_numbers_as_arrows NOPARAMS;
 extern void reset_numbers_as_arrows NOPARAMS;
 extern void print_keymap PARAMS((char **newfile));
 extern char *key_for_func PARAMS((int func));
+extern BOOLEAN LYisNonAlnumKeyname PARAMS((int ch, int keyname));
 
 extern char keymap[]; /* main keymap matrix */
 
diff --git a/src/LYLocal.c b/src/LYLocal.c
index 591b7ff4..95a49abc 100644
--- a/src/LYLocal.c
+++ b/src/LYLocal.c
@@ -5,8 +5,16 @@
 	Added OK_PERMIT compilation option.
 	Support replacement of compiled-in f)ull menu configuration via
 	  DIRED_MENU definitions in lynx.cfg, so that more than one menu
-	  can be driven by the same executable.
-*/
+	  can be driven by the same executable. */
+/* Modified Oct-96 Klaus Weide (kweide@tezcat.com):
+	Changed to use the library's HTList_* functions and macros for
+	  managing the list of tagged file URLs.
+	Keep track of proper level of URL escaping, so that unusual filenames
+	  which contain #% etc. are handled properly (some HTUnEscapeSome()'s
+	  left in to be conservative, and to document where superfluous
+	  unescaping took place before).
+	Dynamic memory instead of fixed length buffers in a few cases.
+	Other minor changes to make things work as intended. */
 
 #ifdef DIRED_SUPPORT
 
@@ -19,6 +27,8 @@
 #include "LYStrings.h"
 #include "LYStructs.h"
 #include "LYGetFile.h"
+#include "LYHistory.h"
+#include "LYUpload.h"
 #include "LYLocal.h"
 #include "LYSystem.h"
 
@@ -30,7 +40,6 @@
 
 #define FREE(x) if (x) {free(x); x = NULL;}
 
-PRIVATE void clear_tags NOPARAMS;
 PRIVATE int my_spawn PARAMS((char *path, char **argv, char *msg));
 PRIVATE char *filename PARAMS((char *prompt, char *buf, int bufsize));
 
@@ -39,7 +48,8 @@ PRIVATE BOOLEAN permit_location PARAMS((char * destpath, char * srcpath,
 					char ** newpath));
 #endif /* OK_PERMIT */
 
-PRIVATE char *render_item PARAMS((char *s, char *path, char *dir, char *buf));
+PRIVATE char *render_item PARAMS((char *s, char *path, char *dir, char *buf,
+				  int bufsize, BOOLEAN url_syntax));
 
 PRIVATE struct dired_menu *menu_head = NULL;
 struct dired_menu {
@@ -129,7 +139,7 @@ struct dired_menu {
 
 #ifdef OK_ZIP
 { DE_DIR,	      "", "Package and compress",
-           "(using zip)", "LYNXDIRED://ZIP%f",			NULL },
+           "(using zip)", "LYNXDIRED://ZIP%p",			NULL },
 #endif /* OK_ZIP */
 
 { DE_FILE,	      "", "Compress",
@@ -142,11 +152,11 @@ struct dired_menu {
 
 #ifdef OK_ZIP
 { DE_FILE,	      "", "Compress",
-           "(using zip)", "LYNXDIRED://ZIP%f",			NULL },
+           "(using zip)", "LYNXDIRED://ZIP%p",			NULL },
 #endif /* OK_ZIP */
 
 { DE_TAG,	      "", "Move all tagged items to another location.",
-		      "", "LYNXDIRED://MOVE_TAGGED",		NULL },
+		      "", "LYNXDIRED://MOVE_TAGGED%d",		NULL },
 
 { DE_TAG,	      "", "Remove all tagged files and directories.",
 		      "", "LYNXDIRED://REMOVE_TAGGED",		NULL },
@@ -162,13 +172,14 @@ PRIVATE BOOLEAN remove_tagged NOARGS
    int c, ans;
    char *cp,*tp;
    char tmpbuf[1024];
-   char testpath[512];
+   char *testpath = NULL;
    struct stat dir_info;
    int count,i;
-   taglink *tag;
+   HTList *tag;
    char *args[5];
 
-   if (tagged == NULL) return 0; /* should never happen */
+   if (HTList_isEmpty(tagged))  /* should never happen */
+       return 0;
 
    _statusline("Remove all tagged files and directories (y or n): ");
    c = LYgetch();
@@ -176,15 +187,14 @@ PRIVATE BOOLEAN remove_tagged NOARGS
 
    count = 0;
    tag = tagged;
-   while(ans == 'Y' && tag != NULL) {
-      cp = tag->name;
+   while(ans == 'Y' && (cp = (char *)HTList_nextObject(tag)) != NULL) {
       if(is_url(cp) == FILE_URL_TYPE) { /* unecessary check */
 	 tp = cp;
 	 if(!strncmp(tp,"file://localhost",16))
 	   tp += 16;
 	 else if(!strncmp(tp,"file:",5))
 	   tp += 5;
-	 strcpy(testpath,tp);
+	 StrAllocCopy(testpath,tp);
 	 HTUnEscape(testpath);
 	 if((i = strlen(testpath)) && testpath[i-1] == '/')
 	   testpath[i-1] = '\0';
@@ -202,19 +212,25 @@ PRIVATE BOOLEAN remove_tagged NOARGS
 	    args[2] = testpath;
 	    args[3] = (char *) 0;
 	    sprintf(tmpbuf, "remove %s", testpath);
-	    if (my_spawn(RM_PATH, args, tmpbuf) <= 0)
+	    if (my_spawn(RM_PATH, args, tmpbuf) <= 0) {
+		FREE(testpath);
 		return count;
+	    }
 	    ++count;
 	 }
       }
-      tag = tag->next;
    }
+   FREE(testpath);
    clear_tags();
    return count;
 }
 
 /* Move all tagged files and directories to a new location. */
 /* Input is current directory. */
+/* The tests in this function can, at best, prevent some user mistakes -
+   anybody who relies on them for security is seriously misguided.
+   If a user has enough permissions to move a file somewhere, the same 
+   uid with Lynx & dired can do the same thing. */
 
 PRIVATE BOOLEAN modify_tagged ARGS1(
 	char *,		testpath)
@@ -224,13 +240,15 @@ PRIVATE BOOLEAN modify_tagged ARGS1(
    ino_t inode;
    uid_t owner;
    char tmpbuf[1024];
-   char savepath[512];
+   char *savepath = NULL;
+   char *srcpath = NULL;
    struct stat dir_info;
    char *args[5];
-   int count;
-   taglink *tag;
+   int count = 0;
+   HTList *tag;
 
-   if (tagged == NULL) return 0; /* should never happen */
+   if (HTList_isEmpty(tagged))  /* should never happen */
+       return 0;
 
    _statusline("Enter new location for tagged items: ");
 
@@ -240,17 +258,46 @@ PRIVATE BOOLEAN modify_tagged ARGS1(
 
 /* determine the ownership of the current location */
 
-      cp = testpath;
+   
+      /*
+       *  This test used to always fail from the dired menu...
+       *  changed to something that hopefully makes more sense - KW
+       */ 
+      if (testpath && *testpath && 0!=strcmp(testpath,"/")) {
+	  /*
+	   *  testpath passed in and is not empty and not
+	   *  a single "/" (which would probably be bogus) - use it
+	   */
+	  cp = testpath;
+      } else {
+	  /*
+	   *  Prepare to get directory path from one of the tagged files.
+	   */
+	  cp = HTList_lastObject(tagged);
+	  testpath = NULL;	/* won't be needed any more in this function,
+				 set to NULL as a flag */
+	  if (!cp)   /* last resort, should never happen */
+	      cp = "/";
+      }
       if (!strncmp(cp,"file://localhost",16))
 	cp += 16;
       else if (!strncmp(cp,"file:",5))
 	cp += 5;
-      strcpy(savepath,cp);
+      if (testpath==NULL) {
+          /*
+	   *  Get the directory containing the file or subdir.
+	   */
+	  cp = strip_trailing_slash(cp);
+	  savepath = HTParse(".", cp, PARSE_PATH+PARSE_PUNCTUATION);
+      } else {
+	  StrAllocCopy(savepath, cp);
+      }
       HTUnEscape(savepath);
       if (stat(savepath,&dir_info) == -1) {
 	 sprintf(tmpbuf,"Unable to get status of %s ",savepath);
 	 _statusline(tmpbuf);
 	 sleep(AlertSecs);
+	 FREE(savepath);
 	 return 0;
       } 
 
@@ -264,20 +311,29 @@ PRIVATE BOOLEAN modify_tagged ARGS1(
 /* replace ~/ references to the home directory */
 
       if (!strncmp(tmpbuf,"~/",2)) {
-	 cp = (char *)Home_Dir();
-	 strcpy(testpath,cp);
-	 strcat(testpath,tmpbuf+1);
-	 strcpy(tmpbuf,testpath);
+	  char *cp1 = NULL;
+	  StrAllocCopy(cp1, (char *)Home_Dir());
+	  StrAllocCat(cp1, (tmpbuf+1));
+	  if (strlen(cp1) > (sizeof(tmpbuf)-1)) {
+	      sprintf(tmpbuf, "%s ", "Path too long");
+	      _statusline(tmpbuf);
+	      sleep(AlertSecs);
+	      FREE(savepath);
+	      FREE(cp1);
+	      return 0;
+	  }
+	  strcpy(tmpbuf, cp1);
+	  FREE(cp1);
       }
 
 /* if path is relative prefix it with current location */
 
       if (tmpbuf[0] != '/') {
 	 if (savepath[strlen(savepath)-1] != '/')
-	   strcat(savepath,"/");
-	 strcat(savepath,tmpbuf);
+	     StrAllocCat(savepath,"/");
+	 StrAllocCat(savepath,tmpbuf);
       } else {
-	 strcpy(savepath,tmpbuf);
+	 StrAllocCopy(savepath,tmpbuf);
       }
 
 /* stat the target location to determine type and ownership */
@@ -286,6 +342,7 @@ PRIVATE BOOLEAN modify_tagged ARGS1(
 	 sprintf(tmpbuf,"Unable to get status of %s ",savepath);
 	 _statusline(tmpbuf);
 	 sleep(AlertSecs);
+	 FREE(savepath);
 	 return 0;
       }
 
@@ -295,6 +352,7 @@ PRIVATE BOOLEAN modify_tagged ARGS1(
 	 _statusline(
 	   "Source and destination are the same location - request ignored!");
 	 sleep(AlertSecs);
+	 FREE(savepath);
 	 return 0;
       }
 
@@ -308,35 +366,38 @@ PRIVATE BOOLEAN modify_tagged ARGS1(
 
 /* move all tagged items to the target location */
 
-	    while (tag != NULL) {
-	       cp = tag->name;
+	    while((cp = (char *)HTList_nextObject(tag)) != NULL) {
 	       if(!strncmp(cp,"file://localhost",16))
 		 cp += 16;
 	       else if(!strncmp(cp,"file:",5))
 		 cp += 5;
-	       strcpy(testpath,cp);
-	       HTUnEscape(testpath);
+	       StrAllocCopy(srcpath, cp);
+	       HTUnEscape(srcpath);
 
-	       sprintf(tmpbuf,"move %s to %s",testpath,savepath);
+	       sprintf(tmpbuf, "move %s to %s", srcpath, savepath);
 	       args[0] = "mv";
-	       args[1] = testpath;
+	       args[1] = srcpath;
 	       args[2] = savepath;
 	       args[3] = (char *) 0;
 	       if (my_spawn(MV_PATH, args, tmpbuf) <= 0)
 		  break;
-	       tag = tag->next;
 	       ++count;
 	    }
+	    FREE(srcpath);
+	    FREE(savepath);
 	    clear_tags();
 	    return count;
 	 } else {
 	    _statusline("Destination has different owner! Request denied. ");
 	    sleep(AlertSecs);
+	    FREE(srcpath);
+	    FREE(savepath);
 	    return 0;
 	 }
       } else {
 	 _statusline("Destination is not a valid directory! Request denied. ");
 	 sleep(AlertSecs);
+	 FREE(savepath);
 	 return 0;
       }
    }
@@ -548,14 +609,14 @@ PUBLIC BOOLEAN local_modify ARGS2(
    char testpath[512]; /* a bit ridiculous */
    int count;
 
-   if (tagged != NULL) {
+   if (!HTList_isEmpty(tagged)) {
       cp = doc->address;
       if (!strncmp(cp,"file://localhost",16))
 	cp += 16;
       else if (!strncmp(cp,"file:",5))
 	cp += 5;
       strcpy(testpath,cp);
-      HTUnEscape(testpath);
+      HTUnEscapeSome(testpath,"/");
       count = modify_tagged(testpath);
 
       if (doc->link > (nlinks-count-1)) doc->link = nlinks-count-1;
@@ -842,7 +903,7 @@ PUBLIC BOOLEAN local_remove ARGS1(
    char testpath[512];
    int count,i;
 
-   if (tagged != NULL) {
+   if (!HTList_isEmpty(tagged)) {
 
       count = remove_tagged();
 
@@ -972,8 +1033,12 @@ PRIVATE BOOLEAN permit_location ARGS3(
 	fprintf(fp0, "<Html><Head>\n<Title>%s</Title>\n</Head>\n<Body>\n",
 		PERMIT_OPTIONS_TITLE);
 	fprintf(fp0,"<H1>Permissions for %s</H1>\n", user_filename);
-	fprintf(fp0, "<Form Action=\"LYNXDIRED://PERMIT_LOCATION%s\">\n",
-		srcpath);
+	{  /* prevent filenames which include '#' or '?' from messing it up */
+	    char * srcpath_url = HTEscape(srcpath, URL_PATH);
+	    fprintf(fp0, "<Form Action=\"LYNXDIRED://PERMIT_LOCATION%s\">\n",
+		    srcpath_url);
+	    FREE(srcpath_url);
+	}
 	
 	fprintf(fp0, "<Ol><Li>Specify permissions below:<Br><Br>\n");
 	fprintf(fp0, "Owner:<Br>\n");
@@ -1038,7 +1103,6 @@ PRIVATE BOOLEAN permit_location ARGS3(
 	char *args[5];
 	char amode[10];
 	
-	HTUnEscape(destpath);
 	cp = destpath;
 	while (*cp != '\0' && *cp != '?') { /* Find filename */
 	    cp++;
@@ -1048,6 +1112,8 @@ PRIVATE BOOLEAN permit_location ARGS3(
 	}
 	*cp++ = '\0';	/* Null terminate file name
 			   and start working on the masks */
+
+	HTUnEscape(destpath);	/* will now operate only on filename part */
 	
 	/* A couple of sanity tests */
 	destpath = strip_trailing_slash(destpath);
@@ -1121,6 +1187,7 @@ PRIVATE BOOLEAN permit_location ARGS3(
 }
 #endif /* OK_PERMIT */
 
+#ifdef NOTDEFINED
 PUBLIC BOOLEAN is_a_file ARGS1(
 	char *,		testname)
 { 
@@ -1143,6 +1210,7 @@ PUBLIC BOOLEAN is_a_file ARGS1(
      else
        return 0;
 }
+#endif /* NOTDEFINED */
 
 /* display or remove a tag from a given link */
 
@@ -1174,19 +1242,19 @@ PUBLIC void tagflag ARGS2(
 }
 
 PUBLIC void showtags ARGS1(
-	taglink *,	t)
+	HTList *,	t)
 {
     int i;
-    taglink *s;
-    
+    HTList *s;
+    char * name;
+
     for(i=0;i<nlinks;i++) {
       s = t;
-      while(s != NULL) {
-	 if(!strcmp(links[i].lname,s->name)) {
+      while((name = HTList_nextObject(s)) != NULL) {
+	 if(!strcmp(links[i].lname,name)) {
 	    tagflag(ON,i);
 	    break;
-	 } else
-	    s = s->next;
+	 }
       }
    }
 }
@@ -1201,18 +1269,31 @@ PUBLIC char * strip_trailing_slash ARGS1(
    return dirname;
 }
 
-/* Perform file management operations for LYNXDIRED URL's */
-
+/*
+**  Perform file management operations for LYNXDIRED URL's.
+**  Attempt to be consistent.  These are (pseudo) URLs - i.e. they should
+**  be in URL syntax: some bytes will be URL-escaped with '%'.  This is 
+**  necessary because these (pseudo) URLs will go through some of the same 
+**  kinds of interpretations and mutilations as real ones: HTParse, stripping
+**  off #fragments etc.  (Some access schemes currently have special rules 
+**  about not escaping parsing '#' "the URL way" built into HTParse, but that
+**  doesn't look like a clean way.)  
+*/
 PUBLIC int local_dired ARGS1(
 	document *,	doc)
 {
-   char *line;
-   char *cp,*tp;
+   char *line_url;    /* will point to doc's address, which is a URL */
+   char *line = NULL; /* same as line_url, but HTUnEscaped, will be alloced */
+   char *cp, *tp, *bp;
    char tmpbuf[256];
    char buffer[512];
 
-   line = doc->address;
-   HTUnEscape(line);
+   line_url = doc->address;
+   HTUnEscapeSome(line_url,"/");	/* don't mess too much with *doc */
+
+   StrAllocCopy(line, line_url);
+   HTUnEscape(line);	/* _file_ (not URL) syntax, for those functions
+			   that need it.  DOn't forget to FREE it. */
 
    /* This causes a SIGSEGV later when StrAllocCopy tries to free tp
     * let's make it point to NULL
@@ -1226,6 +1307,7 @@ PUBLIC int local_dired ARGS1(
    } else if (!strncmp(line,"LYNXDIRED://INSTALL_SRC",23)) {
       local_install(NULL, &line[23], &tp);
       StrAllocCopy(doc->address, tp);
+      FREE(line);
       return 0;
    } else if (!strncmp(line,"LYNXDIRED://INSTALL_DEST",24)) {
       local_install(&line[24], NULL, &tp);
@@ -1235,26 +1317,36 @@ PUBLIC int local_dired ARGS1(
    } else if (!strncmp(line,"LYNXDIRED://MODIFY_LOCATION",27)) {
       if (modify_location(&line[27])) ++LYforce_no_cache;
    } else if (!strncmp(line,"LYNXDIRED://MOVE_TAGGED",23)) {
-      if (modify_tagged(&line[23])) ++LYforce_no_cache;
+      if (modify_tagged(&line_url[23])) ++LYforce_no_cache;
 #ifdef OK_PERMIT
    } else if (!strncmp(line,"LYNXDIRED://PERMIT_SRC",22)) {
       permit_location(NULL, &line[22], &tp);
-      StrAllocCopy(doc->address, tp);
+      if (tp)			/* one of the checks may have failed */
+	  StrAllocCopy(doc->address, tp);
+      FREE(line);
       return 0;
    } else if (!strncmp(line,"LYNXDIRED://PERMIT_LOCATION",27)) {
-       permit_location(&line[27], NULL, &tp);
+       permit_location(&line_url[27], NULL, &tp);
 #endif /* OK_PERMIT */
    } else if (!strncmp(line,"LYNXDIRED://REMOVE_SINGLE",25)) {
       if (remove_single(&line[25])) ++LYforce_no_cache;
    } else if (!strncmp(line,"LYNXDIRED://REMOVE_TAGGED",25)) {
       if (remove_tagged()) ++LYforce_no_cache;
    } else if (!strncmp(line,"LYNXDIRED://UPLOAD",18)) {
-     if (LYUpload(line)) ++LYforce_no_cache;
+      /*
+       *  They're written by LYUpload_options() HTUnEscaped,
+       *  don't want to change that for now... so pass through
+       *  without more unescaping.  Directory names containing
+       *  '#' will probably fail..
+       */
+      if (LYUpload(line_url)) ++LYforce_no_cache;
    } else {
       if (line[strlen(line)-1] == '/')
 	line[strlen(line)-1] = '\0';
-      if ((cp = strrchr(line,'/')) == NULL)
+      if ((cp = strrchr(line,'/')) == NULL) {
+	FREE(line);
 	return 0;
+      }
 
 /* Construct the appropriate system command taking care to escape all
    path references to avoid spoofing the shell. */
@@ -1347,12 +1439,20 @@ PUBLIC int local_dired ARGS1(
 #ifdef OK_ZIP
       } else if (!strncmp(line,"LYNXDIRED://ZIP",15)) {
 	tp = quote_pathname(line+15);
-	sprintf(buffer,"%s -rq %s.zip %s", ZIP_PATH, tp, tp);
+	*cp++ = '\0';
+	bp = quote_pathname(cp);
+	cp = quote_pathname(line+15);
+	sprintf(buffer,"cd %s; %s -rq %s.zip %s", cp, ZIP_PATH, tp, bp);
+	FREE(cp);
+	FREE(bp);
 	FREE(tp);
 # ifndef ARCHIVE_ONLY
       } else if (!strncmp(line,"LYNXDIRED://UNZIP",17)) {
 	tp = quote_pathname(line+17);
-	sprintf(buffer,"%s -q %s", UNZIP_PATH, tp);
+	*cp = '\0';
+	cp = quote_pathname(line+17);
+	sprintf(buffer,"cd %s; %s -q %s", cp, UNZIP_PATH, tp);
+	FREE(cp);
 	FREE(tp);
 # endif /* !ARCHIVE_ONLY */
 #endif /* OK_ZIP */
@@ -1383,6 +1483,7 @@ PUBLIC int local_dired ARGS1(
       }
    }
 
+   FREE(line);
    LYpop(doc);
    return 0;
 }
@@ -1402,7 +1503,10 @@ PUBLIC int dired_options ARGS2(
     struct stat dir_info;
     FILE *fp0;
     char *cp,*tp = NULL;
-    char *escaped;
+    /* char *escaped; */
+    char * dir_url = NULL;
+    char * path_url = NULL;
+    BOOLEAN nothing_tagged;
     int count;
     struct dired_menu *mp;
     char buf[2048];
@@ -1430,6 +1534,9 @@ PUBLIC int dired_options ARGS2(
     else if(!strncmp(cp,"file:",5))
       cp += 5;
     strcpy(dir,cp);
+    StrAllocCopy(dir_url, cp);
+    if (dir_url[strlen(dir_url)-1] == '/')
+      dir_url[strlen(dir_url)-1] = '\0';
     HTUnEscape(dir);
     if (dir[strlen(dir)-1] == '/')
       dir[strlen(dir)-1] = '\0';
@@ -1441,6 +1548,9 @@ PUBLIC int dired_options ARGS2(
        else if(!strncmp(cp,"file:",5))
 	 cp += 5;
        strcpy(path,cp);
+       StrAllocCopy(path_url,cp);
+       if (path_url[strlen(path_url)-1] == '/')
+	   path_url[strlen(path_url)-1] = '\0';
        HTUnEscape(path);
        if (path[strlen(path)-1] == '/')
 	  path[strlen(path)-1] = '\0';
@@ -1449,17 +1559,22 @@ PUBLIC int dired_options ARGS2(
 	  sprintf(tmpbuf,"Unable to get status of %s ",path);
 	  _statusline(tmpbuf);
 	  sleep(AlertSecs);
+	  FREE(dir_url);
+	  FREE(path_url);
 	  return 0;
        } 
 
+#ifdef NOTDEFINED
        if ((cp = strrchr(path,'.')) != NULL && strlen(path) > strlen(cp)) {
 	  *cp = '\0';
 	  tp = strrchr(path,'.');
 	  *cp = '.';
        }
+#endif /* NOTDEFINED */
     } else path[0] = '\0';
 
-    escaped = (char *) HTEscape(path,(unsigned char) 4);
+  /*escaped = (char *) HTEscape(path,(unsigned char) 4); path_url instead- kw*/
+    nothing_tagged = (HTList_isEmpty(tagged));
 
     fprintf(fp0,"<head>\n<title>%s</title></head>\n<body>\n",DIRED_MENU_TITLE);
 
@@ -1468,7 +1583,7 @@ PUBLIC int dired_options ARGS2(
 
     fprintf(fp0,"Current directory is %s <br>\n",dir);
 
-    if (tagged == NULL)
+    if (nothing_tagged)
        if (strlen(path))
           fprintf(fp0,"Current selection is %s <p>\n",path);
        else
@@ -1487,9 +1602,9 @@ PUBLIC int dired_options ARGS2(
     }
 
     for (mp = menu_head; mp != NULL; mp = mp->next) {
-	if (mp->cond != DE_TAG && tagged != NULL)
+	if (mp->cond != DE_TAG && !nothing_tagged)
 	    continue;
-	if (mp->cond == DE_TAG && tagged == NULL)
+	if (mp->cond == DE_TAG && nothing_tagged)
 	    continue;
 	if (mp->cond == DE_DIR && (dir_info.st_mode & S_IFMT) != S_IFDIR)
 	    continue;
@@ -1497,9 +1612,12 @@ PUBLIC int dired_options ARGS2(
 	    continue;
 	if (strcmp(mp->sfx, &path[strlen(path)-strlen(mp->sfx)]) != 0)
 	    continue;
-	fprintf(fp0, "<a href=\"%s", render_item(mp->href, path, dir, buf));
-	fprintf(fp0, "\">%s</a> ", render_item(mp->link, path, dir, buf));
-	fprintf(fp0, "%s<br>\n", render_item(mp->rest, path, dir, buf));
+	fprintf(fp0, "<a href=\"%s",
+		render_item(mp->href, path_url, dir_url, buf,2048, YES));
+	fprintf(fp0, "\">%s</a> ",
+		render_item(mp->link, path, dir, buf,2048, NO));
+	fprintf(fp0, "%s<br>\n",
+		render_item(mp->rest, path, dir, buf,2048, NO));
     }
 
     if (uploaders != NULL) {
@@ -1513,7 +1631,9 @@ PUBLIC int dired_options ARGS2(
     fprintf(fp0,"</body>\n");
     fclose(fp0);
 
-    FREE(escaped);
+    /* FREE(escaped);  not used any more - kw*/
+    FREE(dir_url);
+    FREE(path_url);
 
     LYforce_no_cache = 1;
 
@@ -1534,18 +1654,18 @@ PRIVATE int my_spawn ARGS3(
     int wstatus;
 #endif /* NeXT || AIX4 || sony_news */
 
-    rc = 1;                 /* It will work */
+    rc = 1;		/* It will work */
+    tmpbuf[0] = '\0';	/* empty buffer for alert messages */
     stop_curses();
-    pid = fork(); /* fork and execute rm */
+    pid = fork();	/* fork and execute rm */
     switch (pid) {
       case -1:
 	sprintf(tmpbuf, "Unable to %s due to system error!", msg);
-	_statusline(tmpbuf);
-	sleep(AlertSecs);
 	rc = 0;
+	break;		/* don't fall thru! - KW */
       case 0:  /* child */
 	execv(path, argv);
-	exit(-1);    /* execv failed, give wait() something to look at */
+	exit(-1);	/* execv failed, give wait() something to look at */
       default:  /* parent */
 #if defined(NeXT) || defined(AIX4) || defined(sony_news)
 	while (wait(&wstatus) != pid)
@@ -1557,8 +1677,6 @@ PRIVATE int my_spawn ARGS3(
 	    WTERMSIG(wstatus) != 0)  { /* error return */
 	    sprintf(tmpbuf, "Probable failure to %s due to system error!",
 		    msg);
-	    _statusline(tmpbuf);
-	    sleep(AlertSecs);
 	    rc = 0;
 	}
     }
@@ -1568,7 +1686,19 @@ PRIVATE int my_spawn ARGS3(
 	HadVMSInterrupt = FALSE;
     }
 #endif /* VMS */
+
+    if (rc == 0) {
+        /*
+	 *  Screen may have message from the failed execv'd command.
+	 *  Give user time to look at it before screen refresh.
+	 */
+	sleep(AlertSecs);
+    }
     start_curses();
+    if (tmpbuf[0]) {
+	_statusline(tmpbuf);
+	sleep(AlertSecs);
+    }
 
     return(rc);
 }
@@ -1616,7 +1746,7 @@ PUBLIC BOOLEAN local_install ARGS3(
    static char savepath[512]; /* this will be the link that is to be installed */
    struct stat dir_info;
    char *args[5];
-   taglink *tag;
+   HTList *tag;
    int count = 0;
 
 /* Determine the status of the selected item. */
@@ -1671,40 +1801,39 @@ PUBLIC BOOLEAN local_install ARGS3(
    args[3] = (char *) 0;
    sprintf(tmpbuf, "install %s", destpath);
    tag = tagged;
-   for (;;) {
-      if (tagged) {
-	 args[1] = tag->name;
-	 if (strncmp("file://localhost", args[1], 16) == 0)
-	    args[1] = tag->name + 16;
-      } else
-         args[1] = savepath;
 
-      if (my_spawn(INSTALL_PATH, args, tmpbuf) <= 0)
-         return count;
-      count++;
-      if (!tagged)
-	 break;
-      tag = tag->next;
-      if (!tag)
-	break;
+   if (HTList_isEmpty(tagged)) {
+         args[1] = savepath;
+	 if (my_spawn(INSTALL_PATH, args, tmpbuf) <= 0)
+	     return count;
+	 count++;
+   } else {
+       char * name;
+       while ((name = (char *)HTList_nextObject(tag))) {
+	   args[1] = name;
+	   if (strncmp("file://localhost", args[1], 16) == 0)
+	       args[1] = name + 16;
+
+	   if (my_spawn(INSTALL_PATH, args, tmpbuf) <= 0)
+	       return count;
+	   count++;
+       }	
+       clear_tags();
    }
-   if (tagged)
-      clear_tags();
    statusline("Installation complete");
    sleep(InfoSecs);
    return count;
 }
 
-PRIVATE void clear_tags NOARGS
+PUBLIC void clear_tags NOARGS
 {
-    taglink *t1;
+    char *cp = NULL;
 
-    while((t1=tagged) != NULL) { 
-	tagged = tagged->next;
-	FREE(t1->name);
-	FREE(t1);
+    while ((cp = HTList_removeLastObject(tagged)) != NULL) { 
+	FREE(cp);
     }
-    tagged = NULL;
+    if (HTList_isEmpty(tagged))
+	FREE(tagged);
 }
 
 PUBLIC void add_menu_item ARGS1(
@@ -1758,34 +1887,50 @@ PUBLIC void add_menu_item ARGS1(
 	menu_head = new;
 }
 
-PRIVATE char * render_item ARGS4(
+PRIVATE char * render_item ARGS6(
 	char *,		s,
 	char *,		path,
 	char *,		dir,
-	char *,		buf)
+	char *,		buf,
+	int,		bufsize,
+	BOOLEAN,	url_syntax)
 {
 	char *cp;
 	char *bp;
-	taglink *t1;
-	char *taglist;
+	char overrun = '\0';
+	char *taglist = NULL;
+#define BP_INC (bp>buf+bufsize-2 ?  &overrun : bp++)
+				/* Buffer overrun could happen for very long
+				 tag list, if %l or %t are used */
 
 	bp = buf;
-	while (*s) {
+	while (*s && !overrun) {
 	    if (*s == '%') {
 		s++;
 		switch (*s) {
 		case '%':
-		    *bp++ = '%';
+		    *BP_INC = '%';
+#ifdef NOTDEFINED
+		    /*
+		     *  These chars come from lynx.cfg or the default, let's
+		     *  just assume there won't be any improper %'s there that
+		     *  would need escaping.
+		     */
+		    if(url_syntax) {
+			*BP_INC = '2';
+			*BP_INC = '5';
+		    }
+#endif /* NOTDEFINED */
 		    break;
 		case 'p':
 		    cp = path;
 		    while (*cp)
-			*bp++ = *cp++;
+			*BP_INC = *cp++;
 		    break;
 		case 'd':
 		    cp = dir;
 		    while (*cp)
-			*bp++ = *cp++;
+			*BP_INC = *cp++;
 		    break;
 		case 'f':
 		    cp = strrchr(path, '/');
@@ -1794,32 +1939,59 @@ PRIVATE char * render_item ARGS4(
 		    else
 			cp = path;
 		    while (*cp)
-			*bp++ = *cp++;
+			*BP_INC = *cp++;
 		    break;
 		case 'l':
 		case 't':
-		    FREE(taglist);
-		    for (t1=tagged; t1 != NULL; t1 = t1->next) { 
-			if (*s == 'l' && (cp = strrchr(t1->name, '/')))
-			    cp++;
-			else
-			    cp = t1->name;
-			StrAllocCat(taglist, cp);
-			StrAllocCat(taglist, " ");
+		    if (!HTList_isEmpty(tagged)) {
+			HTList *cur = tagged; 
+			char *name;
+
+			while(!overrun &&
+			      (name = (char *)HTList_nextObject(cur))!=NULL) {
+			    if (*s == 'l' && (cp = strrchr(name, '/')))
+				cp++;
+			    else
+				cp = name;
+			    StrAllocCat(taglist, cp);
+			    StrAllocCat(taglist, " "); /* should this be %20?*/
+			}
+		    }
+		    if (taglist) {
+			/* could HTUnescape here... */
+			cp = taglist;
+			while (*cp)
+			    *BP_INC = *cp++;
+			FREE(taglist);
 		    }
-		    cp = taglist;
-		    while (*cp)
-			*bp++ = *cp++;
 		    break;
 		default:
-		    *bp++ = '%';
-		    *bp++ =*s;
+		    *BP_INC = '%';
+#ifdef NOTDEFINED
+		    if (url_syntax) {
+			*BP_INC = '2';
+			*BP_INC = '5';
+		    }
+#endif /* NOTDEFINED */
+		    *BP_INC =*s;
 		    break;
 		}
-	    } else
-		*bp++ =*s;
+	    } else {
+	        /*
+		 *  Other chars come from the lynx.cfg or
+		 *  the default. Let's assume there isn't
+		 *  anything weird there that needs escaping.
+		 */
+		*BP_INC =*s;
+	    }
 	    s++;
 	}
+	if (overrun & url_syntax) {
+	    sprintf(buf,"Temporary URL or list would be too long.");
+	    _statusline(buf);
+	    sleep(AlertSecs);
+	    bp = buf;	/* set to start, will return empty string as URL */
+	}
 	*bp = '\0';
 	return buf;
 }
diff --git a/src/LYLocal.h b/src/LYLocal.h
index 6771898b..66dbff4d 100644
--- a/src/LYLocal.h
+++ b/src/LYLocal.h
@@ -40,6 +40,9 @@ extern BOOLEAN local_modify PARAMS((document *doc, char **newpath));
 extern BOOLEAN local_remove PARAMS((document *doc));
 extern BOOLEAN local_install PARAMS((char *destpath, char *srcpath, char **newpath));
 
+/* MainLoop needs to know about this one for atexit cleanup */
+extern void clear_tags NOPARAMS;
+
 /* Define the PRIVATE routines in case they ever go PUBLIC
 
 extern BOOLEAN modify_name PARAMS((char *testpath));
@@ -49,10 +52,10 @@ extern BOOLEAN create_directory PARAMS((char *testpath));
 extern BOOLEAN modify_tagged PARAMS((char *testpath));
 extern BOOLEAN remove_tagged NOPARAMS;
 extern BOOLEAN remove_single PARAMS ((char *testpath));
-*/
 extern BOOLEAN is_a_file PARAMS((char *testname));
+*/
 extern void tagflag PARAMS((int flag, int cur)); 
-extern void showtags PARAMS((taglink *tag));
+extern void showtags PARAMS((HTList *tag));
 extern char * strip_trailing_slash PARAMS((char * dirname));
 extern int local_dired PARAMS((document *doc));
 extern int dired_options PARAMS ((document *doc, char ** newfile));
diff --git a/src/LYMail.c b/src/LYMail.c
index fdb2efea..094e6da8 100644
--- a/src/LYMail.c
+++ b/src/LYMail.c
@@ -22,8 +22,11 @@ PRIVATE void remove_tildes PARAMS((char *string));
 /*
 **  mailform() sends form content to the mailto address(es). - FM
 */
-PUBLIC void mailform ARGS3(char *,mailto_address, char *,mailto_subject,
-			   char *,mailto_content)
+PUBLIC void mailform ARGS4(
+	char *,		mailto_address,
+	char *,		mailto_subject,
+	char *,		mailto_content,
+	char *,		mailto_type)
 {
     FILE *fd;
     char *address = NULL;
@@ -46,42 +49,31 @@ PUBLIC void mailform ARGS3(char *,mailto_address, char *,mailto_subject,
     StrAllocCopy(address, mailto_address);
 
     /*
-     *  Check for a ?subject=foo. - FM
+     *  Check for a ?searchpart with subject=foo. - FM
      */
     subject[0] = '\0';
-    if ((cp = strchr(address, '?')) != NULL &&
-	strcasecomp(cp+1, "subject=")) {
-	*cp = '\0';
-	cp += 9;
-	strncpy(subject, cp, 70);
-	subject[70] = '\0';
-	HTUnEscape(subject);
-    }
-
-    /*
-     *  Unescape any hex escaped pounds. - FM
-     */
-    while ((cp1 = strstr(address, "%23")) != NULL) {
-	*cp1 = '#';
-	cp0 = (cp1 + 1);
-	cp1 = (cp0 + 2);
-	for (i = 0; cp1[i]; i++) {
-	    cp0[i] = cp1[i];
+    if ((cp = strchr(address, '?')) != NULL) {
+	*cp++ = '\0';
+	while (*cp != '\0' && strncasecomp(cp, "subject=", 8))
+	    cp++;
+	cp0 = (cp - 1);
+	if ((*cp != '\0') &&
+	    (*cp0 == '\0' || *cp0 == '&' || *cp0 == ';')) {
+	    if ((cp1 = strchr(cp, '&')) != NULL) {
+	        *cp1 = '\0';
+	    } else if ((cp1 = strchr(cp, ';')) != NULL) {
+	        *cp1 = '\0';
+	    }
+	    strncpy(subject, cp, 70);
+	    subject[70] = '\0';
+	    HTUnEscape(subject);
 	}
-	cp0[i] = '\0';
     }
 
     /*
-     *  Unescape any hex escaped percents. - FM
+     *  Unescape the address field. - FM
      */
-    while ((cp1 = strstr(address, "%25")) != NULL) {
-	cp0 = (cp1 + 1);
-	cp1 = (cp0 + 2);
-	for (i = 0; cp1[i]; i++) {
-	    cp0[i] = cp1[i];
-	}
-	cp0[i] = '\0';
-    }
+    HTUnEscape(address);
 
     /*
      * Convert any Explorer semi-colon Internet address
@@ -134,6 +126,10 @@ PUBLIC void mailform ARGS3(char *,mailto_address, char *,mailto_subject,
 	return;
     }
 
+    if (mailto_type && *mailto_type) {
+	fprintf(fd, "Mime-Version: 1.0\n");
+	fprintf(fd, "Content-Type: %s\n", mailto_type);
+    }
     fprintf(fd,"To: %s\n", address);
     if (personal_mail_address && *personal_mail_address)
 	fprintf(fd,"From: %s\n", personal_mail_address);
@@ -141,13 +137,21 @@ PUBLIC void mailform ARGS3(char *,mailto_address, char *,mailto_subject,
     _statusline(SENDING_FORM_CONTENT);
 #endif /* UNIX */
 #ifdef VMS
-    sprintf(tmpfile,"%s%s",lynx_temp_space, "temp_mail.");
+    sprintf(tmpfile,"%s%s",lynx_temp_space, "temp_mail.txt");
     if ((fd = fopen(tmpfile,"w")) == NULL) {
 	HTAlert(FORM_MAILTO_FAILED);
 	FREE(address);
 	return;
     }
-
+    if (mailto_type &&
+        !strncasecomp(mailto_type, "multipart/form-data", 19)) {
+	/*
+	 *  Ugh!  There's no good way to include headers while
+	 *  we're still using "generic" VMS MAIL, so we'll put
+	 *  this in the body of the message. - FM
+	 */
+	fprintf(fd, "X-Content-Type: %s\n\n", mailto_type);
+    }
 #endif /* VMS */
 
     /*
@@ -242,35 +246,15 @@ PUBLIC void mailmsg ARGS4(int,cur, char *,owner_address,
     StrAllocCopy(address, owner_address);
 
     /*
-     *  Check for a ?subject=foo and trim it. - FM
+     *  Check for a ?searchpart and trim it. - FM
      */
-    if ((cp = strchr(address, '?')) != NULL && strcasecomp(cp+1, "subject="))
+    if ((cp = strchr(address, '?')) != NULL && strchr(cp+1, '=') != NULL)
 	*cp = '\0';
 
     /*
-     *  Unescape any hex escaped pounds. - FM
-     */
-    while((cp1 = strstr(address, "%23")) != NULL) {
-	*cp1 = '#';
-	cp0 = (cp1 + 1);
-	cp1 = (cp0 + 2);
-	for (i = 0; cp1[i]; i++) {
-	    cp0[i] = cp1[i];
-	}
-	cp0[i] = '\0';
-    }
-
-    /*
-     *  Unescape any hex escaped percents. - FM
+     *  Unescape the address field. - FM
      */
-    while ((cp1 = strstr(address, "%25")) != NULL) {
-	cp0 = (cp1 + 1);
-	cp1 = (cp0 + 2);
-	for (i = 0; cp1[i]; i++) {
-	    cp0[i] = cp1[i];
-	}
-	cp0[i] = '\0';
-    }
+    HTUnEscape(address);
 	
     /*
      *  Convert any Explorer semi-colon Internet address
@@ -304,7 +288,7 @@ PUBLIC void mailmsg ARGS4(int,cur, char *,owner_address,
     fprintf(fd,"X-Mailer: Lynx, Version %s\n\n",LYNX_VERSION);
 #endif /* UNIX */
 #ifdef VMS
-    sprintf(tmpfile,"%s%s",lynx_temp_space, "temp_mail.");
+    sprintf(tmpfile,"%s%s",lynx_temp_space, "temp_mail.txt");
     if ((fd = fopen(tmpfile,"w")) == NULL) {
 	FREE(address);
 	return;
@@ -417,22 +401,39 @@ PUBLIC void reply_by_mail ARGS3(
     }
 
     tempname(tmpfile,NEW_FILE);
+    if (((cp = strrchr(tmpfile, '.')) != NULL) &&
+#ifdef VMS
+	NULL == strchr(cp, ']') &&
+#endif /* VMS */
+	NULL == strchr(cp, '/')) {
+	*cp = '\0';
+	strcat(tmpfile, ".txt");
+    }
     if ((fd = fopen(tmpfile,"w")) == NULL) {
 	HTAlert(MAILTO_URL_TEMPOPEN_FAILED);
 	return;
     }
 
     /*
-     *  Check for a ?subject=foo. - FM
+     *  Check for a ?searchpart with subject=foo. - FM
      */
     subject[0] = '\0';
-    if ((cp = strchr(address, '?')) != NULL &&
-	strcasecomp(cp+1, "subject=")) {
-	*cp = '\0';
-	cp += 9;
-	strncpy(subject, cp, 70);
-	subject[70] = '\0';
-	HTUnEscape(subject);
+    if ((cp = strchr(address, '?')) != NULL) {
+	*cp++ = '\0';
+	while (*cp != '\0' && strncasecomp(cp, "subject=", 8))
+	    cp++;
+	cp0 = (cp - 1);
+	if ((*cp != '\0') &&
+	    (*cp0 == '\0' || *cp0 == '&' || *cp0 == ';')) {
+	    if ((cp1 = strchr(cp, '&')) != NULL) {
+	        *cp1 = '\0';
+	    } else if ((cp1 = strchr(cp, ';')) != NULL) {
+	        *cp1 = '\0';
+	    }
+	    strncpy(subject, cp, 70);
+	    subject[70] = '\0';
+	    HTUnEscape(subject);
+	}
     }
     if (subject[0] == '\0' && title && *title) {
 	strncpy(subject, title, 70);
@@ -440,31 +441,11 @@ PUBLIC void reply_by_mail ARGS3(
     }
 
     /*
-     *  Unescape any hex escaped pounds. - FM
+     *  Unescape the address field. - FM
      */
-    while ((cp1 = strstr(address, "%23")) != NULL) {
-	*cp1 = '#';
-	cp0 = (cp1 + 1);
-	cp1 = (cp0 + 2);
-	for (i = 0; cp1[i]; i++) {
-	    cp0[i] = cp1[i];
-	}
-	cp0[i] = '\0';
-    }
+    HTUnEscape(address);
 
     /*
-     *  Unescape any hex escaped percents. - FM
-     */
-    while ((cp1 = strstr(address, "%25")) != NULL) {
-	cp0 = (cp1 + 1);
-	cp1 = (cp0 + 2);
-	for (i = 0; cp1[i]; i++) {
-	    cp0[i] = cp1[i];
-	}
-	cp0[i] = '\0';
-    }
-	
-    /*
      *  Convert any Explorer semi-colon Internet address
      *  separators to commas. - FM
      */
diff --git a/src/LYMail.h b/src/LYMail.h
index 9dd72931..b14ab5f4 100644
--- a/src/LYMail.h
+++ b/src/LYMail.h
@@ -1,5 +1,4 @@
 
-
 #ifndef LYMAIL_H
 #define LYMAIL_H
 
@@ -7,13 +6,19 @@
 #include "LYStructs.h"
 #endif /* LYSTRUCTS_H */
 
-extern void mailform PARAMS((char *mailto_address,  char *mailto_subject,
-					char *mailto_content));
-extern void mailmsg PARAMS((int cur, char *owner_address, 
-                			char *filename, char *linkname));
-extern void reply_by_mail PARAMS((char *mail_address,
-					char *filename, char *title));
-
+extern void mailform PARAMS((
+	char *	mailto_address,
+	char *	mailto_subject,
+	char *	mailto_content,
+	char *	mailto_type));
+extern void mailmsg PARAMS((
+	int 	cur,
+	char *	owner_address,
+	char *	filename,
+	char *	linkname));
+extern void reply_by_mail PARAMS((
+	char *	mail_address,
+	char *	filename,
+	char *	title));
 
 #endif /* LYMAIL_H */
-
diff --git a/src/LYMain.c b/src/LYMain.c
index fe4703ca..963a528a 100644
--- a/src/LYMain.c
+++ b/src/LYMain.c
@@ -1,10 +1,15 @@
 #include "HTUtils.h"
 #include "tcp.h"
-#include "HTInit.h"
+#include "HTParse.h"
+#include "HTAccess.h"
+#include "HTList.h"
 #include "HTFile.h"
+#ifdef VMS
+#include "HTVMSUtils.h"
+#endif /* VMS */
+#include "HTInit.h"
 #include "LYCurses.h"
 #include "HTML.h"
-#include "HTAccess.h"
 #include "LYUtils.h"
 #include "LYGlobalDefs.h"
 #include "LYSignal.h"
@@ -16,12 +21,9 @@
 #include "LYReadCFG.h"
 #include "LYrcFile.h"
 #include "LYKeymap.h"
-#include "HTParse.h"
-#ifdef VMS
-#include "HTVMSUtils.h"
-#endif /* VMS */
 #include "LYList.h"
 #include "LYJump.h"
+#include "LYBookmark.h"
 
 #ifndef VMS
 #ifdef SYSLOG_REQUESTED_URLS
@@ -83,7 +85,7 @@ PUBLIC char *syslog_txt = NULL;		/* syslog arb text for session */
 PUBLIC BOOLEAN lynx_edit_mode = FALSE;
 PUBLIC BOOLEAN no_dired_support = FALSE;
 PUBLIC BOOLEAN dir_list_style = MIXED_STYLE;
-PUBLIC taglink *tagged = NULL;
+PUBLIC HTList *tagged = NULL;
 #ifdef OK_OVERRIDE
 PUBLIC BOOLEAN prev_lynx_edit_mode = FALSE;
 #endif /* OK_OVERRIDE */
@@ -177,6 +179,8 @@ PUBLIC BOOLEAN no_suspend = FALSE;
 PUBLIC BOOLEAN no_editor = FALSE;
 PUBLIC BOOLEAN no_shell = FALSE;
 PUBLIC BOOLEAN no_bookmark = FALSE;
+PUBLIC BOOLEAN no_multibook = FALSE;
+PUBLIC BOOLEAN no_bookmark_exec = FALSE;
 PUBLIC BOOLEAN no_option_save = FALSE;
 PUBLIC BOOLEAN no_print = FALSE;
 PUBLIC BOOLEAN no_download = FALSE;
@@ -184,7 +188,6 @@ PUBLIC BOOLEAN no_disk_save = FALSE;
 PUBLIC BOOLEAN no_exec = FALSE;
 PUBLIC BOOLEAN no_lynxcgi = FALSE;
 PUBLIC BOOLEAN exec_frozen = FALSE;
-PUBLIC BOOLEAN no_bookmark_exec = FALSE;
 PUBLIC BOOLEAN no_goto = FALSE;
 PUBLIC BOOLEAN no_goto_cso = FALSE;
 PUBLIC BOOLEAN no_goto_file = FALSE;
@@ -220,7 +223,8 @@ PUBLIC char *homepage = NULL;	/* home page or main screen */
 PUBLIC char *editor = NULL;	/* the name of the current editor */
 PUBLIC char *jumpfile = NULL;	/* the name of the default jumps file */
 PUBLIC char *jumpprompt = NULL;	/* the default jumps prompt */
-PUBLIC char *bookmark_page = NULL; /* the name of the current bookmark page */
+PUBLIC char *bookmark_page = NULL; /* the name of the default bookmark page */
+PUBLIC char *BookmarkPage = NULL;  /* the name of the current bookmark page */
 PUBLIC char *startfile = NULL;	/* the first file */
 PUBLIC char *helpfile = NULL; 	/* the main help file */
 PUBLIC char *helpfilepath = NULL;   /* the path to the help file set */
@@ -276,9 +280,9 @@ PUBLIC int more = FALSE;	/* is there more text to display? */
 PUBLIC int InfoSecs;	/* Seconds to sleep() for Information messages */
 PUBLIC int MessageSecs;	/* Seconds to sleep() for important Messages   */
 PUBLIC int AlertSecs;	/* Seconds to sleep() for HTAlert() messages   */
-PUBLIC BOOLEAN bookmark_start=FALSE;
-PUBLIC char *LYUserAgent=NULL;		/* Lynx User-Agent header          */
-PUBLIC char *LYUserAgentDefault=NULL;	/* Lynx default User-Agent header  */
+PUBLIC BOOLEAN bookmark_start = FALSE;
+PUBLIC char *LYUserAgent = NULL;	/* Lynx User-Agent header          */
+PUBLIC char *LYUserAgentDefault = NULL;	/* Lynx default User-Agent header  */
 PUBLIC BOOLEAN LYNoRefererHeader=FALSE;	/* Never send Referer header?      */
 PUBLIC BOOLEAN LYNoRefererForThis=FALSE;/* No Referer header for this URL? */
 PUBLIC BOOLEAN LYNoFromHeader=FALSE;	/* Never send From header?         */
@@ -288,6 +292,11 @@ PUBLIC BOOLEAN LYisConfiguredForX = FALSE;
 PUBLIC char *URLDomainPrefixes = NULL;
 PUBLIC char *URLDomainSuffixes = NULL;
 PUBLIC BOOLEAN startfile_ok = FALSE;
+PUBLIC BOOLEAN LYSelectPopups = USE_SELECT_POPUPS;
+PUBLIC BOOLEAN LYUseDefSelPop = TRUE;	/* Command line -popup toggle */
+PUBLIC int LYMultiBookmarks = MULTI_BOOKMARK_SUPPORT;
+PUBLIC BOOLEAN LYMBMBlocked = BLOCK_MULTI_BOOKMARKS;
+PUBLIC BOOLEAN LYMBMAdvanced = ADVANCED_MULTI_BOOKMARKS;
 
 /* These are declared in cutil.h for current freeWAIS libraries. - FM */
 #ifdef DECLARE_WAIS_LOGFILES
@@ -367,6 +376,11 @@ PRIVATE void free_lynx_globals NOARGS
     FREE(helpfilepath);
     FREE(aboutfilepath);
     FREE(bookmark_page);
+    FREE(BookmarkPage);
+    for (i = 0; i <= MBM_V_MAXFILES; i++) {
+        FREE(MBM_A_subbookmark[i]);
+        FREE(MBM_A_subdescript[i]);
+    }
     FREE(editor);
     FREE(authentication_info[0]);
     FREE(authentication_info[1]);
@@ -431,14 +445,23 @@ PUBLIC int main ARGS2(int,argc, char **,argv)
      *  Initialize our startup and global variables.
      */
 #ifdef ULTRIX
-    /* Need this for ultrix. */
+    /*
+     *  Need this for ultrix.
+     */
     terminal = getenv("TERM");
     if ((terminal == NULL) || !strncasecomp(terminal, "xterm", 5))
         terminal = "vt100";
 #endif /* ULTRIX */
-    /* Zero the links and history struct array. */
+    /*
+     *  Zero the links and history struct arrays.
+     */
     memset((void *)links, 0, sizeof(linkstruct)*MAXLINKS);
     memset((void *)history, 0, sizeof(histstruct)*MAXHIST);
+    /*
+     *  Zero the MultiBookmark arrays.
+     */
+    memset((void *)MBM_A_subbookmark, 0, sizeof(char)*(MBM_V_MAXFILES+1));
+    memset((void *)MBM_A_subdescript, 0, sizeof(char)*(MBM_V_MAXFILES+1));
 #ifndef VMS
 #ifdef SYSLOG_REQUESTED_URLS
     openlog("lynx", LOG_PID, LOG_LOCAL5);
@@ -840,10 +863,36 @@ PUBLIC int main ARGS2(int,argc, char **,argv)
     if (keypad_mode == NUMBERS_AS_ARROWS)
         set_numbers_as_arrows();
 
-    /* disable news posting if no posting command */
+    /*
+     *  Check the -popup command line toggle. - FM
+     */
+    if (LYUseDefSelPop == FALSE) {
+        if (LYSelectPopups == TRUE)
+	    LYSelectPopups = FALSE;
+	else
+	    LYSelectPopups = TRUE;
+    }
+
+    /*
+     *  Disable news posting if no posting command.
+     */
     if (*inews_path == '\0' || !strcasecomp(inews_path,"none"))
         no_newspost = TRUE;
 
+    /*
+     *  Disable multiple bookmark support if not interactive,
+     *  so it doesn't crash on curses functions, or if the
+     *  support was blocked via userdefs.h and/or lynx.cfg,
+     *  or via command line restrictions. - FM
+     */
+    if (no_multibook)
+        LYMBMBlocked = TRUE;
+    if (dump_output_immediately || LYMBMBlocked || no_multibook) {
+        LYMultiBookmarks = FALSE;
+	LYMBMBlocked = TRUE;
+	no_multibook == TRUE;
+    }
+
 #ifdef VMS
     set_vms_keys();
 #endif /* VMS */
@@ -1015,6 +1064,9 @@ PUBLIC int main ARGS2(int,argc, char **,argv)
     if (!homepage)
 	StrAllocCopy(homepage, startfile);
 
+    /*
+     *  Set up the inside/outside domain restriction flags. - FM
+     */
     if (inlocaldomain()) {
 #if defined(NO_UTMP) || defined(VMS) /* not selective */
         telnet_ok = !no_inside_telnet && !no_outside_telnet && telnet_ok;
@@ -1039,12 +1091,15 @@ PUBLIC int main ARGS2(int,argc, char **,argv)
     }
 
 #ifdef SIGTSTP
+    /*
+     *  Block Control-Z suspending if requested. - FM
+     */
     if (no_suspend)
 	(void) signal(SIGTSTP,SIG_IGN);
 #endif /* SIGTSTP */
 
     /*
-     *  Here's where we do all the work.
+     *  Check for a valid HEAD request. - FM
      */
     if (HEAD_request && strncmp(startfile, "http", 4)) {
         fprintf(stderr,
@@ -1061,6 +1116,10 @@ PUBLIC int main ARGS2(int,argc, char **,argv)
 #endif /* SIGTSTP */
 	exit(-1);
     }
+
+    /*
+     *  Check for a valid MIME headers request. - FM
+     */
     if (keep_mime_headers && strncmp(startfile, "http", 4)) {
         fprintf(stderr,
  "The '-mime_header' switch is for http URLs and cannot be used for\n'%s'.\n",
@@ -1076,6 +1135,10 @@ PUBLIC int main ARGS2(int,argc, char **,argv)
 #endif /* SIGTSTP */
 	exit(-1);
     }
+
+    /*
+     *  Check for a valid traversal request. - FM
+     */
     if (traversal && strncmp(startfile, "http", 4)) {
         fprintf(stderr,
  "The '-traversal' switch is for http URLs and cannot be used for\n'%s'.\n",
@@ -1091,6 +1154,10 @@ PUBLIC int main ARGS2(int,argc, char **,argv)
 #endif /* SIGTSTP */
 	exit(-1);
     }
+
+    /*
+     *  Set up our help and about file base paths. - FM
+     */
     StrAllocCopy(helpfilepath, helpfile);
     if ((cp=strrchr(helpfilepath, '/')) != NULL)
         *cp = '\0';
@@ -1100,9 +1167,32 @@ PUBLIC int main ARGS2(int,argc, char **,argv)
 	StrAllocCat(aboutfilepath, "/about_lynx/");
     }
     StrAllocCat(helpfilepath, "/");
-    if (!bookmark_page || *bookmark_page == '\0')
+
+
+    /*
+     *  Make sure our bookmark default strings
+     *  are all allocated and synchronized. - FM
+     */
+    if (!bookmark_page || *bookmark_page == '\0') {
         StrAllocCopy(bookmark_page, "lynx_bookmarks.html");
+        StrAllocCopy(BookmarkPage, bookmark_page);
+        StrAllocCopy(MBM_A_subbookmark[0], bookmark_page);
+        StrAllocCopy(MBM_A_subdescript[0], "Default");
+    }
+    if (!BookmarkPage || *BookmarkPage == '\0') {
+        StrAllocCopy(BookmarkPage, bookmark_page);
+        StrAllocCopy(MBM_A_subbookmark[0], bookmark_page);
+        StrAllocCopy(MBM_A_subdescript[0], MULTIBOOKMARKS_DEFAULT);
+    }
+
+    /*
+     *  Here's where we do all the work.
+     */
     if (dump_output_immediately) {
+        /*
+	 *  Finish setting up and start a
+	 *  NON-INTERACTIVE session. - FM
+	 */
         if (crawl && !number_links) {
 	    keypad_mode = NUMBERS_AS_ARROWS;
 	} else if (!nolist) {
@@ -1124,6 +1214,10 @@ PUBLIC int main ARGS2(int,argc, char **,argv)
 	  (void) signal(SIGTSTP,SIG_DFL);
 #endif /* SIGTSTP */
     } else {
+        /*
+	 *  Finish setting up and start an
+	 *  INTERACTIVE session. - FM
+	 */
  	if (setup(terminal)) {
 	    if (display != NULL && *display != '\0') {
 	        LYisConfiguredForX = TRUE;
@@ -1253,6 +1347,7 @@ PRIVATE void parse_arg ARGS3(char **, argv, int *, i, int, argc)
 	        printf("\
    jump            disable the 'j' (jump) command\n\
    mail            disallow mail\n\
+   multibook       disallow multiple bookmark files\n\
    news_post       disallow USENET News posting setting in the O)ptions menu\n\
    option_save     disallow saving options in .lynxrc\n");
 #if defined(NO_UTMP) || defined(VMS) /* not selective */
@@ -1345,8 +1440,8 @@ PRIVATE void parse_arg ARGS3(char **, argv, int *, i, int, argc)
 	emacs_keys = TRUE;
 	
     } else if (strncmp(argv[0], "-version", 8) == 0) {
-	printf("\n%s Version %s\n(c)GNU General Public License\n\
-<URL:http://www.nyu.edu/pages/wsn/subir/lynx.html>\n\n",
+	printf("\n%s Version %s\n(c)1996 GNU General Public License\n\
+<URL:http://lynx.browser.org/>\n\n",
 		LYNX_NAME, LYNX_VERSION);
 	exit(0);
 	
@@ -1369,6 +1464,9 @@ PRIVATE void parse_arg ARGS3(char **, argv, int *, i, int, argc)
     } else if (strncmp(argv[0], "-noreferer", 10) == 0) {
 	LYNoRefererHeader = TRUE;
 	
+    } else if (strncmp(argv[0], "-popup", 6) == 0) {
+	LYUseDefSelPop = FALSE;
+
     } else if (strncmp(argv[0], "-crawl", 6) == 0) {
 	crawl = TRUE;
 	LYcols=80;
@@ -1583,6 +1681,7 @@ PRIVATE void parse_arg ARGS3(char **, argv, int *, i, int, argc)
         /* Include mime headers and force source dump */
 	keep_mime_headers = TRUE;
 	dump_output_immediately = TRUE;
+	HTOutputFormat = HTAtom_for("www/dump");
 	LYcols=999;
 
     } else if (strncmp(argv[0], "-error_file", 11) == 0) { /* Output return
@@ -1726,6 +1825,8 @@ PRIVATE void parse_arg ARGS3(char **, argv, int *, i, int, argc)
 #endif /* SOCKS */
 	printf("    -nostatus        disable the miscellaneous information messages\n");
 	printf("    -number_links    force numbering of links\n");
+	printf("    -popup           toggles handling of single-choice SELECT options via\n");
+        printf("                     popup windows or as lists of radio buttons\n");
  	printf("    -post_data       user data for post forms, read from stdin,\n");
         printf("                     terminated by '---' on a line\n");
 	printf("    -print           enable print functions (DEFAULT)\n");
diff --git a/src/LYMainLoop.c b/src/LYMainLoop.c
index bbef7371..83567743 100644
--- a/src/LYMainLoop.c
+++ b/src/LYMainLoop.c
@@ -2,8 +2,12 @@
 #include "tcp.h"
 #include "HTAccess.h"
 #include "HTParse.h"
+#include "HTList.h"
+#include "HTFTP.h"
+#include "HTTP.h"
 #include "LYCurses.h"
 #include "LYGlobalDefs.h"
+#include "HTAlert.h"
 #include "LYUtils.h"
 #include "GridText.h"
 #include "LYStrings.h"
@@ -28,9 +32,6 @@
 #include "LYTraversal.h"
 #include "LYCharSets.h"
 #include "LYCharUtils.h"
-#include "HTFTP.h"
-#include "HTTP.h"
-#include "HTAlert.h"
 
 #ifdef VMS
 #include "HTVMSUtils.h"
@@ -80,13 +81,18 @@ PRIVATE void free_mainloop_variables NOARGS
     FREE(newdoc.address);
     FREE(newdoc.post_data);
     FREE(newdoc.post_content_type);
+    FREE(newdoc.bookmark);
     FREE(curdoc.title);
     FREE(curdoc.address);
     FREE(curdoc.post_data);
     FREE(curdoc.post_content_type);
+    FREE(curdoc.bookmark);
     FREE(traversal_host);
     FREE(traversal_link_to_add);
     FREE(CurrentUserAgent);
+#ifdef DIRED_SUPPORT
+    clear_tags();
+#endif /* DIRED_SUPPORT */
 
     return;
 }
@@ -107,7 +113,8 @@ int mainloop NOARGS
     int newline=0;
     char prev_target[512];
     char user_input_buffer[1024];
-    char *owner_address=NULL;  /* holds the responsible owner's address */
+    char *owner_address=NULL;  /* holds the responsible owner's address     */
+    char *ownerS_address=NULL; /* holds owner's address during source fetch */
     BOOLEAN first_file=TRUE;
     BOOLEAN refresh_screen=FALSE;
     BOOLEAN force_load = FALSE;
@@ -121,6 +128,7 @@ int mainloop NOARGS
     int CurrentCharSet_flag = current_char_set;
     BOOLEAN show_dotfiles_flag = show_dotfiles;
     BOOLEAN LYRawMode_flag = LYRawMode;
+    BOOLEAN LYSelectPopups_flag = LYSelectPopups;
     char cfile[128];
     FILE *cfp;
     char *cp, *toolbar;
@@ -137,7 +145,6 @@ int mainloop NOARGS
     char *tp;
     char tmpbuf[1024];
     struct stat dir_info;
-    taglink *t1, *t2=NULL;
 #endif /* DIRED_SUPPORT */
 
 /*
@@ -153,12 +160,14 @@ int mainloop NOARGS
     newdoc.title = NULL;
     newdoc.post_data = NULL;
     newdoc.post_content_type = NULL;
+    newdoc.bookmark = NULL;
     curdoc.address = NULL;
     curdoc.title = NULL;
     curdoc.post_data = NULL;
     curdoc.post_content_type = NULL;
+    curdoc.bookmark = NULL;
     atexit(free_mainloop_variables);
-    if (strcmp(startfile, homepage))
+    if (startfile && homepage && strcmp(startfile, homepage))
         HTAddGotoURL(homepage);
     HTAddGotoURL(startfile);
 initialize:
@@ -206,12 +215,14 @@ initialize:
 	    goto initialize;
 	} else {
 	    /* 
-	     * See if a bookmark page exists.  If it does,
-	     * replace newdoc.address with it's name 
+	     *  See if a bookmark page exists.  If it does,
+	     *  replace newdoc.address with it's name 
 	     */
-	    if (get_bookmark_filename(&newdoc.address) != NULL) {
+	    if ((cp = get_bookmark_filename(&newdoc.address)) != NULL &&
+	         *cp != '\0' && strcmp(cp, " ")) {
 		LYforce_HTML_mode = TRUE;  /* force HTML */
-		StrAllocCopy(newdoc.title, "Bookmark File");
+		StrAllocCopy(newdoc.title, BOOKMARK_TITLE);
+		StrAllocCopy(newdoc.bookmark, BookmarkPage);
 		StrAllocCopy(startrealm, newdoc.address);
 		FREE(newdoc.post_data);
 		FREE(newdoc.post_content_type);
@@ -276,14 +287,18 @@ try_again:
 		    /*
 		     *  Make SURE this is an appropriate request. - FM
 		     */
-		    if (!strncmp(newdoc.address, "http", 4))
+		    if (newdoc.address &&
+		        !strncmp(newdoc.address, "http", 4))
 		        newdoc.isHEAD = TRUE;
 		    HEAD_request = FALSE;
 		}
 
 		LYRequestTitle = newdoc.title;
+		if (newdoc.bookmark)
+		    LYforce_HTML_mode = TRUE;
 		if (LYValidate &&
 		    startfile_ok &&
+		    newdoc.address && startfile && homepage &&
 		    (!strcmp(newdoc.address, startfile) ||
 		     !strcmp(newdoc.address, homepage))) {
 		    LYPermitURL = TRUE;
@@ -366,6 +381,7 @@ try_again:
 		     */
 		    LYoverride_no_cache = FALSE; /* Was TRUE if popped. - FM */
 		    FREE(newdoc.address); /* to pop last doc */
+		    FREE(newdoc.bookmark);
 		    LYJumpFileURL = FALSE;
 		    reloading = FALSE;
 		    LYPermitURL = FALSE;
@@ -394,10 +410,11 @@ try_again:
 			*/
 		       if (first_file && homepage &&
 #ifdef VMS
-			   strcasecomp(homepage, startfile) != 0) {
+			   strcasecomp(homepage, startfile) != 0)
 #else
-			   strcmp(homepage, startfile) != 0) {
+			   strcmp(homepage, startfile) != 0)
 #endif /* VMS */
+			{
 			   /* 
 			    * Couldn't return to the first file but there is a
 			    * homepage we can use instead. Useful for when the
@@ -411,6 +428,7 @@ try_again:
 			   StrAllocCopy(newdoc.address, homepage);
 			   FREE(newdoc.post_data);
 			   FREE(newdoc.post_content_type);
+			   FREE(newdoc.bookmark);
 			   StrAllocCopy(startfile, homepage);
 			   newdoc.isHEAD = FALSE;
 		       } else {
@@ -447,9 +465,9 @@ try_again:
 
 		    if (traversal) {
 		        /*
-			 * During traversal build up lists of all links
-			 * traversed.  Traversal mode is a special 
-			 * feature for traversing http links in the web.
+			 *  During traversal build up lists of all links
+			 *  traversed.  Traversal mode is a special 
+			 *  feature for traversing http links in the web.
 			 */
 			if (traversal_link_to_add) {
 			    /* Add the address we sought to TRAVERSE_FILE */
@@ -457,7 +475,8 @@ try_again:
 			        add_to_table(traversal_link_to_add);
 			    FREE(traversal_link_to_add);
 			}
-			if (curdoc.address && curdoc.title)
+			if (curdoc.address && curdoc.title &&
+			    strncasecomp(curdoc.address, "LYNXIMGMAP:", 11))
 			    /* Add the address we got to TRAVERSE_FOUND_FILE */
 			    add_to_traverse_list(curdoc.address, curdoc.title);
 		    }
@@ -472,6 +491,7 @@ try_again:
 		        !strcmp(curdoc.title, DOWNLOAD_OPTIONS_TITLE)) {
 			StrAllocCopy(newdoc.address, curdoc.address);
 			StrAllocCopy(newdoc.title, curdoc.title);
+			StrAllocCopy(newdoc.bookmark, curdoc.bookmark);
 			newdoc.line = curdoc.line;
 			newdoc.link = curdoc.link;
 		    }
@@ -506,12 +526,18 @@ try_again:
 	   StrAllocCopy(curdoc.address, newdoc.address);
 	   StrAllocCopy(curdoc.post_data, newdoc.post_data);
 	   StrAllocCopy(curdoc.post_content_type, newdoc.post_content_type);
+	   StrAllocCopy(curdoc.bookmark, newdoc.bookmark);
 	   curdoc.isHEAD = newdoc.isHEAD;
 
 	   /*
 	    *  Reset WWW present mode so that if we were getting
 	    *  the source, we get rendered HTML from now on.
 	    */
+	   if (ownerS_address != NULL) {
+	       if (HTOutputFormat == WWW_SOURCE && !HText_getOwner())
+	           HText_setMainTextOwner(ownerS_address);
+	       FREE(ownerS_address);
+	   }
 	   HTOutputFormat = WWW_PRESENT;
 	   /*
 	    *  Reset all of the other relevant flags. - FM
@@ -595,7 +621,8 @@ try_again:
 		 * Set up the crawl output stuff.
 		 */
 		if (curdoc.address && !lookup(curdoc.address)) {
-		    crawl_ok = TRUE;
+		    if (strncasecomp(curdoc.address, "LYNXIMGMAP:", 11))
+		        crawl_ok = TRUE;
 		    add_to_table(curdoc.address);
 		}
 		/*
@@ -644,7 +671,7 @@ try_again:
 	    HText_pageDisplay(newline, prev_target);
 
 #ifdef DIRED_SUPPORT
-	    if (lynx_edit_mode && nlinks > 0 && tagged != NULL)
+	    if (lynx_edit_mode && nlinks > 0 && !HTList_isEmpty(tagged))
 	      showtags(tagged);
 #endif /* DIRED_SUPPORT */
 	    /* if more equals true then there is more
@@ -685,7 +712,7 @@ try_again:
 	    HText_pageDisplay(newline, prev_target);
 
 #ifdef DIRED_SUPPORT
-	    if (lynx_edit_mode && nlinks > 0 && tagged != NULL)
+	    if (lynx_edit_mode && nlinks > 0 && !HTList_isEmpty(tagged))
 	        showtags(tagged);
 #endif /* DIRED_SUPPORT */
 	    if (user_mode == NOVICE_MODE)
@@ -968,10 +995,15 @@ new_keyboard_input:
 	     */
 	    rlink_exists = (nlinks > 0 && links[curdoc.link].lname != NULL);
 	    if (rlink_exists) {
-	        rlink_allowed = (!lookup_reject(links[curdoc.link].lname) &&
-				 !strncmp(traversal_host,
-				 	  links[curdoc.link].lname,
-					  strlen(traversal_host)));
+	        rlink_allowed =
+		    (!lookup_reject(links[curdoc.link].lname) &&
+		     traversal_host && links[curdoc.link].lname &&
+		     !strncmp(traversal_host,
+			      (strncasecomp(links[curdoc.link].lname,
+					    "LYNXIMGMAP:", 11)
+					 ?
+		links[curdoc.link].lname : (links[curdoc.link].lname + 11)),
+			      strlen(traversal_host)));
 	    } else {	
 	        rlink_allowed = FALSE;
 	    }
@@ -1003,7 +1035,9 @@ new_keyboard_input:
 		} else {
 		    StrAllocCopy(traversal_link_to_add,
 		    		 links[curdoc.link].lname);
-		    crawl_ok = TRUE;
+		    if (strncasecomp(traversal_link_to_add,
+		    		     "LYNXIMGMAP:", 11))
+		        crawl_ok = TRUE;
 		    c = RTARROW;
 		}
 	    } else { /* no good right link, so only down and left arrow ok*/
@@ -1091,6 +1125,7 @@ new_cmd:  /* a goto point for new input without going
 		if (are_different(&curdoc, &newdoc)) {
 		    FREE(newdoc.post_data);
 		    FREE(newdoc.post_content_type);
+		    FREE(newdoc.bookmark);
 		    newdoc.isHEAD = FALSE;
 		}
 		force_load = TRUE;  /* force MainLoop to reload */
@@ -1107,10 +1142,13 @@ new_cmd:  /* a goto point for new input without going
 	    break;
 
 	case LYK_SOURCE:  /* toggle view source mode */
-	    if (HTisDocumentSource()) 
+	    if (HTisDocumentSource()) {
 	        HTOutputFormat = WWW_PRESENT;
-	    else
+	    } else {
+	        if (HText_getOwner())
+		    StrAllocCopy(ownerS_address, HText_getOwner());
 	        HTOutputFormat = WWW_SOURCE;
+	    }
 	    HTuncache_current_document();
 	    FREE(curdoc.address); /* so it doesn't get pushed */
 	    break;
@@ -1381,7 +1419,7 @@ new_cmd:  /* a goto point for new input without going
 
 	    } else if (old_c != real_c) {
 		old_c = real_c;
-		_statusline(ALREADY_AT_FIRST);
+		_statusline(ALREADY_AT_BEGIN);
 		sleep(MessageSecs);
 	    }
 	    break;
@@ -1537,7 +1575,7 @@ new_cmd:  /* a goto point for new input without going
 	    break;
 
 	case LYK_HISTORY: 	/* show the history page */
-	    if (strcmp(curdoc.title, HISTORY_PAGE_TITLE)) {
+	    if (curdoc.title && strcmp(curdoc.title, HISTORY_PAGE_TITLE)) {
 		/*
 		 *  Don't do this if already viewing history page.
 		 *
@@ -1562,6 +1600,7 @@ new_cmd:  /* a goto point for new input without going
  		FREE(curdoc.address);  /* so it doesn't get pushed */
 		FREE(newdoc.post_data);
 		FREE(newdoc.post_content_type);
+		FREE(newdoc.bookmark);
 		newdoc.isHEAD = FALSE;
 
                 LYpop(&curdoc);
@@ -1626,8 +1665,11 @@ new_cmd:  /* a goto point for new input without going
 			   /*
 			    * Do nothing if it's disabled. - FM
 			    */
-			   if (links[curdoc.link].form->disabled == YES)
+			   if (links[curdoc.link].form->disabled == YES) {
+			       HTOutputFormat = WWW_PRESENT;
+			       LYforce_no_cache = FALSE;
 			       break;
+			   }
 			   /*
 			    * Make sure we have an action. - FM
 			    */
@@ -1636,6 +1678,8 @@ new_cmd:  /* a goto point for new input without going
 			       					== '\0') {
 			       _statusline(NO_FORM_ACTION);
 			       sleep(MessageSecs);
+			       HTOutputFormat = WWW_PRESENT;
+			       LYforce_no_cache = FALSE;
 			       break;
 			    }
 		            /*
@@ -1645,6 +1689,8 @@ new_cmd:  /* a goto point for new input without going
 			    if (links[curdoc.link].form->submit_method
 			             == URL_MAIL_METHOD && no_mail) {
 			        HTAlert(FORM_MAILTO_DISALLOWED);
+				HTOutputFormat = WWW_PRESENT;
+				LYforce_no_cache = FALSE;
 			        break;
 			    }
 			    /*
@@ -1656,6 +1702,8 @@ new_cmd:  /* a goto point for new input without going
 				    links[curdoc.link].form->submit_action,
 				    "file:", 5)) {
 				HTAlert(FILE_ACTIONS_DISALLOWED);
+				HTOutputFormat = WWW_PRESENT;
+				LYforce_no_cache = FALSE;
 				break;
 			    }
 #ifdef NOTDEFINED /* We're disabling form inputs instead of using this. - FM */
@@ -1669,6 +1717,8 @@ new_cmd:  /* a goto point for new input without going
 				      "multipart/form-data")) {
 				    HTAlert(
 	"Enctype multipart/form-data not yet supported!  Cannot submit.");
+				    HTOutputFormat = WWW_PRESENT;
+				    LYforce_no_cache = FALSE;
 				    break;
 				}
 			    }
@@ -1697,10 +1747,7 @@ new_cmd:  /* a goto point for new input without going
 			if (strncasecomp(curdoc.address, "file:", 5)) {
 			    HTAlert(FILE_SERVED_LINKS_DISALLOWED);
 			    break;
-			} else if (bookmark_page &&
-				   (strstr(curdoc.address, bookmark_page) ||
-				    !strcmp(curdoc.title,
-					    MOSAIC_BOOKMARK_TITLE))) {
+			} else if (curdoc.bookmark != NULL) {
 			    HTAlert(FILE_BOOKMARKS_DISALLOWED);
 			    break;
 			}
@@ -1717,16 +1764,15 @@ new_cmd:  /* a goto point for new input without going
 		    if (are_different(&curdoc, &newdoc)) {
 		        FREE(newdoc.post_data);
 		        FREE(newdoc.post_content_type);
+			FREE(newdoc.bookmark);
 		    }
-		    if (!no_jump && lynxjumpfile &&
+		    if (!no_jump && lynxjumpfile && curdoc.address &&
 		        !strcmp(lynxjumpfile, curdoc.address)) {
 			LYJumpFileURL = TRUE;
 			LYUserSpecifiedURL = TRUE;
-		    } else if (!strcmp(curdoc.title, HISTORY_PAGE_TITLE) ||
-		    	       (bookmark_page &&
-			        (strstr(curdoc.address, bookmark_page) ||
-				 !strcmp(curdoc.title,
-				 	 MOSAIC_BOOKMARK_TITLE))) ||
+		    } else if (curdoc.title &&
+		    	       !strcmp(curdoc.title, HISTORY_PAGE_TITLE) ||
+			       curdoc.bookmark != NULL ||
 			       (lynxjumpfile &&
 			        !strcmp(lynxjumpfile, curdoc.address))) {
 		        LYUserSpecifiedURL = TRUE;
@@ -1739,7 +1785,11 @@ new_cmd:  /* a goto point for new input without going
 #ifdef DIRED_SUPPORT
 		    if (lynx_edit_mode) {
 			  HTuncache_current_document();
-			  HTUnEscape(newdoc.address);
+			  /*  Unescaping any slash chars in the URL,
+			   *  but avoid double unescaping and too-early
+			   *  unescaping of other chars. - KW
+			   */
+			  HTUnEscapeSome(newdoc.address,"/");
 			  strip_trailing_slash(newdoc.address);
 		    }
 #endif /* DIRED_SUPPORT */
@@ -1777,8 +1827,8 @@ new_cmd:  /* a goto point for new input without going
 	     * Ask the user.
 	     */
 	    _statusline(URL_TO_OPEN);
-	    if ((ch=LYgetstr(user_input_buffer, VISIBLE,
-	    		     sizeof(user_input_buffer), recall)) < 0 ) {
+	    if ((ch = LYgetstr(user_input_buffer, VISIBLE,
+	    		       sizeof(user_input_buffer), recall)) < 0 ) {
 		/*
 		 * User cancelled the Goto via ^G.
 		 * Restore user_input_buffer and break. - FM
@@ -1821,8 +1871,8 @@ check_recall:
 		     * Roll around to the last URL in the list. - FM
 		     */
 		    URLNum = 0;
-		if ((cp=(char *)HTList_objectAt(Goto_URLs,
-	    					    URLNum)) != NULL) {
+		if ((cp = (char *)HTList_objectAt(Goto_URLs,
+	    					  URLNum)) != NULL) {
 		    strcpy(user_input_buffer, cp);
 		    if (goto_buffer && *temp &&
 		        !strcmp(temp, user_input_buffer)) { 
@@ -1833,8 +1883,9 @@ check_recall:
 		    } else {
 			_statusline(EDIT_A_PREV_GOTO);
 		    }
-		    if ((ch=LYgetstr(user_input_buffer, VISIBLE,
-				     sizeof(user_input_buffer), recall)) < 0) {
+		    if ((ch = LYgetstr(user_input_buffer, VISIBLE,
+				      sizeof(user_input_buffer),
+				      recall)) < 0) {
 			/*
 			 * User cancelled the Goto via ^G.
 			 * Restore user_input_buffer and break. - FM
@@ -1877,8 +1928,9 @@ check_recall:
 		    } else {
 			_statusline(EDIT_A_PREV_GOTO);
 		    }
-		    if ((ch=LYgetstr(user_input_buffer, VISIBLE,
-				     sizeof(user_input_buffer), recall)) < 0) {
+		    if ((ch = LYgetstr(user_input_buffer, VISIBLE,
+				       sizeof(user_input_buffer),
+				       recall)) < 0) {
 			/*
 			 * User cancelled the Goto via ^G.
 			 * Restore user_input_buffer and break. - FM
@@ -2063,8 +2115,8 @@ check_recall:
 
 #ifdef VMS
 		/*
-		 * On VMS, a file://localhost/ URL means
-		 * a listing for the login directory. - FM
+		 *  On VMS, a file://localhost/ URL means
+		 *  a listing for the login directory. - FM
 		 */
 		if (!strcmp(user_input_buffer, "file://localhost/"))
 		    strcat(user_input_buffer,
@@ -2074,12 +2126,13 @@ check_recall:
 	        StrAllocCopy(newdoc.address, user_input_buffer);
 		newdoc.isHEAD = FALSE;
 		/*
-		 * Might be an anchor in the same doc from a POST
-		 * form.  If so, dont't free the content. -- FM
+		 *  Might be an anchor in the same doc from a POST
+		 *  form.  If so, dont't free the content. -- FM
 		 */
 		if (are_different(&curdoc, &newdoc)) {
 		    FREE(newdoc.post_data);
 		    FREE(newdoc.post_content_type);
+		    FREE(newdoc.bookmark);
 		}
 		force_load = TRUE;
 		LYUserSpecifiedURL = TRUE;
@@ -2098,6 +2151,7 @@ check_recall:
 	        StrAllocCopy(newdoc.title, "Help Screen");
 		FREE(newdoc.post_data);
 		FREE(newdoc.post_content_type);
+		FREE(newdoc.bookmark);
 		newdoc.isHEAD = FALSE;
 	    }
 	    break;
@@ -2118,6 +2172,7 @@ check_recall:
 	            StrAllocCopy(newdoc.title, "System Index"); /* name it */
 		    FREE(newdoc.post_data);
 		    FREE(newdoc.post_content_type);
+		    FREE(newdoc.bookmark);
 		    newdoc.isHEAD = FALSE;
 	        } /* end else */
 	    }  /* end if */
@@ -2177,6 +2232,7 @@ check_recall:
                     StrAllocCopy(newdoc.title, "Entry into main screen");
 		    FREE(newdoc.post_data);
 		    FREE(newdoc.post_content_type);
+		    FREE(newdoc.bookmark);
 		    newdoc.isHEAD = FALSE;
 	            highlight(OFF,curdoc.link); 
 #ifdef DIRED_SUPPORT
@@ -2212,6 +2268,7 @@ check_recall:
 		CurrentCharSet_flag != current_char_set ||
 		show_dotfiles_flag != show_dotfiles ||
 		LYRawMode_flag != LYRawMode ||
+		LYSelectPopups_flag != LYSelectPopups ||
 		strcmp(CurrentUserAgent, (LYUserAgent ?
 					  LYUserAgent : ""))) {
 		HTuncache_current_document();
@@ -2222,6 +2279,7 @@ check_recall:
 		CurrentCharSet_flag = current_char_set;
 		show_dotfiles_flag = show_dotfiles;
 		LYRawMode_flag = LYRawMode;
+		LYSelectPopups_flag = LYSelectPopups;
 		StrAllocCopy(CurrentUserAgent, (LYUserAgent ?
 						LYUserAgent : ""));
 	    }
@@ -2264,6 +2322,7 @@ check_recall:
 	           StrAllocCopy(newdoc.address, use_this_url_instead);
 		   FREE(newdoc.post_data);
 		   FREE(newdoc.post_content_type);
+		   FREE(newdoc.bookmark);
 		   newdoc.isHEAD = FALSE;
 	           FREE(use_this_url_instead);
 		   force_load = TRUE;
@@ -2318,90 +2377,124 @@ check_recall:
 	    break;
 
 	case LYK_COMMENT:  /* reply by mail */
-	   if (!owner_address) {
+	    if (!owner_address &&
+	        strncasecomp(curdoc.address, "http", 4)) {
 		if (old_c != real_c)	{
-			old_c = real_c;
-			_statusline(NO_OWNER);
-			sleep(MessageSecs);
+		    old_c = real_c;
+		    _statusline(NO_OWNER);
+		    sleep(MessageSecs);
 		}
-	   } else if (no_mail) {
-	       if (old_c != real_c) {
-	           old_c = real_c;
-		   _statusline(MAIL_DISALLOWED);
-		   sleep(MessageSecs);
-	       }
-	   } else {
+	    } else if (no_mail) {
+	        if (old_c != real_c) {
+	            old_c = real_c;
+		    _statusline(MAIL_DISALLOWED);
+		    sleep(MessageSecs);
+	        }
+	    } else {
 		_statusline(CONFIRM_COMMENT);
 		c = LYgetch();
 	        if (TOUPPER(c) == 'Y') {
-
-	           if (is_url(owner_address) != MAILTO_URL_TYPE) { 
-			/* the address is a url */
-			/* just follow the link */
-			
-		       StrAllocCopy(newdoc.address, owner_address);
-
-	           } else {
-		       /* the owner_address is a mailto: url type */
-		       cp = HText_getRevTitle();
-		       if (strchr(owner_address,':')!=NULL)
-			 /* send a reply. The address is after the colon */
-	      	         reply_by_mail(strchr(owner_address,':')+1,
-			 	       curdoc.address,
-				       (cp ? cp : "")); 
-		       else
-	      	         reply_by_mail(owner_address, curdoc.address,
-				       (cp ? cp : "")); 
-
-	               refresh_screen=TRUE;  /* to force a showpage */
-	          }
+		    if (!owner_address) {
+		        /*
+			 *  No owner defined, so make a guess and
+			 *  and offer it to the user. - FM
+			 */
+		        char *address = NULL;
+			char *path = HTParse(curdoc.address, "", PARSE_PATH);
+			if (path != NULL) {
+			    HTUnEscape(path);
+			    if (*path == '~' && strlen(path) > 1) {
+			        /*
+				 *  It's a ~user URL so guess user@host. - FM
+				 */
+			        if ((cp = strchr((path+1), '/')) != NULL)
+				    *cp = '\0';
+				StrAllocCopy(address, "mailto:");
+				StrAllocCat(address, (path+1));
+				StrAllocCat(address, "@");
+			    }
+			    FREE(path);
+			}
+			if (address == NULL)
+			    /*
+			     *  Wasn't a ~user URL so guess WebMaster@host. - FM
+			     */
+		            StrAllocCopy(address, "mailto:WebMaster@");
+		        StrAllocCat(address,
+		       		    HTParse(curdoc.address, "", PARSE_HOST));
+			_user_message(NO_OWNER_USE, address);
+			c = LYgetch();
+			if (TOUPPER(c) == 'Y') {
+			    StrAllocCopy(owner_address, address);
+			    FREE(address);
+			} else {
+			    FREE(address);
+			    break;
+			}
+		    }
+	            if (is_url(owner_address) != MAILTO_URL_TYPE) { 
+			/*
+			 *  The address is a URL.  Just follow the link.
+			 */
+		        StrAllocCopy(newdoc.address, owner_address);
+	            } else {
+		        /*
+			 *  The owner_address is a mailto: URL.
+			 */
+		        cp = HText_getRevTitle();
+		        if (strchr(owner_address,':')!=NULL)
+			     /*
+			      *  Send a reply.  The address is after the colon.
+			      */
+	      	             reply_by_mail(strchr(owner_address,':')+1,
+					   curdoc.address,
+					   (cp ? cp : "")); 
+		        else
+	      	            reply_by_mail(owner_address, curdoc.address,
+				          (cp ? cp : "")); 
+
+	                refresh_screen=TRUE;  /* to force a showpage */
+	           }
 	       }
 	   }
 	   break;
 
 #ifdef DIRED_SUPPORT
-	case LYK_TAG_LINK:  /* tag or untag the current link */
-	   if (lynx_edit_mode && nlinks > 0 && !no_dired_support) {
-	      if (dir_list_style == MIXED_STYLE) {
-		 if (!strcmp(links[curdoc.link].hightext,"../")) 
-		   break;
-	      } else if (!strncmp(links[curdoc.link].hightext,"Up to ",6)) 
-		 break;
-	      t1 = tagged;
-	      while (t1 != NULL) {
-		 if (!strcmp(links[curdoc.link].lname,t1->name)) {
-		    if (t1 == tagged) 
-		      tagged = t1->next;
-		    else 
-		      t2->next = t1->next;
-		    FREE(t1->name);
-		    FREE(t1);
-		    tagflag(OFF,curdoc.link);
+	case LYK_TAG_LINK:	/* tag or untag the current link */
+	    if (lynx_edit_mode && nlinks > 0 && !no_dired_support) {
+	        if (!strcmp(links[curdoc.link].hightext, "..")) 
+		    break;	/* Never tag the parent directory */
+	        if (dir_list_style == MIXED_STYLE) {
+		    if (!strcmp(links[curdoc.link].hightext, "../")) 
+		        break;
+	        } else if (!strncmp(links[curdoc.link].hightext,"Up to ",6)) 
 		    break;
-		 }
-		 t2 = t1;
-		 t1 = t1->next;
-	      }
-	      if (t1 == NULL) {
-		 t1 = (taglink *) malloc(sizeof(taglink));
-		 if (tagged == NULL) 
-		   tagged = t1;
-		 else 
-		   t2->next = t1;
-		 t1->next = NULL;
-		 t1->name = NULL;
-		 StrAllocCopy(t1->name,links[curdoc.link].lname);
-		 tagflag(ON,curdoc.link);
-	      }
-	      if (curdoc.link < nlinks-1) {
-		highlight(OFF, curdoc.link);
-		curdoc.link++;
-	      } else if (!more && newline==1 && curdoc.link==nlinks-1) {
-		highlight(OFF,curdoc.link); 
-		curdoc.link = 0;
-	      } else if (more) {  /* next page */
-                newline += (display_lines);
-	      }
+	        {
+		    /*
+		     *  HTList-based management of tag list, see LYLocal.c - KW
+		     */
+		    HTList * t1 = tagged;
+		    char * tagname = NULL;
+		    BOOLEAN found = FALSE;
+
+		    while ((tagname = (char *)HTList_nextObject(t1)) != NULL) {
+		        if (!strcmp(links[curdoc.link].lname,tagname)) {
+			    found = TRUE;
+			    HTList_removeObject(tagged, tagname);
+			    FREE(tagname);
+			    tagflag(OFF,curdoc.link);
+			    break;
+		        }
+		    }
+		    if (!found) {
+		        if (tagged == NULL)
+			    tagged = HTList_new();
+		        tagname = NULL;
+		        StrAllocCopy(tagname,links[curdoc.link].lname);
+		        HTList_addObject(tagged,tagname);
+		        tagflag(ON,curdoc.link);
+		    }
+	        }
 	   }
 	   break;
 
@@ -2469,7 +2562,7 @@ check_recall:
 		    } else {
 		       if (((dir_info.st_mode) & S_IFMT) == S_IFREG) {
 			  strcpy(tmpbuf,cp);
-			  HTUnEscape(tmpbuf);
+			  HTUnEscapeSome(tmpbuf,"/");
 			  if (edit_current_file(tmpbuf,curdoc.link,newline))
 			  {
 			    	HTuncache_current_document();
@@ -2524,31 +2617,38 @@ check_recall:
 	case LYK_DEL_BOOKMARK:  /* delete home page link */
 #ifdef DIRED_SUPPORT
 	case LYK_REMOVE:  /* remove files and directories */
-	   if (lynx_edit_mode && nlinks > 0 && !no_dired_support)
+	   c = 'N';
+	   if (lynx_edit_mode && nlinks > 0 && !no_dired_support) {
 		local_remove(&curdoc);
-	    else
+		c = 'Y';
+	   } else
 #endif /* DIRED_SUPPORT */
 
-	    if (bookmark_page && (strstr(curdoc.address, bookmark_page) ||
-			!strcmp(curdoc.title, MOSAIC_BOOKMARK_TITLE))) {
+	    if (curdoc.bookmark != NULL) {
 		_statusline(CONFIRM_BOOKMARK_DELETE);
              	c = LYgetch();
                 if (TOUPPER(c) != 'Y')
                     break;
-                remove_bookmark_link(links[curdoc.link].anchor_number-1);
+                remove_bookmark_link(links[curdoc.link].anchor_number-1,
+				     curdoc.bookmark);
 	    } else {	/* behave like REFRESH for backward compatability */
-	        refresh_screen=TRUE;
+	        refresh_screen = TRUE;
 	        break;
 	    }
-	    HTuncache_current_document();
-	    StrAllocCopy(newdoc.address, curdoc.address);
-	    FREE(curdoc.address);	
-	    newdoc.line = curdoc.line;
-	    if (curdoc.link == nlinks-1)
-	       /* if we deleted the last link on the page */
-               newdoc.link=curdoc.link-1; 
-	    else
-                newdoc.link=curdoc.link;
+	    if (TOUPPER(c) == 'Y') {
+		HTuncache_current_document();
+		StrAllocCopy(newdoc.address, curdoc.address);
+		FREE(curdoc.address);	
+		newdoc.line = curdoc.line;
+		if (curdoc.link == nlinks-1) {
+		    /*
+		     *  We deleted the last link on the page. - FM
+		     */
+		    newdoc.link = curdoc.link-1;
+		} else {
+		    newdoc.link = curdoc.link;
+		}
+	    }
 	    break;
 
 #ifdef DIRED_SUPPORT
@@ -2560,7 +2660,7 @@ check_recall:
 
 	case LYK_INFO:  /* show document info */
 	   /* don't do if already viewing info page */	
-	   if (strcmp(curdoc.title, SHOWINFO_TITLE)) {
+	   if (strcmp((curdoc.title ? curdoc.title : ""), SHOWINFO_TITLE)) {
 	        showinfo(&curdoc, lines_in_file, &newdoc, owner_address);
 		LYforce_no_cache = TRUE;
 		if (LYValidate || check_realm)
@@ -2583,7 +2683,8 @@ check_recall:
 	    }
 
 	    /* don't do if already viewing print options page */	
-	    if (strcmp(curdoc.title, PRINT_OPTIONS_TITLE)) {	
+	    if (strcmp((curdoc.title ? curdoc.title : ""),
+	    	       PRINT_OPTIONS_TITLE)) {	
 
                 if (print_options(&newdoc.address, lines_in_file) < 0)
 		    break;
@@ -2595,7 +2696,8 @@ check_recall:
 
 	case LYK_LIST:  /* list links in the current document */
 	    /* don't do if already viewing list page */	
-	    if (strcmp(curdoc.title, LIST_PAGE_TITLE)) {
+	    if (strcmp((curdoc.title ? curdoc.title : ""),
+	    	       LIST_PAGE_TITLE)) {
 		if (showlist(&newdoc.address, TRUE) < 0)
 		    break;
 		refresh_screen=TRUE;  /* redisplay */
@@ -2632,7 +2734,8 @@ check_recall:
        case LYK_DIRED_MENU:  /* provide full file management menu */
 	    /* don't do if not allowed or already viewing the menu */	
 	    if (lynx_edit_mode && !no_dired_support &&
-	        strcmp(curdoc.title, DIRED_MENU_TITLE)) {
+	        strcmp((curdoc.title ? curdoc.title : ""),
+		       DIRED_MENU_TITLE)) {
 	        dired_options(&curdoc,&newdoc.address);
 	        refresh_screen=TRUE;  /* redisplay */
 	    }
@@ -2649,35 +2752,58 @@ check_recall:
 		break;
 	    }
 
-	    if (strcmp(curdoc.title, HISTORY_PAGE_TITLE) &&
-	        strcmp(curdoc.title, SHOWINFO_TITLE) && 
-	        strcmp(curdoc.title, PRINT_OPTIONS_TITLE) &&
+	    if (strcmp((curdoc.title ? curdoc.title : ""),
+	    	       HISTORY_PAGE_TITLE) &&
+	        strcmp((curdoc.title ? curdoc.title : ""),
+		       SHOWINFO_TITLE) && 
+	        strcmp((curdoc.title ? curdoc.title : ""),
+		       PRINT_OPTIONS_TITLE) &&
 #ifdef DIRED_SUPPORT
-	        strcmp(curdoc.title, DIRED_MENU_TITLE) &&
-	        strcmp(curdoc.title, PERMIT_OPTIONS_TITLE) &&
-	        strcmp(curdoc.title, UPLOAD_OPTIONS_TITLE) &&
+	        strcmp((curdoc.title ? curdoc.title : ""),
+		       DIRED_MENU_TITLE) &&
+	        strcmp((curdoc.title ? curdoc.title : ""),
+		       PERMIT_OPTIONS_TITLE) &&
+	        strcmp((curdoc.title ? curdoc.title : ""),
+		       UPLOAD_OPTIONS_TITLE) &&
 #endif /* DIRED_SUPPORT */
-	        strcmp(curdoc.title, DOWNLOAD_OPTIONS_TITLE) &&
-	        strcmp(curdoc.title, LIST_PAGE_TITLE)) {
-
+	        strcmp((curdoc.title ? curdoc.title : ""),
+		       DOWNLOAD_OPTIONS_TITLE) &&
+	        strcmp((curdoc.title ? curdoc.title : ""),
+		       LIST_PAGE_TITLE)) {
 		if (nlinks > 0) {
-		    if (curdoc.post_data == NULL) {
+		    if (curdoc.post_data == NULL &&
+		        curdoc.bookmark == NULL) {
 		        /*
-			 *  Document doesn't have POST content, so
-			 *  we can save either that of the link. - FM
+			 *  Document doesn't have POST content,
+			 *  and is not a bookmark file, so we can
+			 *  save either that or the link. - FM
 			 */
 		        _statusline(BOOK_D_L_OR_CANCEL);
 			c = LYgetch();
 			if (TOUPPER(c) == 'D') {
 			    save_bookmark_link(curdoc.address, curdoc.title);
-			    break;
+                            refresh_screen = TRUE; /* MultiBookmark support */
+			    goto check_add_bookmark_to_self;
 			}
 		    } else {
-		        /*
-			 *  Only offer the link in a document
-			 *  with POST content. - FM
-			 */
-		        _statusline(BOOK_L_OR_CANCEL);
+			if (LYMultiBookmarks == FALSE &&
+			    curdoc.bookmark != NULL) {
+			    /*
+			     *  If multiple bookmarks are disabled, offer
+			     *  the L)ink or C)ancel, but with wording
+			     *  which indicates that the link already
+			     *  exists in this bookmark file. - FM
+			     */
+			    _statusline(MULTIBOOKMARKS_SELF);
+			} else {
+		            /*
+			     *  Only offer the link in a document with
+			     *  POST content, or if the current document
+			     *  is a bookmark file and multiple bookmarks
+			     *  are enabled. - FM
+			     */
+		            _statusline(BOOK_L_OR_CANCEL);
+			}
 			c = LYgetch();
 		    }
 		    if (TOUPPER(c) == 'L') {
@@ -2704,6 +2830,17 @@ check_recall:
 		    if (TOUPPER(c) == 'D')
 		        save_bookmark_link(curdoc.address, curdoc.title);
 		}
+check_add_bookmark_to_self:
+		if (curdoc.bookmark && BookmarkPage &&
+		    !strcmp(curdoc.bookmark, BookmarkPage)) {
+		    HTuncache_current_document();
+		    StrAllocCopy(newdoc.address, curdoc.address);
+		    StrAllocCopy(newdoc.bookmark, curdoc.bookmark);
+		    FREE(curdoc.address);	
+		    newdoc.line = curdoc.line;
+		    newdoc.link = curdoc.link;
+		}
+		FREE(temp);
 	    } else {
 		if (old_c != real_c)	{
 			old_c = real_c;
@@ -2711,6 +2848,7 @@ check_recall:
 			sleep(MessageSecs);
 		}
 	    }
+            refresh_screen = TRUE; /* MultiBookmark support */
 	    break;
 
 	case LYK_VIEW_BOOKMARK:   /* v to view home page */
@@ -2723,13 +2861,20 @@ check_recall:
 		break;
 	    }
 
-	    /* see if a bookmark exists
-	     * if it does replace newdoc.address with it's name 
+	    /*
+	     *  See if a bookmark exists.
+	     *  If it does replace newdoc.address with it's name.
 	     */
-	    if (get_bookmark_filename(&newdoc.address) != NULL) {
+	    if ((cp = get_bookmark_filename(&newdoc.address)) != NULL) {
+	        if (*cp == '\0' || !strcmp(cp, " ") ||
+		    !strcmp(curdoc.address, newdoc.address)) {
+		    refresh_screen = TRUE; /* MultiBookmark support */
+		    break;
+		}
 		LYforce_HTML_mode = TRUE;  /* force HTML */
 		LYforce_no_cache = TRUE;  /*force the document to be reloaded*/
-		StrAllocCopy(newdoc.title, "Bookmark File");
+		StrAllocCopy(newdoc.title, BOOKMARK_TITLE);
+		StrAllocCopy(newdoc.bookmark, BookmarkPage);
 		FREE(newdoc.post_data);
 		FREE(newdoc.post_content_type);
 		newdoc.isHEAD = FALSE;
@@ -2743,6 +2888,7 @@ check_recall:
        			sleep(MessageSecs);
 		}
 	    }
+            refresh_screen = TRUE; /* MultiBookmark support */
 	    break;
 
 	case LYK_SHELL:  /* shell escape */
@@ -2779,18 +2925,35 @@ check_recall:
 	    }
 
 	    /* don't do if already viewing download options page */	
-	    if (0==strcmp(curdoc.title, DOWNLOAD_OPTIONS_TITLE))
+	    if (0==strcmp((curdoc.title ? curdoc.title : ""),
+	    		  DOWNLOAD_OPTIONS_TITLE))
 	        break;
 
 	    if (nlinks > 0) {
                 if (links[curdoc.link].type == WWW_FORM_LINK_TYPE) {
-		    if (old_c != real_c)	{
+		    if (links[curdoc.link].form->type == F_SUBMIT_TYPE) {
+		        if (links[curdoc.link].form->submit_method ==
+				 URL_MAIL_METHOD) {
+			    if (old_c != real_c) {
+				old_c = real_c;
+				_statusline(NO_DOWNLOAD_MAILTO_ACTION);
+				sleep(MessageSecs);
+			    }
+			    break;
+			}
+			HTOutputFormat = HTAtom_for("www/download");
+			LYforce_no_cache = TRUE;
+			cmd = LYK_ACTIVATE;
+			goto new_cmd;
+		    }
+		    if (old_c != real_c) {
 			old_c = real_c;
 			_statusline(NO_DOWNLOAD_INPUT);
 			sleep(MessageSecs);
 		    }
 
-		} else if (0==strcmp(curdoc.title, PRINT_OPTIONS_TITLE)) {
+		} else if (0==strcmp((curdoc.title ? curdoc.title : ""),
+				     PRINT_OPTIONS_TITLE)) {
 		    if (old_c != real_c)	{
 			old_c = real_c;
 			_statusline(NO_DOWNLOAD_PRINT_OP);
@@ -2798,14 +2961,16 @@ check_recall:
 		    }
 
 #ifdef DIRED_SUPPORT
-		} else if (0==strcmp(curdoc.title, UPLOAD_OPTIONS_TITLE)) {
+		} else if (0==strcmp((curdoc.title ? curdoc.title : ""),
+				     UPLOAD_OPTIONS_TITLE)) {
 		    if (old_c != real_c)	{
 			old_c = real_c;
 			_statusline(NO_DOWNLOAD_UPLOAD_OP);
 			sleep(MessageSecs);
 		    }
 
-		} else if (0==strcmp(curdoc.title, PERMIT_OPTIONS_TITLE)) {
+		} else if (0==strcmp((curdoc.title ? curdoc.title : ""),
+				     PERMIT_OPTIONS_TITLE)) {
 		    if (old_c != real_c)	{
 			old_c = real_c;
 			_statusline(NO_DOWNLOAD_PERMIT_OP);
@@ -2822,10 +2987,12 @@ check_recall:
 		    FREE(temp);
 #endif /* DIRED_SUPPORT */
 
-		} else if (0==strcmp(curdoc.title, HISTORY_PAGE_TITLE)) {
+		} else if (0==strcmp((curdoc.title ? curdoc.title : ""),
+				     HISTORY_PAGE_TITLE)) {
 		    int number = atoi(links[curdoc.link].lname+9);
 		    StrAllocCopy(newdoc.address, history[number].address);
                     StrAllocCopy(newdoc.title, links[curdoc.link].hightext);
+		    StrAllocCopy(newdoc.address, history[number].bookmark);
 		    FREE(newdoc.post_data);
 		    FREE(newdoc.post_content_type);
 		    if (history[number].post_data)
@@ -2839,7 +3006,7 @@ check_recall:
 	            HTOutputFormat = HTAtom_for("www/download");
 		    LYUserSpecifiedURL = TRUE;
 		    /*force the document to be reloaded*/
-		    LYforce_no_cache = TRUE;  
+		    LYforce_no_cache = TRUE;
 		    force_load = TRUE;  /* force MainLoop to reload */
 		    
 		} else if (!strncasecomp(links[curdoc.link].lname,
@@ -2864,12 +3031,13 @@ check_recall:
 		    if (are_different(&curdoc, &newdoc)) {
 			FREE(newdoc.post_data);
 			FREE(newdoc.post_content_type);
+			FREE(newdoc.bookmark);
 			newdoc.isHEAD = FALSE;
 		    }
                     newdoc.link = 0;
 	            HTOutputFormat = HTAtom_for("www/download");
 		    /*force the document to be reloaded*/
-		    LYforce_no_cache = TRUE;  
+		    LYforce_no_cache = TRUE;
 		    force_load = TRUE;  /* force MainLoop to reload */
                 }
             } else if (old_c != real_c)	{
@@ -2882,7 +3050,8 @@ check_recall:
 #ifdef DIRED_SUPPORT
 	  case LYK_UPLOAD:
 	    /* don't do if already viewing upload options page */	
-	    if (0==strcmp(curdoc.title, UPLOAD_OPTIONS_TITLE))
+	    if (0==strcmp((curdoc.title ? curdoc.title : ""),
+	    		  UPLOAD_OPTIONS_TITLE))
 	        break;
 
 	    if (lynx_edit_mode && !no_dired_support) {
@@ -3023,6 +3192,7 @@ check_recall:
 		StrAllocCopy(newdoc.address, "LYNXKEYMAP:");
 	        FREE(newdoc.post_data);
 	        FREE(newdoc.post_content_type);
+		FREE(newdoc.bookmark);
 		newdoc.isHEAD = FALSE;
 		/*
 		 * If vi_keys changed, the keymap did too,
@@ -3092,6 +3262,7 @@ check_recall:
 		      StrAllocCopy(lynxjumpfile, ret);
 		      FREE(newdoc.post_data);
 	    	      FREE(newdoc.post_content_type);
+		      FREE(newdoc.bookmark);
 		      newdoc.isHEAD = FALSE;
 		      FREE(ret);
 		      LYUserSpecifiedURL = TRUE;
diff --git a/src/LYMap.c b/src/LYMap.c
index a147a79b..9720586b 100644
--- a/src/LYMap.c
+++ b/src/LYMap.c
@@ -212,6 +212,7 @@ PRIVATE int LYLoadIMGmap ARGS4 (
 	WWWDoc.address = address;
         WWWDoc.post_data = NULL;
         WWWDoc.post_content_type = NULL;
+        WWWDoc.bookmark = NULL;
 	WWWDoc.isHEAD = FALSE;
         LYforce_no_cache = TRUE;
 	reloading = TRUE;
@@ -246,6 +247,7 @@ PRIVATE int LYLoadIMGmap ARGS4 (
 	WWWDoc.address = address;
         WWWDoc.post_data = NULL;
         WWWDoc.post_content_type = NULL;
+        WWWDoc.bookmark = NULL;
 	WWWDoc.isHEAD = FALSE;
         LYforce_no_cache = TRUE;
 	reloading = TRUE;
diff --git a/src/LYNews.c b/src/LYNews.c
index 1ace9b6b..2bdab054 100644
--- a/src/LYNews.c
+++ b/src/LYNews.c
@@ -49,6 +49,7 @@ PUBLIC int LYNewsPost ARGS2(document *,newdoc, BOOLEAN,followup)
 	WWWDoc.address = newdoc->address;
 	WWWDoc.post_data = newdoc->post_data;
 	WWWDoc.post_content_type = newdoc->post_content_type;
+	WWWDoc.bookmark = newdoc->bookmark;
 	WWWDoc.isHEAD = newdoc->isHEAD;
         if(!HTLoadAbsolute(&WWWDoc)) {
 	    FREE(newsgroups);
diff --git a/src/LYOptions.c b/src/LYOptions.c
index deb94e8b..345c7d0e 100644
--- a/src/LYOptions.c
+++ b/src/LYOptions.c
@@ -14,6 +14,7 @@
 #include "LYKeymap.h"
 #include "LYrcFile.h"
 #include "HTAlert.h"
+#include "LYBookmark.h"
 
 #include "LYLeaks.h"
 
@@ -29,7 +30,8 @@
 
 BOOLEAN term_options;
 PRIVATE void terminate_options  PARAMS((int sig));
-PUBLIC int boolean_choice PARAMS((int status, int line, char **choices));
+PRIVATE int boolean_choice PARAMS((int status, int line,
+				   int column, char **choices));
 
 #define MAXCHOICES 10
 
@@ -60,13 +62,12 @@ PRIVATE void option_statusline ARGS1(char *,text)
     refresh();
 }
 
-
 PUBLIC void options NOARGS
 {
 #ifdef ALLOW_USERS_TO_CHANGE_EXEC_WITHIN_OPTIONS
     int itmp;
 #endif /* ALLOW_USERS_TO_CHANGE_EXEC_WITHIN_OPTIONS */
-    int response=0, ch;
+    int response, ch;
     /* if the user changes the display I need memory to put it in */
     char display_option[256]; 
 #ifndef VMS
@@ -106,98 +107,113 @@ PUBLIC void options NOARGS
     term_options = FALSE;
     signal(SIGINT, terminate_options);
 
+draw_options:
+    response = 0;
     clear(); 
-    move(0,5);
+    move(0, 5);
     if (bold_H1 || bold_headers)
         start_bold();
     printw("         Options Menu (%s Version %s)",
     	   			   LYNX_NAME, LYNX_VERSION);
     if (bold_H1 || bold_headers)
         stop_bold();
-    move(L_EDITOR,5);  
+    move(L_EDITOR, 5);  
     printw("E)ditor                      : %s",
     		((editor && *editor) ? editor : "NONE"));
 
-    move(L_DISPLAY,5);  
+    move(L_DISPLAY, 5);  
     printw("D)ISPLAY variable            : %s",
     		((display && *display) ? display : "NONE"));
 
-    move(L_MAIL_ADDRESS,5);  
+    move(L_MAIL_ADDRESS, 5);  
     printw("P)ersonal mail address       : %s",
     		((personal_mail_address && *personal_mail_address) ?
 					     personal_mail_address : "NONE"));
 
-    move(L_HOME,5);  
-    printw("B)ookmark file               : %s",
+    move(L_HOME, 5);
+    printw("mu(L)ti-bookmarks: %s",
+		(LYMultiBookmarks ? (LYMBMAdvanced ?
+                                     "ADVANCED" : "STANDARD") : "OFF"));
+    move(L_HOME, B_BOOK);
+    if (LYMultiBookmarks) {
+        printw("review/edit B)ookmarks files");
+    } else {
+        printw("B)ookmark file: %s",
     		((bookmark_page && *bookmark_page) ? bookmark_page : "NONE"));
+    }
 
-    move(L_FTPSTYPE,5);
+    move(L_FTPSTYPE, 5);
     printw("F)TP sort criteria           : %s",(HTfileSortMethod==FILE_BY_NAME ?
 					"By Filename" :
 					  (HTfileSortMethod==FILE_BY_SIZE ?
 					    "By Size" : 
 					      (HTfileSortMethod==FILE_BY_TYPE ?
 						"By Type" : "By Date"))));
-    move(L_SSEARCH,5); 
+    move(L_SSEARCH, 5); 
     printw("S)earching type              : %s",(case_sensitive ?
 				        "CASE SENSITIVE" : "CASE INSENSITIVE"));
 
-    move(L_CHARSET,5);
+    move(L_CHARSET, 5);
     printw("display (C)haracter set      : %s", 
     					LYchar_set_names[current_char_set]);
     
-    move(L_RAWMODE,5);
+    move(L_RAWMODE, 5);
     printw("Raw 8-bit or CJK m(O)de      : %s", (LYRawMode ? "ON" : "OFF"));
 
-    move(L_LANGUAGE,5);
+    move(L_LANGUAGE, 5);
     printw("preferred document lan(G)uage: %s",
     		((language && *language) ? language : "NONE"));
 
-    move(L_PREF_CHARSET,5);
+    move(L_PREF_CHARSET, 5);
     printw("preferred document c(H)arset : %s",
     		((pref_charset && *pref_charset) ? pref_charset : "NONE"));
 
-    move(L_VIKEYS,5); 
-    printw("V)I keys                     : %s", (vi_keys ? "ON" : "OFF"));
+    move(L_BOOL_A, B_VIKEYS);
+    printw("V)I keys: %s", (vi_keys ? "ON" : "OFF"));
     
-    move(L_EMACSKEYS,5); 
-    printw("e(M)acs keys                 : %s", (emacs_keys ? "ON" : "OFF"));
+    move(L_BOOL_A, B_EMACSKEYS);
+    printw("e(M)acs keys: %s", (emacs_keys ? "ON" : "OFF"));
     
-    move(L_KEYPAD,5); 
+    move(L_BOOL_A, B_SHOW_DOTFILES);
+    printw("sho(W) dot files: %s",
+    			((!no_dotfiles && show_dotfiles) ? "ON" : "OFF"));
+
+    move(L_SELECT_POPUPS, 5);
+    printw("popups for selec(T) fields   : %s",
+    			(LYSelectPopups ? "ON" : "OFF"));
+
+    move(L_KEYPAD, 5); 
     printw("K)eypad mode                 : %s", 
 			   		  (keypad_mode == NUMBERS_AS_ARROWS ? 
 					   "Numbers act as arrows" : 
 				           "Links are numbered"));
 
-    move(L_LINEED,5);
+    move(L_LINEED, 5);
     printw("li(N)e edit style            : %s",
     					   LYLineeditNames[current_lineedit]);
 
 #ifdef DIRED_SUPPORT
-    move(L_DIRED,5);
+    move(L_DIRED, 5);
     printw("l(I)st directory style       : %s",
                      (dir_list_style == FILES_FIRST ? "Files first          " :
 		     (dir_list_style == MIXED_STYLE ? "Mixed style          " : 
                                                       "Directories first    ")));
 #endif /* DIRED_SUPPORT */
 
-    move(L_SHOW_DOTFILES,5);
-    printw("sho(W) dot files             : %s",
-    			((!no_dotfiles && show_dotfiles) ? "ON" : "OFF"));
-
-    move(L_USER_MODE,5);
+    move(L_USER_MODE, 5);
     printw("U)ser mode                   : %s",
 			(user_mode == NOVICE_MODE ? "Novice" : 
 			(user_mode == INTERMEDIATE_MODE ? "Intermediate" :
 							     "Advanced")));
 
-    move(L_USER_AGENT,5);
+    move(L_USER_AGENT, 5);
     printw("user (A)gent                 : %s",
     		((LYUserAgent && *LYUserAgent) ? LYUserAgent : "NONE"));
 
+
 #ifdef ALLOW_USERS_TO_CHANGE_EXEC_WITHIN_OPTIONS
-    move(L_EXEC,5);
-    printw("L)ocal execution links       : ");
+    move(L_EXEC, 5);
+    printw("local e(X)ecution links      : ");
 #ifndef NEVER_ALLOW_REMOTE_EXEC
     addstr((local_exec ? "ALWAYS ON" :
                     (local_exec_on_local_files ? "FOR LOCAL FILES ONLY" :
@@ -230,9 +246,10 @@ PUBLIC void options NOARGS
     addstr("'");
     addstr(TO_RETURN_SEGMENT);
 
-    while(TOUPPER(response) != 'R' && response != LTARROW &&
-          response != '>' && !term_options && response != 7 &&
-	  				      response != 3) {
+    while (TOUPPER(response) != 'R' &&
+    	   !LYisNonAlnumKeyname(response, LYK_PREV_DOC) &&
+           response != '>' && !term_options &&
+	   response != 7 &&  response != 3) {
 
            move(LYlines-2, 0);
 	   start_reverse();
@@ -243,7 +260,11 @@ PUBLIC void options NOARGS
            response = LYgetch();
 	   if (term_options || response == 7 || response == 3)
 	       response = 'R';
-	   switch(response) {
+	   if (LYisNonAlnumKeyname(response, LYK_REFRESH)) {
+	       clearok(curscr, TRUE);
+	       goto draw_options;
+	   }
+	   switch (response) {
 	 	case 'e':  /* change the editor */
 		case 'E':
 	                if (no_editor) {
@@ -275,8 +296,10 @@ PUBLIC void options NOARGS
 			        StrAllocCopy(editor, display_option);
 				addstr(display_option);
 			    }
+			    clrtoeol();
 			    option_statusline(VALUE_ACCEPTED);
 			}
+			response = ' ';
 			break;
 
 		case 'd':  /* change the display */
@@ -298,16 +321,19 @@ PUBLIC void options NOARGS
 			if ((term_options || ch == -1) ||
 			    (display != NULL &&
 #ifdef VMS
-			     0 == strcasecomp(display, display_option))) {
+			     0 == strcasecomp(display, display_option)))
 #else
-			     0 == strcmp(display, display_option))) {
+			     0 == strcmp(display, display_option)))
 #endif /* VMS */
+			{
 			    /*
 			     *  Cancelled, or a non-NULL display string
 			     *  wasn't changed. - FM
 			     */
 			    addstr((display && *display) ? display : "NONE");
+			    clrtoeol();
 			    option_statusline(VALUE_ACCEPTED);
+			    response = ' ';
 			    break;
 			} else if (*display_option == '\0') {
 			    if ((display == NULL) ||
@@ -317,7 +343,9 @@ PUBLIC void options NOARGS
 				 *  wasn't changed. - FM
 				 */
 			        addstr("NONE");
+				clrtoeol();
 				option_statusline(VALUE_ACCEPTED);
+				response = ' ';
 				break;
 			    }
 			}
@@ -340,6 +368,7 @@ PUBLIC void options NOARGS
 			    display = NULL;
 			}
 			addstr(display ? display : "NONE");
+			clrtoeol();
 			if ((display == NULL && *display_option == '\0') ||
 			    (display != NULL &&
 			     0 == strcmp(display, display_option))) {
@@ -359,6 +388,46 @@ PUBLIC void options NOARGS
 			        option_statusline(FAILED_CLEAR_SET_DISPLAY);
 			    }
 			}
+			response = ' ';
+			break;
+
+		case 'l':
+		case 'L':
+			if (LYMBMBlocked) {
+			    option_statusline(MULTIBOOKMARKS_DISALLOWED);
+			    response = ' ';
+			    break;
+			}
+			choices[0] = NULL;
+			StrAllocCopy(choices[0],"OFF     ");
+			choices[1] = NULL;
+			StrAllocCopy(choices[1],"STANDARD");
+			choices[2] = NULL;
+			StrAllocCopy(choices[2],"ADVANCED");
+			choices[3] = NULL;
+			LYMultiBookmarks = boolean_choice(LYMultiBookmarks *
+                                                          (1 + LYMBMAdvanced),
+                                                          L_HOME, C_MULTI,
+                                                          choices);
+			FREE(choices[0]);
+			FREE(choices[1]);
+			FREE(choices[2]);
+                        if (LYMultiBookmarks == 2) {
+                            LYMultiBookmarks = TRUE;
+                            LYMBMAdvanced = TRUE;
+                        } else {
+                            LYMBMAdvanced = FALSE;
+                        }
+			
+			move(L_HOME, B_BOOK);
+			clrtoeol();
+    			if (LYMultiBookmarks) {
+    			    printw("review/edit B)ookmarks files");
+    			} else {
+			    printw("B)ookmark file: %s",
+    		((bookmark_page && *bookmark_page) ? bookmark_page : "NONE"));
+    			}
+			response = ' ';
 			break;
 
 		case 'b':  /* change the bookmark page location */
@@ -367,34 +436,41 @@ PUBLIC void options NOARGS
 			 * change the bookmark page
 			 */
 			if (!no_bookmark) {
+			    if (LYMultiBookmarks) {
+				edit_bookmarks();
+				signal(SIGINT, terminate_options);
+				goto draw_options;
+			    }
 			    if (bookmark_page && *bookmark_page)
 			        strcpy(display_option, bookmark_page);
 			    else {  /* clear the NONE */
-				move(L_HOME, COL_OPTION_VALUES);
-				addstr("    ");
+				move(L_HOME, C_DEFAULT);
+				clrtoeol();
 			        *display_option = '\0';
 			    }
 			    option_statusline(ACCEPT_DATA);
-			    move(L_HOME, COL_OPTION_VALUES);  
+			    move(L_HOME, C_DEFAULT);  
 			    standout();
 			    ch = LYgetstr(display_option, VISIBLE,
 			    		  sizeof(display_option), NORECALL);
 			    standend();
-			    move(L_HOME, COL_OPTION_VALUES);
-			    if (term_options || ch == -1) {
+			    move(L_HOME, C_DEFAULT);
+			    if (term_options ||
+			        ch == -1 || *display_option == '\0') {
 			        addstr((bookmark_page && *bookmark_page) ?
 						    bookmark_page : "NONE");
-			    } else if (*display_option == '\0') {
-				FREE(bookmark_page);
-				addstr("NONE");
 			    } else {
 			        StrAllocCopy(bookmark_page, display_option);
+				StrAllocCopy(MBM_A_subbookmark[0],
+					     bookmark_page);
 				addstr(display_option);
 			    }
+			    clrtoeol();
 			    option_statusline(VALUE_ACCEPTED);
 			} else { /* anonymous */
 			    option_statusline(BOOKMARK_CHANGE_DISALLOWED);
 			}
+			response = ' ';
 			break;
 
 		case 'f':
@@ -410,10 +486,11 @@ PUBLIC void options NOARGS
                         StrAllocCopy(choices[3],"By Date    ");
                         choices[4] = NULL;
                         HTfileSortMethod = boolean_choice(HTfileSortMethod,
-                                               L_FTPSTYPE, choices);
+                                               L_FTPSTYPE, -1, choices);
                         FREE(choices[0]);
                         FREE(choices[1]);
                         FREE(choices[2]);
+			response = ' ';
                         break;
 
 		case 'p':  /* change personal mail address for From headers */
@@ -443,27 +520,30 @@ PUBLIC void options NOARGS
 			    StrAllocCopy(personal_mail_address, display_option);
 			    addstr(display_option);
 			}
+			clrtoeol();
 			option_statusline(VALUE_ACCEPTED);
+			response = ' ';
 			break;
 
 		case 's':
 		case 'S':
-			 /* copy strings into choice array */
-			 choices[0] = NULL;
-			 StrAllocCopy(choices[0],"CASE INSENSITIVE");
-			 choices[1] = NULL;
-			 StrAllocCopy(choices[1],"CASE SENSITIVE  ");
-			 choices[2] = NULL;
-			 case_sensitive = boolean_choice(case_sensitive,
-						L_SSEARCH, choices);
-			 FREE(choices[0]);
-			 FREE(choices[1]);
+			/* copy strings into choice array */
+			choices[0] = NULL;
+			StrAllocCopy(choices[0],"CASE INSENSITIVE");
+			choices[1] = NULL;
+			StrAllocCopy(choices[1],"CASE SENSITIVE  ");
+			choices[2] = NULL;
+			case_sensitive = boolean_choice(case_sensitive,
+						L_SSEARCH, -1, choices);
+			FREE(choices[0]);
+			FREE(choices[1]);
+			response = ' ';
 			break;
 
 		case 'c':
 		case 'C':
 			current_char_set = boolean_choice(current_char_set,
-			    		L_CHARSET, LYchar_set_names);
+			    		L_CHARSET, -1, LYchar_set_names);
 			/*
 			 *  Set the raw 8-bit or CJK mode defaults and
 			 *  character set if changed. - FM
@@ -478,6 +558,7 @@ PUBLIC void options NOARGS
 			    clrtoeol();
 			    addstr(LYRawMode ? "ON " : "OFF");
 			}
+			response = ' ';
 			break;
 
 		case 'o':
@@ -489,7 +570,7 @@ PUBLIC void options NOARGS
 			StrAllocCopy(choices[1], "ON ");
 			choices[2] = NULL;
 			LYRawMode = boolean_choice(LYRawMode,
-						   L_RAWMODE, choices);
+						   L_RAWMODE, -1, choices);
 			/*
 			 *  Set the LYUseDefaultRawMode value and character
 			 *  handling if LYRawMode was changed. - FM
@@ -502,6 +583,7 @@ PUBLIC void options NOARGS
 			}
 			FREE(choices[0]);
 			FREE(choices[1]);
+			response = ' ';
 			break;
 
 		case 'g':  /* change language preference */
@@ -530,7 +612,9 @@ PUBLIC void options NOARGS
 			    StrAllocCopy(language, display_option);
 			    addstr(display_option);
 			}
+			clrtoeol();
 			option_statusline(VALUE_ACCEPTED);
+			response = ' ';
 			break;
 
 		case 'h':  /* change character set preference */
@@ -559,7 +643,9 @@ PUBLIC void options NOARGS
 			    StrAllocCopy(pref_charset, display_option);
 			    addstr(display_option);
 			}
+			clrtoeol();
 			option_statusline(VALUE_ACCEPTED);
+			response = ' ';
 			break;
 
 		case 'v':
@@ -570,13 +656,16 @@ PUBLIC void options NOARGS
 			choices[1] = NULL;
 			StrAllocCopy(choices[1],"ON ");
 			choices[2] = NULL;
-			vi_keys = boolean_choice(vi_keys, L_VIKEYS, choices);
+			vi_keys = boolean_choice(vi_keys,
+						 L_BOOL_A, C_VIKEYS,
+						 choices);
 			if (vi_keys)
                             set_vi_keys();
                         else
                             reset_vi_keys();
 			FREE(choices[0]);
 			FREE(choices[1]);
+			response = ' ';
 			break;
 
 		case 'M':
@@ -587,14 +676,53 @@ PUBLIC void options NOARGS
 			choices[1] = NULL;
 			StrAllocCopy(choices[1],"ON ");
 			choices[2] = NULL;
-			emacs_keys = boolean_choice(emacs_keys, L_EMACSKEYS, 
-								      choices);
+			emacs_keys = boolean_choice(emacs_keys,
+						    L_BOOL_A, C_EMACSKEYS,
+						    choices);
                         if (emacs_keys)
                             set_emacs_keys();
                         else
                             reset_emacs_keys();
 			FREE(choices[0]);
 			FREE(choices[1]);
+			response = ' ';
+			break;
+
+		case 'W':
+		case 'w':
+			   if (no_dotfiles) {
+			       option_statusline(DOTFILE_ACCESS_DISABLED);
+			   } else {
+			       /* copy strings into choice array */
+			       choices[0] = NULL;
+			       StrAllocCopy(choices[0],"OFF");
+			       choices[1] = NULL;
+			       StrAllocCopy(choices[1],"ON ");
+			       choices[2] = NULL;
+			       show_dotfiles = boolean_choice(show_dotfiles,
+							      L_BOOL_A,
+							      C_SHOW_DOTFILES, 
+							      choices);
+			       FREE(choices[0]);
+			       FREE(choices[1]);
+			   }
+			   response = ' ';
+			   break;
+
+		case 't':
+		case 'T':
+			/* copy strings into choice array */
+			choices[0] = NULL;
+			StrAllocCopy(choices[0], "OFF");
+			choices[1] = NULL;
+			StrAllocCopy(choices[1], "ON ");
+			choices[2] = NULL;
+			LYSelectPopups = boolean_choice(LYSelectPopups,
+							L_SELECT_POPUPS, -1,
+							choices);
+			FREE(choices[0]);
+			FREE(choices[1]);
+			response = ' ';
 			break;
 
 		case 'k':
@@ -606,19 +734,21 @@ PUBLIC void options NOARGS
 			StrAllocCopy(choices[1],"Links are numbered   ");
 			choices[2] = NULL;
 			keypad_mode = boolean_choice(keypad_mode,
-			       			     L_KEYPAD, choices);
+			       			     L_KEYPAD, -1, choices);
                         if (keypad_mode == NUMBERS_AS_ARROWS)
                             set_numbers_as_arrows();
                         else
                             reset_numbers_as_arrows();
 			FREE(choices[0]);
 			FREE(choices[1]);
+			response = ' ';
 			break;
 
 		case 'n':
 		case 'N':
 			current_lineedit = boolean_choice(current_lineedit,
-			    		L_LINEED, LYLineeditNames);
+			    		L_LINEED, -1, LYLineeditNames);
+			response = ' ';
 			break;
 
 #ifdef DIRED_SUPPORT
@@ -633,32 +763,14 @@ PUBLIC void options NOARGS
 			StrAllocCopy(choices[2],"Mixed style      ");
 			choices[3] = NULL;
 			dir_list_style = boolean_choice(dir_list_style,
-							L_DIRED, choices);
+							L_DIRED, -1, choices);
 			FREE(choices[0]);
 			FREE(choices[1]);
 			FREE(choices[2]);
+			response = ' ';
 			break;
 #endif /* DIRED_SUPPORT */
 
-		case 'W':
-		case 'w':
-			   if (no_dotfiles) {
-			       option_statusline(DOTFILE_ACCESS_DISABLED);
-			   } else {
-			       /* copy strings into choice array */
-			       choices[0] = NULL;
-			       StrAllocCopy(choices[0],"OFF");
-			       choices[1] = NULL;
-			       StrAllocCopy(choices[1],"ON ");
-			       choices[2] = NULL;
-			       show_dotfiles = boolean_choice(show_dotfiles,
-							      L_SHOW_DOTFILES,
-							      choices);
-			       FREE(choices[0]);
-			       FREE(choices[1]);
-			   }
-			   break;
-
 		case 'u':
 		case 'U':
 			/* copy strings into choice array */
@@ -670,7 +782,7 @@ PUBLIC void options NOARGS
 			StrAllocCopy(choices[2],"Advanced    ");
 			choices[3] = NULL;
 			user_mode = boolean_choice(user_mode,
-							L_USER_MODE, choices);
+						   L_USER_MODE, -1, choices);
 			FREE(choices[0]);
 			FREE(choices[1]);
 			FREE(choices[2]);
@@ -678,6 +790,7 @@ PUBLIC void options NOARGS
 			   display_lines = LYlines-4;
 			else
 			   display_lines = LYlines-2;
+			response = ' ';
 			break;
 
 		case 'a':
@@ -710,6 +823,7 @@ PUBLIC void options NOARGS
 			        StrAllocCopy(LYUserAgent, display_option);
 				addstr(display_option);
 			    }
+			    clrtoeol();
 			    if (LYUserAgent && *LYUserAgent &&
 			    	!strstr(LYUserAgent, "Lynx") &&
 				!strstr(LYUserAgent, "lynx")) {
@@ -720,23 +834,24 @@ PUBLIC void options NOARGS
 			} else { /* disallowed */
 			    option_statusline(UA_COPYRIGHT_WARNING);
 			}
+			response = ' ';
 			break;
 
 #ifdef ALLOW_USERS_TO_CHANGE_EXEC_WITHIN_OPTIONS
-		case 'l':  /* local exec */
-		case 'L':
-			if(!exec_frozen) {
+		case 'x':  /* local exec */
+		case 'X':
+			if (!exec_frozen) {
 #ifndef NEVER_ALLOW_REMOTE_EXEC
-			   if(local_exec) {
-				itmp=2;
-			   } else {
+			    if (local_exec) {
+				itmp = 2;
+			    } else {
 #else
 			  {
 #endif /* NEVER_ALLOW_REMOTE_EXEC */
-			  	if(local_exec_on_local_files)
-				    	itmp=1;
+			  	if (local_exec_on_local_files)
+				    itmp= 1;
 				else
-					itmp=0;
+				    itmp = 0;
 			   }
 			   /* copy strings into choice array */
 			   choices[0] = NULL;
@@ -748,7 +863,7 @@ PUBLIC void options NOARGS
 			   StrAllocCopy(choices[2],"ALWAYS ON           ");
 			   choices[3] = NULL;
 #endif /* NEVER_ALLOW_REMOTE_EXEC */
-			   itmp = boolean_choice(itmp, L_EXEC, choices);
+			   itmp = boolean_choice(itmp, L_EXEC, -1, choices);
   
 			   FREE(choices[0]);
 			   FREE(choices[1]);
@@ -757,23 +872,27 @@ PUBLIC void options NOARGS
 #endif /* NEVER_ALLOW_REMOTE_EXEC */
 			   switch(itmp) {
 			      case 0:
-				  local_exec=FALSE;
-				  local_exec_on_local_files=FALSE;
+				  local_exec = FALSE;
+				  local_exec_on_local_files = FALSE;
+				  response = ' ';
 				  break;
 			      case 1:
-				  local_exec=FALSE;
-				  local_exec_on_local_files=TRUE;
+				  local_exec = FALSE;
+				  local_exec_on_local_files = TRUE;
+				  response = ' ';
 				  break;
 #ifndef NEVER_ALLOW_REMOTE_EXEC
 			      case 2:
-				  local_exec=TRUE;
-				  local_exec_on_local_files=FALSE;
+				  local_exec = TRUE;
+				  local_exec_on_local_files = FALSE;
+				  response = ' ';
 				  break;
 #endif /* NEVER_ALLOW_REMOTE_EXEC */
 			  } /* end switch */
 			} else {
 			   option_statusline(CHANGE_OF_SETTING_DISALLOWED);
 			}
+			response = ' ';
 			break;
 #endif /* ALLOW_USERS_TO_CHANGE_EXEC_WITHIN_OPTIONS */
 
@@ -805,72 +924,329 @@ PUBLIC void options NOARGS
 			    option_statusline(R_TO_RETURN_TO_LYNX);
 			}
 	    }  /* end switch */
-     }  /* end while */
+    }  /* end while */
 
-     term_options = FALSE;
-     signal(SIGINT, cleanup_sig);
+    term_options = FALSE;
+    signal(SIGINT, cleanup_sig);
 }
 
-
 /* take a boolean status and prompt the user for a new status
  * and return it
  */
 
-PUBLIC int boolean_choice ARGS3(int,status, int,line, char **,choices)
+PRIVATE int boolean_choice ARGS4(
+	int,		status,
+	int,		line,
+	int,		column,
+	char **,	choices)
 {
-	int response=0;
-	int number=0;
+    int response = 0;
+    int number = 0;
+    int col = (column >= 0 ? column : COL_OPTION_VALUES);
 	
-	for (; choices[number] != NULL; number++)
-	    ;  /* empty loop body */
+    for (; choices[number] != NULL; number++)
+	;  /* empty loop body */
 
-	number--;
-
-	option_statusline(ACCEPT_DATA);
-	/* highlight the current selection */
-	move(line, COL_OPTION_VALUES);
-	standout();
-	addstr(choices[status]);
+    number--;
 
-	standend();
-	option_statusline(ANY_KEY_CHANGE_RET_ACCEPT);
-	standout();
+    option_statusline(ACCEPT_DATA);
+    /*
+     *  Highlight the current selection.
+     */
+    move(line, col);
+    standout();
+    addstr(choices[status]);
 
-	while(1) {
-	   move(line, COL_OPTION_VALUES);
-	   response = LYgetch();
-	   if (term_options || response == 7 || response == 3)
-		response = '\n';
-	   if(response != '\n' && response != '\r') {
-		if(status == number)
-		    status = 0;  /* go over the top and around */
-		else
-		    status++;
-		addstr(choices[status]);
-	        refresh();
-	    } else {
-		/* unhighlight selection */
-	        move(line, COL_OPTION_VALUES);
-	        standend();
-	        addstr(choices[status]);
+    standend();
+    option_statusline(ANY_KEY_CHANGE_RET_ACCEPT);
+    standout();
 
-		option_statusline(VALUE_ACCEPTED);
-	 	return(status);
-	    }
+    while (1) {
+	move(line, col);
+	response = LYgetch();
+	if (term_options || response == 7 || response == 3)
+	    response = '\n';
+	if (response != '\n' && response != '\r') {
+	    if (status == number)
+		status = 0;  /* go over the top and around */
+	    else
+		status++;
+	    addstr(choices[status]);
+	    refresh();
+	} else {
+	    /*
+	     *  Unhighlight selection.
+	     */
+	    move(line, col);
+	    standend();
+	    addstr(choices[status]);
+
+	    option_statusline(VALUE_ACCEPTED);
+	     return(status);
 	}
+    }
 }
 
-
 PRIVATE void terminate_options ARGS1(int,sig)
 {
-	term_options=TRUE;
-	/* Reassert the AST */
-	signal(SIGINT, terminate_options);
+    term_options=TRUE;
+    /* Reassert the AST */
+    signal(SIGINT, terminate_options);
 #ifdef VMS
-        /* refresh the screen to get rid of the "interrupt" message */
-	if (!dump_output_immediately) {
+    /* refresh the screen to get rid of the "interrupt" message */
+    if (!dump_output_immediately) {
+	clearok(curscr, TRUE);
+	refresh();
+    }
+#endif /* VMS */
+}
+
+/*
+ *  Multi-Bookmark On-Line editing support. - FMG & FM
+ */
+PUBLIC void edit_bookmarks NOARGS
+{
+    int response = 0, def_response = 0, ch;
+    int MBM_current = 1;
+#define	MULTI_OFFSET 8
+    int a; /* misc counter */
+    char MBM_tmp_line[256]; /* buffer for LYgetstr */
+    
+    /*
+     *  We need (MBM_V_MAXFILES + MULTI_OFFSET) lines to display
+     *  the whole list at once.  Otherwise break it up into two
+     *  segments.  We know it won't be less than that because
+     *  'o'ptions needs 23-24 at LEAST.
+     */
+    term_options = FALSE;
+    signal(SIGINT, terminate_options);
+
+draw_bookmark_list:
+    clear(); 
+    move(0, 5);
+    if (bold_H1 || bold_headers)
+        start_bold();
+    if (LYlines < (MBM_V_MAXFILES + MULTI_OFFSET))
+	printw("Editing Bookmark DESCRIPTION and FILEPATH (%d of 2)",
+		MBM_current);
+    else
+        printw("         Editing Bookmark DESCRIPTION and FILEPATH");
+    if (bold_H1 || bold_headers)
+        stop_bold();
+
+    if (LYlines < (MBM_V_MAXFILES + MULTI_OFFSET)) {
+	for (a = ((MBM_V_MAXFILES/2 + 1) * (MBM_current - 1));
+                      a <= ((float)MBM_V_MAXFILES/2 * MBM_current); a++) {
+	    move((3 + a) - ((MBM_V_MAXFILES/2 + 1)*(MBM_current - 1)), 5);
+	    printw("%c : %s", (a + 'A'),
+		   (!MBM_A_subdescript[a] ? "" : MBM_A_subdescript[a]));
+	    move((3 + a) - ((MBM_V_MAXFILES/2 + 1)*(MBM_current - 1)), 35);
+	    printw("| %s",
+		   (!MBM_A_subbookmark[a] ? "" : MBM_A_subbookmark[a]));
+        }
+    } else {
+	for (a = 0; a <= MBM_V_MAXFILES; a++) {
+	    move(3 + a, 5);
+	    printw("%c : %s", (a + 'A'),
+		   (!MBM_A_subdescript[a] ? "" : MBM_A_subdescript[a]));
+	    move(3 + a, 35);
+	    printw("| %s",
+		   (!MBM_A_subbookmark[a] ? "" : MBM_A_subbookmark[a]));
+	}
+    }
+
+    /*
+     *  Only needed when we have 2 screens.
+     */
+    if (LYlines < MBM_V_MAXFILES + MULTI_OFFSET) {
+       move((LYlines - 4), 0);
+       start_reverse();
+       addstr(MULTIBOOKMARKS_MOVE);
+       stop_reverse();
+    }
+
+    move((LYlines - 3), 0);
+    if (!no_option_save) {
+        addstr("'");
+	standout();
+	addstr(">");
+	standend();
+	addstr("'");
+	addstr(TO_SAVE_SEGMENT);
+    }
+    addstr(OR_SEGMENT);
+    addstr("'");
+    standout();
+    addstr("^G");
+    standend();
+    addstr("'");
+    addstr(TO_RETURN_SEGMENT);
+
+    while (!term_options &&
+           !LYisNonAlnumKeyname(response, LYK_PREV_DOC) &&
+	   response != 7 && response != 3 &&
+	   response != '>') {
+
+	move((LYlines - 2), 0);
+	start_reverse();
+	addstr("Letter: ");
+	stop_reverse();
+
+	refresh();
+        response = (def_response ? def_response : LYgetch());
+	def_response = 0;
+
+	/*
+	 *  Check for a cancel.
+	 */
+	if (term_options ||
+	    response == 7 || response == 3 ||
+	    LYisNonAlnumKeyname(response, LYK_PREV_DOC))
+	    continue;
+
+	/*
+	 *  Check for a save.
+	 */
+	if (response == '>') {
+	    if (!no_option_save) {
+		option_statusline(SAVING_OPTIONS);
+		if (save_rc())
+		    option_statusline(OPTIONS_SAVED);
+		else 
+		    HTAlert(OPTIONS_NOT_SAVED);
+	    } else {
+		option_statusline(R_TO_RETURN_TO_LYNX);
+		/*
+		 *  Change response so that we don't exit
+		 *  the options menu.
+		 */
+		response = ' ';
+	    }
+	    continue;
+	}
+
+	/*
+	 *  Check for a refresh.
+	 */
+	if (LYisNonAlnumKeyname(response, LYK_REFRESH)) {
 	    clearok(curscr, TRUE);
-	    refresh();
+	    continue;
 	}
-#endif /* VMS */
+
+	/*
+	 *  Move between the screens - if we can't show it all at once.
+	 */
+	if ((response == ']' ||
+	     LYisNonAlnumKeyname(response, LYK_NEXT_PAGE)) &&
+	    LYlines < (MBM_V_MAXFILES + MULTI_OFFSET)) {
+	    MBM_current++;
+	    if (MBM_current >= 3)
+		MBM_current = 1;
+	    goto draw_bookmark_list;
+	}
+	if ((response == '[' ||
+	     LYisNonAlnumKeyname(response, LYK_PREV_PAGE)) &&
+	    LYlines < (MBM_V_MAXFILES + MULTI_OFFSET)) {
+	    MBM_current--;
+	    if (MBM_current <= 0)
+		MBM_current = 2;
+	    goto draw_bookmark_list;
+	}
+
+	/*
+	 *  Instead of using 26 case statements, we set up
+         *  a scan through the letters and edit the lines
+         *  that way.
+         */
+	for (a = 0; a <= MBM_V_MAXFILES; a++) {
+	    if ((TOUPPER(response) - 'A') == a) {
+		if (LYlines < (MBM_V_MAXFILES + MULTI_OFFSET)) {
+		    if (MBM_current == 1 && a > (MBM_V_MAXFILES/2)) {
+		        MBM_current = 2;
+		        def_response = response;
+			goto draw_bookmark_list;
+		    }
+		    if (MBM_current == 2 && a < (MBM_V_MAXFILES/2)) {
+		        MBM_current = 1;
+		        def_response = response;
+			goto draw_bookmark_list;
+		    }
+		}
+		option_statusline(ACCEPT_DATA);
+
+		if (a > 0) {
+		    standout();
+		    if (LYlines < (MBM_V_MAXFILES + MULTI_OFFSET))
+    		        move(
+			 (3 + a) - ((MBM_V_MAXFILES/2 + 1)*(MBM_current - 1)),
+			     9);
+		    else
+    		        move((3 + a), 9);
+		    strcpy(MBM_tmp_line,
+    		           (!MBM_A_subdescript[a] ?
+			   		       "" : MBM_A_subdescript[a]));
+		    ch = LYgetstr(MBM_tmp_line, VISIBLE,
+	    		          sizeof(MBM_tmp_line), NORECALL);
+		    standend();
+
+		    if (strlen(MBM_tmp_line) < 1) {
+		        FREE(MBM_A_subdescript[a]);
+		    } else {
+		        StrAllocCopy(MBM_A_subdescript[a], MBM_tmp_line);
+		    }
+		    if (LYlines < (MBM_V_MAXFILES + MULTI_OFFSET))
+    			move(
+			 (3 + a) - ((MBM_V_MAXFILES/2 + 1)*(MBM_current - 1)),
+			     5);
+		    else
+    			move((3 + a), 5);
+    		    printw("%c : %s", (a + 'A'),
+    			   (!MBM_A_subdescript[a] ?
+			       		"" : MBM_A_subdescript[a]));
+		    clrtoeol();
+	   	    refresh();
+		}
+
+		if (LYlines < (MBM_V_MAXFILES + MULTI_OFFSET))
+    		    move((3 + a) - ((MBM_V_MAXFILES/2 + 1)*(MBM_current - 1)),
+		    	 35);
+		else
+    		    move((3 + a), 35);
+    		printw("| ");
+
+		standout();
+		strcpy(MBM_tmp_line,
+    		       (!MBM_A_subbookmark[a] ? "" : MBM_A_subbookmark[a]));
+		ch = LYgetstr(MBM_tmp_line, VISIBLE,
+	    		      sizeof(MBM_tmp_line), NORECALL);
+		standend();
+
+		if (*MBM_tmp_line == '\0') {
+		    if (a == 0)
+		        StrAllocCopy(MBM_A_subbookmark[a], bookmark_page);
+		    else
+		        FREE(MBM_A_subbookmark[a]);
+		} else {
+		    StrAllocCopy(MBM_A_subbookmark[a], MBM_tmp_line);
+		    if (a == 0) {
+		        StrAllocCopy(bookmark_page, MBM_A_subbookmark[a]);
+		    }
+		}
+		if (LYlines < (MBM_V_MAXFILES + MULTI_OFFSET))
+    		    move((3 + a) - ((MBM_V_MAXFILES/2 + 1)*(MBM_current-1)),
+		    	 35);
+		else
+    		    move((3 + a), 35);
+    		printw("| %s", (!MBM_A_subbookmark[a] ?
+						   "" : MBM_A_subbookmark[a]));
+	   	clrtoeol();
+		move(LYlines-1, 0);
+		clrtoeol();
+		break;
+	    }
+	}  /* end for */
+    } /* end while */
+
+    term_options = FALSE;
+    signal(SIGINT, cleanup_sig);
 }
diff --git a/src/LYOptions.h b/src/LYOptions.h
index f154ebec..f89159aa 100644
--- a/src/LYOptions.h
+++ b/src/LYOptions.h
@@ -7,7 +7,12 @@ extern void options NOPARAMS;
 /* values for options */
 #define L_EDITOR	 2
 #define L_DISPLAY	 3
+
 #define L_HOME		 4
+#define C_MULTI		24
+#define B_BOOK		34
+#define C_DEFAULT	50
+
 #define L_FTPSTYPE	 5
 #define L_MAIL_ADDRESS	 6
 #define L_SSEARCH	 7
@@ -15,22 +20,28 @@ extern void options NOPARAMS;
 #define L_RAWMODE	 9
 #define L_LANGUAGE	10
 #define L_PREF_CHARSET	11
-#define L_VIKEYS	12
-#define L_EMACSKEYS	13
+
+#define L_BOOL_A	12
+#define B_VIKEYS	5
+#define C_VIKEYS	15
+#define B_EMACSKEYS	22
+#define C_EMACSKEYS	36
+#define B_SHOW_DOTFILES	44
+#define C_SHOW_DOTFILES	62
+
+#define L_SELECT_POPUPS 13
 #define L_KEYPAD	14 
 #define L_LINEED	15
 
 #ifdef DIRED_SUPPORT
 #define L_DIRED		16
-#define L_SHOW_DOTFILES	17
-#define L_USER_MODE	18
-#define L_USER_AGENT	19
-#define L_EXEC		20
-#else
-#define L_SHOW_DOTFILES	16
 #define L_USER_MODE	17
 #define L_USER_AGENT	18
 #define L_EXEC		19
+#else
+#define L_USER_MODE	16
+#define L_USER_AGENT	17
+#define L_EXEC		18
 #endif /* DIRED_SUPPORT */
 
 #endif /* LYOPTIONS_H */
diff --git a/src/LYPrint.c b/src/LYPrint.c
index 34c17363..8e14c938 100644
--- a/src/LYPrint.c
+++ b/src/LYPrint.c
@@ -83,19 +83,30 @@ PUBLIC int printfile ARGS1(document *,newdoc)
     WWWDoc.address = newdoc->address;
     WWWDoc.post_data = newdoc->post_data;
     WWWDoc.post_content_type = newdoc->post_content_type;
+    WWWDoc.bookmark = newdoc->bookmark;
     WWWDoc.isHEAD = newdoc->isHEAD;
     if(!HTLoadAbsolute(&WWWDoc))
         return(NOT_FOUND);
   
-    StrAllocCopy(sug_filename, newdoc->address); /* must be freed */
+    /*
+     *  Load the suggested filename string. - FM
+     */
+    if (HText_getSugFname() != NULL)
+        StrAllocCopy(sug_filename, HText_getSugFname()); /* must be freed */
+    else
+        StrAllocCopy(sug_filename, newdoc->address); /* must be freed */
 
     /*
      *  Get the number of lines in the file.
      */
     if ((cp = (char *)strstr(link_info, "lines=")) != NULL) {
-	/* terminate prev string here */
+	/*
+	 *  Terminate prev string here.
+	 */
 	*cp = '\0';
-        /* number of characters in "lines=" */
+        /*
+	 *  Number of characters in "lines=".
+	 */
 	cp += 6;
 
         lines_in_file = atoi(cp);
@@ -145,7 +156,7 @@ PUBLIC int printfile ARGS1(document *,newdoc)
 	retry:	strcpy(filename, sug_filename);  /* add suggestion info */
 		/* make the sug_filename conform to system specs */
 		change_sug_filename(filename);
-		if ((len = strlen(filename)) > 4) {
+		if (!(HTisDocumentSource()) && (len = strlen(filename)) > 4) {
 		    len -= 5;
 		    if (!strcasecomp((filename + len), ".html")) {
 		        filename[len] = '\0';
@@ -330,6 +341,18 @@ PUBLIC int printfile ARGS1(document *,newdoc)
 		    goto retry;
                 }
 
+		if (HTisDocumentSource()) {
+		    /*
+		     *  Added the document's URL as a BASE tag
+		     *  to the top of the file.  May create
+		     *  technically invalid HTML, but will help
+		     *  get any partial or relative URLs resolved
+		     *  properly if no BASE tag is present to
+		     *  replace it. - FM
+		     */
+		    fprintf(outfile_fp, "<BASE HREF=\"%s\">\n",
+		    			newdoc->address);
+		}
 		print_wwwfile_to_fd(outfile_fp,0);
 		if (keypad_mode)
 		    printlist(outfile_fp,FALSE);
@@ -376,13 +399,41 @@ PUBLIC int printfile ARGS1(document *,newdoc)
 		} else {
 		    remove(tempfile);   /* remove duplicates */
 		}
+		if (HTisDocumentSource()) {
+		    if ((len = strlen(tempfile)) > 3) {
+		        len -= 4;
+			if (!strcasecomp((filename + len), ".txt")) {
+			    filename[len] = '\0';
+			    strcat(filename, ".html");
+			}
+		    }
+		} else if ((len = strlen(tempfile)) > 4) {
+		    len -= 5;
+		    if (!strcasecomp((filename + len), ".html")) {
+		        filename[len] = '\0';
+			strcat(filename, ".txt");
+		    }
+		}
 		if((outfile_fp = fopen(tempfile, "w")) == NULL) {
 		    HTAlert(UNABLE_TO_OPEN_TEMPFILE);
 		    break;
 		}
 
 		/* write the contents to a temp file */
-		fprintf(outfile_fp, "X-URL: %s\n", newdoc->address);
+		if (HTisDocumentSource()) {
+		    /*
+		     *  Added the document's URL as a BASE tag to
+		     *  the top of the message body.  May create
+		     *  technically invalid HTML, but will help
+		     *  get any partial or relative URLs resolved
+		     *  properly if no BASE tag is present to
+		     *  replace it. - FM
+		     */
+		    fprintf(outfile_fp, "<BASE HREF=\"%s\">\n\n",
+		    			newdoc->address);
+		} else {
+		    fprintf(outfile_fp, "X-URL: %s\n\n", newdoc->address);
+		}
 		print_wwwfile_to_fd(outfile_fp, 0);
 		if (keypad_mode)
 		    printlist(outfile_fp, FALSE);
@@ -410,9 +461,45 @@ PUBLIC int printfile ARGS1(document *,newdoc)
 			break;
 		}
 		
-		fprintf(outfile_fp, "X-URL: %s\n", newdoc->address);
+		if (HTisDocumentSource()) {
+		    /*
+		     *  Add Content-Type, Content-Location, and
+		     *  Content-Base headers for HTML source. - FM
+		     *  Also add Mime-Version header. - HM
+		     */
+		    fprintf(outfile_fp, "Mime-Version: 1.0\n");
+		    fprintf(outfile_fp, "Content-Type: text/html");
+		    if (HTLoadedDocumentCharset() != NULL) {
+		        fprintf(outfile_fp, "; charset=%s\n",
+					    HTLoadedDocumentCharset());
+		    } else {
+		        fprintf(outfile_fp, "\n");
+		    }
+		    fprintf(outfile_fp, "Content-Location: %s\n",
+		    			newdoc->address);
+		    fprintf(outfile_fp, "Content-Base: %s\n",
+		    			newdoc->address);
+		} else {
+		    /*
+		     *  Add an X-URL header for rendered HTML or
+		     *  plain text. - FM
+		     */
+		    fprintf(outfile_fp, "X-URL: %s\n", newdoc->address);
+		}
 		fprintf(outfile_fp, "To: %s\nSubject:%s\n\n",
 				     user_response, sug_filename);
+		if (HTisDocumentSource()) {
+		    /*
+		     *  Added the document's URL as a BASE tag to
+		     *  the top of the message body.  May create
+		     *  technically invalid HTML, but will help
+		     *  get any partial or relative URLs resolved
+		     *  properly if no BASE tag is present to
+		     *  replace it. - FM
+		     */
+		    fprintf(outfile_fp, "<BASE HREF=\"%s\">\n\n",
+		    			newdoc->address);
+		}
 		print_wwwfile_to_fd(outfile_fp, 0);
 		if (keypad_mode)
 		    printlist(outfile_fp, FALSE);
@@ -464,6 +551,18 @@ PUBLIC int printfile ARGS1(document *,newdoc)
 		signal(SIGINT, SIG_IGN);
 #endif /* !VMS */
 
+		if (HTisDocumentSource()) {
+		    /*
+		     *  Added the document's URL as a BASE tag
+		     *  to the top of the file.  May create
+		     *  technically invalid HTML, but will help
+		     *  get any partial or relative URLs resolved
+		     *  properly if no BASE tag is present to
+		     *  replace it. - FM
+		     */
+		    fprintf(outfile_fp, "<BASE HREF=\"%s\">\n\n",
+		    			newdoc->address);
+		}
 		print_wwwfile_to_fd(outfile_fp, 0);
 		if (keypad_mode)
 		    printlist(outfile_fp, FALSE);
@@ -538,6 +637,18 @@ PUBLIC int printfile ARGS1(document *,newdoc)
 		    break;
                 }
 
+		if (HTisDocumentSource()) {
+		    /*
+		     *  Added the document's URL as a BASE tag
+		     *  to the top of the file.  May create
+		     *  technically invalid HTML, but will help
+		     *  get any partial or relative URLs resolved
+		     *  properly if no BASE tag is present to
+		     *  replace it. - FM
+		     */
+		    fprintf(outfile_fp, "<BASE HREF=\"%s\">\n\n",
+		    			newdoc->address);
+		}
 		print_wwwfile_to_fd(outfile_fp, 0);
 		if (keypad_mode)
 		    printlist(outfile_fp, FALSE);
diff --git a/src/LYReadCFG.c b/src/LYReadCFG.c
index bc9f6c0e..686eb13e 100644
--- a/src/LYReadCFG.c
+++ b/src/LYReadCFG.c
@@ -12,6 +12,7 @@
 #include "LYCgi.h"
 #include "LYCurses.h"
 #include "LYSignal.h"
+#include "LYBookmark.h"
 
 #ifdef DIRED_SUPPORT
 #include "LYLocal.h"
@@ -590,7 +591,19 @@ PUBLIC void read_cfg ARGS1(
 		   user_mode = ADVANCED_MODE;
 
 	} else if(!strncasecomp(buffer,"DEFAULT_BOOKMARK_FILE:",22)) {
-		StrAllocCopy(bookmark_page,buffer+22);
+		StrAllocCopy(bookmark_page, buffer+22);
+		StrAllocCopy(BookmarkPage, bookmark_page);
+		StrAllocCopy(MBM_A_subbookmark[0], bookmark_page);
+		StrAllocCopy(MBM_A_subdescript[0], MULTIBOOKMARKS_DEFAULT);
+
+	} else if(!strncasecomp(buffer,"MULTI_BOOKMARK_SUPPORT:",23)) {
+		LYMultiBookmarks = is_true(buffer+23);
+
+	} else if(!strncasecomp(buffer,"BLOCK_MULTI_BOOKMARKS:",22)) {
+		LYMBMBlocked = is_true(buffer+22);
+
+	} else if(!strncasecomp(buffer,"ADVANCED_MULTI_BOOKMARKS:",25)) {
+		LYMBMAdvanced = is_true(buffer+25);
 
 	} else if(!system_editor && 
 		  !strncasecomp(buffer,"DEFAULT_EDITOR:",15)) {
@@ -957,6 +970,9 @@ PUBLIC void read_cfg ARGS1(
 		    HTNewsChunkSize = HTNewsMaxChunk;
 		}
 
+	} else if(!strncasecomp(buffer,"USE_SELECT_POPUPS:",17)) {
+		LYSelectPopups = is_true(buffer+17);
+
 #if defined(VMS) && defined(VAXC) && !defined(__DECC)
 	} else if (!strncasecomp(buffer, "DEFAULT_VIRTUAL_MEMORY_SIZE:", 28)) {
 		HTVirtualMemorySize = atoi(buffer+28);
diff --git a/src/LYShowInfo.c b/src/LYShowInfo.c
index fb6e5c5e..9566967e 100644
--- a/src/LYShowInfo.c
+++ b/src/LYShowInfo.c
@@ -11,6 +11,7 @@
 #include "LYShowInfo.h"
 #include "LYSignal.h"
 #include "LYCharUtils.h"
+#include "GridText.h"
 
 #include "LYLeaks.h"
 
@@ -97,9 +98,9 @@ PUBLIC int showinfo ARGS4(
     fprintf(fp0,"<h2>%s Version %s</h2>\n", LYNX_NAME, LYNX_VERSION);
 
 #ifdef DIRED_SUPPORT
-    if (lynx_edit_mode) {
+    if (lynx_edit_mode && nlinks > 0) {
 	fprintf(fp0,
-	   	"<h2>Directory that you are currently viewing</h2>\n<pre>");
+	 "<h2>Directory that you are currently viewing</h2>\n<pre>");
 
 	cp = doc->address;
 	if (!strncmp(cp, "file://localhost", 16)) 
@@ -109,8 +110,8 @@ PUBLIC int showinfo ARGS4(
 	strcpy(temp, cp);
 	HTUnEscape(temp);
 
-	fprintf(fp0,"   Name:  %s\n", temp);
-	fprintf(fp0,"    URL:  %s\n", doc->address);
+	fprintf(fp0,"   <em>Name:</em>  %s\n", temp);
+	fprintf(fp0,"   <em> URL:</em>  %s\n", doc->address);
 
 	cp = links[doc->link].lname;
 	if (!strncmp(cp, "file://localhost", 16)) 
@@ -126,16 +127,18 @@ PUBLIC int showinfo ARGS4(
 	    char modes[80];
 	    if (((dir_info.st_mode) & S_IFMT) == S_IFDIR) {
 		fprintf(fp0,
-		 	"\nDirectory that you have currently selected\n\n");
+		 "\nDirectory that you have currently selected\n\n");
 	    } else if (((dir_info.st_mode) & S_IFMT) == S_IFREG) {
-		fprintf(fp0, "\nFile that you have currently selected\n\n");
+		fprintf(fp0, 
+		      "\nFile that you have currently selected\n\n");
 	    } else if (((dir_info.st_mode) & S_IFMT) == S_IFLNK) {
 		fprintf(fp0,
-		      "\nSymbolic link that you have currently selected\n\n");
+	     "\nSymbolic link that you have currently selected\n\n");
 	    } else {
-		fprintf(fp0, "\nItem that you have currently selected\n\n");
+		fprintf(fp0,
+		      "\nItem that you have currently selected\n\n");
 	    }
-	    fprintf(fp0,"       Full name:  %s\n", temp);
+	    fprintf(fp0,"       <em>Full name:</em>  %s\n", temp);
 	    if (((dir_info.st_mode) & S_IFMT) == S_IFLNK) {
 		char buf[1025];
 		int buf_size;
@@ -145,16 +148,16 @@ PUBLIC int showinfo ARGS4(
 		} else {
 		    strcpy(buf, "Unable to follow link");
 		}
-		fprintf(fp0, "  Points to file:  %s\n", buf);
+		fprintf(fp0, "  <em>Points to file:</em>  %s\n", buf);
 	    }
 	    pw = getpwuid(dir_info.st_uid);
 	    if (pw)
-	        fprintf(fp0, "   Name of owner:  %s\n", pw->pw_name);
+	        fprintf(fp0, "   <em>Name of owner:</em>  %s\n", pw->pw_name);
 	    grp = getgrgid(dir_info.st_gid);
 	    if (grp && grp->gr_name)
-	        fprintf(fp0, "      Group name:  %s\n", grp->gr_name);
+	        fprintf(fp0, "      <em>Group name:</em>  %s\n", grp->gr_name);
 	    if (((dir_info.st_mode) & S_IFMT) == S_IFREG) {
-		sprintf(temp, "       File size:  %ld (bytes)\n",
+		sprintf(temp, "       <em>File size:</em>  %ld (bytes)\n",
 		 	      (long)dir_info.st_size);
 		fprintf(fp0, "%s", temp);
 	    }
@@ -162,16 +165,16 @@ PUBLIC int showinfo ARGS4(
 	     *  Include date and time information.
 	     */
 	    cp = ctime(&dir_info.st_ctime);
-	    fprintf(fp0, "   Creation date:  %s", cp);
+	    fprintf(fp0, "   <em>Creation date:</em>  %s", cp);
 
 	    cp = ctime(&dir_info.st_mtime);	      
-	    fprintf(fp0, "   Last modified:  %s", cp);
+	    fprintf(fp0, "   <em>Last modified:</em>  %s", cp);
 
 	    cp = ctime(&dir_info.st_atime);
-	    fprintf(fp0, "   Last accessed:  %s\n", cp);
+	    fprintf(fp0, "   <em>Last accessed:</em>  %s\n", cp);
 
-	    fprintf(fp0, "   Access Permissions\n");
-	    fprintf(fp0, "      Owner:  ");
+	    fprintf(fp0, "   <em>Access Permissions</em>\n");
+	    fprintf(fp0, "      <em>Owner:</em>  ");
 	    modes[0] = '\0';
 	    modes[1] = '\0';   /* In case there are no permissions */
 	    modes[2] = '\0';
@@ -190,7 +193,7 @@ PUBLIC int showinfo ARGS4(
 	    }
 	    fprintf(fp0, "%s\n", (char *)&modes[2]); /* Skip leading ', ' */
 
-	    fprintf(fp0, "      Group:  ");
+	    fprintf(fp0, "      <em>Group:</em>  ");
 	    modes[0] = '\0';
 	    modes[1] = '\0';   /* In case there are no permissions */
 	    modes[2] = '\0';
@@ -209,7 +212,7 @@ PUBLIC int showinfo ARGS4(
 	    }
 	    fprintf(fp0, "%s\n", (char *)&modes[2]);  /* Skip leading ', ' */
 
-	    fprintf(fp0, "      World:  ");
+	    fprintf(fp0, "      <em>World:</em>  ");
 	    modes[0] = '\0';
 	    modes[1] = '\0';   /* In case there are no permissions */
 	    modes[2] = '\0';
@@ -232,21 +235,33 @@ PUBLIC int showinfo ARGS4(
     } else {
 #endif /* DIRED_SUPPORT */
 
-    fprintf(fp0, "<h2>File that you are currently viewing</h2>\n<dl compact>");
+    fprintf(fp0,
+       "<h2>File that you are currently viewing</h2>\n<dl compact>");
 
     StrAllocCopy(Title, doc->title);
     LYEntify(&Title, TRUE);
-    fprintf(fp0,"<dt>Linkname: %s\n", Title);
+    fprintf(fp0,"<dt><em>Linkname:</em> %s\n", Title);
 
     StrAllocCopy(Address, doc->address);
     LYEntify(&Address, FALSE);
-    fprintf(fp0, "<dt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;URL: %s\n", Address);
+    fprintf(fp0,
+    	    "<dt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<em>URL:</em> %s\n", Address);
+
+    if ((cp = HText_getServer()) != NULL && *cp != '\0')
+        fprintf(fp0, "<dt><em>&nbsp;&nbsp;Server:</em> %s\n", cp);
+
+    if ((cp = HText_getDate()) != NULL && *cp != '\0')
+        fprintf(fp0, "<dt><em>&nbsp;&nbsp;&nbsp;&nbsp;Date:</em> %s\n", cp);
+
+    if ((cp = HText_getLastModified()) != NULL && *cp != '\0')
+        fprintf(fp0, "<dt><em>Last Mod:</em> %s\n", cp);
 
     if (doc->post_data) {
         StrAllocCopy(Address, doc->post_data);
 	LYEntify(&Address, FALSE);
-	fprintf(fp0, "<dt>Post Data: %s\n", Address);
-	fprintf(fp0, "<dt>Post Content Type: %s\n", doc->post_content_type);
+	fprintf(fp0, "<dt><em>Post Data:</em> <xmp>%s</xmp>\n", Address);
+	fprintf(fp0,
+	     "<dt><em>Post Content Type:</em> %s\n", doc->post_content_type);
     }
 
     if (owner_address) {
@@ -255,34 +270,41 @@ PUBLIC int showinfo ARGS4(
     } else {
         StrAllocCopy(Address, "None");
     }
-    fprintf(fp0, "<dt>Owner(s): %s\n", Address);
+    fprintf(fp0, "<dt><em>Owner(s):</em> %s\n", Address);
 
-    fprintf(fp0, "<dt>&nbsp;&nbsp;&nbsp;&nbsp;size: %d lines\n", size_of_file);
+    fprintf(fp0,
+	"<dt>&nbsp;&nbsp;&nbsp;&nbsp;<em>size:</em> %d lines\n", size_of_file);
 
-    fprintf(fp0, "<dt>&nbsp;&nbsp;&nbsp;&nbsp;mode: %s\n",
+    fprintf(fp0, "<dt>&nbsp;&nbsp;&nbsp;&nbsp;<em>mode:</em> %s\n",
 		 (lynx_mode == FORMS_LYNX_MODE ? "forms mode" : "normal"));
 
     fprintf(fp0, "</dl>\n");  /* end of list */
 
     if (nlinks > 0) {
 	fprintf(fp0,
-	        "<h2>Link that you currently have selected</h2>\n<dl compact>");
+      "<h2>Link that you currently have selected</h2>\n<dl compact>");
 	StrAllocCopy(Title, links[doc->link].hightext);
 	LYEntify(&Title, TRUE);
-	fprintf(fp0, "<dt>Linkname: %s\n", Title);
+	fprintf(fp0, "<dt><em>Linkname:</em> %s\n", Title);
 	if (lynx_mode == FORMS_LYNX_MODE &&
 	    links[doc->link].type == WWW_FORM_LINK_TYPE) {
 	    if (links[doc->link].form->submit_method) {
 	        int method = links[doc->link].form->submit_method;
-		fprintf(fp0, "<dt>&nbsp;&nbsp;Method: %s\n",
+	        char *enctype = links[doc->link].form->submit_enctype;
+
+		fprintf(fp0, "<dt>&nbsp;&nbsp;<em>Method:</em> %s\n",
 			     (method == URL_POST_METHOD) ? "POST" :
 			     (method == URL_MAIL_METHOD) ? "(email)" :
 							   "GET");
+		fprintf(fp0, "<dt>&nbsp;<em>Enctype:</em> %s\n",
+			     (enctype &&
+			      *enctype ?
+			       enctype : "application/x-www-form-urlencoded"));
 	    }
 	    if (links[doc->link].form->submit_action) {
 	        StrAllocCopy(Address, links[doc->link].form->submit_action);
 		LYEntify(&Address, FALSE);
-	        fprintf(fp0, "<dt>&nbsp;&nbsp;Action: %s\n", Address);
+	        fprintf(fp0, "<dt>&nbsp;&nbsp;<em>Action:</em> %s\n", Address);
 	    }
 	    if (!(links[doc->link].form->submit_method &&
 		links[doc->link].form->submit_action)) {
@@ -295,7 +317,7 @@ PUBLIC int showinfo ARGS4(
 	    } else {
 	        StrAllocCopy(Title, "");
 	    }
-	    fprintf(fp0, "<dt>Filename: %s\n", Title);
+	    fprintf(fp0, "<dt><em>Filename:</em> %s\n", Title);
 	}
 	fprintf(fp0, "</dl>\n");  /* end of list */
 
diff --git a/src/LYStructs.h b/src/LYStructs.h
index 1be79aed..6ef5d8a7 100644
--- a/src/LYStructs.h
+++ b/src/LYStructs.h
@@ -33,15 +33,9 @@ typedef struct _document {
    int    link;
    int    line;
    BOOL   isHEAD;
+   char * bookmark;
 } document;
 
-#ifdef DIRED_SUPPORT
-typedef struct _taglink {
-   char *name;
-   struct _taglink *next;
-} taglink;
-#endif
-
 #ifndef HTFORMS_H
 #include "HTForms.h" 
 #endif /* HTFORMS_H */
@@ -54,6 +48,7 @@ typedef struct _histstruct {
     int    link;
     int    page;
     BOOL   isHEAD;
+    char * bookmark;
 } histstruct;
 
 extern histstruct history[MAXHIST];
diff --git a/src/LYUtils.c b/src/LYUtils.c
index d775f493..d1cf11f7 100644
--- a/src/LYUtils.c
+++ b/src/LYUtils.c
@@ -3,6 +3,7 @@
 #include "HTParse.h"
 #include "HTAccess.h"
 #include "HTCJK.h"
+#include "HTAlert.h"
 #include "LYCurses.h"
 #include "LYUtils.h"
 #include "LYStrings.h"
@@ -1431,14 +1432,24 @@ PUBLIC void change_sug_filename ARGS1(char *,fname)
 
      /** Replace all but the last period with _'s, or second **/
      /** to last if last is followed by a terminal Z or z,   **/
+     /** or GZ or gz,					     **/
      /** e.g., convert foo.tar.Z to                          **/
      /**               foo.tar_Z                             **/
+     /**   or, convert foo.tar.gz to                         **/
+     /**               foo.tar-gz                            **/
      j = strlen(fname) - 1;
      if ((dot = strrchr(fname, '.')) != NULL) {
-	  if (((fname[j] == 'Z' || fname[j] == 'z') && fname[j-1] == '.') &&
-	      (((cp = strchr(fname, '.')) != NULL) && cp < dot)) {
-	       *dot = '_';
-	       dot = strrchr(fname, '.');
+	  if (TOUPPER(fname[j]) == 'Z') {
+	      if ((fname[j-1] == '.') &&
+	          (((cp = strchr(fname, '.')) != NULL) && cp < dot)) {
+		  *dot = '_';
+		  dot = strrchr(fname, '.');
+	      } else if (((TOUPPER(fname[j-1]) == 'G') &&
+	      		  fname[j-2] == '.') &&
+			 (((cp = strchr(fname, '.')) != NULL) && cp < dot)) {
+		  *dot = '-';
+		  dot = strrchr(fname, '.');
+	      }
 	  }
 	  cp = fname;
 	  while ((cp = strchr(cp, '.')) != NULL && cp < dot)
@@ -1592,6 +1603,8 @@ PRIVATE char *restrict_name[] = {
        "editor"        ,
        "shell"         ,
        "bookmark"      ,
+       "multibook"     ,
+       "bookmark_exec" ,
        "option_save"   ,
        "print"         ,
        "download"      ,
@@ -1599,7 +1612,6 @@ PRIVATE char *restrict_name[] = {
        "exec"          ,
        "lynxcgi"       ,
        "exec_frozen"   ,
-       "bookmark_exec" ,
        "goto"          ,
        "jump"          ,
        "file_url"      ,
@@ -1633,6 +1645,8 @@ PRIVATE BOOLEAN *restrict_flag[] = {
        &no_editor   ,
        &no_shell    ,
        &no_bookmark ,
+       &no_multibook ,
+       &no_bookmark_exec,
        &no_option_save,
        &no_print    ,
        &no_download ,
@@ -1640,7 +1654,6 @@ PRIVATE BOOLEAN *restrict_flag[] = {
        &no_exec     ,
        &no_lynxcgi  ,
        &exec_frozen ,
-       &no_bookmark_exec,
        &no_goto     ,
        &no_jump     ,
        &no_file_url ,
@@ -2132,7 +2145,7 @@ PUBLIC BOOLEAN LYExpandHostForURL ARGS3(
 {
     char DomainPrefix[80], *StartP, *EndP;
     char DomainSuffix[80], *StartS, *EndS;
-    char *Str = NULL, *StrColon = NULL;
+    char *Str = NULL, *StrColon = NULL, *MsgStr = NULL;
     char *Host = NULL, *HostColon = NULL;
     char *Path = NULL;
     struct hostent  *phost;
@@ -2176,9 +2189,16 @@ PUBLIC BOOLEAN LYExpandHostForURL ARGS3(
      *  Do a DNS test on the potential host field
      *  as presently trimmed. - FM
      */
+    if (LYCursesON) {
+        StrAllocCopy(MsgStr, "Looking up ");
+	StrAllocCat(MsgStr, Str);
+	StrAllocCat(MsgStr, " first.");
+	HTProgress(MsgStr);
+    }
     if ((phost = gethostbyname(Str)) != NULL) {
         GotHost = TRUE;
         FREE(Str);
+        FREE(MsgStr);
 	return GotHost;
     }
 
@@ -2232,15 +2252,36 @@ PUBLIC BOOLEAN LYExpandHostForURL ARGS3(
 	        isdigit(HostColon[1])) {
 		*HostColon = '\0';
 	    }
+	    if (LYCursesON) {
+ 	        StrAllocCopy(MsgStr, "Looking up ");
+ 		StrAllocCat(MsgStr, Host);
+ 		StrAllocCat(MsgStr, ", guessing...");
+ 		HTProgress(MsgStr);
+	    }
 	    GotHost = ((phost = gethostbyname(Host)) != NULL);
 	    if (HostColon != NULL) {
 	        *HostColon = ':';
 	    }
 	    if (GotHost == FALSE) {
+		/*
+		 *  Give the user chance to interrupt lookup cycles. - KW
+		 */
+		if (LYCursesON && HTCheckForInterrupt()) {
+		    if (TRACE) {
+			fprintf(stderr,
+	 "*** LYExpandHostForURL interrupted while %s failed to resolve\n",
+				Host);
+			    }
+		    FREE(Str);
+		    FREE(MsgStr);
+		    FREE(Host);
+		    return FALSE; /* We didn't find a valid name. */
+		}
+
 	        /*
 		**  Advance to the next suffix, or end of suffix list. - FM
 		*/
-		StartS = ((EndS == '\0') ? EndS : (EndS + 1));
+		StartS = ((*EndS == '\0') ? EndS : (EndS + 1));
 		while ((*StartS) && (WHITE(*StartS) || *StartS == ',')) {
 		    StartS++;	/* Skip whitespace and separators */
 		}
@@ -2256,7 +2297,7 @@ PUBLIC BOOLEAN LYExpandHostForURL ARGS3(
 	   /*
 	   **  Advance to the next prefix, or end of prefix list. - FM
 	   */
-	   StartP = ((EndP == '\0') ? EndP : (EndP + 1));
+	   StartP = ((*EndP == '\0') ? EndP : (EndP + 1));
 	   while ((*StartP) && (WHITE(*StartP) || *StartP == ',')) {
 	       StartP++;	/* Skip whitespace and separators */
 	   }
@@ -2290,6 +2331,7 @@ PUBLIC BOOLEAN LYExpandHostForURL ARGS3(
      *  Clean up and return the last test result. - FM
      */
     FREE(Str);
+    FREE(MsgStr);
     FREE(Host);
     return GotHost;
 }
@@ -2591,3 +2633,76 @@ putenv (string)
   return 0;
 }
 #endif /* NO_PUTENV */
+
+#ifdef VMS
+/*
+ *  This function appends fname to the home path and returns
+ *  the full path and filename in VMS syntax.  The fname
+ *  string can be just a filename, or include a subirectory
+ *  off the home directory, in which chase fname should
+ *  with "./" (e.g., ./BM/lynx_bookmarks.html). - FM
+ */
+PUBLIC void LYVMS_HomePathAndFilename ARGS3(
+	char *,		fbuffer,
+	int,		fbuffer_size,
+	char *,		fname)
+{
+    char *home = NULL;
+    char *temp = NULL;
+    int len;
+
+    /*
+     *  Make sure we have a buffer and string. - FM
+     */
+    if (!fbuffer)
+        return;
+    if (!(fname && *fname) || fbuffer_size < 1) {
+        fbuffer[0] = '\0';
+	return;
+    }
+
+    /*
+     *  Set up home string and length. - FM
+     */
+    StrAllocCopy(home, Home_Dir());
+    if (!(home && *home))
+        StrAllocCopy(home, "Error:");
+    len = fbuffer_size - strlen(home) - 1;
+    if (len < 0) {
+        len = 0;
+	home[fbuffer_size] = '\0';
+    }
+
+    /*
+     *  Check whether we have a subdirectory path or just a filename. - FM
+     */
+    if (!strncmp(fname, "./", 2)) {
+        /*
+	 *  We have a subdirectory path. - FM
+	 */
+	if (home[strlen(home)-1] == ']') {
+	    /*
+	     *  We got the home directory, so convert it to
+	     *  SHELL syntax and append subdirectory path,
+	     *  then convert that to VMS syntax. - FM
+	     */
+	    temp = (char *)calloc(1, (strlen(home) + strlen(fname) + 10));
+	    sprintf(temp, "%s%s", HTVMS_wwwName(home), (fname + 1));
+	    sprintf(fbuffer, "%.*s",
+	    	    (fbuffer_size - 1), HTVMS_name("", temp));
+	    FREE(temp);
+	} else {
+	    /*
+	     *  This will fail, but we need something in the buffer. - FM
+	     */
+	    sprintf(fbuffer,"%s%.*s", home, len, fname);
+	}
+    } else {
+        /*
+	 *  We have a file in the home directory. - FM
+	 */
+	sprintf(fbuffer,"%s%.*s", home, len, fname);
+    }
+    FREE(home);
+}
+#endif /* VMS */
diff --git a/src/LYUtils.h b/src/LYUtils.h
index d9fb84b6..7e91f071 100644
--- a/src/LYUtils.h
+++ b/src/LYUtils.h
@@ -47,7 +47,10 @@ extern BOOLEAN LYExpandHostForURL PARAMS((
 extern BOOLEAN LYAddSchemeForURL PARAMS((
 	char **AllocatedString, char *default_scheme));
 #ifdef VMS
-extern void Define_VMSLogical PARAMS((char *LogicalName, char *LogicalValue));
+extern void Define_VMSLogical PARAMS((
+	char *LogicalName, char *LogicalValue));
+extern void LYVMS_HomePathAndFilename PARAMS((
+	char *fbuffer, int fbuffer_size, char * fname));
 #endif /* VMS */
 
 /*	Whether or not the status line must be shown.
diff --git a/src/LYrcFile.c b/src/LYrcFile.c
index 504a0a57..9b8aaa39 100644
--- a/src/LYrcFile.c
+++ b/src/LYrcFile.c
@@ -6,6 +6,7 @@
 #include "LYStrings.h"
 #include "LYGlobalDefs.h"
 #include "LYCharSets.h"
+#include "LYBookmark.h"
 
 #include "LYLeaks.h"
 
@@ -16,6 +17,10 @@ PUBLIC void read_rc()
     FILE *fp;
     char *cp, *cp2;
     int number_sign;
+    char MBM_line[256];
+    int  MBM_counter, MBM_counter2;
+    char *MBM_cp, *MBM_cp2, *MBM_cp1;
+    int  MBM_i1, MBM_i2;
 
     /* make a name */
 #ifdef UNIX
@@ -113,18 +118,92 @@ PUBLIC void read_rc()
  	       StrAllocCopy(editor, cp);
 
 	/* bookmark file */
-	} else if ((cp=LYstrstr(line_buffer,"bookmark_file"))!=NULL &&
-		cp-line_buffer < number_sign) {
+	} else if ((cp = LYstrstr(line_buffer, "bookmark_file")) != NULL &&
+		    cp-line_buffer < number_sign) {
 
-	   if ((cp2 = (char *)strchr(cp,'=')) != NULL)
+	    if ((cp2 = (char *)strchr(cp,'=')) != NULL)
 		cp = cp2+1;
 
-	   while (isspace(*cp)) cp++;  /* get rid of spaces */
-
-	   StrAllocCopy(bookmark_page, cp);
+	    while (isspace(*cp))
+	        cp++;  /* get rid of spaces */
+
+            /*
+	     *  Since this is the "default" saveto, we save it.
+	     */
+	    StrAllocCopy(bookmark_page, cp);
+	    StrAllocCopy(BookmarkPage, cp);
+            StrAllocCopy(MBM_A_subbookmark[0], cp);
+            StrAllocCopy(MBM_A_subdescript[0], MULTIBOOKMARKS_DEFAULT);
+
+	} else if ((cp = LYstrstr(line_buffer, "multi_bookmark")) != NULL &&
+                   cp-line_buffer < number_sign) {
+            /*
+	     *  Found the root, now cycle through all the
+             *  possible spaces and match specific ones.
+             */
+            for (MBM_counter = 1;
+	         MBM_counter <= MBM_V_MAXFILES; MBM_counter++) {
+                sprintf(MBM_line, "multi_bookmark%c", (MBM_counter + 'A'));
+
+                if ((cp = LYstrstr(line_buffer, MBM_line)) != NULL &&
+                    cp-line_buffer < number_sign) {
+                    if ((MBM_cp1 = (char *)strchr(cp, '=')) == NULL) {
+                        break;
+                    } else {
+                        if ((MBM_cp2 = (char *)strchr(cp, ',')) == NULL) {
+                            break;
+                        } else {
+                            MBM_i2 = 0;
+			    /*
+			     *  skip over the '='.
+			     */
+                            MBM_cp1++;
+                            while (MBM_cp1 && MBM_cp1 != MBM_cp2) {
+                                /*
+				 *  Skip spaces.
+				 */
+                                if (isspace(*MBM_cp1)) {
+                                    MBM_cp1++;
+                                    continue;
+                                } else {
+                                    MBM_line[MBM_i2++] = *MBM_cp1++;
+				}
+			    }
+                            MBM_line[MBM_i2++] = '\0';
+
+                            StrAllocCopy(MBM_A_subbookmark[MBM_counter],
+			    		 MBM_line);
+
+                            /*
+			     *  Now get the description ',' and ->.
+			     */
+                            MBM_cp1 = (char *)strchr(cp, ',');
+
+                            MBM_i2 = 0;
+			    /*
+			     *  Skip over the ','.
+			     */
+                            MBM_cp1++;
+                            /*
+			     *  Eat spaces in front of description.
+			     */
+                            while (isspace(*MBM_cp1))
+                                MBM_cp1++;
+                            while (*MBM_cp1)
+                                MBM_line[MBM_i2++] = *MBM_cp1++;
+                            MBM_line[MBM_i2++] = '\0';
+
+                            StrAllocCopy(MBM_A_subdescript[MBM_counter],
+			    		 MBM_line);
+
+                            break;
+			}
+                    }
+		}
+	    }
 
 	/* personal_mail_address */
-	} else if ((cp=LYstrstr(line_buffer,"personal_mail_address"))!=NULL &&
+        } else if((cp=LYstrstr(line_buffer,"personal_mail_address"))!=NULL &&
 		cp-line_buffer < number_sign) {
 
 	   if ((cp2 = (char *)strchr(cp,'=')) != NULL)
@@ -223,9 +302,31 @@ PUBLIC void read_rc()
            else
               emacs_keys=FALSE;
 
+	/* multi bookmarks */
+        } else if ((cp = LYstrstr(line_buffer, "sub_bookmarks")) != NULL &&
+                   cp-line_buffer < number_sign) {
+
+           if ((cp2 = (char *)strchr(cp, '=')) != NULL)
+                cp = (cp2 + 1);
+
+           while (isspace(*cp))
+	       cp++;  /* get rid of spaces */
+
+           if (!strncmp(cp, "on", 2) || !strncmp(cp, "ON", 2))
+              LYMultiBookmarks = TRUE;
+           else if (!strncmp(cp, "standard", 8) ||
+                        !strncmp(cp, "STANDARD", 8)) {
+              LYMultiBookmarks = TRUE;
+              LYMBMAdvanced = FALSE;
+           } else if (!strncmp(cp, "advanced", 8) ||
+                          !strncmp(cp, "ADVANCED", 8)) {
+              LYMultiBookmarks = TRUE;
+              LYMBMAdvanced = TRUE;
+           } else
+              LYMultiBookmarks = FALSE;
 
 	} else if ((cp=LYstrstr(line_buffer,"keypad_mode"))!=NULL &&
-		cp-line_buffer < number_sign) {
+		   cp-line_buffer < number_sign) {
 
 	   if ((cp2 = (char *)strchr(cp,'=')) != NULL)
 		cp = cp2+1;
@@ -292,6 +393,20 @@ PUBLIC void read_rc()
 
 #endif /* DIRED_SUPPORT */
 
+	/* select popups */
+	} else if ((cp=LYstrstr(line_buffer,"select_popups")) != NULL &&
+		cp-line_buffer < number_sign) {
+
+	   if ((cp2 = (char * )strchr(cp,'=')) != NULL)
+		cp = cp2+1;
+
+	   while (isspace(*cp)) cp++;  /* get rid of spaces */
+	
+	   if (!strncasecomp(cp, "off", 3))
+	       LYSelectPopups = FALSE;
+	   else
+	       LYSelectPopups = TRUE;
+
 	} /* end of if */
 
     } /* end of while */
@@ -304,6 +419,7 @@ PUBLIC int save_rc ()
     char rcfile[256];
     FILE *fp;
     int i;
+    int MBM_c;
 
     /* make a name */
 #ifdef UNIX
@@ -392,7 +508,8 @@ PUBLIC int save_rc ()
 
 #if defined(EXEC_LINKS) || defined(EXEC_SCRIPTS) 
     /* local_exec */
-    fprintf(fp,"# if run all execution links is on then all local exection links will\n\
+    fprintf(fp,"\
+# if run all execution links is on then all local exection links will\n\
 # be executed when they are selected.\n\
 #\n\
 # WARNING - this is potentially VERY dangerous.  Since you may view\n\
@@ -402,10 +519,11 @@ PUBLIC int save_rc ()
 #           or compromise security.  This should only be set to on if you\n\
 #           are viewing trusted source information\n");
 
-    fprintf(fp,"run_all_execution_links=%s\n\n",(local_exec ? "on" : "off"));
+    fprintf(fp, "run_all_execution_links=%s\n\n",(local_exec ? "on" : "off"));
 
     /* local_exec_on_local_files */
-    fprintf(fp,"# if run all execution links is on then all local exection links that\n\
+    fprintf(fp,"\
+# if run all execution links is on then all local exection links that\n\
 # are found in LOCAL files will be executed when they are selected.\n\
 # This is different from \"run all execution links\" in that only files\n\
 # that reside on the local system will have execution link permissions\n\
@@ -438,7 +556,34 @@ PUBLIC int save_rc ()
 # ^N - down    ^p - up\n\
 # ^B - left    ^F - right\n\
 # will be enabled.\n");
-    fprintf(fp,"emacs_keys=%s\n\n",(emacs_keys ? "on" : "off"));
+    fprintf(fp, "emacs_keys=%s\n\n", (emacs_keys ? "on" : "off"));
+
+    /* multiple bookmarks - on or off */
+    fprintf(fp,"\
+# If sub_bookmarks are turned on then all bookmark operations\n\
+# will first prompt the user to select an active sub-bookmark file.\n\
+# If the default lynx bookmarks file is defined, it will be used as\n\
+# the default selection. When this option is set to 'advanced', and\n\
+# the user mode is advanced, the 'v'iew bookmark command will invoke\n\
+# a statusline prompt instead of the menu seen in novice and intermediate\n\
+# user modes. When this option is set to 'standard', the menu will be\n\
+# presented regardless of user mode. If this option is set to 'off',\n\
+# sub-bookmark files are disabled.\n");
+    fprintf(fp,"sub_bookmarks=%s\n\n", (LYMultiBookmarks ?
+          (LYMBMAdvanced ? "advanced" : "standard") : "off"));
+
+    /* multiple bookmarks support - list out sub-bookmarks */
+    fprintf(fp,"\
+# The following allow you to define sub-bookmark files and definitions.\n\
+# Format is <keyword><letter>=<filename>,<description>\n\
+# Up to MBM_V_MAXFILES (26 MAX) are allowed.\n\
+# We start with 'multi_bookmarkB' since 'A' is reserved!\n");
+    for (MBM_c = 1; MBM_c <= MBM_V_MAXFILES; MBM_c++)
+       fprintf(fp,"multi_bookmark%c=%s%s%s\n", (MBM_c + 'A'),
+             (MBM_A_subbookmark[MBM_c] ? MBM_A_subbookmark[MBM_c] : ""),
+             (MBM_A_subbookmark[MBM_c] ? "," : ""),
+             (MBM_A_subdescript[MBM_c] ? MBM_A_subdescript[MBM_c] : ""));
+    fprintf(fp,"\n");
 
     /* keypad mode */
     fprintf(fp,"\
@@ -513,6 +658,17 @@ PUBLIC int save_rc ()
 							"DIRECTORIES_FIRST")));
 #endif /* DIRED_SUPPORT */
 
+    /* select popups */
+    fprintf(fp, "\
+# select_popups specifies whether the OPTIONs in a SELECT block which\n\
+# lacks a MULTIPLE attribute are presented as a vertical list of radio\n\
+# buttons or via a popup menu.  Note that if the MULTIPLE attribute is\n\
+# present in the SELECT start tag, Lynx always will create a vertical list\n\
+# of checkboxes for the OPTIONs.  A value of \"on\" will set popup menus\n\
+# as the default while a value of \"off\" will set use of radio boxes.\n\
+# The default can be overridden via the -popup command line toggle.\n");
+    fprintf(fp, "select_popups=%s\n\n",(LYSelectPopups ? "on" : "off"));
+
     fclose(fp);
 
     /* get rid of any copies of the .lynxrc file that VMS creates */