#include "HTUtils.h"
#include "tcp.h"
#include "LYCurses.h"
#include "HTAccess.h"
#include "HTParse.h"
#include "GridText.h"
#include "LYGlobalDefs.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 "LYSystem.h"
#include "LYKeymap.h"
#include "LYJump.h"
#include "LYDownload.h"
#include "LYList.h"
#include "LYMap.h"
#include "LYTraversal.h"
#include "LYCharSets.h"
#include "HTFTP.h"
#include "HTTP.h"
#include "HTAlert.h"

#ifdef VMS
#include "HTVMSUtils.h"
#endif /* VMS */

#ifdef DIRED_SUPPORT
#include "LYLocal.h"
#include "LYUpload.h"
#endif /* DIRED_SUPPORT */

#include "LYexit.h"
#include "LYLeaks.h"

extern BOOL reloading;		/* For Flushing Cache on Proxy Server */
extern BOOL HTJapanese;		/* For Japanese character translations */

PRIVATE int are_different PARAMS((document *doc1, document *doc2));
PUBLIC void HTGotoURLs_free NOPARAMS;
PUBLIC void HTAddGotoURL PARAMS((char *url));

#define FASTTAB
#ifdef FASTTAB
PRIVATE int sametext ARGS2(char *,een, char *,twee) {
    if (een && twee)
        return (strcmp(een, twee) == 0);
    return TRUE;
}
#endif /* FASTTAB */

#define FREE(x) if (x) {free(x); x = NULL;}

PUBLIC  HTList * Goto_URLs = NULL;  /* List of Goto URLs */

PUBLIC char * LYRequestTitle = NULL; /* newdoc.title in calls to getfile() */

/*
 * 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 alot of variable
 * manipulation.  This need some work to make it neater.
 */

