about summary refs log tree commit diff stats
path: root/src/LYMainLoop.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/LYMainLoop.c')
-rw-r--r--src/LYMainLoop.c7862
1 files changed, 7862 insertions, 0 deletions
diff --git a/src/LYMainLoop.c b/src/LYMainLoop.c
new file mode 100644
index 00000000..585c1e12
--- /dev/null
+++ b/src/LYMainLoop.c
@@ -0,0 +1,7862 @@
+/*
+ * $LynxId: LYMainLoop.c,v 1.163 2010/04/29 22:11:50 tom Exp $
+ */
+#include <HTUtils.h>
+#include <HTAccess.h>
+#include <HTParse.h>
+#include <HTList.h>
+#include <HTML.h>
+#include <HTFTP.h>
+#include <HTFile.h>
+#include <HTTP.h>
+#include <HTAABrow.h>
+#include <HTNews.h>
+#include <LYCurses.h>
+#include <LYStyle.h>
+#include <LYGlobalDefs.h>
+#include <HTAlert.h>
+#include <LYUtils.h>
+#include <GridText.h>
+#include <LYStrings.h>
+#include <LYOptions.h>
+#include <LYSignal.h>
+#include <LYGetFile.h>
+#include <HTForms.h>
+#include <LYSearch.h>
+#include <LYClean.h>
+#include <LYHistory.h>
+#include <LYPrint.h>
+#include <LYMail.h>
+#include <LYEdit.h>
+#include <LYShowInfo.h>
+#include <LYBookmark.h>
+#include <LYKeymap.h>
+#include <LYJump.h>
+#include <LYDownload.h>
+#include <LYList.h>
+#include <LYMap.h>
+#include <LYTraversal.h>
+#include <LYCharSets.h>
+#include <LYCharUtils.h>
+#include <LYCookie.h>
+#include <LYMainLoop.h>
+#include <LYPrettySrc.h>
+
+#ifdef USE_SESSIONS
+#include <LYSession.h>
+#endif
+
+#ifdef KANJI_CODE_OVERRIDE
+#include <HTCJK.h>
+#endif
+
+#define LinkIsTextarea(linkNumber) \
+		(links[linkNumber].type == WWW_FORM_LINK_TYPE && \
+		 links[linkNumber].l_form->type == F_TEXTAREA_TYPE)
+
+#ifdef KANJI_CODE_OVERRIDE
+char *str_kcode(HTkcode code)
+{
+    char *p;
+    static char buff[8];
+
+    if (current_char_set == TRANSPARENT) {
+	p = "THRU";
+    } else if (!LYRawMode) {
+	p = "RAW";
+    } else {
+	switch (code) {
+	case NOKANJI:
+	    p = "AUTO";
+	    break;
+
+	case EUC:
+	    p = "EUC+";
+	    break;
+
+	case SJIS:
+	    p = "SJIS";
+	    break;
+
+	case JIS:
+	    p = " JIS";
+	    break;
+
+	default:
+	    p = " ???";
+	    break;
+	}
+    }
+
+    if (no_table_center) {
+	buff[0] = '!';
+	strcpy(buff + 1, p);
+    } else {
+	strcpy(buff, p);
+    }
+
+    return buff;
+}
+#endif
+
+#ifdef WIN_EX
+
+static char *str_sjis(char *to, char *from)
+{
+    if (!LYRawMode) {
+	strcpy(to, from);
+#ifdef KANJI_CODE_OVERRIDE
+    } else if (last_kcode == EUC) {
+	EUC_TO_SJIS(from, to);
+    } else if (last_kcode == SJIS) {
+	strcpy(to, from);
+#endif
+    } else {
+	TO_SJIS(from, to);
+    }
+    return to;
+}
+
+static void set_ws_title(char *str)
+{
+    SetConsoleTitle(str);
+}
+
+#endif /* WIN_EX */
+
+#if defined(USE_EXTERNALS) || defined(WIN_EX)
+#include <LYExtern.h>
+#endif
+
+#ifdef __EMX__
+#include <io.h>
+#endif
+
+#ifdef DIRED_SUPPORT
+#include <LYLocal.h>
+#include <LYUpload.h>
+#endif /* DIRED_SUPPORT */
+
+#include <LYexit.h>
+#include <LYLeaks.h>
+
+/* two constants: */
+HTLinkType *HTInternalLink = 0;
+HTAtom *WWW_SOURCE = 0;
+
+#ifndef DONT_TRACK_INTERNAL_LINKS
+#define NO_INTERNAL_OR_DIFFERENT(c,n) TRUE
+#define NONINTERNAL_OR_PHYS_DIFFERENT(p,n) (!curdoc.internal_link || \
+			   are_phys_different(p,n))
+#else /* TRACK_INTERNAL_LINKS */
+#define NO_INTERNAL_OR_DIFFERENT(c,n) are_different(c,n)
+#define NONINTERNAL_OR_PHYS_DIFFERENT(p,n) are_different(p,n)
+#endif /* TRACK_INTERNAL_LINKS */
+
+static void exit_immediately_with_error_message(int state, BOOLEAN first_file);
+static void status_link(char *curlink_name, BOOLEAN show_more, BOOLEAN show_indx);
+static void show_main_statusline(const LinkInfo curlink, int for_what);
+static void form_noviceline(int);
+static int are_different(DocInfo *doc1, DocInfo *doc2);
+
+#ifndef DONT_TRACK_INTERNAL_LINKS
+static int are_phys_different(DocInfo *doc1, DocInfo *doc2);
+#endif
+
+#define FASTTAB
+
+static int sametext(char *een,
+		    char *twee)
+{
+    if (een && twee)
+	return (strcmp(een, twee) == 0);
+    return TRUE;
+}
+
+HTList *Goto_URLs = NULL;	/* List of Goto URLs */
+
+char *LYRequestTitle = NULL;	/* newdoc.title in calls to getfile() */
+char *LYRequestReferer = NULL;	/* Referer, may be set in getfile() */
+
+static char prev_target[MAX_LINE];
+
+#ifdef DISP_PARTIAL
+BOOLEAN display_partial = FALSE;	/* could be enabled in HText_new() */
+int NumOfLines_partial = 0;	/* number of lines displayed in partial mode */
+#endif
+
+static int Newline = 0;
+static DocInfo newdoc;
+static DocInfo curdoc;
+static char *traversal_host = NULL;
+static char *traversal_link_to_add = NULL;
+static char *owner_address = NULL;	/* Holds the responsible owner's address     */
+static char *ownerS_address = NULL;	/* Holds owner's address during source fetch */
+
+#ifdef TEXTFIELDS_MAY_NEED_ACTIVATION
+static BOOL textinput_activated = FALSE;
+
+#else
+#define textinput_activated TRUE	/* a current text input is always active */
+#endif
+#ifdef INACTIVE_INPUT_STYLE_VH
+BOOL textinput_redrawn = FALSE;
+
+    /*must be public since used in LYhighlight(..) */
+#endif
+
+#ifdef LY_FIND_LEAKS
+/*
+ * Function for freeing allocated mainloop() variables.  - FM
+ */
+static void free_mainloop_variables(void)
+{
+    LYFreeDocInfo(&newdoc);
+    LYFreeDocInfo(&curdoc);
+
+#ifdef USE_COLOR_STYLE
+    FREE(curdoc.style);
+    FREE(newdoc.style);
+#endif
+    FREE(traversal_host);
+    FREE(traversal_link_to_add);
+    FREE(owner_address);
+    FREE(ownerS_address);
+#ifdef DIRED_SUPPORT
+    clear_tags();
+    reset_dired_menu();
+#endif /* DIRED_SUPPORT */
+    FREE(WWW_Download_File);	/* LYGetFile.c/HTFWriter.c */
+    FREE(LYRequestReferer);
+
+    return;
+}
+#endif /* LY_FIND_LEAKS */
+
+#ifndef NO_LYNX_TRACE
+static void TracelogOpenFailed(void)
+{
+    WWW_TraceFlag = FALSE;
+    if (LYCursesON) {
+	HTUserMsg(TRACELOG_OPEN_FAILED);
+    } else {
+	fprintf(stderr, "%s\n", TRACELOG_OPEN_FAILED);
+	exit_immediately(EXIT_FAILURE);
+    }
+}
+
+static BOOLEAN LYReopenTracelog(BOOLEAN *trace_flag_ptr)
+{
+    CTRACE((tfp, "\nTurning off TRACE for fetch of log.\n"));
+    LYCloseTracelog();
+    if ((LYTraceLogFP = LYAppendToTxtFile(LYTraceLogPath)) == NULL) {
+	TracelogOpenFailed();
+	return FALSE;
+    }
+    if (TRACE) {
+	WWW_TraceFlag = FALSE;
+	*trace_flag_ptr = TRUE;
+    }
+    return TRUE;
+}
+
+static void turn_trace_back_on(BOOLEAN *trace_flag_ptr)
+{
+    if (*trace_flag_ptr == TRUE) {
+	WWW_TraceFlag = TRUE;
+	*trace_flag_ptr = FALSE;
+	fprintf(tfp, "Turning TRACE back on.\n\n");
+    }
+}
+#else
+#define LYReopenTracelog(flag) TRUE
+#define turn_trace_back_on(flag)	/*nothing */
+#endif /* NO_LYNX_TRACE */
+
+FILE *TraceFP(void)
+{
+#ifndef NO_LYNX_TRACE
+    if (LYTraceLogFP != 0) {
+	return LYTraceLogFP;
+    }
+#endif /* NO_LYNX_TRACE */
+    return stderr;
+}
+
+BOOLEAN LYOpenTraceLog(void)
+{
+#ifndef NO_LYNX_TRACE
+    if (TRACE && LYUseTraceLog && LYTraceLogFP == NULL) {
+	/*
+	 * If we can't open it for writing, give up.  Otherwise, on VMS close
+	 * it, delete it and any versions from previous sessions so they don't
+	 * accumulate, and open it again.  - FM
+	 */
+	if ((LYTraceLogFP = LYNewTxtFile(LYTraceLogPath)) == NULL) {
+	    TracelogOpenFailed();
+	    return FALSE;
+	}
+#ifdef VMS
+	LYCloseTracelog();
+	HTSYS_remove(LYTraceLogPath);
+	if ((LYTraceLogFP = LYNewTxtFile(LYTraceLogPath)) == NULL) {
+	    TracelogOpenFailed();
+	    return FALSE;
+	}
+#endif /* VMS */
+	fflush(stdout);
+	fflush(stderr);
+	fprintf(tfp, "\t\t%s (%s)\n\n", LYNX_TRACELOG_TITLE, LYNX_VERSION);
+	/*
+	 * If TRACE is on, indicate whether the anonymous restrictions are set. 
+	 * - FM, LP, kw
+	 *
+	 * This is only a summary for convenience - it doesn't take the case of
+	 * individual -restrictions= options into account.  - kw
+	 */
+	if (LYValidate) {
+	    if (LYRestricted && had_restrictions_default) {
+		CTRACE((tfp,
+			"Validate and some anonymous restrictions are set.\n"));
+	    } else if (had_restrictions_default) {
+		CTRACE((tfp,
+			"Validate restrictions set, restriction \"default\" was given.\n"));
+	    } else if (LYRestricted) {
+		CTRACE((tfp,
+			"Validate restrictions set, additional anonymous restrictions ignored.\n"));
+	    } else {
+		CTRACE((tfp, "Validate restrictions are set.\n"));
+	    }
+	    /* But none of the above can actually happen, since there should
+	     * never be a Trace Log with -validate.  If it appears in a log
+	     * file something went wrong! */
+	} else if (LYRestricted) {
+	    if (had_restrictions_all) {
+		CTRACE((tfp,
+			"Anonymous restrictions set, restriction \"all\" was given.\n"));
+	    } else {
+		CTRACE((tfp, "Anonymous restrictions are set.\n"));
+	    }
+	} else if (had_restrictions_all && had_restrictions_default) {
+	    CTRACE((tfp, "Restrictions \"all\" and \"default\" were given.\n"));
+	} else if (had_restrictions_default) {
+	    CTRACE((tfp, "Restriction \"default\" was given.\n"));
+	} else if (had_restrictions_all) {
+	    CTRACE((tfp, "\"all\" restrictions are set.\n"));
+	}
+    }
+#endif /* NO_LYNX_TRACE */
+    return TRUE;
+}
+
+void LYCloseTracelog(void)
+{
+#ifndef NO_LYNX_TRACE
+    if (LYTraceLogFP != 0) {
+	fflush(stdout);
+	fflush(stderr);
+	fclose(LYTraceLogFP);
+	LYTraceLogFP = 0;
+    }
+#endif /* NO_LYNX_TRACE */
+}
+
+void handle_LYK_TRACE_TOGGLE(void)
+{
+#ifndef NO_LYNX_TRACE
+    WWW_TraceFlag = (BOOLEAN) !WWW_TraceFlag;
+    if (LYOpenTraceLog())
+	HTUserMsg(WWW_TraceFlag ? TRACE_ON : TRACE_OFF);
+#else
+    HTUserMsg(TRACE_DISABLED);
+#endif /* NO_LYNX_TRACE */
+}
+
+void LYSetNewline(int value)
+{
+    Newline = value;
+}
+
+#define LYSetNewline(value)	Newline = value
+
+int LYGetNewline(void)
+{
+    return Newline;
+}
+
+#define LYGetNewline()		Newline
+
+void LYChgNewline(int adjust)
+{
+    LYSetNewline(Newline + adjust);
+}
+
+#define LYChgNewline(adjust)	Newline += (adjust)
+
+#ifdef USE_SOURCE_CACHE
+static BOOLEAN from_source_cache = FALSE;
+
+/*
+ * Like HTreparse_document(), but also set the flag.
+ */
+static BOOLEAN reparse_document(void)
+{
+    BOOLEAN result;
+
+    from_source_cache = TRUE;	/* set for LYMainLoop_pageDisplay() */
+    if ((result = HTreparse_document()) != FALSE) {
+	from_source_cache = TRUE;	/* set for mainloop refresh */
+    } else {
+	from_source_cache = FALSE;
+    }
+    return result;
+}
+#endif /* USE_SOURCE_CACHE */
+
+/*
+ * Prefer reparsing if we can, but reload if we must - to force regeneration
+ * of the display.
+ */
+static BOOLEAN reparse_or_reload(int *cmd)
+{
+#ifdef USE_SOURCE_CACHE
+    if (reparse_document()) {
+	return FALSE;
+    }
+#endif
+    *cmd = LYK_RELOAD;
+    return TRUE;
+}
+
+/*
+ * Functions for setting the current address
+ */
+static void set_address(DocInfo *doc, const char *address)
+{
+    StrAllocCopy(doc->address, address);
+}
+
+static void copy_address(DocInfo *dst, DocInfo *src)
+{
+    StrAllocCopy(dst->address, src->address);
+}
+
+static void free_address(DocInfo *doc)
+{
+    FREE(doc->address);
+}
+
+static void move_address(DocInfo *dst, DocInfo *src)
+{
+    copy_address(dst, src);
+    free_address(src);
+}
+
+#ifdef DISP_PARTIAL
+/*
+ * This is for traversal call from within partial mode in LYUtils.c
+ * and HTFormat.c  It simply calls HText_pageDisplay() but utilizes
+ * LYMainLoop.c static variables to manage proper newline position
+ * in case of #fragment
+ */
+BOOL LYMainLoop_pageDisplay(int line_num)
+{
+    const char *pound;
+    int prev_newline = LYGetNewline();
+
+    /*
+     * Override Newline with a new value if user scrolled the document while
+     * loading (in LYUtils.c).
+     */
+    LYSetNewline(line_num);
+
+#ifdef USE_SOURCE_CACHE
+    /*
+     * reparse_document() acts on 'curdoc' which always on top of the
+     * history stack: no need to resolve #fragment position since
+     * we already know it (curdoc.line).
+     * So bypass here. Sorry for possible confusion...
+     */
+    if (!from_source_cache)
+#endif
+	/*
+	 * If the requested URL has the #fragment, and we are not popped
+	 * from the history stack, and have not scrolled the document yet -
+	 * we should calculate correct newline position for the fragment.
+	 * (This is a bit suboptimal since HTFindPoundSelector() traverse
+	 * anchors list each time, so we have a quadratic complexity
+	 * and may load CPU in a worst case).
+	 */
+	if (display_partial
+	    && newdoc.line == 1 && line_num == 1 && prev_newline == 1
+	    && (pound = findPoundSelector(newdoc.address))
+	    && *pound && *(pound + 1)) {
+	    if (HTFindPoundSelector(pound + 1)) {
+		/* HTFindPoundSelector will initialize www_search_result */
+		LYSetNewline(www_search_result);
+	    } else {
+		LYSetNewline(prev_newline);	/* restore ??? */
+		return NO;	/* no repaint */
+	    }
+	}
+
+    HText_pageDisplay(LYGetNewline(), prev_target);
+    return YES;
+}
+#endif /* DISP_PARTIAL */
+
+static BOOL set_curdoc_link(int nextlink)
+{
+    BOOL result = FALSE;
+
+    if (curdoc.link != nextlink
+	&& nextlink >= 0
+	&& nextlink < nlinks) {
+	if (curdoc.link >= 0 && curdoc.link < nlinks) {
+	    LYhighlight(OFF, curdoc.link, prev_target);
+	    result = TRUE;
+	}
+	curdoc.link = nextlink;
+    }
+    return result;
+}
+
+/*
+ * Setup newdoc to jump to the given line.
+ *
+ * FIXME: prefer to also jump to the link given in a URL fragment, but the
+ * interface of getfile() does not provide that ability yet.
+ */
+static void goto_line(int nextline)
+{
+    int n;
+    int old_link = newdoc.link;
+
+    newdoc.link = 0;
+    for (n = 0; n < nlinks; ++n) {
+	if (nextline == links[n].anchor_line_num + 1) {
+	    CTRACE((tfp, "top_of_screen %d\n", HText_getTopOfScreen() + 1));
+	    CTRACE((tfp, "goto_line(%d) -> link %d -> %d\n", nextline,
+		    old_link, n));
+	    newdoc.link = n;
+	    break;
+	}
+    }
+}
+
+#ifdef USE_MOUSE
+static void set_curdoc_link_by_mouse(int nextlink)
+{
+    if (set_curdoc_link(nextlink)) {
+	LYhighlight(ON, nextlink, prev_target);
+	LYmsec_delay(20);
+    }
+}
+#else
+#define set_curdoc_link_by_mouse(nextlink) set_curdoc_link(nextlink)
+#endif
+
+static int do_change_link(void)
+{
+#ifdef USE_MOUSE
+    /* Is there a mouse-clicked link waiting? */
+    int mouse_tmp = get_mouse_link();
+
+    /* If yes, use it as the link */
+    if (mouse_tmp != -1) {
+	if (mouse_tmp < 0 || mouse_tmp >= nlinks) {
+	    char *msgtmp = NULL;
+
+	    HTSprintf0(&msgtmp,
+		       gettext("Internal error: Invalid mouse link %d!"),
+		       mouse_tmp);
+	    HTAlert(msgtmp);
+	    FREE(msgtmp);
+	    return (-1);	/* indicates unexpected error */
+	}
+	set_curdoc_link_by_mouse(mouse_tmp);
+    }
+#endif /* USE_MOUSE */
+    return (0);			/* indicates OK */
+}
+
+#ifdef DIRED_SUPPORT
+#define DIRED_UNCACHE_1 if (LYAutoUncacheDirLists < 1) /*nothing*/ ;\
+			else HTuncache_current_document()
+#define DIRED_UNCACHE_2 if (LYAutoUncacheDirLists < 2) /*nothing*/ ;\
+			else HTuncache_current_document()
+#endif /* DIRED_SUPPORT */
+
+static void do_check_goto_URL(char *user_input_buffer,
+			      char **old_user_input,
+			      BOOLEAN *force_load)
+{
+    static BOOLEAN always = TRUE;
+    /* *INDENT-OFF* */
+    static struct {
+	const char *name;
+	BOOLEAN *flag;
+    } table[] = {
+	{ STR_FILE_URL,		&no_file_url },
+	{ STR_FILE_URL,		&no_goto_file },
+	{ STR_LYNXEXEC,		&no_goto_lynxexec },
+	{ STR_LYNXPROG,		&no_goto_lynxprog },
+	{ STR_LYNXCGI,		&no_goto_lynxcgi },
+	{ STR_CSO_URL,		&no_goto_cso },
+	{ STR_FINGER_URL,	&no_goto_finger },
+	{ STR_FTP_URL,		&no_goto_ftp },
+	{ STR_GOPHER_URL,	&no_goto_gopher },
+	{ STR_HTTP_URL,		&no_goto_http },
+	{ STR_HTTPS_URL,	&no_goto_https },
+	{ STR_MAILTO_URL,	&no_goto_mailto },
+	{ STR_RLOGIN_URL,	&no_goto_rlogin },
+	{ STR_TELNET_URL,	&no_goto_telnet },
+	{ STR_TN3270_URL,	&no_goto_tn3270 },
+	{ STR_WAIS_URL,		&no_goto_wais },
+#ifndef DISABLE_BIBP
+	{ STR_BIBP_URL,		&no_goto_bibp },
+#endif
+#ifndef DISABLE_NEWS
+	{ STR_NEWS_URL,		&no_goto_news },
+	{ STR_NNTP_URL,		&no_goto_nntp },
+	{ STR_SNEWS_URL,	&no_goto_snews },
+#endif
+#ifdef EXEC_LINKS
+	{ STR_LYNXEXEC,		&local_exec_on_local_files },
+	{ STR_LYNXPROG,		&local_exec_on_local_files },
+#endif /* EXEC_LINKS */
+	{ STR_LYNXCFG,		&no_goto_configinfo },
+	{ STR_LYNXCFLAGS,	&no_goto_configinfo },
+	{ STR_LYNXCOOKIE,	&always },
+#ifdef USE_CACHEJAR
+	{ STR_LYNXCACHE,	&always },
+#endif
+	{ STR_LYNXDIRED,	&always },
+	{ STR_LYNXDOWNLOAD,	&always },
+	{ STR_LYNXOPTIONS,	&always },
+	{ STR_LYNXPRINT,	&always },
+    };
+    /* *INDENT-ON* */
+
+    unsigned n;
+    BOOLEAN found = FALSE;
+
+    /* allow going to anchors */
+    if (*user_input_buffer == '#') {
+	if (user_input_buffer[1] &&
+	    HTFindPoundSelector(user_input_buffer + 1)) {
+	    /* HTFindPoundSelector will initialize www_search_result,
+	       so we do nothing else. */
+	    HTAddGotoURL(user_input_buffer);
+	    trimPoundSelector(curdoc.address);
+	    StrAllocCat(curdoc.address, user_input_buffer);
+	}
+    } else {
+	/*
+	 * If it's not a URL then make it one.
+	 */
+	StrAllocCopy(*old_user_input, user_input_buffer);
+	LYEnsureAbsoluteURL(old_user_input, "", TRUE);
+	sprintf(user_input_buffer, "%.*s",
+		(int) (MAX_LINE - 1), *old_user_input);
+	FREE(*old_user_input);
+
+	for (n = 0; n < TABLESIZE(table); n++) {
+	    if (*(table[n].flag)
+		&& !strncmp(user_input_buffer, table[n].name, strlen(table[n].name))) {
+		found = TRUE;
+		HTUserMsg2(GOTO_XXXX_DISALLOWED, table[n].name);
+		break;
+	    }
+	}
+	if (found) {
+	    ;
+	} else if (LYValidate &&
+		   !isHTTP_URL(user_input_buffer) &&
+		   !isHTTPS_URL(user_input_buffer)) {
+	    HTUserMsg(GOTO_NON_HTTP_DISALLOWED);
+
+	} else {
+	    set_address(&newdoc, 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
+	     */
+	    if (are_different(&curdoc, &newdoc)) {
+		/*
+		 * Make a name for this new URL.
+		 */
+		StrAllocCopy(newdoc.title,
+			     gettext("A URL specified by the user"));
+		LYFreePostData(&newdoc);
+		FREE(newdoc.bookmark);
+		newdoc.safe = FALSE;
+		newdoc.internal_link = FALSE;
+		*force_load = TRUE;
+#ifdef DIRED_SUPPORT
+		if (lynx_edit_mode) {
+		    DIRED_UNCACHE_2;
+		}
+#endif /* DIRED_SUPPORT */
+	    }
+	    LYUserSpecifiedURL = TRUE;
+	    HTAddGotoURL(newdoc.address);
+	}
+    }
+}
+
+/* returns FALSE if user cancelled input or URL was invalid, TRUE otherwise */
+static BOOL do_check_recall(int ch,
+			    char *user_input_buffer,
+			    char **old_user_input,
+			    int URLTotal,
+			    int *URLNum,
+			    RecallType recall,
+			    BOOLEAN *FirstURLRecall)
+{
+    char *cp;
+    BOOL ret = FALSE;
+
+    if (*old_user_input == 0)
+	StrAllocCopy(*old_user_input, "");
+
+    for (;;) {
+#ifdef WIN_EX			/* 1998/10/11 (Sun) 10:41:05 */
+	int len = strlen(user_input_buffer);
+
+	if (len >= 3) {
+	    if (len < MAX_LINE - 1
+		&& LYIsHtmlSep(user_input_buffer[len - 3])
+		&& LYIsDosDrive(user_input_buffer + len - 2))
+		LYAddPathSep0(user_input_buffer);
+
+	} else if (len == 2 && user_input_buffer[1] == ':') {
+	    if (LYIsDosDrive(user_input_buffer)) {
+		LYAddPathSep0(user_input_buffer);
+	    } else {
+		HTUserMsg2(WWW_ILLEGAL_URL_MESSAGE, user_input_buffer);
+		LYstrncpy(user_input_buffer, *old_user_input, MAX_LINE - 1);
+		FREE(*old_user_input);
+		ret = FALSE;
+		break;
+	    }
+	}
+#endif
+	/*
+	 * Get rid of leading spaces (and any other spaces).
+	 */
+	LYTrimAllStartfile(user_input_buffer);
+	if (*user_input_buffer == '\0' &&
+	    !(recall && (ch == UPARROW || ch == DNARROW))) {
+	    LYstrncpy(user_input_buffer, *old_user_input, MAX_LINE - 1);
+	    FREE(*old_user_input);
+	    HTInfoMsg(CANCELLED);
+	    ret = FALSE;
+	    break;
+	}
+	if (recall && ch == UPARROW) {
+	    if (*FirstURLRecall) {
+		/*
+		 * Use last URL in the list.  - FM
+		 */
+		*FirstURLRecall = FALSE;
+		*URLNum = 0;
+	    } else {
+		/*
+		 * Go back to the previous URL in the list.  - FM
+		 */
+		*URLNum += 1;
+	    }
+	    if (*URLNum >= URLTotal)
+		/*
+		 * Roll around to the last URL in the list.  - FM
+		 */
+		*URLNum = 0;
+	    if ((cp = (char *) HTList_objectAt(Goto_URLs,
+					       *URLNum)) != NULL) {
+		LYstrncpy(user_input_buffer, cp, MAX_LINE - 1);
+		if (goto_buffer
+		    && **old_user_input
+		    && !strcmp(*old_user_input, user_input_buffer)) {
+		    _statusline(EDIT_CURRENT_GOTO);
+		} else if ((goto_buffer && URLTotal == 2) ||
+			   (!goto_buffer && URLTotal == 1)) {
+		    _statusline(EDIT_THE_PREV_GOTO);
+		} else {
+		    _statusline(EDIT_A_PREV_GOTO);
+		}
+		if ((ch = LYgetstr(user_input_buffer, VISIBLE,
+				   MAX_LINE,
+				   recall)) < 0) {
+		    /*
+		     * User cancelled the Goto via ^G.  Restore
+		     * user_input_buffer and break.  - FM
+		     */
+		    LYstrncpy(user_input_buffer, *old_user_input, MAX_LINE - 1);
+		    FREE(*old_user_input);
+		    HTInfoMsg(CANCELLED);
+		    ret = FALSE;
+		    break;
+		}
+		continue;
+	    }
+	} else if (recall && ch == DNARROW) {
+	    if (*FirstURLRecall) {
+		/*
+		 * Use the first URL in the list.  - FM
+		 */
+		*FirstURLRecall = FALSE;
+		*URLNum = URLTotal - 1;
+	    } else {
+		/*
+		 * Advance to the next URL in the list.  - FM
+		 */
+		*URLNum -= 1;
+	    }
+	    if (*URLNum < 0)
+		/*
+		 * Roll around to the first URL in the list.  - FM
+		 */
+		*URLNum = URLTotal - 1;
+	    if ((cp = (char *) HTList_objectAt(Goto_URLs, *URLNum)) != NULL) {
+		LYstrncpy(user_input_buffer, cp, MAX_LINE - 1);
+		if (goto_buffer && **old_user_input &&
+		    !strcmp(*old_user_input, user_input_buffer)) {
+		    _statusline(EDIT_CURRENT_GOTO);
+		} else if ((goto_buffer && URLTotal == 2) ||
+			   (!goto_buffer && URLTotal == 1)) {
+		    _statusline(EDIT_THE_PREV_GOTO);
+		} else {
+		    _statusline(EDIT_A_PREV_GOTO);
+		}
+		if ((ch = LYgetstr(user_input_buffer, VISIBLE,
+				   MAX_LINE,
+				   recall)) < 0) {
+		    /*
+		     * User cancelled the Goto via ^G.  Restore
+		     * user_input_buffer and break.  - FM
+		     */
+		    LYstrncpy(user_input_buffer, *old_user_input, MAX_LINE - 1);
+		    FREE(*old_user_input);
+		    HTInfoMsg(CANCELLED);
+		    ret = FALSE;
+		    break;
+		}
+		continue;
+	    }
+	} else {
+	    ret = TRUE;
+	    break;
+	}
+    }
+    return ret;
+}
+
+static void do_cleanup_after_delete(void)
+{
+    HTuncache_current_document();
+    move_address(&newdoc, &curdoc);
+    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;
+    }
+}
+
+static int find_link_near_col(int col,
+			      int delta)
+{
+    int i;
+
+    for (i = curdoc.link; delta > 0 ? (i < nlinks) : (i >= 0); i += delta) {
+	if ((links[i].ly - links[curdoc.link].ly) * delta > 0) {
+	    int cy = links[i].ly, best = -1, dist = 1000000;
+
+	    while ((delta > 0 ? (i < nlinks) : (i >= 0)) && cy == links[i].ly) {
+		int cx = links[i].lx;
+		const char *text = LYGetHiliteStr(i, 0);
+
+		if (text != NULL)
+		    cx += (int) strlen(text) / 2;
+		cx -= col;
+		if (cx < 0)
+		    cx = -cx;
+		if (cx < dist) {
+		    dist = cx;
+		    best = i;
+		}
+		i += delta;
+	    }
+	    return (best);
+	}
+    }
+    return (-1);
+}
+
+/*
+ * This is a special feature to traverse every http link derived from startfile
+ * and check for errors or create crawl output files.  Only URL's that begin
+ * with "traversal_host" are searched - this keeps the search from crossing to
+ * other servers (a feature, not a bug!).
+ */
+static int DoTraversal(int c,
+		       BOOLEAN *crawl_ok)
+{
+    BOOLEAN rlink_rejected = FALSE;
+    BOOLEAN rlink_exists;
+    BOOLEAN rlink_allowed;
+
+    rlink_exists = (BOOL) (nlinks > 0 &&
+			   links[curdoc.link].type != WWW_FORM_LINK_TYPE &&
+			   links[curdoc.link].lname != NULL);
+
+    if (rlink_exists) {
+	rlink_rejected = lookup_reject(links[curdoc.link].lname);
+	if (!rlink_rejected &&
+	    traversal_host &&
+	    links[curdoc.link].lname) {
+	    if (!isLYNXIMGMAP(links[curdoc.link].lname)) {
+		rlink_allowed = (BOOL) !strncmp(traversal_host,
+						links[curdoc.link].lname,
+						strlen(traversal_host));
+	    } else {
+		rlink_allowed = (BOOL) !strncmp(traversal_host,
+						links[curdoc.link].lname + LEN_LYNXIMGMAP,
+						strlen(traversal_host));
+	    }
+	} else {
+	    rlink_allowed = FALSE;
+	}
+    } else {
+	rlink_allowed = FALSE;
+    }
+    if (rlink_exists && rlink_allowed) {
+	if (lookup_link(links[curdoc.link].lname)) {
+	    if (more_links ||
+		(curdoc.link > -1 && curdoc.link < nlinks - 1)) {
+		c = DNARROW;
+	    } else {
+		if (STREQ(curdoc.title, "Entry into main screen") ||
+		    (nhist <= 0)) {
+		    if (!dump_output_immediately) {
+			cleanup();
+			exit_immediately(EXIT_FAILURE);
+		    }
+		    c = -1;
+		} else {
+		    c = LTARROW;
+		}
+	    }
+	} else {
+	    StrAllocCopy(traversal_link_to_add,
+			 links[curdoc.link].lname);
+	    if (!isLYNXIMGMAP(traversal_link_to_add))
+		*crawl_ok = TRUE;
+	    c = RTARROW;
+	}
+    } else {			/* no good right link, so only down and left arrow ok */
+	if (rlink_exists /* && !rlink_rejected */ )
+	    /* uncomment in previous line to avoid duplicates - kw */
+	    add_to_reject_list(links[curdoc.link].lname);
+	if (more_links ||
+	    (curdoc.link > -1 && curdoc.link < nlinks - 1)) {
+	    c = DNARROW;
+	} else {
+	    /*
+	     * curdoc.title doesn't always work, so bail out if the history
+	     * list is empty.
+	     */
+	    if (STREQ(curdoc.title, "Entry into main screen") ||
+		(nhist <= 0)) {
+		if (!dump_output_immediately) {
+		    cleanup();
+		    exit_immediately(EXIT_FAILURE);
+		}
+		c = -1;
+	    } else {
+		c = LTARROW;
+	    }
+	}
+    }
+    CTRACE((tfp, "DoTraversal(%d:%d) -> %s\n",
+	    nlinks > 0 ? curdoc.link : 0,
+	    nlinks,
+	    LYKeycodeToString(c, FALSE)));
+    return c;
+}
+
+#ifndef DONT_TRACK_INTERNAL_LINKS
+static BOOLEAN check_history(void)
+{
+    const char *base;
+
+    if (!curdoc.post_data)
+	/*
+	 * Normal case - List Page is not associated with post data.  - kw
+	 */
+	return TRUE;
+
+    if (nhist > 0
+	&& !LYresubmit_posts
+	&& HDOC(nhist - 1).post_data
+	&& BINEQ(curdoc.post_data, HDOC(nhist - 1).post_data)
+	&& (base = HText_getContentBase()) != 0) {
+	char *text = !isLYNXIMGMAP(HDOC(nhist - 1).address)
+	? HDOC(nhist - 1).address
+	: HDOC(nhist - 1).address + LEN_LYNXIMGMAP;
+
+	if (!strncmp(base, text, strlen(base))) {
+	    /*
+	     * Normal case - as best as we can check, the document at the top
+	     * of the history stack seems to be the document the List Page is
+	     * about (or a LYNXIMGMAP derived from it), and LYresubmit_posts is
+	     * not set, so don't prompt here.  If we actually have to repeat a
+	     * POST because, against expectations, the underlying document
+	     * isn't cached any more, HTAccess will prompt for confirmation,
+	     * unless we had LYK_NOCACHE -kw
+	     */
+	    return TRUE;
+	}
+    }
+    return FALSE;
+}
+#endif
+
+static int handle_LYK_ACTIVATE(int *c,
+			       int cmd GCC_UNUSED,
+			       BOOLEAN *try_internal GCC_UNUSED,
+			       BOOLEAN *refresh_screen,
+			       BOOLEAN *force_load,
+			       int real_cmd)
+{
+    if (do_change_link() == -1) {
+	LYforce_no_cache = FALSE;
+	reloading = FALSE;
+	return 1;		/* mouse stuff was confused, ignore - kw */
+    }
+    if (nlinks > 0) {
+	if (links[curdoc.link].type == WWW_FORM_LINK_TYPE) {
+#ifdef TEXTFIELDS_MAY_NEED_ACTIVATION
+	    if (real_cmd == LYK_ACTIVATE && textfields_need_activation &&
+		F_TEXTLIKE(links[curdoc.link].l_form->type)) {
+
+		textinput_activated = TRUE;
+		show_main_statusline(links[curdoc.link], FOR_INPUT);
+		textfields_need_activation = textfields_activation_option;
+
+		return 0;
+	    }
+#endif
+	    /*
+	     * Don't try to submit forms with bad actions.  - FM
+	     */
+	    if (links[curdoc.link].l_form->type == F_SUBMIT_TYPE ||
+		links[curdoc.link].l_form->type == F_IMAGE_SUBMIT_TYPE ||
+		links[curdoc.link].l_form->type ==
+		F_TEXT_SUBMIT_TYPE) {
+		/*
+		 * Do nothing if it's disabled.  - FM
+		 */
+		if (links[curdoc.link].l_form->disabled == YES) {
+		    HTOutputFormat = WWW_PRESENT;
+		    LYforce_no_cache = FALSE;
+		    reloading = FALSE;
+		    return 0;
+		}
+		/*
+		 * Make sure we have an action.  - FM
+		 */
+		if (!links[curdoc.link].l_form->submit_action ||
+		    *links[curdoc.link].l_form->submit_action
+		    == '\0') {
+		    HTUserMsg(NO_FORM_ACTION);
+		    HTOutputFormat = WWW_PRESENT;
+		    LYforce_no_cache = FALSE;
+		    reloading = FALSE;
+		    return 0;
+		}
+		/*
+		 * Check for no_mail if the form action is a mailto URL.  - FM
+		 */
+		if (links[curdoc.link].l_form->submit_method
+		    == URL_MAIL_METHOD && no_mail) {
+		    HTAlert(FORM_MAILTO_DISALLOWED);
+		    HTOutputFormat = WWW_PRESENT;
+		    LYforce_no_cache = FALSE;
+		    reloading = FALSE;
+		    return 0;
+		}
+		/*
+		 * Make sure this isn't a spoof in an account with restrictions
+		 * on file URLs.  - FM
+		 */
+		if (no_file_url &&
+		    isFILE_URL(links[curdoc.link].l_form->submit_action)) {
+		    HTAlert(FILE_ACTIONS_DISALLOWED);
+		    HTOutputFormat = WWW_PRESENT;
+		    LYforce_no_cache = FALSE;
+		    reloading = FALSE;
+		    return 0;
+		}
+		/*
+		 * Make sure this isn't a spoof attempt via an internal URL.  -
+		 * FM
+		 */
+		if (isLYNXCOOKIE(links[curdoc.link].l_form->submit_action) ||
+#ifdef USE_CACHEJAR
+		    isLYNXCACHE(links[curdoc.link].l_form->submit_action) ||
+#endif
+#ifdef DIRED_SUPPORT
+#ifdef OK_PERMIT
+		    (isLYNXDIRED(links[curdoc.link].l_form->submit_action) &&
+		     (no_dired_support ||
+		      strncasecomp((links[curdoc.link].l_form->submit_action
+				    + 10),
+				   "//PERMIT_LOCATION", 17) ||
+		      !LYIsUIPage(curdoc.address, UIP_PERMIT_OPTIONS))) ||
+#else
+		    isLYNXDIRED(links[curdoc.link].l_form->submit_action) ||
+#endif /* OK_PERMIT */
+#endif /* DIRED_SUPPORT */
+		    isLYNXDOWNLOAD(links[curdoc.link].l_form->submit_action) ||
+		    isLYNXHIST(links[curdoc.link].l_form->submit_action) ||
+		    isLYNXKEYMAP(links[curdoc.link].l_form->submit_action) ||
+		    isLYNXIMGMAP(links[curdoc.link].l_form->submit_action) ||
+		    isLYNXPRINT(links[curdoc.link].l_form->submit_action) ||
+		    isLYNXEXEC(links[curdoc.link].l_form->submit_action) ||
+		    isLYNXPROG(links[curdoc.link].l_form->submit_action)) {
+
+		    HTAlert(SPECIAL_ACTION_DISALLOWED);
+		    CTRACE((tfp, "LYMainLoop: Rejected '%s'\n",
+			    links[curdoc.link].l_form->submit_action));
+		    HTOutputFormat = WWW_PRESENT;
+		    LYforce_no_cache = FALSE;
+		    reloading = FALSE;
+		    return 0;
+		}
+#ifdef NOTDEFINED		/* We're disabling form inputs instead of using this. - FM */
+		/*
+		 * Check for enctype and let user know we don't yet support
+		 * multipart/form-data - FM
+		 */
+		if (links[curdoc.link].l_form->submit_enctype) {
+		    if (!strcmp(links[curdoc.link].l_form->submit_enctype,
+				"multipart/form-data")) {
+			HTAlert(gettext("Enctype multipart/form-data not yet supported!  Cannot submit."));
+			HTOutputFormat = WWW_PRESENT;
+			LYforce_no_cache = FALSE;
+			reloading = FALSE;
+			return 0;
+		    }
+		}
+#endif /* NOTDEFINED */
+		if (check_realm) {
+		    LYPermitURL = TRUE;
+		}
+		if (no_filereferer == TRUE && isFILE_URL(curdoc.address)) {
+		    LYNoRefererForThis = TRUE;
+		}
+		if (links[curdoc.link].l_form->submit_method != URL_MAIL_METHOD) {
+		    StrAllocCopy(newdoc.title,
+				 LYGetHiliteStr(curdoc.link, 0));
+		}
+	    }
+
+	    /*
+	     * Normally we don't get here for text input fields, but it can
+	     * happen as a result of mouse positioning.  In that case the
+	     * statusline will not have updated info, so update it now.  - kw
+	     */
+	    if (F_TEXTLIKE(links[curdoc.link].l_form->type)) {
+		show_formlink_statusline(links[curdoc.link].l_form,
+					 (real_cmd == LYK_NOCACHE ||
+					  real_cmd == LYK_DOWNLOAD ||
+					  real_cmd == LYK_HEAD ||
+					  (real_cmd == LYK_SUBMIT &&
+					   !textinput_activated)) ?
+					 FOR_PANEL : FOR_INPUT);
+		if (user_mode == NOVICE_MODE &&
+		    textinput_activated &&
+		    (real_cmd == LYK_ACTIVATE || real_cmd == LYK_SUBMIT)) {
+		    form_noviceline(links[curdoc.link].l_form->disabled);
+		}
+	    }
+
+	    *c = change_form_link(curdoc.link,
+				  &newdoc, refresh_screen,
+				  FALSE,
+				  (BOOLEAN) (real_cmd == LYK_SUBMIT ||
+					     real_cmd == LYK_NOCACHE ||
+					     real_cmd == LYK_DOWNLOAD ||
+					     real_cmd == LYK_HEAD));
+	    if (*c != LKC_DONE || *refresh_screen) {
+		/*
+		 * Cannot have been a submit field for which newdoc was filled
+		 * in.  - kw
+		 */
+		if ((links[curdoc.link].l_form->type == F_SUBMIT_TYPE ||
+		     links[curdoc.link].l_form->type == F_IMAGE_SUBMIT_TYPE ||
+		     links[curdoc.link].l_form->type == F_TEXT_SUBMIT_TYPE) &&
+		    links[curdoc.link].l_form->submit_method
+		    != URL_MAIL_METHOD) {
+		    /*
+		     * Try to undo change of newdoc.title done above.
+		     */
+		    if (HText_getTitle()) {
+			StrAllocCopy(newdoc.title, HText_getTitle());
+		    } else if (curdoc.title) {
+			StrAllocCopy(newdoc.title, curdoc.title);
+		    }
+		}
+	    } else {
+		if (HTOutputFormat == HTAtom_for("www/download") &&
+		    newdoc.post_data != NULL &&
+		    newdoc.safe == FALSE) {
+
+		    if ((HText_POSTReplyLoaded(&newdoc) == TRUE) &&
+			HTConfirm(CONFIRM_POST_RESUBMISSION) == FALSE) {
+			HTInfoMsg(CANCELLED);
+			HTOutputFormat = WWW_PRESENT;
+			LYforce_no_cache = FALSE;
+			copy_address(&newdoc, &curdoc);
+			StrAllocCopy(newdoc.title, curdoc.title);
+			BStrCopy(newdoc.post_data, curdoc.post_data);
+			StrAllocCopy(newdoc.post_content_type,
+				     curdoc.post_content_type);
+			StrAllocCopy(newdoc.bookmark, curdoc.bookmark);
+			newdoc.isHEAD = curdoc.isHEAD;
+			newdoc.safe = curdoc.safe;
+			newdoc.internal_link = curdoc.internal_link;
+			return 0;
+		    }
+		}
+		/*
+		 * Moved here from earlier to only apply when it should. 
+		 * Anyway, why should realm checking be overridden for form
+		 * submissions, this seems to be an unnecessary loophole??  But
+		 * that's the way it was, maybe there is some reason.  However,
+		 * at least make sure this doesn't weaken restrictions implied
+		 * by -validate!
+		 * - kw 1999-05-25
+		 */
+		if (check_realm && !LYValidate) {
+		    LYPermitURL = TRUE;
+		}
+	    }
+	    if (*c == LKC_DONE) {
+		*c = DO_NOTHING;
+	    } else if (*c == 23) {
+		*c = DO_NOTHING;
+		*refresh_screen = TRUE;
+	    } else {
+		/* Avoid getting stuck with repeatedly calling
+		 * handle_LYK_ACTIVATE(), instead of calling change_form_link()
+		 * directly from mainloop(), for text input fields.  - kw
+		 */
+		switch (LKC_TO_C(*c)) {
+		case '\n':
+		case '\r':
+		default:
+		    if ((real_cmd == LYK_ACTIVATE || real_cmd == LYK_SUBMIT) &&
+			F_TEXTLIKE(links[curdoc.link].l_form->type) &&
+			textinput_activated) {
+			return 3;
+		    }
+		    break;
+		}
+	    }
+	    return 2;
+	} else {
+	    /*
+	     * Not a forms link.
+	     *
+	     * Make sure this isn't a spoof in an account with restrictions on
+	     * file URLs.  - FM
+	     */
+	    if (no_file_url && isFILE_URL(links[curdoc.link].lname)) {
+		if (!isFILE_URL(curdoc.address) &&
+		    !((isLYNXKEYMAP(curdoc.address) ||
+#ifndef USE_CACHEJAR
+		       isLYNXCOOKIE(curdoc.address)) &&
+#else
+		       isLYNXCOOKIE(curdoc.address) ||
+		       isLYNXCACHE(curdoc.address)) &&
+#endif
+		      !strncmp(links[curdoc.link].lname,
+			       helpfilepath,
+			       strlen(helpfilepath)))) {
+		    HTAlert(FILE_SERVED_LINKS_DISALLOWED);
+		    reloading = FALSE;
+		    return 0;
+		} else if (curdoc.bookmark != NULL) {
+		    HTAlert(FILE_BOOKMARKS_DISALLOWED);
+		    reloading = FALSE;
+		    return 0;
+		}
+	    }
+	    /*
+	     * Make sure this isn't a spoof attempt via an internal URL in a
+	     * non-internal document.  - FM
+	     */
+	    if ((isLYNXCOOKIE(links[curdoc.link].lname) &&
+		 (strcmp(NonNull(curdoc.title), COOKIE_JAR_TITLE) ||
+		  !isLYNXCOOKIE(curdoc.address))) ||
+#ifdef USE_CACHEJAR
+		(isLYNXCACHE(links[curdoc.link].lname) &&
+		 (strcmp(NonNull(curdoc.title), CACHE_JAR_TITLE) ||
+		  !isLYNXCACHE(curdoc.address))) ||
+#endif
+#ifdef DIRED_SUPPORT
+		(isLYNXDIRED(links[curdoc.link].lname) &&
+		 !LYIsUIPage(curdoc.address, UIP_DIRED_MENU) &&
+		 !LYIsUIPage(curdoc.address, UIP_PERMIT_OPTIONS) &&
+#ifdef OK_INSTALL
+		 !LYIsUIPage(curdoc.address, UIP_INSTALL) &&
+#endif /* OK_INSTALL */
+		 !LYIsUIPage(curdoc.address, UIP_UPLOAD_OPTIONS)) ||
+#endif /* DIRED_SUPPORT */
+		(isLYNXDOWNLOAD(links[curdoc.link].lname) &&
+		 !LYIsUIPage(curdoc.address, UIP_DOWNLOAD_OPTIONS)) ||
+		(isLYNXHIST(links[curdoc.link].lname) &&
+		 !LYIsUIPage(curdoc.address, UIP_HISTORY) &&
+		 !LYIsUIPage(curdoc.address, UIP_LIST_PAGE) &&
+		 !LYIsUIPage(curdoc.address, UIP_ADDRLIST_PAGE)) ||
+		(isLYNXPRINT(links[curdoc.link].lname) &&
+		 !LYIsUIPage(curdoc.address, UIP_PRINT_OPTIONS))) {
+		HTAlert(SPECIAL_VIA_EXTERNAL_DISALLOWED);
+		HTOutputFormat = WWW_PRESENT;
+		LYforce_no_cache = FALSE;
+		reloading = FALSE;
+		return 0;
+	    }
+#ifdef USE_EXTERNALS
+	    if (run_external(links[curdoc.link].lname, TRUE)) {
+		*refresh_screen = TRUE;
+		return 0;
+	    }
+#endif /* USE_EXTERNALS */
+
+	    /*
+	     * Follow a normal link or anchor.
+	     */
+	    set_address(&newdoc, links[curdoc.link].lname);
+	    StrAllocCopy(newdoc.title, LYGetHiliteStr(curdoc.link, 0));
+#ifndef DONT_TRACK_INTERNAL_LINKS
+	    /*
+	     * For internal links, retain POST content if present.  If we are
+	     * on the List Page, prevent pushing it on the history stack. 
+	     * Otherwise set try_internal to signal that the top of the loop
+	     * should attempt to reposition directly, without calling getfile. 
+	     * - kw
+	     */
+	    /*
+	     * Might be an internal link anchor in the same doc.  If so, take
+	     * the try_internal shortcut if we didn't fall through from
+	     * LYK_NOCACHE.  - kw
+	     */
+	    newdoc.internal_link =
+		(links[curdoc.link].type == WWW_INTERN_LINK_TYPE);
+	    if (newdoc.internal_link) {
+		/*
+		 * Special case of List Page document with an internal link
+		 * indication, which may really stand for an internal link
+		 * within the document the List Page is about.  - kw
+		 */
+		if (LYIsListpageTitle(NonNull(curdoc.title)) &&
+		    (LYIsUIPage(curdoc.address, UIP_LIST_PAGE) ||
+		     LYIsUIPage(curdoc.address, UIP_ADDRLIST_PAGE))) {
+		    if (check_history()) {
+			LYinternal_flag = TRUE;
+		    } else {
+			HTLastConfirmCancelled();	/* reset flag */
+			if (!confirm_post_resub(newdoc.address,
+						newdoc.title,
+						((LYresubmit_posts &&
+						  HText_POSTReplyLoaded(&newdoc))
+						 ? 1
+						 : 2),
+						2)) {
+			    if (HTLastConfirmCancelled() ||
+				(LYresubmit_posts &&
+				 cmd != LYK_NOCACHE &&
+				 !HText_POSTReplyLoaded(&newdoc))) {
+				/* cancel the whole thing */
+				LYforce_no_cache = FALSE;
+				reloading = FALSE;
+				copy_address(&newdoc, &curdoc);
+				StrAllocCopy(newdoc.title, curdoc.title);
+				newdoc.internal_link = curdoc.internal_link;
+				HTInfoMsg(CANCELLED);
+				return 1;
+			    } else if (LYresubmit_posts &&
+				       cmd != LYK_NOCACHE) {
+				/* If LYresubmit_posts is set, and the
+				   answer was No, and the key wasn't
+				   NOCACHE, and we have a cached copy,
+				   then use it. - kw */
+				LYforce_no_cache = FALSE;
+			    } else {
+				/* if No, but not ^C or ^G, drop
+				 * the post data.  Maybe the link
+				 * wasn't meant to be internal after
+				 * all, here we can recover from that
+				 * assumption. - kw */
+				LYFreePostData(&newdoc);
+				newdoc.internal_link = FALSE;
+				HTAlert(DISCARDING_POST_DATA);
+			    }
+			}
+		    }
+		    /*
+		     * Don't push the List Page if we follow an internal link
+		     * given by it.  - kw
+		     */
+		    free_address(&curdoc);
+		} else if (cmd != LYK_NOCACHE) {
+		    *try_internal = TRUE;
+		}
+		if (!(LYresubmit_posts && newdoc.post_data))
+		    LYinternal_flag = TRUE;
+		/* We still set force_load so that history pushing
+		 * etc. will be done.  - kw
+		 */
+		*force_load = TRUE;
+		return 1;
+	    } else {
+		/*
+		 * Free POST content if not an internal link.  - kw
+		 */
+		LYFreePostData(&newdoc);
+	    }
+#endif /* TRACK_INTERNAL_LINKS */
+	    /*
+	     * Might be an anchor in the same doc from a POST form.  If so,
+	     * don't free the content.  -- FM
+	     */
+	    if (are_different(&curdoc, &newdoc)) {
+		LYFreePostData(&newdoc);
+		FREE(newdoc.bookmark);
+		if (isLYNXMESSAGES(newdoc.address))
+		    LYforce_no_cache = TRUE;
+	    }
+	    if (!no_jump && lynxjumpfile && curdoc.address &&
+		!strcmp(lynxjumpfile, curdoc.address)) {
+		LYJumpFileURL = TRUE;
+		LYUserSpecifiedURL = TRUE;
+	    } else if ((curdoc.title &&
+			(LYIsUIPage(curdoc.address, UIP_HISTORY) ||
+			 !strcmp(curdoc.title, HISTORY_PAGE_TITLE))) ||
+		       curdoc.bookmark != NULL ||
+		       (lynxjumpfile &&
+			!strcmp(lynxjumpfile, curdoc.address))) {
+		LYUserSpecifiedURL = TRUE;
+	    } else if (no_filereferer == TRUE && isFILE_URL(curdoc.address)) {
+		LYNoRefererForThis = TRUE;
+	    }
+	    newdoc.link = 0;
+	    *force_load = TRUE;	/* force MainLoop to reload */
+#ifdef USE_PRETTYSRC
+	    psrc_view = FALSE;	/* we get here if link is not internal */
+#endif
+
+#if defined(DIRED_SUPPORT) && !defined(__DJGPP__)
+	    if (lynx_edit_mode) {
+		DIRED_UNCACHE_2;
+		/*
+		 * Unescaping any slash chars in the URL, but avoid double
+		 * unescaping and too-early unescaping of other chars.  - KW
+		 */
+		HTUnEscapeSome(newdoc.address, "/");
+		/* avoid stripping final slash for root dir - kw */
+		if (strcasecomp(newdoc.address, "file://localhost/"))
+		    strip_trailing_slash(newdoc.address);
+	    }
+#endif /* DIRED_SUPPORT  && !__DJGPP__ */
+	    if (isLYNXCOOKIE(curdoc.address)
+#ifdef USE_CACHEJAR
+		|| isLYNXCACHE(curdoc.address)
+#endif
+		) {
+		HTuncache_current_document();
+	    }
+	}
+    }
+    return 0;
+}
+
+#ifdef EXP_ADDRLIST_PAGE
+static BOOLEAN handle_LYK_ADDRLIST(int *cmd)
+{
+    /*
+     * Don't do if already viewing list addresses page.
+     */
+    if (LYIsUIPage(curdoc.address, UIP_ADDRLIST_PAGE)) {
+	/*
+	 * Already viewing list page, so get out.
+	 */
+	*cmd = LYK_PREV_DOC;
+	return TRUE;
+    }
+
+    /*
+     * Print address list page to file.
+     */
+    if (showlist(&newdoc, FALSE) < 0)
+	return FALSE;
+    StrAllocCopy(newdoc.title, ADDRLIST_PAGE_TITLE);
+    /*
+     * showlist will set newdoc's other fields.  It may leave post_data intact
+     * so the list can be used to follow internal links in the current document
+     * even if it is a POST response.  - kw
+     */
+
+    if (LYValidate || check_realm) {
+	LYPermitURL = TRUE;
+	StrAllocCopy(lynxlistfile, newdoc.address);
+    }
+    return FALSE;
+}
+#endif /* EXP_ADDRLIST_PAGE */
+
+static void handle_LYK_ADD_BOOKMARK(BOOLEAN *refresh_screen,
+				    int *old_c,
+				    int real_c)
+{
+    int c;
+
+    if (LYValidate) {
+	if (*old_c != real_c) {
+	    *old_c = real_c;
+	    HTUserMsg(BOOKMARKS_DISABLED);
+	}
+	return;
+    }
+
+    if (!LYIsUIPage(curdoc.address, UIP_HISTORY) &&
+	!LYIsUIPage(curdoc.address, UIP_SHOWINFO) &&
+	!LYIsUIPage(curdoc.address, UIP_PRINT_OPTIONS) &&
+#ifdef DIRED_SUPPORT
+	!LYIsUIPage(curdoc.address, UIP_DIRED_MENU) &&
+	!LYIsUIPage(curdoc.address, UIP_PERMIT_OPTIONS) &&
+	!LYIsUIPage(curdoc.address, UIP_UPLOAD_OPTIONS) &&
+#endif /* DIRED_SUPPORT */
+	!LYIsUIPage(curdoc.address, UIP_DOWNLOAD_OPTIONS) &&
+	!isLYNXCOOKIE(curdoc.address) &&
+#ifdef USE_CACHEJAR
+	!isLYNXCACHE(curdoc.address) &&
+#endif
+	!LYIsUIPage(curdoc.address, UIP_OPTIONS_MENU) &&
+	((nlinks <= 0) ||
+	 (links[curdoc.link].lname != NULL &&
+	  !isLYNXHIST(links[curdoc.link].lname) &&
+	  !isLYNXPRINT(links[curdoc.link].lname) &&
+	  !isLYNXDIRED(links[curdoc.link].lname) &&
+	  !isLYNXDOWNLOAD(links[curdoc.link].lname) &&
+	  !isLYNXCOOKIE(links[curdoc.link].lname) &&
+#ifdef USE_CACHEJAR
+	  !isLYNXCACHE(links[curdoc.link].lname) &&
+#endif
+	  !isLYNXPRINT(links[curdoc.link].lname)))) {
+	if (nlinks > 0) {
+	    if (curdoc.post_data == NULL &&
+		curdoc.bookmark == NULL &&
+		!LYIsUIPage(curdoc.address, UIP_LIST_PAGE) &&
+		!LYIsUIPage(curdoc.address, UIP_ADDRLIST_PAGE) &&
+		!LYIsUIPage(curdoc.address, UIP_VLINKS)) {
+		/*
+		 * The document doesn't have POST content, and is not a
+		 * bookmark file, nor is the list or visited links page, so we
+		 * can save either that or the link.  - FM
+		 */
+		_statusline(BOOK_D_L_OR_CANCEL);
+		if ((c = LYgetch_single()) == 'D') {
+		    save_bookmark_link(curdoc.address, curdoc.title);
+		    *refresh_screen = TRUE;	/* MultiBookmark support */
+		    goto check_add_bookmark_to_self;
+		}
+	    } else {
+		if (LYMultiBookmarks == MBM_OFF &&
+		    curdoc.bookmark != NULL &&
+		    strstr(curdoc.address,
+			   (*bookmark_page == '.'
+			    ? (bookmark_page + 1)
+			    : bookmark_page)) != 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 if (curdoc.post_data != NULL &&
+			   links[curdoc.link].type == WWW_INTERN_LINK_TYPE) {
+		    /*
+		     * Internal link, and document has POST content.
+		     */
+		    HTUserMsg(NOBOOK_POST_FORM);
+		    return;
+		} 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_single();
+	    }
+	    if (c == 'L') {
+		if (curdoc.post_data != NULL &&
+		    links[curdoc.link].type == WWW_INTERN_LINK_TYPE) {
+		    /*
+		     * Internal link, and document has POST content.
+		     */
+		    HTUserMsg(NOBOOK_POST_FORM);
+		    return;
+		}
+		/*
+		 * User does want to save the link.  - FM
+		 */
+		if (links[curdoc.link].type != WWW_FORM_LINK_TYPE) {
+		    save_bookmark_link(links[curdoc.link].lname,
+				       LYGetHiliteStr(curdoc.link, 0));
+		    *refresh_screen = TRUE;	/* MultiBookmark support */
+		} else {
+		    HTUserMsg(NOBOOK_FORM_FIELD);
+		    return;
+		}
+	    } else {
+		return;
+	    }
+	} else if (curdoc.post_data != NULL) {
+	    /*
+	     * No links, and document has POST content.  - FM
+	     */
+	    HTUserMsg(NOBOOK_POST_FORM);
+	    return;
+	} else if (curdoc.bookmark != NULL) {
+	    /*
+	     * It's a bookmark file from which all of the links were deleted. 
+	     * - FM
+	     */
+	    HTUserMsg(BOOKMARKS_NOLINKS);
+	    return;
+	} else {
+	    _statusline(BOOK_D_OR_CANCEL);
+	    if (LYgetch_single() == 'D') {
+		save_bookmark_link(curdoc.address, curdoc.title);
+		*refresh_screen = TRUE;		/* MultiBookmark support */
+	    } else {
+		return;
+	    }
+	}
+      check_add_bookmark_to_self:
+	if (curdoc.bookmark && BookmarkPage &&
+	    !strcmp(curdoc.bookmark, BookmarkPage)) {
+	    HTuncache_current_document();
+	    move_address(&newdoc, &curdoc);
+	    StrAllocCopy(newdoc.bookmark, curdoc.bookmark);
+	    newdoc.line = curdoc.line;
+	    newdoc.link = curdoc.link;
+	    newdoc.internal_link = FALSE;
+	}
+    } else {
+	if (*old_c != real_c) {
+	    *old_c = real_c;
+	    HTUserMsg(NOBOOK_HSML);
+	}
+    }
+}
+
+static void handle_LYK_CLEAR_AUTH(int *old_c,
+				  int real_c)
+{
+    if (*old_c != real_c) {
+	*old_c = real_c;
+	if (HTConfirm(CLEAR_ALL_AUTH_INFO)) {
+	    FREE(authentication_info[0]);
+	    FREE(authentication_info[1]);
+	    FREE(proxyauth_info[0]);
+	    FREE(proxyauth_info[1]);
+	    HTClearHTTPAuthInfo();
+#ifndef DISABLE_NEWS
+	    HTClearNNTPAuthInfo();
+#endif
+#ifndef DISABLE_FTP
+	    HTClearFTPPassword();
+#endif
+	    HTUserMsg(AUTH_INFO_CLEARED);
+	} else {
+	    HTUserMsg(CANCELLED);
+	}
+    }
+}
+
+static int handle_LYK_COMMAND(char *user_input_buffer)
+{
+    LYKeymapCode ch;
+    Kcmd *mp;
+    char *src, *tmp;
+
+    *user_input_buffer = 0;
+    _statusline(": ");
+    if (LYgetstr(user_input_buffer, VISIBLE, MAX_LINE, RECALL_CMD) >= 0) {
+	src = LYSkipBlanks(user_input_buffer);
+	tmp = LYSkipNonBlanks(src);
+	*tmp = 0;
+	ch = ((mp = LYStringToKcmd(src)) != 0) ? mp->code : LYK_UNKNOWN;
+	CTRACE((tfp, "LYK_COMMAND(%s.%s) = %d\n", src, tmp, (int) ch));
+	if (ch == 0) {
+	    return *src ? -1 : 0;
+	}
+	/* FIXME: reuse the rest of the buffer for parameters */
+	return ch;
+    }
+    return 0;
+}
+
+static void handle_LYK_COMMENT(BOOLEAN *refresh_screen,
+			       char **owner_address_p,
+			       int *old_c,
+			       int real_c)
+{
+    int c;
+
+    if (!*owner_address_p &&
+	strncasecomp(curdoc.address, "http", 4)) {
+	if (*old_c != real_c) {
+	    *old_c = real_c;
+	    HTUserMsg(NO_OWNER);
+	}
+    } else if (no_mail) {
+	if (*old_c != real_c) {
+	    *old_c = real_c;
+	    HTUserMsg(MAIL_DISALLOWED);
+	}
+    } else {
+	if (HTConfirmDefault(CONFIRM_COMMENT, NO)) {
+	    if (!*owner_address_p) {
+		/*
+		 * No owner defined, so make a guess and and offer it to the
+		 * user.  - FM
+		 */
+		char *address = NULL;
+		char *temp = HTParse(curdoc.address, "", PARSE_PATH);
+		char *cp;
+
+		if (temp != NULL) {
+		    HTUnEscape(temp);
+		    if (LYIsTilde(*temp) && strlen(temp) > 1) {
+			/*
+			 * It's a ~user URL so guess user@host.  - FM
+			 */
+			if ((cp = strchr((temp + 1), '/')) != NULL)
+			    *cp = '\0';
+			StrAllocCopy(address, STR_MAILTO_URL);
+			StrAllocCat(address, (temp + 1));
+			StrAllocCat(address, "@");
+		    }
+		    FREE(temp);
+		}
+		if (address == NULL)
+		    /*
+		     * Wasn't a ~user URL so guess WebMaster@host.  - FM
+		     */
+		    StrAllocCopy(address, "mailto:WebMaster@");
+		temp = HTParse(curdoc.address, "", PARSE_HOST);
+		StrAllocCat(address, temp);
+		HTSprintf0(&temp, NO_OWNER_USE, address);
+		c = HTConfirmDefault(temp, NO);
+		FREE(temp);
+		if (c == YES) {
+		    StrAllocCopy(*owner_address_p, address);
+		    FREE(address);
+		} else {
+		    FREE(address);
+		    return;
+		}
+	    }
+	    if (is_url(*owner_address_p) != MAILTO_URL_TYPE) {
+		/*
+		 * The address is a URL.  Just follow the link.
+		 */
+		set_address(&newdoc, *owner_address_p);
+		newdoc.internal_link = FALSE;
+	    } else {
+		/*
+		 * The owner_address is a mailto:  URL.
+		 */
+		const char *kp = HText_getRevTitle();
+		const char *id = HText_getMessageID();
+		char *tmptitle = NULL;
+
+		if (!kp && HTMainAnchor) {
+		    kp = HTAnchor_subject(HTMainAnchor);
+		    if (non_empty(kp)) {
+			if (strncasecomp(kp, "Re: ", 4)) {
+			    StrAllocCopy(tmptitle, "Re: ");
+			    StrAllocCat(tmptitle, kp);
+			    kp = tmptitle;
+			}
+		    }
+		}
+
+		if (strchr(*owner_address_p, ':') != NULL)
+		    /*
+		     * Send a reply.  The address is after the colon.
+		     */
+		    reply_by_mail(strchr(*owner_address_p, ':') + 1,
+				  curdoc.address,
+				  NonNull(kp), id);
+		else
+		    reply_by_mail(*owner_address_p, curdoc.address,
+				  NonNull(kp), id);
+
+		FREE(tmptitle);
+		*refresh_screen = TRUE;		/* to force a showpage */
+	    }
+	}
+    }
+}
+
+#ifdef USE_CACHEJAR
+static BOOLEAN handle_LYK_CACHE_JAR(int *cmd)
+{
+    /*
+     * Don't do this if already viewing cache jar.
+     */
+    if (!isLYNXCACHE(curdoc.address)) {
+	set_address(&newdoc, STR_LYNXCACHE "/");
+	LYFreePostData(&newdoc);
+	FREE(newdoc.bookmark);
+	newdoc.isHEAD = FALSE;
+	newdoc.safe = FALSE;
+	newdoc.internal_link = FALSE;
+	LYforce_no_cache = TRUE;
+	if (LYValidate || check_realm) {
+	    LYPermitURL = TRUE;
+	}
+    } else {
+	/*
+	 * If already in the cache jar, get out.
+	 */
+	*cmd = LYK_PREV_DOC;
+	return TRUE;
+    }
+    return FALSE;
+}
+#endif /* USE_CACHEJAR */
+
+static BOOLEAN handle_LYK_COOKIE_JAR(int *cmd)
+{
+    /*
+     * Don't do if already viewing the cookie jar.
+     */
+    if (!isLYNXCOOKIE(curdoc.address)) {
+	set_address(&newdoc, "LYNXCOOKIE:/");
+	LYFreePostData(&newdoc);
+	FREE(newdoc.bookmark);
+	newdoc.isHEAD = FALSE;
+	newdoc.safe = FALSE;
+	newdoc.internal_link = FALSE;
+	LYforce_no_cache = TRUE;
+	if (LYValidate || check_realm) {
+	    LYPermitURL = TRUE;
+	}
+    } else {
+	/*
+	 * If already in the cookie jar, get out.
+	 */
+	*cmd = LYK_PREV_DOC;
+	return TRUE;
+    }
+    return FALSE;
+}
+
+#if defined(DIRED_SUPPORT)
+static void handle_LYK_CREATE(void)
+{
+    if (lynx_edit_mode && !no_dired_support) {
+	if (local_create(&curdoc) > 0) {
+	    DIRED_UNCACHE_1;
+	    move_address(&newdoc, &curdoc);
+	    LYFreePostData(&newdoc);
+	    FREE(newdoc.bookmark);
+	    newdoc.isHEAD = FALSE;
+	    newdoc.safe = FALSE;
+	    newdoc.line = curdoc.line;
+	    newdoc.link = curdoc.link > -1 ? curdoc.link : 0;
+	    LYclear();
+	}
+    }
+}
+#endif /* DIRED_SUPPORT */
+
+static void handle_LYK_DEL_BOOKMARK(BOOLEAN *refresh_screen,
+				    int *old_c,
+				    int real_c)
+{
+    if (curdoc.bookmark != NULL) {
+	if (HTConfirmDefault(CONFIRM_BOOKMARK_DELETE, NO) != YES)
+	    return;
+	remove_bookmark_link(links[curdoc.link].anchor_number - 1,
+			     curdoc.bookmark);
+    } else {			/* behave like REFRESH for backward compatibility */
+	*refresh_screen = TRUE;
+	if (*old_c != real_c) {
+	    *old_c = real_c;
+	    lynx_force_repaint();
+	}
+	return;
+    }
+    do_cleanup_after_delete();
+}
+
+#if defined(DIRED_SUPPORT) || defined(VMS)
+static void handle_LYK_DIRED_MENU(BOOLEAN *refresh_screen,
+				  int *old_c GCC_UNUSED,
+				  int real_c GCC_UNUSED)
+{
+#ifdef VMS
+    char *cp, *temp = 0;
+    const char *test = HTGetProgramPath(ppCSWING);
+
+    /*
+     * Check if the CSwing Directory/File Manager is available.  Will be
+     * disabled if CSWING path is NULL, zero-length, or "none" (case
+     * insensitive), if no_file_url was set via the file_url restriction, if
+     * no_goto_file was set for the anonymous account, or if HTDirAccess was
+     * set to HT_DIR_FORBID or HT_DIR_SELECTIVE via the -nobrowse or -selective
+     * switches.  - FM
+     */
+    if (isEmpty(test) ||
+	!strcasecomp(test, "none") ||
+	no_file_url || no_goto_file ||
+	HTDirAccess == HT_DIR_FORBID ||
+	HTDirAccess == HT_DIR_SELECTIVE) {
+	if (*old_c != real_c) {
+	    *old_c = real_c;
+	    HTUserMsg(DFM_NOT_AVAILABLE);
+	}
+	return;
+    }
+
+    /*
+     * If we are viewing a local directory listing or a local file which is not
+     * temporary, invoke CSwing with the URL's directory converted to VMS path
+     * specs and passed as the argument, so we start up CSwing positioned on
+     * that node of the directory tree.  Otherwise, pass the current default
+     * directory as the argument.  - FM
+     */
+    if (LYisLocalFile(curdoc.address) &&
+	strncasecomp(curdoc.address,
+		     lynx_temp_space, strlen(lynx_temp_space))) {
+	/*
+	 * We are viewing a local directory or a local file which is not
+	 * temporary.  - FM
+	 */
+	struct stat stat_info;
+
+	cp = HTParse(curdoc.address, "", PARSE_PATH | PARSE_PUNCTUATION);
+	HTUnEscape(cp);
+	if (HTStat(cp, &stat_info) == -1) {
+	    CTRACE((tfp, "mainloop: Can't stat %s\n", cp));
+	    FREE(cp);
+	    HTSprintf0(&temp, "%s []", HTGetProgramPath(ppCSWING));
+	    *refresh_screen = TRUE;	/* redisplay */
+	} else {
+	    char *VMSdir = NULL;
+
+	    if (S_ISDIR(stat_info.st_mode)) {
+		/*
+		 * We're viewing a local directory.  Make that the CSwing
+		 * argument.  - FM
+		 */
+		LYAddPathSep(&cp);
+		StrAllocCopy(VMSdir, HTVMS_name("", cp));
+		FREE(cp);
+	    } else {
+		/*
+		 * We're viewing a local file.  Make its directory the CSwing
+		 * argument.  - FM
+		 */
+		StrAllocCopy(VMSdir, HTVMS_name("", cp));
+		FREE(cp);
+		if ((cp = strrchr(VMSdir, ']')) != NULL) {
+		    *(cp + 1) = '\0';
+		    cp == NULL;
+		} else if ((cp = strrchr(VMSdir, ':')) != NULL) {
+		    *(cp + 1) = '\0';
+		    cp == NULL;
+		}
+	    }
+	    HTSprintf0(&temp, "%s %s", HTGetProgramPath(ppCSWING), VMSdir);
+	    FREE(VMSdir);
+	    /*
+	     * Uncache the current document in case we change, move, or delete
+	     * it during the CSwing session.  - FM
+	     */
+	    /* could use DIRED_UNCACHE_1 but it's currently only defined
+	       for dired - kw */
+	    HTuncache_current_document();
+	    move_address(&newdoc, &curdoc);
+	    StrAllocCopy(newdoc.title, NonNull(curdoc.title));
+	    StrAllocCopy(newdoc.bookmark, curdoc.bookmark);
+	    newdoc.line = curdoc.line;
+	    newdoc.link = curdoc.link;
+	}
+    } else {
+	/*
+	 * We're not viewing a local directory or file.  Pass CSwing the
+	 * current default directory as an argument and don't uncache the
+	 * current document.  - FM
+	 */
+	HTSprintf0(&temp, "%s []", HTGetProgramPath(ppCSWING));
+	*refresh_screen = TRUE;	/* redisplay */
+    }
+    stop_curses();
+    LYSystem(temp);
+    start_curses();
+    FREE(temp);
+#else
+    /*
+     * Don't do if not allowed or already viewing the menu.
+     */
+    if (lynx_edit_mode && !no_dired_support &&
+	!LYIsUIPage(curdoc.address, UIP_DIRED_MENU) &&
+	strcmp(NonNull(curdoc.title), DIRED_MENU_TITLE)) {
+	dired_options(&curdoc, &newdoc.address);
+	*refresh_screen = TRUE;	/* redisplay */
+    }
+#endif /* VMS */
+}
+#endif /* defined(DIRED_SUPPORT) || defined(VMS) */
+
+static int handle_LYK_DOWNLOAD(int *cmd,
+			       int *old_c,
+			       int real_c)
+{
+
+    /*
+     * Don't do if both download and disk_save are restricted.
+     */
+    if (LYValidate ||
+	(no_download && !override_no_download && no_disk_save)) {
+	if (*old_c != real_c) {
+	    *old_c = real_c;
+	    HTUserMsg(DOWNLOAD_DISABLED);
+	}
+	return 0;
+    }
+
+    /*
+     * Don't do if already viewing download options page.
+     */
+    if (LYIsUIPage(curdoc.address, UIP_DOWNLOAD_OPTIONS))
+	return 0;
+
+    if (do_change_link() == -1)
+	return 1;		/* mouse stuff was confused, ignore - kw */
+    if (nlinks > 0) {
+	if (links[curdoc.link].type == WWW_FORM_LINK_TYPE) {
+	    if (links[curdoc.link].l_form->type == F_SUBMIT_TYPE ||
+		links[curdoc.link].l_form->type == F_IMAGE_SUBMIT_TYPE ||
+		links[curdoc.link].l_form->type == F_TEXT_SUBMIT_TYPE) {
+		if (links[curdoc.link].l_form->submit_method ==
+		    URL_MAIL_METHOD) {
+		    if (*old_c != real_c) {
+			*old_c = real_c;
+			HTUserMsg(NO_DOWNLOAD_MAILTO_ACTION);
+		    }
+		    return 0;
+		}
+		if (isLYNXOPTIONS(links[curdoc.link].l_form->submit_action)) {
+		    if (*old_c != real_c) {
+			*old_c = real_c;
+			HTUserMsg(NO_DOWNLOAD_SPECIAL);
+		    }
+		    return 0;
+		}
+		HTOutputFormat = HTAtom_for("www/download");
+		LYforce_no_cache = TRUE;
+		*cmd = LYK_ACTIVATE;
+		return 2;
+	    }
+	    if (*old_c != real_c) {
+		*old_c = real_c;
+		HTUserMsg(NO_DOWNLOAD_INPUT);
+	    }
+
+	} else if (isLYNXCOOKIE(curdoc.address)) {
+	    if (*old_c != real_c) {
+		*old_c = real_c;
+		HTUserMsg(NO_DOWNLOAD_COOKIES);
+	    }
+	} else if (LYIsUIPage(curdoc.address, UIP_PRINT_OPTIONS)) {
+	    if (*old_c != real_c) {
+		*old_c = real_c;
+		HTUserMsg(NO_DOWNLOAD_PRINT_OP);
+	    }
+#ifdef DIRED_SUPPORT
+	} else if (LYIsUIPage(curdoc.address, UIP_UPLOAD_OPTIONS)) {
+	    if (*old_c != real_c) {
+		*old_c = real_c;
+		HTUserMsg(NO_DOWNLOAD_UPLOAD_OP);
+	    }
+
+	} else if (LYIsUIPage(curdoc.address, UIP_PERMIT_OPTIONS)) {
+	    if (*old_c != real_c) {
+		*old_c = real_c;
+		HTUserMsg(NO_DOWNLOAD_PERMIT_OP);
+	    }
+
+	} else if (lynx_edit_mode && !no_dired_support &&
+		   !strstr(links[curdoc.link].lname, "/SugFile=")) {
+	    /*
+	     * Don't bother making a /tmp copy of the local file.
+	     */
+	    static DocInfo temp;
+
+	    copy_address(&temp, &newdoc);
+	    set_address(&newdoc, links[curdoc.link].lname);
+	    if (LYdownload_options(&newdoc.address,
+				   links[curdoc.link].lname) < 0)
+		copy_address(&newdoc, &temp);
+	    else
+		newdoc.internal_link = FALSE;
+	    LYFreeDocInfo(&temp);
+#endif /* DIRED_SUPPORT */
+
+	} else if (LYIsUIPage(curdoc.address, UIP_HISTORY) &&
+		   isLYNXHIST(links[curdoc.link].lname)) {
+	    int number = atoi(links[curdoc.link].lname + LEN_LYNXHIST);
+
+	    if (number >= nhist || number < 0) {
+		HTUserMsg(NO_DOWNLOAD_SPECIAL);
+		return 0;
+	    }
+	    if ((HDOC(number).post_data != NULL &&
+		 HDOC(number).safe != TRUE) &&
+		HTConfirm(CONFIRM_POST_RESUBMISSION) == FALSE) {
+		HTInfoMsg(CANCELLED);
+		return 0;
+	    }
+	    /*
+	     * OK, we download from history page, restore URL from stack.
+	     */
+	    copy_address(&newdoc, &HDOC(number));
+	    StrAllocCopy(newdoc.title, LYGetHiliteStr(curdoc.link, 0));
+	    StrAllocCopy(newdoc.bookmark, HDOC(number).bookmark);
+	    LYFreePostData(&newdoc);
+	    if (HDOC(number).post_data)
+		BStrCopy(newdoc.post_data,
+			 HDOC(number).post_data);
+	    if (HDOC(number).post_content_type)
+		StrAllocCopy(newdoc.post_content_type,
+			     HDOC(number).post_content_type);
+	    newdoc.isHEAD = HDOC(number).isHEAD;
+	    newdoc.safe = HDOC(number).safe;
+	    newdoc.internal_link = FALSE;
+	    newdoc.link = (user_mode == NOVICE_MODE) ? 1 : 0;
+	    HTOutputFormat = HTAtom_for("www/download");
+	    LYUserSpecifiedURL = TRUE;
+	    /*
+	     * Force the document to be reloaded.
+	     */
+	    LYforce_no_cache = TRUE;
+
+	} else if (!strncmp(links[curdoc.link].lname, "data:", 5)) {
+	    if (*old_c != real_c) {
+		*old_c = real_c;
+		HTAlert(UNSUPPORTED_DATA_URL);
+	    }
+
+	} else if (isLYNXCOOKIE(links[curdoc.link].lname) ||
+#ifdef USE_CACHEJAR
+		   isLYNXCACHE(links[curdoc.link].lname) ||
+#endif
+		   isLYNXDIRED(links[curdoc.link].lname) ||
+		   isLYNXDOWNLOAD(links[curdoc.link].lname) ||
+		   isLYNXPRINT(links[curdoc.link].lname) ||
+		   isLYNXOPTIONS(links[curdoc.link].lname) ||
+		   isLYNXHIST(links[curdoc.link].lname) ||
+	    /* handled above if valid - kw */
+/* @@@ should next two be downloadable? - kw */
+		   isLYNXHIST(links[curdoc.link].lname) ||
+		   isLYNXCFLAGS(links[curdoc.link].lname) ||
+		   isLYNXEXEC(links[curdoc.link].lname) ||
+		   isLYNXPROG(links[curdoc.link].lname)) {
+	    HTUserMsg(NO_DOWNLOAD_SPECIAL);
+
+	} else if (isMAILTO_URL(links[curdoc.link].lname)) {
+	    HTUserMsg(NO_DOWNLOAD_MAILTO_LINK);
+
+	    /*
+	     * From here on we could have a remote host, so check if that's
+	     * allowed.
+	     *
+	     * We copy all these checks from getfile() to LYK_DOWNLOAD here
+	     * because LYNXDOWNLOAD:// will NOT be pushing the previous
+	     * document into the history stack so preserve getfile() from
+	     * returning a wrong status (NULLFILE).
+	     */
+	} else if (local_host_only &&
+		   !(LYisLocalHost(links[curdoc.link].lname) ||
+		     LYisLocalAlias(links[curdoc.link].lname))) {
+	    HTUserMsg(ACCESS_ONLY_LOCALHOST);
+	} else {		/* Not a forms, options or history link */
+	    /*
+	     * Follow a normal link or anchor.  Note that if it's an anchor
+	     * within the same document, entire document will be downloaded.
+	     */
+	    set_address(&newdoc, links[curdoc.link].lname);
+	    StrAllocCopy(newdoc.title, LYGetHiliteStr(curdoc.link, 0));
+#ifndef DONT_TRACK_INTERNAL_LINKS
+	    /*
+	     * Might be an internal link in the same doc from a POST form.  If
+	     * so, don't free the content.  - kw
+	     */
+	    if (links[curdoc.link].type != WWW_INTERN_LINK_TYPE)
+#else
+	    /*
+	     * Might be an anchor in the same doc from a POST form.  If so,
+	     * don't free the content.  -- FM
+	     */
+	    if (are_different(&curdoc, &newdoc))
+#endif /* TRACK_INTERNAL_LINKS */
+	    {
+		LYFreePostData(&newdoc);
+		FREE(newdoc.bookmark);
+		newdoc.isHEAD = FALSE;
+		newdoc.safe = FALSE;
+	    }
+	    newdoc.internal_link = FALSE;
+	    newdoc.link = (user_mode == NOVICE_MODE) ? 1 : 0;
+	    HTOutputFormat = HTAtom_for("www/download");
+	    /*
+	     * Force the document to be reloaded.
+	     */
+	    LYforce_no_cache = TRUE;
+	}
+    } else if (*old_c != real_c) {
+	*old_c = real_c;
+	HTUserMsg(NO_DOWNLOAD_CHOICE);
+    }
+    return 0;
+}
+
+static void handle_LYK_DOWN_xxx(int *old_c,
+				int real_c,
+				int scroll_by)
+{
+    int i;
+
+    if (more_text) {
+	LYChgNewline(scroll_by);
+	if (nlinks > 0 && curdoc.link > -1 &&
+	    links[curdoc.link].ly > scroll_by) {
+	    newdoc.link = curdoc.link;
+	    for (i = 0; links[i].ly <= scroll_by; i++)
+		--newdoc.link;
+	}
+    } else if (*old_c != real_c) {
+	*old_c = real_c;
+	HTInfoMsg(ALREADY_AT_END);
+    }
+}
+
+static void handle_LYK_DOWN_HALF(int *old_c,
+				 int real_c)
+{
+    handle_LYK_DOWN_xxx(old_c, real_c, display_lines / 2);
+}
+
+static void handle_LYK_DOWN_LINK(int *follow_col,
+				 int *old_c,
+				 int real_c)
+{
+    if (curdoc.link < (nlinks - 1)) {	/* more links? */
+	int newlink;
+
+	if (*follow_col == -1) {
+	    const char *text = LYGetHiliteStr(curdoc.link, 0);
+
+	    *follow_col = links[curdoc.link].lx;
+
+	    if (text != NULL)
+		*follow_col += (int) strlen(text) / 2;
+	}
+
+	newlink = find_link_near_col(*follow_col, 1);
+	if (newlink > -1) {
+	    set_curdoc_link(newlink);
+	} else if (more_text) {	/* next page */
+	    LYChgNewline(display_lines);
+	} else if (*old_c != real_c) {
+	    *old_c = real_c;
+	    HTUserMsg(NO_LINKS_BELOW);
+	    return;
+	}
+    } else if (more_text) {	/* next page */
+	LYChgNewline(display_lines);
+    } else if (*old_c != real_c) {
+	*old_c = real_c;
+	HTInfoMsg(ALREADY_AT_END);
+    }
+}
+
+static void handle_LYK_DOWN_TWO(int *old_c,
+				int real_c)
+{
+    handle_LYK_DOWN_xxx(old_c, real_c, 2);
+}
+
+static int handle_LYK_DWIMEDIT(int *cmd,
+			       int *old_c,
+			       int real_c)
+{
+#ifdef TEXTAREA_AUTOEXTEDIT
+    /*
+     * If we're in a forms TEXTAREA, invoke the editor on *its* contents,
+     * rather than attempting to edit the html source document.  KED
+     */
+    if (nlinks > 0 &&
+	LinkIsTextarea(curdoc.link)) {
+	*cmd = LYK_EDIT_TEXTAREA;
+	return 2;
+    }
+
+    /*
+     * If we're in a forms TEXT type, tell user the request is bogus (though in
+     * reality, without this trap, if the document with the TEXT field is
+     * local, the editor *would* be invoked on the source .html file; eg, the
+     * o(ptions) form tempfile).
+     *
+     * [This is done to avoid possible user confusion, due to auto invocation
+     * of the editor on the TEXTAREA's contents via the above if() statement.]
+     */
+    if (nlinks > 0 &&
+	links[curdoc.link].type == WWW_FORM_LINK_TYPE &&
+	links[curdoc.link].l_form->type == F_TEXT_TYPE) {
+	HTUserMsg(CANNOT_EDIT_FIELD);
+	return 1;
+    }
+
+    if (no_editor) {
+	if (*old_c != real_c) {
+	    *old_c = real_c;
+	    HTUserMsg(ANYEDIT_DISABLED);
+	}
+	return 1;
+    }
+#endif /* TEXTAREA_AUTOEXTEDIT */
+    return 0;
+}
+
+static int handle_LYK_ECGOTO(int *ch,
+			     char *user_input_buffer,
+			     char **old_user_input,
+			     int *old_c,
+			     int real_c)
+{
+    if (no_goto && !LYValidate) {
+	/*
+	 * Go to not allowed.  - FM
+	 */
+	if (*old_c != real_c) {
+	    *old_c = real_c;
+	    HTUserMsg(GOTO_DISALLOWED);
+	}
+	return 0;
+    }
+#ifdef DIRED_SUPPORT
+    if (LYIsUIPage(curdoc.address, UIP_DIRED_MENU) ||
+	LYIsUIPage(curdoc.address, UIP_PERMIT_OPTIONS) ||
+	LYIsUIPage(curdoc.address, UIP_UPLOAD_OPTIONS)) {
+	/*
+	 * Disallow editing of File Management URLs.  - FM
+	 */
+	if (*old_c != real_c) {
+	    *old_c = real_c;
+	    HTUserMsg(EDIT_FM_MENU_URLS_DISALLOWED);
+	}
+	return 0;
+    }
+#endif /* DIRED_SUPPORT */
+
+    /*
+     * Save the current user_input_buffer string, and load the current
+     * document's address.
+     */
+    StrAllocCopy(*old_user_input, user_input_buffer);
+    LYstrncpy(user_input_buffer,
+	      curdoc.address,
+	      (MAX_LINE - 1));
+
+    /*
+     * Warn the user if the current document has POST data associated with it. 
+     * - FM
+     */
+    if (curdoc.post_data)
+	HTAlert(CURRENT_DOC_HAS_POST_DATA);
+
+    /*
+     * Offer the current document's URL for editing.  - FM
+     */
+    _statusline(EDIT_CURDOC_URL);
+    if (((*ch = LYgetstr(user_input_buffer, VISIBLE,
+			 MAX_LINE, RECALL_URL)) >= 0) &&
+	user_input_buffer[0] != '\0' &&
+	strcmp(user_input_buffer, curdoc.address)) {
+	LYTrimAllStartfile(user_input_buffer);
+	if (user_input_buffer[0] != '\0') {
+	    return 2;
+	}
+    }
+    /*
+     * User cancelled via ^G, a full deletion, or not modifying the URL.  - FM
+     */
+    HTInfoMsg(CANCELLED);
+    LYstrncpy(user_input_buffer, *old_user_input, MAX_LINE - 1);
+    FREE(*old_user_input);
+    return 0;
+}
+
+static void handle_LYK_EDIT(int *old_c,
+			    int real_c)
+{
+#ifdef DIRED_SUPPORT
+    char *cp;
+    char *tp = NULL;
+    struct stat dir_info;
+#endif /* DIRED_SUPPORT */
+
+    if (no_editor) {
+	if (*old_c != real_c) {
+	    *old_c = real_c;
+	    HTUserMsg(EDIT_DISABLED);
+	}
+    }
+#ifdef DIRED_SUPPORT
+    /*
+     * Allow the user to edit the link rather than curdoc in edit mode.
+     */
+    else if (lynx_edit_mode &&
+	     non_empty(editor) && !no_dired_support) {
+	if (nlinks > 0) {
+	    cp = links[curdoc.link].lname;
+	    if (is_url(cp) == FILE_URL_TYPE) {
+		cp = HTfullURL_toFile(cp);
+		StrAllocCopy(tp, cp);
+		FREE(cp);
+
+		if (stat(tp, &dir_info) == -1) {
+		    HTAlert(NO_STATUS);
+		} else {
+		    if (S_ISREG(dir_info.st_mode)) {
+			StrAllocCopy(tp, links[curdoc.link].lname);
+			HTUnEscapeSome(tp, "/");
+			if (edit_current_file(tp, curdoc.link, -1)) {
+			    DIRED_UNCACHE_1;
+			    move_address(&newdoc, &curdoc);
+#ifdef NO_SEEK_OLD_POSITION
+			    /*
+			     * Go to top of file.
+			     */
+			    newdoc.line = 1;
+			    newdoc.link = 0;
+#else
+			    /*
+			     * Seek old position, which probably changed.
+			     */
+			    newdoc.line = curdoc.line;
+			    newdoc.link = curdoc.link;
+#endif /* NO_SEEK_OLD_POSITION */
+			    LYclear();	/* clear the screen */
+			}
+		    }
+		}
+		FREE(tp);
+	    }
+	}
+    }
+#endif /* DIRED_SUPPORT */
+    else if (non_empty(editor)) {
+	if (edit_current_file(newdoc.address, curdoc.link, LYGetNewline())) {
+	    HTuncache_current_document();
+	    LYforce_no_cache = TRUE;	/*force reload of document */
+	    free_address(&curdoc);	/* so it doesn't get pushed */
+#ifdef NO_SEEK_OLD_POSITION
+	    /*
+	     * Go to top of file.
+	     */
+	    newdoc.line = 1;
+	    newdoc.link = 0;
+#else
+	    /*
+	     * Seek old position, which probably changed.
+	     */
+	    newdoc.line = curdoc.line;
+	    newdoc.link = curdoc.link;
+#endif /* NO_SEEK_OLD_POSITION */
+	    LYclear();		/* clear the screen */
+	}
+
+    } else {
+	if (*old_c != real_c) {
+	    *old_c = real_c;
+	    HTUserMsg(NO_EDITOR);
+	}
+    }
+}
+
+static void handle_LYK_DWIMHELP(const char **cshelpfile)
+{
+    /*
+     * Currently a help file different from the main 'helpfile' is shown only
+     * if current link is a text input form field.  - kw
+     */
+    if (curdoc.link >= 0 && curdoc.link < nlinks &&
+	links[curdoc.link].type == WWW_FORM_LINK_TYPE &&
+	!links[curdoc.link].l_form->disabled &&
+	F_TEXTLIKE(links[curdoc.link].l_form->type)) {
+	*cshelpfile = LYLineeditHelpURL();
+    }
+}
+
+static void handle_LYK_EDIT_TEXTAREA(BOOLEAN *refresh_screen,
+				     int *old_c,
+				     int real_c)
+{
+    if (no_editor) {
+	if (*old_c != real_c) {
+	    *old_c = real_c;
+	    HTUserMsg(ANYEDIT_DISABLED);
+	}
+    } else if (isEmpty(editor)) {
+	if (*old_c != real_c) {
+	    *old_c = real_c;
+	    HTUserMsg(NO_EDITOR);
+	}
+    }
+    /*
+     * See if the current link is in a form TEXTAREA.
+     */
+    else if (LinkIsTextarea(curdoc.link)) {
+
+	/* stop screen */
+	stop_curses();
+
+	(void) HText_ExtEditForm(&links[curdoc.link]);
+
+	/*
+	 * TODO:
+	 * Move cursor "n" lines from the current line to position it on the
+	 * 1st trailing blank line in the now edited TEXTAREA.  If the target
+	 * line/ anchor requires us to scroll up/down, position the target in
+	 * the approximate center of the screen.
+	 */
+
+	/* curdoc.link += n; */
+	/* works, except for page crossing, */
+	/* damnit; why is nothing ever easy */
+
+	/* start screen */
+	start_curses();
+	*refresh_screen = TRUE;
+
+    } else {
+
+	HTInfoMsg(NOT_IN_TEXTAREA_NOEDIT);
+    }
+}
+
+static int handle_LYK_ELGOTO(int *ch,
+			     char *user_input_buffer,
+			     char **old_user_input,
+			     int *old_c,
+			     int real_c)
+{
+    if (no_goto && !LYValidate) {
+	/*
+	 * Go to not allowed.  - FM
+	 */
+	if (*old_c != real_c) {
+	    *old_c = real_c;
+	    HTUserMsg(GOTO_DISALLOWED);
+	}
+	return 0;
+    }
+    if (!(nlinks > 0 && curdoc.link > -1) ||
+	(links[curdoc.link].type == WWW_FORM_LINK_TYPE &&
+	 links[curdoc.link].l_form->type != F_SUBMIT_TYPE &&
+	 links[curdoc.link].l_form->type != F_IMAGE_SUBMIT_TYPE &&
+	 links[curdoc.link].l_form->type != F_TEXT_SUBMIT_TYPE)) {
+	/*
+	 * No links on page, or not a normal link or form submit button.  - FM
+	 */
+	if (*old_c != real_c) {
+	    *old_c = real_c;
+	    HTUserMsg(NOT_ON_SUBMIT_OR_LINK);
+	}
+	return 0;
+    }
+    if ((links[curdoc.link].type == WWW_FORM_LINK_TYPE) &&
+	(!links[curdoc.link].l_form->submit_action ||
+	 *links[curdoc.link].l_form->submit_action == '\0')) {
+	/*
+	 * Form submit button with no ACTION defined.  - FM
+	 */
+	if (*old_c != real_c) {
+	    *old_c = real_c;
+	    HTUserMsg(NO_FORM_ACTION);
+	}
+	return 0;
+    }
+#ifdef DIRED_SUPPORT
+    if (isLYNXDIRED(links[curdoc.link].lname) ||
+	LYIsUIPage(curdoc.address, UIP_DIRED_MENU) ||
+	LYIsUIPage(curdoc.address, UIP_PERMIT_OPTIONS) ||
+	LYIsUIPage(curdoc.address, UIP_UPLOAD_OPTIONS)) {
+	/*
+	 * Disallow editing of File Management URLs.  - FM
+	 */
+	if (*old_c != real_c) {
+	    *old_c = real_c;
+	    HTUserMsg(EDIT_FM_MENU_URLS_DISALLOWED);
+	}
+	return 0;
+    }
+#endif /* DIRED_SUPPORT */
+
+    /*
+     * Save the current user_input_buffer string, and load the current link's
+     * address.  - FM
+     */
+    StrAllocCopy(*old_user_input, user_input_buffer);
+    LYstrncpy(user_input_buffer,
+	      ((links[curdoc.link].type == WWW_FORM_LINK_TYPE)
+	       ?
+	       links[curdoc.link].l_form->submit_action : links[curdoc.link].lname),
+	      (MAX_LINE - 1));
+
+    /*
+     * Offer the current link's URL for editing.  - FM
+     */
+    _statusline(EDIT_CURLINK_URL);
+    if (((*ch = LYgetstr(user_input_buffer, VISIBLE,
+			 MAX_LINE, RECALL_URL)) >= 0) &&
+	user_input_buffer[0] != '\0' &&
+	strcmp(user_input_buffer,
+	       ((links[curdoc.link].type == WWW_FORM_LINK_TYPE)
+		? links[curdoc.link].l_form->submit_action
+		: links[curdoc.link].lname))) {
+	LYTrimAllStartfile(user_input_buffer);
+	if (user_input_buffer[0] != '\0') {
+	    return 2;
+	}
+    }
+    /*
+     * User cancelled via ^G, a full deletion, or not modifying the URL.  - FM
+     */
+    HTInfoMsg(CANCELLED);
+    LYstrncpy(user_input_buffer, *old_user_input, MAX_LINE - 1);
+    FREE(*old_user_input);
+    return 0;
+}
+
+#ifdef USE_EXTERNALS
+static void handle_LYK_EXTERN_LINK(BOOLEAN *refresh_screen)
+{
+    if ((nlinks > 0) && (links[curdoc.link].lname != NULL)) {
+	run_external(links[curdoc.link].lname, FALSE);
+	*refresh_screen = TRUE;
+    }
+}
+
+static void handle_LYK_EXTERN_PAGE(BOOLEAN *refresh_screen)
+{
+    if (curdoc.address != NULL) {
+	run_external(curdoc.address, FALSE);
+	*refresh_screen = TRUE;
+    }
+}
+#endif
+
+static BOOLEAN handle_LYK_FASTBACKW_LINK(int *cmd,
+					 int *old_c,
+					 int real_c)
+{
+    int samepage = 0, nextlink = curdoc.link;
+    int res;
+    BOOLEAN code = FALSE;
+
+    if (nlinks > 1) {
+
+	/*
+	 * If in textarea, move to first link or textarea group before it if
+	 * there is one on this screen.  - kw
+	 */
+	if (LinkIsTextarea(curdoc.link)) {
+	    int thisgroup = links[curdoc.link].l_form->number;
+	    char *thisname = links[curdoc.link].l_form->name;
+
+	    if (curdoc.link > 0 &&
+		!(LinkIsTextarea(0) &&
+		  links[0].l_form->number == thisgroup &&
+		  sametext(links[0].l_form->name, thisname))) {
+		do
+		    nextlink--;
+		while
+		    (LinkIsTextarea(nextlink) &&
+		     links[nextlink].l_form->number == thisgroup &&
+		     sametext(links[nextlink].l_form->name, thisname));
+		samepage = 1;
+
+	    } else if (!more_text && LYGetNewline() == 1 &&
+		       (LinkIsTextarea(0) &&
+			links[0].l_form->number == thisgroup &&
+			sametext(links[0].l_form->name, thisname)) &&
+		       !(LinkIsTextarea(nlinks - 1) &&
+			 links[nlinks - 1].l_form->number == thisgroup &&
+			 sametext(links[nlinks - 1].l_form->name, thisname))) {
+		nextlink = nlinks - 1;
+		samepage = 1;
+
+	    } else if (!more_text && LYGetNewline() == 1 && curdoc.link > 0) {
+		nextlink = 0;
+		samepage = 1;
+	    }
+	} else if (curdoc.link > 0) {
+	    nextlink--;
+	    samepage = 1;
+	} else if (!more_text && LYGetNewline() == 1) {
+	    nextlink = nlinks - 1;
+	    samepage = 1;
+	}
+    }
+
+    if (samepage) {
+	/*
+	 * If the link as determined so far is part of a group of textarea
+	 * fields, try to use the first of them that's on the screen instead. 
+	 * - kw
+	 */
+	if (nextlink > 0 &&
+	    LinkIsTextarea(nextlink)) {
+	    int thisgroup = links[nextlink].l_form->number;
+	    char *thisname = links[nextlink].l_form->name;
+
+	    if (LinkIsTextarea(0) &&
+		links[0].l_form->number == thisgroup &&
+		sametext(links[0].l_form->name, thisname)) {
+		nextlink = 0;
+	    } else
+		while
+		    (nextlink > 1 &&
+		     LinkIsTextarea(nextlink - 1) &&
+		     links[nextlink - 1].l_form->number == thisgroup &&
+		     sametext(links[nextlink - 1].l_form->name, thisname)) {
+		    nextlink--;
+		}
+	}
+	set_curdoc_link(nextlink);
+
+    } else if (LYGetNewline() > 1 &&	/* need a previous page */
+	       (res = HTGetLinkOrFieldStart(curdoc.link,
+					    &Newline, &newdoc.link,
+					    -1, TRUE)) != NO) {
+	if (res == LINK_DO_ARROWUP) {
+	    /*
+	     * It says we should use the normal PREV_LINK mechanism, so we'll
+	     * do that.  - kw
+	     */
+	    if (nlinks > 0)
+		curdoc.link = 0;
+	    *cmd = LYK_PREV_LINK;
+	    code = TRUE;
+	} else {
+	    LYChgNewline(1);	/* our line counting starts with 1 not 0 */
+	}
+    } else if (*old_c != real_c) {
+	*old_c = real_c;
+	HTInfoMsg(NO_LINKS_ABOVE);
+    }
+    return code;
+}
+
+static void handle_LYK_FASTFORW_LINK(int *old_c,
+				     int real_c)
+{
+    int samepage = 0, nextlink = curdoc.link;
+
+    if (nlinks > 1) {
+
+	/*
+	 * If in textarea, move to first link or field after it if there is one
+	 * on this screen.  - kw
+	 */
+	if (LinkIsTextarea(curdoc.link)) {
+	    int thisgroup = links[curdoc.link].l_form->number;
+	    char *thisname = links[curdoc.link].l_form->name;
+
+	    if (curdoc.link < nlinks - 1 &&
+		!(LinkIsTextarea(nlinks - 1) &&
+		  links[nlinks - 1].l_form->number == thisgroup &&
+		  sametext(links[nlinks - 1].l_form->name, thisname))) {
+		do
+		    nextlink++;
+		while
+		    (LinkIsTextarea(nextlink) &&
+		     links[nextlink].l_form->number == thisgroup &&
+		     sametext(links[nextlink].l_form->name, thisname));
+		samepage = 1;
+	    } else if (!more_text && LYGetNewline() == 1 && curdoc.link > 0) {
+		nextlink = 0;
+		samepage = 1;
+	    }
+	} else if (curdoc.link < nlinks - 1) {
+	    nextlink++;
+	    samepage = 1;
+	} else if (!more_text && LYGetNewline() == 1 && curdoc.link > 0) {
+	    nextlink = 0;
+	    samepage = 1;
+	}
+    }
+
+    if (samepage) {
+	set_curdoc_link(nextlink);
+    } else if (!more_text && LYGetNewline() == 1 && curdoc.link == nlinks - 1) {
+	/*
+	 * At the bottom of list and there is only one page.  Move to the top
+	 * link on the page.
+	 */
+	set_curdoc_link(0);
+
+    } else if (more_text &&	/* need a later page */
+	       HTGetLinkOrFieldStart(curdoc.link,
+				     &Newline, &newdoc.link,
+				     1, TRUE) != NO) {
+	LYChgNewline(1);	/* our line counting starts with 1 not 0 */
+	/* nothing more to do here */
+
+    } else if (*old_c != real_c) {
+	*old_c = real_c;
+	HTInfoMsg(NO_LINKS_BELOW);
+    }
+    return;
+}
+
+static void handle_LYK_FIRST_LINK(void)
+{
+    int i = curdoc.link;
+
+    for (;;) {
+	if (--i < 0
+	    || links[i].ly != links[curdoc.link].ly) {
+	    set_curdoc_link(i + 1);
+	    break;
+	}
+    }
+}
+
+static BOOLEAN handle_LYK_GOTO(int *ch,
+			       char *user_input_buffer,
+			       char **old_user_input,
+			       RecallType * recall,
+			       int *URLTotal,
+			       int *URLNum,
+			       BOOLEAN *FirstURLRecall,
+			       int *old_c,
+			       int real_c)
+{
+
+    if (no_goto && !LYValidate) {
+	if (*old_c != real_c) {
+	    *old_c = real_c;
+	    HTUserMsg(GOTO_DISALLOWED);
+	}
+	return FALSE;
+    }
+
+    StrAllocCopy(*old_user_input, user_input_buffer);
+    if (!goto_buffer)
+	*user_input_buffer = '\0';
+
+    *URLTotal = (Goto_URLs ? HTList_count(Goto_URLs) : 0);
+    if (goto_buffer && *user_input_buffer) {
+	*recall = ((*URLTotal > 1) ? RECALL_URL : NORECALL);
+	*URLNum = 0;
+	*FirstURLRecall = FALSE;
+    } else {
+	*recall = ((*URLTotal >= 1) ? RECALL_URL : NORECALL);
+	*URLNum = *URLTotal;
+	*FirstURLRecall = TRUE;
+    }
+
+    /*
+     * Ask the user.
+     */
+    _statusline(URL_TO_OPEN);
+    if ((*ch = LYgetstr(user_input_buffer, VISIBLE,
+			MAX_LINE, *recall)) < 0) {
+	/*
+	 * User cancelled the Goto via ^G.  Restore user_input_buffer and
+	 * break.  - FM
+	 */
+	LYstrncpy(user_input_buffer, *old_user_input, MAX_LINE - 1);
+	FREE(*old_user_input);
+	HTInfoMsg(CANCELLED);
+	return FALSE;
+    }
+    return TRUE;
+}
+
+static void handle_LYK_GROW_TEXTAREA(BOOLEAN *refresh_screen)
+{
+    /*
+     * See if the current link is in a form TEXTAREA.
+     */
+    if (LinkIsTextarea(curdoc.link)) {
+
+	HText_ExpandTextarea(&links[curdoc.link], TEXTAREA_EXPAND_SIZE);
+
+	*refresh_screen = TRUE;
+
+    } else {
+
+	HTInfoMsg(NOT_IN_TEXTAREA);
+    }
+}
+
+static BOOLEAN handle_LYK_HEAD(int *cmd)
+{
+    int c;
+
+    if (nlinks > 0 &&
+	(links[curdoc.link].type != WWW_FORM_LINK_TYPE ||
+	 links[curdoc.link].l_form->type == F_SUBMIT_TYPE ||
+	 links[curdoc.link].l_form->type == F_IMAGE_SUBMIT_TYPE ||
+	 links[curdoc.link].l_form->type == F_TEXT_SUBMIT_TYPE)) {
+	/*
+	 * We have links, and the current link is a normal link or a form's
+	 * submit button.  - FM
+	 */
+	_statusline(HEAD_D_L_OR_CANCEL);
+	c = LYgetch_single();
+	if (c == 'D') {
+	    char *scheme = !isLYNXIMGMAP(curdoc.address)
+	    ? curdoc.address
+	    : curdoc.address + LEN_LYNXIMGMAP;
+
+	    if (LYCanDoHEAD(scheme) != TRUE) {
+		HTUserMsg(DOC_NOT_HTTP_URL);
+	    } else {
+		/*
+		 * Check if this is a reply from a POST, and if so, seek
+		 * confirmation if the safe element is not set.  - FM
+		 */
+		if ((curdoc.post_data != NULL &&
+		     curdoc.safe != TRUE) &&
+		    HTConfirm(CONFIRM_POST_DOC_HEAD) == FALSE) {
+		    HTInfoMsg(CANCELLED);
+		} else {
+		    HEAD_request = TRUE;
+		    LYforce_no_cache = TRUE;
+		    StrAllocCopy(newdoc.title, curdoc.title);
+		    if (HTLoadedDocumentIsHEAD()) {
+			HText_setNoCache(HTMainText);
+			free_address(&curdoc);
+		    } else {
+			StrAllocCat(newdoc.title, " - HEAD");
+		    }
+		}
+	    }
+	} else if (c == 'L') {
+	    if (links[curdoc.link].type != WWW_FORM_LINK_TYPE &&
+		strncmp(links[curdoc.link].lname, "http", 4) &&
+		strncmp(links[curdoc.link].lname,
+			"LYNXIMGMAP:http", 15) &&
+		LYCanDoHEAD(links[curdoc.link].lname) != TRUE &&
+		(links[curdoc.link].type != WWW_INTERN_LINK_TYPE ||
+		 !curdoc.address ||
+		 strncmp(curdoc.address, "http", 4))) {
+		HTUserMsg(LINK_NOT_HTTP_URL);
+	    } else if (links[curdoc.link].type == WWW_FORM_LINK_TYPE &&
+		       links[curdoc.link].l_form->disabled) {
+		HTUserMsg(FORM_ACTION_DISABLED);
+	    } else if (links[curdoc.link].type == WWW_FORM_LINK_TYPE &&
+		       links[curdoc.link].l_form->submit_action != 0 &&
+		       !isLYNXCGI(links[curdoc.link].l_form->submit_action) &&
+		       strncmp(links[curdoc.link].l_form->submit_action,
+			       "http", 4)) {
+		HTUserMsg(FORM_ACTION_NOT_HTTP_URL);
+	    } else if (links[curdoc.link].type == WWW_FORM_LINK_TYPE &&
+		       links[curdoc.link].l_form->submit_method ==
+		       URL_POST_METHOD &&
+		       HTConfirm(CONFIRM_POST_LINK_HEAD) == FALSE) {
+		HTInfoMsg(CANCELLED);
+	    } else {
+		HEAD_request = TRUE;
+		LYforce_no_cache = TRUE;
+		*cmd = LYK_ACTIVATE;
+		return TRUE;
+	    }
+	}
+    } else {
+	/*
+	 * We can offer only this document for a HEAD request.  Check if this
+	 * is a reply from a POST, and if so, seek confirmation if the safe
+	 * element is not set.  - FM
+	 */
+	if ((curdoc.post_data != NULL &&
+	     curdoc.safe != TRUE) &&
+	    HTConfirm(CONFIRM_POST_DOC_HEAD) == FALSE) {
+	    HTInfoMsg(CANCELLED);
+	} else {
+	    if (nlinks > 0) {
+		/*
+		 * The current link is a non-submittable form link, so prompt
+		 * the user to make it clear that the HEAD request would be for
+		 * the current document, not the form link.  - FM
+		 */
+		_statusline(HEAD_D_OR_CANCEL);
+		c = LYgetch_single();
+	    } else {
+		/*
+		 * No links, so we can just assume that the user wants a HEAD
+		 * request for the current document.  - FM
+		 */
+		c = 'D';
+	    }
+	    if (c == 'D') {
+		char *scheme = !isLYNXIMGMAP(curdoc.address)
+		? curdoc.address
+		: curdoc.address + LEN_LYNXIMGMAP;
+
+		/*
+		 * The user didn't cancel, so check if a HEAD request is
+		 * appropriate for the current document.  - FM
+		 */
+		if (LYCanDoHEAD(scheme) != TRUE) {
+		    HTUserMsg(DOC_NOT_HTTP_URL);
+		} else {
+		    HEAD_request = TRUE;
+		    LYforce_no_cache = TRUE;
+		    StrAllocCopy(newdoc.title, curdoc.title);
+		    if (HTLoadedDocumentIsHEAD()) {
+			HText_setNoCache(HTMainText);
+			free_address(&curdoc);
+		    } else {
+			StrAllocCat(newdoc.title, " - HEAD");
+		    }
+		}
+	    }
+	}
+    }
+    return FALSE;
+}
+
+static void handle_LYK_HELP(const char **cshelpfile)
+{
+    char *my_value = NULL;
+
+    if (*cshelpfile == NULL)
+	*cshelpfile = helpfile;
+    StrAllocCopy(my_value, *cshelpfile);
+    LYEnsureAbsoluteURL(&my_value, *cshelpfile, FALSE);
+    if (!STREQ(curdoc.address, my_value)) {
+	/*
+	 * Set the filename.
+	 */
+	set_address(&newdoc, my_value);
+	/*
+	 * Make a name for this help file.
+	 */
+	StrAllocCopy(newdoc.title, gettext("Help Screen"));
+	LYFreePostData(&newdoc);
+	FREE(newdoc.bookmark);
+	newdoc.isHEAD = FALSE;
+	newdoc.safe = FALSE;
+	newdoc.internal_link = FALSE;
+    }
+    FREE(my_value);
+    *cshelpfile = NULL;		/* reset pointer - kw */
+}
+
+static void handle_LYK_HISTORICAL(void)
+{
+#ifdef USE_SOURCE_CACHE
+    if (!HTcan_reparse_document()) {
+#endif
+	/*
+	 * Check if this is a reply from a POST, and if so, seek confirmation
+	 * of reload if the safe element is not set.  - FM
+	 */
+	if ((curdoc.post_data != NULL &&
+	     curdoc.safe != TRUE) &&
+	    confirm_post_resub(curdoc.address, NULL, 0, 0) == FALSE) {
+	    HTInfoMsg(WILL_NOT_RELOAD_DOC);
+	} else {
+	    HText_setNoCache(HTMainText);
+	    move_address(&newdoc, &curdoc);
+	    newdoc.line = curdoc.line;
+	    newdoc.link = curdoc.link;
+	}
+#ifdef USE_SOURCE_CACHE
+    }				/* end if no bypass */
+#endif
+    historical_comments = (BOOLEAN) !historical_comments;
+    if (minimal_comments) {
+	HTAlert(historical_comments ?
+		HISTORICAL_ON_MINIMAL_OFF : HISTORICAL_OFF_MINIMAL_ON);
+    } else {
+	HTAlert(historical_comments ?
+		HISTORICAL_ON_VALID_OFF : HISTORICAL_OFF_VALID_ON);
+    }
+#ifdef USE_SOURCE_CACHE
+    (void) reparse_document();
+#endif
+    return;
+}
+
+static BOOLEAN handle_LYK_HISTORY(BOOLEAN ForcePush)
+{
+    if (curdoc.title && !LYIsUIPage(curdoc.address, UIP_HISTORY)) {
+	/*
+	 * Don't do this if already viewing history page.
+	 *
+	 * Push the current file so that the history list contains the current
+	 * file for printing purposes.  Pop the file afterwards to prevent
+	 * multiple copies.
+	 */
+	if (TRACE && !LYUseTraceLog && LYCursesON) {
+	    LYHideCursor();	/* make sure cursor is down */
+#ifdef USE_SLANG
+	    LYaddstr("\n");
+#endif /* USE_SLANG */
+	    LYrefresh();
+	}
+	LYpush(&curdoc, ForcePush);
+
+	/*
+	 * Print history options to file.
+	 */
+	if (showhistory(&newdoc.address) < 0) {
+	    LYpop(&curdoc);
+	    return TRUE;
+	}
+	LYRegisterUIPage(newdoc.address, UIP_HISTORY);
+	StrAllocCopy(newdoc.title, HISTORY_PAGE_TITLE);
+	LYFreePostData(&newdoc);
+	FREE(newdoc.bookmark);
+	newdoc.isHEAD = FALSE;
+	newdoc.safe = FALSE;
+	newdoc.internal_link = FALSE;
+	newdoc.link = 1;	/*@@@ bypass "recent statusline messages" link */
+	free_address(&curdoc);	/* so it doesn't get pushed */
+
+	if (LYValidate || check_realm) {
+	    LYPermitURL = TRUE;
+	}
+	return TRUE;
+    }				/* end if strncmp */
+    return FALSE;
+}
+
+static BOOLEAN handle_LYK_IMAGE_TOGGLE(int *cmd)
+{
+    clickable_images = (BOOLEAN) !clickable_images;
+
+    HTUserMsg(clickable_images ?
+	      CLICKABLE_IMAGES_ON : CLICKABLE_IMAGES_OFF);
+    return reparse_or_reload(cmd);
+}
+
+static void handle_LYK_INDEX(int *old_c,
+			     int real_c)
+{
+    /*
+     * Make sure we are not in the index already.
+     */
+    if (!STREQ(curdoc.address, indexfile)) {
+
+	if (indexfile[0] == '\0') {	/* no defined index */
+	    if (*old_c != real_c) {
+		*old_c = real_c;
+		HTUserMsg(NO_INDEX_FILE);
+	    }
+
+	} else {
+#ifdef KANJI_CODE_OVERRIDE
+	    if (HTCJK == JAPANESE) {
+		last_kcode = NOKANJI;	/* AUTO */
+	    }
+#endif
+	    set_address(&newdoc, indexfile);
+	    StrAllocCopy(newdoc.title, gettext("System Index"));	/* name it */
+	    LYFreePostData(&newdoc);
+	    FREE(newdoc.bookmark);
+	    newdoc.isHEAD = FALSE;
+	    newdoc.safe = FALSE;
+	    newdoc.internal_link = FALSE;
+	}			/* end else */
+    }				/* end if */
+}
+
+static void handle_LYK_INDEX_SEARCH(BOOLEAN *force_load,
+				    BOOLEAN ForcePush,
+				    int *old_c,
+				    int real_c)
+{
+    if (is_www_index) {
+	/*
+	 * Perform a database search.
+	 *
+	 * do_www_search will try to go out and get the document.  If it
+	 * returns TRUE, a new document was returned and is named in the
+	 * newdoc.address.
+	 */
+	newdoc.isHEAD = FALSE;
+	newdoc.safe = FALSE;
+	if (do_www_search(&newdoc) == NORMAL) {
+	    /*
+	     * Yah, the search succeeded.
+	     */
+	    if (TRACE && !LYUseTraceLog && LYCursesON) {
+		/*
+		 * Make sure cursor is down.
+		 */
+		LYHideCursor();
+#ifdef USE_SLANG
+		LYaddstr("\n");
+#endif /* USE_SLANG */
+		LYrefresh();
+	    }
+	    LYpush(&curdoc, ForcePush);
+	    /*
+	     * Make the curdoc.address the newdoc.address so that getfile
+	     * doesn't try to get the newdoc.address.  Since we have already
+	     * gotten it.
+	     */
+	    copy_address(&curdoc, &newdoc);
+	    BStrCopy(newdoc.post_data, curdoc.post_data);
+	    StrAllocCopy(newdoc.post_content_type, curdoc.post_content_type);
+	    newdoc.internal_link = FALSE;
+	    curdoc.line = -1;
+	    LYSetNewline(0);
+	} else if (use_this_url_instead != NULL) {
+	    /*
+	     * Got back a redirecting URL.  Check it out.
+	     */
+	    HTUserMsg2(WWW_USING_MESSAGE, use_this_url_instead);
+
+	    /*
+	     * Make a name for this URL.
+	     */
+	    StrAllocCopy(newdoc.title,
+			 "A URL specified by redirection");
+	    set_address(&newdoc, use_this_url_instead);
+	    LYFreePostData(&newdoc);
+	    FREE(newdoc.bookmark);
+	    newdoc.isHEAD = FALSE;
+	    newdoc.safe = FALSE;
+	    newdoc.internal_link = FALSE;
+	    FREE(use_this_url_instead);
+	    *force_load = TRUE;
+	} else {
+	    /*
+	     * Yuk, the search failed.  Restore the old file.
+	     */
+	    copy_address(&newdoc, &curdoc);
+	    BStrCopy(newdoc.post_data, curdoc.post_data);
+	    StrAllocCopy(newdoc.post_content_type,
+			 curdoc.post_content_type);
+	    StrAllocCopy(newdoc.bookmark, curdoc.bookmark);
+	    newdoc.isHEAD = curdoc.isHEAD;
+	    newdoc.safe = curdoc.safe;
+	    newdoc.internal_link = curdoc.internal_link;
+	}
+    } else if (*old_c != real_c) {
+	*old_c = real_c;
+	HTUserMsg(NOT_ISINDEX);
+    }
+}
+
+static BOOLEAN handle_LYK_INFO(int *cmd)
+{
+    /*
+     * Don't do if already viewing info page.
+     */
+    if (!LYIsUIPage(curdoc.address, UIP_SHOWINFO)) {
+	if (do_change_link() != -1
+	    && LYShowInfo(&curdoc, &newdoc, owner_address) >= 0) {
+	    LYRegisterUIPage(newdoc.address, UIP_SHOWINFO);
+	    StrAllocCopy(newdoc.title, SHOWINFO_TITLE);
+	    LYFreePostData(&newdoc);
+	    FREE(newdoc.bookmark);
+	    newdoc.isHEAD = FALSE;
+	    newdoc.safe = FALSE;
+	    newdoc.internal_link = FALSE;
+	    LYforce_no_cache = TRUE;
+	    if (LYValidate || check_realm)
+		LYPermitURL = TRUE;
+	}
+    } else {
+	/*
+	 * If already in info page, get out.
+	 */
+	*cmd = LYK_PREV_DOC;
+	return TRUE;
+    }
+    return FALSE;
+}
+
+static BOOLEAN handle_LYK_INLINE_TOGGLE(int *cmd)
+{
+    pseudo_inline_alts = (BOOLEAN) !pseudo_inline_alts;
+
+    HTUserMsg(pseudo_inline_alts ?
+	      PSEUDO_INLINE_ALTS_ON : PSEUDO_INLINE_ALTS_OFF);
+    return reparse_or_reload(cmd);
+}
+
+static void handle_LYK_INSERT_FILE(BOOLEAN *refresh_screen,
+				   int *old_c,
+				   int real_c)
+{
+    /*
+     * See if the current link is in a form TEXTAREA.
+     */
+    if (LinkIsTextarea(curdoc.link)) {
+
+	/*
+	 * Reject attempts to use this for gaining access to local files when
+	 * such access is restricted:  if no_file_url was set via the file_url
+	 * restriction, if no_goto_file was set for the anonymous account, or
+	 * if HTDirAccess was set to HT_DIR_FORBID or HT_DIR_SELECTIVE via the
+	 * -nobrowse or -selective switches, it is assumed that inserting files
+	 * or checking for existence of files needs to be denied.  - kw
+	 */
+	if (no_file_url || no_goto_file ||
+	    HTDirAccess == HT_DIR_FORBID ||
+	    HTDirAccess == HT_DIR_SELECTIVE) {
+	    if (*old_c != real_c) {
+		*old_c = real_c;
+		if (no_goto_file)
+		    HTUserMsg2(GOTO_XXXX_DISALLOWED, STR_FILE_URL);
+		else
+		    HTUserMsg(NOAUTH_TO_ACCESS_FILES);
+		HTInfoMsg(FILE_INSERT_CANCELLED);
+	    }
+	    return;
+	}
+
+	(void) HText_InsertFile(&links[curdoc.link]);
+
+	/*
+	 * TODO:
+	 * Move cursor "n" lines from the current line to position it on the
+	 * 1st line following the text that was inserted.  If the target
+	 * line/anchor requires us to scroll up/down, position the target in
+	 * the approximate center of the screen.
+	 *
+	 * [Current behavior leaves cursor on the same line relative to the
+	 * start of the TEXTAREA that it was on before the insertion.  This is
+	 * the same behavior that occurs with (my) editor, so this TODO will
+	 * stay unimplemented.]
+	 */
+
+	*refresh_screen = TRUE;
+
+    } else {
+
+	HTInfoMsg(NOT_IN_TEXTAREA);
+    }
+}
+
+#if defined(DIRED_SUPPORT) && defined(OK_INSTALL)
+static void handle_LYK_INSTALL(void)
+{
+    if (lynx_edit_mode && nlinks > 0 && !no_dired_support)
+	local_install(NULL, links[curdoc.link].lname, &newdoc.address);
+}
+#endif
+
+static BOOLEAN handle_LYK_JUMP(int c,
+			       char *user_input_buffer,
+			       char **old_user_input GCC_UNUSED,
+			       RecallType * recall GCC_UNUSED,
+			       BOOLEAN *FirstURLRecall GCC_UNUSED,
+			       int *URLNum GCC_UNUSED,
+			       int *URLTotal GCC_UNUSED,
+			       int *ch GCC_UNUSED,
+			       int *old_c,
+			       int real_c)
+{
+    char *ret;
+
+    if (no_jump || JThead == NULL) {
+	if (*old_c != real_c) {
+	    *old_c = real_c;
+	    if (no_jump)
+		HTUserMsg(JUMP_DISALLOWED);
+	    else
+		HTUserMsg(NO_JUMPFILE);
+	}
+    } else {
+	LYJumpFileURL = TRUE;
+	if ((ret = LYJump(c)) != NULL) {
+#ifdef PERMIT_GOTO_FROM_JUMP
+	    if (!strncasecomp(ret, "Go ", 3)) {
+		LYJumpFileURL = FALSE;
+		StrAllocCopy(*old_user_input, user_input_buffer);
+		*URLTotal = (Goto_URLs ? HTList_count(Goto_URLs) : 0);
+		*recall = ((*URLTotal >= 1) ? RECALL_URL : NORECALL);
+		*URLNum = *URLTotal;
+		*FirstURLRecall = TRUE;
+		if (!strcasecomp(ret, "Go :")) {
+		    if (recall) {
+			*ch = UPARROW;
+			return TRUE;
+		    }
+		    FREE(*old_user_input);
+		    HTUserMsg(NO_RANDOM_URLS_YET);
+		    return FALSE;
+		}
+		ret = HTParse((ret + 3), startfile, PARSE_ALL);
+		LYstrncpy(user_input_buffer, ret, MAX_LINE - 1);
+		FREE(ret);
+		return TRUE;
+	    }
+#endif /* PERMIT_GOTO_FROM_JUMP */
+	    ret = HTParse(ret, startfile, PARSE_ALL);
+	    if (!LYTrimStartfile(ret)) {
+		LYRemoveBlanks(user_input_buffer);
+	    }
+	    set_address(&newdoc, ret);
+	    StrAllocCopy(lynxjumpfile, ret);
+	    LYFreePostData(&newdoc);
+	    FREE(newdoc.bookmark);
+	    newdoc.isHEAD = FALSE;
+	    newdoc.safe = FALSE;
+	    newdoc.internal_link = FALSE;
+	    FREE(ret);
+	    LYUserSpecifiedURL = TRUE;
+	} else {
+	    LYJumpFileURL = FALSE;
+	}
+    }
+    return FALSE;
+}
+
+static void handle_LYK_KEYMAP(BOOLEAN *vi_keys_flag,
+			      BOOLEAN *emacs_keys_flag,
+			      int *old_c,
+			      int real_c)
+{
+    if (*old_c != real_c) {
+	*old_c = real_c;
+	set_address(&newdoc, STR_LYNXKEYMAP);
+	StrAllocCopy(newdoc.title, CURRENT_KEYMAP_TITLE);
+	LYFreePostData(&newdoc);
+	FREE(newdoc.bookmark);
+	newdoc.isHEAD = FALSE;
+	newdoc.safe = FALSE;
+	newdoc.internal_link = FALSE;
+	/*
+	 * If vi_keys changed, the keymap did too, so force no cache, and reset
+	 * the flag.  - FM
+	 */
+	if (*vi_keys_flag != vi_keys ||
+	    *emacs_keys_flag != emacs_keys) {
+	    LYforce_no_cache = TRUE;
+	    *vi_keys_flag = vi_keys;
+	    *emacs_keys_flag = emacs_keys;
+	}
+#if defined(DIRED_SUPPORT) && defined(OK_OVERRIDE)
+	/*
+	 * Remember whether we are in dired menu so we can display the right
+	 * keymap.
+	 */
+	if (!no_dired_support) {
+	    prev_lynx_edit_mode = lynx_edit_mode;
+	}
+#endif /* DIRED_SUPPORT && OK_OVERRIDE */
+	LYforce_no_cache = TRUE;
+    }
+}
+
+static void handle_LYK_LAST_LINK(void)
+{
+    int i = curdoc.link;
+
+    for (;;) {
+	if (++i >= nlinks
+	    || links[i].ly != links[curdoc.link].ly) {
+	    set_curdoc_link(i - 1);
+	    break;
+	}
+    }
+}
+
+static void handle_LYK_LEFT_LINK(void)
+{
+    if (curdoc.link > 0 &&
+	links[curdoc.link].ly == links[curdoc.link - 1].ly) {
+	set_curdoc_link(curdoc.link - 1);
+    }
+}
+
+static BOOLEAN handle_LYK_LIST(int *cmd)
+{
+    /*
+     * Don't do if already viewing list page.
+     */
+    if (!strcmp(NonNull(curdoc.title), LIST_PAGE_TITLE) &&
+	LYIsUIPage(curdoc.address, UIP_LIST_PAGE)) {
+	/*
+	 * Already viewing list page, so get out.
+	 */
+	*cmd = LYK_PREV_DOC;
+	return TRUE;
+    }
+
+    /*
+     * Print list page to file.
+     */
+    if (showlist(&newdoc, TRUE) < 0)
+	return FALSE;
+    StrAllocCopy(newdoc.title, LIST_PAGE_TITLE);
+    /*
+     * showlist will set newdoc's other fields.  It may leave post_data intact
+     * so the list can be used to follow internal links in the current document
+     * even if it is a POST response.  - kw
+     */
+
+    if (LYValidate || check_realm) {
+	LYPermitURL = TRUE;
+	StrAllocCopy(lynxlistfile, newdoc.address);
+    }
+    return FALSE;
+}
+
+static void handle_LYK_MAIN_MENU(int *old_c,
+				 int real_c)
+{
+    /*
+     * If its already the homepage then don't reload it.
+     */
+    if (!STREQ(curdoc.address, homepage)) {
+
+	if (HTConfirmDefault(CONFIRM_MAIN_SCREEN, NO) == YES) {
+	    set_address(&newdoc, homepage);
+	    StrAllocCopy(newdoc.title, gettext("Entry into main screen"));
+	    LYFreePostData(&newdoc);
+	    FREE(newdoc.bookmark);
+	    newdoc.isHEAD = FALSE;
+	    newdoc.safe = FALSE;
+	    newdoc.internal_link = FALSE;
+	    LYhighlight(OFF, curdoc.link, prev_target);
+#ifdef DIRED_SUPPORT
+	    if (lynx_edit_mode) {
+		DIRED_UNCACHE_2;
+	    }
+#endif /* DIRED_SUPPORT */
+	}
+    } else {
+	if (*old_c != real_c) {
+	    *old_c = real_c;
+	    HTUserMsg(IN_MAIN_SCREEN);
+	}
+    }
+}
+
+static void handle_LYK_MINIMAL(void)
+{
+    if (!historical_comments) {
+#ifdef USE_SOURCE_CACHE
+	if (!HTcan_reparse_document()) {
+#endif
+	    /*
+	     * Check if this is a reply from a POST, and if so, seek
+	     * confirmation of reload if the safe element is not set.  - FM
+	     */
+	    if ((curdoc.post_data != NULL &&
+		 curdoc.safe != TRUE) &&
+		confirm_post_resub(curdoc.address, NULL, 0, 0) == FALSE) {
+		HTInfoMsg(WILL_NOT_RELOAD_DOC);
+	    } else {
+		HText_setNoCache(HTMainText);
+		move_address(&newdoc, &curdoc);
+		newdoc.line = curdoc.line;
+		newdoc.link = curdoc.link;
+	    }
+#ifdef USE_SOURCE_CACHE
+	}			/* end if no bypass */
+#endif
+    }
+    minimal_comments = (BOOLEAN) !minimal_comments;
+    if (!historical_comments) {
+	HTAlert(minimal_comments ?
+		MINIMAL_ON_IN_EFFECT : MINIMAL_OFF_VALID_ON);
+    } else {
+	HTAlert(minimal_comments ?
+		MINIMAL_ON_BUT_HISTORICAL : MINIMAL_OFF_HISTORICAL_ON);
+    }
+#ifdef USE_SOURCE_CACHE
+    (void) reparse_document();
+#endif
+    return;
+}
+
+#if defined(DIRED_SUPPORT)
+static void handle_LYK_MODIFY(BOOLEAN *refresh_screen)
+{
+    if (lynx_edit_mode && nlinks > 0 && !no_dired_support) {
+	int ret;
+
+	ret = local_modify(&curdoc, &newdoc.address);
+	if (ret == PERMIT_FORM_RESULT) {	/* Permit form thrown up */
+	    *refresh_screen = TRUE;
+	} else if (ret) {
+	    DIRED_UNCACHE_1;
+	    move_address(&newdoc, &curdoc);
+	    LYFreePostData(&newdoc);
+	    FREE(newdoc.bookmark);
+	    newdoc.isHEAD = FALSE;
+	    newdoc.safe = FALSE;
+	    newdoc.internal_link = FALSE;
+	    newdoc.line = curdoc.line;
+	    newdoc.link = curdoc.link;
+	    LYclear();
+	}
+    }
+}
+#endif /* DIRED_SUPPORT */
+
+#ifdef EXP_NESTED_TABLES
+static BOOLEAN handle_LYK_NESTED_TABLES(int *cmd)
+{
+    nested_tables = (BOOLEAN) !nested_tables;
+    HTUserMsg(nested_tables ? NESTED_TABLES_ON : NESTED_TABLES_OFF);
+    return reparse_or_reload(cmd);
+}
+#endif
+
+static BOOLEAN handle_LYK_OPTIONS(int *cmd,
+				  BOOLEAN *refresh_screen)
+{
+#ifndef NO_OPTION_MENU
+    if (!LYUseFormsOptions) {
+	BOOLEAN LYUseDefaultRawMode_flag = LYUseDefaultRawMode;
+	BOOLEAN LYSelectPopups_flag = LYSelectPopups;
+	BOOLEAN verbose_img_flag = verbose_img;
+	BOOLEAN keypad_mode_flag = (BOOL) keypad_mode;
+	BOOLEAN show_dotfiles_flag = show_dotfiles;
+	BOOLEAN user_mode_flag = (BOOL) user_mode;
+	int CurrentAssumeCharSet_flag = UCLYhndl_for_unspec;
+	int CurrentCharSet_flag = current_char_set;
+	int HTfileSortMethod_flag = HTfileSortMethod;
+	char *CurrentUserAgent = NULL;
+	char *CurrentNegoLanguage = NULL;
+	char *CurrentNegoCharset = NULL;
+
+	StrAllocCopy(CurrentUserAgent, NonNull(LYUserAgent));
+	StrAllocCopy(CurrentNegoLanguage, NonNull(language));
+	StrAllocCopy(CurrentNegoCharset, NonNull(pref_charset));
+
+	LYoptions(); /** do the old-style options stuff **/
+
+	if (keypad_mode_flag != keypad_mode ||
+	    (user_mode_flag != user_mode &&
+	     (user_mode_flag == NOVICE_MODE ||
+	      user_mode == NOVICE_MODE)) ||
+	    (((HTfileSortMethod_flag != HTfileSortMethod) ||
+	      (show_dotfiles_flag != show_dotfiles)) &&
+	     (isFILE_URL(curdoc.address) ||
+	      isFTP_URL(curdoc.address))) ||
+	    CurrentCharSet_flag != current_char_set ||
+	    CurrentAssumeCharSet_flag != UCLYhndl_for_unspec ||
+	    verbose_img_flag != verbose_img ||
+	    LYUseDefaultRawMode_flag != LYUseDefaultRawMode ||
+	    LYSelectPopups_flag != LYSelectPopups ||
+	    ((strcmp(CurrentUserAgent, NonNull(LYUserAgent)) ||
+	      strcmp(CurrentNegoLanguage, NonNull(language)) ||
+	      strcmp(CurrentNegoCharset, NonNull(pref_charset))) &&
+	     (!strncmp(curdoc.address, "http", 4) ||
+	      isLYNXCGI(curdoc.address)))) {
+
+	    BOOLEAN canreparse_post = FALSE;
+
+	    /*
+	     * Check if this is a reply from a POST, and if so, seek
+	     * confirmation of reload if the safe element is not set.  - FM
+	     */
+	    if ((curdoc.post_data != NULL &&
+		 curdoc.safe != TRUE) &&
+#ifdef USE_SOURCE_CACHE
+		(!(canreparse_post = HTcan_reparse_document())) &&
+#endif
+		confirm_post_resub(curdoc.address, curdoc.title,
+				   2, 1) == FALSE) {
+		HTInfoMsg(WILL_NOT_RELOAD_DOC);
+	    } else {
+		copy_address(&newdoc, &curdoc);
+		if (((strcmp(CurrentUserAgent, NonNull(LYUserAgent)) ||
+		      strcmp(CurrentNegoLanguage, NonNull(language)) ||
+		      strcmp(CurrentNegoCharset, NonNull(pref_charset))) &&
+		     (strncmp(curdoc.address, "http", 4) == 0 ||
+		      !isLYNXCGI(curdoc.address) == 0))) {
+		    /*
+		     * An option has changed which may influence content
+		     * negotiation, and the resource is from a http or https or
+		     * lynxcgi URL (the only protocols which currently do
+		     * anything with this information).  Set reloading = TRUE
+		     * so that proxy caches will be flushed, which is necessary
+		     * until the time when all proxies understand HTTP 1.1
+		     * Vary:  and all Servers properly use it...  Treat like
+		     * case LYK_RELOAD (see comments there).  - KW
+		     */
+		    reloading = TRUE;
+		}
+		if (HTisDocumentSource()) {
+		    srcmode_for_next_retrieval(1);
+		}
+#ifdef USE_SOURCE_CACHE
+		if (reloading == FALSE) {
+		    /* one more attempt to be smart enough: */
+		    if (reparse_document()) {
+			FREE(CurrentUserAgent);
+			FREE(CurrentNegoLanguage);
+			FREE(CurrentNegoCharset);
+			return FALSE;
+		    }
+		}
+#endif
+		if (canreparse_post &&
+		    confirm_post_resub(curdoc.address, curdoc.title,
+				       2, 1) == FALSE) {
+		    if (HTisDocumentSource()) {
+			srcmode_for_next_retrieval(0);
+		    }
+		    FREE(CurrentUserAgent);
+		    FREE(CurrentNegoLanguage);
+		    FREE(CurrentNegoCharset);
+		    return FALSE;
+		}
+
+		HEAD_request = HTLoadedDocumentIsHEAD();
+		HText_setNoCache(HTMainText);
+		newdoc.line = curdoc.line;
+		newdoc.link = curdoc.link;
+		LYforce_no_cache = TRUE;
+		free_address(&curdoc);	/* So it doesn't get pushed. */
+	    }
+	}
+	FREE(CurrentUserAgent);
+	FREE(CurrentNegoLanguage);
+	FREE(CurrentNegoCharset);
+	*refresh_screen = TRUE;	/* to repaint screen */
+	return FALSE;
+    }				/* end if !LYUseFormsOptions */
+#endif /* !NO_OPTION_MENU */
+#ifndef NO_OPTION_FORMS
+    /*
+     * Generally stolen from LYK_COOKIE_JAR.  Options menu handling is
+     * done in postoptions(), called from getfile() currently.
+     *
+     * postoptions() is also responsible for reloading the document
+     * before the 'options menu' but only when (a few) important
+     * options were changed.
+     *
+     * It is critical that post_data is freed here since the
+     * submission of changed options is done via the same protocol as
+     * LYNXOPTIONS:
+     */
+    /*
+     * Don't do if already viewing options page.
+     */
+    if (!LYIsUIPage(curdoc.address, UIP_OPTIONS_MENU)) {
+
+	set_address(&newdoc, LYNXOPTIONS_PAGE("/"));
+	LYFreePostData(&newdoc);
+	FREE(newdoc.bookmark);
+	newdoc.isHEAD = FALSE;
+	newdoc.safe = FALSE;
+	newdoc.internal_link = FALSE;
+	LYforce_no_cache = TRUE;
+	/* change to 'if (check_realm && !LYValidate)' and
+	   make change near top of getfile to forbid
+	   using forms options menu with -validate:  - kw */
+	if (LYValidate || check_realm) {
+	    LYPermitURL = TRUE;
+	}
+    } else {
+	/*
+	 * If already in the options menu, get out.
+	 */
+	*cmd = LYK_PREV_DOC;
+	return TRUE;
+    }
+#endif /* !NO_OPTION_FORMS */
+    return FALSE;
+}
+
+static void handle_NEXT_DOC(void)
+{
+    if (LYhist_next(&curdoc, &newdoc)) {
+	free_address(&curdoc);	/* avoid push */
+	return;
+    }
+    HTUserMsg(gettext("No next document present"));
+}
+
+static void handle_LYK_NEXT_LINK(int c,
+				 int *old_c,
+				 int real_c)
+{
+    if (curdoc.link < nlinks - 1) {	/* next link */
+	LYhighlight(OFF, curdoc.link, prev_target);
+#ifdef FASTTAB
+	/*
+	 * Move to different textarea if TAB in textarea.
+	 */
+	if (LinkIsTextarea(curdoc.link) &&
+	    c == '\t') {
+	    int thisgroup = links[curdoc.link].l_form->number;
+	    char *thisname = links[curdoc.link].l_form->name;
+
+	    do
+		curdoc.link++;
+	    while ((curdoc.link < nlinks - 1) &&
+		   LinkIsTextarea(curdoc.link) &&
+		   links[curdoc.link].l_form->number == thisgroup &&
+		   sametext(links[curdoc.link].l_form->name, thisname));
+	} else {
+	    curdoc.link++;
+	}
+#else
+	curdoc.link++;
+#endif /* FASTTAB */
+	/*
+	 * At the bottom of list and there is only one page.  Move to the top
+	 * link on the page.
+	 */
+    } else if (!more_text && LYGetNewline() == 1 && curdoc.link == nlinks - 1) {
+	set_curdoc_link(0);
+
+    } else if (more_text) {	/* next page */
+	LYChgNewline(display_lines);
+    } else if (*old_c != real_c) {
+	*old_c = real_c;
+	HTInfoMsg(ALREADY_AT_END);
+    }
+}
+
+static void handle_LYK_NEXT_PAGE(int *old_c,
+				 int real_c)
+{
+    if (more_text) {
+	LYChgNewline(display_lines);
+    } else if (curdoc.link < nlinks - 1) {
+	set_curdoc_link(nlinks - 1);
+    } else if (*old_c != real_c) {
+	*old_c = real_c;
+	HTInfoMsg(ALREADY_AT_END);
+    }
+}
+
+static BOOLEAN handle_LYK_NOCACHE(int *old_c,
+				  int real_c)
+{
+    if (nlinks > 0) {
+	if (links[curdoc.link].type == WWW_FORM_LINK_TYPE &&
+	    links[curdoc.link].l_form->type != F_SUBMIT_TYPE &&
+	    links[curdoc.link].l_form->type != F_IMAGE_SUBMIT_TYPE &&
+	    links[curdoc.link].l_form->type != F_TEXT_SUBMIT_TYPE) {
+	    if (*old_c != real_c) {
+		*old_c = real_c;
+		HTUserMsg(NOT_ON_SUBMIT_OR_LINK);
+	    }
+	    return FALSE;
+	} else {
+	    LYforce_no_cache = TRUE;
+	    reloading = TRUE;
+	}
+    }
+    return TRUE;
+}
+
+static void handle_LYK_PREV_LINK(int *arrowup,
+				 int *old_c,
+				 int real_c)
+{
+    if (curdoc.link > 0) {	/* previous link */
+	set_curdoc_link(curdoc.link - 1);
+
+    } else if (!more_text &&
+	       curdoc.link == 0 && LYGetNewline() == 1) {	/* at the top of list */
+	/*
+	 * If there is only one page of data and the user goes off the top,
+	 * just move the cursor to last link on the page.
+	 */
+	set_curdoc_link(nlinks - 1);
+
+    } else if (curdoc.line > 1) {	/* previous page */
+	/*
+	 * Go back to the previous page.
+	 */
+	int scrollamount = (LYGetNewline() > display_lines
+			    ? display_lines
+			    : LYGetNewline() - 1);
+
+	LYChgNewline(-scrollamount);
+	if (scrollamount < display_lines &&
+	    nlinks > 0 && curdoc.link == 0 &&
+	    links[0].ly - 1 + scrollamount <= display_lines) {
+	    newdoc.link = HText_LinksInLines(HTMainText,
+					     1,
+					     scrollamount) - 1;
+	} else {
+	    *arrowup = TRUE;
+	}
+
+    } else if (*old_c != real_c) {
+	*old_c = real_c;
+	HTInfoMsg(ALREADY_AT_BEGIN);
+    }
+}
+
+#define nhist_1 (nhist - 1)	/* workaround for indent */
+
+static int handle_PREV_DOC(int *cmd,
+			   int *old_c,
+			   int real_c)
+{
+    if (nhist > 0) {		/* if there is anything to go back to */
+	/*
+	 * Check if the previous document is a reply from a POST, and if so,
+	 * seek confirmation of resubmission if the safe element is not set and
+	 * the document is not still in the cache or LYresubmit_posts is set. 
+	 * If not confirmed and it is not the startfile, pop it so we go to the
+	 * yet previous document, until we're OK or reach the startfile.  If we
+	 * reach the startfile and its not OK or we don't get confirmation,
+	 * cancel.  - FM
+	 */
+	DocAddress WWWDoc;
+	HTParentAnchor *tmpanchor;
+	BOOLEAN conf = FALSE, first = TRUE;
+
+	HTLastConfirmCancelled();	/* reset flag */
+	while (nhist > 0) {
+	    conf = FALSE;
+	    if (HDOC(nhist_1).post_data == NULL) {
+		break;
+	    }
+	    WWWDoc.address = HDOC(nhist_1).address;
+	    WWWDoc.post_data = HDOC(nhist_1).post_data;
+	    WWWDoc.post_content_type =
+		HDOC(nhist_1).post_content_type;
+	    WWWDoc.bookmark = HDOC(nhist_1).bookmark;
+	    WWWDoc.isHEAD = HDOC(nhist_1).isHEAD;
+	    WWWDoc.safe = HDOC(nhist_1).safe;
+	    tmpanchor = HTAnchor_findAddress(&WWWDoc);
+	    if (HTAnchor_safe(tmpanchor)) {
+		break;
+	    }
+	    if ((HTAnchor_document(tmpanchor) == NULL &&
+		 (isLYNXIMGMAP(WWWDoc.address) ||
+		  (conf = confirm_post_resub(WWWDoc.address,
+					     HDOC(nhist_1).title,
+					     0, 0))
+		  == FALSE)) ||
+		((LYresubmit_posts && !conf &&
+		  (NONINTERNAL_OR_PHYS_DIFFERENT((DocInfo *) &history[(nhist_1)],
+						 &curdoc) ||
+		   NONINTERNAL_OR_PHYS_DIFFERENT((DocInfo *) &history[(nhist_1)],
+						 &newdoc))) &&
+		 !confirm_post_resub(WWWDoc.address,
+				     HDOC(nhist_1).title,
+				     2, 2))) {
+		if (HTLastConfirmCancelled()) {
+		    if (!first && curdoc.internal_link)
+			free_address(&curdoc);
+		    *cmd = LYK_DO_NOTHING;
+		    return 2;
+		}
+		if (nhist == 1) {
+		    HTInfoMsg(CANCELLED);
+		    *old_c = 0;
+		    *cmd = LYK_DO_NOTHING;
+		    return 2;
+		} else {
+		    HTUserMsg2(WWW_SKIP_MESSAGE, WWWDoc.address);
+		    do {	/* Should be LYhist_prev when _next supports */
+			LYpop(&curdoc);		/* skipping of forms */
+		    } while (nhist > 1
+			     && !are_different((DocInfo *) &history[nhist_1],
+					       &curdoc));
+		    first = FALSE;	/* have popped at least one */
+		    continue;
+		}
+	    } else {
+		/*
+		 * Break from loop; if user just confirmed to load again
+		 * because document wasn't in cache, set LYforce_no_cache to
+		 * avoid unnecessary repeat question down the road.  - kw
+		 */
+		if (conf)
+		    LYforce_no_cache = TRUE;
+		break;
+	    }
+	}
+
+	if (!first)
+	    curdoc.internal_link = FALSE;
+
+	/*
+	 * Set newdoc.address to empty to pop a file.
+	 */
+	LYhist_prev_register(&curdoc);	/* Why not call _prev instead of zeroing address?  */
+	free_address(&newdoc);
+#ifdef DIRED_SUPPORT
+	if (lynx_edit_mode) {
+	    DIRED_UNCACHE_2;
+	}
+#endif /* DIRED_SUPPORT */
+    } else if (child_lynx == TRUE) {
+	return (1);		/* exit on left arrow in main screen */
+
+    } else if (*old_c != real_c) {
+	*old_c = real_c;
+	HTUserMsg(ALREADY_AT_FIRST);
+    }
+    return 0;
+}
+
+static void handle_LYK_PREV_PAGE(int *old_c,
+				 int real_c)
+{
+    if (LYGetNewline() > 1) {
+	LYChgNewline(-display_lines);
+    } else if (curdoc.link > 0) {
+	set_curdoc_link(0);
+    } else if (*old_c != real_c) {
+	*old_c = real_c;
+	HTInfoMsg(ALREADY_AT_BEGIN);
+    }
+}
+
+static void handle_LYK_PRINT(BOOLEAN *ForcePush,
+			     int *old_c,
+			     int real_c)
+{
+    if (LYValidate) {
+	if (*old_c != real_c) {
+	    *old_c = real_c;
+	    HTUserMsg(PRINT_DISABLED);
+	}
+	return;
+    }
+
+    /*
+     * Don't do if already viewing print options page.
+     */
+    if (!LYIsUIPage(curdoc.address, UIP_PRINT_OPTIONS)
+	&& print_options(&newdoc.address,
+			 curdoc.address, HText_getNumOfLines()) >= 0) {
+	LYRegisterUIPage(newdoc.address, UIP_PRINT_OPTIONS);
+	StrAllocCopy(newdoc.title, PRINT_OPTIONS_TITLE);
+	LYFreePostData(&newdoc);
+	FREE(newdoc.bookmark);
+	newdoc.isHEAD = FALSE;
+	newdoc.safe = FALSE;
+	*ForcePush = TRUE;	/* see LYpush() and print_options() */
+	if (check_realm)
+	    LYPermitURL = TRUE;
+    }
+}
+
+static BOOLEAN handle_LYK_QUIT(void)
+{
+    int c;
+
+    if (LYQuitDefaultYes == TRUE) {
+	c = HTConfirmDefault(REALLY_QUIT, YES);
+    } else {
+	c = HTConfirmDefault(REALLY_QUIT, NO);
+    }
+    if (LYQuitDefaultYes == TRUE) {
+	if (c != NO) {
+	    return (TRUE);
+	} else {
+	    HTInfoMsg(NO_CANCEL);
+	}
+    } else if (c == YES) {
+	return (TRUE);
+    } else {
+	HTInfoMsg(NO_CANCEL);
+    }
+    return FALSE;
+}
+
+static BOOLEAN handle_LYK_RAW_TOGGLE(int *cmd)
+{
+    if (HTLoadedDocumentCharset()) {
+	HTUserMsg(gettext("charset for this document specified explicitly, sorry..."));
+	return FALSE;
+    } else {
+	LYUseDefaultRawMode = (BOOL) !LYUseDefaultRawMode;
+	HTUserMsg(LYRawMode ? RAWMODE_OFF : RAWMODE_ON);
+	HTMLSetCharacterHandling(current_char_set);
+	return reparse_or_reload(cmd);
+    }
+}
+
+static void handle_LYK_RELOAD(int real_cmd)
+{
+    /*
+     * Check if this is a reply from a POST, and if so,
+     * seek confirmation if the safe element is not set.  - FM
+     */
+    if ((curdoc.post_data != NULL &&
+	 curdoc.safe != TRUE) &&
+	HTConfirm(CONFIRM_POST_RESUBMISSION) == FALSE) {
+	HTInfoMsg(CANCELLED);
+	return;
+    }
+
+    /*
+     * Check to see if should reload source, or load html
+     */
+
+    if (HTisDocumentSource()) {
+	if ((forced_UCLYhdnl = HTMainText_Get_UCLYhndl()) >= 0)
+	    force_old_UCLYhndl_on_reload = TRUE;
+	srcmode_for_next_retrieval(1);
+    }
+
+    HEAD_request = HTLoadedDocumentIsHEAD();
+    HText_setNoCache(HTMainText);
+    /*
+     * Do assume the reloaded document will be the same.  - FM
+     *
+     * (I don't remember all the reasons why we couldn't assume this.  As the
+     * problems show up, we'll try to fix them, or add warnings.  - FM)
+     */
+    newdoc.line = curdoc.line;
+    newdoc.link = curdoc.link;
+    free_address(&curdoc);	/* so it doesn't get pushed */
+#ifdef VMS
+    lynx_force_repaint();
+#endif /* VMS */
+    /*
+     * Reload should force a cache refresh on a proxy.  -- Ari L. 
+     * <luotonen@dxcern.cern.ch>
+     *
+     * -- but only if this was really a reload requested by the user, not if we
+     * jumped here to handle reloading for INLINE_TOGGLE, IMAGE_TOGGLE,
+     * RAW_TOGGLE, etc.  - KW
+     */
+    if (real_cmd == LYK_RELOAD)
+	reloading = TRUE;
+
+    return;
+}
+
+#ifdef DIRED_SUPPORT
+static void handle_LYK_REMOVE(BOOLEAN *refresh_screen)
+{
+    if (lynx_edit_mode && nlinks > 0 && !no_dired_support) {
+	int linkno = curdoc.link;	/* may be changed in local_remove - kw */
+
+	local_remove(&curdoc);
+	if (LYAutoUncacheDirLists >= 1)
+	    do_cleanup_after_delete();
+	else if (curdoc.link != linkno)
+	    *refresh_screen = TRUE;
+    }
+}
+#endif /* DIRED_SUPPORT */
+
+static void handle_LYK_RIGHT_LINK(void)
+{
+    if (curdoc.link < nlinks - 1 &&
+	links[curdoc.link].ly == links[curdoc.link + 1].ly) {
+	set_curdoc_link(curdoc.link + 1);
+    }
+}
+
+static void handle_LYK_SHELL(BOOLEAN *refresh_screen,
+			     int *old_c,
+			     int real_c)
+{
+    if (!no_shell) {
+	stop_curses();
+	printf("%s\r\n", SPAWNING_MSG);
+#if defined(__CYGWIN__)
+	/* handling "exec $SHELL" does not work if $SHELL is null */
+	if (LYGetEnv("SHELL") == NULL) {
+	    Cygwin_Shell();
+	} else
+#endif
+	{
+	    static char *shell = NULL;
+
+	    if (shell == 0)
+		StrAllocCopy(shell, LYSysShell());
+	    LYSystem(shell);
+	}
+	start_curses();
+	*refresh_screen = TRUE;	/* for an HText_pageDisplay() */
+    } else {
+	if (*old_c != real_c) {
+	    *old_c = real_c;
+	    HTUserMsg(SPAWNING_DISABLED);
+	}
+    }
+}
+
+static void handle_LYK_SOFT_DQUOTES(void)
+{
+#ifdef USE_SOURCE_CACHE
+    if (!HTcan_reparse_document()) {
+#endif
+	/*
+	 * Check if this is a reply from a POST, and if so, seek confirmation
+	 * of reload if the safe element is not set.  - FM
+	 */
+	if ((curdoc.post_data != NULL &&
+	     curdoc.safe != TRUE) &&
+	    confirm_post_resub(curdoc.address, NULL, 1, 1) == FALSE) {
+	    HTInfoMsg(WILL_NOT_RELOAD_DOC);
+	} else {
+	    HText_setNoCache(HTMainText);
+	    move_address(&newdoc, &curdoc);
+	    newdoc.line = curdoc.line;
+	    newdoc.link = curdoc.link;
+	}
+#ifdef USE_SOURCE_CACHE
+    }				/* end if no bypass */
+#endif
+    soft_dquotes = (BOOLEAN) !soft_dquotes;
+    HTUserMsg(soft_dquotes ?
+	      SOFT_DOUBLE_QUOTE_ON : SOFT_DOUBLE_QUOTE_OFF);
+#ifdef USE_SOURCE_CACHE
+    (void) reparse_document();
+#endif
+    return;
+}
+
+#define GetAnchorNumber(link) \
+			((nlinks > 0 && link >= 0) \
+    			 ? links[link].anchor_number \
+			 : -1)
+#define GetAnchorLineNo(link) \
+			((nlinks > 0 && link >= 0) \
+    			 ? links[link].anchor_line_num \
+			 : -1)
+
+/*
+ * Adjust the top-of-screen line number for the new document if the redisplayed
+ * screen would not show the given link-number.
+ */
+#ifdef USE_SOURCE_CACHE
+static int wrap_reparse_document(void)
+{
+    int result;
+    int anchor_number = GetAnchorNumber(curdoc.link);
+    int old_line_num = HText_getAbsLineNumber(HTMainText, anchor_number);
+    int old_from_top = old_line_num - LYGetNewline() + 1;
+
+    /* get the offset for the current anchor */
+    int old_offset = ((nlinks > 0 && curdoc.link >= 0)
+		      ? links[curdoc.link].sgml_offset
+		      : -1);
+
+    CTRACE((tfp, "original anchor %d, topline %d, link %d, offset %d\n",
+	    anchor_number, old_line_num, curdoc.link, old_offset));
+
+    /* reparse the document (producing a new anchor list) */
+    result = reparse_document();
+
+    /* readjust top-line and link-number */
+    if (result && old_offset >= 0) {
+	int new_anchor = HText_closestAnchor(HTMainText, old_offset);
+	int new_lineno = HText_getAbsLineNumber(HTMainText, new_anchor);
+	int top_lineno;
+
+	CTRACE((tfp, "old anchor %d -> new anchor %d\n", anchor_number, new_anchor));
+
+	if (new_lineno - old_from_top < 0)
+	    old_from_top = new_lineno;
+
+	/* Newline and newdoc.line are 1-based,
+	 * but 0-based lines are simpler to work with.
+	 */
+	top_lineno = HText_getPreferredTopLine(HTMainText, new_lineno -
+					       old_from_top) + 1;
+	CTRACE((tfp, "preferred top %d\n", top_lineno));
+
+	if (top_lineno != LYGetNewline()) {
+	    LYSetNewline(top_lineno);
+	    newdoc.link = HText_anchorRelativeTo(HTMainText, top_lineno - 1, new_anchor);
+	    curdoc.link = newdoc.link;
+	    CTRACE((tfp,
+		    "adjusted anchor %d, topline %d, link %d, offset %d\n",
+		    new_anchor,
+		    top_lineno,
+		    curdoc.link,
+		    HText_locateAnchor(HTMainText, new_anchor)));
+	} else {
+	    newdoc.link = curdoc.link;
+	}
+    }
+    return result;
+}
+#endif /* USE_SOURCE_CACHE */
+
+static void handle_LYK_SOURCE(char **ownerS_address_p)
+{
+#ifdef USE_SOURCE_CACHE
+    BOOLEAN canreparse_post = FALSE;
+#endif
+
+    /*
+     * Check if this is a reply from a POST, and if so,
+     * seek confirmation if the safe element is not set.  - FM
+     */
+    if ((curdoc.post_data != NULL &&
+	 curdoc.safe != TRUE) &&
+#ifdef USE_SOURCE_CACHE
+	(!(canreparse_post = HTcan_reparse_document())) &&
+#endif
+	(curdoc.isHEAD ? HTConfirm(CONFIRM_POST_RESUBMISSION) :
+	 confirm_post_resub(curdoc.address, curdoc.title, 1, 1)) == FALSE) {
+	HTInfoMsg(CANCELLED);
+	return;
+    }
+
+    if (HTisDocumentSource()) {
+	srcmode_for_next_retrieval(-1);
+    } else {
+	if (HText_getOwner())
+	    StrAllocCopy(*ownerS_address_p, HText_getOwner());
+	LYUCPushAssumed(HTMainAnchor);
+	srcmode_for_next_retrieval(1);
+    }
+
+#ifdef USE_SOURCE_CACHE
+    if (wrap_reparse_document()) {
+	/*
+	 * These normally get cleaned up after getfile() returns;
+	 * since we're not calling getfile(), we have to clean them
+	 * up ourselves.  -dsb
+	 */
+	HTOutputFormat = WWW_PRESENT;
+#ifdef USE_PRETTYSRC
+	if (psrc_view)
+	    HTMark_asSource();
+	psrc_view = FALSE;
+#endif
+	FREE(*ownerS_address_p);	/* not used with source_cache */
+	LYUCPopAssumed();	/* probably a right place here */
+	HTMLSetCharacterHandling(current_char_set);	/* restore now */
+
+	return;
+    } else if (canreparse_post) {
+	srcmode_for_next_retrieval(0);
+	LYUCPopAssumed();	/* probably a right place here */
+	return;
+    }
+#endif
+
+    if (curdoc.title)
+	StrAllocCopy(newdoc.title, curdoc.title);
+
+    free_address(&curdoc);	/* so it doesn't get pushed */
+    LYforce_no_cache = TRUE;
+}
+
+static void handle_LYK_SWITCH_DTD(void)
+{
+#ifdef USE_SOURCE_CACHE
+    BOOLEAN canreparse = FALSE;
+
+    if (!(canreparse = HTcan_reparse_document())) {
+#endif
+	/*
+	 * Check if this is a reply from a POST, and if so,
+	 * seek confirmation of reload if the safe element
+	 * is not set.  - FM, kw
+	 */
+	if ((curdoc.post_data != NULL &&
+	     curdoc.safe != TRUE) &&
+	    confirm_post_resub(curdoc.address, NULL, 1, 1) == FALSE) {
+	    HTInfoMsg(WILL_NOT_RELOAD_DOC);
+	} else {
+	    /*
+	     * If currently viewing preparsed source, switching to the other
+	     * DTD parsing may show source differences, so stay in source view
+	     * - kw
+	     */
+
+	    /* NOTE: this conditional can be considered incorrect -
+	       current behaviour - when viewing source and
+	       LYPreparsedSource==TRUE, pressing ^V will toggle parser mode
+	       AND switch back from the source view to presentation view.-HV
+	     */
+	    if (HTisDocumentSource() && LYPreparsedSource) {
+		srcmode_for_next_retrieval(1);
+	    }
+	    HText_setNoCache(HTMainText);
+	    move_address(&newdoc, &curdoc);
+	    newdoc.line = curdoc.line;
+	    newdoc.link = curdoc.link;
+	}
+#ifdef USE_SOURCE_CACHE
+    }				/* end if no bypass */
+#endif
+    Old_DTD = !Old_DTD;
+    HTSwitchDTD(!Old_DTD);
+    HTUserMsg(Old_DTD ? USING_DTD_0 : USING_DTD_1);
+#ifdef USE_SOURCE_CACHE
+    if (canreparse) {
+	if (HTisDocumentSource() && LYPreparsedSource) {
+	    srcmode_for_next_retrieval(1);
+	}
+	if (!reparse_document()) {
+	    srcmode_for_next_retrieval(0);
+	}
+    }
+#endif
+    return;
+}
+
+#ifdef DIRED_SUPPORT
+static void handle_LYK_TAG_LINK(void)
+{
+    if (lynx_edit_mode && nlinks > 0 && !no_dired_support) {
+	if (!strcmp(LYGetHiliteStr(curdoc.link, 0), ".."))
+	    return;		/* Never tag the parent directory */
+	if (dir_list_style == MIXED_STYLE) {
+	    if (!strcmp(LYGetHiliteStr(curdoc.link, 0), "../"))
+		return;
+	} else if (!strncmp(LYGetHiliteStr(curdoc.link, 0), "Up to ", 6))
+	    return;
+	{
+	    /*
+	     * 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);
+	    }
+	}
+	if (curdoc.link < nlinks - 1) {
+	    set_curdoc_link(curdoc.link + 1);
+	} else if (!more_text && LYGetNewline() == 1 && curdoc.link == nlinks
+		   - 1) {
+	    set_curdoc_link(0);
+	} else if (more_text) {	/* next page */
+	    LYChgNewline(display_lines);
+	}
+    }
+}
+#endif /* DIRED_SUPPORT */
+
+static void handle_LYK_TOGGLE_HELP(void)
+{
+    if (user_mode == NOVICE_MODE) {
+	toggle_novice_line();
+	noviceline(more_text);
+    }
+}
+
+static void handle_LYK_TOOLBAR(BOOLEAN *try_internal,
+			       BOOLEAN *force_load,
+			       int *old_c,
+			       int real_c)
+{
+    char *cp;
+    char *toolbar = NULL;
+
+    if (!HText_hasToolbar(HTMainText)) {
+	if (*old_c != real_c) {
+	    *old_c = real_c;
+	    HTUserMsg(NO_TOOLBAR);
+	}
+    } else if (*old_c != real_c) {
+	*old_c = real_c;
+	cp = trimPoundSelector(curdoc.address);
+	HTSprintf0(&toolbar, "%s#%s", curdoc.address, LYToolbarName);
+	restorePoundSelector(cp);
+	set_address(&newdoc, toolbar);
+	FREE(toolbar);
+	*try_internal = TRUE;
+	*force_load = TRUE;	/* force MainLoop to reload */
+    }
+}
+
+static void handle_LYK_TRACE_LOG(BOOLEAN *trace_flag_ptr)
+{
+#ifndef NO_LYNX_TRACE
+    /*
+     * Check whether we've started a TRACE log in this session.  - FM
+     */
+    if (LYTraceLogFP == NULL) {
+	HTUserMsg(NO_TRACELOG_STARTED);
+	return;
+    }
+
+    /*
+     * Don't do if already viewing the TRACE log.  - FM
+     */
+    if (LYIsUIPage(curdoc.address, UIP_TRACELOG))
+	return;
+
+    /*
+     * If TRACE mode is on, turn it off during this fetch of the TRACE log, so
+     * we don't enter stuff about this fetch, and set a flag for turning it
+     * back on when we return to this loop.  Note that we'll miss any messages
+     * about memory exhaustion if it should occur.  It seems unlikely that
+     * anything else bad might happen, but if it does, we'll miss messages
+     * about that too.  We also fflush(), close, and open it again, to make
+     * sure all stderr messages thus far will be in the log.  - FM
+     */
+    if (!LYReopenTracelog(trace_flag_ptr))
+	return;
+
+    LYLocalFileToURL(&(newdoc.address), LYTraceLogPath);
+    LYRegisterUIPage(newdoc.address, UIP_TRACELOG);
+    StrAllocCopy(newdoc.title, LYNX_TRACELOG_TITLE);
+    LYFreePostData(&newdoc);
+    FREE(newdoc.bookmark);
+    newdoc.isHEAD = FALSE;
+    newdoc.safe = FALSE;
+    newdoc.internal_link = FALSE;
+    if (LYValidate || check_realm) {
+	LYPermitURL = TRUE;
+    }
+    LYforce_no_cache = TRUE;
+#else
+    HTUserMsg(TRACE_DISABLED);
+#endif /* NO_LYNX_TRACE */
+}
+
+#ifdef DIRED_SUPPORT
+static void handle_LYK_UPLOAD(void)
+{
+    /*
+     * Don't do if already viewing upload options page.
+     */
+    if (LYIsUIPage(curdoc.address, UIP_UPLOAD_OPTIONS))
+	return;
+
+    if (lynx_edit_mode && !no_dired_support) {
+	LYUpload_options(&(newdoc.address), curdoc.address);
+	StrAllocCopy(newdoc.title, UPLOAD_OPTIONS_TITLE);
+	LYFreePostData(&newdoc);
+	FREE(newdoc.bookmark);
+	newdoc.isHEAD = FALSE;
+	newdoc.safe = FALSE;
+	newdoc.internal_link = FALSE;
+	/*
+	 * Uncache the current listing so that it will be updated to included
+	 * the uploaded file if placed in the current directory.  - FM
+	 */
+	DIRED_UNCACHE_1;
+    }
+}
+#endif /* DIRED_SUPPORT */
+
+static void handle_LYK_UP_xxx(int *arrowup,
+			      int *old_c,
+			      int real_c,
+			      int scroll_by)
+{
+    if (LYGetNewline() > 1) {
+	if (LYGetNewline() - scroll_by < 1)
+	    scroll_by = LYGetNewline() - 1;
+	LYChgNewline(-scroll_by);
+	if (nlinks > 0 && curdoc.link > -1) {
+	    if (links[curdoc.link].ly + scroll_by <= display_lines) {
+		newdoc.link = curdoc.link +
+		    HText_LinksInLines(HTMainText,
+				       LYGetNewline(),
+				       scroll_by);
+	    } else {
+		*arrowup = TRUE;
+	    }
+	}
+    } else if (*old_c != real_c) {
+	*old_c = real_c;
+	HTInfoMsg(ALREADY_AT_BEGIN);
+    }
+}
+
+static void handle_LYK_UP_HALF(int *arrowup,
+			       int *old_c,
+			       int real_c)
+{
+    handle_LYK_UP_xxx(arrowup, old_c, real_c, display_lines / 2);
+}
+
+static void handle_LYK_UP_LINK(int *follow_col,
+			       int *arrowup,
+			       int *old_c,
+			       int real_c)
+{
+    if (curdoc.link > 0 &&
+	(links[0].ly != links[curdoc.link].ly ||
+	 !HText_LinksInLines(HTMainText, 1, LYGetNewline() - 1))) {
+	/* more links before this on screen, and first of them on
+	   a different line or no previous links before this screen? */
+	int newlink;
+
+	if (*follow_col == -1) {
+	    const char *text = LYGetHiliteStr(curdoc.link, 0);
+
+	    *follow_col = links[curdoc.link].lx;
+
+	    if (text != NULL)
+		*follow_col += (int) strlen(text) / 2;
+	}
+
+	newlink = find_link_near_col(*follow_col, -1);
+	if (newlink > -1) {
+	    set_curdoc_link(newlink);
+	} else if (*old_c != real_c) {
+	    *old_c = real_c;
+	    HTUserMsg(NO_LINKS_ABOVE);
+	}
+
+    } else if (curdoc.line > 1 && LYGetNewline() > 1) {		/* previous page */
+	int scrollamount = (LYGetNewline() > display_lines
+			    ? display_lines
+			    : LYGetNewline() - 1);
+
+	LYChgNewline(-scrollamount);
+	if (scrollamount < display_lines &&
+	    nlinks > 0 && curdoc.link > -1 &&
+	    links[0].ly - 1 + scrollamount <= display_lines) {
+	    newdoc.link = HText_LinksInLines(HTMainText,
+					     1,
+					     scrollamount) - 1;
+	} else {
+	    *arrowup = TRUE;
+	}
+
+    } else if (*old_c != real_c) {
+	*old_c = real_c;
+	HTInfoMsg(ALREADY_AT_BEGIN);
+    }
+}
+
+static void handle_LYK_UP_TWO(int *arrowup,
+			      int *old_c,
+			      int real_c)
+{
+    handle_LYK_UP_xxx(arrowup, old_c, real_c, 2);
+}
+
+static void handle_LYK_VIEW_BOOKMARK(BOOLEAN *refresh_screen,
+				     int *old_c,
+				     int real_c)
+{
+    const char *cp;
+
+    if (LYValidate) {
+	if (*old_c != real_c) {
+	    *old_c = real_c;
+	    HTUserMsg(BOOKMARKS_DISABLED);
+	}
+	return;
+    }
+
+    /*
+     * See if a bookmark exists.  If it does replace newdoc.address with its
+     * name.
+     */
+    if ((cp = get_bookmark_filename(&newdoc.address)) != NULL) {
+	if (*cp == '\0' || !strcmp(cp, " ") ||
+	    !strcmp(curdoc.address, newdoc.address)) {
+	    if (LYMultiBookmarks != MBM_OFF)
+		*refresh_screen = TRUE;
+	    return;
+	}
+#ifdef KANJI_CODE_OVERRIDE
+	if (HTCJK == JAPANESE) {
+	    last_kcode = NOKANJI;	/* AUTO */
+	}
+#endif
+	LYforce_no_cache = TRUE;	/*force the document to be reloaded */
+	StrAllocCopy(newdoc.title, BOOKMARK_TITLE);
+	StrAllocCopy(newdoc.bookmark, BookmarkPage);
+	LYFreePostData(&newdoc);
+	newdoc.isHEAD = FALSE;
+	newdoc.safe = FALSE;
+	newdoc.internal_link = FALSE;
+    } else {
+	if (*old_c != real_c) {
+	    *old_c = real_c;
+	    LYMBM_statusline(BOOKMARKS_NOT_OPEN);
+	    LYSleepAlert();
+	    if (LYMultiBookmarks != MBM_OFF) {
+		*refresh_screen = TRUE;
+	    }
+	}
+    }
+}
+
+static BOOLEAN handle_LYK_VLINKS(int *cmd,
+				 BOOLEAN *newdoc_link_is_absolute)
+{
+    int c;
+
+    if (LYIsUIPage(curdoc.address, UIP_VLINKS)) {
+	/*
+	 * Already viewing visited links page, so get out.
+	 */
+	*cmd = LYK_PREV_DOC;
+	return TRUE;
+    }
+
+    /*
+     * Print visited links page to file.
+     */
+    c = LYShowVisitedLinks(&newdoc.address);
+    if (c < 0) {
+	HTUserMsg(VISITED_LINKS_EMPTY);
+	return FALSE;
+    }
+    StrAllocCopy(newdoc.title, VISITED_LINKS_TITLE);
+    LYFreePostData(&newdoc);
+    FREE(newdoc.bookmark);
+    newdoc.isHEAD = FALSE;
+    newdoc.safe = FALSE;
+    newdoc.internal_link = FALSE;
+    if (c > 0) {
+	/* Select a correct link. */
+	*newdoc_link_is_absolute = TRUE;
+	newdoc.link = c - 1;
+    }
+    if (LYValidate || check_realm) {
+	LYPermitURL = TRUE;
+	StrAllocCopy(lynxlinksfile, newdoc.address);
+    }
+    return FALSE;
+}
+
+void handle_LYK_WHEREIS(int cmd,
+			BOOLEAN *refresh_screen)
+{
+    BOOLEAN have_target_onscreen = (BOOLEAN) (*prev_target != '\0' &&
+					      HText_pageHasPrevTarget());
+    BOOL found;
+    int oldcur = curdoc.link;	/* temporarily remember */
+    char *remember_old_target = NULL;
+
+    if (have_target_onscreen)
+	StrAllocCopy(remember_old_target, prev_target);
+    else
+	StrAllocCopy(remember_old_target, "");
+
+    if (cmd == LYK_WHEREIS) {
+	/*
+	 * Reset prev_target to force prompting for a new search string and to
+	 * turn off highlighting if no search string is entered by the user.
+	 */
+	*prev_target = '\0';
+    }
+    found = textsearch(&curdoc, prev_target, sizeof(prev_target) - 1,
+		       (cmd == LYK_WHEREIS)
+		       ? 0
+		       : ((cmd == LYK_NEXT)
+			  ? 1
+			  : -1));
+
+    /*
+     * Force a redraw to ensure highlighting of hits even when found on the
+     * same page, or clearing of highlighting if the default search string was
+     * erased without replacement.  - FM
+     */
+    /*
+     * Well let's try to avoid it at least in a few cases
+     * where it is not needed. - kw
+     */
+    if (www_search_result >= 0 && www_search_result != curdoc.line) {
+	*refresh_screen = TRUE;	/* doesn't really matter */
+    } else if (!found) {
+	*refresh_screen = have_target_onscreen;
+    } else if (!have_target_onscreen && found) {
+	*refresh_screen = TRUE;
+    } else if (www_search_result == curdoc.line &&
+	       curdoc.link == oldcur &&
+	       curdoc.link >= 0 && nlinks > 0 &&
+	       links[curdoc.link].ly >= (display_lines / 3)) {
+	*refresh_screen = TRUE;
+    } else if ((case_sensitive && 0 != strcmp(prev_target,
+					      remember_old_target)) ||
+	       (!case_sensitive && 0 != strcasecomp8(prev_target,
+						     remember_old_target))) {
+	*refresh_screen = TRUE;
+    }
+    FREE(remember_old_target);
+}
+
+/*
+ * Get a number from the user and follow that link number.
+ */
+static void handle_LYK_digit(int c,
+			     BOOLEAN *force_load,
+			     char *user_input_buffer,
+			     int *old_c,
+			     int real_c,
+			     BOOLEAN *try_internal GCC_UNUSED)
+{
+    int lindx = ((nlinks > 0) ? curdoc.link : 0);
+    int number;
+    char *temp = NULL;
+
+    /* pass cur line num for use in follow_link_number()
+     * Note: Current line may not equal links[cur].line
+     */
+    number = curdoc.line;
+    switch (follow_link_number(c, lindx, &newdoc, &number)) {
+    case DO_LINK_STUFF:
+	/*
+	 * Follow a normal link.
+	 */
+	set_address(&newdoc, links[lindx].lname);
+	StrAllocCopy(newdoc.title, LYGetHiliteStr(lindx, 0));
+#ifndef DONT_TRACK_INTERNAL_LINKS
+	/*
+	 * For internal links, retain POST content if present.  If we are on
+	 * the List Page, prevent pushing it on the history stack.  Otherwise
+	 * set try_internal to signal that the top of the loop should attempt
+	 * to reposition directly, without calling getfile.  - kw
+	 */
+	if (links[lindx].type == WWW_INTERN_LINK_TYPE) {
+	    LYinternal_flag = TRUE;
+	    newdoc.internal_link = TRUE;
+	    if (LYIsListpageTitle(NonNull(curdoc.title)) &&
+		(LYIsUIPage(curdoc.address, UIP_LIST_PAGE) ||
+		 LYIsUIPage(curdoc.address, UIP_ADDRLIST_PAGE))) {
+		if (check_history()) {
+		    LYinternal_flag = TRUE;
+		} else {
+		    HTLastConfirmCancelled();	/* reset flag */
+		    if (!confirm_post_resub(newdoc.address,
+					    newdoc.title,
+					    ((LYresubmit_posts &&
+					      HText_POSTReplyLoaded(&newdoc))
+					     ? 1
+					     : 2),
+					    2)) {
+			if (HTLastConfirmCancelled() ||
+			    (LYresubmit_posts &&
+			     !HText_POSTReplyLoaded(&newdoc))) {
+			    /* cancel the whole thing */
+			    LYforce_no_cache = FALSE;
+			    reloading = FALSE;
+			    copy_address(&newdoc, &curdoc);
+			    StrAllocCopy(newdoc.title, curdoc.title);
+			    newdoc.internal_link = curdoc.internal_link;
+			    HTInfoMsg(CANCELLED);
+			    if (nlinks > 0)
+				HText_pageDisplay(curdoc.line, prev_target);
+			    break;
+			} else if (LYresubmit_posts) {
+			    /* If LYresubmit_posts is set, and the
+			       answer was No, and we have a cached
+			       copy, then use it. - kw */
+			    LYforce_no_cache = FALSE;
+			} else {
+			    /* if No, but not ^C or ^G, drop
+			     * the post data.  Maybe the link
+			     * wasn't meant to be internal after
+			     * all, here we can recover from that
+			     * assumption. - kw */
+			    LYFreePostData(&newdoc);
+			    newdoc.internal_link = FALSE;
+			    HTAlert(DISCARDING_POST_DATA);
+			}
+		    }
+		}
+		/*
+		 * Don't push the List Page if we follow an internal link given
+		 * by it.  - kw
+		 */
+		free_address(&curdoc);
+	    } else
+		*try_internal = TRUE;
+	    if (!(LYresubmit_posts && newdoc.post_data))
+		LYinternal_flag = TRUE;
+	    *force_load = TRUE;
+	    break;
+	} else {
+	    /*
+	     * Free POST content if not an internal link.  - kw
+	     */
+	    LYFreePostData(&newdoc);
+	}
+#endif /* DONT_TRACK_INTERNAL_LINKS */
+	/*
+	 * Might be an anchor in the same doc from a POST form.  If so, don't
+	 * free the content.  -- FM
+	 */
+	if (are_different(&curdoc, &newdoc)) {
+	    LYFreePostData(&newdoc);
+	    FREE(newdoc.bookmark);
+	    newdoc.isHEAD = FALSE;
+	    newdoc.safe = FALSE;
+	    if (isLYNXMESSAGES(newdoc.address))
+		LYforce_no_cache = TRUE;
+	}
+	newdoc.internal_link = FALSE;
+	*force_load = TRUE;	/* force MainLoop to reload */
+	break;
+
+    case DO_GOTOLINK_STUFF:
+	/*
+	 * Position on a normal link, don't follow it.  - KW
+	 */
+	LYSetNewline(newdoc.line);
+	newdoc.line = 1;
+	if (LYGetNewline() == curdoc.line) {
+	    /*
+	     * It's a link in the current page.  - FM
+	     */
+	    if (nlinks > 0 && curdoc.link > -1) {
+		if (curdoc.link == newdoc.link) {
+		    /*
+		     * It's the current link, and presumably reflects a typo in
+		     * the statusline entry, so issue a statusline message for
+		     * the typo-prone users (like me 8-).  - FM
+		     */
+		    StrAllocCopy(temp, user_input_buffer);
+		    sprintf(user_input_buffer,
+			    LINK_ALREADY_CURRENT, number);
+		    HTUserMsg(user_input_buffer);
+		    LYstrncpy(user_input_buffer, temp, MAX_LINE - 1);
+		    FREE(temp);
+		} else {
+		    /*
+		     * It's a different link on this page,
+		     */
+		    set_curdoc_link(newdoc.link);
+		    newdoc.link = 0;
+		}
+	    }
+	}
+	break;			/* nothing more to do */
+
+    case DO_GOTOPAGE_STUFF:
+	/*
+	 * Position on a page in this document.  - FM
+	 */
+	LYSetNewline(newdoc.line);
+	newdoc.line = 1;
+	if (LYGetNewline() == curdoc.line) {
+	    /*
+	     * It's the current page, so issue a statusline message for the
+	     * typo-prone users (like me 8-).  - FM
+	     */
+	    if (LYGetNewline() <= 1) {
+		HTInfoMsg(ALREADY_AT_BEGIN);
+	    } else if (!more_text) {
+		HTInfoMsg(ALREADY_AT_END);
+	    } else {
+		StrAllocCopy(temp, user_input_buffer);
+		sprintf(user_input_buffer,
+			ALREADY_AT_PAGE, number);
+		HTUserMsg(user_input_buffer);
+		LYstrncpy(user_input_buffer, temp, MAX_LINE - 1);
+		FREE(temp);
+	    }
+	}
+	break;
+
+    case PRINT_ERROR:
+	*old_c = real_c;
+	HTUserMsg(BAD_LINK_NUM_ENTERED);
+	break;
+    }
+    return;
+}
+
+#ifdef SUPPORT_CHDIR
+
+/* original implementation by VH */
+void handle_LYK_CHDIR(void)
+{
+    static char buf[LY_MAXPATH];
+    char *p = NULL;
+
+    if (no_chdir) {
+	HTUserMsg(CHDIR_DISABLED);
+	return;
+    }
+
+    _statusline(gettext("cd to:"));
+    /* some people may prefer automatic clearing of the previous user input,
+       here, to do this, just uncomment next line - VH */
+    /* buf[0]='\0'; */
+    if (LYgetstr(buf, VISIBLE, sizeof(buf) - 1, NORECALL) < 0 || !*buf) {
+	HTInfoMsg(CANCELLED);
+	return;
+    }
+
+    if (LYIsTilde(*buf) && (LYIsPathSep(buf[1]) || buf[1] == '\0')) {
+	HTSprintf0(&p, "%s%s", Home_Dir(), buf + 1);
+    } else {
+	StrAllocCopy(p, buf);
+    }
+
+    CTRACE((tfp, "changing directory to '%s'\n", p));
+    if (chdir(p)) {
+	switch (errno) {
+	case EACCES:
+	    HTInfoMsg(COULD_NOT_ACCESS_DIR);
+	    break;
+	case ENOENT:
+	    HTInfoMsg(gettext("No such directory"));
+	    break;
+	case ENOTDIR:
+	    HTInfoMsg(gettext("A component of path is not a directory"));
+	    break;
+	default:
+	    HTInfoMsg(gettext("failed to change directory"));
+	    break;
+	}
+    } else {
+#ifdef DIRED_SUPPORT
+	/*if in dired, load content of other directory */
+	if (!no_dired_support
+	    && (lynx_edit_mode || (LYIsUIPage(curdoc.address, UIP_DIRED_MENU)))) {
+	    char buf2[LY_MAXPATH];
+	    char *addr = NULL;
+
+	    Current_Dir(buf2);
+	    LYLocalFileToURL(&addr, buf2);
+
+	    newdoc.address = addr;
+	    newdoc.isHEAD = FALSE;
+	    StrAllocCopy(newdoc.title, gettext("A URL specified by the user"));
+	    LYFreePostData(&newdoc);
+	    FREE(newdoc.bookmark);
+	    newdoc.safe = FALSE;
+	    newdoc.internal_link = FALSE;
+	    /**force_load = TRUE;*/
+	    if (lynx_edit_mode) {
+		DIRED_UNCACHE_2;
+	    }
+	} else
+#endif
+	    HTInfoMsg(OPERATION_DONE);
+    }
+    FREE(p);
+}
+#endif
+
+#ifdef USE_CURSES_PADS
+/*
+ * Having jumps larger than this is counter-productive.  Indeed, it is natural
+ * to expect that when the relevant text appears, one would "overshoot" and
+ * would scroll 3-4 extra full screens.  When going back, the "accumulation"
+ * logic would again start moving in full screens, so one would overshoot
+ * again, etc.
+ *
+ * Going back, one can fix it in 28 keypresses. The relevant text will appear
+ * on the screen soon enough for the key-repeat to become not that important,
+ * and we are still moving in smaller steps than when we overshot.  Since key
+ * repeat is not important, even if we overshoot again, it is going to be by 30
+ * steps, which is easy to fix by reversing the direction again.
+ */
+static int repeat_to_delta(int n)
+{
+    int threshold = LYcols / 3;
+
+    while (threshold > 0) {
+	if (n >= threshold) {
+	    n = threshold;
+	    break;
+	}
+	threshold = (threshold * 2) / 3;
+    }
+    return n;
+}
+
+static void handle_LYK_SHIFT_LEFT(BOOLEAN *flag, int count)
+{
+    if (!LYwideLines) {
+	HTAlert(SHIFT_VS_LINEWRAP);
+	return;
+    }
+    if (LYshiftWin > 0) {
+	LYshiftWin -= repeat_to_delta(count);
+	*flag = TRUE;
+    }
+    if (LYshiftWin < 0)
+	LYshiftWin = 0;
+}
+
+static void handle_LYK_SHIFT_RIGHT(BOOLEAN *flag, int count)
+{
+    if (!LYwideLines) {
+	HTAlert(SHIFT_VS_LINEWRAP);
+	return;
+    }
+    LYshiftWin += repeat_to_delta(count);
+    *flag = TRUE;
+}
+
+static BOOLEAN handle_LYK_LINEWRAP_TOGGLE(int *cmd,
+					  BOOLEAN *flag)
+{
+    static const char *choices[] =
+    {
+	"Try to fit screen width",
+	"No line wrap in columns",
+	"Wrap columns at screen width",
+	"Wrap columns at 3/4 screen width",
+	"Wrap columns at 2/3 screen width",
+	"Wrap columns at 1/2 screen width",
+	"Wrap columns at 1/3 screen width",
+	"Wrap columns at 1/4 screen width",
+	NULL
+    };
+    static int wrap[] =
+    {
+	0,
+	0,
+	12,			/* In units of 1/12 */
+	9,
+	8,
+	6,
+	4,
+	3
+    };
+    int c;
+
+    if (LYwin == stdscr)
+	return FALSE;
+
+    /* Somehow the mouse is over the number instead of being over the
+       name, so we decrease x. */
+    c = LYChoosePopup(!LYwideLines, LYlines / 2 - 2, LYcolLimit / 2 - 6,
+		      choices, TABLESIZE(choices) - 1, FALSE, TRUE);
+    /*
+     * LYhandlePopupList() wasn't really meant to be used outside of old-style
+     * Options menu processing.  One result of mis-using it here is that we
+     * have to deal with side-effects regarding SIGINT signal handler and the
+     * term_options global variable.  - kw
+     */
+    if (term_options)
+	return FALSE;
+    LYwideLines = c;
+    LYtableCols = wrap[c];
+
+    if (LYwideLines == 0)
+	LYshiftWin = 0;
+    *flag = TRUE;
+    HTUserMsg(LYwideLines ? LINEWRAP_OFF : LINEWRAP_ON);
+    return reparse_or_reload(cmd);
+}
+#endif
+
+/*
+ * Here's where we do all the work.
+ * mainloop is basically just a big switch dependent on the users input.  I
+ * have tried to offload most of the work done here to procedures to make it
+ * more modular, but this procedure still does a lot of variable manipulation. 
+ * This needs some work to make it neater.  - Lou Moutilli
+ *					(memoir from the original Lynx - FM)
+ */
+
+int mainloop(void)
+{
+#if defined(WIN_EX)		/* 1997/10/08 (Wed) 14:52:06 */
+    char sjis_buff[MAX_LINE];
+    char temp_buff[sizeof(sjis_buff) * 4];
+#endif
+    int c = 0;
+    int real_c = 0;
+    int old_c = 0;
+    int pending_form_c = -1;
+    int cmd = LYK_DO_NOTHING, real_cmd = LYK_DO_NOTHING;
+    int getresult;
+    int arrowup = FALSE, show_help = FALSE;
+    char user_input_buffer[MAX_LINE];
+    const char *cshelpfile = NULL;
+    BOOLEAN first_file = TRUE;
+    BOOLEAN popped_doc = FALSE;
+    BOOLEAN refresh_screen = FALSE;
+    BOOLEAN force_load = FALSE;
+    BOOLEAN try_internal = FALSE;
+    BOOLEAN crawl_ok = FALSE;
+    BOOLEAN vi_keys_flag = vi_keys;
+    BOOLEAN emacs_keys_flag = emacs_keys;
+    BOOLEAN trace_mode_flag = FALSE;
+    BOOLEAN forced_HTML_mode = LYforce_HTML_mode;
+    char cfile[128];
+    FILE *cfp;
+    char *cp;
+    int ch = 0;
+    RecallType recall = NORECALL;
+    int URLTotal = 0;
+    int URLNum;
+    BOOLEAN FirstURLRecall = TRUE;
+    char *temp = NULL;
+    BOOLEAN ForcePush = FALSE;
+    BOOLEAN override_LYresubmit_posts = FALSE;
+    BOOLEAN newdoc_link_is_absolute = FALSE;
+    BOOLEAN curlink_is_editable;
+    BOOLEAN use_last_tfpos;
+    unsigned int len;
+    int i;
+    int follow_col = -1, key_count = 0, last_key = 0;
+    int tmpNewline;
+    DocInfo tmpDocInfo;
+
+    /* "internal" means "within the same document, with certainty".  It includes a
+     * space so it cannot conflict with any (valid) "TYPE" attributes on A
+     * elements.  [According to which DTD, anyway??] - kw
+     */
+    HTInternalLink = HTAtom_for("internal link");	/* init, used as const */
+
+#ifndef WWW_SOURCE
+    WWW_SOURCE = HTAtom_for("www/source");	/* init, used as const */
+#endif
+
+    /*
+     * curdoc.address contains the name of the file that is currently open.
+     * newdoc.address contains the name of the file that will soon be
+     *                opened if it exits.
+     * prev_target    contains the last search string the user searched for.
+     * newdoc.title   contains the link name that the user last chose to get
+     *                into the current link (file).
+     */
+    /* initialize some variables */
+    newdoc.address = NULL;
+    newdoc.title = NULL;
+    newdoc.post_data = NULL;
+    newdoc.post_content_type = NULL;
+    newdoc.bookmark = NULL;
+    newdoc.internal_link = FALSE;
+    curdoc.address = NULL;
+    curdoc.title = NULL;
+    curdoc.post_data = NULL;
+    curdoc.post_content_type = NULL;
+    curdoc.bookmark = NULL;
+    curdoc.internal_link = FALSE;
+#ifdef USE_COLOR_STYLE
+    curdoc.style = NULL;
+    newdoc.style = NULL;
+#endif
+#ifndef USE_SESSIONS
+    nhist = 0;
+#endif
+    user_input_buffer[(sizeof(user_input_buffer) - 1)] = '\0';
+    *prev_target = '\0';
+    *user_input_buffer = '\0';
+#ifdef LY_FIND_LEAKS
+    atexit(free_mainloop_variables);
+#endif
+  initialize:
+    set_address(&newdoc, startfile);
+    StrAllocCopy(startrealm, startfile);
+    StrAllocCopy(newdoc.title, gettext("Entry into main screen"));
+    newdoc.isHEAD = FALSE;
+    newdoc.safe = FALSE;
+    newdoc.line = 1;
+    newdoc.link = 0;
+
+#ifdef USE_SLANG
+    if (TRACE && LYCursesON) {
+	LYaddstr("\n");
+	LYrefresh();
+    }
+#endif /* USE_SLANG */
+    CTRACE((tfp, "Entering mainloop, startfile=%s\n", startfile));
+
+    if (form_post_data) {
+	BStrCopy0(newdoc.post_data, form_post_data);
+	StrAllocCopy(newdoc.post_content_type,
+		     "application/x-www-form-urlencoded");
+    } else if (form_get_data) {
+	StrAllocCat(newdoc.address, form_get_data);
+    }
+
+    if (bookmark_start) {
+	if (LYValidate) {
+	    HTAlert(BOOKMARKS_DISABLED);
+	    bookmark_start = FALSE;
+	    goto initialize;
+	} else if (traversal) {
+	    HTAlert(BOOKMARKS_NOT_TRAVERSED);
+	    traversal = FALSE;
+	    crawl = FALSE;
+	    bookmark_start = FALSE;
+	    goto initialize;
+	} else {
+	    const char *cp1;
+
+	    /*
+	     * See if a bookmark page exists.  If it does, replace
+	     * newdoc.address with its name
+	     */
+	    if ((cp1 = get_bookmark_filename(&newdoc.address)) != NULL &&
+		*cp1 != '\0' && strcmp(cp1, " ")) {
+		StrAllocCopy(newdoc.title, BOOKMARK_TITLE);
+		StrAllocCopy(newdoc.bookmark, BookmarkPage);
+		StrAllocCopy(startrealm, newdoc.address);
+		LYFreePostData(&newdoc);
+		newdoc.isHEAD = FALSE;
+		newdoc.safe = FALSE;
+		CTRACE((tfp, "Using bookmarks=%s\n", newdoc.address));
+	    } else {
+		HTUserMsg(BOOKMARKS_NOT_OPEN);
+		bookmark_start = FALSE;
+		goto initialize;
+	    }
+	}
+    }
+
+    FREE(form_post_data);
+    FREE(form_get_data);
+
+    LYSetDisplayLines();
+
+    while (TRUE) {
+#ifdef USE_COLOR_STYLE
+	if (curdoc.style != NULL)
+	    force_load = TRUE;
+#endif
+	/*
+	 * If newdoc.address is different from curdoc.address then we need to
+	 * go out and find and load newdoc.address.
+	 */
+	if (LYforce_no_cache || force_load ||
+	    are_different(&curdoc, &newdoc)) {
+
+	    force_load = FALSE;	/* done */
+	    if (TRACE && LYCursesON) {
+		LYHideCursor();	/* make sure cursor is down */
+#ifdef USE_SLANG
+		LYaddstr("\n");
+#endif /* USE_SLANG */
+		LYrefresh();
+	    }
+	  try_again:
+	    /*
+	     * Push the old file onto the history stack if we have a current
+	     * doc and a new address.  - FM
+	     */
+	    if (curdoc.address && newdoc.address) {
+		/*
+		 * Don't actually push if this is a LYNXDOWNLOAD URL, because
+		 * that returns NORMAL even if it fails due to a spoof attempt
+		 * or file access problem, and we set the newdoc structure
+		 * elements to the curdoc structure elements under case NORMAL. 
+		 * - FM
+		 */
+		if (!isLYNXDOWNLOAD(newdoc.address)) {
+		    LYpush(&curdoc, ForcePush);
+		}
+	    } else if (!newdoc.address) {
+		/*
+		 * If newdoc.address is empty then pop a file and load it.  -
+		 * FM
+		 */
+		LYhist_prev(&newdoc);
+		popped_doc = TRUE;
+
+#ifndef DONT_TRACK_INTERNAL_LINKS
+		/*
+		 * If curdoc had been reached via an internal
+		 * (fragment) link from what we now have just
+		 * popped into newdoc, then override non-caching in
+		 * all cases. - kw
+		 */
+		if (curdoc.internal_link &&
+		    !are_phys_different(&curdoc, &newdoc)) {
+		    LYinternal_flag = TRUE;
+		    LYoverride_no_cache = TRUE;
+		    LYforce_no_cache = FALSE;
+		    try_internal = TRUE;
+		} else
+#endif /* TRACK_INTERNAL_LINKS */
+		{
+		    /*
+		     * Force a no_cache override unless it's a bookmark file,
+		     * or it has POST content and LYresubmit_posts is set
+		     * without safe also set, and we are not going to another
+		     * position in the current document or restoring the
+		     * previous document due to a NOT_FOUND or NULLFILE return
+		     * value from getfile().  - FM
+		     */
+		    if ((newdoc.bookmark != NULL) ||
+			(newdoc.post_data != NULL &&
+			 !newdoc.safe &&
+			 LYresubmit_posts &&
+			 !override_LYresubmit_posts &&
+			 NO_INTERNAL_OR_DIFFERENT(&curdoc, &newdoc))) {
+			LYoverride_no_cache = FALSE;
+		    } else {
+			LYoverride_no_cache = TRUE;
+		    }
+		}
+	    }
+	    override_LYresubmit_posts = FALSE;
+
+	    if (HEAD_request) {
+		/*
+		 * Make SURE this is an appropriate request.  - FM
+		 */
+		if (newdoc.address) {
+		    if (LYCanDoHEAD(newdoc.address) == TRUE) {
+			newdoc.isHEAD = TRUE;
+		    } else if (isLYNXIMGMAP(newdoc.address)) {
+			if (LYCanDoHEAD(newdoc.address + LEN_LYNXIMGMAP) == TRUE) {
+			    StrAllocCopy(temp, newdoc.address + LEN_LYNXIMGMAP);
+			    free_address(&newdoc);
+			    newdoc.address = temp;
+			    newdoc.isHEAD = TRUE;
+			    temp = NULL;
+			}
+		    }
+		}
+		try_internal = FALSE;
+		HEAD_request = FALSE;
+	    }
+
+	    /*
+	     * If we're getting the TRACE log and it's not new, check whether
+	     * its HText structure has been dumped, and if so, fflush() and
+	     * fclose() it to ensure it's fully updated, and then fopen() it
+	     * again.  - FM
+	     */
+	    if (LYUseTraceLog == TRUE &&
+		trace_mode_flag == FALSE &&
+		LYTraceLogFP != NULL &&
+		LYIsUIPage(newdoc.address, UIP_TRACELOG)) {
+		DocAddress WWWDoc;
+		HTParentAnchor *tmpanchor;
+
+		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;
+		WWWDoc.safe = newdoc.safe;
+		tmpanchor = HTAnchor_findAddress(&WWWDoc);
+		if ((HText *) HTAnchor_document(tmpanchor) == NULL) {
+		    if (!LYReopenTracelog(&trace_mode_flag)) {
+			old_c = 0;
+			cmd = LYK_PREV_DOC;
+			goto new_cmd;
+		    }
+		}
+	    }
+
+	    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;
+	    }
+
+	    /* reset these two variables here before getfile()
+	     * so they will be available in partial mode
+	     * (was previously implemented in case NORMAL).
+	     */
+	    *prev_target = '\0';	/* Reset for new coming document */
+	    LYSetNewline(newdoc.line);	/* set for LYGetNewline() */
+
+#ifdef USE_PRETTYSRC
+	    psrc_first_tag = TRUE;
+#endif
+#ifdef TEXTFIELDS_MAY_NEED_ACTIVATION
+	    textfields_need_activation = textfields_activation_option;
+#endif
+	    FREE(LYRequestReferer);
+	    /*
+	     * Don't send Referer if we have to load a document again that we
+	     * got from the history stack.  We don't know any more how we
+	     * originally got to that page.  Using a Referer based on the
+	     * current HTMainText could only be right by coincidence.  - kw
+	     * 1999-11-01
+	     */
+	    if (popped_doc)
+		LYNoRefererForThis = TRUE;
+
+#ifndef DONT_TRACK_INTERNAL_LINKS
+	    if (try_internal) {
+		if (newdoc.address &&
+		    isLYNXIMGMAP(newdoc.address)) {
+		    try_internal = FALSE;
+		} else if (curdoc.address &&
+			   isLYNXIMGMAP(curdoc.address)) {
+		    try_internal = FALSE;
+		}
+	    }
+	    if (try_internal) {
+		char *hashp = findPoundSelector(newdoc.address);
+
+		if (hashp) {
+		    HTFindPoundSelector(hashp + 1);
+		}
+		getresult = (HTMainText != NULL) ? NORMAL : NOT_FOUND;
+		try_internal = FALSE;	/* done */
+		/* fix up newdoc.address which may have been fragment-only */
+		if (getresult == NORMAL && (!hashp || hashp == newdoc.address)) {
+		    if (!hashp) {
+			set_address(&newdoc, HTLoadedDocumentURL());
+		    } else {
+			StrAllocCopy(temp, HTLoadedDocumentURL());
+			StrAllocCat(temp, hashp);	/* append fragment */
+			set_address(&newdoc, temp);
+			FREE(temp);
+		    }
+		}
+	    } else {
+		if (newdoc.internal_link && newdoc.address &&
+		    *newdoc.address == '#' && nhist > 0) {
+		    char *cp0;
+
+		    if (isLYNXIMGMAP(HDOC(nhist_1).address))
+			cp0 = HDOC(nhist_1).address + LEN_LYNXIMGMAP;
+		    else
+			cp0 = HDOC(nhist_1).address;
+		    StrAllocCopy(temp, cp0);
+		    (void) trimPoundSelector(temp);
+		    StrAllocCat(temp, newdoc.address);
+		    free_address(&newdoc);
+		    newdoc.address = temp;
+		    temp = NULL;
+		}
+		tmpDocInfo = newdoc;
+		tmpNewline = -1;
+		getresult = getfile(&newdoc, &tmpNewline);
+		if (!reloading && !popped_doc && (tmpNewline >= 0)) {
+		    LYSetNewline(tmpNewline);
+		} else {
+		    newdoc.link = tmpDocInfo.link;
+		}
+	    }
+#else /* TRACK_INTERNAL_LINKS */
+	    tmpDocInfo = newdoc;
+	    tmpNewline = -1;
+	    getresult = getfile(&newdoc, &tmpNewline);
+	    if (!reloading && !popped_doc && (tmpNewline >= 0)) {
+		LYSetNewline(tmpNewline);
+	    } else {
+		newdoc.link = tmpDocInfo.link;
+	    }
+#endif /* TRACK_INTERNAL_LINKS */
+
+#ifdef INACTIVE_INPUT_STYLE_VH
+	    textinput_redrawn = FALSE;	/* for sure */
+#endif
+
+	    switch (getresult) {
+
+	    case NOT_FOUND:
+		/*
+		 * OK!  can't find the file, so it must not be around now.  Do
+		 * any error logging, if appropriate.
+		 */
+		LYoverride_no_cache = FALSE;	/* Was TRUE if popped. - FM */
+		LYinternal_flag = FALSE;	/* Reset to default. - kw */
+		turn_trace_back_on(&trace_mode_flag);
+		if (!first_file && !LYCancelledFetch) {
+		    /*
+		     * Do error mail sending and/or traversal stuff.  Note that
+		     * the links[] elements may not be valid at this point, if
+		     * we did call HTuncache_current_document!  This should not
+		     * have happened for traversal, but for sending error mail
+		     * check that HTMainText exists for this reason.  - kw
+		     */
+		    if (error_logging && nhist > 0 && !popped_doc &&
+			!LYUserSpecifiedURL &&
+			HTMainText &&
+			nlinks > 0 && curdoc.link < nlinks &&
+			!isLYNXHIST(NonNull(newdoc.address)) &&
+#ifdef USE_CACHEJAR
+			!isLYNXCACHE(NonNull(newdoc.address)) &&
+#endif
+			!isLYNXCOOKIE(NonNull(newdoc.address))) {
+			char *mail_owner = NULL;
+
+			if (owner_address && isMAILTO_URL(owner_address)) {
+			    mail_owner = owner_address + LEN_MAILTO_URL;
+			}
+			/*
+			 * Email a bad link message to the owner of the
+			 * document, or to ALERTMAIL if defined, but NOT to
+			 * lynx-dev (it is rejected in mailmsg).  - FM, kw
+			 */
+#ifndef ALERTMAIL
+			if (mail_owner)
+#endif
+			    mailmsg(curdoc.link,
+				    mail_owner,
+				    HDOC(nhist_1).address,
+				    HDOC(nhist_1).title);
+		    }
+		    if (traversal) {
+			FILE *ofp;
+
+			if ((ofp = LYAppendToTxtFile(TRAVERSE_ERRORS)) == NULL) {
+			    if ((ofp = LYNewTxtFile(TRAVERSE_ERRORS)) == NULL) {
+				perror(NOOPEN_TRAV_ERR_FILE);
+				exit_immediately(EXIT_FAILURE);
+			    }
+			}
+			if (nhist > 0) {
+			    fprintf(ofp,
+				    "%s %s\tin %s\n",
+				    popped_doc ?
+				    newdoc.address : links[curdoc.link].lname,
+				    links[curdoc.link].target,
+				    HDOC(nhist_1).address);
+			} else {
+			    fprintf(ofp,
+				    "%s %s\t\n",
+				    popped_doc ?
+				    newdoc.address : links[curdoc.link].lname,
+				    links[curdoc.link].target);
+			}
+			LYCloseOutput(ofp);
+		    }
+		}
+
+		/*
+		 * Fall through to do the NULL stuff and reload the old file,
+		 * unless the first file wasn't found or has gone missing.
+		 */
+		if (!nhist) {
+		    /*
+		     * If nhist = 0 then it must be the first file.
+		     */
+		    exit_immediately_with_error_message(NOT_FOUND, first_file);
+		    return (EXIT_FAILURE);
+		}
+		/* FALLTHRU */
+
+	    case NULLFILE:
+		/*
+		 * Not supposed to return any file.
+		 */
+		LYoverride_no_cache = FALSE;	/* Was TRUE if popped. - FM */
+		popped_doc = FALSE;	/* Was TRUE if popped. - FM */
+		LYinternal_flag = FALSE;	/* Reset to default. - kw */
+		turn_trace_back_on(&trace_mode_flag);
+		free_address(&newdoc);	/* to pop last doc */
+		FREE(newdoc.bookmark);
+		LYJumpFileURL = FALSE;
+		reloading = FALSE;
+		LYPermitURL = FALSE;
+		LYCancelledFetch = FALSE;
+		ForcePush = FALSE;
+		LYforce_HTML_mode = FALSE;
+		force_old_UCLYhndl_on_reload = FALSE;
+		if (traversal) {
+		    crawl_ok = FALSE;
+		    if (traversal_link_to_add) {
+			/*
+			 * It's a binary file, or the fetch attempt failed. 
+			 * Add it to TRAVERSE_REJECT_FILE so we don't try again
+			 * in this run.
+			 */
+			if (!lookup_reject(traversal_link_to_add)) {
+			    add_to_reject_list(traversal_link_to_add);
+			}
+			FREE(traversal_link_to_add);
+		    }
+		}
+		/*
+		 * Make sure the first file was found and has not gone missing.
+		 */
+		if (!nhist) {
+		    /*
+		     * If nhist = 0 then it must be the first file.
+		     */
+		    if (first_file && homepage &&
+			!LYSameFilename(homepage, startfile)) {
+			/*
+			 * Couldn't return to the first file but there is a
+			 * homepage we can use instead.  Useful for when the
+			 * first URL causes a program to be invoked.  - GL
+			 *
+			 * But first make sure homepage is different from
+			 * startfile (above), then make it the same (below) so
+			 * we don't enter an infinite getfile() loop on on
+			 * failures to find the files.  - FM
+			 */
+			set_address(&newdoc, homepage);
+			LYFreePostData(&newdoc);
+			FREE(newdoc.bookmark);
+			StrAllocCopy(startfile, homepage);
+			newdoc.isHEAD = FALSE;
+			newdoc.safe = FALSE;
+			newdoc.internal_link = FALSE;
+			goto try_again;
+		    } else {
+			exit_immediately_with_error_message(NULLFILE, first_file);
+			return (EXIT_FAILURE);
+		    }
+		}
+
+		/*
+		 * If we're going to pop from history because getfile didn't
+		 * succeed, reset LYforce_no_cache first.  This would have been
+		 * done in HTAccess.c if the request got that far, but the URL
+		 * may have been handled or rejected in getfile without taking
+		 * care of that.  - kw
+		 */
+		LYforce_no_cache = FALSE;
+		/*
+		 * Retrieval of a newdoc just failed, and just going to
+		 * try_again would pop the next doc from history and try to get
+		 * it without further questions.  This may not be the right
+		 * thing to do if we have POST data, so fake a PREV_DOC key if
+		 * it seems that some prompting should be done.  This doesn't
+		 * affect the traversal logic, since with traversal POST data
+		 * can never occur.  - kw
+		 */
+		if (HDOC(nhist - 1).post_data &&
+		    !HDOC(nhist - 1).safe) {
+		    if (HText_POSTReplyLoaded((DocInfo *) &history[(nhist_1)])) {
+			override_LYresubmit_posts = TRUE;
+			goto try_again;
+		    }
+		    /* Set newdoc fields, just in case the PREV_DOC gets
+		     * cancelled.  - kw
+		     */
+		    if (!curdoc.address) {
+			set_address(&newdoc, HTLoadedDocumentURL());
+			StrAllocCopy(newdoc.title, HTLoadedDocumentTitle());
+			if (HTMainAnchor
+			    && HTMainAnchor->post_data) {
+			    BStrCopy(newdoc.post_data,
+				     HTMainAnchor->post_data);
+			    StrAllocCopy(newdoc.post_content_type,
+					 HTMainAnchor->post_content_type);
+			} else {
+			    BStrFree(newdoc.post_data);
+			}
+			newdoc.isHEAD = HTLoadedDocumentIsHEAD();
+			newdoc.safe = HTLoadedDocumentIsSafe();
+			newdoc.internal_link = FALSE;
+		    } else {
+			copy_address(&newdoc, &curdoc);
+			StrAllocCopy(newdoc.title, curdoc.title);
+			BStrCopy(newdoc.post_data, curdoc.post_data);
+			StrAllocCopy(newdoc.post_content_type,
+				     curdoc.post_content_type);
+			newdoc.isHEAD = curdoc.isHEAD;
+			newdoc.safe = curdoc.safe;
+			newdoc.internal_link = curdoc.internal_link;
+			newdoc.line = curdoc.line;
+			newdoc.link = curdoc.link;
+		    }
+		    cmd = LYK_PREV_DOC;
+		    goto new_cmd;
+		}
+		override_LYresubmit_posts = TRUE;
+		goto try_again;
+
+	    case NORMAL:
+		/*
+		 * Marvelously, we got the document!
+		 */
+		LYoverride_no_cache = FALSE;	/* Was TRUE if popped. - FM */
+		LYinternal_flag = FALSE;	/* Reset to default. - kw */
+		turn_trace_back_on(&trace_mode_flag);
+
+		/*
+		 * If it's the first file and we're interactive, check whether
+		 * it's a bookmark file which was not accessed via the -book
+		 * switch.  - FM
+		 */
+		if (((first_file == TRUE) &&
+		     (dump_output_immediately == FALSE) &&
+		     isEmpty(newdoc.bookmark)) &&
+		    ((LYisLocalFile(newdoc.address) == TRUE) &&
+		     !(strcmp(NonNull(HText_getTitle()),
+			      BOOKMARK_TITLE))) &&
+		    (temp = HTParse(newdoc.address, "",
+				    PARSE_PATH + PARSE_PUNCTUATION)) != NULL) {
+		    const char *name = wwwName(Home_Dir());
+
+		    len = strlen(name);
+#ifdef VMS
+		    if (!strncasecomp(temp, name, len) &&
+			strlen(temp) > len)
+#else
+		    if (!strncmp(temp, name, len) &&
+			strlen(temp) > len)
+#endif /* VMS */
+		    {
+			/*
+			 * We're interactive and this might be a bookmark file
+			 * entered as a startfile rather than invoked via
+			 * -book.  Check if it's in our bookmark file list, and
+			 * if so, reload if with the relevant bookmark elements
+			 * set.  - FM
+			 */
+			cp = NULL;
+			if (temp[len] == '/') {
+			    if (strchr(&temp[(len + 1)], '/')) {
+				HTSprintf0(&cp, ".%s", &temp[len]);
+			    } else {
+				StrAllocCopy(cp, &temp[(len + 1)]);
+			    }
+			} else {
+			    StrAllocCopy(cp, &temp[len]);
+			}
+			for (i = 0; i <= MBM_V_MAXFILES; i++) {
+			    if (MBM_A_subbookmark[i] &&
+				LYSameFilename(cp, MBM_A_subbookmark[i])) {
+				StrAllocCopy(BookmarkPage,
+					     MBM_A_subbookmark[i]);
+				break;
+			    }
+			}
+			FREE(cp);
+			if (i <= MBM_V_MAXFILES) {
+			    FREE(temp);
+			    if (LYValidate) {
+				HTAlert(BOOKMARKS_DISABLED);
+				return (EXIT_FAILURE);
+			    }
+			    if ((temp = HTParse(newdoc.address, "",
+						PARSE_ACCESS + PARSE_HOST + PARSE_PUNCTUATION))) {
+				set_address(&newdoc, temp);
+				HTuncache_current_document();
+				free_address(&curdoc);
+				StrAllocCat(newdoc.address,
+					    wwwName(Home_Dir()));
+				StrAllocCat(newdoc.address, "/");
+				StrAllocCat(newdoc.address,
+					    (strncmp(BookmarkPage, "./", 2) ?
+					     BookmarkPage :
+					     (BookmarkPage + 2)));
+				StrAllocCopy(newdoc.title, BOOKMARK_TITLE);
+				StrAllocCopy(newdoc.bookmark, BookmarkPage);
+#ifdef USE_COLOR_STYLE
+				if (curdoc.style)
+				    StrAllocCopy(newdoc.style, curdoc.style);
+#endif
+				StrAllocCopy(startrealm, newdoc.address);
+				LYFreePostData(&newdoc);
+				newdoc.isHEAD = FALSE;
+				newdoc.safe = FALSE;
+				FREE(temp);
+				if (!strcmp(homepage, startfile))
+				    StrAllocCopy(homepage, newdoc.address);
+				StrAllocCopy(startfile, newdoc.address);
+				CTRACE((tfp, "Reloading as bookmarks=%s\n",
+					newdoc.address));
+				goto try_again;
+			    }
+			}
+		    }
+		    cp = NULL;
+		}
+		FREE(temp);
+
+		if (traversal) {
+		    /*
+		     * 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.
+			 */
+			if (!lookup_link(traversal_link_to_add))
+			    add_to_table(traversal_link_to_add);
+			FREE(traversal_link_to_add);
+		    }
+		    if (curdoc.address && curdoc.title &&
+			!isLYNXIMGMAP(curdoc.address))
+			/*
+			 * Add the address we got to TRAVERSE_FOUND_FILE.
+			 */
+			add_to_traverse_list(curdoc.address, curdoc.title);
+		}
+
+		/*
+		 * If this was a LYNXDOWNLOAD, we still have curdoc, not a
+		 * newdoc, so reset the address, title and positioning
+		 * elements.  - FM
+		 */
+		if (newdoc.address && curdoc.address &&
+		    isLYNXDOWNLOAD(newdoc.address)) {
+		    copy_address(&newdoc, &curdoc);
+		    StrAllocCopy(newdoc.title, NonNull(curdoc.title));
+		    StrAllocCopy(newdoc.bookmark, curdoc.bookmark);
+		    newdoc.line = curdoc.line;
+		    newdoc.link = curdoc.link;
+		    newdoc.internal_link = FALSE;	/* can't be true. - kw */
+		}
+
+		/*
+		 * Set Newline to the saved line.  It contains the line the
+		 * user was on if s/he has been in the file before, or it is 1
+		 * if this is a new file.
+		 *
+		 * We already set Newline before getfile() and probably update
+		 * it explicitly if popping from the history stack via LYpop()
+		 * or LYpop_num() within getfile() cycle.
+		 *
+		 * In partial mode, Newline was probably updated in
+		 * LYMainLoop_pageDisplay() if user scrolled the document while
+		 * loading.  Incremental loading stage already closed in
+		 * HT*Copy().
+		 */
+#ifdef DISP_PARTIAL
+		/* Newline = newdoc.line; */
+		display_partial = FALSE;	/* for sure, LYNXfoo:/ may be a problem */
+#else
+		/* Should not be needed either if we remove "DISP_PARTIAL" from
+		 * LYHistory.c, but lets leave it as an important comment for
+		 * now.
+		 */
+		/* Newline = newdoc.line; */
+#endif
+
+		/*
+		 * If we are going to a target line or the first page of a
+		 * popped document, override any www_search line result.
+		 */
+		if (LYGetNewline() > 1 || popped_doc == TRUE)
+		    www_search_result = -1;
+
+		/*
+		 * Make sure curdoc.line will not be equal to Newline, so we
+		 * get a redraw.
+		 */
+		curdoc.line = -1;
+		break;
+	    }			/* end switch */
+
+	    if (TRACE) {
+		if (!LYTraceLogFP || trace_mode_flag) {
+		    LYSleepAlert();	/* allow me to look at the results */
+		}
+	    }
+
+	    /*
+	     * Set the files the same.
+	     */
+	    copy_address(&curdoc, &newdoc);
+	    BStrCopy(curdoc.post_data, newdoc.post_data);
+	    StrAllocCopy(curdoc.post_content_type, newdoc.post_content_type);
+	    StrAllocCopy(curdoc.bookmark, newdoc.bookmark);
+#ifdef USE_COLOR_STYLE
+	    StrAllocCopy(curdoc.style, HText_getStyle());
+	    if (curdoc.style != NULL)
+		style_readFromFile(curdoc.style);
+#endif
+	    curdoc.isHEAD = newdoc.isHEAD;
+	    curdoc.internal_link = newdoc.internal_link;
+
+	    /*
+	     * Set the remaining document elements and add to the visited links
+	     * list.  - FM
+	     */
+	    if (ownerS_address != NULL) {
+#ifndef USE_PRETTYSRC
+		if (HTOutputFormat == WWW_SOURCE && !HText_getOwner())
+#else
+		if ((LYpsrc ? psrc_view : HTOutputFormat == WWW_SOURCE)
+		    && !HText_getOwner())
+#endif
+		    HText_setMainTextOwner(ownerS_address);
+		FREE(ownerS_address);
+	    }
+	    if (HText_getTitle()) {
+		StrAllocCopy(curdoc.title, HText_getTitle());
+	    } else if (!dump_output_immediately) {
+		StrAllocCopy(curdoc.title, newdoc.title);
+	    }
+	    StrAllocCopy(owner_address, HText_getOwner());
+	    curdoc.safe = HTLoadedDocumentIsSafe();
+	    if (!dump_output_immediately) {
+		LYAddVisitedLink(&curdoc);
+	    }
+
+	    /*
+	     * Reset WWW present mode so that if we were getting the source, we
+	     * get rendered HTML from now on.
+	     */
+	    HTOutputFormat = WWW_PRESENT;
+#ifdef USE_PRETTYSRC
+	    psrc_view = FALSE;
+#endif
+
+	    HTMLSetCharacterHandling(current_char_set);		/* restore, for sure? */
+
+	    /*
+	     * Reset all of the other relevant flags.  - FM
+	     */
+	    LYUserSpecifiedURL = FALSE;		/* only set for goto's and jumps's */
+	    LYJumpFileURL = FALSE;	/* only set for jump's */
+	    LYNoRefererForThis = FALSE;		/* always reset on return here */
+	    reloading = FALSE;	/* set for RELOAD and NOCACHE keys */
+	    HEAD_request = FALSE;	/* only set for HEAD requests */
+	    LYPermitURL = FALSE;	/* only for LYValidate or check_realm */
+	    ForcePush = FALSE;	/* only set for some PRINT requests. */
+	    LYforce_HTML_mode = FALSE;
+	    force_old_UCLYhndl_on_reload = FALSE;
+	    popped_doc = FALSE;
+	    pending_form_c = -1;
+
+	}
+	/* end if (LYforce_no_cache || force_load || are_different(...)) */
+	if (dump_output_immediately) {
+	    if (crawl) {
+		print_crawl_to_fd(stdout, curdoc.address, curdoc.title);
+	    } else if (!dump_links_only) {
+		print_wwwfile_to_fd(stdout, FALSE, FALSE);
+	    }
+	    return ((dump_server_status >= 400) ? EXIT_FAILURE : EXIT_SUCCESS);
+	}
+
+	/*
+	 * If the recent_sizechange variable is set to TRUE then the window
+	 * size changed recently.
+	 */
+	if (recent_sizechange) {
+	    /*
+	     * First we need to make sure the display library - curses, slang,
+	     * whatever - gets notified about the change, and gets a chance to
+	     * update external structures appropriately.  Hopefully the
+	     * stop_curses()/start_curses() sequence achieves this, at least if
+	     * the display library has a way to get the new screen size from
+	     * the OS.
+	     *
+	     * However, at least for ncurses, the update of the internal
+	     * structures will come still too late - the changed screen size is
+	     * detected in doupdate(), which would only be called (indirectly
+	     * through the HText_pageDisplay below) after the WINDOW structures
+	     * are already filled based on the old size.  So we notify the
+	     * ncurses library directly here.  - kw
+	     */
+#if defined(NCURSES) && defined(HAVE_RESIZETERM) && defined(HAVE_WRESIZE)
+	    resizeterm(LYlines, LYcols);
+	    wresize(LYwin, LYlines, LYcols);
+#else
+#if 0				/* defined(PDCURSES) && defined(HAVE_XCURSES) */
+	    resize_term(LYlines, LYcols);
+	    if (LYwin != 0)
+		LYwin = resize_window(LYwin, LYlines, LYcols);
+	    refresh();
+#else
+	    stop_curses();
+	    start_curses();
+	    LYclear();
+#endif
+#endif
+	    refresh_screen = TRUE;	/* to force a redraw */
+	    if (HTMainText)	/* to REALLY force it... - kw */
+		HText_setStale(HTMainText);
+	    recent_sizechange = FALSE;
+
+	    LYSetDisplayLines();
+	}
+
+	if (www_search_result != -1) {
+	    /*
+	     * This was a WWW search, set the line to the result of the search.
+	     */
+	    LYSetNewline(www_search_result);
+	    www_search_result = -1;	/* reset */
+	}
+
+	if (first_file == TRUE) {
+	    /*
+	     * We can never again have the first file.
+	     */
+	    first_file = FALSE;
+
+	    /*
+	     * Set the startrealm, and deal as best we can with preserving
+	     * forced HTML mode for a local startfile.  - FM
+	     */
+	    temp = HTParse(curdoc.address, "",
+			   PARSE_ACCESS + PARSE_HOST + PARSE_PUNCTUATION);
+	    if (isEmpty(temp)) {
+		StrAllocCopy(startrealm, NO_NOTHING);
+	    } else {
+		StrAllocCopy(startrealm, temp);
+		FREE(temp);
+		if (!(temp = HTParse(curdoc.address, "",
+				     PARSE_PATH + PARSE_PUNCTUATION))) {
+		    LYAddHtmlSep(&startrealm);
+		} else {
+		    if (forced_HTML_mode &&
+			!dump_output_immediately &&
+			!curdoc.bookmark &&
+			isFILE_URL(curdoc.address) &&
+			strlen(temp) > 1) {
+			/*
+			 * We forced HTML for a local startfile which is not a
+			 * bookmark file and has a path of at least two
+			 * letters.  If it doesn't have a suffix mapped to
+			 * text/html, we'll set the entire path (including the
+			 * lead slash) as a "suffix" mapped to text/html to
+			 * ensure it is always treated as an HTML source file. 
+			 * We are counting on a tail match to this full path
+			 * for some other URL fetched during the session having
+			 * too low a probability to worry about, but it could
+			 * happen.  - FM
+			 */
+			HTAtom *encoding;
+
+			if (HTFileFormat(temp, &encoding, NULL) != WWW_HTML) {
+			    HTSetSuffix(temp, "text/html", "8bit", 1.0);
+			}
+		    }
+		    if ((cp = strrchr(temp, '/')) != NULL) {
+			*(cp + 1) = '\0';
+			StrAllocCat(startrealm, temp);
+		    }
+		}
+	    }
+	    FREE(temp);
+	    CTRACE((tfp, "Starting realm is '%s'\n\n", startrealm));
+	    if (traversal) {
+		/*
+		 * Set up the crawl output stuff.
+		 */
+		if (curdoc.address && !lookup_link(curdoc.address)) {
+		    if (!isLYNXIMGMAP(curdoc.address))
+			crawl_ok = TRUE;
+		    add_to_table(curdoc.address);
+		}
+		/*
+		 * Set up the traversal_host comparison string.
+		 */
+		if (strncmp((curdoc.address ? curdoc.address : "NULL"),
+			    "http", 4)) {
+		    StrAllocCopy(traversal_host, NO_NOTHING);
+		} else if (check_realm) {
+		    StrAllocCopy(traversal_host, startrealm);
+		} else {
+		    temp = HTParse(curdoc.address, "",
+				   PARSE_ACCESS + PARSE_HOST + PARSE_PUNCTUATION);
+		    if (isEmpty(temp)) {
+			StrAllocCopy(traversal_host, NO_NOTHING);
+		    } else {
+			StrAllocCopy(traversal_host, temp);
+			LYAddHtmlSep(&traversal_host);
+		    }
+		    FREE(temp);
+		}
+		CTRACE((tfp, "Traversal host is '%s'\n\n", traversal_host));
+	    }
+	    if (startfile) {
+		/*
+		 * If homepage was not equated to startfile, make the homepage
+		 * URL the first goto entry.  - FM
+		 */
+		if (homepage && strcmp(startfile, homepage))
+		    HTAddGotoURL(homepage);
+		/*
+		 * If we are not starting up with startfile (e.g., had -book),
+		 * or if we are using the startfile and it has no POST content,
+		 * make the startfile URL a goto entry.  - FM
+		 */
+		if (strcmp(startfile, newdoc.address) ||
+		    newdoc.post_data == NULL)
+		    HTAddGotoURL(startfile);
+	    }
+	    if (TRACE) {
+		refresh_screen = TRUE;
+		if (!LYTraceLogFP || trace_mode_flag) {
+		    LYSleepAlert();
+		}
+	    }
+	}
+#ifdef USE_SOURCE_CACHE
+	/*
+	 * If the parse settings have changed since this HText was
+	 * generated, we need to reparse and redraw it.  -dsb
+	 *
+	 * Should be configured to avoid shock for experienced lynx users.
+	 * Currently enabled for cached sources only.
+	 */
+	if (HTdocument_settings_changed()) {
+	    if (HTcan_reparse_document()) {
+		HTInfoMsg(gettext("Reparsing document under current settings..."));
+		reparse_document();
+	    } else {
+		/*
+		 * Urk.  I have no idea how to recover from a failure here.
+		 * At a guess, I'll try reloading.  -dsb
+		 */
+		/*  currently disabled ***
+		   HTUserMsg(gettext("Reparsing document under current settings..."));
+		   cmd = LYK_RELOAD;
+		   goto new_cmd;
+		 */
+	    }
+	}
+
+	if (from_source_cache) {
+	    from_source_cache = FALSE;	/* reset */
+	    curdoc.line = -1;	/* so curdoc.line != Newline, see below */
+	}
+#endif
+
+	/*
+	 * If the curdoc.line is different than Newline then there must have
+	 * been a change since last update.  Run HText_pageDisplay() to create
+	 * a fresh screen of text output.
+	 *
+	 * If we got new HTMainText go this way.  All display_partial calls
+	 * ends here for final redraw.
+	 */
+	if (curdoc.line != LYGetNewline()) {
+#ifdef INACTIVE_INPUT_STYLE_VH
+	    textinput_redrawn = FALSE;
+#endif
+
+	    refresh_screen = FALSE;
+
+	    HText_pageDisplay(LYGetNewline(), prev_target);
+
+#ifdef DIRED_SUPPORT
+	    if (lynx_edit_mode && nlinks > 0 && !HTList_isEmpty(tagged))
+		showtags(tagged);
+#endif /* DIRED_SUPPORT */
+
+	    /*
+	     * Check if there is more info below this page.
+	     */
+	    more_text = HText_canScrollDown();
+
+	    if (newdoc.link < 0)
+		goto_line(LYGetNewline());
+	    LYSetNewline(HText_getTopOfScreen() + 1);
+	    curdoc.line = LYGetNewline();
+
+	    if (curdoc.title == NULL) {
+		/*
+		 * If we don't yet have a title, try to get it, or set to that
+		 * for newdoc.title.  - FM
+		 */
+		if (HText_getTitle()) {
+		    StrAllocCopy(curdoc.title, HText_getTitle());
+		} else {
+		    StrAllocCopy(curdoc.title, newdoc.title);
+		}
+	    }
+
+	    /*
+	     * If the request is to highlight a link which is counted from the
+	     * start of document, correct the link number:
+	     */
+	    if (newdoc_link_is_absolute) {
+		newdoc_link_is_absolute = FALSE;
+		if (curdoc.line > 1)
+		    newdoc.link -= HText_LinksInLines(HTMainText, 1,
+						      curdoc.line - 1);
+	    }
+
+	    if (arrowup) {
+		/*
+		 * arrowup is set if we just came up from a page below.
+		 */
+		curdoc.link = nlinks - 1;
+		arrowup = FALSE;
+	    } else {
+		curdoc.link = newdoc.link;
+		if (curdoc.link >= nlinks) {
+		    curdoc.link = nlinks - 1;
+		} else if (curdoc.link < 0 && nlinks > 0) {
+		    /*
+		     * We may have popped a doc (possibly in local_dired) which
+		     * didn't have any links when it was pushed, but does have
+		     * links now (e.g., a file was created).  Code below
+		     * assumes that curdoc.link is valid and that
+		     * (curdoc.link==-1) only occurs if (nlinks==0) is true.  -
+		     * KW
+		     */
+		    curdoc.link = 0;
+		}
+	    }
+
+	    show_help = FALSE;	/* reset */
+	    newdoc.line = 1;
+	    newdoc.link = 0;
+	    curdoc.line = LYGetNewline();	/* set */
+	} else if (newdoc.link < 0) {
+	    newdoc.link = 0;	/* ...just in case getfile set this */
+	}
+
+	/*
+	 * Refresh the screen if necessary.
+	 */
+	if (refresh_screen) {
+#if defined(FANCY_CURSES) || defined (USE_SLANG)
+	    if (enable_scrollback) {
+		LYclear();
+	    } else {
+		LYerase();
+	    }
+#else
+	    LYclear();
+#endif /* FANCY_CURSES || USE_SLANG */
+	    HText_pageDisplay(LYGetNewline(), prev_target);
+
+#ifdef DIRED_SUPPORT
+	    if (lynx_edit_mode && nlinks > 0 && !HTList_isEmpty(tagged))
+		showtags(tagged);
+#endif /* DIRED_SUPPORT */
+
+	    /*
+	     * Check if there is more info below this page.
+	     */
+	    more_text = HText_canScrollDown();
+
+	    /*
+	     * Adjust curdoc.link as above; nlinks may have changed, if the
+	     * refresh_screen flag was set as a result of a size change.  Code
+	     * below assumes that curdoc.link is valid and that
+	     * (curdoc.link==-1) only occurs if (nlinks==0) is true.  - kw
+	     */
+	    if (curdoc.link >= nlinks) {
+		curdoc.link = nlinks - 1;
+	    } else if (curdoc.link < 0 && nlinks > 0) {
+		curdoc.link = 0;
+	    }
+
+	    if (user_mode == NOVICE_MODE)
+		noviceline(more_text);	/* print help message */
+	    refresh_screen = FALSE;
+
+	}
+
+	curlink_is_editable = (BOOLEAN)
+	    (nlinks > 0 &&
+	     (links[curdoc.link].type == WWW_FORM_LINK_TYPE) &&
+	     F_TEXTLIKE(links[curdoc.link].l_form->type));
+
+	use_last_tfpos = (BOOLEAN)
+	    (curlink_is_editable &&
+	     (real_cmd == LYK_LPOS_PREV_LINK ||
+	      real_cmd == LYK_LPOS_NEXT_LINK));
+
+#ifdef TEXTFIELDS_MAY_NEED_ACTIVATION
+	if (!textfields_need_activation)
+	    textinput_activated = TRUE;
+#endif
+
+#if defined(WIN_EX)		/* 1997/10/08 (Wed) 14:52:06 */
+	if (nlinks > 0) {
+	    char *p = "LYNX (unknown link type)";
+
+	    /* Show the URL & kanji code . */
+	    if (strlen(links[curdoc.link].lname) == 0) {
+
+		if (links[curdoc.link].type == WWW_FORM_LINK_TYPE) {
+
+		    switch (links[curdoc.link].l_form->type) {
+		    case F_TEXT_SUBMIT_TYPE:
+		    case F_SUBMIT_TYPE:
+		    case F_IMAGE_SUBMIT_TYPE:
+			p = "[SUBMIT]";
+			break;
+		    case F_PASSWORD_TYPE:
+			p = "Password";
+			break;
+		    case F_OPTION_LIST_TYPE:
+			p = "Option list";
+			break;
+		    case F_CHECKBOX_TYPE:
+			p = "Check box";
+			break;
+		    case F_RADIO_TYPE:
+			p = "[Radio]";
+			break;
+		    case F_RESET_TYPE:
+			p = "[Reset]";
+			break;
+		    case F_TEXT_TYPE:
+			p = "Text input";
+			break;
+		    case F_TEXTAREA_TYPE:
+			p = "Text input lines";
+			break;
+		    default:
+			break;
+		    }
+		    set_ws_title(p);
+		}
+	    } else {
+		if (user_mode == ADVANCED_MODE) {
+		    p = curdoc.title;
+		} else {
+		    p = links[curdoc.link].lname;
+		}
+
+		if (strlen(p) < (sizeof(sjis_buff) / 10)) {
+		    strcpy(temp_buff, p);
+		    if (strchr(temp_buff, '%')) {
+			HTUnEscape(temp_buff);
+		    }
+		    str_sjis(sjis_buff, temp_buff);
+		    set_ws_title(LYElideString(sjis_buff, 10));
+		}
+	    }
+	} else {
+	    if (strlen(curdoc.address) < sizeof(temp_buff) - 1) {
+		if (user_mode == ADVANCED_MODE) {
+		    str_sjis(temp_buff, curdoc.title);
+		} else {
+		    strcpy(temp_buff, curdoc.address);
+		}
+		set_ws_title(HTUnEscape(temp_buff));
+	    }
+	}
+#endif /* WIN_EX */
+
+	/*
+	 * Report unread or new mail, if appropriate.
+	 */
+	if (check_mail && !no_mail)
+	    LYCheckMail();
+
+	/*
+	 * If help is not on the screen, then put a message on the screen to
+	 * tell the user other misc info.
+	 */
+	if (!show_help) {
+	    show_main_statusline(links[curdoc.link],
+				 (curlink_is_editable && textinput_activated) ?
+				 FOR_INPUT : FOR_PANEL);
+	} else {
+	    show_help = FALSE;
+	}
+
+	if (nlinks > 0) {
+	    /*
+	     * Highlight current link, unless it is an active text input field.
+	     */
+	    if (!curlink_is_editable) {
+		LYhighlight(ON, curdoc.link, prev_target);
+#ifndef INACTIVE_INPUT_STYLE_VH
+	    } else if (!textinput_activated) {
+		LYhighlight(ON, curdoc.link, prev_target);
+#endif
+	    }
+	}
+
+	if (traversal) {
+	    /*
+	     * Don't go interactively into forms, or accept keystrokes from the
+	     * user
+	     */
+	    if (crawl && crawl_ok) {
+		crawl_ok = FALSE;
+#ifdef FNAMES_8_3
+		sprintf(cfile, "lnk%05d.dat", crawl_count);
+#else
+		sprintf(cfile, "lnk%08d.dat", crawl_count);
+#endif /* FNAMES_8_3 */
+		crawl_count = crawl_count + 1;
+		if ((cfp = LYNewTxtFile(cfile)) != NULL) {
+		    print_crawl_to_fd(cfp, curdoc.address, curdoc.title);
+		    LYCloseOutput(cfp);
+		} else {
+		    if (!dump_output_immediately)
+			cleanup();
+		    fprintf(
+#ifdef UNIX
+			       (dump_output_immediately
+				? stderr
+				: stdout),
+#else
+			       stdout,
+#endif
+			       gettext("Fatal error - could not open output file %s\n"), cfile);
+		    if (!dump_output_immediately) {
+			exit_immediately(EXIT_FAILURE);
+		    }
+		    return (EXIT_FAILURE);
+		}
+	    }
+	} else {
+	    /*
+	     * Normal, non-traversal handling.
+	     */
+	    if (curlink_is_editable &&
+		(textinput_activated || pending_form_c != -1)) {
+		if (pending_form_c != -1) {
+		    real_c = pending_form_c;
+		    pending_form_c = -1;
+		} else {
+		    /*
+		     * Replace novice lines if in NOVICE_MODE.
+		     */
+		    if (user_mode == NOVICE_MODE) {
+			form_noviceline(links[curdoc.link].l_form->disabled);
+		    }
+		    real_c = change_form_link(curdoc.link,
+					      &newdoc, &refresh_screen,
+					      use_last_tfpos, FALSE);
+		}
+#ifdef TEXTFIELDS_MAY_NEED_ACTIVATION
+		if (textfields_need_activation)
+		    textinput_activated = FALSE;
+#ifdef INACTIVE_INPUT_STYLE_VH
+		textinput_redrawn = FALSE;
+#endif
+#endif
+
+		c = (real_c == LKC_DONE) ? DO_NOTHING : LKC_TO_C(real_c);
+		if (c != DO_NOTHING &&
+		    peek_mouse_link() != -1 && peek_mouse_link() != -2)
+		    old_c = 0;
+		if (peek_mouse_link() >= 0 &&
+		    LKC_TO_LAC(keymap, real_c) != LYK_CHANGE_LINK) {
+		    do_change_link();
+		    if ((c == '\n' || c == '\r') &&
+			links[curdoc.link].type == WWW_FORM_LINK_TYPE &&
+			F_TEXTLIKE(links[curdoc.link].l_form->type) &&
+			!textfields_need_activation) {
+			c = DO_NOTHING;
+		    }
+#ifdef TEXTFIELDS_MAY_NEED_ACTIVATION
+		} else if (LinkIsTextarea(curdoc.link)
+			   && textfields_need_activation
+			   && !links[curdoc.link].l_form->disabled
+			   && peek_mouse_link() < 0 &&
+			   (((LKC_TO_LAC(keymap, real_c) == LYK_NEXT_LINK ||
+#ifdef TEXTAREA_AUTOGROW
+			      LKC_TO_LAC(keymap, real_c) == LYK_ACTIVATE ||
+#endif
+			      LKC_TO_LAC(keymap, real_c) == LYK_LPOS_NEXT_LINK ||
+			      LKC_TO_LAC(keymap, real_c) == LYK_DOWN_LINK) &&
+			     ((curdoc.link < nlinks - 1 &&
+			       LinkIsTextarea(curdoc.link + 1)
+			       && (links[curdoc.link].l_form->number ==
+				   links[curdoc.link + 1].l_form->number)
+			       && strcmp(links[curdoc.link].l_form->name,
+					 links[curdoc.link + 1].l_form->name)
+			       == 0) ||
+			      (curdoc.link == nlinks - 1 && more_text &&
+			       HText_TAHasMoreLines(curdoc.link, 1)))) ||
+			    ((LKC_TO_LAC(keymap, real_c) == LYK_PREV_LINK ||
+			      LKC_TO_LAC(keymap, real_c) == LYK_LPOS_PREV_LINK ||
+			      LKC_TO_LAC(keymap, real_c) == LYK_UP_LINK) &&
+			     ((curdoc.link > 0 &&
+			       LinkIsTextarea(curdoc.link - 1)
+			       && (links[curdoc.link].l_form->number ==
+				   links[curdoc.link - 1].l_form->number) &&
+			       strcmp(links[curdoc.link].l_form->name,
+				      links[curdoc.link - 1].l_form->name) == 0)
+			      || (curdoc.link == 0 && curdoc.line > 1 &&
+				  HText_TAHasMoreLines(curdoc.link, -1)))))) {
+		    textinput_activated = TRUE;
+#ifdef TEXTAREA_AUTOGROW
+		    if ((c == '\n' || c == '\r') &&
+			LKC_TO_LAC(keymap, real_c) == LYK_ACTIVATE)
+			c = LAC_TO_LKC0(LYK_NEXT_LINK);
+#endif /* TEXTAREA_AUTOGROW */
+#endif /* TEXTFIELDS_MAY_NEED_ACTIVATION */
+		} else
+		    switch (c) {
+		    case '\n':
+		    case '\r':
+#ifdef TEXTAREA_AUTOGROW
+			/*
+			 * If on the bottom line of a TEXTAREA, and the user
+			 * hit the ENTER key, we add a new line/anchor
+			 * automatically, positioning the cursor on it.
+			 *
+			 * If at the bottom of the screen, we effectively
+			 * perform an LYK_DOWN_HALF-like operation, then move
+			 * down to the new line we just added.  --KED 02/14/99
+			 *
+			 * [There is some redundancy and non-standard
+			 * indentation in the monster-if() below.  This is
+			 * intentional ...  to try and improve the
+			 * "readability" (such as it is).  Caveat emptor to
+			 * anyone trying to change it.]
+			 */
+			if (LinkIsTextarea(curdoc.link)
+			    && ((curdoc.link == nlinks - 1 &&
+				 !(more_text &&
+				   HText_TAHasMoreLines(curdoc.link, 1)))
+				||
+				((curdoc.link < nlinks - 1) &&
+				 !LinkIsTextarea(curdoc.link + 1))
+				||
+				((curdoc.link < nlinks - 1) &&
+				 (LinkIsTextarea(curdoc.link + 1)
+				  && ((links[curdoc.link].l_form->number !=
+				       links[curdoc.link + 1].l_form->number) ||
+				      (strcmp(links[curdoc.link].l_form->name,
+					      links[curdoc.link + 1].l_form->name)
+				       != 0)))))) {
+
+			    HText_ExpandTextarea(&links[curdoc.link], 1);
+
+			    if (links[curdoc.link].ly < display_lines) {
+				refresh_screen = TRUE;
+			    } else {
+				LYChgNewline(display_lines / 2);
+				if (nlinks > 0 && curdoc.link > -1 &&
+				    links[curdoc.link].ly > display_lines / 2) {
+				    newdoc.link = curdoc.link;
+				    for (i = 0;
+					 links[i].ly <= (display_lines / 2);
+					 i++)
+					--newdoc.link;
+				    newdoc.link++;
+				}
+			    }
+#ifdef TEXTFIELDS_MAY_NEED_ACTIVATION
+			    if (textfields_need_activation) {
+				textinput_activated = TRUE;
+				textfields_need_activation = textfields_activation_option;
+#ifdef INACTIVE_INPUT_STYLE_VH
+				textinput_redrawn = TRUE;
+#endif
+			    };
+#endif
+
+			}
+#endif /* TEXTAREA_AUTOGROW */
+
+			/*
+			 * Make return in input field (if it was returned by
+			 * change_form_link) act as LYK_NEXT_LINK, independent
+			 * of what key (if any) is mapped to LYK_NEXT_LINK.  -
+			 * kw
+			 */
+			c = LAC_TO_LKC0(LYK_NEXT_LINK);
+			break;
+		    default:
+
+			if (old_c != c && old_c != real_c && c != real_c)
+			    real_c = c;
+		    }
+	    } else {
+#if defined(TEXTFIELDS_MAY_NEED_ACTIVATION) && defined(INACTIVE_INPUT_STYLE_VH)
+		if (curlink_is_editable && !textinput_redrawn) {
+		    /*draw the text entry, but don't activate it */
+		    textinput_redrawn = TRUE;
+		    change_form_link_ex(curdoc.link,
+					&newdoc, &refresh_screen,
+					use_last_tfpos, FALSE, TRUE);
+		    if (LYShowCursor) {
+			LYmove(links[curdoc.link].ly,
+			       ((links[curdoc.link].lx > 0) ?
+				(links[curdoc.link].lx - 1) : 0));
+		    } else {
+			LYHideCursor();
+		    }
+		}
+#endif /* TEXTFIELDS_MAY_NEED_ACTIVATION && INACTIVE_INPUT_STYLE_VH */
+		/*
+		 * Get a keystroke from the user.  Save the last keystroke to
+		 * avoid redundant error reporting.
+		 */
+		real_c = c = LYgetch();		/* get user input */
+
+		if (c != last_key)
+		    key_count = 0;
+		key_count++;
+		last_key = c;
+#ifndef VMS
+		if (c == 3) {	/* ^C */
+		    /*
+		     * This shouldn't happen.  We'll try to deal with whatever
+		     * bug caused it.  - FM
+		     */
+		    signal(SIGINT, cleanup_sig);
+		    old_c = 0;
+		    cmd = LYK_QUIT;
+		    goto new_cmd;
+		}
+#endif /* !VMS */
+		if (LKC_HAS_ESC_MOD(c) && EditBinding(c) != LYE_FORM_PASS) {
+		    /*
+		     * If ESC + <key> was read (and not recognized as a
+		     * terminal escape sequence for another key), ignore the
+		     * ESC modifier and act on <key> only if the line editor
+		     * binding would have passed the same ESC-modified
+		     * lynxkeycode back to us if it had been pressed in a text
+		     * input field.  Otherwise set interesting part so that it
+		     * will map to 0, to prevent that ESC + <key> acts like
+		     * <key>, which might be unexpected.  - kw
+		     */
+		    c = (c & ~LKC_MASK) | LAC_TO_LKC(0);
+		}
+		if (old_c != real_c) {
+		    old_c = 0;
+		}
+	    }
+	}
+
+#ifdef VMS
+	if (HadVMSInterrupt) {
+	    HadVMSInterrupt = FALSE;
+	    c = DO_NOTHING;
+	}
+#else
+	if (recent_sizechange) {
+	    if (c <= 0)
+		c = DO_NOTHING;
+	}
+#endif /* VMS */
+
+      new_keyboard_input:
+	/*
+	 * A goto point for new input without going back through the getch()
+	 * loop.
+	 */
+	if (traversal) {
+	    if ((c = DoTraversal(c, &crawl_ok)) < 0)
+		return (EXIT_FAILURE);
+	}
+	/* traversal */
+#ifdef WIN_EX
+	if (c == DO_NOTHING)
+	    cmd = LYK_DO_NOTHING;
+	else
+#endif
+	    cmd = LKC_TO_LAC(keymap, c);	/* adds 1 to map EOF to 0 */
+
+#if defined(DIRED_SUPPORT) && defined(OK_OVERRIDE)
+	if (lynx_edit_mode && !no_dired_support && LKC_TO_LAC(key_override, c))
+	    cmd = LKC_TO_LAC(key_override, c);
+#endif /* DIRED_SUPPORT && OK_OVERRIDE */
+
+	real_cmd = cmd;
+
+	/*
+	 * A goto point for new input without going back through the getch()
+	 * loop.
+	 */
+      new_cmd:
+
+	force_old_UCLYhndl_on_reload = FALSE;
+	CTRACE_FLUSH(tfp);
+
+	if (cmd != LYK_UP_LINK && cmd != LYK_DOWN_LINK)
+	    follow_col = -1;
+
+	switch (cmd) {
+	case -1:
+	    HTUserMsg(COMMAND_UNKNOWN);
+	    break;
+	case 0:		/* unmapped character */
+	default:
+	    if (curdoc.link >= 0 && curdoc.link < nlinks &&
+		links[curdoc.link].type == WWW_FORM_LINK_TYPE &&
+		F_TEXTLIKE(links[curdoc.link].l_form->type)) {
+
+#ifdef TEXTFIELDS_MAY_NEED_ACTIVATION
+		if (textfields_need_activation) {
+		    show_main_statusline(links[curdoc.link], FOR_PANEL);
+#ifdef INACTIVE_INPUT_STYLE_VH
+		    textinput_redrawn = FALSE;
+#endif
+		} else
+#endif
+		    show_main_statusline(links[curdoc.link], FOR_INPUT);
+	    } else if (more_text) {
+		HTInfoMsg(MOREHELP);
+	    } else {
+		HTInfoMsg(HELP);
+	    }
+	    show_help = TRUE;
+
+	    if (TRACE) {
+		sprintf(cfile, "%d", c);
+		LYaddstr(cfile);	/* show the user input */
+		cfile[0] = '\0';
+	    }
+	    break;
+
+	case LYK_COMMAND:
+	    cmd = handle_LYK_COMMAND(user_input_buffer);
+	    goto new_cmd;
+
+	case LYK_INTERRUPT:
+	    /*
+	     * No network transmission to interrupt - 'til we multithread.
+	     */
+	    break;
+
+	case LYK_F_LINK_NUM:
+	    c = '\0';
+	    /* FALLTHRU */
+	case LYK_1:		/* FALLTHRU */
+	case LYK_2:		/* FALLTHRU */
+	case LYK_3:		/* FALLTHRU */
+	case LYK_4:		/* FALLTHRU */
+	case LYK_5:		/* FALLTHRU */
+	case LYK_6:		/* FALLTHRU */
+	case LYK_7:		/* FALLTHRU */
+	case LYK_8:		/* FALLTHRU */
+	case LYK_9:
+	    handle_LYK_digit(c, &force_load, user_input_buffer,
+			     &old_c, real_c, &try_internal);
+	    break;
+
+	case LYK_SOURCE:	/* toggle view source mode */
+	    handle_LYK_SOURCE(&ownerS_address);
+	    break;
+
+	case LYK_CHG_CENTER:	/* ^Q */
+
+	    if (no_table_center) {
+		no_table_center = FALSE;
+		HTInfoMsg(gettext("TABLE center enable."));
+	    } else {
+		no_table_center = TRUE;
+		HTInfoMsg(gettext("TABLE center disable."));
+	    }
+	    /* FALLTHRU */
+
+	case LYK_RELOAD:	/* control-R to reload and refresh */
+	    handle_LYK_RELOAD(real_cmd);
+	    break;
+
+	case LYK_HISTORICAL:	/* toggle 'historical' comments parsing */
+	    handle_LYK_HISTORICAL();
+	    break;
+
+	case LYK_MINIMAL:	/* toggle 'minimal' comments parsing */
+	    handle_LYK_MINIMAL();
+	    break;
+
+	case LYK_SOFT_DQUOTES:
+	    handle_LYK_SOFT_DQUOTES();
+	    break;
+
+	case LYK_SWITCH_DTD:
+	    handle_LYK_SWITCH_DTD();
+	    break;
+
+	case LYK_QUIT:		/* quit */
+	    if (handle_LYK_QUIT())
+		return (EXIT_SUCCESS);
+	    break;
+
+	case LYK_ABORT:	/* don't ask the user about quitting */
+	    return (EXIT_SUCCESS);
+
+	case LYK_NEXT_PAGE:	/* next page */
+	    handle_LYK_NEXT_PAGE(&old_c, real_c);
+	    break;
+
+	case LYK_PREV_PAGE:	/* page up */
+	    handle_LYK_PREV_PAGE(&old_c, real_c);
+	    break;
+
+	case LYK_UP_TWO:
+	    handle_LYK_UP_TWO(&arrowup, &old_c, real_c);
+	    break;
+
+	case LYK_DOWN_TWO:
+	    handle_LYK_DOWN_TWO(&old_c, real_c);
+	    break;
+
+	case LYK_UP_HALF:
+	    handle_LYK_UP_HALF(&arrowup, &old_c, real_c);
+	    break;
+
+	case LYK_DOWN_HALF:
+	    handle_LYK_DOWN_HALF(&old_c, real_c);
+	    break;
+
+#ifdef CAN_CUT_AND_PASTE
+	case LYK_TO_CLIPBOARD:	/* ^S */
+	    {
+		char *s;
+		int ch2;
+
+		/* The logic resembles one of ADD_BOOKMARK */
+		if (nlinks > 0 && links[curdoc.link].lname
+		    && links[curdoc.link].type != WWW_FORM_LINK_TYPE) {
+		    /* Makes sense to copy a link */
+		    _statusline("Copy D)ocument's or L)ink's URL to clipboard or C)ancel?");
+		    ch2 = LYgetch_single();
+		    if (ch2 == 'D')
+			s = curdoc.address;
+		    else if (ch2 == 'C')
+			break;
+		    else
+			s = links[curdoc.link].lname;
+		} else
+		    s = curdoc.address;
+		if (isEmpty(s))
+		    HTInfoMsg(gettext("Current URL is empty."));
+		if (put_clip(s))
+		    HTInfoMsg(gettext("Copy to clipboard failed."));
+		else if (s == curdoc.address)
+		    HTInfoMsg(gettext("Document URL put to clipboard."));
+		else
+		    HTInfoMsg(gettext("Link URL put to clipboard."));
+	    }
+	    break;
+
+	case LYK_PASTE_URL:
+	    if (no_goto && !LYValidate) {	/*  Go to not allowed. - FM */
+		HTUserMsg(GOTO_DISALLOWED);
+	    } else {
+		unsigned char *s = (unsigned char *) get_clip_grab(), *e, *t;
+		char *buf;
+		int len2;
+
+		if (!s)
+		    break;
+		len2 = (int) strlen((const char *) s);
+		e = s + len2;
+		while (s < e && strchr(" \t\n\r", *s))
+		    s++;
+		while (s < e && strchr(" \t\n\r", e[-1]))
+		    e--;
+		if (s[0] == '<' && e > s && e[-1] == '>') {
+		    s++;
+		    e--;
+		    if (!strncasecomp((const char *) s, "URL:", 4))
+			s += 4;
+		}
+		if (s >= e) {
+		    HTInfoMsg(gettext("No URL in the clipboard."));
+		    break;
+		}
+		len = (unsigned) (e - s + 1);
+		if (len < MAX_LINE)
+		    len = MAX_LINE;	/* Required for do_check_goto_URL() */
+		buf = (char *) malloc(len);
+		strncpy(buf, (const char *) s, (unsigned) (e - s));
+		buf[e - s] = '\0';
+		t = (unsigned char *) buf;
+
+		while (s < e) {
+		    if (strchr(" \t\n\r", *s)) {
+			int nl2 = 0;	/* Keep whitespace without NL - file names! */
+			unsigned char *s1 = s;
+
+			while (strchr(" \t\n\r", *s)) {
+			    if (!nl2 && *s == '\n')
+				nl2 = 1;
+			    s++;
+			}
+			if (!nl2) {
+			    while (s1 < s) {
+				if (*s1 != '\r' && *s1 != '\r')
+				    *t = *s1;
+				t++, s1++;
+			    }
+			}
+		    } else
+			*t++ = *s++;
+		}
+		*t = '\0';
+		get_clip_release();
+		do_check_goto_URL(buf, &temp, &force_load);
+		free(buf);
+	    }
+	    break;
+#endif
+
+#ifdef KANJI_CODE_OVERRIDE
+	case LYK_CHG_KCODE:
+	    if (LYRawMode && (HTCJK == JAPANESE)) {
+		switch (last_kcode) {
+		case NOKANJI:
+		    last_kcode = SJIS;
+		    break;
+		case SJIS:
+		    last_kcode = EUC;
+		    break;
+		case EUC:
+		    last_kcode = NOKANJI;
+		    break;
+		default:
+		    break;
+		}
+	    }
+	    LYmove(0, 0);
+	    lynx_start_title_color();
+	    LYaddstr(str_kcode(last_kcode));
+	    lynx_stop_title_color();
+
+	    break;
+#endif
+
+	case LYK_REFRESH:
+	    refresh_screen = TRUE;
+	    lynx_force_repaint();
+	    break;
+
+	case LYK_HOME:
+	    if (curdoc.line > 1) {
+		LYSetNewline(1);
+	    } else {
+		cmd = LYK_PREV_PAGE;
+		goto new_cmd;
+	    }
+	    break;
+
+	case LYK_END:
+	    i = HText_getNumOfLines() - display_lines + 2;
+	    if (i >= 1 && LYGetNewline() != i) {
+		LYSetNewline(i);	/* go to end of file */
+		arrowup = TRUE;	/* position on last link */
+	    } else {
+		cmd = LYK_NEXT_PAGE;
+		goto new_cmd;
+	    }
+	    break;
+
+	case LYK_FIRST_LINK:
+	    handle_LYK_FIRST_LINK();
+	    break;
+
+	case LYK_LAST_LINK:
+	    handle_LYK_LAST_LINK();
+	    break;
+
+	case LYK_PREV_LINK:
+	case LYK_LPOS_PREV_LINK:
+	    handle_LYK_PREV_LINK(&arrowup, &old_c, real_c);
+	    break;
+
+	case LYK_NEXT_LINK:
+	case LYK_LPOS_NEXT_LINK:
+	    handle_LYK_NEXT_LINK(c, &old_c, real_c);
+	    break;
+
+	case LYK_FASTFORW_LINK:
+	    handle_LYK_FASTFORW_LINK(&old_c, real_c);
+	    break;
+
+	case LYK_FASTBACKW_LINK:
+	    if (handle_LYK_FASTBACKW_LINK(&cmd, &old_c, real_c))
+		goto new_cmd;
+	    break;
+
+	case LYK_UP_LINK:
+	    handle_LYK_UP_LINK(&follow_col, &arrowup, &old_c, real_c);
+	    break;
+
+	case LYK_DOWN_LINK:
+	    handle_LYK_DOWN_LINK(&follow_col, &old_c, real_c);
+	    break;
+
+	case LYK_CHANGE_LINK:
+	    do_change_link();
+#if defined(TEXTFIELDS_MAY_NEED_ACTIVATION) && defined(INACTIVE_INPUT_STYLE_VH)
+	    if (textfields_need_activation)
+		textinput_redrawn = FALSE;
+#endif /* TEXTFIELDS_MAY_NEED_ACTIVATION && INACTIVE_INPUT_STYLE_VH */
+	    break;
+
+	case LYK_RIGHT_LINK:
+	    handle_LYK_RIGHT_LINK();
+	    break;
+
+	case LYK_LEFT_LINK:
+	    handle_LYK_LEFT_LINK();
+	    break;
+
+	case LYK_COOKIE_JAR:	/* show the cookie jar */
+	    if (handle_LYK_COOKIE_JAR(&cmd))
+		goto new_cmd;
+	    break;
+
+#ifdef USE_CACHEJAR
+	case LYK_CACHE_JAR:	/* show the cache jar */
+	    if (handle_LYK_CACHE_JAR(&cmd))
+		goto new_cmd;
+	    break;
+#endif
+
+	case LYK_HISTORY:	/* show the history page */
+	    if (handle_LYK_HISTORY(ForcePush))
+		break;
+
+	    /* FALLTHRU */
+	case LYK_PREV_DOC:	/* back up a level */
+	    switch (handle_PREV_DOC(&cmd, &old_c, real_c)) {
+	    case 1:
+		return (EXIT_SUCCESS);
+	    case 2:
+		goto new_cmd;
+	    }
+	    break;
+
+	case LYK_NEXT_DOC:	/* undo back up a level */
+	    handle_NEXT_DOC();
+	    break;
+
+	case LYK_NOCACHE:	/* Force submission of form or link with no-cache */
+	    if (!handle_LYK_NOCACHE(&old_c, real_c))
+		break;
+
+	    /* FALLTHRU */
+	case LYK_ACTIVATE:	/* follow a link */
+	case LYK_SUBMIT:	/* follow a link, submit TEXT_SUBMIT input */
+	    switch (handle_LYK_ACTIVATE(&c,
+					cmd,
+					&try_internal,
+					&refresh_screen,
+					&force_load,
+					real_cmd)) {
+	    case 1:
+		continue;
+	    case 2:
+		goto new_keyboard_input;
+	    case 3:
+		pending_form_c = c;
+		break;
+	    }
+	    break;
+
+	case LYK_ELGOTO:	/* edit URL of current link and go to it  */
+	    if (handle_LYK_ELGOTO(&ch, user_input_buffer, &temp, &old_c, real_c))
+		do_check_goto_URL(user_input_buffer, &temp, &force_load);
+	    break;
+
+	case LYK_ECGOTO:	/* edit current URL and go to to it     */
+	    if (handle_LYK_ECGOTO(&ch, user_input_buffer, &temp, &old_c, real_c))
+		do_check_goto_URL(user_input_buffer, &temp, &force_load);
+	    break;
+
+	case LYK_GOTO:		/* 'g' to goto a random URL  */
+	    if (handle_LYK_GOTO(&ch, user_input_buffer, &temp, &recall,
+				&URLTotal, &URLNum, &FirstURLRecall, &old_c,
+				real_c)) {
+		if (do_check_recall(ch, user_input_buffer, &temp, URLTotal,
+				    &URLNum, recall, &FirstURLRecall))
+		    do_check_goto_URL(user_input_buffer, &temp, &force_load);
+	    }
+	    break;
+
+	case LYK_DWIMHELP:	/* show context-dependent help file */
+	    handle_LYK_DWIMHELP(&cshelpfile);
+	    /* FALLTHRU */
+
+	case LYK_HELP:		/* show help file */
+	    handle_LYK_HELP(&cshelpfile);
+	    break;
+
+	case LYK_INDEX:	/* index file */
+	    handle_LYK_INDEX(&old_c, real_c);
+	    break;
+
+	case LYK_MAIN_MENU:	/* return to main screen */
+	    handle_LYK_MAIN_MENU(&old_c, real_c);
+	    break;
+
+#ifdef EXP_NESTED_TABLES
+	case LYK_NESTED_TABLES:
+	    if (handle_LYK_NESTED_TABLES(&cmd))
+		goto new_cmd;
+	    break;
+#endif
+	case LYK_OPTIONS:	/* options screen */
+	    if (handle_LYK_OPTIONS(&cmd, &refresh_screen))
+		goto new_cmd;
+	    break;
+
+	case LYK_INDEX_SEARCH:	/* search for a user string */
+	    handle_LYK_INDEX_SEARCH(&force_load, ForcePush, &old_c, real_c);
+	    break;
+
+	case LYK_WHEREIS:	/* search within the document */
+	case LYK_NEXT:		/* find the next occurrence in the document */
+	case LYK_PREV:		/* find the previous occurrence in the document */
+	    handle_LYK_WHEREIS(cmd, &refresh_screen);
+	    break;
+
+	case LYK_COMMENT:	/* reply by mail */
+	    handle_LYK_COMMENT(&refresh_screen, &owner_address, &old_c, real_c);
+	    break;
+
+#ifdef DIRED_SUPPORT
+	case LYK_TAG_LINK:	/* tag or untag the current link */
+	    handle_LYK_TAG_LINK();
+	    break;
+
+	case LYK_MODIFY:	/* rename a file or directory */
+	    handle_LYK_MODIFY(&refresh_screen);
+	    break;
+
+	case LYK_CREATE:	/* create a new file or directory */
+	    handle_LYK_CREATE();
+	    break;
+#endif /* DIRED_SUPPORT */
+
+	case LYK_DWIMEDIT:	/* context-dependent edit */
+	    switch (handle_LYK_DWIMEDIT(&cmd, &old_c, real_c)) {
+	    case 1:
+		continue;
+	    case 2:
+		goto new_cmd;
+	    }
+	    /* FALLTHRU */
+
+	case LYK_EDIT:		/* edit */
+	    handle_LYK_EDIT(&old_c, real_c);
+	    break;
+
+	case LYK_DEL_BOOKMARK:	/* remove a bookmark file link */
+	    handle_LYK_DEL_BOOKMARK(&refresh_screen, &old_c, real_c);
+	    break;
+
+#ifdef DIRED_SUPPORT
+	case LYK_REMOVE:	/* remove files and directories */
+	    handle_LYK_REMOVE(&refresh_screen);
+	    break;
+#endif /* DIRED_SUPPORT */
+
+#if defined(DIRED_SUPPORT) && defined(OK_INSTALL)
+	case LYK_INSTALL:	/* install a file into system area */
+	    handle_LYK_INSTALL();
+	    break;
+#endif /* DIRED_SUPPORT && OK_INSTALL */
+
+	case LYK_INFO:		/* show document info */
+	    if (handle_LYK_INFO(&cmd))
+		goto new_cmd;
+	    break;
+
+	case LYK_EDIT_TEXTAREA:	/* use external editor on a TEXTAREA - KED */
+	    handle_LYK_EDIT_TEXTAREA(&refresh_screen, &old_c, real_c);
+	    break;
+
+	case LYK_GROW_TEXTAREA:	/* add new lines to bottom of TEXTAREA - KED */
+	    handle_LYK_GROW_TEXTAREA(&refresh_screen);
+	    break;
+
+	case LYK_INSERT_FILE:	/* insert file in TEXTAREA, above cursor - KED */
+	    handle_LYK_INSERT_FILE(&refresh_screen, &old_c, real_c);
+	    break;
+
+	case LYK_PRINT:	/* print the file */
+	    handle_LYK_PRINT(&ForcePush, &old_c, real_c);
+	    break;
+
+	case LYK_LIST:		/* list links in the current document */
+	    if (handle_LYK_LIST(&cmd))
+		goto new_cmd;
+	    break;
+
+#ifdef EXP_ADDRLIST_PAGE
+	case LYK_ADDRLIST:	/* always list URL's (only) */
+	    if (handle_LYK_ADDRLIST(&cmd))
+		goto new_cmd;
+	    break;
+#endif /* EXP_ADDRLIST_PAGE */
+
+	case LYK_VLINKS:	/* list links visited during the current session */
+	    if (handle_LYK_VLINKS(&cmd, &newdoc_link_is_absolute))
+		goto new_cmd;
+	    break;
+
+	case LYK_TOOLBAR:	/* go to Toolbar or Banner in current document */
+	    handle_LYK_TOOLBAR(&try_internal, &force_load, &old_c, real_c);
+	    break;
+
+#if defined(DIRED_SUPPORT) || defined(VMS)
+	case LYK_DIRED_MENU:	/* provide full file management menu */
+	    handle_LYK_DIRED_MENU(&refresh_screen, &old_c, real_c);
+	    break;
+#endif /* DIRED_SUPPORT || VMS */
+
+#ifdef USE_EXTERNALS
+	case LYK_EXTERN_LINK:	/* use external program on url */
+	    handle_LYK_EXTERN_LINK(&refresh_screen);
+	    break;
+	case LYK_EXTERN_PAGE:	/* use external program on current page */
+	    handle_LYK_EXTERN_PAGE(&refresh_screen);
+	    break;
+#endif /* USE_EXTERNALS */
+
+	case LYK_ADD_BOOKMARK:	/* add link to bookmark file */
+	    handle_LYK_ADD_BOOKMARK(&refresh_screen, &old_c, real_c);
+	    break;
+
+	case LYK_VIEW_BOOKMARK:	/* v to view home page */
+	    handle_LYK_VIEW_BOOKMARK(&refresh_screen, &old_c, real_c);
+	    break;
+
+	case LYK_SHELL:	/* (!) shell escape */
+	    handle_LYK_SHELL(&refresh_screen, &old_c, real_c);
+	    break;
+
+	case LYK_DOWNLOAD:
+	    switch (handle_LYK_DOWNLOAD(&cmd, &old_c, real_c)) {
+	    case 1:
+		continue;
+	    case 2:
+		goto new_cmd;
+	    }
+	    break;
+
+#ifdef DIRED_SUPPORT
+	case LYK_UPLOAD:
+	    handle_LYK_UPLOAD();
+	    break;
+#endif /* DIRED_SUPPORT */
+
+	case LYK_TRACE_TOGGLE:	/*  Toggle TRACE mode. */
+	    handle_LYK_TRACE_TOGGLE();
+	    break;
+
+	case LYK_TRACE_LOG:	/*  View TRACE log. */
+	    handle_LYK_TRACE_LOG(&trace_mode_flag);
+	    break;
+
+	case LYK_IMAGE_TOGGLE:
+	    if (handle_LYK_IMAGE_TOGGLE(&cmd))
+		goto new_cmd;
+	    break;
+
+	case LYK_INLINE_TOGGLE:
+	    if (handle_LYK_INLINE_TOGGLE(&cmd))
+		goto new_cmd;
+	    break;
+
+	case LYK_RAW_TOGGLE:
+	    if (handle_LYK_RAW_TOGGLE(&cmd))
+		goto new_cmd;
+	    break;
+
+	case LYK_HEAD:
+	    if (handle_LYK_HEAD(&cmd))
+		goto new_cmd;
+	    break;
+
+	case LYK_TOGGLE_HELP:
+	    handle_LYK_TOGGLE_HELP();
+	    break;
+
+	case LYK_KEYMAP:
+	    handle_LYK_KEYMAP(&vi_keys_flag, &emacs_keys_flag, &old_c, real_c);
+	    break;
+
+	case LYK_JUMP:
+	    if (handle_LYK_JUMP(c, user_input_buffer, &temp, &recall,
+				&FirstURLRecall, &URLNum, &URLTotal, &ch,
+				&old_c, real_c)) {
+		if (do_check_recall(ch, user_input_buffer, &temp, URLTotal,
+				    &URLNum, recall, &FirstURLRecall))
+		    do_check_goto_URL(user_input_buffer, &temp, &force_load);
+	    }
+	    break;
+
+	case LYK_CLEAR_AUTH:
+	    handle_LYK_CLEAR_AUTH(&old_c, real_c);
+	    break;
+
+	case LYK_DO_NOTHING:	/* pretty self explanatory */
+	    break;
+#ifdef SUPPORT_CHDIR
+	case LYK_CHDIR:
+	    handle_LYK_CHDIR();
+	    break;
+#endif
+#ifdef USE_CURSES_PADS
+	case LYK_SHIFT_LEFT:
+	    handle_LYK_SHIFT_LEFT(&refresh_screen, key_count);
+	    break;
+	case LYK_SHIFT_RIGHT:
+	    handle_LYK_SHIFT_RIGHT(&refresh_screen, key_count);
+	    break;
+	case LYK_LINEWRAP_TOGGLE:
+	    if (handle_LYK_LINEWRAP_TOGGLE(&cmd, &refresh_screen))
+		goto new_cmd;
+	    break;
+#endif
+	}			/* end of BIG switch */
+    }
+}
+
+static int are_different(DocInfo *doc1, DocInfo *doc2)
+{
+    char *cp1, *cp2;
+
+    /*
+     * Do we have two addresses?
+     */
+    if (!doc1->address || !doc2->address)
+	return (TRUE);
+
+    /*
+     * Do they differ in the type of request?
+     */
+    if (doc1->isHEAD != doc2->isHEAD)
+	return (TRUE);
+
+    /*
+     * See if the addresses are different, making sure we're not tripped up by
+     * multiple anchors in the the same document from a POST form.  -- FM
+     */
+    cp1 = trimPoundSelector(doc1->address);
+    cp2 = trimPoundSelector(doc2->address);
+    /*
+     * Are the base addresses different?
+     */
+    if (strcmp(doc1->address, doc2->address)) {
+	restorePoundSelector(cp1);
+	restorePoundSelector(cp2);
+	return (TRUE);
+    }
+    restorePoundSelector(cp1);
+    restorePoundSelector(cp2);
+
+    /*
+     * Do the docs have different contents?
+     */
+    if (doc1->post_data) {
+	if (doc2->post_data) {
+	    if (!BINEQ(doc1->post_data, doc2->post_data))
+		return (TRUE);
+	} else
+	    return (TRUE);
+    } else if (doc2->post_data)
+	return (TRUE);
+
+    /*
+     * We'll assume the two documents in fact are the same.
+     */
+    return (FALSE);
+}
+
+/* This determines whether two docs are _physically_ different,
+ * meaning they are "from different files". - kw
+ */
+#ifndef DONT_TRACK_INTERNAL_LINKS
+static int are_phys_different(DocInfo *doc1, DocInfo *doc2)
+{
+    char *cp1, *cp2, *ap1 = doc1->address, *ap2 = doc2->address;
+
+    /*
+     * Do we have two addresses?
+     */
+    if (!doc1->address || !doc2->address)
+	return (TRUE);
+
+    /*
+     * Do they differ in the type of request?
+     */
+    if (doc1->isHEAD != doc2->isHEAD)
+	return (TRUE);
+
+    /*
+     * Skip over possible LYNXIMGMAP parts. - kw
+     */
+    if (isLYNXIMGMAP(doc1->address))
+	ap1 += LEN_LYNXIMGMAP;
+    if (isLYNXIMGMAP(doc2->address))
+	ap2 += LEN_LYNXIMGMAP;
+    /*
+     * If there isn't any real URL in doc2->address, but maybe just
+     * a fragment, doc2 is assumed to be an internal reference in
+     * the same physical document, so return FALSE. - kw
+     */
+    if (*ap2 == '\0' || *ap2 == '#')
+	return (FALSE);
+
+    /*
+     * See if the addresses are different, making sure we're not tripped up by
+     * multiple anchors in the the same document from a POST form.  -- FM
+     */
+    cp1 = trimPoundSelector(doc1->address);
+    cp2 = trimPoundSelector(doc2->address);
+    /*
+     * Are the base addresses different?
+     */
+    if (strcmp(ap1, ap2)) {
+	restorePoundSelector(cp1);
+	restorePoundSelector(cp2);
+	return (TRUE);
+    }
+    restorePoundSelector(cp1);
+    restorePoundSelector(cp2);
+
+    /*
+     * Do the docs have different contents?
+     */
+    if (doc1->post_data) {
+	if (doc2->post_data) {
+	    if (!BINEQ(doc1->post_data, doc2->post_data))
+		return (TRUE);
+	} else
+	    return (TRUE);
+    } else if (doc2->post_data)
+	return (TRUE);
+
+    /*
+     * We'll assume the two documents in fact are the same.
+     */
+    return (FALSE);
+}
+#endif
+
+/*
+ * Utility for freeing the list of goto URLs.  - FM
+ */
+#ifdef LY_FIND_LEAKS
+static void HTGotoURLs_free(void)
+{
+    LYFreeStringList(Goto_URLs);
+    Goto_URLs = NULL;
+}
+#endif
+
+/*
+ * Utility for listing Goto URLs, making any repeated URLs the most current in
+ * the list.  - FM
+ */
+void HTAddGotoURL(char *url)
+{
+    char *copy = NULL;
+    char *old;
+    HTList *cur;
+
+    if (isEmpty(url))
+	return;
+
+    CTRACE((tfp, "HTAddGotoURL %s\n", url));
+    StrAllocCopy(copy, url);
+
+    if (!Goto_URLs) {
+	Goto_URLs = HTList_new();
+#ifdef LY_FIND_LEAKS
+	atexit(HTGotoURLs_free);
+#endif
+	HTList_addObject(Goto_URLs, copy);
+	return;
+    }
+
+    cur = Goto_URLs;
+    while (NULL != (old = (char *) HTList_nextObject(cur))) {
+	if (!strcmp(old, copy)) {
+	    HTList_removeObject(Goto_URLs, old);
+	    FREE(old);
+	    break;
+	}
+    }
+    HTList_addObject(Goto_URLs, copy);
+
+    return;
+}
+
+/*
+ * When help is not on the screen, put a message on the screen to tell the user
+ * other misc info.
+ */
+static void show_main_statusline(const LinkInfo curlink,
+				 int for_what)
+{
+    /*
+     * Make sure form novice lines are replaced.
+     */
+    if (user_mode == NOVICE_MODE && for_what != FOR_INPUT) {
+	noviceline(more_text);
+    }
+
+    if (HTisDocumentSource()) {
+	/*
+	 * Currently displaying HTML source.
+	 */
+	_statusline(SOURCE_HELP);
+
+	/*
+	 * If we are in forms mode then explicitly tell the user what each kind
+	 * of link is.
+	 */
+#ifdef INDICATE_FORMS_MODE_FOR_ALL_LINKS_ON_PAGE
+    } else if (lynx_mode == FORMS_LYNX_MODE && nlinks > 0) {
+#else
+#ifdef NORMAL_NON_FORM_LINK_STATUSLINES_FOR_ALL_USER_MODES
+    } else if (lynx_mode == FORMS_LYNX_MODE && nlinks > 0 &&
+	       !(curlink.type & WWW_LINK_TYPE)) {
+#else
+    } else if (lynx_mode == FORMS_LYNX_MODE && nlinks > 0 &&
+	       !(user_mode == ADVANCED_MODE &&
+		 (curlink.type & WWW_LINK_TYPE))) {
+#endif /* NORMAL_NON_FORM_LINK_STATUSLINES_FOR_ALL_USER_MODES */
+#endif /* INDICATE_FORMS_MODE_FOR_ALL_LINKS_ON_PAGE */
+	if (curlink.type == WWW_FORM_LINK_TYPE) {
+	    show_formlink_statusline(curlink.l_form, for_what);
+	} else {
+	    statusline(NORMAL_LINK_MESSAGE);
+	}
+
+	/*
+	 * Let them know if it's an index -- very rare.
+	 */
+	if (is_www_index) {
+	    const char *indx = gettext("-index-");
+
+	    LYmove(LYlines - 1, LYcolLimit - (int) strlen(indx));
+	    lynx_start_reverse();
+	    LYaddstr(indx);
+	    lynx_stop_reverse();
+	}
+
+    } else if (user_mode == ADVANCED_MODE && nlinks > 0) {
+	/*
+	 * Show the URL or, for some internal links, the fragment
+	 */
+	char *cp = NULL;
+
+	if (curlink.type == WWW_INTERN_LINK_TYPE &&
+	    !isLYNXIMGMAP(curlink.lname)) {
+	    cp = findPoundSelector(curlink.lname);
+	}
+	if (!cp)
+	    cp = curlink.lname;
+	status_link(cp, more_text, is_www_index);
+    } else if (is_www_index && more_text) {
+	char buf[128];
+
+	sprintf(buf, WWW_INDEX_MORE_MESSAGE, key_for_func(LYK_INDEX_SEARCH));
+	_statusline(buf);
+    } else if (is_www_index) {
+	char buf[128];
+
+	sprintf(buf, WWW_INDEX_MESSAGE, key_for_func(LYK_INDEX_SEARCH));
+	_statusline(buf);
+    } else if (more_text) {
+	if (user_mode == NOVICE_MODE)
+	    _statusline(MORE);
+	else
+	    _statusline(MOREHELP);
+    } else {
+	_statusline(HELP);
+    }
+
+    /* turn off cursor since now it's probably on statusline -HV */
+    /* But not if LYShowCursor is on.  -show_cursor may be used as a
+     * workaround to avoid putting the cursor in the last position, for
+     * curses implementations or terminals that cannot deal with that
+     * correctly. - kw */
+    if (!LYShowCursor) {
+	LYHideCursor();
+    }
+}
+
+/*
+ * Public function for redrawing the statusline appropriate for the selected
+ * link.  It should only be called at times when curdoc.link, nlinks, and the
+ * links[] array are valid.  - kw
+ */
+void repaint_main_statusline(int for_what)
+{
+    if (curdoc.link >= 0 && curdoc.link < nlinks)
+	show_main_statusline(links[curdoc.link], for_what);
+}
+
+static void form_noviceline(int disabled)
+{
+    LYmove(LYlines - 2, 0);
+    LYclrtoeol();
+    if (!disabled) {
+	LYaddstr(FORM_NOVICELINE_ONE);
+    }
+    LYmove(LYlines - 1, 0);
+    LYclrtoeol();
+    if (disabled)
+	return;
+    if (EditBinding(FROMASCII('\025')) == LYE_ERASE) {
+	LYaddstr(FORM_NOVICELINE_TWO);
+    } else if (EditBinding(FROMASCII('\025')) == LYE_DELBL) {
+	LYaddstr(FORM_NOVICELINE_TWO_DELBL);
+    } else {
+	char *temp = NULL;
+	char *erasekey = fmt_keys(LYKeyForEditAction(LYE_ERASE), -1);
+
+	if (erasekey) {
+	    HTSprintf0(&temp, FORM_NOVICELINE_TWO_VAR, erasekey);
+	} else {
+	    erasekey = fmt_keys(LYKeyForEditAction(LYE_DELBL), -1);
+	    if (erasekey)
+		HTSprintf0(&temp,
+			   FORM_NOVICELINE_TWO_DELBL_VAR, erasekey);
+	}
+	if (temp) {
+	    LYaddstr(temp);
+	    FREE(temp);
+	}
+	FREE(erasekey);
+    }
+}
+
+static void exit_immediately_with_error_message(int state,
+						BOOLEAN first_file)
+{
+    char *buf = 0;
+    char *buf2 = 0;
+
+    if (first_file) {
+	/* print statusline messages as a hint, if any */
+	LYstatusline_messages_on_exit(&buf2);
+    }
+
+    if (state == NOT_FOUND) {
+	HTSprintf0(&buf, "%s\n%s %s\n",
+		   NonNull(buf2),
+		   gettext("lynx: Can't access startfile"),
+	/*
+	 * hack: if we fail in HTAccess.c
+	 * avoid duplicating URL, oh.
+	 */
+		   (buf2 && strstr(buf2, gettext("Can't Access"))) ?
+		   "" : startfile);
+    }
+
+    if (state == NULLFILE) {
+	HTSprintf0(&buf, "%s\n%s\n%s\n",
+		   NonNull(buf2),
+		   gettext("lynx: Start file could not be found or is not text/html or text/plain"),
+		   gettext("      Exiting..."));
+    }
+
+    FREE(buf2);
+
+    if (!dump_output_immediately)
+	cleanup();
+
+#ifdef UNIX
+    if (dump_output_immediately) {
+	fputs(buf, stderr);
+    } else
+#endif /* UNIX */
+    {
+	SetOutputMode(O_TEXT);
+	fputs(buf, stdout);
+	SetOutputMode(O_BINARY);
+    }
+
+    FREE(buf);
+
+    if (!dump_output_immediately) {
+	exit_immediately(EXIT_FAILURE);
+    }
+    /* else: return(EXIT_FAILURE) in mainloop */
+}
+
+static void status_link(char *curlink_name,
+			BOOLEAN show_more,
+			BOOLEAN show_indx)
+{
+#define MAX_STATUS (LYcolLimit - 1)
+#define MIN_STATUS 0
+    char format[MAX_LINE];
+    int prefix = 0;
+    int length;
+
+    *format = 0;
+    if (show_more && !nomore) {
+	sprintf(format, "%.*s ",
+		(int) (sizeof(format) - 2),
+		gettext("-more-"));
+	prefix = (int) strlen(format);
+    }
+    if (show_indx) {
+	sprintf(format + prefix, "%.*s ",
+		((int) sizeof(format) - prefix - 2),
+		gettext("-index-"));
+    }
+    prefix = (int) strlen(format);
+    length = (int) strlen(curlink_name);
+
+    if (prefix > MAX_STATUS || prefix >= MAX_LINE - 1) {
+	_user_message("%s", format);	/* no room for url */
+    } else {
+	sprintf(format + prefix, "%%.%ds", MAX_STATUS - prefix);
+
+	if ((length + prefix > MAX_STATUS) && long_url_ok) {
+	    char *buf = NULL;
+	    int cut_from_pos;
+	    int cut_to_pos;
+	    int n;
+
+	    StrAllocCopy(buf, curlink_name);
+	    /*
+	     * Scan to find the final leaf of the URL.  Ignore trailing '/'.
+	     */
+	    for (cut_to_pos = length - 2;
+		 (cut_to_pos > 0) && (buf[cut_to_pos] != '/');
+		 cut_to_pos--) ;
+	    /*
+	     * Jump back to the next leaf to remove.
+	     */
+	    for (cut_from_pos = cut_to_pos - 4;
+		 (cut_from_pos > 0) && ((buf[cut_from_pos] != '/')
+					|| ((prefix + cut_from_pos
+					     + 4
+					     + (length - cut_to_pos)) >= MAX_STATUS));
+		 cut_from_pos--) ;
+	    /*
+	     * Replace some leaves to '...', if possible, and put the final
+	     * leaf at the end.  We assume that one can recognize the link from
+	     * at least MIN_STATUS characters.
+	     */
+	    if (cut_from_pos > MIN_STATUS) {
+		for (n = 1; n <= 3; n++)
+		    buf[cut_from_pos + n] = '.';
+		for (n = 0; cut_to_pos + n <= length; n++)
+		    buf[cut_from_pos + 4 + n] = buf[cut_to_pos + n];
+	    }
+	    _user_message(format, buf);
+	    CTRACE((tfp, "lastline = %s\n", buf));	/* don't forget to erase me */
+	    FREE(buf);
+	} else {		/* show (possibly truncated) url */
+	    _user_message(format, curlink_name);
+	}
+    }
+}
+
+const char *LYDownLoadAddress(void)
+{
+    return NonNull(newdoc.address);
+}