summaryrefslogtreecommitdiffstats
path: root/src/LYMainLoop.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/LYMainLoop.c')
-rw-r--r--src/LYMainLoop.c8212
1 files changed, 8212 insertions, 0 deletions
diff --git a/src/LYMainLoop.c b/src/LYMainLoop.c
new file mode 100644
index 0000000..6b13814
--- /dev/null
+++ b/src/LYMainLoop.c
@@ -0,0 +1,8212 @@
+/*
+ * $LynxId: LYMainLoop.c,v 1.250 2023/01/05 09:17:16 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
+
+#ifdef PREVENT_KEYBOARD_WRAPAROUND
+#define HandleForwardWraparound() \
+ *old_c = real_c; \
+ HTInfoMsg(ALREADY_AT_END)
+#define HandleReverseWraparound() \
+ *old_c = real_c; \
+ HTInfoMsg(ALREADY_AT_BEGIN)
+#else
+#define HandleForwardWraparound() \
+ LYSetNewline(1)
+#define HandleReverseWraparound() \
+ int i; \
+ i = HText_getNumOfLines() - display_lines + 2; \
+ if (i >= 1 && Newline != i) { \
+ LYSetNewline(i); \
+ *arrowup = TRUE; \
+ }
+#endif
+
+#define LinkIsTextarea(linkNumber) \
+ (links[linkNumber].type == WWW_FORM_LINK_TYPE && \
+ links[linkNumber].l_form->type == F_TEXTAREA_TYPE)
+
+#define LinkIsTextLike(linkNumber) \
+ (links[linkNumber].type == WWW_FORM_LINK_TYPE && \
+ F_TEXTLIKE(links[linkNumber].l_form->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((unsigned char *) from, (unsigned char *) 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;
+
+#define NONINTERNAL_OR_PHYS_DIFFERENT(p,n) \
+ ((track_internal_links && \
+ (!curdoc.internal_link || are_phys_different(p,n))) || \
+ are_different(p,n))
+
+#define NO_INTERNAL_OR_DIFFERENT(c,n) \
+ (track_internal_links || are_different(c,n))
+
+static void exit_immediately_with_error_message(int state, int first_file);
+static void status_link(const char *curlink_name, int show_more, int 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);
+
+static int are_phys_different(DocInfo *doc1, DocInfo *doc2);
+
+#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 bstring *prev_target = NULL;
+
+#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->str);
+ 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(FALSE, curdoc.link, prev_target->str);
+ 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(TRUE, nextlink, prev_target->str);
+ 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(bstring **user_input,
+ 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)->str[0] == '#') {
+ if ((*user_input)->str[1] &&
+ HTFindPoundSelector((*user_input)->str + 1)) {
+ /* HTFindPoundSelector will initialize www_search_result,
+ so we do nothing else. */
+ HTAddGotoURL((*user_input)->str);
+ trimPoundSelector(curdoc.address);
+ StrAllocCat(curdoc.address, (*user_input)->str);
+ }
+ } else {
+ /*
+ * If it's not a URL then make it one.
+ */
+ StrAllocCopy(*old_user_input, (*user_input)->str);
+ LYEnsureAbsoluteURL(old_user_input, "", TRUE);
+ BStrCopy0((*user_input), *old_user_input);
+ FREE(*old_user_input);
+
+ for (n = 0; n < TABLESIZE(table); n++) {
+ if (*(table[n].flag)
+ && !StrNCmp((*user_input)->str,
+ 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)->str) &&
+ !isHTTPS_URL((*user_input)->str)) {
+ HTUserMsg(GOTO_NON_HTTP_DISALLOWED);
+
+ } else {
+ set_address(&newdoc, (*user_input)->str);
+ newdoc.isHEAD = FALSE;
+ /*
+ * 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)) {
+ /*
+ * 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,
+ bstring **user_input,
+ 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 = (int) strlen((*user_input)->str);
+
+ if (len >= 3) {
+ if (len < MAX_LINE - 1
+ && LYIsHtmlSep((*user_input)->str[len - 3])
+ && LYIsDosDrive((*user_input)->str + len - 2))
+ LYAddPathSep0((*user_input)->str);
+
+ } else if (len == 2 && (*user_input)->str[1] == ':') {
+ if (LYIsDosDrive((*user_input)->str)) {
+ LYAddPathSep0((*user_input)->str);
+ } else {
+ HTUserMsg2(WWW_ILLEGAL_URL_MESSAGE, (*user_input)->str);
+ BStrCopy0((*user_input), *old_user_input);
+ FREE(*old_user_input);
+ ret = FALSE;
+ break;
+ }
+ }
+#endif
+ /*
+ * Get rid of leading spaces (and any other spaces).
+ */
+ LYTrimAllStartfile((*user_input)->str);
+ if (isBEmpty(*user_input) &&
+ !(recall && (ch == UPARROW_KEY || ch == DNARROW_KEY))) {
+ BStrCopy0((*user_input), *old_user_input);
+ FREE(*old_user_input);
+ HTInfoMsg(CANCELLED);
+ ret = FALSE;
+ break;
+ }
+ if (recall && ch == UPARROW_KEY) {
+ 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) {
+ BStrCopy0((*user_input), cp);
+ if (goto_buffer
+ && **old_user_input
+ && !strcmp(*old_user_input, (*user_input)->str)) {
+ _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 = LYgetBString(user_input, FALSE, 0, recall)) < 0) {
+ /*
+ * User cancelled the Goto via ^G. Restore
+ * user_input and break. - FM
+ */
+ BStrCopy0((*user_input), *old_user_input);
+ FREE(*old_user_input);
+ HTInfoMsg(CANCELLED);
+ ret = FALSE;
+ break;
+ }
+ continue;
+ }
+ } else if (recall && ch == DNARROW_KEY) {
+ 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) {
+ BStrCopy0((*user_input), cp);
+ if (goto_buffer && **old_user_input &&
+ !strcmp(*old_user_input, (*user_input)->str)) {
+ _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 = LYgetBString(user_input, FALSE, 0, recall)) < 0) {
+ /*
+ * User cancelled the Goto via ^G. Restore
+ * user_input and break. - FM
+ */
+ BStrCopy0((*user_input), *old_user_input);
+ 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_KEY;
+ } 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_KEY;
+ }
+ }
+ } else {
+ StrAllocCopy(traversal_link_to_add,
+ links[curdoc.link].lname);
+ if (!isLYNXIMGMAP(traversal_link_to_add))
+ *crawl_ok = TRUE;
+ c = RTARROW_KEY;
+ }
+ } 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_KEY;
+ } 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_KEY;
+ }
+ }
+ }
+ CTRACE((tfp, "DoTraversal(%d:%d) -> %s\n",
+ nlinks > 0 ? curdoc.link : 0,
+ nlinks,
+ LYKeycodeToString(c, FALSE)));
+ return c;
+}
+
+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;
+}
+
+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 (isEmpty(links[curdoc.link].l_form->submit_action)) {
+ 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) ||
+ isLYNXCACHE(links[curdoc.link].l_form->submit_action) ||
+#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) ||
+ isLYNXEDITMAP(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_MOUSE_SUBMIT &&
+ !textinput_activated)) ?
+ FOR_PANEL : FOR_INPUT);
+ if (user_mode == NOVICE_MODE &&
+ textinput_activated &&
+ (real_cmd == LYK_ACTIVATE ||
+ real_cmd == LYK_MOUSE_SUBMIT)) {
+ form_noviceline(FormIsReadonly(links[curdoc.link].l_form));
+ }
+ }
+
+ *c = change_form_link(curdoc.link,
+ &newdoc, refresh_screen,
+ FALSE,
+ (real_cmd == LYK_MOUSE_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 == 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_MOUSE_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) &&
+ !((isLYNXEDITMAP(curdoc.address) ||
+ isLYNXKEYMAP(curdoc.address) ||
+ isLYNXCOOKIE(curdoc.address) ||
+ isLYNXCACHE(curdoc.address)) &&
+ !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));
+ /*
+ * 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 (track_internal_links) {
+ /*
+ * 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);
+ }
+ }
+ /*
+ * 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 &&
+ curdoc.address &&
+ !strcmp(lynxjumpfile, curdoc.address))) {
+ LYUserSpecifiedURL = TRUE;
+ } else if (no_filereferer == TRUE &&
+ curdoc.address != NULL &&
+ 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)
+ || isLYNXCACHE(curdoc.address)) {
+ HTuncache_current_document();
+ }
+ }
+ }
+ return 0;
+}
+/*
+ * If the given form link does not point to the requested type, search for
+ * the first link belonging to the form which does. If there are none,
+ * return null.
+ */
+#define SameFormAction(form,submit) \
+ ((submit) \
+ ? (F_SUBMITLIKE((form)->type)) \
+ : ((form)->type == F_RESET_TYPE))
+
+static FormInfo *FindFormAction(FormInfo * given, int submit)
+{
+ FormInfo *result = NULL;
+ FormInfo *fi;
+ int i;
+
+ if (given == NULL) {
+ HTAlert(LINK_NOT_IN_FORM);
+ } else if (SameFormAction(given, submit)) {
+ result = given;
+ } else {
+ for (i = 0; i < nlinks; i++) {
+ if ((fi = links[i].l_form) != 0 &&
+ fi->number == given->number &&
+ (SameFormAction(fi, submit))) {
+ result = fi;
+ break;
+ }
+ }
+ }
+ return result;
+}
+
+static FormInfo *MakeFormAction(FormInfo * given, int submit)
+{
+ FormInfo *result = 0;
+
+ if (given != 0) {
+ result = typecalloc(FormInfo);
+
+ if (result == NULL)
+ outofmem(__FILE__, "MakeFormAction");
+
+ *result = *given;
+ if (submit) {
+ if (result->submit_action == 0) {
+ PerFormInfo *pfi = HText_PerFormInfo(result->number);
+
+ *result = pfi->data;
+ }
+ result->type = F_SUBMIT_TYPE;
+ } else {
+ result->type = F_RESET_TYPE;
+ }
+ result->number = given->number;
+ }
+ return result;
+}
+
+static void handle_LYK_SUBMIT(int cur, DocInfo *doc, BOOLEAN *refresh_screen)
+{
+ FormInfo *form = FindFormAction(links[cur].l_form, 1);
+ FormInfo *make = NULL;
+ char *save_submit_action = NULL;
+
+ if (form == 0) {
+ make = MakeFormAction(links[cur].l_form, 1);
+ form = make;
+ }
+
+ if (form != 0) {
+ StrAllocCopy(save_submit_action, form->submit_action);
+ form->submit_action = HTPrompt(EDIT_SUBMIT_URL, form->submit_action);
+
+ if (isEmpty(form->submit_action) ||
+ (!isLYNXCGI(form->submit_action) &&
+ StrNCmp(form->submit_action, "http", 4))) {
+ HTUserMsg(FORM_ACTION_NOT_HTTP_URL);
+ } else {
+ HTInfoMsg(SUBMITTING_FORM);
+ HText_SubmitForm(form, doc, form->name, form->value);
+ *refresh_screen = TRUE;
+ }
+
+ StrAllocCopy(form->submit_action, save_submit_action);
+ FREE(make);
+ }
+}
+
+static void handle_LYK_RESET(int cur, BOOLEAN *refresh_screen)
+{
+ FormInfo *form = FindFormAction(links[cur].l_form, 0);
+ FormInfo *make = NULL;
+
+ if (form == 0) {
+ make = MakeFormAction(links[cur].l_form, 0);
+ form = make;
+ }
+
+ if (form != 0) {
+ HTInfoMsg(RESETTING_FORM);
+ HText_ResetForm(form);
+ *refresh_screen = TRUE;
+ FREE(make);
+ }
+}
+
+#ifdef USE_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 /* USE_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) &&
+ !isLYNXCACHE(curdoc.address) &&
+ !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) &&
+ !isLYNXCACHE(links[curdoc.link].lname) &&
+ !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(bstring **user_input)
+{
+ LYKeymapCode ch;
+ Kcmd *mp;
+ char *src, *tmp;
+
+ BStrCopy0((*user_input), "");
+ _statusline(": ");
+ if (LYgetBString(user_input, FALSE, 0, RECALL_CMD) >= 0) {
+ src = LYSkipBlanks((*user_input)->str);
+ 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';
+ } else if ((cp = strrchr(VMSdir, ':')) != NULL) {
+ *(cp + 1) = '\0';
+ }
+ }
+ 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 (isEmpty(links[curdoc.link].l_form->submit_action) ||
+ isLYNXOPTIONS(links[curdoc.link].l_form->submit_action)) {
+ if (*old_c != real_c) {
+ *old_c = real_c;
+ HTUserMsg(NO_DOWNLOAD_SPECIAL);
+ }
+ return 0;
+ }
+ HTOutputFormat = 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 &&
+ !LYstrstr(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 = 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) ||
+ isLYNXCACHE(links[curdoc.link].lname) ||
+ 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));
+ /*
+ * Might be an internal link in the same doc from a POST form. If
+ * so, don't free the content. - kw
+ */
+ if (track_internal_links) {
+ if (links[curdoc.link].type != WWW_INTERN_LINK_TYPE) {
+ LYFreePostData(&newdoc);
+ FREE(newdoc.bookmark);
+ newdoc.isHEAD = FALSE;
+ newdoc.safe = FALSE;
+ }
+ } 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)) {
+ LYFreePostData(&newdoc);
+ FREE(newdoc.bookmark);
+ newdoc.isHEAD = FALSE;
+ newdoc.safe = FALSE;
+ }
+ }
+ newdoc.internal_link = FALSE;
+ newdoc.link = (user_mode == NOVICE_MODE) ? 1 : 0;
+ HTOutputFormat = 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) {
+ HandleForwardWraparound();
+ }
+}
+
+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) {
+ HandleForwardWraparound();
+ }
+}
+
+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_EDITTEXTAREA;
+ 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,
+ bstring **user_input,
+ 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 string, and load the current
+ * document's address.
+ */
+ StrAllocCopy(*old_user_input, (*user_input)->str);
+ BStrCopy0((*user_input), curdoc.address);
+
+ /*
+ * 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 = LYgetBString(user_input, FALSE, 0, RECALL_URL)) >= 0) &&
+ !isBEmpty(*user_input) &&
+ strcmp((*user_input)->str, curdoc.address)) {
+ LYTrimAllStartfile((*user_input)->str);
+ if (!isBEmpty(*user_input)) {
+ return 2;
+ }
+ }
+ /*
+ * User cancelled via ^G, a full deletion, or not modifying the URL. - FM
+ */
+ HTInfoMsg(CANCELLED);
+ BStrCopy0((*user_input), *old_user_input);
+ 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 &&
+ !FormIsReadonly(links[curdoc.link].l_form) &&
+ LinkIsTextLike(curdoc.link)) {
+ *cshelpfile = STR_LYNXEDITMAP;
+ }
+}
+
+static void handle_LYK_EDITMAP(int *old_c,
+ int real_c)
+{
+ if (*old_c != real_c) {
+ *old_c = real_c;
+ set_address(&newdoc, STR_LYNXEDITMAP);
+ StrAllocCopy(newdoc.title, CURRENT_EDITMAP_TITLE);
+ LYFreePostData(&newdoc);
+ FREE(newdoc.bookmark);
+ newdoc.isHEAD = FALSE;
+ newdoc.safe = FALSE;
+ newdoc.internal_link = FALSE;
+#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_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);
+ }
+ } else if (LinkIsTextarea(curdoc.link)) {
+ /*
+ * if the current link is in a form TEXTAREA, it requires handling
+ * for the possible multiple lines.
+ */
+
+ /* stop screen */
+ stop_curses();
+
+ (void) HText_EditTextArea(&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 if (LinkIsTextLike(curdoc.link)) {
+ /*
+ * other text fields are single-line
+ */
+ stop_curses();
+ HText_EditTextField(&links[curdoc.link]);
+ start_curses();
+ *refresh_screen = TRUE;
+ } else {
+
+ HTInfoMsg(NOT_IN_TEXTAREA_NOEDIT);
+ }
+}
+
+static int handle_LYK_ELGOTO(int *ch,
+ bstring **user_input,
+ 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) &&
+ (isEmpty(links[curdoc.link].l_form->submit_action))) {
+ /*
+ * 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 string, and load the current link's
+ * address. - FM
+ */
+ StrAllocCopy(*old_user_input, (*user_input)->str);
+ BStrCopy0((*user_input),
+ ((links[curdoc.link].type == WWW_FORM_LINK_TYPE)
+ ? links[curdoc.link].l_form->submit_action
+ : links[curdoc.link].lname));
+ /*
+ * Offer the current link's URL for editing. - FM
+ */
+ _statusline(EDIT_CURLINK_URL);
+ if (((*ch = LYgetBString(user_input, FALSE, 0, RECALL_URL)) >= 0) &&
+ !isBEmpty(*user_input) &&
+ strcmp((*user_input)->str,
+ ((links[curdoc.link].type == WWW_FORM_LINK_TYPE)
+ ? links[curdoc.link].l_form->submit_action
+ : links[curdoc.link].lname))) {
+ LYTrimAllStartfile((*user_input)->str);
+ if (!isBEmpty(*user_input)) {
+ return 2;
+ }
+ }
+ /*
+ * User cancelled via ^G, a full deletion, or not modifying the URL. - FM
+ */
+ HTInfoMsg(CANCELLED);
+ BStrCopy0((*user_input), *old_user_input);
+ 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,
+ bstring **user_input,
+ 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)->str);
+ if (!goto_buffer)
+ BStrCopy0((*user_input), "");
+
+ *URLTotal = (Goto_URLs ? HTList_count(Goto_URLs) : 0);
+ if (goto_buffer && !isBEmpty(*user_input)) {
+ *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 = LYgetBString(user_input, FALSE, 0, *recall)) < 0) {
+ /*
+ * User cancelled the Goto via ^G. Restore user_input and
+ * break. - FM
+ */
+ BStrCopy0((*user_input), *old_user_input);
+ 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 &&
+ FormIsReadonly(links[curdoc.link].l_form)) {
+ 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(int 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
+#ifdef USE_PROGRAM_DIR
+ if (is_url(indexfile) == 0) {
+ char *tmp = NULL;
+
+ HTSprintf0(&tmp, "%s\\%s", program_dir, indexfile);
+ FREE(indexfile);
+ LYLocalFileToURL(&indexfile, tmp);
+ FREE(tmp);
+ }
+#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,
+ int 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 const char *hexy = "0123456789ABCDEF";
+
+#define HEX(n) hexy[(n) & 0xf]
+/*
+ * URL-encode a parameter which can then be appended to a URI.
+ * RFC-3986 lists reserved characters, which should be encoded.
+ */
+static char *urlencode(char *str)
+{
+ char *result = NULL;
+ char *ptr;
+ int ch;
+
+ if (str != NULL) {
+ result = malloc(strlen(str) * 3 + 1);
+ ptr = result;
+
+ if (result == NULL)
+ outofmem(__FILE__, "urlencode");
+
+ while ((ch = UCH(*str++)) != 0) {
+ if (ch == ' ') {
+ *ptr = '+';
+ ptr++;
+ } else if (ch > 127 ||
+ StrChr(":/?#[]@!$&'()*+,;=", ch) != 0) {
+ *ptr++ = '%';
+ *ptr++ = HEX(ch >> 4);
+ *ptr++ = HEX(ch);
+ } else {
+ *ptr++ = (char) ch;
+ }
+ }
+ *ptr = '\0';
+ }
+
+ return result;
+}
+
+/*
+ * Fill in "%s" marker(s) in the url_template by prompting the user for the
+ * values.
+ */
+static BOOLEAN check_JUMP_param(char **url_template)
+{
+ int param = 1;
+ char *subs;
+ char *result = *url_template;
+ char *encoded = NULL;
+ int code = TRUE;
+ bstring *input = NULL;
+
+ CTRACE((tfp, "check_JUMP_param: %s\n", NONNULL(result)));
+
+ while (result != NULL && (subs = strstr(result, "%s")) != 0) {
+ char prompt[MAX_LINE];
+ RecallType recall = NORECALL;
+
+ CTRACE((tfp, "Prompt for query param%d: %s\n", param, result));
+
+ sprintf(prompt, gettext("Query parameter %d: "), param++);
+ statusline(prompt);
+ BStrCopy0(input, "");
+
+ if (encoded)
+ FREE(encoded);
+
+ if (LYgetBString(&input, FALSE, 0, recall) < 0) {
+ /*
+ * cancelled via ^G
+ */
+ HTInfoMsg(CANCELLED);
+ code = FALSE;
+ break;
+ } else if ((encoded = urlencode(input->str)) != NULL && *encoded != '\0') {
+ int subs_at = (int) (subs - result);
+ int fill_in = (int) strlen(encoded) - 2;
+ size_t have = strlen(result);
+ size_t want = strlen(encoded) + have - 1;
+ int n;
+ char *update = realloc(result, want + 1);
+
+ if (update == 0) {
+ HTInfoMsg(NOT_ENOUGH_MEMORY);
+ code = FALSE;
+ break;
+ }
+
+ CTRACE((tfp, " reply: %s\n", input->str));
+ CTRACE((tfp, " coded: %s\n", encoded));
+
+ result = update;
+ result[want] = '\0';
+ for (n = (int) want; (n - fill_in) >= subs_at; --n) {
+ result[n] = result[n - fill_in];
+ }
+ for (n = subs_at; encoded[n - subs_at] != '\0'; ++n) {
+ result[n] = encoded[n - subs_at];
+ }
+ CTRACE((tfp, " subst: %s\n", result));
+ } else {
+ HTInfoMsg(CANCELLED);
+ code = FALSE;
+ break;
+ }
+ }
+ BStrFree(input);
+ FREE(encoded);
+ *url_template = result;
+ return (BOOLEAN) code;
+}
+
+static void fill_JUMP_Params(char **addressp)
+{
+ if (LYJumpFileURL) {
+ check_JUMP_param(addressp);
+ }
+}
+
+static BOOLEAN handle_LYK_JUMP(int c,
+ bstring **user_input,
+ 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)->str);
+ *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_KEY;
+ return TRUE;
+ }
+ FREE(*old_user_input);
+ HTUserMsg(NO_RANDOM_URLS_YET);
+ return FALSE;
+ }
+ ret = HTParse((ret + 3), startfile, PARSE_ALL);
+ BStrCopy0((*user_input), ret);
+ FREE(ret);
+ return TRUE;
+ }
+#endif /* PERMIT_GOTO_FROM_JUMP */
+ ret = HTParse(ret, startfile, PARSE_ALL);
+ if (!LYTrimStartfile(ret)) {
+ LYRemoveBlanks((*user_input)->str);
+ }
+ if (check_JUMP_param(&ret)) {
+ set_address(&newdoc, ret);
+ StrAllocCopy(lynxjumpfile, ret);
+ LYFreePostData(&newdoc);
+ FREE(newdoc.bookmark);
+ newdoc.isHEAD = FALSE;
+ newdoc.safe = FALSE;
+ newdoc.internal_link = FALSE;
+ LYUserSpecifiedURL = TRUE;
+ }
+ FREE(ret);
+ } 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(FALSE, curdoc.link, prev_target->str);
+#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)))) {
+ /*
+ * 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 */
+#else
+ (void) refresh_screen;
+#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;
+ }
+#else
+ (void) cmd;
+#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(FALSE, curdoc.link, prev_target->str);
+#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) {
+ HandleForwardWraparound();
+ }
+}
+
+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) {
+ HandleReverseWraparound();
+ }
+}
+
+#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 = REAL_RELOAD;
+
+ 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(FALSE, curdoc.link);
+ break;
+ }
+ }
+ if (!found) {
+ if (tagged == NULL)
+ tagged = HTList_new();
+ tagname = NULL;
+ StrAllocCopy(tagname, links[curdoc.link].lname);
+ HTList_addObject(tagged, tagname);
+ tagflag(TRUE, 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) {
+ HandleReverseWraparound();
+ }
+}
+
+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) {
+ HandleReverseWraparound();
+ }
+}
+
+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) (!isBEmpty(prev_target) &&
+ 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->str);
+ 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.
+ */
+ BStrCopy0(prev_target, "");
+ }
+ found = textsearch(&curdoc, &prev_target,
+ (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 ((LYcase_sensitive && 0 != strcmp(prev_target->str,
+ remember_old_target)) ||
+ (!LYcase_sensitive && 0 != strcasecomp8(prev_target->str,
+ 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,
+ 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));
+ /*
+ * 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 (track_internal_links) {
+ 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->str);
+ 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);
+ }
+ }
+ /*
+ * 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
+ */
+ HTSprintf0(&temp, LINK_ALREADY_CURRENT, number);
+ HTUserMsg(temp);
+ 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 {
+ HTSprintf0(&temp, ALREADY_AT_PAGE, number);
+ HTUserMsg(temp);
+ 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 bstring *buf = NULL;
+ char *p = NULL;
+
+ if (no_chdir) {
+ HTUserMsg(CHDIR_DISABLED);
+ return;
+ }
+
+ _statusline(gettext("cd to:"));
+ if (LYgetBString(&buf, FALSE, 0, NORECALL) < 0 || isBEmpty(buf)) {
+ HTInfoMsg(CANCELLED);
+ return;
+ }
+
+ if (LYIsTilde(buf->str[0]) &&
+ (LYIsPathSep(buf->str[1]) || buf->str[1] == '\0')) {
+ HTSprintf0(&p, "%s%s", Home_Dir(), buf->str + 1);
+ } else {
+ StrAllocCopy(p, buf->str);
+ }
+
+ 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);
+}
+
+static void handle_LYK_PWD(void)
+{
+ char buffer[LY_MAXPATH];
+ int save_secs = InfoSecs;
+ BOOLEAN save_wait = no_pause;
+
+ if (Secs2SECS(save_secs) < 1)
+ InfoSecs = SECS2Secs(1);
+ no_pause = FALSE;
+
+ HTInfoMsg(Current_Dir(buffer));
+
+ InfoSecs = save_secs;
+ no_pause = save_wait;
+}
+#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;
+ int code = FALSE;
+
+ CTRACE((tfp, "Entering handle_LYK_LINEWRAP_TOGGLE\n"));
+ if (LYwin != stdscr) {
+ /* 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, (int) 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) {
+ CTRACE((tfp,
+ "...setting LYwideLines %d, LYtableCols %d (have %d and %d)\n",
+ c, wrap[c],
+ LYwideLines,
+ LYtableCols));
+
+ LYwideLines = c;
+ LYtableCols = wrap[c];
+
+ if (LYwideLines == 0)
+ LYshiftWin = 0;
+ *flag = TRUE;
+ HTUserMsg(LYwideLines ? LINEWRAP_OFF : LINEWRAP_ON);
+ code = reparse_or_reload(cmd);
+ }
+ }
+ return (BOOLEAN) code;
+}
+#endif
+
+#ifdef USE_MAXSCREEN_TOGGLE
+static BOOLEAN handle_LYK_MAXSCREEN_TOGGLE(int *cmd)
+{
+ static int flag = 0;
+
+ CTRACE((tfp, "Entering handle_LYK_MAXSCREEN_TOGGLE\n"));
+ if (flag) {
+ CTRACE((tfp, "Calling recoverWindowSize()\n"));
+ recoverWindowSize();
+ flag = 0;
+ } else {
+ CTRACE((tfp, "Calling maxmizeWindowSize()\n"));
+ maxmizeWindowSize();
+ flag = 1;
+ }
+ return reparse_or_reload(cmd);
+}
+#endif
+
+#ifdef LY_FIND_LEAKS
+#define CleanupMainLoop() \
+ BStrFree(prev_target); \
+ BStrFree(user_input_buffer)
+#else
+#define CleanupMainLoop() /* nothing */
+#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;
+ bstring *user_input_buffer = NULL;
+ 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(STR_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
+ BStrCopy0(user_input_buffer, "");
+ BStrCopy0(prev_target, "");
+#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;
+
+ /*
+ * 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 (track_internal_links &&
+ curdoc.internal_link &&
+ !are_phys_different(&curdoc, &newdoc)) {
+ LYinternal_flag = TRUE;
+ LYoverride_no_cache = TRUE;
+ LYforce_no_cache = FALSE;
+ try_internal = TRUE;
+ } else {
+ /*
+ * 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).
+ */
+ BStrCopy0(prev_target, ""); /* 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;
+
+ if (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;
+ fill_JUMP_Params(&newdoc.address);
+ getresult = getfile(&newdoc, &tmpNewline);
+ if (!reloading && !popped_doc && (tmpNewline >= 0)) {
+ LYSetNewline(tmpNewline);
+ } else {
+ newdoc.link = tmpDocInfo.link;
+ }
+ }
+ } else {
+ tmpDocInfo = newdoc;
+ tmpNewline = -1;
+ fill_JUMP_Params(&newdoc.address);
+ getresult = getfile(&newdoc, &tmpNewline);
+ if (!reloading && !popped_doc && (tmpNewline >= 0)) {
+ LYSetNewline(tmpNewline);
+ } else {
+ newdoc.link = tmpDocInfo.link;
+ }
+ }
+
+#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)) &&
+ !isLYNXCACHE(NonNull(newdoc.address)) &&
+ !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.
+ */
+ CleanupMainLoop();
+ 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 {
+ CleanupMainLoop();
+ 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 = (unsigned) 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);
+ CleanupMainLoop();
+ 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);
+ }
+ CleanupMainLoop();
+ 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, STR_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->str);
+
+#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->str);
+
+#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 &&
+ LinkIsTextLike(curdoc.link));
+
+ 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 || user_mode == MINIMAL_MODE) {
+ p = curdoc.title;
+ } else {
+ p = links[curdoc.link].lname;
+ }
+
+ if (strlen(p) < ((sizeof(sjis_buff) / 2) - 1)) {
+ 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 || user_mode == MINIMAL_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(TRUE, curdoc.link, prev_target->str);
+#ifndef INACTIVE_INPUT_STYLE_VH
+ } else if (!textinput_activated) {
+ LYhighlight(TRUE, curdoc.link, prev_target->str);
+#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 {
+#ifdef UNIX
+ FILE *fp = (dump_output_immediately
+ ? stderr
+ : stdout);
+
+#else
+ FILE *fp = stdout;
+#endif
+ if (!dump_output_immediately)
+ cleanup();
+ fprintf(fp,
+ gettext("Fatal error - could not open output file %s\n"),
+ cfile);
+ CleanupMainLoop();
+ 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(FormIsReadonly(links[curdoc.link].l_form));
+ }
+ 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') &&
+ LinkIsTextLike(curdoc.link) &&
+ !textfields_need_activation) {
+ c = DO_NOTHING;
+ }
+#ifdef TEXTFIELDS_MAY_NEED_ACTIVATION
+ } else if (LinkIsTextarea(curdoc.link)
+ && textfields_need_activation
+ && !FormIsReadonly(links[curdoc.link].l_form)
+ && 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) {
+ CleanupMainLoop();
+ 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;
+
+ CTRACE((tfp, "Handling key as %s\n",
+ ((LYKeycodeToKcmd((LYKeymapCode) cmd) != 0)
+ ? LYKeycodeToKcmd((LYKeymapCode) cmd)->name
+ : "unknown")));
+ switch (cmd) {
+ case -1:
+ HTUserMsg(COMMAND_UNKNOWN);
+ break;
+ case 0: /* unmapped character */
+ default:
+ if (curdoc.link >= 0 && curdoc.link < nlinks &&
+ LinkIsTextLike(curdoc.link)) {
+
+#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, &old_c, real_c, &try_internal);
+ break;
+
+ case LYK_SOURCE: /* toggle view source mode */
+ handle_LYK_SOURCE(&ownerS_address);
+ break;
+
+ case LYK_CHANGE_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()) {
+ CleanupMainLoop();
+ return (EXIT_SUCCESS);
+ }
+ break;
+
+ case LYK_ABORT: /* don't ask the user about quitting */
+ CleanupMainLoop();
+ 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 = typeMallocn(char, len);
+
+ LYStrNCpy(buf, (const char *) s, (e - s));
+ 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 != '\n')
+ *t = *s1;
+ t++, s1++;
+ }
+ }
+ } else
+ *t++ = *s++;
+ }
+ *t = '\0';
+ get_clip_release();
+ BStrCopy0(user_input_buffer, buf);
+ do_check_goto_URL(&user_input_buffer, &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:
+ CleanupMainLoop();
+ 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_MOUSE_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_SUBMIT:
+ handle_LYK_SUBMIT(curdoc.link, &newdoc, &refresh_screen);
+ break;
+
+ case LYK_RESET:
+ handle_LYK_RESET(curdoc.link, &refresh_screen);
+ 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_EDITTEXTAREA: /* use external editor on a TEXTAREA - KED */
+ handle_LYK_EDIT_TEXTAREA(&refresh_screen, &old_c, real_c);
+ break;
+
+ case LYK_GROWTEXTAREA: /* add new lines to bottom of TEXTAREA - KED */
+ handle_LYK_GROW_TEXTAREA(&refresh_screen);
+ break;
+
+ case LYK_INSERTFILE: /* 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 USE_ADDRLIST_PAGE
+ case LYK_ADDRLIST: /* always list URL's (only) */
+ if (handle_LYK_ADDRLIST(&cmd))
+ goto new_cmd;
+ break;
+#endif /* USE_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_EDITMAP:
+ handle_LYK_EDITMAP(&old_c, real_c);
+ 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;
+ case LYK_PWD:
+ handle_LYK_PWD();
+ 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
+
+#ifdef USE_MAXSCREEN_TOGGLE
+ case LYK_MAXSCREEN_TOGGLE:
+ if (handle_LYK_MAXSCREEN_TOGGLE(&cmd))
+ 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
+ */
+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);
+}
+
+/*
+ * 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 *mycopy = NULL;
+ char *old;
+ HTList *cur;
+
+ if (isEmpty(url))
+ return;
+
+ CTRACE((tfp, "HTAddGotoURL %s\n", url));
+ StrAllocCopy(mycopy, url);
+
+ if (!Goto_URLs) {
+ Goto_URLs = HTList_new();
+#ifdef LY_FIND_LEAKS
+ atexit(HTGotoURLs_free);
+#endif
+ HTList_addObject(Goto_URLs, mycopy);
+ return;
+ }
+
+ cur = Goto_URLs;
+ while (NULL != (old = (char *) HTList_nextObject(cur))) {
+ if (!strcmp(old, mycopy)) {
+ HTList_removeObject(Goto_URLs, old);
+ FREE(old);
+ break;
+ }
+ }
+ HTList_addObject(Goto_URLs, mycopy);
+
+ 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 || user_mode == MINIMAL_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 ((user_mode == MINIMAL_MODE) && nlinks > 0) {
+ /*
+ * no URL
+ */
+ status_link("", 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 if (user_mode != MINIMAL_MODE) {
+ _statusline(HELP);
+ } else {
+ _statusline("");
+ }
+
+ /* 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);
+ }
+ LYParkCursor();
+
+ 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, int 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();
+
+ if (buf != 0) {
+#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(const char *curlink_name,
+ int show_more,
+ int 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 - 10) {
+ _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);
+}