int mainloop NOARGS
{
    int  c=0, real_c=0, old_c=0, cmd, arrowup=FALSE, show_help=FALSE;
    int lines_in_file= -1;
    int newline=0;
    char prev_target[512];
    char user_input_buffer[1024];
    char *owner_address=NULL;  /* holds the responsible owner's address */
    document newdoc, curdoc;
    BOOLEAN first_file=TRUE;
    BOOLEAN refresh_screen=FALSE;
    BOOLEAN force_load = FALSE;
    BOOLEAN crawl_ok = FALSE;
    BOOLEAN rlink_exists;
    BOOLEAN rlink_allowed;
    BOOLEAN vi_keys_flag = vi_keys;
    BOOLEAN emacs_keys_flag = emacs_keys;
    BOOLEAN keypad_mode_flag = keypad_mode;
    BOOLEAN HTfileSortMethod_flag = HTfileSortMethod;
    int CurrentCharSet_flag = current_char_set;
    BOOLEAN show_dotfiles_flag = show_dotfiles;
    char cfile[128];
    FILE *cfp;
    char *traversal_host = NULL;
    char *traversal_link_to_add = NULL;
    char *cp, *toolbar;
#ifdef VMS
    extern BOOLEAN HadVMSInterrupt;   /* Flag from cleanup_sig */
#endif /* VMS */
    int ch, recall;
    int URLTotal;
    int URLNum;
    BOOLEAN FirstURLRecall = TRUE;
    char *temp = NULL;

#ifdef DIRED_SUPPORT
    char *tp;
    char tmpbuf[1024];
    struct stat dir_info;
    taglink *t1, *t2=NULL;
#endif /* DIRED_SUPPORT */

/*
 *  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:
    /* initalize some variables*/
    nhist = 0;
    newdoc.address=0;
    curdoc.address=0;
    StrAllocCopy(newdoc.address, startfile);
    StrAllocCopy(startrealm, startfile);
    newdoc.title=0;
    curdoc.title=0;
    StrAllocCopy(newdoc.title, "Entry into main screen");
    newdoc.post_data=0;
    curdoc.post_data=0;
    newdoc.post_content_type=0;
    curdoc.post_content_type=0;
    newdoc.isHEAD=FALSE;
    newdoc.line=1;
    newdoc.link=0;
    *prev_target='\0';
    *user_input_buffer='\0';

    if(TRACE)
	fprintf(stderr,"Entering mainloop, startfile=%s\n",startfile);

    if (form_post_data) {
 	StrAllocCopy(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) {
	    _statusline("Bookmark features are currently disabled.");
	    sleep(AlertSecs);
	    bookmark_start = FALSE;
	    goto initialize;
        } else if (traversal) {
	    _statusline(
	    	"Bookmark files cannot be traversed (only http URLs).");
	    sleep(AlertSecs);
	    traversal = FALSE;
	    crawl = FALSE;
	    bookmark_start = FALSE;
	    goto initialize;
	} else {
	    /* 
	     * See if a bookmark page exists.  If it does,
	     * replace newdoc.address with it's name 
	     */
	    if (get_bookmark_filename(&newdoc.address) != NULL) {
		LYforce_HTML_mode = TRUE;  /* force HTML */
		StrAllocCopy(newdoc.title, "Bookmark File");
		StrAllocCopy(startrealm, newdoc.address);
		free_and_clear(&newdoc.post_data);
		free_and_clear(&newdoc.post_content_type);
		newdoc.isHEAD = FALSE;
		if (TRACE)
		    fprintf(stderr, "Using bookmarks=%s\n", newdoc.address);
	    } else {
		_statusline(
		"Unable to open bookmark file, use 'a' to save a link first");
       		sleep(MessageSecs);
		bookmark_start = FALSE;
		goto initialize;
	    }
	}
    }

    if (form_post_data) {
	free_and_clear(&form_post_data);
    } else if (form_get_data) {
	free_and_clear(&form_get_data);
    }

    if(user_mode==NOVICE_MODE)
        display_lines = LYlines-4;
    else
        display_lines = LYlines-2;

    while (TRUE) {
	/* if newdoc.address is different then 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 */
try_again:
		/*
		 * Push the old file onto the history stack.
	 	 */
		if (curdoc.address && newdoc.address) {
		    LYpush(&curdoc);

		} else if(!newdoc.address) {
		    /*
		     * If newdoc.address is empty then pop a file and load it.
		     */
                    LYpop(&newdoc);
		}

		if (HEAD_request) {
		    /*
		     * Make SURE this is an appropriate request. - FM
		     */
		    if (!strncmp(newdoc.address, "http", 4))
		        newdoc.isHEAD = TRUE;
		    HEAD_request = FALSE;
		}

		LYRequestTitle = newdoc.title;
		switch(getfile(&newdoc)) {

		case NOT_FOUND: 
		    /*
		     * OK! can't find the file, so it must not be around now.
		     * Do any error logging, if appropriate.
		     */
		    if (error_logging && !first_file && owner_address &&
		        !LYCancelledFetch) {
		        /* send an error message */
		        if (!strncasecomp(owner_address, "mailto:", 7)) {
			    mailmsg(curdoc.link,
			    	    (owner_address+7), 
				    history[nhist-1].address,
				    history[nhist-1].title);
		        }
		    } else if (traversal && !first_file && !LYCancelledFetch) {
		        FILE *ofp;

		        if ((ofp = fopen(TRAVERSE_ERRORS,"a+")) == NULL) {
 			    if ((ofp = fopen(TRAVERSE_ERRORS,"w")) == NULL) {
			        perror(
			      "unable to open traversal errors output file");
			        exit(-1);
			    }
		        }
		        fprintf(ofp, "%s %s	in %s\n",
		       		     links[curdoc.link].lname, 
				     links[curdoc.link].target,
				     history[nhist-1].address);
		        fclose(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.
			 */
			if (!dump_output_immediately)
			    cleanup();
		        printf("\nlynx: Can't access start file %s\n",
								  startfile);
			if (!dump_output_immediately)
			    exit(-1);
			return(-1);
		    }

		case NULLFILE:
		    /*
		     * Not supposed to return any file.
		     */
		    newdoc.address = 0; /* to pop last doc */
		    LYJumpFileURL = FALSE;
		    reloading = FALSE;
		    LYPermitURL = FALSE;
		    LYCancelledFetch = 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 &&
#ifdef VMS
			   strcasecomp(homepage, startfile) != 0) {
#else
			   strcmp(homepage, startfile) != 0) {
#endif /* VMS */
			   /* 
			    * Couldn't return to the first file but there is a
			    * homepage we can use instead. Useful for when the
			    * 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
			    */
			   StrAllocCopy(newdoc.address, homepage);
			   free_and_clear(&newdoc.post_data);
			   free_and_clear(&newdoc.post_content_type);
			   StrAllocCopy(startfile, homepage);
			   newdoc.isHEAD = FALSE;
		       } else {
		           if (!dump_output_immediately)
			       cleanup();
			   printf(
 "\nlynx: Start file could not be found or is not text/html or text/plain\n");
			   printf("      Exiting...\n");
		           if (!dump_output_immediately)
			       exit(-1);
			   return(-1);
		       }
		    }

		    goto try_again;
                    break;

		case NORMAL:
		    /*
		     * Marvelously, we got the document!
		     */
		    *prev_target = '\0'; /* Reset for this document. -FM */

		    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(traversal_link_to_add))
			        add_to_table(traversal_link_to_add);
			    FREE(traversal_link_to_add);
			}
			if(curdoc.address && curdoc.title)
			    /* Add the address we got to TRAVERSE_FOUND_FILE */
			    add_to_traverse_list(curdoc.address, curdoc.title);
		    }

		    /*
		     *  If this was a NORMAL download, we still have curdoc,
		     *  not a newdoc, so reset the address, title and
		     *  positioning elements. - FM
		     */
		    if (newdoc.address && curdoc.title &&
		        !strncmp(newdoc.address, "LYNXDOWNLOAD:", 13) &&
		        !strcmp(curdoc.title, DOWNLOAD_OPTIONS_TITLE)) {
			StrAllocCopy(newdoc.address, curdoc.address);
			StrAllocCopy(newdoc.title, curdoc.title);
			newdoc.line = curdoc.line;
			newdoc.link = curdoc.link;
		    }

		    /*
            	     * 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.
		     */
                    newline = newdoc.line;

		    /* 
		     * If we are going to a target line,
		     * override any www_search line result.
		     */
		    if(newline > 1)	
			 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)
	      sleep(AlertSecs); /* allow me to look at the results */

	   /* set the files the same */
	   StrAllocCopy(curdoc.address, newdoc.address);
	   StrAllocCopy(curdoc.post_data, newdoc.post_data);
	   StrAllocCopy(curdoc.post_content_type, newdoc.post_content_type);
	   curdoc.isHEAD = newdoc.isHEAD;

	   /* reset WWW present mode so that if we were getting
	    * the source, we get rendered HTML from now on
	    */
	   HTOutputFormat = WWW_PRESENT;
	   LYUserSpecifiedURL = FALSE;	/* only set for goto's */
	   LYJumpFileURL = FALSE;	/* only set for jump's */
	   reloading = FALSE;		/* only set for RELOAD and RESUBMIT */
	   HEAD_request = FALSE;	/* only set for HEAD requests */
	   LYPermitURL = FALSE;		/* only set for LYValidate */

  	} /* end if(STREQ(newdoc.address,curdoc.address) */

        if(dump_output_immediately) {
	    if (crawl) {
                if(HText_getTitle())
	            StrAllocCopy(curdoc.title, HText_getTitle());
                print_crawl_to_fd(stdout,curdoc.address,curdoc.title);
	    } else {
                print_wwwfile_to_fd(stdout,0);
	    }
	    return(0);
	}

	/* if the resent_sizechange variable is set to true
	   then the window size changed recently. 
	*/
	if(recent_sizechange) {
		stop_curses();
		start_curses(); 
		clear();
		refresh_screen = TRUE; /*to force a redraw */
		recent_sizechange=FALSE;
		if (user_mode==NOVICE_MODE) {
		    display_lines = LYlines-4;
		} else {
		    display_lines = LYlines-2;
		}
	}

        if(www_search_result != -1) {
             /* This was a WWW search, set the line
              * to the result of the search
              */
             newline = www_search_result;
             www_search_result = -1;  /* reset */
	     more = HText_canScrollDown();
        }


	/* if the curdoc.line is different than newline then there must
	 * have been a change since last update. Run showpage.
	 * showpage will put a fresh screen of text out.
	 * If this is a WWW document then use the 
	 * WWW routine HText_pageDisplay to put the page on
	 * the screen
         */
	if (curdoc.line != newline) {
	
   	    refresh_screen = FALSE;

	    HText_pageDisplay(newline, prev_target);

#ifdef DIRED_SUPPORT
	    if(lynx_edit_mode && nlinks && tagged != NULL)
	      showtags(tagged);
#endif /* DIRED_SUPPORT */
	    /* if more equals true then there is more
	     * info below this page 
	     */
	    more = HText_canScrollDown();
	    curdoc.line = newline = HText_getTopOfScreen()+1;
            lines_in_file = HText_getNumOfLines();

            if(HText_getTitle()) {
	        StrAllocCopy(curdoc.title, HText_getTitle());
	    } else {
	        StrAllocCopy(curdoc.title, newdoc.title);
	    }
	   owner_address = HText_getOwner();

	   if (arrowup) { 
		/* arrow up 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;
	   }

	   show_help = FALSE; /* reset */
	   newdoc.line = 1;
	   newdoc.link = 0;
	   curdoc.line = newline; /* set */
	}

	/* refesh the screen if neccessary */
	if(refresh_screen) {
	    clear();
	    HText_pageDisplay(newline, prev_target);

#ifdef DIRED_SUPPORT
	    if(lynx_edit_mode && nlinks && tagged != NULL)
	      showtags(tagged);
#endif /* DIRED_SUPPORT */
	    if(user_mode == NOVICE_MODE)
		noviceline(more);  /* print help message */
	    refresh_screen=FALSE;

	}

	/* report unread or new mail, if appropriate */
	if (check_mail && !no_mail && LYCheckMail())
	    sleep(MessageSecs);

	if (first_file == TRUE) {
	    /*
	     * We can never again have the first file.
	     */
	    first_file = FALSE; 

	    temp = HTParse(curdoc.address, "",
			   PARSE_ACCESS+PARSE_HOST+PARSE_PUNCTUATION);
	    if (!temp || *temp == '\0') {
		StrAllocCopy(startrealm, "None");
	    } else {
		StrAllocCopy(startrealm, temp);
		FREE(temp);
		if (!(temp = HTParse(curdoc.address, "",
		    		     PARSE_PATH+PARSE_PUNCTUATION))) {
		    if (startrealm[strlen(startrealm)-1] != '/') {
			StrAllocCat(startrealm, "/");
		    }
		} else {
		    if ((cp = strrchr(temp, '/')) != NULL) {
			*(cp+1) = '\0';
			StrAllocCat(startrealm, temp);
		    }
		}
	    }
	    FREE(temp);
	    if (TRACE) {
		fprintf(stderr, "Starting realm is '%s'\n\n", startrealm);
	    }
	    if (traversal) {
	        /*
		 * Set up the crawl output stuff.
		 */
		if(curdoc.address && !lookup(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, "None");
		} else if (check_realm) {
		    StrAllocCopy(traversal_host, startrealm);
		} else {
		    char *temp = HTParse(curdoc.address, "",
		    		 PARSE_ACCESS+PARSE_HOST+PARSE_PUNCTUATION);
		    if (!temp || *temp == '\0') {
		        StrAllocCopy(traversal_host, "None");
		    } else {
		        StrAllocCopy(traversal_host, temp);
			if (traversal_host[strlen(traversal_host)-1] != '/') {
			    StrAllocCat(traversal_host, "/");
			}
		    }
		    FREE(temp);
		}
		if (TRACE) {
		    fprintf(stderr,
		    	    "Traversal host is '%s'\n\n", traversal_host);
		}
	    }
	}

	/* if help is not on the screen 
	 * then put a message on the screen
	 * to tell the user other misc info
	 */
	if (!show_help) {
	    /* make sure form novice lines are replaced */
	    if(user_mode == NOVICE_MODE) {
		noviceline(more);
	    }

	    /* if we are in forms mode then explicitly
	     * tell the user what each kind of link is
	     */
	    if(HTisDocumentSource()) {
		/* currently displaying HTML source */
		_statusline(SOURCE_HELP);

#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 &&
	    	       links[curdoc.link].type != WWW_LINK_TYPE) {
#else
	    } else if (lynx_mode == FORMS_LYNX_MODE && nlinks > 0 &&
	    	       !(user_mode == ADVANCED_MODE &&
	    	         links[curdoc.link].type == WWW_LINK_TYPE)) {
#endif /* NORMAL_NON_FORM_LINK_STATUSLINES_FOR_ALL_USER_MODES */
#endif /* INDICATE_FORMS_MODE_FOR_ALL_LINKS_ON_PAGE */
                if (links[curdoc.link].type == WWW_FORM_LINK_TYPE)
		    switch(links[curdoc.link].form->type) {
                    case F_PASSWORD_TYPE:
		  	if (links[curdoc.link].form->disabled == YES)
			    statusline(FORM_LINK_PASSWORD_UNM_MSG);
   			else
                            statusline(FORM_LINK_PASSWORD_MESSAGE);
		        break;
		    case F_OPTION_LIST_TYPE:
		  	if (links[curdoc.link].form->disabled == YES)
			    statusline(FORM_LINK_OPTION_LIST_UNM_MSG);
   			else
                            statusline(FORM_LINK_OPTION_LIST_MESSAGE);
			break;
                    case F_CHECKBOX_TYPE:
                    case F_RADIO_TYPE:
		  	if (links[curdoc.link].form->disabled == YES)
			    statusline(FORM_LINK_CHECKBOX_UNM_MSG);
   			else
                            statusline(FORM_LINK_CHECKBOX_MESSAGE);
		        break;
                    case F_TEXT_SUBMIT_TYPE:
		  	if (links[curdoc.link].form->disabled == YES) {
			    statusline(FORM_LINK_TEXT_SUBMIT_UNM_MSG);
			} else if (links[curdoc.link].form->submit_method ==
				 URL_MAIL_METHOD) {
			    if (no_mail)
			        statusline(
				       FORM_LINK_TEXT_SUBMIT_MAILTO_DIS_MSG);
			    else
 			        statusline(FORM_LINK_TEXT_SUBMIT_MAILTO_MSG);
   			} else {
                            statusline(FORM_LINK_TEXT_SUBMIT_MESSAGE);
			}
		        break;
                    case F_SUBMIT_TYPE:
		  	if (links[curdoc.link].form->disabled == YES) {
			    statusline(FORM_LINK_SUBMIT_DIS_MSG);
			} else if (links[curdoc.link].form->submit_method ==
				 URL_MAIL_METHOD) {
			    if (no_mail)
			        statusline(FORM_LINK_SUBMIT_MAILTO_DIS_MSG);
			    else
 			        statusline(FORM_LINK_SUBMIT_MAILTO_MSG);
   			} else if (LYresubmit_forms ||
				   links[curdoc.link].form->no_cache) {
                            statusline(FORM_LINK_RESUBMIT_MESSAGE);
 			} else { 
                            statusline(FORM_LINK_SUBMIT_MESSAGE);
			}
		        break;
                    case F_RESET_TYPE:
		  	if (links[curdoc.link].form->disabled == YES)
			    statusline(FORM_LINK_RESET_DIS_MSG);
   			else
                            statusline(FORM_LINK_RESET_MESSAGE);
		        break;
                    case F_TEXT_TYPE:
		    case F_TEXTAREA_TYPE:
		  	if (links[curdoc.link].form->disabled == YES)
			    statusline(FORM_LINK_TEXT_UNM_MSG);
   			else
                            statusline(FORM_LINK_TEXT_MESSAGE);
		        break;
                    }
		else
	            statusline(NORMAL_LINK_MESSAGE);

		/* let them know if it's an index -- very rare*/
		if(is_www_index) {
		    move(LYlines-1,LYcols-8);
		    start_reverse();
		    addstr("-index-");
		    stop_reverse();
		}
			
	    } else if(user_mode == ADVANCED_MODE && nlinks > 0) {
		/* show the URL */
		if(more)
		    if(is_www_index)
		        _user_message("-more- -index- %s",
						 links[curdoc.link].lname);
		    else
		        _user_message("-more- %s",links[curdoc.link].lname);
		else
		    if(is_www_index)
		        _user_message("-index- %s",links[curdoc.link].lname);
		    else
		        statusline(links[curdoc.link].lname);
	    } else if(is_www_index && more) {
		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) {
		if(user_mode == NOVICE_MODE)
			_statusline(MORE);
		else
			_statusline(MOREHELP);
	    } else {
	       _statusline(HELP);
   	    }	     
	} else {
	   show_help = FALSE;
	}

	if (!(nlinks > 0 &&
	      links[curdoc.link].type == WWW_FORM_LINK_TYPE &&
              (links[curdoc.link].form->type == F_TEXT_TYPE ||
	       links[curdoc.link].form->type == F_TEXTAREA_TYPE)))
	    highlight(ON, curdoc.link); /* highlight current link */

	if (traversal) {
	    /*
	     * Don't go interactively into forms,
	     * or accept keystrokes from the user
	     */
            if (crawl && crawl_ok) {
	        crawl_ok = FALSE;
	        sprintf(cfile,"lnk%08d.dat",ccount);
	        ccount = ccount + 1;
	        if ((cfp = fopen(cfile,"w"))  != NULL) {
	            print_crawl_to_fd(cfp,curdoc.address,curdoc.title);
	            fclose(cfp);
	        } else {
		    if (!dump_output_immediately)
		        cleanup();
	            printf(
		        "Fatal error - could not open output file %s\n",cfile);
		    if (!dump_output_immediately)
	                exit(-1);
		    return(-1);
	        }
	    }
	} else {
	    /*
	     * Normal, non-traversal handling
	     */
	    if (nlinks > 0 &&
	    	links[curdoc.link].type == WWW_FORM_LINK_TYPE &&
		(links[curdoc.link].form->type == F_TEXT_TYPE ||
		 links[curdoc.link].form->type == F_TEXT_SUBMIT_TYPE ||
		 links[curdoc.link].form->type == F_PASSWORD_TYPE ||
		 links[curdoc.link].form->type == F_TEXTAREA_TYPE)) {
	        /*
		 * Replace novice lines if in NOVICE_MODE
		 */
    	        if(user_mode==NOVICE_MODE) {
		    move(LYlines-2,0); clrtoeol();
		    addstr(FORM_NOVICELINE_ONE);
		    move(LYlines-1,0); clrtoeol();
		    addstr(FORM_NOVICELINE_TWO);
	        }
	        c=change_form_link(&links[curdoc.link],
			       FORM_UP, &newdoc, &refresh_screen,
			       links[curdoc.link].form->name,
			       links[curdoc.link].form->value);

	        if(c == '\n' || c == '\r')
#ifdef FASTTAB
		    /*
		     * Make return act like downarrow
		     */
		    c = DNARROW;
#else
		    /*
		     * Make return act like tab
		     */
		    c = '\t';
#endif /* FASTTAB */
	    } else {
	        /* Get a keystroke from the user
	         * Save the last keystroke to avoid redundant
		 * error reporting.
	         */
	        real_c = c = LYgetch();	/* get user input */
#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(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) {
	    /*
	     * This is a special feature to traverse every http link
	     * derived from startfile and check for errors or create
	     * crawl ouput 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!)
	     */
	    rlink_exists = (nlinks > 0 && links[curdoc.link].lname != NULL);
	    if (rlink_exists) {
	        rlink_allowed = (!lookup_reject(links[curdoc.link].lname) &&
				 !strncmp(traversal_host,
				 	  links[curdoc.link].lname,
					  strlen(traversal_host)));
	    } else {	
	        rlink_allowed = FALSE;
	    }
	    if (rlink_exists && rlink_allowed) {
	        if (lookup(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(-1);
			    }
			    return(-1);
			}
			c=LTARROW;
		    }
		} else {
		    StrAllocCopy(traversal_link_to_add,
		    		 links[curdoc.link].lname);
		    crawl_ok = TRUE;
		    c = RTARROW;
		}
	    } else { /* no good right link, so only down and left arrow ok*/
	        if (rlink_exists)
		    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(-1);
			}
			return(-1);
		    }
	            c=LTARROW;
		}
	    } /* right link not NULL or link to another site*/
	} /* traversal */

	cmd=keymap[c+1];  /* add 1 to map EOF to 0 */

#if defined(DIRED_SUPPORT) && defined(OK_OVERRIDE)
	if (lynx_edit_mode && override[c+1] && !no_dired_support)
	  cmd = override[c+1];
#endif /* DIRED_SUPPORT && OK_OVERRIDE */

new_cmd:  /* a goto point for new input without going
           * back through the getch() loop
           */

	switch(cmd) {
	case 0: /* unmapped character */
	default:
	    if (more)
                _statusline(MOREHELP);
            else
                _statusline(HELP);
            show_help = TRUE;

            if(TRACE)
                printw("%d", c);  /* show the user input */
            break;

	case LYK_INTERRUPT:
	    /* No network transmission to interrupt - 'til we multithread */
	    break;

	case LYK_1:
	case LYK_2:
	case LYK_3:
	case LYK_4:
	case LYK_5:
	case LYK_6:
	case LYK_7:
	case LYK_8:
	case LYK_9:
	    /* get a number from the user and follow that link number */
	    switch(follow_link_number(c, ((nlinks > 0) ? curdoc.link : 0))) {
	    case DO_LINK_STUFF:
                /* follow a normal link */
		if (nlinks > 0)
                    StrAllocCopy(newdoc.address, links[curdoc.link].lname);
		else
		    StrAllocCopy(newdoc.address, links[0].lname);
		/*
		 * 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)) {
		    free_and_clear(&newdoc.post_data);
		    free_and_clear(&newdoc.post_content_type);
		    newdoc.isHEAD = FALSE;
		}
		force_load = TRUE;  /* force MainLoop to reload */
		break;

	    case PRINT_ERROR:
		if(old_c == real_c)
			break;
		old_c = real_c;
		_statusline(BAD_LINK_NUM_ENTERED);
		sleep(MessageSecs);
		break;
	    }
	    break;

	case LYK_SOURCE:  /* toggle view source mode */
	    if(HTisDocumentSource()) 
	        HTOutputFormat = WWW_PRESENT;
	    else
	        HTOutputFormat = WWW_SOURCE;
	    HTuncache_current_document();
	    free_and_clear(&curdoc.address); /* so it doesn't get pushed */
	    break;

	case LYK_RELOAD:  /* control-R to reload and refresh */
	    /*
	     * Check to see if should reload source, or load html
	     */
	    if(HTisDocumentSource()) {
		HTOutputFormat = WWW_SOURCE;
	    }
	    HEAD_request = HTLoadedDocumentIsHEAD();
	    HTuncache_current_document();
#ifdef NO_ASSUME_SAME_DOC
	    /*
	     * Don't assume the reloaded document will be the same. - FM
	     */
	    newdoc.line=1;
	    newdoc.link=0;
#else
	    /*
	     * 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)
	     */
	    if (lynx_mode == FORMS_LYNX_MODE) {
	        /*
		 * Note that if there are no form links on the current
		 * page, lynx_mode won't have this setting and we won't
		 * know that this warning should be issued. - FM
		 */
		_statusline(
		 "Reloading document.  Any form entries will be lost!");
		sleep(AlertSecs);
	    }
	    newdoc.line = ((curdoc.line > 0) ? curdoc.line : 1);
	    newdoc.link = ((curdoc.link > -1) ? curdoc.link : 0);
#endif /* NO_ASSUME_SAME_DOC */
	    free_and_clear(&curdoc.address); /* so it doesn't get pushed */
#ifdef VMS
	    clearok(curscr, TRUE);
#endif /* VMS */
	    /*
	     * Reload should force a cache refresh on a proxy
	     *        -- Ari L. <luotonen@dxcern.cern.ch>
	     */
            reloading = TRUE;
	    break;

	case LYK_HISTORICAL:  
	    HTuncache_current_document();
	    newdoc.address = curdoc.address;
	    curdoc.address = NULL;
	    if (historical_comments)
	        historical_comments = FALSE;
	    else
	        historical_comments = TRUE;
	    if (minimal_comments) {
	        _statusline(historical_comments ?
	    	    "Historical comment parsing ON (Minimal is overridden)!" :
		    "Historical comment parsing OFF (Minimal is in effect)!");
	    } else {
	        _statusline(historical_comments ?
	    	    "Historical comment parsing ON (Valid is overridden)!" :
		    "Historical comment parsing OFF (Valid is in effect)!");
	    }
	    sleep(AlertSecs);
	    break;

	case LYK_MINIMAL:  
	    if (!historical_comments) {
	        HTuncache_current_document();
	        newdoc.address = curdoc.address;
	        curdoc.address = NULL;
	    }
	    if (minimal_comments)
	        minimal_comments = FALSE;
	    else
	        minimal_comments = TRUE;
	    if (!historical_comments) {
	        _statusline(minimal_comments ?
	    	    "Minimal comment parsing ON (and in effect)!" :
		    "Minimal comment parsing OFF (Valid is in effect)!");
	    } else {
	        _statusline(minimal_comments ?
	    	  "Minimal comment parsing ON (but Historical is in effect)!" :
		  "Minimal comment parsing OFF (but Historical in effect)!");
	    }
	    sleep(AlertSecs);
	    break;

	case LYK_SOFT_DQUOTES:  
	    HTuncache_current_document();
	    newdoc.address = curdoc.address;
	    curdoc.address = NULL;
	    if (soft_dquotes)
	        soft_dquotes = FALSE;
	    else
	        soft_dquotes = TRUE;
	    _statusline(soft_dquotes ?
	    		"Soft double-quote parsing ON!" :
			"Soft double-quote parsing OFF!");
	    sleep(MessageSecs);
	    break;

#ifdef NOT_DONE_YET
	case LYK_PIPE:  
	    /* ignore for now */
	    break;
#endif /* NOT_DONE_YET */

	case LYK_QUIT:	/* quit */
	    _statusline(QUIT);
	    c = LYgetch();
#ifdef QUIT_DEFAULT_YES
	    if(TOUPPER(c) != 'N')
#else
	    if(TOUPPER(c) == 'Y')
#endif /* QUIT_DEFAULT_YES */
	        return(0);
	    else {
	        statusline("Excellent!");
		sleep(InfoSecs);
	    }
	    break;
	
	case LYK_ABORT:
	    return(0);  /* dont ask the user about quitting */
	    break;

	case LYK_NEXT_PAGE:	/* next page */
	    if(more)
	           newline += display_lines;
	    else if(curdoc.link < nlinks-1) 
		{
		   highlight(OFF,curdoc.link);
		   curdoc.link = nlinks-1;  /* put on last link */
	        } 
	    else if(old_c != real_c)
		{
		   old_c = real_c;
		   _statusline(
		   	"You are already at the end of this document.");
		   sleep(MessageSecs);
		}
	    break;

	case LYK_PREV_PAGE:  /* page up */
	   if(newline > 1) 
	           newline -= display_lines;
	   else if(curdoc.link > 0) 
		{
		   highlight(OFF,curdoc.link);
		   curdoc.link = 0;  /* put on last link */
		}
	    else if(old_c != real_c) 
		{
		   old_c = real_c;
		   _statusline(
		   	"You are already at the beginning of this document.");
		   sleep(MessageSecs);
		}
	   break;

	case  LYK_UP_TWO:
	    if (newline >= 2) {
	        newline -= 2;
		if (nlinks && curdoc.link > -1) {
		    if (links[curdoc.link].ly <= (display_lines - 2)) {
		        newdoc.link = curdoc.link +
				      HText_LinksInLines(HTMainText,
				      			 newline, 2);
		    } else {
		        arrowup = TRUE;
		    }
		}
	    } else if (old_c != real_c) {
	        {
		   old_c = real_c;
		   _statusline(
		   	"You are already at the beginning of this document.");
		   sleep(MessageSecs);
	        }
	    }
	    break;

	case  LYK_DOWN_TWO:
	    if (more) {
	        newline += 2;
		if (nlinks && curdoc.link > -1 && links[curdoc.link].ly > 2) {
		    int i;
		    newdoc.link = curdoc.link;
		    for (i = 0; links[i].ly <= 2; i++)
		        --newdoc.link;
		}
	    } else if (old_c != real_c) {
		{
		   old_c = real_c;
		   _statusline("You are already at the end of this document.");
		   sleep(MessageSecs);
		}
	    }
	    break;

	case LYK_REFRESH:
	   refresh_screen=TRUE;
#ifdef VMS
	   clearok(curscr, TRUE);
#endif /* VMS */
	   break;

	case LYK_HOME:
	    if(curdoc.line > 1)
	        newline = 1;
	    else {
		cmd = LYK_PREV_PAGE;
		goto new_cmd;
	    }
	    break;

	case LYK_END:
	    if(more) {
	       newline = MAXINT; /* go to end of file */
	       arrowup = TRUE;  /* position on last link */
	    } else {
		cmd = LYK_NEXT_PAGE;
		goto new_cmd;
	    }
	    break;

	case LYK_PREV_LINK:
	    if (curdoc.link>0) {		/* previous link */
		highlight(OFF, curdoc.link);   /* unhighlight the current link */
		curdoc.link--;

	    } else if(!more && curdoc.link==0 && newline==1) { /* at the top of list */ 
		/* if there is only one page of data and the user
		 * goes off the top, then just move the cursor to
		 * last link on the page
		 */
		highlight(OFF,curdoc.link); /* unhighlight the current link */
		curdoc.link = nlinks-1;  /* the last link */

	    } else if (curdoc.line > 1) {	/* previous page */
		/* go back to the previous page */
		newline -= (display_lines);
		arrowup = TRUE;

	    } else if(old_c != real_c) {
		old_c = real_c;
		_statusline("You're already at the top of this document");
		sleep(MessageSecs);
	    }
	    break;

	case LYK_NEXT_LINK:
	    if (curdoc.link < nlinks-1) {	/* next link */
		highlight(OFF, curdoc.link);
#ifdef FASTTAB
		/*
		 * Move to different textarea if TAB in textarea.
		 */
		if (links[curdoc.link].type == WWW_FORM_LINK_TYPE &&
                    links[curdoc.link].form->type == F_TEXTAREA_TYPE &&
                    c=='\t') {
		    int thisgroup = links[curdoc.link].form->number;
		    char *thisname = links[curdoc.link].form->name;

		    do curdoc.link++;
		    while ((curdoc.link < nlinks-1) &&
			   links[curdoc.link].type == WWW_FORM_LINK_TYPE &&
			   links[curdoc.link].form->type == F_TEXTAREA_TYPE &&
			   links[curdoc.link].form->number == thisgroup &&
			   sametext(links[curdoc.link].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 && newline == 1 && curdoc.link == nlinks-1) {
		highlight(OFF,curdoc.link); 
		curdoc.link = 0;

            } else if (more) {  /* next page */
                 newline += (display_lines);

	    } else if(old_c != real_c) {
		old_c = real_c;
		_statusline("You're already at the end of this document");
		sleep(MessageSecs);
	    }
	    break;

        case LYK_UP_LINK:
            if (curdoc.link > 0) {  /* more links above? */
                int i, newlink = -1;
                for (i = curdoc.link; i >= 0; i--) {
                    if (links[i].ly < links[curdoc.link].ly) {
                        newlink = i;
                        break;
                    }
		}
                if(newlink > -1) {
                    highlight(OFF, curdoc.link);
                    curdoc.link = newlink;
#ifdef NOTDEFINED
                } else if (!more && newline == 1 && curdoc.link == 0) {
                    highlight(OFF, curdoc.link);
                    curdoc.link = (nlinks-1);
                } else if (more) {  /* next page */
                        newline += (display_lines);
                }
#else
		} else if (old_c != real_c) {
		    old_c = real_c;
		    _statusline(
			"There are no links above this line of the document."
				);
		    sleep(MessageSecs);
		}
#endif /* NOTDEFINED */

#ifdef NOTDEFINED
            /* at the bottom of list and there is only one page
             * move to the top link on the page
             */
            } else if (!more && newline == 1 && curdoc.link == (nlinks-1)) {
                highlight(OFF, curdoc.link);
                curdoc.link = 0;
#endif /* NOTDEFINED */

            } else if (curdoc.line > 1 && newline > 1) {  /* previous page */
                    newline -= (display_lines);
		    arrowup = TRUE;

	    } else if (old_c != real_c) {
		old_c = real_c;
		_statusline("You're already at the top of this document.");
		sleep(MessageSecs);
            }
            break;

	case LYK_DOWN_LINK:
	    if (curdoc.link < (nlinks-1)) {	/* more links? */
		int i, newlink = -1;
		for (i = curdoc.link; i < nlinks; i++)
		   if (links[i].ly > links[curdoc.link].ly) {
			newlink = i;
			break;
		   }
			
		if (newlink > -1) {
                    highlight(OFF, curdoc.link);
                    curdoc.link = newlink;
#ifdef NOTDEFINED
		} else if (!more &&
			   newline == 1 && curdoc.link == (nlinks-1)) {
                    highlight(OFF, curdoc.link);
                    curdoc.link = 0;
#endif /* NOTDEFINED */
                } else if (more) {  /* next page */
                        newline += (display_lines);
		} else if (old_c != real_c) {
		    old_c = real_c;
		    _statusline(
			"There are no links below this line of the document."
				);
		    sleep(MessageSecs);
		    break;
		}
#ifdef NOTDEFINED
            /* at the bottom of list and there is only one page
             * move to the top link on the page
             */
            } else if (!more && newline == 1 && curdoc.link == (nlinks-1)) {
                highlight(OFF, curdoc.link);
                curdoc.link = 0;
#endif /* NOTDEFINED */
            } else if (more) {  /* next page */
                    newline += (display_lines);

	    } else if (old_c != real_c) {
		old_c = real_c;
		_statusline("You're already at the bottom of this document.");
		sleep(MessageSecs);
            }
            break;

	case LYK_RIGHT_LINK:
	    if (curdoc.link<nlinks-1 &&
			links[curdoc.link].ly == links[curdoc.link+1].ly) {
                highlight(OFF,curdoc.link);
		curdoc.link++;
	    }
	    break;

	case LYK_LEFT_LINK:
	    if (curdoc.link>0 &&
			links[curdoc.link].ly == links[curdoc.link-1].ly) {
                highlight(OFF,curdoc.link);
		curdoc.link--;
	    }
	    break;

	case LYK_HISTORY: 	/* show the history page */
	  if(strcmp(curdoc.title, HISTORY_PAGE_TITLE)) {
		/* don't do if already viewing history page */

		/* push current file so that the history
		 * list contains the current file for printing
		 * purposes.
		 * pop the file afterwards to prevent multiple copies 
		 */
                LYpush(&curdoc);

		/* print history options to file */
	    	showhistory(&newdoc.address);
 		free_and_clear(&curdoc.address);  /* so it doesn't get pushed */
		free_and_clear(&newdoc.post_data);
		free_and_clear(&newdoc.post_content_type);
		newdoc.isHEAD = FALSE;

                LYpop(&curdoc);

		refresh_screen=TRUE;
		if (LYValidate || check_realm) {
		    LYPermitURL = TRUE;
		}
	        break; 
	  } /* end if strncmp */

	/* dont put break here so that if the backspace key is pressed in
	 * the history page, then it acts like a left arrow 
	 */

	case LYK_PREV_DOC:			/* back up a level */
	        if (nhist>0) {  /* if there is anything to go back to */

		    /* set newdoc.address to empty to pop a file */
		    free_and_clear(&newdoc.address);
#ifdef DIRED_SUPPORT
		    if (lynx_edit_mode)
		      HTuncache_current_document();
#endif /* DIRED_SUPPORT */
	        } else if(child_lynx==TRUE) {
	   	   return(0); /* exit on left arrow in main screen */ 

		} else if(old_c != real_c) {
		    old_c = real_c;
		    _statusline("Already at the first document");
		    sleep(MessageSecs);
		}
	     break;

	case LYK_NOCACHE: /* Force submission of form or link with no-cache */
	     if(nlinks > 0) {
	         if (links[curdoc.link].type == WWW_FORM_LINK_TYPE &&
	             links[curdoc.link].form->type != F_SUBMIT_TYPE) {
		     if(old_c == real_c)
		         break;
		     old_c = real_c;
	_statusline("You are not on a form submission button or normal link.");
		     sleep(MessageSecs);
		     break;
	         } else {
	             LYforce_no_cache = TRUE;
		     reloading = TRUE;
		 }
	     } /* fall through to LYK_ACTIVATE */

	case LYK_ACTIVATE:			/* follow a link */
	    if (nlinks > 0) {
	        if(links[curdoc.link].type == WWW_FORM_LINK_TYPE) {
		        /*
			 * Don't try to submit forms with bad actions. - FM
			 */
			if(links[curdoc.link].form->type == F_SUBMIT_TYPE ||
			   links[curdoc.link].form->type ==
			   			       F_TEXT_SUBMIT_TYPE) {
			   /*
			    * Do nothing if it's disabled. - FM
			    */
			   if (links[curdoc.link].form->disabled == YES)
			       break;
			   /*
			    * Make sure we have an action. - FM
			    */
			   if (!links[curdoc.link].form->submit_action ||
			       *links[curdoc.link].form->submit_action
			       					== '\0') {
			       _statusline(
			       	 "** Bad HTML!!  No form action defined. **");
			       sleep(MessageSecs);
			       break;
			    }
		            /*
			     * Check for no_mail if the form action
			     * is a mailto URL. - FM
			     */
			    else if (links[curdoc.link].form->submit_method
			             == URL_MAIL_METHOD && no_mail) {
			        HTAlert("Mail disallowed!  Cannot submit.");
			        break;
			    }
			    if (check_realm) {
			        LYPermitURL = TRUE;
			    }
			}
		        c = change_form_link(&links[curdoc.link],
					     FORM_UP, &newdoc, &refresh_screen,
					     links[curdoc.link].form->name,
					     links[curdoc.link].form->value);
			goto new_keyboard_input;

	        } else {   /* Not a forms link */
		    /* follow a normal link or anchor */
		    StrAllocCopy(newdoc.address, links[curdoc.link].lname);
		    StrAllocCopy(newdoc.title, links[curdoc.link].hightext);
		    /*
		     * Might be an anchor in the same doc from a POST
		     * form.  If so, dont't free the content. -- FM
		     */
		    if (are_different(&curdoc, &newdoc)) {
		        free_and_clear(&newdoc.post_data);
		        free_and_clear(&newdoc.post_content_type);
		    }
		    if (check_realm && lynxjumpfile &&
		        !strcmp(lynxjumpfile, curdoc.address)) {
			LYJumpFileURL = TRUE;
		    }
		    newdoc.link = 0;
		    force_load = TRUE;  /* force MainLoop to reload */
#ifdef DIRED_SUPPORT
		    if (lynx_edit_mode) {
			  HTuncache_current_document();
			  HTUnEscape(newdoc.address);
			  strip_trailing_slash(newdoc.address);
		    }
#endif /* DIRED_SUPPORT */
		}
	    }
	    break;

	case LYK_GOTO:   /* 'g' to goto a random URL  */
	 
	    if (no_goto && !LYValidate) {
	        if (old_c != real_c) {
	   	    old_c = real_c;
	    	    _statusline("Goto a random URL is disallowed!");
		    sleep(MessageSecs);
		}
		break;
	    }
	 
	    StrAllocCopy(temp, 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 : NORECALL);
		URLNum = 0;
		FirstURLRecall = FALSE;
	    } else {
		recall = ((URLTotal >= 1) ? RECALL : NORECALL);
		URLNum = URLTotal;
		FirstURLRecall = TRUE;
	    }

	    /*
	     * Ask the user.
	     */
	    _statusline("URL to open: ");
	    if ((ch=LYgetstr(user_input_buffer, VISIBLE,
	    		     sizeof(user_input_buffer), recall)) < 0 ) {
		/*
		 * User cancelled the Goto via ^G.
		 * Restore user_input_buffer and break. - FM
		 */
		strcpy(user_input_buffer, temp);
		FREE(temp);
		_statusline("Cancelled!!!");
		sleep(InfoSecs);
		break;
	    }

check_recall:
	    /*
	     * Get rid of leading spaces (and any other spaces).
	     */
	    collapse_spaces(user_input_buffer);
	    if (*user_input_buffer == '\0' &&
	        !(recall && (ch == UPARROW || ch == DNARROW))) {
		strcpy(user_input_buffer, temp);
		FREE(temp);
		_statusline("Cancelled!!!");
		sleep(InfoSecs);
	        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++;
		}
		if (URLNum >= URLTotal)
		    /*
		     * Roll around to the last URL in the list. - FM
		     */
		    URLNum = 0;
		if ((cp=(char *)HTList_objectAt(Goto_URLs,
	    					    URLNum)) != NULL) {
		    strcpy(user_input_buffer, cp);
		    if (goto_buffer && *temp &&
		        !strcmp(temp, user_input_buffer)) { 
		        _statusline("Edit the current Goto URL: ");
		    } else if ((goto_buffer && URLTotal == 2) ||
			       (!goto_buffer && URLTotal == 1)) {
			_statusline("Edit the previous Goto URL: ");
		    } else {
			_statusline("Edit a previous Goto URL: ");
		    }
		    if ((ch=LYgetstr(user_input_buffer, VISIBLE,
				     sizeof(user_input_buffer), recall)) < 0) {
			/*
			 * User cancelled the Goto via ^G.
			 * Restore user_input_buffer and break. - FM
			 */
			strcpy(user_input_buffer, temp);
			FREE(temp);
			_statusline("Cancelled!!!");
			sleep(InfoSecs);
			break;
		    }
		    goto check_recall;
		}
	    } 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--;
		}
		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) {
		    strcpy(user_input_buffer, cp);
		    if (goto_buffer && *temp &&
		        !strcmp(temp, user_input_buffer)) { 
		        _statusline("Edit the current Goto URL: ");
		    } else if ((goto_buffer && URLTotal == 2) ||
			       (!goto_buffer && URLTotal == 1)) {
			_statusline("Edit the previous Goto URL: ");
		    } else {
			_statusline("Edit a previous Goto URL: ");
		    }
		    if ((ch=LYgetstr(user_input_buffer, VISIBLE,
				     sizeof(user_input_buffer), recall)) < 0) {
			/*
			 * User cancelled the Goto via ^G.
			 * Restore user_input_buffer and break. - FM
			 */
			strcpy(user_input_buffer, temp);
			FREE(temp);
			_statusline("Cancelled!!!");
			sleep(InfoSecs);
			break;
		    }
		    goto check_recall;
		}
	    }

	    /*
	     * If its not a URL then make it one.
	     */
	    if (!is_url(user_input_buffer)) {
	        char *user_input_string = NULL;
	        if (TRACE)
		    fprintf(stderr, "\n'%s' is not a URL\n",
		    			user_input_buffer);
		StrAllocCopy(user_input_string, user_input_buffer);
		LYConvertToURL(&user_input_string);
		strcpy(user_input_buffer, user_input_string);
		FREE(user_input_string);
	    }
	    if (no_file_url && !strncmp(user_input_buffer,"file:",5)) {
                _statusline("You are not allowed to goto \"file:\" URL's");
                sleep(MessageSecs);

#ifdef EXEC_LINKS
            } else if ((no_shell || local_exec_on_local_files) &&
#else
            } else if (no_shell &&
#endif /* EXEC_LINKS */
	    	       !strncmp(user_input_buffer, "lynxexec:",9)) {
                _statusline("You are not allowed to goto \"lynxexec:\" URL's");
                sleep(MessageSecs);

#ifdef EXEC_LINKS
            } else if ((no_shell || local_exec_on_local_files) &&
#else
            } else if (no_shell &&
#endif /* EXEC_LINKS */
	    	       !strncmp(user_input_buffer, "lynxprog:",9)) {
                _statusline("You are not allowed to goto \"lynxprog:\" URL's");
                sleep(MessageSecs);

            } else if(no_shell && !strncmp(user_input_buffer, "lynxcgi:",8)) {
                _statusline("You are not allowed to goto \"lynxcgi:\" URL's");
                sleep(MessageSecs);

            } else if (LYValidate &&
		       strncmp(user_input_buffer, "http:", 5) &&
		       strncmp(user_input_buffer, "https:", 6)) {
                _statusline("Goto a non-http URL is disallowed!");
                sleep(MessageSecs);

            } else {
		/* make a name for this new URL */
	        StrAllocCopy(newdoc.title, "A URL specified by the user");

		/*
		 * If it's a file URL and the host is defaulted,
		 * force in "//localhost".  We need this until
		 * all the other Lynx code which performs security
		 * checks based on the "localhost" string is changed
		 * to assume "//localhost" when a host field is not
		 * present in file URLs - FM
		 */
		if (!strncmp(user_input_buffer, "file:", 5)) {
		    if (user_input_buffer[5] == '\0') {
		        strcat(user_input_buffer, "//localhost");
		    } else if (!strcmp(user_input_buffer, "file://")) {
		        strcat(user_input_buffer, "localhost");
		    } else if (!strncmp(user_input_buffer, "file:///", 8)) {
		        StrAllocCopy(temp, (user_input_buffer+7));
		        strcpy(user_input_buffer, "file://localhost");
		        strcat(user_input_buffer, temp);
		        FREE(temp);
		    } else if (!strncmp(user_input_buffer, "file:/", 6) &&
		    	      user_input_buffer[6] != '/') {
		        StrAllocCopy(temp, (user_input_buffer+5));
		        strcpy(user_input_buffer, "file://localhost");
		        strcat(user_input_buffer, temp);
		        FREE(temp);
		    }
		}

		/*
		 * No path in a file://localhost URL means a
		 * directory listing for the current default. - FM
		 */
		if (!strcmp(user_input_buffer, "file://localhost")) {
#ifdef VMS
		    strcat(user_input_buffer, HTVMS_wwwName(getenv("PATH")));
#else
		    char curdir[DIRNAMESIZE];
#ifdef NO_GETCWD
		    getwd (curdir);
#else
		    getcwd (curdir, DIRNAMESIZE);
#endif /* NO_GETCWD */
		    strcat(user_input_buffer, curdir);
#endif /* VMS */
		}

#ifdef VMS
		/*
		 * On VMS, a file://localhost/ URL means
		 * a listing for the login directory. - FM
		 */
		if (!strcmp(user_input_buffer, "file://localhost/"))
		    strcat(user_input_buffer,
		    	   (HTVMS_wwwName((char *)Home_Dir())+1));
#endif /* VMS */

	        StrAllocCopy(newdoc.address, user_input_buffer);
		newdoc.isHEAD = FALSE;
		/*
		 * Might be an anchor in the same doc from a POST
		 * form.  If so, dont't free the content. -- FM
		 */
		if (are_different(&curdoc, &newdoc)) {
		    free_and_clear(&newdoc.post_data);
		    free_and_clear(&newdoc.post_content_type);
		}
		force_load = TRUE;
		LYUserSpecifiedURL = TRUE;
#ifdef DIRED_SUPPORT
		if (lynx_edit_mode) 
		    HTuncache_current_document();
#endif /* DIRED_SUPPORT */
		HTAddGotoURL(user_input_buffer);
	    } 
	    break;

	case LYK_HELP:			/* show help file */
	    if(!STREQ(curdoc.address, helpfile)) {
	        StrAllocCopy(newdoc.address, helpfile);  /* set the filename */
		/* make a name for this help file */
	        StrAllocCopy(newdoc.title, "Help Screen");
		free_and_clear(&newdoc.post_data);
		free_and_clear(&newdoc.post_content_type);
		newdoc.isHEAD = FALSE;
	    }
	    break;

	case LYK_INDEX:  /* index file */
	    /* 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;
		    		_statusline(
					"No index is currently available.");
		    		sleep(MessageSecs);
			}

	        } else {
	            StrAllocCopy(newdoc.address, indexfile);
	            StrAllocCopy(newdoc.title, "System Index"); /* name it */
		    free_and_clear(&newdoc.post_data);
		    free_and_clear(&newdoc.post_content_type);
		    newdoc.isHEAD = FALSE;
	        } /* end else */
	    }  /* end if */
	    break;

#ifdef NOT_USED
	case LYK_FORM_UP:  /* change form */
	    break;	   /* not implemented */
	    if(lynx_mode == FORMS_LYNX_MODE) {
		if(links[curdoc.link].type == WWW_FORM_LINK_TYPE) {
		    c = change_form_link(&links[curdoc.link],
					 FORM_UP, &newdoc, &refresh_screen,
					 links[curdoc.link].form->name,
					 links[curdoc.link].form->value);
		    /*
		     *	Code to handle multiple submit buttons?
		     *	Taken out due to bug it causes.
		    if(links[curdoc.link].form->type == F_SUBMIT_TYPE)	{
			curdoc.address = NULL;
		    }
		     */
		    goto new_keyboard_input;
		} else {
		    _statusline("'X' can only toggle a form link");
		}
	    } else {
		_statusline("'X' only toggles in forms mode");
	    }
	    break;	

	case LYK_FORM_DOWN:  /* change form */
	    break;	     /* not implemented */
	    if(lynx_mode==FORMS_LYNX_MODE) {
		if(links[curdoc.link].type == WWW_FORM_LINK_TYPE) {
		    c = change_form_link(&links[curdoc.link],
					 FORM_DOWN,&newdoc,&refresh_screen,
					 links[curdoc.link].form->name,
					 links[curdoc.link].form->value);
		    goto new_keyboard_input;
		} else {
		    _statusline("'Z' can only toggle a form link");
		}
	    } else {
		_statusline("'Z' only toggles in forms mode");
	    }
	    break;	
#endif /* NOT_USED */

	case LYK_MAIN_MENU:	/* return to main screen */
	    /* if its already the homepage then don't reload it */
	    if(!STREQ(curdoc.address,homepage)) {
		
		_statusline("Do you really want to go to the Main screen? (y/n) [n] ");
		c = LYgetch();
		if(TOUPPER(c)=='Y') {
	            StrAllocCopy(newdoc.address, homepage);
                    StrAllocCopy(newdoc.title, "Entry into main screen");
		    free_and_clear(&newdoc.post_data);
		    free_and_clear(&newdoc.post_content_type);
		    newdoc.isHEAD = FALSE;
	            highlight(OFF,curdoc.link); 
#ifdef DIRED_SUPPORT
		    if (lynx_edit_mode)
		      HTuncache_current_document();
#endif /* DIRED_SUPPORT */
		}
#ifdef VMS
		if (HadVMSInterrupt)
		    HadVMSInterrupt = FALSE;
#endif /* VMS */
	    } else {
		if(old_c != real_c)	{
			old_c = real_c;
			_statusline("Already at main screen!");
			sleep(MessageSecs);
		}
	    }
	    break;

	case LYK_OPTIONS:     /* options screen */

#ifdef DIRED_SUPPORT
	    c = dir_list_style;
#endif /* DIRED_SUPPORT */
	    options(); /* do the options stuff */

	    if (keypad_mode_flag != keypad_mode ||
#ifdef DIRED_SUPPORT
		c != dir_list_style ||
#endif /* DIRED_SUPPORT */
		HTfileSortMethod_flag != HTfileSortMethod ||
		CurrentCharSet_flag != current_char_set ||
		show_dotfiles_flag != show_dotfiles) {
		HTuncache_current_document();
		newdoc.address = curdoc.address;
		curdoc.address = NULL;
		keypad_mode_flag = keypad_mode;
		HTfileSortMethod_flag = HTfileSortMethod;
		CurrentCharSet_flag = current_char_set;
		show_dotfiles_flag = show_dotfiles;
	    }
	    refresh_screen = TRUE; /* to repaint screen */
	    break;

	case LYK_INDEX_SEARCH: /* search for a user string */
	     if(is_www_index) {
               /* perform a database search */

		/* do_www_search will try to go out and get the document
		 * if it returns yes a new document was returned and is
		 * named in the newdoc.address
		 */
		newdoc.isHEAD = FALSE;
		if(do_www_search(&newdoc) == NORMAL) {
		   /* Yah, the search succeeded. */
                   LYpush(&curdoc);
		   /* Make the curdoc.address the newdoc.address so that
		    * getfile doesn't try to get the newdoc.address.
		    * Since we have already gotton it.
		    */ 
		   StrAllocCopy(curdoc.address, newdoc.address);
		   StrAllocCopy(newdoc.post_data, curdoc.post_data);
		   curdoc.line = -1;
		   newline = 0;
		   refresh_screen = TRUE; /* redisplay it */
		} else if (use_this_url_instead != NULL) {
		   /* Got back a redirecting URL. Check it out. */
		   _user_message("Using %s", use_this_url_instead);
		   /* Make a name for this URL */
	           StrAllocCopy(newdoc.title, "A URL specified by redirection");
	           StrAllocCopy(newdoc.address, use_this_url_instead);
		   free_and_clear(&newdoc.post_data);
		   free_and_clear(&newdoc.post_content_type);
		   newdoc.isHEAD = FALSE;
	           FREE(use_this_url_instead);
		   force_load = TRUE;
		   break;
		} else {
		   /* Yuk, the search failed.  Restore the old file. */
		   StrAllocCopy(newdoc.address, curdoc.address);  
		   StrAllocCopy(newdoc.post_data, curdoc.post_data);
		   newdoc.isHEAD = curdoc.isHEAD;
		}
	     } else if(old_c != real_c)	{
		old_c = real_c;
		_statusline(
  "Not a searchable indexed document -- press '/' to search for a text string");
		sleep(MessageSecs);
	     }
	     break;

	case LYK_WHEREIS: /* search within the document */
  	/* search for the next occurrence of the user string */
	case LYK_NEXT:
            /* user search */
	    if(cmd != LYK_NEXT) {
	        /* 
		 * Reset prev_target to force prompting
		 * for a new search string and to turn
		 * off highlighting in no search string
		 * is entered by the user.
		 */
	        *prev_target = '\0';
	        textsearch(&curdoc, prev_target, FALSE);
	    } else {
	        /*
		 * When the third argument is TRUE, the previous
		 * search string, if any, will be recalled from
		 * a buffer, loaded into prev_target, and used
		 * for the search without prompting for a new
		 * search string.  This allows the LYK_NEXT
		 * command to repeat a search in a new document,
		 * after prev_target was reset on fetch of that
		 * document.
		 */
	        textsearch(&curdoc, prev_target, TRUE);
	    }
	    
	    /*
	     * Force a redraw to ensure highlighting of hits
	     * even when found on the same page, or clearing
	     * of highlighting is the default search string
	     * was erased without replacement. - FM
	     */
	    refresh_screen = TRUE;
	    break;

	case LYK_COMMENT:  /* reply by mail */
	   if(!owner_address) {
		if(old_c != real_c)	{
			old_c = real_c;
			_statusline(
	    "No owner is defined for this file so you cannot send a comment");
			sleep(MessageSecs);
		}
	   } else if (no_mail) {
	       if(old_c != real_c) {
	           old_c = real_c;
		   _statusline(
		   	"Mail is disallowed so you cannot send a comment");
		   sleep(MessageSecs);
	       }
	   } else {
		_statusline("Do you wish to send a comment? [N]");
		c = LYgetch();
	        if(TOUPPER(c) == 'Y') {

	           if(is_url(owner_address) != MAILTO_URL_TYPE) { 
			/* the address is a url */
			/* just follow the link */
			
		       StrAllocCopy(newdoc.address, owner_address);

	           } else {
		       /* the owner_address is a mailto: url type */
		       cp = HText_getRevTitle();
		       if(strchr(owner_address,':')!=NULL)
			 /* send a reply. The address is after the colon */
	      	         reply_by_mail(strchr(owner_address,':')+1,
			 	       curdoc.address,
				       (cp ? cp : "")); 
		       else
	      	         reply_by_mail(owner_address, curdoc.address,
				       (cp ? cp : "")); 

	               refresh_screen=TRUE;  /* to force a showpage */
	          }
	       }
	   }
	   break;

#ifdef DIRED_SUPPORT
	case LYK_TAG_LINK:  /* tag or untag the current link */
	   if (lynx_edit_mode && nlinks && !no_dired_support) {
	      if (dir_list_style == MIXED_STYLE) {
		 if (!strcmp(links[curdoc.link].hightext,"../")) 
		   break;
	      } else if (!strncmp(links[curdoc.link].hightext,"Up to ",6)) 
		 break;
	      t1 = tagged;
	      while (t1 != NULL) {
		 if (!strcmp(links[curdoc.link].lname,t1->name)) {
		    if (t1 == tagged) 
		      tagged = t1->next;
		    else 
		      t2->next = t1->next;
		    FREE(t1->name);
		    FREE(t1);
		    tagflag(OFF,curdoc.link);
		    break;
		 }
		 t2 = t1;
		 t1 = t1->next;
	      }
	      if (t1 == NULL) {
		 t1 = (taglink *) malloc(sizeof(taglink));
		 if (tagged == NULL) 
		   tagged = t1;
		 else 
		   t2->next = t1;
		 t1->next = NULL;
		 t1->name = NULL;
		 StrAllocCopy(t1->name,links[curdoc.link].lname);
		 tagflag(ON,curdoc.link);
	      }
	      if (curdoc.link < nlinks-1) {
		highlight(OFF, curdoc.link);
		curdoc.link++;
	      } else if (!more && newline==1 && curdoc.link==nlinks-1) {
		highlight(OFF,curdoc.link); 
		curdoc.link = 0;
	      } else if (more) {  /* next page */
                newline += (display_lines);
	      }
	   }
	   break;

	case LYK_MODIFY:  /* rename a file or directory */
           if(lynx_edit_mode && nlinks && !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) {
		 HTuncache_current_document();
		 newdoc.address = curdoc.address;
		 curdoc.address = NULL;	
		 newdoc.line = curdoc.line;
		 newdoc.link = curdoc.link;
		 clear();
	      }
	   }
           break;

	case LYK_CREATE:  /* create a new file or directory */
           if(lynx_edit_mode && !no_dired_support) {
	      if (local_create(&curdoc)) {
		 HTuncache_current_document();
		 newdoc.address = curdoc.address;
		 curdoc.address = NULL;	
		 newdoc.line = curdoc.line;
		 newdoc.link = curdoc.link > -1 ? curdoc.link : 0;
		 clear();
	      }
	   }
           break;
#endif /* DIRED_SUPPORT */

	case LYK_EDIT:  /* edit */
	    if (no_editor) {
		if(old_c != real_c)	{
		    old_c = real_c;
		    _statusline(
		        "The 'e'dit command is currently disabled.");
		    sleep(MessageSecs);
		}
		break;
	    }

#ifdef DIRED_SUPPORT

/* Allow the user to edit the link rather than curdoc in edit mode */

	   if(lynx_edit_mode && editor && *editor != '\0' &&
	   			!no_dired_support) {
	      if (nlinks > 0) {
		 cp = links[curdoc.link].lname;
		 if(is_url(cp) == FILE_URL_TYPE) {
		    tp = cp;
		    if(!strncmp(tp,"file://localhost",16))
		       tp += 16;
		    else if(!strncmp(tp,"file:",5))
		       tp += 5;
		    strcpy(tmpbuf,tp);
		    HTUnEscape(tmpbuf);
		    if(stat(tmpbuf,&dir_info) == -1) {
		       _statusline("System error - failure to get status. ");
		       sleep(AlertSecs);
		    } else {
		       if (((dir_info.st_mode) & S_IFMT) == S_IFREG) {
			  strcpy(tmpbuf,cp);
			  HTUnEscape(tmpbuf);
			  if(edit_current_file(tmpbuf,curdoc.link,newline))
			  {
			    	HTuncache_current_document();
			  	newdoc.address = curdoc.address;
			  	curdoc.address = NULL;
#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 > 0) ? curdoc.line : 1);
				newdoc.link =
					((curdoc.link > -1) ? curdoc.link : 0);
#endif /* NO_SEEK_OLD_POSITION */
			  	clear();  /* clear the screen */
			  }
			}
		    }
		 }
	      }
	   } else
#endif /* DIRED_SUPPORT */
	   if(editor && *editor != '\0') {
		if(edit_current_file(newdoc.address,curdoc.link,newline))
		{
	            	HTuncache_current_document();
			LYforce_no_cache = TRUE;  /*force the document to be reloaded*/
	        	free_and_clear(&curdoc.address); /* 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 */
			clear();  /* clear the screen */
		}

	   } else {
		if(old_c != real_c)	{
			old_c = real_c;
			_statusline("No editor is defined!");
			sleep(MessageSecs);
		}
	   }
	   break;

	case LYK_DEL_BOOKMARK:  /* delete home page link */
#ifdef DIRED_SUPPORT
	case LYK_REMOVE:  /* remove files and directories */
	   if(lynx_edit_mode && nlinks && !no_dired_support)
		local_remove(&curdoc);
	    else
#endif /* DIRED_SUPPORT */

	    if(bookmark_page && (strstr(curdoc.address, bookmark_page) ||
			!strcmp(curdoc.title, MOSAIC_BOOKMARK_TITLE))) {
		_statusline(
     "Do you really want to delete this link from your bookmark file? (y/n)");
             	c = LYgetch();
                if(TOUPPER(c) != 'Y')
                    break;
                remove_bookmark_link(links[curdoc.link].anchor_number-1);
	    } else {	/* behave like REFRESH for backward compatability */
	        refresh_screen=TRUE;
	        break;
	    }
	    HTuncache_current_document();
	    newdoc.address = curdoc.address;
	    curdoc.address = NULL;	
	    newdoc.line = curdoc.line;
	    if(curdoc.link == nlinks-1)
	       /* if we deleted the last link on the page */
               newdoc.link=curdoc.link-1; 
	    else
                newdoc.link=curdoc.link;
	    break;

#ifdef DIRED_SUPPORT
	case LYK_INSTALL:  /* install a file into system area */
	   if(lynx_edit_mode && nlinks && !no_dired_support)
	      local_install(NULL, links[curdoc.link].lname, &newdoc.address);
	   break;
#endif /* DIRED_SUPPORT */

	case LYK_INFO:  /* show document info */
	   /* don't do if already viewing info page */	
	   if(strcmp(curdoc.title, SHOWINFO_TITLE)) {
	        showinfo(&curdoc, lines_in_file, &newdoc, owner_address);
		LYforce_no_cache = TRUE;
		if (LYValidate || check_realm)
		    LYPermitURL = TRUE;
            } else {
		/* if already in info page; get out */
		cmd = LYK_PREV_DOC;
		goto new_cmd;
	    }
            break;

	case LYK_PRINT:  /* print the file */
	    if (LYValidate) {
		if(old_c != real_c)	{
		    old_c = real_c;
		    _statusline(
		        "The 'p'rint command is currently disabled.");
		    sleep(MessageSecs);
		}
		break;
	    }

	    /* don't do if already viewing print options page */	
	    if(strcmp(curdoc.title, PRINT_OPTIONS_TITLE)) {	

                print_options(&newdoc.address, lines_in_file);
		if (check_realm)
		    LYPermitURL = TRUE;
	        refresh_screen=TRUE;  /* redisplay */
	    }
	    break;

	case LYK_LIST:  /* list links in the current document */
	    /* don't do if already viewing list page */	
	    if(strcmp(curdoc.title, LIST_PAGE_TITLE)) {	
		showlist(&newdoc.address, TRUE);
	        refresh_screen=TRUE;  /* redisplay */
		if (LYValidate || check_realm) {
		    LYPermitURL = TRUE;
		    StrAllocCopy(lynxlistfile, newdoc.address);
		}
	    }
	    break;

	case LYK_TOOLBAR:  /* go to Toolbar or Banner in current document */
	    if (!HText_hasToolbar(HTMainText)) {
		if(old_c != real_c) {
		    old_c = real_c;
		    _statusline("Document has no Toolbar links or Banner.");
		    sleep(MessageSecs);
		}
	    } else if (old_c != real_c) {
	        old_c = real_c;
		if ((cp = strchr(curdoc.address, '#')) != NULL)
		    *cp = '\0';
		toolbar = (char *)malloc(strlen(curdoc.address) +
	    				 strlen(LYToolbarName) + 2);
		sprintf(toolbar, "%s#%s", curdoc.address, LYToolbarName);
		if (cp)
		    *cp = '#';
		StrAllocCopy(newdoc.address, toolbar);
		FREE(toolbar);
		force_load = TRUE;  /* force MainLoop to reload */
	    }
	    break;

#ifdef DIRED_SUPPORT
       case LYK_DIRED_MENU:  /* provide full file management menu */
	   /* don't do if not allowed or already viewing the menu */	
	   if(lynx_edit_mode && !no_dired_support &&
	      strcmp(curdoc.title, DIRED_MENU_TITLE)) {
	      dired_options(&curdoc,&newdoc.address);
	      refresh_screen=TRUE;  /* redisplay */
	   }
	   break;
#endif /* DIRED_SUPPORT */

	case LYK_ADD_BOOKMARK:  /* a to add link to bookmark file */
	   if (LYValidate) {
		if(old_c != real_c)	{
		    old_c = real_c;
		    _statusline(
		        "Bookmark features are currently disabled.");
		    sleep(MessageSecs);
		}
		break;
	   }

	   if(strcmp(curdoc.title, HISTORY_PAGE_TITLE) &&
	      strcmp(curdoc.title, SHOWINFO_TITLE) && 
	      strcmp(curdoc.title, PRINT_OPTIONS_TITLE) &&
#ifdef DIRED_SUPPORT
	      strcmp(curdoc.title, DIRED_MENU_TITLE) &&
	      strcmp(curdoc.title, PERMIT_OPTIONS_TITLE) &&
	      strcmp(curdoc.title, UPLOAD_OPTIONS_TITLE) &&
#endif /* DIRED_SUPPORT */
	      strcmp(curdoc.title, DOWNLOAD_OPTIONS_TITLE) &&
	      strcmp(curdoc.title, LIST_PAGE_TITLE)) {

		if(nlinks) {
		    _statusline(
	    "Save D)ocument or L)ink to bookmark file or C)ancel? (d,l,c): ");
		    c = LYgetch();
		    if(TOUPPER(c) == 'D')
		        save_bookmark_link(curdoc.address, curdoc.title);
		    else if(TOUPPER(c) == 'L')
			if(links[curdoc.link].type != WWW_FORM_LINK_TYPE)
                            save_bookmark_link(links[curdoc.link].lname, 
						links[curdoc.link].hightext);
			else
			  {
			    _statusline("Cannot save form fields/links");
			    sleep(MessageSecs);
			  }
		} else {
		    _statusline(
		       "Save D)ocument to bookmark file or C)ancel? (d,c): ");
		    c = LYgetch();
		    if(TOUPPER(c) == 'D')
		        save_bookmark_link(curdoc.address, curdoc.title);
		}
	   } else {
		if(old_c != real_c)	{
			old_c = real_c;
			_statusline(
      "History, showinfo, menu and list files cannot be saved as bookmarks.");
			sleep(MessageSecs);
		}
	   }
		
	   break;

	case LYK_VIEW_BOOKMARK:   /* v to view home page */
	    if (LYValidate) {
		if(old_c != real_c)	{
		    old_c = real_c;
		    _statusline(
		        "Bookmark features are currently disabled.");
		    sleep(MessageSecs);
		}
		break;
	    }

	    /* see if a bookmark exists
	     * if it does replace newdoc.address with it's name 
	     */
	    if(get_bookmark_filename(&newdoc.address) != NULL) {
		LYforce_HTML_mode = TRUE;  /* force HTML */
		LYforce_no_cache = TRUE;  /*force the document to be reloaded*/
		StrAllocCopy(newdoc.title, "Bookmark File");
		free_and_clear(&newdoc.post_data);
		free_and_clear(&newdoc.post_content_type);
		newdoc.isHEAD = FALSE;
		if (check_realm) {
		    StrAllocCopy(lynxbookfile, newdoc.address);
		}
	    } else {
		if(old_c != real_c)	{
			old_c = real_c;
			_statusline(
		"Unable to open bookmark file, use 'a' to save a link first");
       			sleep(MessageSecs);
		}
	    }
	    break;

	case LYK_SHELL:  /* shell escape */
	    if(!no_shell) {
	        stop_curses();
#ifdef VMS
		printf("Spawning DCL subprocess.  Logout to return to Lynx.");
		fflush(stdout);
		system("");
#else
                signal(SIGINT, SIG_IGN);
		printf(
		 "Spawning your default shell.  Use 'exit' to return to Lynx.\n");
	        fflush(stdout);
	        system("exec $SHELL");
                signal (SIGINT, cleanup_sig);
#endif /* VMS */
	        start_curses();
	        refresh_screen=TRUE;  /* for a showpage */
	    } else {
		if(old_c != real_c)	{
			old_c = real_c;
			_statusline("The (!) command is currently disabled");
			sleep(MessageSecs);
		}
	    }
	    break;

	case LYK_DOWNLOAD:
	    /* 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;
		    _statusline(
		        "The 'd'ownload command is currently disabled.");
		    sleep(MessageSecs);
		}
		break;
	    }

	    /* don't do if already viewing download options page */	
	    if(0==strcmp(curdoc.title, DOWNLOAD_OPTIONS_TITLE))
	        break;

	    if (nlinks > 0) {
                if(links[curdoc.link].type == WWW_FORM_LINK_TYPE) {
		    if(old_c != real_c)	{
			old_c = real_c;
			_statusline("You cannot download a input field.");
			sleep(MessageSecs);
		    }

		} else if (0==strcmp(curdoc.title, PRINT_OPTIONS_TITLE)) {
		    if(old_c != real_c)	{
			old_c = real_c;
			_statusline("You cannot download a printing option.");
			sleep(MessageSecs);
		    }

#ifdef DIRED_SUPPORT
		} else if (0==strcmp(curdoc.title, UPLOAD_OPTIONS_TITLE)) {
		    if(old_c != real_c)	{
			old_c = real_c;
			_statusline("You cannot download an upload option.");
			sleep(MessageSecs);
		    }

		} else if (0==strcmp(curdoc.title, PERMIT_OPTIONS_TITLE)) {
		    if(old_c != real_c)	{
			old_c = real_c;
			_statusline("You cannot download an permit option.");
			sleep(MessageSecs);
		    }

                } else if (lynx_edit_mode && !no_dired_support) {
		    /* Don't bother making a /tmp copy of the local file */
                    StrAllocCopy(newdoc.address, links[curdoc.link].lname);
                    LYdownload_options(&newdoc.address,
		    		       links[curdoc.link].lname);
#endif /* DIRED_SUPPORT */

		} else if (0==strcmp(curdoc.title, HISTORY_PAGE_TITLE)) {
		    int number = atoi(links[curdoc.link].lname+9);
		    StrAllocCopy(newdoc.address, history[number].address);
                    StrAllocCopy(newdoc.title, links[curdoc.link].hightext);
		    free_and_clear(&newdoc.post_data);
		    free_and_clear(&newdoc.post_content_type);
		    if (history[number].post_data)
		        StrAllocCopy(newdoc.post_data,
				     history[number].post_data);
		    if (history[number].post_content_type)
		        StrAllocCopy(newdoc.post_content_type,
				     history[number].post_content_type);
		    newdoc.isHEAD = history[number].isHEAD;
                    newdoc.link = 0;
	            HTOutputFormat = HTAtom_for("www/download");
		    /*force the document to be reloaded*/
		    LYforce_no_cache = TRUE;  
		    force_load = TRUE;  /* force MainLoop to reload */
		    
                } 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
		     */
                    StrAllocCopy(newdoc.address, links[curdoc.link].lname);
                    StrAllocCopy(newdoc.title, links[curdoc.link].hightext);
		    /*
		     * 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)) {
			free_and_clear(&newdoc.post_data);
			free_and_clear(&newdoc.post_content_type);
			newdoc.isHEAD = FALSE;
		    }
                    newdoc.link = 0;
	            HTOutputFormat = HTAtom_for("www/download");
		    /*force the document to be reloaded*/
		    LYforce_no_cache = TRUE;  
		    force_load = TRUE;  /* force MainLoop to reload */
                }
            } else if(old_c != real_c)	{
		old_c = real_c;
		_statusline("Nothing to download.");
		sleep(MessageSecs);
	    }
	    break;

#ifdef DIRED_SUPPORT
	  case LYK_UPLOAD:
	    /* don't do if already viewing upload options page */	
	    if(0==strcmp(curdoc.title, UPLOAD_OPTIONS_TITLE))
	        break;

	    if (lynx_edit_mode && !no_dired_support) {
                LYUpload_options((char **)&newdoc.address,
				 (char *)curdoc.address);
	     }
	    break;
#endif /* DIRED_SUPPORT */

	case LYK_TRACE_TOGGLE:
	    if(WWW_TraceFlag)
		WWW_TraceFlag = FALSE;	
	    else 
		WWW_TraceFlag = TRUE;	

	    _statusline(WWW_TraceFlag ? "Trace ON!" : "Trace OFF!");
	    sleep(MessageSecs);
	    break;

	case LYK_IMAGE_TOGGLE:
	    if(clickable_images)
		clickable_images = FALSE;	
	    else 
		clickable_images = TRUE;	

	    _statusline(clickable_images ?
		"Links will be included for all images!  Reloading..." :
		"Standard image handling restored!  Reloading...");
	    sleep(MessageSecs);
	    cmd = LYK_RELOAD;
	    goto new_cmd;
	    break;

	case LYK_INLINE_TOGGLE:
	    if(pseudo_inline_alts)
		pseudo_inline_alts = FALSE;	
	    else 
		pseudo_inline_alts = TRUE;	

	    _statusline(pseudo_inline_alts ?
"Pseudo_ALTs will be inserted for inlines without ALT strings!  Reloading..." :
"Inlines without an ALT string specified will be ignored!  Reloading...");
	    sleep(MessageSecs);
	    cmd = LYK_RELOAD;
	    goto new_cmd;
	    break;

	case LYK_JPN_TOGGLE:
	    if(HTJapanese)
		HTJapanese = FALSE;	
	    else 
		HTJapanese = TRUE;	

	    _statusline(HTJapanese ?
		"Japanese character translations ON!  Reloading..." :
		"Japanese character translations OFF!  Reloading...");
	    sleep(MessageSecs);
	    cmd = LYK_RELOAD;
	    goto new_cmd;
	    break;

	case LYK_HEAD:
	    if (nlinks && (links[curdoc.link].type != WWW_FORM_LINK_TYPE ||
	    		   links[curdoc.link].form->type == F_SUBMIT_TYPE)) {
	        _statusline(
	"Send HEAD request for D)ocument or L)ink, or C)ancel? (d,l,c): ");
		c = LYgetch();
		if (TOUPPER(c) == 'D') {
		    if (strncmp(curdoc.address, "http", 4)) {
		        _statusline("Sorry, the document is not an http URL.");
			sleep(MessageSecs);
		    } else {
		        HEAD_request = TRUE;
			LYforce_no_cache = TRUE;
			if (HTLoadedDocumentIsHEAD()) {
	            	    HTuncache_current_document();
			    free_and_clear(&curdoc.address);
			}
		    }
		    break;
		} else if (TOUPPER(c) == 'L') {
		    if (links[curdoc.link].type != WWW_FORM_LINK_TYPE &&
		    	strncmp(links[curdoc.link].lname, "http", 4)) {
		        _statusline("Sorry, the link is not an http URL.");
			sleep(MessageSecs);
		    } else if (links[curdoc.link].type == WWW_FORM_LINK_TYPE &&
		    	       links[curdoc.link].form->disabled) {
			_statusline(
			    "Sorry, the ACTION for this form is disabled.");
			sleep(MessageSecs);
		    } else if (links[curdoc.link].type == WWW_FORM_LINK_TYPE &&
		    	       strncmp(links[curdoc.link].form->submit_action,
								 "http", 4)) {
			_statusline(
			 "Sorry, the ACTION for this form is not an http URL.");
			sleep(MessageSecs);
		    } else {
			HEAD_request = TRUE;
			LYforce_no_cache = TRUE;
			cmd = LYK_ACTIVATE;
			goto new_cmd;
		    }
		    break;
		}
		break;
	    } else {
	        if (nlinks) {
		    _statusline(
		      "Send HEAD request for D)ocument, or C)ancel? (d,c): ");
	            c = LYgetch();
		} else {
		    c = 'D';
		}
		if(TOUPPER(c) == 'D') {
		    if(strncmp(curdoc.address, "http", 4)) {
		        _statusline("Sorry, the document is not an http URL.");
			sleep(MessageSecs);
		    } else {
		        HEAD_request = TRUE;
			LYforce_no_cache = TRUE;
			if (HTLoadedDocumentIsHEAD()) {
	            	    HTuncache_current_document();
			    free_and_clear(&curdoc.address);
			}
		    }
		}
	    }
	    break;	

	case LYK_DO_NOTHING:
	    /* pretty self explanitory */
	    break;

	case LYK_TOGGLE_HELP:
    	    if(user_mode==NOVICE_MODE) {
	        toggle_novice_line();
	        noviceline(more);
	    }
	    break;

	case LYK_KEYMAP:
	    if(old_c != real_c) {
	        old_c = real_c;
		StrAllocCopy(newdoc.address, "LYNXKEYMAP:");
	        free_and_clear(&newdoc.post_data);
	        free_and_clear(&newdoc.post_content_type);
		newdoc.isHEAD = 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.
		 * Don't cache the keymap because it changes. - EF
		 */
		if (!no_dired_support) {
		    prev_lynx_edit_mode = lynx_edit_mode;
		    LYforce_no_cache = TRUE;
		}
#endif /* DIRED_SUPPORT && OK_OVERRIDE */
	    }
	    break;

	case LYK_JUMP:
	    {
	      char *ret;

	      if (no_jump || JThead == NULL) {
	          if (old_c != real_c) {
	   	      old_c = real_c;
		      if (no_jump)
	    	          _statusline(
			      "Jumping to a shortcut URL is disallowed!");
		      else
		          _statusline(NO_JUMPFILE);
		      sleep(MessageSecs);
		  }
	      } else {
		  LYJumpFileURL = TRUE;
	          if ((ret = LYJump(c)) != NULL) {
#ifdef PERMIT_GOTO_FROM_JUMP
		      if (!strncasecomp(ret, "Go ", 3)) {
		          LYJumpFileURL = FALSE;
			  StrAllocCopy(temp, user_input_buffer);
			  URLTotal = (Goto_URLs ? HTList_count(Goto_URLs) : 0);
			  recall = ((URLTotal >= 1) ? RECALL : NORECALL);
			  URLNum = URLTotal;
			  FirstURLRecall = TRUE;
			  if (!strcasecomp(ret, "Go :")) {
			      if (recall) {
			          ch = UPARROW;
				  goto check_recall;
			      }
			      FREE(temp);
			      statusline(
			          "No random URLs have been used thus far.");
			      sleep(MessageSecs);
			      break;
			  }
			  ret = HTParse((ret+3), startfile, PARSE_ALL);
			  strcpy(user_input_buffer, ret);
			  FREE(ret);
			  goto check_recall;
		      }
#endif /* PERMIT_GOTO_FROM_JUMP */
		      ret = HTParse(ret, startfile, PARSE_ALL);
		      StrAllocCopy(newdoc.address, ret);
		      StrAllocCopy(lynxjumpfile, ret);
		      free_and_clear(&newdoc.post_data);
	    	      free_and_clear(&newdoc.post_content_type);
		      newdoc.isHEAD = FALSE;
		      FREE(ret);
		      LYUserSpecifiedURL = TRUE;
		  } else {
		      LYJumpFileURL = FALSE;
		  }
	      }
	      break;
	    }

#ifdef NOT_USED
	case LYK_VERSION:
	    if(old_c != real_c) {
	        char version[128];
		old_c = real_c;
		sprintf(version, "*** %s Version %s ***",
	      			 LYNX_NAME, LYNX_VERSION);
	        statusline(version);
	        sleep(AlertMessage);
	    }
	    break;
#endif /* NOT_USED */

	} /* end of BIG switch */
    }
}

PRIVATE int are_different ARGS2(document *,doc1, document *,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
     */
    if (cp1=strchr(doc1->address, '#'))
        *cp1 = '\0';
    if (cp2=strchr(doc2->address, '#'))
        *cp2 = '\0';
    /* 
     * Are the base addresses different?
     */
    if(strcmp(doc1->address, doc2->address))
      {
        if (cp1)
	    *cp1 = '#';
        if (cp2)
	    *cp2 = '#';
	return(TRUE);
      }
    if (cp1)
        *cp1 = '#';
    if (cp2)
        *cp2 = '#';

    /*
     * Do the docs have different contents?
     */
    if(doc1->post_data)
      {
	if(doc2->post_data)
	  {
	    if(strcmp(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);
}

/* 
 * Utility for freeing the list of goto URLs. - FM
 */
PUBLIC void HTGotoURLs_free NOARGS
{
    char *url;
    HTList *cur = Goto_URLs;

    if (!cur)
        return;

    while (NULL != (url = (char *)HTList_nextObject(cur))) {
	FREE(url);
    }
    HTList_delete(Goto_URLs);
    Goto_URLs = NULL;
    return;
}

/* 
 * Utility for listing Goto URLs, making any
 * repeated URLs the most current in the list. - FM
 */
PUBLIC void HTAddGotoURL ARGS1(char *, url)
{
    char *new;
    char *old;
    HTList *cur;

    if (!(url && *url))
        return;

    if ((new = (char *)calloc(1, (strlen(url) + 1))) == NULL)
    	outofmem(__FILE__, "HTAddGotoURL");
    strcpy(new, url);

    if (!Goto_URLs) {
        Goto_URLs = HTList_new();
	atexit(HTGotoURLs_free);
	HTList_addObject(Goto_URLs, new);
	return;
    }

    cur = Goto_URLs;
    while (NULL != (old = (char *)HTList_nextObject(cur))) {
	if (!strcmp(old, new)) {
	    HTList_removeObject(Goto_URLs, old);
	    FREE(old);
	    break;
	}
    }
    HTList_addObject(Goto_URLs, new);

    return;
}
