1 /*
2  * $LynxId: LYMainLoop.c,v 1.217 2013/05/05 20:36:20 tom Exp $
3  */
4 #include <HTUtils.h>
5 #include <HTAccess.h>
6 #include <HTParse.h>
7 #include <HTList.h>
8 #include <HTML.h>
9 #include <HTFTP.h>
10 #include <HTFile.h>
11 #include <HTTP.h>
12 #include <HTAABrow.h>
13 #include <HTNews.h>
14 #include <LYCurses.h>
15 #include <LYStyle.h>
16 #include <LYGlobalDefs.h>
17 #include <HTAlert.h>
18 #include <LYUtils.h>
19 #include <GridText.h>
20 #include <LYStrings.h>
21 #include <LYOptions.h>
22 #include <LYSignal.h>
23 #include <LYGetFile.h>
24 #include <HTForms.h>
25 #include <LYSearch.h>
26 #include <LYClean.h>
27 #include <LYHistory.h>
28 #include <LYPrint.h>
29 #include <LYMail.h>
30 #include <LYEdit.h>
31 #include <LYShowInfo.h>
32 #include <LYBookmark.h>
33 #include <LYKeymap.h>
34 #include <LYJump.h>
35 #include <LYDownload.h>
36 #include <LYList.h>
37 #include <LYMap.h>
38 #include <LYTraversal.h>
39 #include <LYCharSets.h>
40 #include <LYCharUtils.h>
41 #include <LYCookie.h>
42 #include <LYMainLoop.h>
43 #include <LYPrettySrc.h>
44 
45 #ifdef USE_SESSIONS
46 #include <LYSession.h>
47 #endif
48 
49 #ifdef KANJI_CODE_OVERRIDE
50 #include <HTCJK.h>
51 #endif
52 
53 #define LinkIsTextarea(linkNumber) \
54 		(links[linkNumber].type == WWW_FORM_LINK_TYPE && \
55 		 links[linkNumber].l_form->type == F_TEXTAREA_TYPE)
56 
57 #define LinkIsTextLike(linkNumber) \
58 	     (links[linkNumber].type == WWW_FORM_LINK_TYPE && \
59 	      F_TEXTLIKE(links[linkNumber].l_form->type))
60 
61 #ifdef KANJI_CODE_OVERRIDE
str_kcode(HTkcode code)62 char *str_kcode(HTkcode code)
63 {
64     char *p;
65     static char buff[8];
66 
67     if (current_char_set == TRANSPARENT) {
68 	p = "THRU";
69     } else if (!LYRawMode) {
70 	p = "RAW";
71     } else {
72 	switch (code) {
73 	case NOKANJI:
74 	    p = "AUTO";
75 	    break;
76 
77 	case EUC:
78 	    p = "EUC+";
79 	    break;
80 
81 	case SJIS:
82 	    p = "SJIS";
83 	    break;
84 
85 	case JIS:
86 	    p = " JIS";
87 	    break;
88 
89 	default:
90 	    p = " ???";
91 	    break;
92 	}
93     }
94 
95     if (no_table_center) {
96 	buff[0] = '!';
97 	strcpy(buff + 1, p);
98     } else {
99 	strcpy(buff, p);
100     }
101 
102     return buff;
103 }
104 #endif
105 
106 #ifdef WIN_EX
107 
str_sjis(char * to,char * from)108 static char *str_sjis(char *to, char *from)
109 {
110     if (!LYRawMode) {
111 	strcpy(to, from);
112 #ifdef KANJI_CODE_OVERRIDE
113     } else if (last_kcode == EUC) {
114 	EUC_TO_SJIS(from, to);
115     } else if (last_kcode == SJIS) {
116 	strcpy(to, from);
117 #endif
118     } else {
119 	TO_SJIS((unsigned char *) from, (unsigned char *) to);
120     }
121     return to;
122 }
123 
set_ws_title(char * str)124 static void set_ws_title(char *str)
125 {
126     SetConsoleTitle(str);
127 }
128 
129 #endif /* WIN_EX */
130 
131 #if defined(USE_EXTERNALS) || defined(WIN_EX)
132 #include <LYExtern.h>
133 #endif
134 
135 #ifdef __EMX__
136 #include <io.h>
137 #endif
138 
139 #ifdef DIRED_SUPPORT
140 #include <LYLocal.h>
141 #include <LYUpload.h>
142 #endif /* DIRED_SUPPORT */
143 
144 #include <LYexit.h>
145 #include <LYLeaks.h>
146 
147 /* two constants: */
148 HTLinkType *HTInternalLink = 0;
149 HTAtom *WWW_SOURCE = 0;
150 
151 #define NONINTERNAL_OR_PHYS_DIFFERENT(p,n) \
152 	((track_internal_links && \
153 	 (!curdoc.internal_link || are_phys_different(p,n))) || \
154 	are_different(p,n))
155 
156 #define NO_INTERNAL_OR_DIFFERENT(c,n) \
157 	(track_internal_links || are_different(c,n))
158 
159 static void exit_immediately_with_error_message(int state, int first_file);
160 static void status_link(char *curlink_name, int show_more, int show_indx);
161 static void show_main_statusline(const LinkInfo curlink, int for_what);
162 static void form_noviceline(int);
163 static int are_different(DocInfo *doc1, DocInfo *doc2);
164 
165 static int are_phys_different(DocInfo *doc1, DocInfo *doc2);
166 
167 #define FASTTAB
168 
sametext(char * een,char * twee)169 static int sametext(char *een,
170 		    char *twee)
171 {
172     if (een && twee)
173 	return (strcmp(een, twee) == 0);
174     return TRUE;
175 }
176 
177 HTList *Goto_URLs = NULL;	/* List of Goto URLs */
178 
179 char *LYRequestTitle = NULL;	/* newdoc.title in calls to getfile() */
180 char *LYRequestReferer = NULL;	/* Referer, may be set in getfile() */
181 
182 static bstring *prev_target = NULL;
183 
184 #ifdef DISP_PARTIAL
185 BOOLEAN display_partial = FALSE;	/* could be enabled in HText_new() */
186 int NumOfLines_partial = 0;	/* number of lines displayed in partial mode */
187 #endif
188 
189 static int Newline = 0;
190 static DocInfo newdoc;
191 static DocInfo curdoc;
192 static char *traversal_host = NULL;
193 static char *traversal_link_to_add = NULL;
194 static char *owner_address = NULL;	/* Holds the responsible owner's address     */
195 static char *ownerS_address = NULL;	/* Holds owner's address during source fetch */
196 
197 #ifdef TEXTFIELDS_MAY_NEED_ACTIVATION
198 static BOOL textinput_activated = FALSE;
199 
200 #else
201 #define textinput_activated TRUE	/* a current text input is always active */
202 #endif
203 #ifdef INACTIVE_INPUT_STYLE_VH
204 BOOL textinput_redrawn = FALSE;
205 
206     /*must be public since used in LYhighlight(..) */
207 #endif
208 
209 #ifdef LY_FIND_LEAKS
210 /*
211  * Function for freeing allocated mainloop() variables.  - FM
212  */
free_mainloop_variables(void)213 static void free_mainloop_variables(void)
214 {
215     LYFreeDocInfo(&newdoc);
216     LYFreeDocInfo(&curdoc);
217 
218 #ifdef USE_COLOR_STYLE
219     FREE(curdoc.style);
220     FREE(newdoc.style);
221 #endif
222     FREE(traversal_host);
223     FREE(traversal_link_to_add);
224     FREE(owner_address);
225     FREE(ownerS_address);
226 #ifdef DIRED_SUPPORT
227     clear_tags();
228     reset_dired_menu();
229 #endif /* DIRED_SUPPORT */
230     FREE(WWW_Download_File);	/* LYGetFile.c/HTFWriter.c */
231     FREE(LYRequestReferer);
232 
233     return;
234 }
235 #endif /* LY_FIND_LEAKS */
236 
237 #ifndef NO_LYNX_TRACE
TracelogOpenFailed(void)238 static void TracelogOpenFailed(void)
239 {
240     WWW_TraceFlag = FALSE;
241     if (LYCursesON) {
242 	HTUserMsg(TRACELOG_OPEN_FAILED);
243     } else {
244 	fprintf(stderr, "%s\n", TRACELOG_OPEN_FAILED);
245 	exit_immediately(EXIT_FAILURE);
246     }
247 }
248 
LYReopenTracelog(BOOLEAN * trace_flag_ptr)249 static BOOLEAN LYReopenTracelog(BOOLEAN *trace_flag_ptr)
250 {
251     CTRACE((tfp, "\nTurning off TRACE for fetch of log.\n"));
252     LYCloseTracelog();
253     if ((LYTraceLogFP = LYAppendToTxtFile(LYTraceLogPath)) == NULL) {
254 	TracelogOpenFailed();
255 	return FALSE;
256     }
257     if (TRACE) {
258 	WWW_TraceFlag = FALSE;
259 	*trace_flag_ptr = TRUE;
260     }
261     return TRUE;
262 }
263 
turn_trace_back_on(BOOLEAN * trace_flag_ptr)264 static void turn_trace_back_on(BOOLEAN *trace_flag_ptr)
265 {
266     if (*trace_flag_ptr == TRUE) {
267 	WWW_TraceFlag = TRUE;
268 	*trace_flag_ptr = FALSE;
269 	fprintf(tfp, "Turning TRACE back on.\n\n");
270     }
271 }
272 #else
273 #define LYReopenTracelog(flag) TRUE
274 #define turn_trace_back_on(flag)	/*nothing */
275 #endif /* NO_LYNX_TRACE */
276 
TraceFP(void)277 FILE *TraceFP(void)
278 {
279 #ifndef NO_LYNX_TRACE
280     if (LYTraceLogFP != 0) {
281 	return LYTraceLogFP;
282     }
283 #endif /* NO_LYNX_TRACE */
284     return stderr;
285 }
286 
LYOpenTraceLog(void)287 BOOLEAN LYOpenTraceLog(void)
288 {
289 #ifndef NO_LYNX_TRACE
290     if (TRACE && LYUseTraceLog && LYTraceLogFP == NULL) {
291 	/*
292 	 * If we can't open it for writing, give up.  Otherwise, on VMS close
293 	 * it, delete it and any versions from previous sessions so they don't
294 	 * accumulate, and open it again.  - FM
295 	 */
296 	if ((LYTraceLogFP = LYNewTxtFile(LYTraceLogPath)) == NULL) {
297 	    TracelogOpenFailed();
298 	    return FALSE;
299 	}
300 #ifdef VMS
301 	LYCloseTracelog();
302 	HTSYS_remove(LYTraceLogPath);
303 	if ((LYTraceLogFP = LYNewTxtFile(LYTraceLogPath)) == NULL) {
304 	    TracelogOpenFailed();
305 	    return FALSE;
306 	}
307 #endif /* VMS */
308 	fflush(stdout);
309 	fflush(stderr);
310 	fprintf(tfp, "\t\t%s (%s)\n\n", LYNX_TRACELOG_TITLE, LYNX_VERSION);
311 	/*
312 	 * If TRACE is on, indicate whether the anonymous restrictions are set.
313 	 * - FM, LP, kw
314 	 *
315 	 * This is only a summary for convenience - it doesn't take the case of
316 	 * individual -restrictions= options into account.  - kw
317 	 */
318 	if (LYValidate) {
319 	    if (LYRestricted && had_restrictions_default) {
320 		CTRACE((tfp,
321 			"Validate and some anonymous restrictions are set.\n"));
322 	    } else if (had_restrictions_default) {
323 		CTRACE((tfp,
324 			"Validate restrictions set, restriction \"default\" was given.\n"));
325 	    } else if (LYRestricted) {
326 		CTRACE((tfp,
327 			"Validate restrictions set, additional anonymous restrictions ignored.\n"));
328 	    } else {
329 		CTRACE((tfp, "Validate restrictions are set.\n"));
330 	    }
331 	    /* But none of the above can actually happen, since there should
332 	     * never be a Trace Log with -validate.  If it appears in a log
333 	     * file something went wrong! */
334 	} else if (LYRestricted) {
335 	    if (had_restrictions_all) {
336 		CTRACE((tfp,
337 			"Anonymous restrictions set, restriction \"all\" was given.\n"));
338 	    } else {
339 		CTRACE((tfp, "Anonymous restrictions are set.\n"));
340 	    }
341 	} else if (had_restrictions_all && had_restrictions_default) {
342 	    CTRACE((tfp, "Restrictions \"all\" and \"default\" were given.\n"));
343 	} else if (had_restrictions_default) {
344 	    CTRACE((tfp, "Restriction \"default\" was given.\n"));
345 	} else if (had_restrictions_all) {
346 	    CTRACE((tfp, "\"all\" restrictions are set.\n"));
347 	}
348     }
349 #endif /* NO_LYNX_TRACE */
350     return TRUE;
351 }
352 
LYCloseTracelog(void)353 void LYCloseTracelog(void)
354 {
355 #ifndef NO_LYNX_TRACE
356     if (LYTraceLogFP != 0) {
357 	fflush(stdout);
358 	fflush(stderr);
359 	fclose(LYTraceLogFP);
360 	LYTraceLogFP = 0;
361     }
362 #endif /* NO_LYNX_TRACE */
363 }
364 
handle_LYK_TRACE_TOGGLE(void)365 void handle_LYK_TRACE_TOGGLE(void)
366 {
367 #ifndef NO_LYNX_TRACE
368     WWW_TraceFlag = (BOOLEAN) !WWW_TraceFlag;
369     if (LYOpenTraceLog())
370 	HTUserMsg(WWW_TraceFlag ? TRACE_ON : TRACE_OFF);
371 #else
372     HTUserMsg(TRACE_DISABLED);
373 #endif /* NO_LYNX_TRACE */
374 }
375 
LYSetNewline(int value)376 void LYSetNewline(int value)
377 {
378     Newline = value;
379 }
380 
381 #define LYSetNewline(value)	Newline = value
382 
LYGetNewline(void)383 int LYGetNewline(void)
384 {
385     return Newline;
386 }
387 
388 #define LYGetNewline()		Newline
389 
LYChgNewline(int adjust)390 void LYChgNewline(int adjust)
391 {
392     LYSetNewline(Newline + adjust);
393 }
394 
395 #define LYChgNewline(adjust)	Newline += (adjust)
396 
397 #ifdef USE_SOURCE_CACHE
398 static BOOLEAN from_source_cache = FALSE;
399 
400 /*
401  * Like HTreparse_document(), but also set the flag.
402  */
reparse_document(void)403 static BOOLEAN reparse_document(void)
404 {
405     BOOLEAN result;
406 
407     from_source_cache = TRUE;	/* set for LYMainLoop_pageDisplay() */
408     if ((result = HTreparse_document()) != FALSE) {
409 	from_source_cache = TRUE;	/* set for mainloop refresh */
410     } else {
411 	from_source_cache = FALSE;
412     }
413     return result;
414 }
415 #endif /* USE_SOURCE_CACHE */
416 
417 /*
418  * Prefer reparsing if we can, but reload if we must - to force regeneration
419  * of the display.
420  */
reparse_or_reload(int * cmd)421 static BOOLEAN reparse_or_reload(int *cmd)
422 {
423 #ifdef USE_SOURCE_CACHE
424     if (reparse_document()) {
425 	return FALSE;
426     }
427 #endif
428     *cmd = LYK_RELOAD;
429     return TRUE;
430 }
431 
432 /*
433  * Functions for setting the current address
434  */
set_address(DocInfo * doc,const char * address)435 static void set_address(DocInfo *doc, const char *address)
436 {
437     StrAllocCopy(doc->address, address);
438 }
439 
copy_address(DocInfo * dst,DocInfo * src)440 static void copy_address(DocInfo *dst, DocInfo *src)
441 {
442     StrAllocCopy(dst->address, src->address);
443 }
444 
free_address(DocInfo * doc)445 static void free_address(DocInfo *doc)
446 {
447     FREE(doc->address);
448 }
449 
move_address(DocInfo * dst,DocInfo * src)450 static void move_address(DocInfo *dst, DocInfo *src)
451 {
452     copy_address(dst, src);
453     free_address(src);
454 }
455 
456 #ifdef DISP_PARTIAL
457 /*
458  * This is for traversal call from within partial mode in LYUtils.c
459  * and HTFormat.c  It simply calls HText_pageDisplay() but utilizes
460  * LYMainLoop.c static variables to manage proper newline position
461  * in case of #fragment
462  */
LYMainLoop_pageDisplay(int line_num)463 BOOL LYMainLoop_pageDisplay(int line_num)
464 {
465     const char *pound;
466     int prev_newline = LYGetNewline();
467 
468     /*
469      * Override Newline with a new value if user scrolled the document while
470      * loading (in LYUtils.c).
471      */
472     LYSetNewline(line_num);
473 
474 #ifdef USE_SOURCE_CACHE
475     /*
476      * reparse_document() acts on 'curdoc' which always on top of the
477      * history stack: no need to resolve #fragment position since
478      * we already know it (curdoc.line).
479      * So bypass here. Sorry for possible confusion...
480      */
481     if (!from_source_cache)
482 #endif
483 	/*
484 	 * If the requested URL has the #fragment, and we are not popped
485 	 * from the history stack, and have not scrolled the document yet -
486 	 * we should calculate correct newline position for the fragment.
487 	 * (This is a bit suboptimal since HTFindPoundSelector() traverse
488 	 * anchors list each time, so we have a quadratic complexity
489 	 * and may load CPU in a worst case).
490 	 */
491 	if (display_partial
492 	    && newdoc.line == 1 && line_num == 1 && prev_newline == 1
493 	    && (pound = findPoundSelector(newdoc.address))
494 	    && *pound && *(pound + 1)) {
495 	    if (HTFindPoundSelector(pound + 1)) {
496 		/* HTFindPoundSelector will initialize www_search_result */
497 		LYSetNewline(www_search_result);
498 	    } else {
499 		LYSetNewline(prev_newline);	/* restore ??? */
500 		return NO;	/* no repaint */
501 	    }
502 	}
503 
504     HText_pageDisplay(LYGetNewline(), prev_target->str);
505     return YES;
506 }
507 #endif /* DISP_PARTIAL */
508 
set_curdoc_link(int nextlink)509 static BOOL set_curdoc_link(int nextlink)
510 {
511     BOOL result = FALSE;
512 
513     if (curdoc.link != nextlink
514 	&& nextlink >= 0
515 	&& nextlink < nlinks) {
516 	if (curdoc.link >= 0 && curdoc.link < nlinks) {
517 	    LYhighlight(FALSE, curdoc.link, prev_target->str);
518 	    result = TRUE;
519 	}
520 	curdoc.link = nextlink;
521     }
522     return result;
523 }
524 
525 /*
526  * Setup newdoc to jump to the given line.
527  *
528  * FIXME: prefer to also jump to the link given in a URL fragment, but the
529  * interface of getfile() does not provide that ability yet.
530  */
goto_line(int nextline)531 static void goto_line(int nextline)
532 {
533     int n;
534     int old_link = newdoc.link;
535 
536     newdoc.link = 0;
537     for (n = 0; n < nlinks; ++n) {
538 	if (nextline == links[n].anchor_line_num + 1) {
539 	    CTRACE((tfp, "top_of_screen %d\n", HText_getTopOfScreen() + 1));
540 	    CTRACE((tfp, "goto_line(%d) -> link %d -> %d\n", nextline,
541 		    old_link, n));
542 	    newdoc.link = n;
543 	    break;
544 	}
545     }
546 }
547 
548 #ifdef USE_MOUSE
set_curdoc_link_by_mouse(int nextlink)549 static void set_curdoc_link_by_mouse(int nextlink)
550 {
551     if (set_curdoc_link(nextlink)) {
552 	LYhighlight(TRUE, nextlink, prev_target->str);
553 	LYmsec_delay(20);
554     }
555 }
556 #else
557 #define set_curdoc_link_by_mouse(nextlink) set_curdoc_link(nextlink)
558 #endif
559 
do_change_link(void)560 static int do_change_link(void)
561 {
562 #ifdef USE_MOUSE
563     /* Is there a mouse-clicked link waiting? */
564     int mouse_tmp = get_mouse_link();
565 
566     /* If yes, use it as the link */
567     if (mouse_tmp != -1) {
568 	if (mouse_tmp < 0 || mouse_tmp >= nlinks) {
569 	    char *msgtmp = NULL;
570 
571 	    HTSprintf0(&msgtmp,
572 		       gettext("Internal error: Invalid mouse link %d!"),
573 		       mouse_tmp);
574 	    HTAlert(msgtmp);
575 	    FREE(msgtmp);
576 	    return (-1);	/* indicates unexpected error */
577 	}
578 	set_curdoc_link_by_mouse(mouse_tmp);
579     }
580 #endif /* USE_MOUSE */
581     return (0);			/* indicates OK */
582 }
583 
584 #ifdef DIRED_SUPPORT
585 #define DIRED_UNCACHE_1 if (LYAutoUncacheDirLists < 1) /*nothing*/ ;\
586 			else HTuncache_current_document()
587 #define DIRED_UNCACHE_2 if (LYAutoUncacheDirLists < 2) /*nothing*/ ;\
588 			else HTuncache_current_document()
589 #endif /* DIRED_SUPPORT */
590 
do_check_goto_URL(bstring ** user_input,char ** old_user_input,BOOLEAN * force_load)591 static void do_check_goto_URL(bstring **user_input,
592 			      char **old_user_input,
593 			      BOOLEAN *force_load)
594 {
595     static BOOLEAN always = TRUE;
596     /* *INDENT-OFF* */
597     static struct {
598 	const char *name;
599 	BOOLEAN *flag;
600     } table[] = {
601 	{ STR_FILE_URL,		&no_file_url },
602 	{ STR_FILE_URL,		&no_goto_file },
603 	{ STR_LYNXEXEC,		&no_goto_lynxexec },
604 	{ STR_LYNXPROG,		&no_goto_lynxprog },
605 	{ STR_LYNXCGI,		&no_goto_lynxcgi },
606 	{ STR_CSO_URL,		&no_goto_cso },
607 	{ STR_FINGER_URL,	&no_goto_finger },
608 	{ STR_FTP_URL,		&no_goto_ftp },
609 	{ STR_GOPHER_URL,	&no_goto_gopher },
610 	{ STR_HTTP_URL,		&no_goto_http },
611 	{ STR_HTTPS_URL,	&no_goto_https },
612 	{ STR_MAILTO_URL,	&no_goto_mailto },
613 	{ STR_RLOGIN_URL,	&no_goto_rlogin },
614 	{ STR_TELNET_URL,	&no_goto_telnet },
615 	{ STR_TN3270_URL,	&no_goto_tn3270 },
616 	{ STR_WAIS_URL,		&no_goto_wais },
617 #ifndef DISABLE_BIBP
618 	{ STR_BIBP_URL,		&no_goto_bibp },
619 #endif
620 #ifndef DISABLE_NEWS
621 	{ STR_NEWS_URL,		&no_goto_news },
622 	{ STR_NNTP_URL,		&no_goto_nntp },
623 	{ STR_SNEWS_URL,	&no_goto_snews },
624 #endif
625 #ifdef EXEC_LINKS
626 	{ STR_LYNXEXEC,		&local_exec_on_local_files },
627 	{ STR_LYNXPROG,		&local_exec_on_local_files },
628 #endif /* EXEC_LINKS */
629 	{ STR_LYNXCFG,		&no_goto_configinfo },
630 	{ STR_LYNXCFLAGS,	&no_goto_configinfo },
631 	{ STR_LYNXCOOKIE,	&always },
632 #ifdef USE_CACHEJAR
633 	{ STR_LYNXCACHE,	&always },
634 #endif
635 	{ STR_LYNXDIRED,	&always },
636 	{ STR_LYNXDOWNLOAD,	&always },
637 	{ STR_LYNXOPTIONS,	&always },
638 	{ STR_LYNXPRINT,	&always },
639     };
640     /* *INDENT-ON* */
641 
642     unsigned n;
643     BOOLEAN found = FALSE;
644 
645     /* allow going to anchors */
646     if ((*user_input)->str[0] == '#') {
647 	if ((*user_input)->str[1] &&
648 	    HTFindPoundSelector((*user_input)->str + 1)) {
649 	    /* HTFindPoundSelector will initialize www_search_result,
650 	       so we do nothing else. */
651 	    HTAddGotoURL((*user_input)->str);
652 	    trimPoundSelector(curdoc.address);
653 	    StrAllocCat(curdoc.address, (*user_input)->str);
654 	}
655     } else {
656 	/*
657 	 * If it's not a URL then make it one.
658 	 */
659 	StrAllocCopy(*old_user_input, (*user_input)->str);
660 	LYEnsureAbsoluteURL(old_user_input, "", TRUE);
661 	BStrCopy0((*user_input), *old_user_input);
662 	FREE(*old_user_input);
663 
664 	for (n = 0; n < TABLESIZE(table); n++) {
665 	    if (*(table[n].flag)
666 		&& !StrNCmp((*user_input)->str,
667 			    table[n].name,
668 			    strlen(table[n].name))) {
669 		found = TRUE;
670 		HTUserMsg2(GOTO_XXXX_DISALLOWED, table[n].name);
671 		break;
672 	    }
673 	}
674 	if (found) {
675 	    ;
676 	} else if (LYValidate &&
677 		   !isHTTP_URL((*user_input)->str) &&
678 		   !isHTTPS_URL((*user_input)->str)) {
679 	    HTUserMsg(GOTO_NON_HTTP_DISALLOWED);
680 
681 	} else {
682 	    set_address(&newdoc, (*user_input)->str);
683 	    newdoc.isHEAD = FALSE;
684 	    /*
685 	     * Might be an anchor in the same doc from a POST form.  If so,
686 	     * dont't free the content.  -- FM
687 	     */
688 	    if (are_different(&curdoc, &newdoc)) {
689 		/*
690 		 * Make a name for this new URL.
691 		 */
692 		StrAllocCopy(newdoc.title,
693 			     gettext("A URL specified by the user"));
694 		LYFreePostData(&newdoc);
695 		FREE(newdoc.bookmark);
696 		newdoc.safe = FALSE;
697 		newdoc.internal_link = FALSE;
698 		*force_load = TRUE;
699 #ifdef DIRED_SUPPORT
700 		if (lynx_edit_mode) {
701 		    DIRED_UNCACHE_2;
702 		}
703 #endif /* DIRED_SUPPORT */
704 	    }
705 	    LYUserSpecifiedURL = TRUE;
706 	    HTAddGotoURL(newdoc.address);
707 	}
708     }
709 }
710 
711 /* returns FALSE if user cancelled input or URL was invalid, TRUE otherwise */
do_check_recall(int ch,bstring ** user_input,char ** old_user_input,int URLTotal,int * URLNum,RecallType recall,BOOLEAN * FirstURLRecall)712 static BOOL do_check_recall(int ch,
713 			    bstring **user_input,
714 			    char **old_user_input,
715 			    int URLTotal,
716 			    int *URLNum,
717 			    RecallType recall,
718 			    BOOLEAN *FirstURLRecall)
719 {
720     char *cp;
721     BOOL ret = FALSE;
722 
723     if (*old_user_input == 0)
724 	StrAllocCopy(*old_user_input, "");
725 
726     for (;;) {
727 #ifdef WIN_EX			/* 1998/10/11 (Sun) 10:41:05 */
728 	int len = strlen((*user_input)->str);
729 
730 	if (len >= 3) {
731 	    if (len < MAX_LINE - 1
732 		&& LYIsHtmlSep((*user_input)->str[len - 3])
733 		&& LYIsDosDrive((*user_input)->str + len - 2))
734 		LYAddPathSep0((*user_input)->str);
735 
736 	} else if (len == 2 && (*user_input)->str[1] == ':') {
737 	    if (LYIsDosDrive((*user_input)->str)) {
738 		LYAddPathSep0((*user_input)->str);
739 	    } else {
740 		HTUserMsg2(WWW_ILLEGAL_URL_MESSAGE, (*user_input)->str);
741 		BStrCopy0((*user_input), *old_user_input);
742 		FREE(*old_user_input);
743 		ret = FALSE;
744 		break;
745 	    }
746 	}
747 #endif
748 	/*
749 	 * Get rid of leading spaces (and any other spaces).
750 	 */
751 	LYTrimAllStartfile((*user_input)->str);
752 	if (isBEmpty(*user_input) &&
753 	    !(recall && (ch == UPARROW || ch == DNARROW))) {
754 	    BStrCopy0((*user_input), *old_user_input);
755 	    FREE(*old_user_input);
756 	    HTInfoMsg(CANCELLED);
757 	    ret = FALSE;
758 	    break;
759 	}
760 	if (recall && ch == UPARROW) {
761 	    if (*FirstURLRecall) {
762 		/*
763 		 * Use last URL in the list.  - FM
764 		 */
765 		*FirstURLRecall = FALSE;
766 		*URLNum = 0;
767 	    } else {
768 		/*
769 		 * Go back to the previous URL in the list.  - FM
770 		 */
771 		*URLNum += 1;
772 	    }
773 	    if (*URLNum >= URLTotal)
774 		/*
775 		 * Roll around to the last URL in the list.  - FM
776 		 */
777 		*URLNum = 0;
778 	    if ((cp = (char *) HTList_objectAt(Goto_URLs,
779 					       *URLNum)) != NULL) {
780 		BStrCopy0((*user_input), cp);
781 		if (goto_buffer
782 		    && **old_user_input
783 		    && !strcmp(*old_user_input, (*user_input)->str)) {
784 		    _statusline(EDIT_CURRENT_GOTO);
785 		} else if ((goto_buffer && URLTotal == 2) ||
786 			   (!goto_buffer && URLTotal == 1)) {
787 		    _statusline(EDIT_THE_PREV_GOTO);
788 		} else {
789 		    _statusline(EDIT_A_PREV_GOTO);
790 		}
791 		if ((ch = LYgetBString(user_input, VISIBLE, 0, recall)) < 0) {
792 		    /*
793 		     * User cancelled the Goto via ^G.  Restore
794 		     * user_input and break.  - FM
795 		     */
796 		    BStrCopy0((*user_input), *old_user_input);
797 		    FREE(*old_user_input);
798 		    HTInfoMsg(CANCELLED);
799 		    ret = FALSE;
800 		    break;
801 		}
802 		continue;
803 	    }
804 	} else if (recall && ch == DNARROW) {
805 	    if (*FirstURLRecall) {
806 		/*
807 		 * Use the first URL in the list.  - FM
808 		 */
809 		*FirstURLRecall = FALSE;
810 		*URLNum = URLTotal - 1;
811 	    } else {
812 		/*
813 		 * Advance to the next URL in the list.  - FM
814 		 */
815 		*URLNum -= 1;
816 	    }
817 	    if (*URLNum < 0)
818 		/*
819 		 * Roll around to the first URL in the list.  - FM
820 		 */
821 		*URLNum = URLTotal - 1;
822 	    if ((cp = (char *) HTList_objectAt(Goto_URLs, *URLNum)) != NULL) {
823 		BStrCopy0((*user_input), cp);
824 		if (goto_buffer && **old_user_input &&
825 		    !strcmp(*old_user_input, (*user_input)->str)) {
826 		    _statusline(EDIT_CURRENT_GOTO);
827 		} else if ((goto_buffer && URLTotal == 2) ||
828 			   (!goto_buffer && URLTotal == 1)) {
829 		    _statusline(EDIT_THE_PREV_GOTO);
830 		} else {
831 		    _statusline(EDIT_A_PREV_GOTO);
832 		}
833 		if ((ch = LYgetBString(user_input, VISIBLE, 0, recall)) < 0) {
834 		    /*
835 		     * User cancelled the Goto via ^G.  Restore
836 		     * user_input and break.  - FM
837 		     */
838 		    BStrCopy0((*user_input), *old_user_input);
839 		    FREE(*old_user_input);
840 		    HTInfoMsg(CANCELLED);
841 		    ret = FALSE;
842 		    break;
843 		}
844 		continue;
845 	    }
846 	} else {
847 	    ret = TRUE;
848 	    break;
849 	}
850     }
851     return ret;
852 }
853 
do_cleanup_after_delete(void)854 static void do_cleanup_after_delete(void)
855 {
856     HTuncache_current_document();
857     move_address(&newdoc, &curdoc);
858     newdoc.line = curdoc.line;
859     if (curdoc.link == nlinks - 1) {
860 	/*
861 	 * We deleted the last link on the page.  - FM
862 	 */
863 	newdoc.link = curdoc.link - 1;
864     } else {
865 	newdoc.link = curdoc.link;
866     }
867 }
868 
find_link_near_col(int col,int delta)869 static int find_link_near_col(int col,
870 			      int delta)
871 {
872     int i;
873 
874     for (i = curdoc.link; delta > 0 ? (i < nlinks) : (i >= 0); i += delta) {
875 	if ((links[i].ly - links[curdoc.link].ly) * delta > 0) {
876 	    int cy = links[i].ly, best = -1, dist = 1000000;
877 
878 	    while ((delta > 0 ? (i < nlinks) : (i >= 0)) && cy == links[i].ly) {
879 		int cx = links[i].lx;
880 		const char *text = LYGetHiliteStr(i, 0);
881 
882 		if (text != NULL)
883 		    cx += (int) strlen(text) / 2;
884 		cx -= col;
885 		if (cx < 0)
886 		    cx = -cx;
887 		if (cx < dist) {
888 		    dist = cx;
889 		    best = i;
890 		}
891 		i += delta;
892 	    }
893 	    return (best);
894 	}
895     }
896     return (-1);
897 }
898 
899 /*
900  * This is a special feature to traverse every http link derived from startfile
901  * and check for errors or create crawl output files.  Only URLs that begin
902  * with "traversal_host" are searched - this keeps the search from crossing to
903  * other servers (a feature, not a bug!).
904  */
DoTraversal(int c,BOOLEAN * crawl_ok)905 static int DoTraversal(int c,
906 		       BOOLEAN *crawl_ok)
907 {
908     BOOLEAN rlink_rejected = FALSE;
909     BOOLEAN rlink_exists;
910     BOOLEAN rlink_allowed;
911 
912     rlink_exists = (BOOL) (nlinks > 0 &&
913 			   links[curdoc.link].type != WWW_FORM_LINK_TYPE &&
914 			   links[curdoc.link].lname != NULL);
915 
916     if (rlink_exists) {
917 	rlink_rejected = lookup_reject(links[curdoc.link].lname);
918 	if (!rlink_rejected &&
919 	    traversal_host &&
920 	    links[curdoc.link].lname) {
921 	    if (!isLYNXIMGMAP(links[curdoc.link].lname)) {
922 		rlink_allowed = (BOOL) !StrNCmp(traversal_host,
923 						links[curdoc.link].lname,
924 						strlen(traversal_host));
925 	    } else {
926 		rlink_allowed = (BOOL) !StrNCmp(traversal_host,
927 						links[curdoc.link].lname + LEN_LYNXIMGMAP,
928 						strlen(traversal_host));
929 	    }
930 	} else {
931 	    rlink_allowed = FALSE;
932 	}
933     } else {
934 	rlink_allowed = FALSE;
935     }
936     if (rlink_exists && rlink_allowed) {
937 	if (lookup_link(links[curdoc.link].lname)) {
938 	    if (more_links ||
939 		(curdoc.link > -1 && curdoc.link < nlinks - 1)) {
940 		c = DNARROW;
941 	    } else {
942 		if (STREQ(curdoc.title, "Entry into main screen") ||
943 		    (nhist <= 0)) {
944 		    if (!dump_output_immediately) {
945 			cleanup();
946 			exit_immediately(EXIT_FAILURE);
947 		    }
948 		    c = -1;
949 		} else {
950 		    c = LTARROW;
951 		}
952 	    }
953 	} else {
954 	    StrAllocCopy(traversal_link_to_add,
955 			 links[curdoc.link].lname);
956 	    if (!isLYNXIMGMAP(traversal_link_to_add))
957 		*crawl_ok = TRUE;
958 	    c = RTARROW;
959 	}
960     } else {			/* no good right link, so only down and left arrow ok */
961 	if (rlink_exists /* && !rlink_rejected */ )
962 	    /* uncomment in previous line to avoid duplicates - kw */
963 	    add_to_reject_list(links[curdoc.link].lname);
964 	if (more_links ||
965 	    (curdoc.link > -1 && curdoc.link < nlinks - 1)) {
966 	    c = DNARROW;
967 	} else {
968 	    /*
969 	     * curdoc.title doesn't always work, so bail out if the history
970 	     * list is empty.
971 	     */
972 	    if (STREQ(curdoc.title, "Entry into main screen") ||
973 		(nhist <= 0)) {
974 		if (!dump_output_immediately) {
975 		    cleanup();
976 		    exit_immediately(EXIT_FAILURE);
977 		}
978 		c = -1;
979 	    } else {
980 		c = LTARROW;
981 	    }
982 	}
983     }
984     CTRACE((tfp, "DoTraversal(%d:%d) -> %s\n",
985 	    nlinks > 0 ? curdoc.link : 0,
986 	    nlinks,
987 	    LYKeycodeToString(c, FALSE)));
988     return c;
989 }
990 
check_history(void)991 static BOOLEAN check_history(void)
992 {
993     const char *base;
994 
995     if (!curdoc.post_data)
996 	/*
997 	 * Normal case - List Page is not associated with post data.  - kw
998 	 */
999 	return TRUE;
1000 
1001     if (nhist > 0
1002 	&& !LYresubmit_posts
1003 	&& HDOC(nhist - 1).post_data
1004 	&& BINEQ(curdoc.post_data, HDOC(nhist - 1).post_data)
1005 	&& (base = HText_getContentBase()) != 0) {
1006 	char *text = !isLYNXIMGMAP(HDOC(nhist - 1).address)
1007 	? HDOC(nhist - 1).address
1008 	: HDOC(nhist - 1).address + LEN_LYNXIMGMAP;
1009 
1010 	if (!StrNCmp(base, text, strlen(base))) {
1011 	    /*
1012 	     * Normal case - as best as we can check, the document at the top
1013 	     * of the history stack seems to be the document the List Page is
1014 	     * about (or a LYNXIMGMAP derived from it), and LYresubmit_posts is
1015 	     * not set, so don't prompt here.  If we actually have to repeat a
1016 	     * POST because, against expectations, the underlying document
1017 	     * isn't cached any more, HTAccess will prompt for confirmation,
1018 	     * unless we had LYK_NOCACHE -kw
1019 	     */
1020 	    return TRUE;
1021 	}
1022     }
1023     return FALSE;
1024 }
1025 
handle_LYK_ACTIVATE(int * c,int cmd GCC_UNUSED,BOOLEAN * try_internal GCC_UNUSED,BOOLEAN * refresh_screen,BOOLEAN * force_load,int real_cmd)1026 static int handle_LYK_ACTIVATE(int *c,
1027 			       int cmd GCC_UNUSED,
1028 			       BOOLEAN *try_internal GCC_UNUSED,
1029 			       BOOLEAN *refresh_screen,
1030 			       BOOLEAN *force_load,
1031 			       int real_cmd)
1032 {
1033     if (do_change_link() == -1) {
1034 	LYforce_no_cache = FALSE;
1035 	reloading = FALSE;
1036 	return 1;		/* mouse stuff was confused, ignore - kw */
1037     }
1038     if (nlinks > 0) {
1039 	if (links[curdoc.link].type == WWW_FORM_LINK_TYPE) {
1040 #ifdef TEXTFIELDS_MAY_NEED_ACTIVATION
1041 	    if (real_cmd == LYK_ACTIVATE && textfields_need_activation &&
1042 		F_TEXTLIKE(links[curdoc.link].l_form->type)) {
1043 
1044 		textinput_activated = TRUE;
1045 		show_main_statusline(links[curdoc.link], FOR_INPUT);
1046 		textfields_need_activation = textfields_activation_option;
1047 
1048 		return 0;
1049 	    }
1050 #endif
1051 	    /*
1052 	     * Don't try to submit forms with bad actions.  - FM
1053 	     */
1054 	    if (links[curdoc.link].l_form->type == F_SUBMIT_TYPE ||
1055 		links[curdoc.link].l_form->type == F_IMAGE_SUBMIT_TYPE ||
1056 		links[curdoc.link].l_form->type == F_TEXT_SUBMIT_TYPE) {
1057 		/*
1058 		 * Do nothing if it's disabled.  - FM
1059 		 */
1060 		if (links[curdoc.link].l_form->disabled == YES) {
1061 		    HTOutputFormat = WWW_PRESENT;
1062 		    LYforce_no_cache = FALSE;
1063 		    reloading = FALSE;
1064 		    return 0;
1065 		}
1066 		/*
1067 		 * Make sure we have an action.  - FM
1068 		 */
1069 		if (isEmpty(links[curdoc.link].l_form->submit_action)) {
1070 		    HTUserMsg(NO_FORM_ACTION);
1071 		    HTOutputFormat = WWW_PRESENT;
1072 		    LYforce_no_cache = FALSE;
1073 		    reloading = FALSE;
1074 		    return 0;
1075 		}
1076 		/*
1077 		 * Check for no_mail if the form action is a mailto URL.  - FM
1078 		 */
1079 		if (links[curdoc.link].l_form->submit_method
1080 		    == URL_MAIL_METHOD && no_mail) {
1081 		    HTAlert(FORM_MAILTO_DISALLOWED);
1082 		    HTOutputFormat = WWW_PRESENT;
1083 		    LYforce_no_cache = FALSE;
1084 		    reloading = FALSE;
1085 		    return 0;
1086 		}
1087 		/*
1088 		 * Make sure this isn't a spoof in an account with restrictions
1089 		 * on file URLs.  - FM
1090 		 */
1091 		if (no_file_url &&
1092 		    isFILE_URL(links[curdoc.link].l_form->submit_action)) {
1093 		    HTAlert(FILE_ACTIONS_DISALLOWED);
1094 		    HTOutputFormat = WWW_PRESENT;
1095 		    LYforce_no_cache = FALSE;
1096 		    reloading = FALSE;
1097 		    return 0;
1098 		}
1099 		/*
1100 		 * Make sure this isn't a spoof attempt via an internal URL.  -
1101 		 * FM
1102 		 */
1103 		if (isLYNXCOOKIE(links[curdoc.link].l_form->submit_action) ||
1104 #ifdef USE_CACHEJAR
1105 		    isLYNXCACHE(links[curdoc.link].l_form->submit_action) ||
1106 #endif
1107 #ifdef DIRED_SUPPORT
1108 #ifdef OK_PERMIT
1109 		    (isLYNXDIRED(links[curdoc.link].l_form->submit_action) &&
1110 		     (no_dired_support ||
1111 		      strncasecomp((links[curdoc.link].l_form->submit_action
1112 				    + 10),
1113 				   "//PERMIT_LOCATION", 17) ||
1114 		      !LYIsUIPage(curdoc.address, UIP_PERMIT_OPTIONS))) ||
1115 #else
1116 		    isLYNXDIRED(links[curdoc.link].l_form->submit_action) ||
1117 #endif /* OK_PERMIT */
1118 #endif /* DIRED_SUPPORT */
1119 		    isLYNXDOWNLOAD(links[curdoc.link].l_form->submit_action) ||
1120 		    isLYNXHIST(links[curdoc.link].l_form->submit_action) ||
1121 		    isLYNXKEYMAP(links[curdoc.link].l_form->submit_action) ||
1122 		    isLYNXIMGMAP(links[curdoc.link].l_form->submit_action) ||
1123 		    isLYNXPRINT(links[curdoc.link].l_form->submit_action) ||
1124 		    isLYNXEXEC(links[curdoc.link].l_form->submit_action) ||
1125 		    isLYNXPROG(links[curdoc.link].l_form->submit_action)) {
1126 
1127 		    HTAlert(SPECIAL_ACTION_DISALLOWED);
1128 		    CTRACE((tfp, "LYMainLoop: Rejected '%s'\n",
1129 			    links[curdoc.link].l_form->submit_action));
1130 		    HTOutputFormat = WWW_PRESENT;
1131 		    LYforce_no_cache = FALSE;
1132 		    reloading = FALSE;
1133 		    return 0;
1134 		}
1135 #ifdef NOTDEFINED		/* We're disabling form inputs instead of using this. - FM */
1136 		/*
1137 		 * Check for enctype and let user know we don't yet support
1138 		 * multipart/form-data - FM
1139 		 */
1140 		if (links[curdoc.link].l_form->submit_enctype) {
1141 		    if (!strcmp(links[curdoc.link].l_form->submit_enctype,
1142 				"multipart/form-data")) {
1143 			HTAlert(gettext("Enctype multipart/form-data not yet supported!  Cannot submit."));
1144 			HTOutputFormat = WWW_PRESENT;
1145 			LYforce_no_cache = FALSE;
1146 			reloading = FALSE;
1147 			return 0;
1148 		    }
1149 		}
1150 #endif /* NOTDEFINED */
1151 		if (check_realm) {
1152 		    LYPermitURL = TRUE;
1153 		}
1154 		if (no_filereferer == TRUE && isFILE_URL(curdoc.address)) {
1155 		    LYNoRefererForThis = TRUE;
1156 		}
1157 		if (links[curdoc.link].l_form->submit_method != URL_MAIL_METHOD) {
1158 		    StrAllocCopy(newdoc.title,
1159 				 LYGetHiliteStr(curdoc.link, 0));
1160 		}
1161 	    }
1162 
1163 	    /*
1164 	     * Normally we don't get here for text input fields, but it can
1165 	     * happen as a result of mouse positioning.  In that case the
1166 	     * statusline will not have updated info, so update it now.  - kw
1167 	     */
1168 	    if (F_TEXTLIKE(links[curdoc.link].l_form->type)) {
1169 		show_formlink_statusline(links[curdoc.link].l_form,
1170 					 (real_cmd == LYK_NOCACHE ||
1171 					  real_cmd == LYK_DOWNLOAD ||
1172 					  real_cmd == LYK_HEAD ||
1173 					  (real_cmd == LYK_MOUSE_SUBMIT &&
1174 					   !textinput_activated)) ?
1175 					 FOR_PANEL : FOR_INPUT);
1176 		if (user_mode == NOVICE_MODE &&
1177 		    textinput_activated &&
1178 		    (real_cmd == LYK_ACTIVATE ||
1179 		     real_cmd == LYK_MOUSE_SUBMIT)) {
1180 		    form_noviceline(FormIsReadonly(links[curdoc.link].l_form));
1181 		}
1182 	    }
1183 
1184 	    *c = change_form_link(curdoc.link,
1185 				  &newdoc, refresh_screen,
1186 				  FALSE,
1187 				  (real_cmd == LYK_MOUSE_SUBMIT ||
1188 				   real_cmd == LYK_NOCACHE ||
1189 				   real_cmd == LYK_DOWNLOAD ||
1190 				   real_cmd == LYK_HEAD));
1191 	    if (*c != LKC_DONE || *refresh_screen) {
1192 		/*
1193 		 * Cannot have been a submit field for which newdoc was filled
1194 		 * in.  - kw
1195 		 */
1196 		if ((links[curdoc.link].l_form->type == F_SUBMIT_TYPE ||
1197 		     links[curdoc.link].l_form->type == F_IMAGE_SUBMIT_TYPE ||
1198 		     links[curdoc.link].l_form->type == F_TEXT_SUBMIT_TYPE) &&
1199 		    links[curdoc.link].l_form->submit_method
1200 		    != URL_MAIL_METHOD) {
1201 		    /*
1202 		     * Try to undo change of newdoc.title done above.
1203 		     */
1204 		    if (HText_getTitle()) {
1205 			StrAllocCopy(newdoc.title, HText_getTitle());
1206 		    } else if (curdoc.title) {
1207 			StrAllocCopy(newdoc.title, curdoc.title);
1208 		    }
1209 		}
1210 	    } else {
1211 		if (HTOutputFormat == HTAtom_for("www/download") &&
1212 		    newdoc.post_data != NULL &&
1213 		    newdoc.safe == FALSE) {
1214 
1215 		    if ((HText_POSTReplyLoaded(&newdoc) == TRUE) &&
1216 			HTConfirm(CONFIRM_POST_RESUBMISSION) == FALSE) {
1217 			HTInfoMsg(CANCELLED);
1218 			HTOutputFormat = WWW_PRESENT;
1219 			LYforce_no_cache = FALSE;
1220 			copy_address(&newdoc, &curdoc);
1221 			StrAllocCopy(newdoc.title, curdoc.title);
1222 			BStrCopy(newdoc.post_data, curdoc.post_data);
1223 			StrAllocCopy(newdoc.post_content_type,
1224 				     curdoc.post_content_type);
1225 			StrAllocCopy(newdoc.bookmark, curdoc.bookmark);
1226 			newdoc.isHEAD = curdoc.isHEAD;
1227 			newdoc.safe = curdoc.safe;
1228 			newdoc.internal_link = curdoc.internal_link;
1229 			return 0;
1230 		    }
1231 		}
1232 		/*
1233 		 * Moved here from earlier to only apply when it should.
1234 		 * Anyway, why should realm checking be overridden for form
1235 		 * submissions, this seems to be an unnecessary loophole??  But
1236 		 * that's the way it was, maybe there is some reason.  However,
1237 		 * at least make sure this doesn't weaken restrictions implied
1238 		 * by -validate!
1239 		 * - kw 1999-05-25
1240 		 */
1241 		if (check_realm && !LYValidate) {
1242 		    LYPermitURL = TRUE;
1243 		}
1244 	    }
1245 	    if (*c == LKC_DONE) {
1246 		*c = DO_NOTHING;
1247 	    } else if (*c == 23) {
1248 		*c = DO_NOTHING;
1249 		*refresh_screen = TRUE;
1250 	    } else {
1251 		/* Avoid getting stuck with repeatedly calling
1252 		 * handle_LYK_ACTIVATE(), instead of calling change_form_link()
1253 		 * directly from mainloop(), for text input fields.  - kw
1254 		 */
1255 		switch (LKC_TO_C(*c)) {
1256 		case '\n':
1257 		case '\r':
1258 		default:
1259 		    if ((real_cmd == LYK_ACTIVATE ||
1260 			 real_cmd == LYK_MOUSE_SUBMIT) &&
1261 			F_TEXTLIKE(links[curdoc.link].l_form->type) &&
1262 			textinput_activated) {
1263 			return 3;
1264 		    }
1265 		    break;
1266 		}
1267 	    }
1268 	    return 2;
1269 	} else {
1270 	    /*
1271 	     * Not a forms link.
1272 	     *
1273 	     * Make sure this isn't a spoof in an account with restrictions on
1274 	     * file URLs.  - FM
1275 	     */
1276 	    if (no_file_url && isFILE_URL(links[curdoc.link].lname)) {
1277 		if (!isFILE_URL(curdoc.address) &&
1278 		    !((isLYNXKEYMAP(curdoc.address) ||
1279 #ifndef USE_CACHEJAR
1280 		       isLYNXCOOKIE(curdoc.address)) &&
1281 #else
1282 		       isLYNXCOOKIE(curdoc.address) ||
1283 		       isLYNXCACHE(curdoc.address)) &&
1284 #endif
1285 		      !StrNCmp(links[curdoc.link].lname,
1286 			       helpfilepath,
1287 			       strlen(helpfilepath)))) {
1288 		    HTAlert(FILE_SERVED_LINKS_DISALLOWED);
1289 		    reloading = FALSE;
1290 		    return 0;
1291 		} else if (curdoc.bookmark != NULL) {
1292 		    HTAlert(FILE_BOOKMARKS_DISALLOWED);
1293 		    reloading = FALSE;
1294 		    return 0;
1295 		}
1296 	    }
1297 	    /*
1298 	     * Make sure this isn't a spoof attempt via an internal URL in a
1299 	     * non-internal document.  - FM
1300 	     */
1301 	    if ((isLYNXCOOKIE(links[curdoc.link].lname) &&
1302 		 (strcmp(NonNull(curdoc.title), COOKIE_JAR_TITLE) ||
1303 		  !isLYNXCOOKIE(curdoc.address))) ||
1304 #ifdef USE_CACHEJAR
1305 		(isLYNXCACHE(links[curdoc.link].lname) &&
1306 		 (strcmp(NonNull(curdoc.title), CACHE_JAR_TITLE) ||
1307 		  !isLYNXCACHE(curdoc.address))) ||
1308 #endif
1309 #ifdef DIRED_SUPPORT
1310 		(isLYNXDIRED(links[curdoc.link].lname) &&
1311 		 !LYIsUIPage(curdoc.address, UIP_DIRED_MENU) &&
1312 		 !LYIsUIPage(curdoc.address, UIP_PERMIT_OPTIONS) &&
1313 #ifdef OK_INSTALL
1314 		 !LYIsUIPage(curdoc.address, UIP_INSTALL) &&
1315 #endif /* OK_INSTALL */
1316 		 !LYIsUIPage(curdoc.address, UIP_UPLOAD_OPTIONS)) ||
1317 #endif /* DIRED_SUPPORT */
1318 		(isLYNXDOWNLOAD(links[curdoc.link].lname) &&
1319 		 !LYIsUIPage(curdoc.address, UIP_DOWNLOAD_OPTIONS)) ||
1320 		(isLYNXHIST(links[curdoc.link].lname) &&
1321 		 !LYIsUIPage(curdoc.address, UIP_HISTORY) &&
1322 		 !LYIsUIPage(curdoc.address, UIP_LIST_PAGE) &&
1323 		 !LYIsUIPage(curdoc.address, UIP_ADDRLIST_PAGE)) ||
1324 		(isLYNXPRINT(links[curdoc.link].lname) &&
1325 		 !LYIsUIPage(curdoc.address, UIP_PRINT_OPTIONS))) {
1326 		HTAlert(SPECIAL_VIA_EXTERNAL_DISALLOWED);
1327 		HTOutputFormat = WWW_PRESENT;
1328 		LYforce_no_cache = FALSE;
1329 		reloading = FALSE;
1330 		return 0;
1331 	    }
1332 #ifdef USE_EXTERNALS
1333 	    if (run_external(links[curdoc.link].lname, TRUE)) {
1334 		*refresh_screen = TRUE;
1335 		return 0;
1336 	    }
1337 #endif /* USE_EXTERNALS */
1338 
1339 	    /*
1340 	     * Follow a normal link or anchor.
1341 	     */
1342 	    set_address(&newdoc, links[curdoc.link].lname);
1343 	    StrAllocCopy(newdoc.title, LYGetHiliteStr(curdoc.link, 0));
1344 	    /*
1345 	     * For internal links, retain POST content if present.  If we are
1346 	     * on the List Page, prevent pushing it on the history stack.
1347 	     * Otherwise set try_internal to signal that the top of the loop
1348 	     * should attempt to reposition directly, without calling getfile.
1349 	     * - kw
1350 	     */
1351 	    if (track_internal_links) {
1352 		/*
1353 		 * Might be an internal link anchor in the same doc.  If so, take
1354 		 * the try_internal shortcut if we didn't fall through from
1355 		 * LYK_NOCACHE.  - kw
1356 		 */
1357 		newdoc.internal_link =
1358 		    (links[curdoc.link].type == WWW_INTERN_LINK_TYPE);
1359 		if (newdoc.internal_link) {
1360 		    /*
1361 		     * Special case of List Page document with an internal link
1362 		     * indication, which may really stand for an internal link
1363 		     * within the document the List Page is about.  - kw
1364 		     */
1365 		    if (LYIsListpageTitle(NonNull(curdoc.title)) &&
1366 			(LYIsUIPage(curdoc.address, UIP_LIST_PAGE) ||
1367 			 LYIsUIPage(curdoc.address, UIP_ADDRLIST_PAGE))) {
1368 			if (check_history()) {
1369 			    LYinternal_flag = TRUE;
1370 			} else {
1371 			    HTLastConfirmCancelled();	/* reset flag */
1372 			    if (!confirm_post_resub(newdoc.address,
1373 						    newdoc.title,
1374 						    ((LYresubmit_posts &&
1375 						      HText_POSTReplyLoaded(&newdoc))
1376 						     ? 1
1377 						     : 2),
1378 						    2)) {
1379 				if (HTLastConfirmCancelled() ||
1380 				    (LYresubmit_posts &&
1381 				     cmd != LYK_NOCACHE &&
1382 				     !HText_POSTReplyLoaded(&newdoc))) {
1383 				    /* cancel the whole thing */
1384 				    LYforce_no_cache = FALSE;
1385 				    reloading = FALSE;
1386 				    copy_address(&newdoc, &curdoc);
1387 				    StrAllocCopy(newdoc.title, curdoc.title);
1388 				    newdoc.internal_link = curdoc.internal_link;
1389 				    HTInfoMsg(CANCELLED);
1390 				    return 1;
1391 				} else if (LYresubmit_posts &&
1392 					   cmd != LYK_NOCACHE) {
1393 				    /* If LYresubmit_posts is set, and the
1394 				       answer was No, and the key wasn't
1395 				       NOCACHE, and we have a cached copy,
1396 				       then use it. - kw */
1397 				    LYforce_no_cache = FALSE;
1398 				} else {
1399 				    /* if No, but not ^C or ^G, drop
1400 				     * the post data.  Maybe the link
1401 				     * wasn't meant to be internal after
1402 				     * all, here we can recover from that
1403 				     * assumption. - kw */
1404 				    LYFreePostData(&newdoc);
1405 				    newdoc.internal_link = FALSE;
1406 				    HTAlert(DISCARDING_POST_DATA);
1407 				}
1408 			    }
1409 			}
1410 			/*
1411 			 * Don't push the List Page if we follow an internal link
1412 			 * given by it.  - kw
1413 			 */
1414 			free_address(&curdoc);
1415 		    } else if (cmd != LYK_NOCACHE) {
1416 			*try_internal = TRUE;
1417 		    }
1418 		    if (!(LYresubmit_posts && newdoc.post_data))
1419 			LYinternal_flag = TRUE;
1420 		    /* We still set force_load so that history pushing
1421 		     * etc. will be done.  - kw
1422 		     */
1423 		    *force_load = TRUE;
1424 		    return 1;
1425 		} else {
1426 		    /*
1427 		     * Free POST content if not an internal link.  - kw
1428 		     */
1429 		    LYFreePostData(&newdoc);
1430 		}
1431 	    }
1432 	    /*
1433 	     * Might be an anchor in the same doc from a POST form.  If so,
1434 	     * don't free the content.  -- FM
1435 	     */
1436 	    if (are_different(&curdoc, &newdoc)) {
1437 		LYFreePostData(&newdoc);
1438 		FREE(newdoc.bookmark);
1439 		if (isLYNXMESSAGES(newdoc.address))
1440 		    LYforce_no_cache = TRUE;
1441 	    }
1442 	    if (!no_jump && lynxjumpfile && curdoc.address &&
1443 		!strcmp(lynxjumpfile, curdoc.address)) {
1444 		LYJumpFileURL = TRUE;
1445 		LYUserSpecifiedURL = TRUE;
1446 	    } else if ((curdoc.title &&
1447 			(LYIsUIPage(curdoc.address, UIP_HISTORY) ||
1448 			 !strcmp(curdoc.title, HISTORY_PAGE_TITLE))) ||
1449 		       curdoc.bookmark != NULL ||
1450 		       (lynxjumpfile &&
1451 			curdoc.address &&
1452 			!strcmp(lynxjumpfile, curdoc.address))) {
1453 		LYUserSpecifiedURL = TRUE;
1454 	    } else if (no_filereferer == TRUE &&
1455 		       curdoc.address != NULL &&
1456 		       isFILE_URL(curdoc.address)) {
1457 		LYNoRefererForThis = TRUE;
1458 	    }
1459 	    newdoc.link = 0;
1460 	    *force_load = TRUE;	/* force MainLoop to reload */
1461 #ifdef USE_PRETTYSRC
1462 	    psrc_view = FALSE;	/* we get here if link is not internal */
1463 #endif
1464 
1465 #if defined(DIRED_SUPPORT) && !defined(__DJGPP__)
1466 	    if (lynx_edit_mode) {
1467 		DIRED_UNCACHE_2;
1468 		/*
1469 		 * Unescaping any slash chars in the URL, but avoid double
1470 		 * unescaping and too-early unescaping of other chars.  - KW
1471 		 */
1472 		HTUnEscapeSome(newdoc.address, "/");
1473 		/* avoid stripping final slash for root dir - kw */
1474 		if (strcasecomp(newdoc.address, "file://localhost/"))
1475 		    strip_trailing_slash(newdoc.address);
1476 	    }
1477 #endif /* DIRED_SUPPORT  && !__DJGPP__ */
1478 	    if (isLYNXCOOKIE(curdoc.address)
1479 #ifdef USE_CACHEJAR
1480 		|| isLYNXCACHE(curdoc.address)
1481 #endif
1482 		) {
1483 		HTuncache_current_document();
1484 	    }
1485 	}
1486     }
1487     return 0;
1488 }
1489 /*
1490  * If the given form link does not point to the requested type, search for
1491  * the first link belonging to the form which does.  If there are none,
1492  * return null.
1493  */
1494 #define SameFormAction(form,submit) \
1495  	((submit) \
1496 	 ? (F_SUBMITLIKE((form)->type)) \
1497 	 : ((form)->type == F_RESET_TYPE))
1498 
FindFormAction(FormInfo * given,BOOLEAN submit)1499 static FormInfo *FindFormAction(FormInfo * given, BOOLEAN submit)
1500 {
1501     FormInfo *result = NULL;
1502     FormInfo *fi;
1503     int i;
1504 
1505     if (given == NULL) {
1506 	HTAlert(LINK_NOT_IN_FORM);
1507     } else if (SameFormAction(given, submit)) {
1508 	result = given;
1509     } else {
1510 	for (i = 0; i < nlinks; i++) {
1511 	    if ((fi = links[i].l_form) != 0 &&
1512 		fi->number == given->number &&
1513 		(SameFormAction(fi, submit))) {
1514 		result = fi;
1515 		break;
1516 	    }
1517 	}
1518     }
1519     return result;
1520 }
1521 
MakeFormAction(FormInfo * given,BOOLEAN submit)1522 static FormInfo *MakeFormAction(FormInfo * given, BOOLEAN submit)
1523 {
1524     FormInfo *result = 0;
1525 
1526     if (given != 0) {
1527 	result = typecalloc(FormInfo);
1528 
1529 	if (result == NULL)
1530 	    outofmem(__FILE__, "MakeFormAction");
1531 
1532 	*result = *given;
1533 	if (submit) {
1534 	    if (result->submit_action == 0) {
1535 		PerFormInfo *pfi = HText_PerFormInfo(result->number);
1536 
1537 		*result = pfi->data;
1538 	    }
1539 	    result->type = F_SUBMIT_TYPE;
1540 	} else {
1541 	    result->type = F_RESET_TYPE;
1542 	}
1543 	result->number = given->number;
1544     }
1545     return result;
1546 }
1547 
handle_LYK_SUBMIT(int cur,DocInfo * doc,BOOLEAN * refresh_screen)1548 static void handle_LYK_SUBMIT(int cur, DocInfo *doc, BOOLEAN *refresh_screen)
1549 {
1550     FormInfo *form = FindFormAction(links[cur].l_form, TRUE);
1551     FormInfo *make = NULL;
1552     char *save_submit_action = NULL;
1553 
1554     if (form == 0) {
1555 	make = MakeFormAction(links[cur].l_form, TRUE);
1556 	form = make;
1557     }
1558 
1559     StrAllocCopy(save_submit_action, form->submit_action);
1560     form->submit_action = HTPrompt(EDIT_SUBMIT_URL, form->submit_action);
1561 
1562     if (isEmpty(form->submit_action) ||
1563 	(!isLYNXCGI(form->submit_action) &&
1564 	 StrNCmp(form->submit_action, "http", 4))) {
1565 	HTUserMsg(FORM_ACTION_NOT_HTTP_URL);
1566     } else {
1567 	HTInfoMsg(SUBMITTING_FORM);
1568 	HText_SubmitForm(form, doc, form->name, form->value);
1569 	*refresh_screen = TRUE;
1570     }
1571 
1572     StrAllocCopy(form->submit_action, save_submit_action);
1573     FREE(make);
1574 }
1575 
handle_LYK_RESET(int cur,BOOLEAN * refresh_screen)1576 static void handle_LYK_RESET(int cur, BOOLEAN *refresh_screen)
1577 {
1578     FormInfo *form = FindFormAction(links[cur].l_form, FALSE);
1579     FormInfo *make = NULL;
1580 
1581     if (form == 0) {
1582 	make = MakeFormAction(links[cur].l_form, FALSE);
1583 	form = make;
1584     }
1585 
1586     if (form != 0) {
1587 	HTInfoMsg(RESETTING_FORM);
1588 	HText_ResetForm(form);
1589 	*refresh_screen = TRUE;
1590 	FREE(make);
1591     }
1592 }
1593 
1594 #ifdef USE_ADDRLIST_PAGE
handle_LYK_ADDRLIST(int * cmd)1595 static BOOLEAN handle_LYK_ADDRLIST(int *cmd)
1596 {
1597     /*
1598      * Don't do if already viewing list addresses page.
1599      */
1600     if (LYIsUIPage(curdoc.address, UIP_ADDRLIST_PAGE)) {
1601 	/*
1602 	 * Already viewing list page, so get out.
1603 	 */
1604 	*cmd = LYK_PREV_DOC;
1605 	return TRUE;
1606     }
1607 
1608     /*
1609      * Print address list page to file.
1610      */
1611     if (showlist(&newdoc, FALSE) < 0)
1612 	return FALSE;
1613     StrAllocCopy(newdoc.title, ADDRLIST_PAGE_TITLE);
1614     /*
1615      * showlist will set newdoc's other fields.  It may leave post_data intact
1616      * so the list can be used to follow internal links in the current document
1617      * even if it is a POST response.  - kw
1618      */
1619 
1620     if (LYValidate || check_realm) {
1621 	LYPermitURL = TRUE;
1622 	StrAllocCopy(lynxlistfile, newdoc.address);
1623     }
1624     return FALSE;
1625 }
1626 #endif /* USE_ADDRLIST_PAGE */
1627 
handle_LYK_ADD_BOOKMARK(BOOLEAN * refresh_screen,int * old_c,int real_c)1628 static void handle_LYK_ADD_BOOKMARK(BOOLEAN *refresh_screen,
1629 				    int *old_c,
1630 				    int real_c)
1631 {
1632     int c;
1633 
1634     if (LYValidate) {
1635 	if (*old_c != real_c) {
1636 	    *old_c = real_c;
1637 	    HTUserMsg(BOOKMARKS_DISABLED);
1638 	}
1639 	return;
1640     }
1641 
1642     if (!LYIsUIPage(curdoc.address, UIP_HISTORY) &&
1643 	!LYIsUIPage(curdoc.address, UIP_SHOWINFO) &&
1644 	!LYIsUIPage(curdoc.address, UIP_PRINT_OPTIONS) &&
1645 #ifdef DIRED_SUPPORT
1646 	!LYIsUIPage(curdoc.address, UIP_DIRED_MENU) &&
1647 	!LYIsUIPage(curdoc.address, UIP_PERMIT_OPTIONS) &&
1648 	!LYIsUIPage(curdoc.address, UIP_UPLOAD_OPTIONS) &&
1649 #endif /* DIRED_SUPPORT */
1650 	!LYIsUIPage(curdoc.address, UIP_DOWNLOAD_OPTIONS) &&
1651 	!isLYNXCOOKIE(curdoc.address) &&
1652 #ifdef USE_CACHEJAR
1653 	!isLYNXCACHE(curdoc.address) &&
1654 #endif
1655 	!LYIsUIPage(curdoc.address, UIP_OPTIONS_MENU) &&
1656 	((nlinks <= 0) ||
1657 	 (links[curdoc.link].lname != NULL &&
1658 	  !isLYNXHIST(links[curdoc.link].lname) &&
1659 	  !isLYNXPRINT(links[curdoc.link].lname) &&
1660 	  !isLYNXDIRED(links[curdoc.link].lname) &&
1661 	  !isLYNXDOWNLOAD(links[curdoc.link].lname) &&
1662 	  !isLYNXCOOKIE(links[curdoc.link].lname) &&
1663 #ifdef USE_CACHEJAR
1664 	  !isLYNXCACHE(links[curdoc.link].lname) &&
1665 #endif
1666 	  !isLYNXPRINT(links[curdoc.link].lname)))) {
1667 	if (nlinks > 0) {
1668 	    if (curdoc.post_data == NULL &&
1669 		curdoc.bookmark == NULL &&
1670 		!LYIsUIPage(curdoc.address, UIP_LIST_PAGE) &&
1671 		!LYIsUIPage(curdoc.address, UIP_ADDRLIST_PAGE) &&
1672 		!LYIsUIPage(curdoc.address, UIP_VLINKS)) {
1673 		/*
1674 		 * The document doesn't have POST content, and is not a
1675 		 * bookmark file, nor is the list or visited links page, so we
1676 		 * can save either that or the link.  - FM
1677 		 */
1678 		_statusline(BOOK_D_L_OR_CANCEL);
1679 		if ((c = LYgetch_single()) == 'D') {
1680 		    save_bookmark_link(curdoc.address, curdoc.title);
1681 		    *refresh_screen = TRUE;	/* MultiBookmark support */
1682 		    goto check_add_bookmark_to_self;
1683 		}
1684 	    } else {
1685 		if (LYMultiBookmarks == MBM_OFF &&
1686 		    curdoc.bookmark != NULL &&
1687 		    strstr(curdoc.address,
1688 			   (*bookmark_page == '.'
1689 			    ? (bookmark_page + 1)
1690 			    : bookmark_page)) != NULL) {
1691 		    /*
1692 		     * If multiple bookmarks are disabled, offer the L)ink or
1693 		     * C)ancel, but with wording which indicates that the link
1694 		     * already exists in this bookmark file.  - FM
1695 		     */
1696 		    _statusline(MULTIBOOKMARKS_SELF);
1697 		} else if (curdoc.post_data != NULL &&
1698 			   links[curdoc.link].type == WWW_INTERN_LINK_TYPE) {
1699 		    /*
1700 		     * Internal link, and document has POST content.
1701 		     */
1702 		    HTUserMsg(NOBOOK_POST_FORM);
1703 		    return;
1704 		} else {
1705 		    /*
1706 		     * Only offer the link in a document with POST content, or
1707 		     * if the current document is a bookmark file and multiple
1708 		     * bookmarks are enabled.  - FM
1709 		     */
1710 		    _statusline(BOOK_L_OR_CANCEL);
1711 		}
1712 		c = LYgetch_single();
1713 	    }
1714 	    if (c == 'L') {
1715 		if (curdoc.post_data != NULL &&
1716 		    links[curdoc.link].type == WWW_INTERN_LINK_TYPE) {
1717 		    /*
1718 		     * Internal link, and document has POST content.
1719 		     */
1720 		    HTUserMsg(NOBOOK_POST_FORM);
1721 		    return;
1722 		}
1723 		/*
1724 		 * User does want to save the link.  - FM
1725 		 */
1726 		if (links[curdoc.link].type != WWW_FORM_LINK_TYPE) {
1727 		    save_bookmark_link(links[curdoc.link].lname,
1728 				       LYGetHiliteStr(curdoc.link, 0));
1729 		    *refresh_screen = TRUE;	/* MultiBookmark support */
1730 		} else {
1731 		    HTUserMsg(NOBOOK_FORM_FIELD);
1732 		    return;
1733 		}
1734 	    } else {
1735 		return;
1736 	    }
1737 	} else if (curdoc.post_data != NULL) {
1738 	    /*
1739 	     * No links, and document has POST content.  - FM
1740 	     */
1741 	    HTUserMsg(NOBOOK_POST_FORM);
1742 	    return;
1743 	} else if (curdoc.bookmark != NULL) {
1744 	    /*
1745 	     * It's a bookmark file from which all of the links were deleted.
1746 	     * - FM
1747 	     */
1748 	    HTUserMsg(BOOKMARKS_NOLINKS);
1749 	    return;
1750 	} else {
1751 	    _statusline(BOOK_D_OR_CANCEL);
1752 	    if (LYgetch_single() == 'D') {
1753 		save_bookmark_link(curdoc.address, curdoc.title);
1754 		*refresh_screen = TRUE;		/* MultiBookmark support */
1755 	    } else {
1756 		return;
1757 	    }
1758 	}
1759       check_add_bookmark_to_self:
1760 	if (curdoc.bookmark && BookmarkPage &&
1761 	    !strcmp(curdoc.bookmark, BookmarkPage)) {
1762 	    HTuncache_current_document();
1763 	    move_address(&newdoc, &curdoc);
1764 	    StrAllocCopy(newdoc.bookmark, curdoc.bookmark);
1765 	    newdoc.line = curdoc.line;
1766 	    newdoc.link = curdoc.link;
1767 	    newdoc.internal_link = FALSE;
1768 	}
1769     } else {
1770 	if (*old_c != real_c) {
1771 	    *old_c = real_c;
1772 	    HTUserMsg(NOBOOK_HSML);
1773 	}
1774     }
1775 }
1776 
handle_LYK_CLEAR_AUTH(int * old_c,int real_c)1777 static void handle_LYK_CLEAR_AUTH(int *old_c,
1778 				  int real_c)
1779 {
1780     if (*old_c != real_c) {
1781 	*old_c = real_c;
1782 	if (HTConfirm(CLEAR_ALL_AUTH_INFO)) {
1783 	    FREE(authentication_info[0]);
1784 	    FREE(authentication_info[1]);
1785 	    FREE(proxyauth_info[0]);
1786 	    FREE(proxyauth_info[1]);
1787 	    HTClearHTTPAuthInfo();
1788 #ifndef DISABLE_NEWS
1789 	    HTClearNNTPAuthInfo();
1790 #endif
1791 #ifndef DISABLE_FTP
1792 	    HTClearFTPPassword();
1793 #endif
1794 	    HTUserMsg(AUTH_INFO_CLEARED);
1795 	} else {
1796 	    HTUserMsg(CANCELLED);
1797 	}
1798     }
1799 }
1800 
handle_LYK_COMMAND(bstring ** user_input)1801 static int handle_LYK_COMMAND(bstring **user_input)
1802 {
1803     LYKeymapCode ch;
1804     Kcmd *mp;
1805     char *src, *tmp;
1806 
1807     BStrCopy0((*user_input), "");
1808     _statusline(": ");
1809     if (LYgetBString(user_input, VISIBLE, 0, RECALL_CMD) >= 0) {
1810 	src = LYSkipBlanks((*user_input)->str);
1811 	tmp = LYSkipNonBlanks(src);
1812 	*tmp = 0;
1813 	ch = ((mp = LYStringToKcmd(src)) != 0) ? mp->code : LYK_UNKNOWN;
1814 	CTRACE((tfp, "LYK_COMMAND(%s.%s) = %d\n", src, tmp, (int) ch));
1815 	if (ch == 0) {
1816 	    return *src ? -1 : 0;
1817 	}
1818 	/* FIXME: reuse the rest of the buffer for parameters */
1819 	return ch;
1820     }
1821     return 0;
1822 }
1823 
handle_LYK_COMMENT(BOOLEAN * refresh_screen,char ** owner_address_p,int * old_c,int real_c)1824 static void handle_LYK_COMMENT(BOOLEAN *refresh_screen,
1825 			       char **owner_address_p,
1826 			       int *old_c,
1827 			       int real_c)
1828 {
1829     int c;
1830 
1831     if (!*owner_address_p &&
1832 	strncasecomp(curdoc.address, "http", 4)) {
1833 	if (*old_c != real_c) {
1834 	    *old_c = real_c;
1835 	    HTUserMsg(NO_OWNER);
1836 	}
1837     } else if (no_mail) {
1838 	if (*old_c != real_c) {
1839 	    *old_c = real_c;
1840 	    HTUserMsg(MAIL_DISALLOWED);
1841 	}
1842     } else {
1843 	if (HTConfirmDefault(CONFIRM_COMMENT, NO)) {
1844 	    if (!*owner_address_p) {
1845 		/*
1846 		 * No owner defined, so make a guess and and offer it to the
1847 		 * user.  - FM
1848 		 */
1849 		char *address = NULL;
1850 		char *temp = HTParse(curdoc.address, "", PARSE_PATH);
1851 		char *cp;
1852 
1853 		if (temp != NULL) {
1854 		    HTUnEscape(temp);
1855 		    if (LYIsTilde(*temp) && strlen(temp) > 1) {
1856 			/*
1857 			 * It's a ~user URL so guess user@host.  - FM
1858 			 */
1859 			if ((cp = strchr((temp + 1), '/')) != NULL)
1860 			    *cp = '\0';
1861 			StrAllocCopy(address, STR_MAILTO_URL);
1862 			StrAllocCat(address, (temp + 1));
1863 			StrAllocCat(address, "@");
1864 		    }
1865 		    FREE(temp);
1866 		}
1867 		if (address == NULL)
1868 		    /*
1869 		     * Wasn't a ~user URL so guess WebMaster@host.  - FM
1870 		     */
1871 		    StrAllocCopy(address, "mailto:WebMaster@");
1872 		temp = HTParse(curdoc.address, "", PARSE_HOST);
1873 		StrAllocCat(address, temp);
1874 		HTSprintf0(&temp, NO_OWNER_USE, address);
1875 		c = HTConfirmDefault(temp, NO);
1876 		FREE(temp);
1877 		if (c == YES) {
1878 		    StrAllocCopy(*owner_address_p, address);
1879 		    FREE(address);
1880 		} else {
1881 		    FREE(address);
1882 		    return;
1883 		}
1884 	    }
1885 	    if (is_url(*owner_address_p) != MAILTO_URL_TYPE) {
1886 		/*
1887 		 * The address is a URL.  Just follow the link.
1888 		 */
1889 		set_address(&newdoc, *owner_address_p);
1890 		newdoc.internal_link = FALSE;
1891 	    } else {
1892 		/*
1893 		 * The owner_address is a mailto:  URL.
1894 		 */
1895 		const char *kp = HText_getRevTitle();
1896 		const char *id = HText_getMessageID();
1897 		char *tmptitle = NULL;
1898 
1899 		if (!kp && HTMainAnchor) {
1900 		    kp = HTAnchor_subject(HTMainAnchor);
1901 		    if (non_empty(kp)) {
1902 			if (strncasecomp(kp, "Re: ", 4)) {
1903 			    StrAllocCopy(tmptitle, "Re: ");
1904 			    StrAllocCat(tmptitle, kp);
1905 			    kp = tmptitle;
1906 			}
1907 		    }
1908 		}
1909 
1910 		if (strchr(*owner_address_p, ':') != NULL)
1911 		    /*
1912 		     * Send a reply.  The address is after the colon.
1913 		     */
1914 		    reply_by_mail(strchr(*owner_address_p, ':') + 1,
1915 				  curdoc.address,
1916 				  NonNull(kp), id);
1917 		else
1918 		    reply_by_mail(*owner_address_p, curdoc.address,
1919 				  NonNull(kp), id);
1920 
1921 		FREE(tmptitle);
1922 		*refresh_screen = TRUE;		/* to force a showpage */
1923 	    }
1924 	}
1925     }
1926 }
1927 
1928 #ifdef USE_CACHEJAR
handle_LYK_CACHE_JAR(int * cmd)1929 static BOOLEAN handle_LYK_CACHE_JAR(int *cmd)
1930 {
1931     /*
1932      * Don't do this if already viewing cache jar.
1933      */
1934     if (!isLYNXCACHE(curdoc.address)) {
1935 	set_address(&newdoc, STR_LYNXCACHE "/");
1936 	LYFreePostData(&newdoc);
1937 	FREE(newdoc.bookmark);
1938 	newdoc.isHEAD = FALSE;
1939 	newdoc.safe = FALSE;
1940 	newdoc.internal_link = FALSE;
1941 	LYforce_no_cache = TRUE;
1942 	if (LYValidate || check_realm) {
1943 	    LYPermitURL = TRUE;
1944 	}
1945     } else {
1946 	/*
1947 	 * If already in the cache jar, get out.
1948 	 */
1949 	*cmd = LYK_PREV_DOC;
1950 	return TRUE;
1951     }
1952     return FALSE;
1953 }
1954 #endif /* USE_CACHEJAR */
1955 
handle_LYK_COOKIE_JAR(int * cmd)1956 static BOOLEAN handle_LYK_COOKIE_JAR(int *cmd)
1957 {
1958     /*
1959      * Don't do if already viewing the cookie jar.
1960      */
1961     if (!isLYNXCOOKIE(curdoc.address)) {
1962 	set_address(&newdoc, "LYNXCOOKIE:/");
1963 	LYFreePostData(&newdoc);
1964 	FREE(newdoc.bookmark);
1965 	newdoc.isHEAD = FALSE;
1966 	newdoc.safe = FALSE;
1967 	newdoc.internal_link = FALSE;
1968 	LYforce_no_cache = TRUE;
1969 	if (LYValidate || check_realm) {
1970 	    LYPermitURL = TRUE;
1971 	}
1972     } else {
1973 	/*
1974 	 * If already in the cookie jar, get out.
1975 	 */
1976 	*cmd = LYK_PREV_DOC;
1977 	return TRUE;
1978     }
1979     return FALSE;
1980 }
1981 
1982 #if defined(DIRED_SUPPORT)
handle_LYK_CREATE(void)1983 static void handle_LYK_CREATE(void)
1984 {
1985     if (lynx_edit_mode && !no_dired_support) {
1986 	if (local_create(&curdoc) > 0) {
1987 	    DIRED_UNCACHE_1;
1988 	    move_address(&newdoc, &curdoc);
1989 	    LYFreePostData(&newdoc);
1990 	    FREE(newdoc.bookmark);
1991 	    newdoc.isHEAD = FALSE;
1992 	    newdoc.safe = FALSE;
1993 	    newdoc.line = curdoc.line;
1994 	    newdoc.link = curdoc.link > -1 ? curdoc.link : 0;
1995 	    LYclear();
1996 	}
1997     }
1998 }
1999 #endif /* DIRED_SUPPORT */
2000 
handle_LYK_DEL_BOOKMARK(BOOLEAN * refresh_screen,int * old_c,int real_c)2001 static void handle_LYK_DEL_BOOKMARK(BOOLEAN *refresh_screen,
2002 				    int *old_c,
2003 				    int real_c)
2004 {
2005     if (curdoc.bookmark != NULL) {
2006 	if (HTConfirmDefault(CONFIRM_BOOKMARK_DELETE, NO) != YES)
2007 	    return;
2008 	remove_bookmark_link(links[curdoc.link].anchor_number - 1,
2009 			     curdoc.bookmark);
2010     } else {			/* behave like REFRESH for backward compatibility */
2011 	*refresh_screen = TRUE;
2012 	if (*old_c != real_c) {
2013 	    *old_c = real_c;
2014 	    lynx_force_repaint();
2015 	}
2016 	return;
2017     }
2018     do_cleanup_after_delete();
2019 }
2020 
2021 #if defined(DIRED_SUPPORT) || defined(VMS)
handle_LYK_DIRED_MENU(BOOLEAN * refresh_screen,int * old_c GCC_UNUSED,int real_c GCC_UNUSED)2022 static void handle_LYK_DIRED_MENU(BOOLEAN *refresh_screen,
2023 				  int *old_c GCC_UNUSED,
2024 				  int real_c GCC_UNUSED)
2025 {
2026 #ifdef VMS
2027     char *cp, *temp = 0;
2028     const char *test = HTGetProgramPath(ppCSWING);
2029 
2030     /*
2031      * Check if the CSwing Directory/File Manager is available.  Will be
2032      * disabled if CSWING path is NULL, zero-length, or "none" (case
2033      * insensitive), if no_file_url was set via the file_url restriction, if
2034      * no_goto_file was set for the anonymous account, or if HTDirAccess was
2035      * set to HT_DIR_FORBID or HT_DIR_SELECTIVE via the -nobrowse or -selective
2036      * switches.  - FM
2037      */
2038     if (isEmpty(test) ||
2039 	!strcasecomp(test, "none") ||
2040 	no_file_url || no_goto_file ||
2041 	HTDirAccess == HT_DIR_FORBID ||
2042 	HTDirAccess == HT_DIR_SELECTIVE) {
2043 	if (*old_c != real_c) {
2044 	    *old_c = real_c;
2045 	    HTUserMsg(DFM_NOT_AVAILABLE);
2046 	}
2047 	return;
2048     }
2049 
2050     /*
2051      * If we are viewing a local directory listing or a local file which is not
2052      * temporary, invoke CSwing with the URLs directory converted to VMS path
2053      * specs and passed as the argument, so we start up CSwing positioned on
2054      * that node of the directory tree.  Otherwise, pass the current default
2055      * directory as the argument.  - FM
2056      */
2057     if (LYisLocalFile(curdoc.address) &&
2058 	strncasecomp(curdoc.address,
2059 		     lynx_temp_space, strlen(lynx_temp_space))) {
2060 	/*
2061 	 * We are viewing a local directory or a local file which is not
2062 	 * temporary.  - FM
2063 	 */
2064 	struct stat stat_info;
2065 
2066 	cp = HTParse(curdoc.address, "", PARSE_PATH | PARSE_PUNCTUATION);
2067 	HTUnEscape(cp);
2068 	if (HTStat(cp, &stat_info) == -1) {
2069 	    CTRACE((tfp, "mainloop: Can't stat %s\n", cp));
2070 	    FREE(cp);
2071 	    HTSprintf0(&temp, "%s []", HTGetProgramPath(ppCSWING));
2072 	    *refresh_screen = TRUE;	/* redisplay */
2073 	} else {
2074 	    char *VMSdir = NULL;
2075 
2076 	    if (S_ISDIR(stat_info.st_mode)) {
2077 		/*
2078 		 * We're viewing a local directory.  Make that the CSwing
2079 		 * argument.  - FM
2080 		 */
2081 		LYAddPathSep(&cp);
2082 		StrAllocCopy(VMSdir, HTVMS_name("", cp));
2083 		FREE(cp);
2084 	    } else {
2085 		/*
2086 		 * We're viewing a local file.  Make its directory the CSwing
2087 		 * argument.  - FM
2088 		 */
2089 		StrAllocCopy(VMSdir, HTVMS_name("", cp));
2090 		FREE(cp);
2091 		if ((cp = strrchr(VMSdir, ']')) != NULL) {
2092 		    *(cp + 1) = '\0';
2093 		} else if ((cp = strrchr(VMSdir, ':')) != NULL) {
2094 		    *(cp + 1) = '\0';
2095 		}
2096 	    }
2097 	    HTSprintf0(&temp, "%s %s", HTGetProgramPath(ppCSWING), VMSdir);
2098 	    FREE(VMSdir);
2099 	    /*
2100 	     * Uncache the current document in case we change, move, or delete
2101 	     * it during the CSwing session.  - FM
2102 	     */
2103 	    /* could use DIRED_UNCACHE_1 but it's currently only defined
2104 	       for dired - kw */
2105 	    HTuncache_current_document();
2106 	    move_address(&newdoc, &curdoc);
2107 	    StrAllocCopy(newdoc.title, NonNull(curdoc.title));
2108 	    StrAllocCopy(newdoc.bookmark, curdoc.bookmark);
2109 	    newdoc.line = curdoc.line;
2110 	    newdoc.link = curdoc.link;
2111 	}
2112     } else {
2113 	/*
2114 	 * We're not viewing a local directory or file.  Pass CSwing the
2115 	 * current default directory as an argument and don't uncache the
2116 	 * current document.  - FM
2117 	 */
2118 	HTSprintf0(&temp, "%s []", HTGetProgramPath(ppCSWING));
2119 	*refresh_screen = TRUE;	/* redisplay */
2120     }
2121     stop_curses();
2122     LYSystem(temp);
2123     start_curses();
2124     FREE(temp);
2125 #else
2126     /*
2127      * Don't do if not allowed or already viewing the menu.
2128      */
2129     if (lynx_edit_mode && !no_dired_support &&
2130 	!LYIsUIPage(curdoc.address, UIP_DIRED_MENU) &&
2131 	strcmp(NonNull(curdoc.title), DIRED_MENU_TITLE)) {
2132 	dired_options(&curdoc, &newdoc.address);
2133 	*refresh_screen = TRUE;	/* redisplay */
2134     }
2135 #endif /* VMS */
2136 }
2137 #endif /* defined(DIRED_SUPPORT) || defined(VMS) */
2138 
handle_LYK_DOWNLOAD(int * cmd,int * old_c,int real_c)2139 static int handle_LYK_DOWNLOAD(int *cmd,
2140 			       int *old_c,
2141 			       int real_c)
2142 {
2143 
2144     /*
2145      * Don't do if both download and disk_save are restricted.
2146      */
2147     if (LYValidate ||
2148 	(no_download && !override_no_download && no_disk_save)) {
2149 	if (*old_c != real_c) {
2150 	    *old_c = real_c;
2151 	    HTUserMsg(DOWNLOAD_DISABLED);
2152 	}
2153 	return 0;
2154     }
2155 
2156     /*
2157      * Don't do if already viewing download options page.
2158      */
2159     if (LYIsUIPage(curdoc.address, UIP_DOWNLOAD_OPTIONS))
2160 	return 0;
2161 
2162     if (do_change_link() == -1)
2163 	return 1;		/* mouse stuff was confused, ignore - kw */
2164     if (nlinks > 0) {
2165 	if (links[curdoc.link].type == WWW_FORM_LINK_TYPE) {
2166 	    if (links[curdoc.link].l_form->type == F_SUBMIT_TYPE ||
2167 		links[curdoc.link].l_form->type == F_IMAGE_SUBMIT_TYPE ||
2168 		links[curdoc.link].l_form->type == F_TEXT_SUBMIT_TYPE) {
2169 		if (links[curdoc.link].l_form->submit_method ==
2170 		    URL_MAIL_METHOD) {
2171 		    if (*old_c != real_c) {
2172 			*old_c = real_c;
2173 			HTUserMsg(NO_DOWNLOAD_MAILTO_ACTION);
2174 		    }
2175 		    return 0;
2176 		}
2177 		if (isLYNXOPTIONS(links[curdoc.link].l_form->submit_action)) {
2178 		    if (*old_c != real_c) {
2179 			*old_c = real_c;
2180 			HTUserMsg(NO_DOWNLOAD_SPECIAL);
2181 		    }
2182 		    return 0;
2183 		}
2184 		HTOutputFormat = HTAtom_for("www/download");
2185 		LYforce_no_cache = TRUE;
2186 		*cmd = LYK_ACTIVATE;
2187 		return 2;
2188 	    }
2189 	    if (*old_c != real_c) {
2190 		*old_c = real_c;
2191 		HTUserMsg(NO_DOWNLOAD_INPUT);
2192 	    }
2193 
2194 	} else if (isLYNXCOOKIE(curdoc.address)) {
2195 	    if (*old_c != real_c) {
2196 		*old_c = real_c;
2197 		HTUserMsg(NO_DOWNLOAD_COOKIES);
2198 	    }
2199 	} else if (LYIsUIPage(curdoc.address, UIP_PRINT_OPTIONS)) {
2200 	    if (*old_c != real_c) {
2201 		*old_c = real_c;
2202 		HTUserMsg(NO_DOWNLOAD_PRINT_OP);
2203 	    }
2204 #ifdef DIRED_SUPPORT
2205 	} else if (LYIsUIPage(curdoc.address, UIP_UPLOAD_OPTIONS)) {
2206 	    if (*old_c != real_c) {
2207 		*old_c = real_c;
2208 		HTUserMsg(NO_DOWNLOAD_UPLOAD_OP);
2209 	    }
2210 
2211 	} else if (LYIsUIPage(curdoc.address, UIP_PERMIT_OPTIONS)) {
2212 	    if (*old_c != real_c) {
2213 		*old_c = real_c;
2214 		HTUserMsg(NO_DOWNLOAD_PERMIT_OP);
2215 	    }
2216 
2217 	} else if (lynx_edit_mode && !no_dired_support &&
2218 		   !strstr(links[curdoc.link].lname, "/SugFile=")) {
2219 	    /*
2220 	     * Don't bother making a /tmp copy of the local file.
2221 	     */
2222 	    static DocInfo temp;
2223 
2224 	    copy_address(&temp, &newdoc);
2225 	    set_address(&newdoc, links[curdoc.link].lname);
2226 	    if (LYdownload_options(&newdoc.address,
2227 				   links[curdoc.link].lname) < 0)
2228 		copy_address(&newdoc, &temp);
2229 	    else
2230 		newdoc.internal_link = FALSE;
2231 	    LYFreeDocInfo(&temp);
2232 #endif /* DIRED_SUPPORT */
2233 
2234 	} else if (LYIsUIPage(curdoc.address, UIP_HISTORY) &&
2235 		   isLYNXHIST(links[curdoc.link].lname)) {
2236 	    int number = atoi(links[curdoc.link].lname + LEN_LYNXHIST);
2237 
2238 	    if (number >= nhist || number < 0) {
2239 		HTUserMsg(NO_DOWNLOAD_SPECIAL);
2240 		return 0;
2241 	    }
2242 	    if ((HDOC(number).post_data != NULL &&
2243 		 HDOC(number).safe != TRUE) &&
2244 		HTConfirm(CONFIRM_POST_RESUBMISSION) == FALSE) {
2245 		HTInfoMsg(CANCELLED);
2246 		return 0;
2247 	    }
2248 	    /*
2249 	     * OK, we download from history page, restore URL from stack.
2250 	     */
2251 	    copy_address(&newdoc, &HDOC(number));
2252 	    StrAllocCopy(newdoc.title, LYGetHiliteStr(curdoc.link, 0));
2253 	    StrAllocCopy(newdoc.bookmark, HDOC(number).bookmark);
2254 	    LYFreePostData(&newdoc);
2255 	    if (HDOC(number).post_data)
2256 		BStrCopy(newdoc.post_data,
2257 			 HDOC(number).post_data);
2258 	    if (HDOC(number).post_content_type)
2259 		StrAllocCopy(newdoc.post_content_type,
2260 			     HDOC(number).post_content_type);
2261 	    newdoc.isHEAD = HDOC(number).isHEAD;
2262 	    newdoc.safe = HDOC(number).safe;
2263 	    newdoc.internal_link = FALSE;
2264 	    newdoc.link = (user_mode == NOVICE_MODE) ? 1 : 0;
2265 	    HTOutputFormat = HTAtom_for("www/download");
2266 	    LYUserSpecifiedURL = TRUE;
2267 	    /*
2268 	     * Force the document to be reloaded.
2269 	     */
2270 	    LYforce_no_cache = TRUE;
2271 
2272 	} else if (!StrNCmp(links[curdoc.link].lname, "data:", 5)) {
2273 	    if (*old_c != real_c) {
2274 		*old_c = real_c;
2275 		HTAlert(UNSUPPORTED_DATA_URL);
2276 	    }
2277 
2278 	} else if (isLYNXCOOKIE(links[curdoc.link].lname) ||
2279 #ifdef USE_CACHEJAR
2280 		   isLYNXCACHE(links[curdoc.link].lname) ||
2281 #endif
2282 		   isLYNXDIRED(links[curdoc.link].lname) ||
2283 		   isLYNXDOWNLOAD(links[curdoc.link].lname) ||
2284 		   isLYNXPRINT(links[curdoc.link].lname) ||
2285 		   isLYNXOPTIONS(links[curdoc.link].lname) ||
2286 		   isLYNXHIST(links[curdoc.link].lname) ||
2287 	    /* handled above if valid - kw */
2288 /* @@@ should next two be downloadable? - kw */
2289 		   isLYNXHIST(links[curdoc.link].lname) ||
2290 		   isLYNXCFLAGS(links[curdoc.link].lname) ||
2291 		   isLYNXEXEC(links[curdoc.link].lname) ||
2292 		   isLYNXPROG(links[curdoc.link].lname)) {
2293 	    HTUserMsg(NO_DOWNLOAD_SPECIAL);
2294 
2295 	} else if (isMAILTO_URL(links[curdoc.link].lname)) {
2296 	    HTUserMsg(NO_DOWNLOAD_MAILTO_LINK);
2297 
2298 	    /*
2299 	     * From here on we could have a remote host, so check if that's
2300 	     * allowed.
2301 	     *
2302 	     * We copy all these checks from getfile() to LYK_DOWNLOAD here
2303 	     * because LYNXDOWNLOAD:// will NOT be pushing the previous
2304 	     * document into the history stack so preserve getfile() from
2305 	     * returning a wrong status (NULLFILE).
2306 	     */
2307 	} else if (local_host_only &&
2308 		   !(LYisLocalHost(links[curdoc.link].lname) ||
2309 		     LYisLocalAlias(links[curdoc.link].lname))) {
2310 	    HTUserMsg(ACCESS_ONLY_LOCALHOST);
2311 	} else {		/* Not a forms, options or history link */
2312 	    /*
2313 	     * Follow a normal link or anchor.  Note that if it's an anchor
2314 	     * within the same document, entire document will be downloaded.
2315 	     */
2316 	    set_address(&newdoc, links[curdoc.link].lname);
2317 	    StrAllocCopy(newdoc.title, LYGetHiliteStr(curdoc.link, 0));
2318 	    /*
2319 	     * Might be an internal link in the same doc from a POST form.  If
2320 	     * so, don't free the content.  - kw
2321 	     */
2322 	    if (track_internal_links) {
2323 		if (links[curdoc.link].type != WWW_INTERN_LINK_TYPE) {
2324 		    LYFreePostData(&newdoc);
2325 		    FREE(newdoc.bookmark);
2326 		    newdoc.isHEAD = FALSE;
2327 		    newdoc.safe = FALSE;
2328 		}
2329 	    } else {
2330 		/*
2331 		 * Might be an anchor in the same doc from a POST form.  If so,
2332 		 * don't free the content.  -- FM
2333 		 */
2334 		if (are_different(&curdoc, &newdoc)) {
2335 		    LYFreePostData(&newdoc);
2336 		    FREE(newdoc.bookmark);
2337 		    newdoc.isHEAD = FALSE;
2338 		    newdoc.safe = FALSE;
2339 		}
2340 	    }
2341 	    newdoc.internal_link = FALSE;
2342 	    newdoc.link = (user_mode == NOVICE_MODE) ? 1 : 0;
2343 	    HTOutputFormat = HTAtom_for("www/download");
2344 	    /*
2345 	     * Force the document to be reloaded.
2346 	     */
2347 	    LYforce_no_cache = TRUE;
2348 	}
2349     } else if (*old_c != real_c) {
2350 	*old_c = real_c;
2351 	HTUserMsg(NO_DOWNLOAD_CHOICE);
2352     }
2353     return 0;
2354 }
2355 
handle_LYK_DOWN_xxx(int * old_c,int real_c,int scroll_by)2356 static void handle_LYK_DOWN_xxx(int *old_c,
2357 				int real_c,
2358 				int scroll_by)
2359 {
2360     int i;
2361 
2362     if (more_text) {
2363 	LYChgNewline(scroll_by);
2364 	if (nlinks > 0 && curdoc.link > -1 &&
2365 	    links[curdoc.link].ly > scroll_by) {
2366 	    newdoc.link = curdoc.link;
2367 	    for (i = 0; links[i].ly <= scroll_by; i++)
2368 		--newdoc.link;
2369 	}
2370     } else if (*old_c != real_c) {
2371 	*old_c = real_c;
2372 	HTInfoMsg(ALREADY_AT_END);
2373     }
2374 }
2375 
handle_LYK_DOWN_HALF(int * old_c,int real_c)2376 static void handle_LYK_DOWN_HALF(int *old_c,
2377 				 int real_c)
2378 {
2379     handle_LYK_DOWN_xxx(old_c, real_c, display_lines / 2);
2380 }
2381 
handle_LYK_DOWN_LINK(int * follow_col,int * old_c,int real_c)2382 static void handle_LYK_DOWN_LINK(int *follow_col,
2383 				 int *old_c,
2384 				 int real_c)
2385 {
2386     if (curdoc.link < (nlinks - 1)) {	/* more links? */
2387 	int newlink;
2388 
2389 	if (*follow_col == -1) {
2390 	    const char *text = LYGetHiliteStr(curdoc.link, 0);
2391 
2392 	    *follow_col = links[curdoc.link].lx;
2393 
2394 	    if (text != NULL)
2395 		*follow_col += (int) strlen(text) / 2;
2396 	}
2397 
2398 	newlink = find_link_near_col(*follow_col, 1);
2399 	if (newlink > -1) {
2400 	    set_curdoc_link(newlink);
2401 	} else if (more_text) {	/* next page */
2402 	    LYChgNewline(display_lines);
2403 	} else if (*old_c != real_c) {
2404 	    *old_c = real_c;
2405 	    HTUserMsg(NO_LINKS_BELOW);
2406 	    return;
2407 	}
2408     } else if (more_text) {	/* next page */
2409 	LYChgNewline(display_lines);
2410     } else if (*old_c != real_c) {
2411 	*old_c = real_c;
2412 	HTInfoMsg(ALREADY_AT_END);
2413     }
2414 }
2415 
handle_LYK_DOWN_TWO(int * old_c,int real_c)2416 static void handle_LYK_DOWN_TWO(int *old_c,
2417 				int real_c)
2418 {
2419     handle_LYK_DOWN_xxx(old_c, real_c, 2);
2420 }
2421 
handle_LYK_DWIMEDIT(int * cmd,int * old_c,int real_c)2422 static int handle_LYK_DWIMEDIT(int *cmd,
2423 			       int *old_c,
2424 			       int real_c)
2425 {
2426 #ifdef TEXTAREA_AUTOEXTEDIT
2427     /*
2428      * If we're in a forms TEXTAREA, invoke the editor on *its* contents,
2429      * rather than attempting to edit the html source document.  KED
2430      */
2431     if (nlinks > 0 &&
2432 	LinkIsTextarea(curdoc.link)) {
2433 	*cmd = LYK_EDIT_TEXTAREA;
2434 	return 2;
2435     }
2436 
2437     /*
2438      * If we're in a forms TEXT type, tell user the request is bogus (though in
2439      * reality, without this trap, if the document with the TEXT field is
2440      * local, the editor *would* be invoked on the source .html file; eg, the
2441      * o(ptions) form tempfile).
2442      *
2443      * [This is done to avoid possible user confusion, due to auto invocation
2444      * of the editor on the TEXTAREA's contents via the above if() statement.]
2445      */
2446     if (nlinks > 0 &&
2447 	links[curdoc.link].type == WWW_FORM_LINK_TYPE &&
2448 	links[curdoc.link].l_form->type == F_TEXT_TYPE) {
2449 	HTUserMsg(CANNOT_EDIT_FIELD);
2450 	return 1;
2451     }
2452 
2453     if (no_editor) {
2454 	if (*old_c != real_c) {
2455 	    *old_c = real_c;
2456 	    HTUserMsg(ANYEDIT_DISABLED);
2457 	}
2458 	return 1;
2459     }
2460 #endif /* TEXTAREA_AUTOEXTEDIT */
2461     return 0;
2462 }
2463 
handle_LYK_ECGOTO(int * ch,bstring ** user_input,char ** old_user_input,int * old_c,int real_c)2464 static int handle_LYK_ECGOTO(int *ch,
2465 			     bstring **user_input,
2466 			     char **old_user_input,
2467 			     int *old_c,
2468 			     int real_c)
2469 {
2470     if (no_goto && !LYValidate) {
2471 	/*
2472 	 * Go to not allowed.  - FM
2473 	 */
2474 	if (*old_c != real_c) {
2475 	    *old_c = real_c;
2476 	    HTUserMsg(GOTO_DISALLOWED);
2477 	}
2478 	return 0;
2479     }
2480 #ifdef DIRED_SUPPORT
2481     if (LYIsUIPage(curdoc.address, UIP_DIRED_MENU) ||
2482 	LYIsUIPage(curdoc.address, UIP_PERMIT_OPTIONS) ||
2483 	LYIsUIPage(curdoc.address, UIP_UPLOAD_OPTIONS)) {
2484 	/*
2485 	 * Disallow editing of File Management URLs.  - FM
2486 	 */
2487 	if (*old_c != real_c) {
2488 	    *old_c = real_c;
2489 	    HTUserMsg(EDIT_FM_MENU_URLS_DISALLOWED);
2490 	}
2491 	return 0;
2492     }
2493 #endif /* DIRED_SUPPORT */
2494 
2495     /*
2496      * Save the current user_input string, and load the current
2497      * document's address.
2498      */
2499     StrAllocCopy(*old_user_input, (*user_input)->str);
2500     BStrCopy0((*user_input), curdoc.address);
2501 
2502     /*
2503      * Warn the user if the current document has POST data associated with it.
2504      * - FM
2505      */
2506     if (curdoc.post_data)
2507 	HTAlert(CURRENT_DOC_HAS_POST_DATA);
2508 
2509     /*
2510      * Offer the current document's URL for editing.  - FM
2511      */
2512     _statusline(EDIT_CURDOC_URL);
2513     if (((*ch = LYgetBString(user_input, VISIBLE, 0, RECALL_URL)) >= 0) &&
2514 	!isBEmpty(*user_input) &&
2515 	strcmp((*user_input)->str, curdoc.address)) {
2516 	LYTrimAllStartfile((*user_input)->str);
2517 	if (!isBEmpty(*user_input)) {
2518 	    return 2;
2519 	}
2520     }
2521     /*
2522      * User cancelled via ^G, a full deletion, or not modifying the URL.  - FM
2523      */
2524     HTInfoMsg(CANCELLED);
2525     BStrCopy0((*user_input), *old_user_input);
2526     FREE(*old_user_input);
2527     return 0;
2528 }
2529 
handle_LYK_EDIT(int * old_c,int real_c)2530 static void handle_LYK_EDIT(int *old_c,
2531 			    int real_c)
2532 {
2533 #ifdef DIRED_SUPPORT
2534     char *cp;
2535     char *tp = NULL;
2536     struct stat dir_info;
2537 #endif /* DIRED_SUPPORT */
2538 
2539     if (no_editor) {
2540 	if (*old_c != real_c) {
2541 	    *old_c = real_c;
2542 	    HTUserMsg(EDIT_DISABLED);
2543 	}
2544     }
2545 #ifdef DIRED_SUPPORT
2546     /*
2547      * Allow the user to edit the link rather than curdoc in edit mode.
2548      */
2549     else if (lynx_edit_mode &&
2550 	     non_empty(editor) && !no_dired_support) {
2551 	if (nlinks > 0) {
2552 	    cp = links[curdoc.link].lname;
2553 	    if (is_url(cp) == FILE_URL_TYPE) {
2554 		cp = HTfullURL_toFile(cp);
2555 		StrAllocCopy(tp, cp);
2556 		FREE(cp);
2557 
2558 		if (stat(tp, &dir_info) == -1) {
2559 		    HTAlert(NO_STATUS);
2560 		} else {
2561 		    if (S_ISREG(dir_info.st_mode)) {
2562 			StrAllocCopy(tp, links[curdoc.link].lname);
2563 			HTUnEscapeSome(tp, "/");
2564 			if (edit_current_file(tp, curdoc.link, -1)) {
2565 			    DIRED_UNCACHE_1;
2566 			    move_address(&newdoc, &curdoc);
2567 #ifdef NO_SEEK_OLD_POSITION
2568 			    /*
2569 			     * Go to top of file.
2570 			     */
2571 			    newdoc.line = 1;
2572 			    newdoc.link = 0;
2573 #else
2574 			    /*
2575 			     * Seek old position, which probably changed.
2576 			     */
2577 			    newdoc.line = curdoc.line;
2578 			    newdoc.link = curdoc.link;
2579 #endif /* NO_SEEK_OLD_POSITION */
2580 			    LYclear();	/* clear the screen */
2581 			}
2582 		    }
2583 		}
2584 		FREE(tp);
2585 	    }
2586 	}
2587     }
2588 #endif /* DIRED_SUPPORT */
2589     else if (non_empty(editor)) {
2590 	if (edit_current_file(newdoc.address, curdoc.link, LYGetNewline())) {
2591 	    HTuncache_current_document();
2592 	    LYforce_no_cache = TRUE;	/*force reload of document */
2593 	    free_address(&curdoc);	/* so it doesn't get pushed */
2594 #ifdef NO_SEEK_OLD_POSITION
2595 	    /*
2596 	     * Go to top of file.
2597 	     */
2598 	    newdoc.line = 1;
2599 	    newdoc.link = 0;
2600 #else
2601 	    /*
2602 	     * Seek old position, which probably changed.
2603 	     */
2604 	    newdoc.line = curdoc.line;
2605 	    newdoc.link = curdoc.link;
2606 #endif /* NO_SEEK_OLD_POSITION */
2607 	    LYclear();		/* clear the screen */
2608 	}
2609 
2610     } else {
2611 	if (*old_c != real_c) {
2612 	    *old_c = real_c;
2613 	    HTUserMsg(NO_EDITOR);
2614 	}
2615     }
2616 }
2617 
handle_LYK_DWIMHELP(const char ** cshelpfile)2618 static void handle_LYK_DWIMHELP(const char **cshelpfile)
2619 {
2620     /*
2621      * Currently a help file different from the main 'helpfile' is shown only
2622      * if current link is a text input form field.  - kw
2623      */
2624     if (curdoc.link >= 0 && curdoc.link < nlinks &&
2625 	!FormIsReadonly(links[curdoc.link].l_form) &&
2626 	LinkIsTextLike(curdoc.link)) {
2627 	*cshelpfile = LYLineeditHelpURL();
2628     }
2629 }
2630 
handle_LYK_EDIT_TEXTAREA(BOOLEAN * refresh_screen,int * old_c,int real_c)2631 static void handle_LYK_EDIT_TEXTAREA(BOOLEAN *refresh_screen,
2632 				     int *old_c,
2633 				     int real_c)
2634 {
2635     if (no_editor) {
2636 	if (*old_c != real_c) {
2637 	    *old_c = real_c;
2638 	    HTUserMsg(ANYEDIT_DISABLED);
2639 	}
2640     } else if (isEmpty(editor)) {
2641 	if (*old_c != real_c) {
2642 	    *old_c = real_c;
2643 	    HTUserMsg(NO_EDITOR);
2644 	}
2645     } else if (LinkIsTextarea(curdoc.link)) {
2646 	/*
2647 	 * if the current link is in a form TEXTAREA, it requires handling
2648 	 * for the possible multiple lines.
2649 	 */
2650 
2651 	/* stop screen */
2652 	stop_curses();
2653 
2654 	(void) HText_EditTextArea(&links[curdoc.link]);
2655 
2656 	/*
2657 	 * TODO:
2658 	 * Move cursor "n" lines from the current line to position it on the
2659 	 * 1st trailing blank line in the now edited TEXTAREA.  If the target
2660 	 * line/ anchor requires us to scroll up/down, position the target in
2661 	 * the approximate center of the screen.
2662 	 */
2663 
2664 	/* curdoc.link += n; */
2665 	/* works, except for page crossing, */
2666 	/* damnit; why is nothing ever easy */
2667 
2668 	/* start screen */
2669 	start_curses();
2670 	*refresh_screen = TRUE;
2671 
2672     } else if (LinkIsTextLike(curdoc.link)) {
2673 	/*
2674 	 * other text fields are single-line
2675 	 */
2676 	stop_curses();
2677 	HText_EditTextField(&links[curdoc.link]);
2678 	start_curses();
2679 	*refresh_screen = TRUE;
2680     } else {
2681 
2682 	HTInfoMsg(NOT_IN_TEXTAREA_NOEDIT);
2683     }
2684 }
2685 
handle_LYK_ELGOTO(int * ch,bstring ** user_input,char ** old_user_input,int * old_c,int real_c)2686 static int handle_LYK_ELGOTO(int *ch,
2687 			     bstring **user_input,
2688 			     char **old_user_input,
2689 			     int *old_c,
2690 			     int real_c)
2691 {
2692     if (no_goto && !LYValidate) {
2693 	/*
2694 	 * Go to not allowed.  - FM
2695 	 */
2696 	if (*old_c != real_c) {
2697 	    *old_c = real_c;
2698 	    HTUserMsg(GOTO_DISALLOWED);
2699 	}
2700 	return 0;
2701     }
2702     if (!(nlinks > 0 && curdoc.link > -1) ||
2703 	(links[curdoc.link].type == WWW_FORM_LINK_TYPE &&
2704 	 links[curdoc.link].l_form->type != F_SUBMIT_TYPE &&
2705 	 links[curdoc.link].l_form->type != F_IMAGE_SUBMIT_TYPE &&
2706 	 links[curdoc.link].l_form->type != F_TEXT_SUBMIT_TYPE)) {
2707 	/*
2708 	 * No links on page, or not a normal link or form submit button.  - FM
2709 	 */
2710 	if (*old_c != real_c) {
2711 	    *old_c = real_c;
2712 	    HTUserMsg(NOT_ON_SUBMIT_OR_LINK);
2713 	}
2714 	return 0;
2715     }
2716     if ((links[curdoc.link].type == WWW_FORM_LINK_TYPE) &&
2717 	(isEmpty(links[curdoc.link].l_form->submit_action))) {
2718 	/*
2719 	 * Form submit button with no ACTION defined.  - FM
2720 	 */
2721 	if (*old_c != real_c) {
2722 	    *old_c = real_c;
2723 	    HTUserMsg(NO_FORM_ACTION);
2724 	}
2725 	return 0;
2726     }
2727 #ifdef DIRED_SUPPORT
2728     if (isLYNXDIRED(links[curdoc.link].lname) ||
2729 	LYIsUIPage(curdoc.address, UIP_DIRED_MENU) ||
2730 	LYIsUIPage(curdoc.address, UIP_PERMIT_OPTIONS) ||
2731 	LYIsUIPage(curdoc.address, UIP_UPLOAD_OPTIONS)) {
2732 	/*
2733 	 * Disallow editing of File Management URLs.  - FM
2734 	 */
2735 	if (*old_c != real_c) {
2736 	    *old_c = real_c;
2737 	    HTUserMsg(EDIT_FM_MENU_URLS_DISALLOWED);
2738 	}
2739 	return 0;
2740     }
2741 #endif /* DIRED_SUPPORT */
2742 
2743     /*
2744      * Save the current user_input string, and load the current link's
2745      * address.  - FM
2746      */
2747     StrAllocCopy(*old_user_input, (*user_input)->str);
2748     BStrCopy0((*user_input),
2749 	      ((links[curdoc.link].type == WWW_FORM_LINK_TYPE)
2750 	       ? links[curdoc.link].l_form->submit_action
2751 	       : links[curdoc.link].lname));
2752     /*
2753      * Offer the current link's URL for editing.  - FM
2754      */
2755     _statusline(EDIT_CURLINK_URL);
2756     if (((*ch = LYgetBString(user_input, VISIBLE, 0, RECALL_URL)) >= 0) &&
2757 	!isBEmpty(*user_input) &&
2758 	strcmp((*user_input)->str,
2759 	       ((links[curdoc.link].type == WWW_FORM_LINK_TYPE)
2760 		? links[curdoc.link].l_form->submit_action
2761 		: links[curdoc.link].lname))) {
2762 	LYTrimAllStartfile((*user_input)->str);
2763 	if (!isBEmpty(*user_input)) {
2764 	    return 2;
2765 	}
2766     }
2767     /*
2768      * User cancelled via ^G, a full deletion, or not modifying the URL.  - FM
2769      */
2770     HTInfoMsg(CANCELLED);
2771     BStrCopy0((*user_input), *old_user_input);
2772     FREE(*old_user_input);
2773     return 0;
2774 }
2775 
2776 #ifdef USE_EXTERNALS
handle_LYK_EXTERN_LINK(BOOLEAN * refresh_screen)2777 static void handle_LYK_EXTERN_LINK(BOOLEAN *refresh_screen)
2778 {
2779     if ((nlinks > 0) && (links[curdoc.link].lname != NULL)) {
2780 	run_external(links[curdoc.link].lname, FALSE);
2781 	*refresh_screen = TRUE;
2782     }
2783 }
2784 
handle_LYK_EXTERN_PAGE(BOOLEAN * refresh_screen)2785 static void handle_LYK_EXTERN_PAGE(BOOLEAN *refresh_screen)
2786 {
2787     if (curdoc.address != NULL) {
2788 	run_external(curdoc.address, FALSE);
2789 	*refresh_screen = TRUE;
2790     }
2791 }
2792 #endif
2793 
handle_LYK_FASTBACKW_LINK(int * cmd,int * old_c,int real_c)2794 static BOOLEAN handle_LYK_FASTBACKW_LINK(int *cmd,
2795 					 int *old_c,
2796 					 int real_c)
2797 {
2798     int samepage = 0, nextlink = curdoc.link;
2799     int res;
2800     BOOLEAN code = FALSE;
2801 
2802     if (nlinks > 1) {
2803 
2804 	/*
2805 	 * If in textarea, move to first link or textarea group before it if
2806 	 * there is one on this screen.  - kw
2807 	 */
2808 	if (LinkIsTextarea(curdoc.link)) {
2809 	    int thisgroup = links[curdoc.link].l_form->number;
2810 	    char *thisname = links[curdoc.link].l_form->name;
2811 
2812 	    if (curdoc.link > 0 &&
2813 		!(LinkIsTextarea(0) &&
2814 		  links[0].l_form->number == thisgroup &&
2815 		  sametext(links[0].l_form->name, thisname))) {
2816 		do
2817 		    nextlink--;
2818 		while
2819 		    (LinkIsTextarea(nextlink) &&
2820 		     links[nextlink].l_form->number == thisgroup &&
2821 		     sametext(links[nextlink].l_form->name, thisname));
2822 		samepage = 1;
2823 
2824 	    } else if (!more_text && LYGetNewline() == 1 &&
2825 		       (LinkIsTextarea(0) &&
2826 			links[0].l_form->number == thisgroup &&
2827 			sametext(links[0].l_form->name, thisname)) &&
2828 		       !(LinkIsTextarea(nlinks - 1) &&
2829 			 links[nlinks - 1].l_form->number == thisgroup &&
2830 			 sametext(links[nlinks - 1].l_form->name, thisname))) {
2831 		nextlink = nlinks - 1;
2832 		samepage = 1;
2833 
2834 	    } else if (!more_text && LYGetNewline() == 1 && curdoc.link > 0) {
2835 		nextlink = 0;
2836 		samepage = 1;
2837 	    }
2838 	} else if (curdoc.link > 0) {
2839 	    nextlink--;
2840 	    samepage = 1;
2841 	} else if (!more_text && LYGetNewline() == 1) {
2842 	    nextlink = nlinks - 1;
2843 	    samepage = 1;
2844 	}
2845     }
2846 
2847     if (samepage) {
2848 	/*
2849 	 * If the link as determined so far is part of a group of textarea
2850 	 * fields, try to use the first of them that's on the screen instead.
2851 	 * - kw
2852 	 */
2853 	if (nextlink > 0 &&
2854 	    LinkIsTextarea(nextlink)) {
2855 	    int thisgroup = links[nextlink].l_form->number;
2856 	    char *thisname = links[nextlink].l_form->name;
2857 
2858 	    if (LinkIsTextarea(0) &&
2859 		links[0].l_form->number == thisgroup &&
2860 		sametext(links[0].l_form->name, thisname)) {
2861 		nextlink = 0;
2862 	    } else
2863 		while
2864 		    (nextlink > 1 &&
2865 		     LinkIsTextarea(nextlink - 1) &&
2866 		     links[nextlink - 1].l_form->number == thisgroup &&
2867 		     sametext(links[nextlink - 1].l_form->name, thisname)) {
2868 		    nextlink--;
2869 		}
2870 	}
2871 	set_curdoc_link(nextlink);
2872 
2873     } else if (LYGetNewline() > 1 &&	/* need a previous page */
2874 	       (res = HTGetLinkOrFieldStart(curdoc.link,
2875 					    &Newline, &newdoc.link,
2876 					    -1, TRUE)) != NO) {
2877 	if (res == LINK_DO_ARROWUP) {
2878 	    /*
2879 	     * It says we should use the normal PREV_LINK mechanism, so we'll
2880 	     * do that.  - kw
2881 	     */
2882 	    if (nlinks > 0)
2883 		curdoc.link = 0;
2884 	    *cmd = LYK_PREV_LINK;
2885 	    code = TRUE;
2886 	} else {
2887 	    LYChgNewline(1);	/* our line counting starts with 1 not 0 */
2888 	}
2889     } else if (*old_c != real_c) {
2890 	*old_c = real_c;
2891 	HTInfoMsg(NO_LINKS_ABOVE);
2892     }
2893     return code;
2894 }
2895 
handle_LYK_FASTFORW_LINK(int * old_c,int real_c)2896 static void handle_LYK_FASTFORW_LINK(int *old_c,
2897 				     int real_c)
2898 {
2899     int samepage = 0, nextlink = curdoc.link;
2900 
2901     if (nlinks > 1) {
2902 
2903 	/*
2904 	 * If in textarea, move to first link or field after it if there is one
2905 	 * on this screen.  - kw
2906 	 */
2907 	if (LinkIsTextarea(curdoc.link)) {
2908 	    int thisgroup = links[curdoc.link].l_form->number;
2909 	    char *thisname = links[curdoc.link].l_form->name;
2910 
2911 	    if (curdoc.link < nlinks - 1 &&
2912 		!(LinkIsTextarea(nlinks - 1) &&
2913 		  links[nlinks - 1].l_form->number == thisgroup &&
2914 		  sametext(links[nlinks - 1].l_form->name, thisname))) {
2915 		do
2916 		    nextlink++;
2917 		while
2918 		    (LinkIsTextarea(nextlink) &&
2919 		     links[nextlink].l_form->number == thisgroup &&
2920 		     sametext(links[nextlink].l_form->name, thisname));
2921 		samepage = 1;
2922 	    } else if (!more_text && LYGetNewline() == 1 && curdoc.link > 0) {
2923 		nextlink = 0;
2924 		samepage = 1;
2925 	    }
2926 	} else if (curdoc.link < nlinks - 1) {
2927 	    nextlink++;
2928 	    samepage = 1;
2929 	} else if (!more_text && LYGetNewline() == 1 && curdoc.link > 0) {
2930 	    nextlink = 0;
2931 	    samepage = 1;
2932 	}
2933     }
2934 
2935     if (samepage) {
2936 	set_curdoc_link(nextlink);
2937     } else if (!more_text && LYGetNewline() == 1 && curdoc.link == nlinks - 1) {
2938 	/*
2939 	 * At the bottom of list and there is only one page.  Move to the top
2940 	 * link on the page.
2941 	 */
2942 	set_curdoc_link(0);
2943 
2944     } else if (more_text &&	/* need a later page */
2945 	       HTGetLinkOrFieldStart(curdoc.link,
2946 				     &Newline, &newdoc.link,
2947 				     1, TRUE) != NO) {
2948 	LYChgNewline(1);	/* our line counting starts with 1 not 0 */
2949 	/* nothing more to do here */
2950 
2951     } else if (*old_c != real_c) {
2952 	*old_c = real_c;
2953 	HTInfoMsg(NO_LINKS_BELOW);
2954     }
2955     return;
2956 }
2957 
handle_LYK_FIRST_LINK(void)2958 static void handle_LYK_FIRST_LINK(void)
2959 {
2960     int i = curdoc.link;
2961 
2962     for (;;) {
2963 	if (--i < 0
2964 	    || links[i].ly != links[curdoc.link].ly) {
2965 	    set_curdoc_link(i + 1);
2966 	    break;
2967 	}
2968     }
2969 }
2970 
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)2971 static BOOLEAN handle_LYK_GOTO(int *ch,
2972 			       bstring **user_input,
2973 			       char **old_user_input,
2974 			       RecallType * recall,
2975 			       int *URLTotal,
2976 			       int *URLNum,
2977 			       BOOLEAN *FirstURLRecall,
2978 			       int *old_c,
2979 			       int real_c)
2980 {
2981 
2982     if (no_goto && !LYValidate) {
2983 	if (*old_c != real_c) {
2984 	    *old_c = real_c;
2985 	    HTUserMsg(GOTO_DISALLOWED);
2986 	}
2987 	return FALSE;
2988     }
2989 
2990     StrAllocCopy(*old_user_input, (*user_input)->str);
2991     if (!goto_buffer)
2992 	BStrCopy0((*user_input), "");
2993 
2994     *URLTotal = (Goto_URLs ? HTList_count(Goto_URLs) : 0);
2995     if (goto_buffer && !isBEmpty(*user_input)) {
2996 	*recall = ((*URLTotal > 1) ? RECALL_URL : NORECALL);
2997 	*URLNum = 0;
2998 	*FirstURLRecall = FALSE;
2999     } else {
3000 	*recall = ((*URLTotal >= 1) ? RECALL_URL : NORECALL);
3001 	*URLNum = *URLTotal;
3002 	*FirstURLRecall = TRUE;
3003     }
3004 
3005     /*
3006      * Ask the user.
3007      */
3008     _statusline(URL_TO_OPEN);
3009     if ((*ch = LYgetBString(user_input, VISIBLE, 0, *recall)) < 0) {
3010 	/*
3011 	 * User cancelled the Goto via ^G.  Restore user_input and
3012 	 * break.  - FM
3013 	 */
3014 	BStrCopy0((*user_input), *old_user_input);
3015 	FREE(*old_user_input);
3016 	HTInfoMsg(CANCELLED);
3017 	return FALSE;
3018     }
3019     return TRUE;
3020 }
3021 
handle_LYK_GROW_TEXTAREA(BOOLEAN * refresh_screen)3022 static void handle_LYK_GROW_TEXTAREA(BOOLEAN *refresh_screen)
3023 {
3024     /*
3025      * See if the current link is in a form TEXTAREA.
3026      */
3027     if (LinkIsTextarea(curdoc.link)) {
3028 
3029 	HText_ExpandTextarea(&links[curdoc.link], TEXTAREA_EXPAND_SIZE);
3030 
3031 	*refresh_screen = TRUE;
3032 
3033     } else {
3034 
3035 	HTInfoMsg(NOT_IN_TEXTAREA);
3036     }
3037 }
3038 
handle_LYK_HEAD(int * cmd)3039 static BOOLEAN handle_LYK_HEAD(int *cmd)
3040 {
3041     int c;
3042 
3043     if (nlinks > 0 &&
3044 	(links[curdoc.link].type != WWW_FORM_LINK_TYPE ||
3045 	 links[curdoc.link].l_form->type == F_SUBMIT_TYPE ||
3046 	 links[curdoc.link].l_form->type == F_IMAGE_SUBMIT_TYPE ||
3047 	 links[curdoc.link].l_form->type == F_TEXT_SUBMIT_TYPE)) {
3048 	/*
3049 	 * We have links, and the current link is a normal link or a form's
3050 	 * submit button.  - FM
3051 	 */
3052 	_statusline(HEAD_D_L_OR_CANCEL);
3053 	c = LYgetch_single();
3054 	if (c == 'D') {
3055 	    char *scheme = !isLYNXIMGMAP(curdoc.address)
3056 	    ? curdoc.address
3057 	    : curdoc.address + LEN_LYNXIMGMAP;
3058 
3059 	    if (LYCanDoHEAD(scheme) != TRUE) {
3060 		HTUserMsg(DOC_NOT_HTTP_URL);
3061 	    } else {
3062 		/*
3063 		 * Check if this is a reply from a POST, and if so, seek
3064 		 * confirmation if the safe element is not set.  - FM
3065 		 */
3066 		if ((curdoc.post_data != NULL &&
3067 		     curdoc.safe != TRUE) &&
3068 		    HTConfirm(CONFIRM_POST_DOC_HEAD) == FALSE) {
3069 		    HTInfoMsg(CANCELLED);
3070 		} else {
3071 		    HEAD_request = TRUE;
3072 		    LYforce_no_cache = TRUE;
3073 		    StrAllocCopy(newdoc.title, curdoc.title);
3074 		    if (HTLoadedDocumentIsHEAD()) {
3075 			HText_setNoCache(HTMainText);
3076 			free_address(&curdoc);
3077 		    } else {
3078 			StrAllocCat(newdoc.title, " - HEAD");
3079 		    }
3080 		}
3081 	    }
3082 	} else if (c == 'L') {
3083 	    if (links[curdoc.link].type != WWW_FORM_LINK_TYPE &&
3084 		StrNCmp(links[curdoc.link].lname, "http", 4) &&
3085 		StrNCmp(links[curdoc.link].lname, "LYNXIMGMAP:http", 15) &&
3086 		LYCanDoHEAD(links[curdoc.link].lname) != TRUE &&
3087 		(links[curdoc.link].type != WWW_INTERN_LINK_TYPE ||
3088 		 !curdoc.address ||
3089 		 StrNCmp(curdoc.address, "http", 4))) {
3090 		HTUserMsg(LINK_NOT_HTTP_URL);
3091 	    } else if (links[curdoc.link].type == WWW_FORM_LINK_TYPE &&
3092 		       FormIsReadonly(links[curdoc.link].l_form)) {
3093 		HTUserMsg(FORM_ACTION_DISABLED);
3094 	    } else if (links[curdoc.link].type == WWW_FORM_LINK_TYPE &&
3095 		       links[curdoc.link].l_form->submit_action != 0 &&
3096 		       !isLYNXCGI(links[curdoc.link].l_form->submit_action) &&
3097 		       StrNCmp(links[curdoc.link].l_form->submit_action,
3098 			       "http", 4)) {
3099 		HTUserMsg(FORM_ACTION_NOT_HTTP_URL);
3100 	    } else if (links[curdoc.link].type == WWW_FORM_LINK_TYPE &&
3101 		       links[curdoc.link].l_form->submit_method ==
3102 		       URL_POST_METHOD &&
3103 		       HTConfirm(CONFIRM_POST_LINK_HEAD) == FALSE) {
3104 		HTInfoMsg(CANCELLED);
3105 	    } else {
3106 		HEAD_request = TRUE;
3107 		LYforce_no_cache = TRUE;
3108 		*cmd = LYK_ACTIVATE;
3109 		return TRUE;
3110 	    }
3111 	}
3112     } else {
3113 	/*
3114 	 * We can offer only this document for a HEAD request.  Check if this
3115 	 * is a reply from a POST, and if so, seek confirmation if the safe
3116 	 * element is not set.  - FM
3117 	 */
3118 	if ((curdoc.post_data != NULL &&
3119 	     curdoc.safe != TRUE) &&
3120 	    HTConfirm(CONFIRM_POST_DOC_HEAD) == FALSE) {
3121 	    HTInfoMsg(CANCELLED);
3122 	} else {
3123 	    if (nlinks > 0) {
3124 		/*
3125 		 * The current link is a non-submittable form link, so prompt
3126 		 * the user to make it clear that the HEAD request would be for
3127 		 * the current document, not the form link.  - FM
3128 		 */
3129 		_statusline(HEAD_D_OR_CANCEL);
3130 		c = LYgetch_single();
3131 	    } else {
3132 		/*
3133 		 * No links, so we can just assume that the user wants a HEAD
3134 		 * request for the current document.  - FM
3135 		 */
3136 		c = 'D';
3137 	    }
3138 	    if (c == 'D') {
3139 		char *scheme = !isLYNXIMGMAP(curdoc.address)
3140 		? curdoc.address
3141 		: curdoc.address + LEN_LYNXIMGMAP;
3142 
3143 		/*
3144 		 * The user didn't cancel, so check if a HEAD request is
3145 		 * appropriate for the current document.  - FM
3146 		 */
3147 		if (LYCanDoHEAD(scheme) != TRUE) {
3148 		    HTUserMsg(DOC_NOT_HTTP_URL);
3149 		} else {
3150 		    HEAD_request = TRUE;
3151 		    LYforce_no_cache = TRUE;
3152 		    StrAllocCopy(newdoc.title, curdoc.title);
3153 		    if (HTLoadedDocumentIsHEAD()) {
3154 			HText_setNoCache(HTMainText);
3155 			free_address(&curdoc);
3156 		    } else {
3157 			StrAllocCat(newdoc.title, " - HEAD");
3158 		    }
3159 		}
3160 	    }
3161 	}
3162     }
3163     return FALSE;
3164 }
3165 
handle_LYK_HELP(const char ** cshelpfile)3166 static void handle_LYK_HELP(const char **cshelpfile)
3167 {
3168     char *my_value = NULL;
3169 
3170     if (*cshelpfile == NULL)
3171 	*cshelpfile = helpfile;
3172     StrAllocCopy(my_value, *cshelpfile);
3173     LYEnsureAbsoluteURL(&my_value, *cshelpfile, FALSE);
3174     if (!STREQ(curdoc.address, my_value)) {
3175 	/*
3176 	 * Set the filename.
3177 	 */
3178 	set_address(&newdoc, my_value);
3179 	/*
3180 	 * Make a name for this help file.
3181 	 */
3182 	StrAllocCopy(newdoc.title, gettext("Help Screen"));
3183 	LYFreePostData(&newdoc);
3184 	FREE(newdoc.bookmark);
3185 	newdoc.isHEAD = FALSE;
3186 	newdoc.safe = FALSE;
3187 	newdoc.internal_link = FALSE;
3188     }
3189     FREE(my_value);
3190     *cshelpfile = NULL;		/* reset pointer - kw */
3191 }
3192 
handle_LYK_HISTORICAL(void)3193 static void handle_LYK_HISTORICAL(void)
3194 {
3195 #ifdef USE_SOURCE_CACHE
3196     if (!HTcan_reparse_document()) {
3197 #endif
3198 	/*
3199 	 * Check if this is a reply from a POST, and if so, seek confirmation
3200 	 * of reload if the safe element is not set.  - FM
3201 	 */
3202 	if ((curdoc.post_data != NULL &&
3203 	     curdoc.safe != TRUE) &&
3204 	    confirm_post_resub(curdoc.address, NULL, 0, 0) == FALSE) {
3205 	    HTInfoMsg(WILL_NOT_RELOAD_DOC);
3206 	} else {
3207 	    HText_setNoCache(HTMainText);
3208 	    move_address(&newdoc, &curdoc);
3209 	    newdoc.line = curdoc.line;
3210 	    newdoc.link = curdoc.link;
3211 	}
3212 #ifdef USE_SOURCE_CACHE
3213     }				/* end if no bypass */
3214 #endif
3215     historical_comments = (BOOLEAN) !historical_comments;
3216     if (minimal_comments) {
3217 	HTAlert(historical_comments ?
3218 		HISTORICAL_ON_MINIMAL_OFF : HISTORICAL_OFF_MINIMAL_ON);
3219     } else {
3220 	HTAlert(historical_comments ?
3221 		HISTORICAL_ON_VALID_OFF : HISTORICAL_OFF_VALID_ON);
3222     }
3223 #ifdef USE_SOURCE_CACHE
3224     (void) reparse_document();
3225 #endif
3226     return;
3227 }
3228 
handle_LYK_HISTORY(int ForcePush)3229 static BOOLEAN handle_LYK_HISTORY(int ForcePush)
3230 {
3231     if (curdoc.title && !LYIsUIPage(curdoc.address, UIP_HISTORY)) {
3232 	/*
3233 	 * Don't do this if already viewing history page.
3234 	 *
3235 	 * Push the current file so that the history list contains the current
3236 	 * file for printing purposes.  Pop the file afterwards to prevent
3237 	 * multiple copies.
3238 	 */
3239 	if (TRACE && !LYUseTraceLog && LYCursesON) {
3240 	    LYHideCursor();	/* make sure cursor is down */
3241 #ifdef USE_SLANG
3242 	    LYaddstr("\n");
3243 #endif /* USE_SLANG */
3244 	    LYrefresh();
3245 	}
3246 	LYpush(&curdoc, ForcePush);
3247 
3248 	/*
3249 	 * Print history options to file.
3250 	 */
3251 	if (showhistory(&newdoc.address) < 0) {
3252 	    LYpop(&curdoc);
3253 	    return TRUE;
3254 	}
3255 	LYRegisterUIPage(newdoc.address, UIP_HISTORY);
3256 	StrAllocCopy(newdoc.title, HISTORY_PAGE_TITLE);
3257 	LYFreePostData(&newdoc);
3258 	FREE(newdoc.bookmark);
3259 	newdoc.isHEAD = FALSE;
3260 	newdoc.safe = FALSE;
3261 	newdoc.internal_link = FALSE;
3262 	newdoc.link = 1;	/*@@@ bypass "recent statusline messages" link */
3263 	free_address(&curdoc);	/* so it doesn't get pushed */
3264 
3265 	if (LYValidate || check_realm) {
3266 	    LYPermitURL = TRUE;
3267 	}
3268 	return TRUE;
3269     }				/* end if StrNCmp */
3270     return FALSE;
3271 }
3272 
handle_LYK_IMAGE_TOGGLE(int * cmd)3273 static BOOLEAN handle_LYK_IMAGE_TOGGLE(int *cmd)
3274 {
3275     clickable_images = (BOOLEAN) !clickable_images;
3276 
3277     HTUserMsg(clickable_images ?
3278 	      CLICKABLE_IMAGES_ON : CLICKABLE_IMAGES_OFF);
3279     return reparse_or_reload(cmd);
3280 }
3281 
handle_LYK_INDEX(int * old_c,int real_c)3282 static void handle_LYK_INDEX(int *old_c,
3283 			     int real_c)
3284 {
3285     /*
3286      * Make sure we are not in the index already.
3287      */
3288     if (!STREQ(curdoc.address, indexfile)) {
3289 
3290 	if (indexfile[0] == '\0') {	/* no defined index */
3291 	    if (*old_c != real_c) {
3292 		*old_c = real_c;
3293 		HTUserMsg(NO_INDEX_FILE);
3294 	    }
3295 
3296 	} else {
3297 #ifdef KANJI_CODE_OVERRIDE
3298 	    if (HTCJK == JAPANESE) {
3299 		last_kcode = NOKANJI;	/* AUTO */
3300 	    }
3301 #endif
3302 #ifdef USE_PROGRAM_DIR
3303 	    if (is_url(indexfile) == 0) {
3304 		char *tmp = NULL;
3305 
3306 		HTSprintf0(&tmp, "%s\\%s", program_dir, indexfile);
3307 		FREE(indexfile);
3308 		LYLocalFileToURL(&indexfile, tmp);
3309 		FREE(tmp);
3310 	    }
3311 #endif
3312 	    set_address(&newdoc, indexfile);
3313 	    StrAllocCopy(newdoc.title, gettext("System Index"));	/* name it */
3314 	    LYFreePostData(&newdoc);
3315 	    FREE(newdoc.bookmark);
3316 	    newdoc.isHEAD = FALSE;
3317 	    newdoc.safe = FALSE;
3318 	    newdoc.internal_link = FALSE;
3319 	}			/* end else */
3320     }				/* end if */
3321 }
3322 
handle_LYK_INDEX_SEARCH(BOOLEAN * force_load,int ForcePush,int * old_c,int real_c)3323 static void handle_LYK_INDEX_SEARCH(BOOLEAN *force_load,
3324 				    int ForcePush,
3325 				    int *old_c,
3326 				    int real_c)
3327 {
3328     if (is_www_index) {
3329 	/*
3330 	 * Perform a database search.
3331 	 *
3332 	 * do_www_search will try to go out and get the document.  If it
3333 	 * returns TRUE, a new document was returned and is named in the
3334 	 * newdoc.address.
3335 	 */
3336 	newdoc.isHEAD = FALSE;
3337 	newdoc.safe = FALSE;
3338 	if (do_www_search(&newdoc) == NORMAL) {
3339 	    /*
3340 	     * Yah, the search succeeded.
3341 	     */
3342 	    if (TRACE && !LYUseTraceLog && LYCursesON) {
3343 		/*
3344 		 * Make sure cursor is down.
3345 		 */
3346 		LYHideCursor();
3347 #ifdef USE_SLANG
3348 		LYaddstr("\n");
3349 #endif /* USE_SLANG */
3350 		LYrefresh();
3351 	    }
3352 	    LYpush(&curdoc, ForcePush);
3353 	    /*
3354 	     * Make the curdoc.address the newdoc.address so that getfile
3355 	     * doesn't try to get the newdoc.address.  Since we have already
3356 	     * gotten it.
3357 	     */
3358 	    copy_address(&curdoc, &newdoc);
3359 	    BStrCopy(newdoc.post_data, curdoc.post_data);
3360 	    StrAllocCopy(newdoc.post_content_type, curdoc.post_content_type);
3361 	    newdoc.internal_link = FALSE;
3362 	    curdoc.line = -1;
3363 	    LYSetNewline(0);
3364 	} else if (use_this_url_instead != NULL) {
3365 	    /*
3366 	     * Got back a redirecting URL.  Check it out.
3367 	     */
3368 	    HTUserMsg2(WWW_USING_MESSAGE, use_this_url_instead);
3369 
3370 	    /*
3371 	     * Make a name for this URL.
3372 	     */
3373 	    StrAllocCopy(newdoc.title,
3374 			 "A URL specified by redirection");
3375 	    set_address(&newdoc, use_this_url_instead);
3376 	    LYFreePostData(&newdoc);
3377 	    FREE(newdoc.bookmark);
3378 	    newdoc.isHEAD = FALSE;
3379 	    newdoc.safe = FALSE;
3380 	    newdoc.internal_link = FALSE;
3381 	    FREE(use_this_url_instead);
3382 	    *force_load = TRUE;
3383 	} else {
3384 	    /*
3385 	     * Yuk, the search failed.  Restore the old file.
3386 	     */
3387 	    copy_address(&newdoc, &curdoc);
3388 	    BStrCopy(newdoc.post_data, curdoc.post_data);
3389 	    StrAllocCopy(newdoc.post_content_type,
3390 			 curdoc.post_content_type);
3391 	    StrAllocCopy(newdoc.bookmark, curdoc.bookmark);
3392 	    newdoc.isHEAD = curdoc.isHEAD;
3393 	    newdoc.safe = curdoc.safe;
3394 	    newdoc.internal_link = curdoc.internal_link;
3395 	}
3396     } else if (*old_c != real_c) {
3397 	*old_c = real_c;
3398 	HTUserMsg(NOT_ISINDEX);
3399     }
3400 }
3401 
handle_LYK_INFO(int * cmd)3402 static BOOLEAN handle_LYK_INFO(int *cmd)
3403 {
3404     /*
3405      * Don't do if already viewing info page.
3406      */
3407     if (!LYIsUIPage(curdoc.address, UIP_SHOWINFO)) {
3408 	if (do_change_link() != -1
3409 	    && LYShowInfo(&curdoc, &newdoc, owner_address) >= 0) {
3410 	    LYRegisterUIPage(newdoc.address, UIP_SHOWINFO);
3411 	    StrAllocCopy(newdoc.title, SHOWINFO_TITLE);
3412 	    LYFreePostData(&newdoc);
3413 	    FREE(newdoc.bookmark);
3414 	    newdoc.isHEAD = FALSE;
3415 	    newdoc.safe = FALSE;
3416 	    newdoc.internal_link = FALSE;
3417 	    LYforce_no_cache = TRUE;
3418 	    if (LYValidate || check_realm)
3419 		LYPermitURL = TRUE;
3420 	}
3421     } else {
3422 	/*
3423 	 * If already in info page, get out.
3424 	 */
3425 	*cmd = LYK_PREV_DOC;
3426 	return TRUE;
3427     }
3428     return FALSE;
3429 }
3430 
handle_LYK_INLINE_TOGGLE(int * cmd)3431 static BOOLEAN handle_LYK_INLINE_TOGGLE(int *cmd)
3432 {
3433     pseudo_inline_alts = (BOOLEAN) !pseudo_inline_alts;
3434 
3435     HTUserMsg(pseudo_inline_alts ?
3436 	      PSEUDO_INLINE_ALTS_ON : PSEUDO_INLINE_ALTS_OFF);
3437     return reparse_or_reload(cmd);
3438 }
3439 
handle_LYK_INSERT_FILE(BOOLEAN * refresh_screen,int * old_c,int real_c)3440 static void handle_LYK_INSERT_FILE(BOOLEAN *refresh_screen,
3441 				   int *old_c,
3442 				   int real_c)
3443 {
3444     /*
3445      * See if the current link is in a form TEXTAREA.
3446      */
3447     if (LinkIsTextarea(curdoc.link)) {
3448 
3449 	/*
3450 	 * Reject attempts to use this for gaining access to local files when
3451 	 * such access is restricted:  if no_file_url was set via the file_url
3452 	 * restriction, if no_goto_file was set for the anonymous account, or
3453 	 * if HTDirAccess was set to HT_DIR_FORBID or HT_DIR_SELECTIVE via the
3454 	 * -nobrowse or -selective switches, it is assumed that inserting files
3455 	 * or checking for existence of files needs to be denied.  - kw
3456 	 */
3457 	if (no_file_url || no_goto_file ||
3458 	    HTDirAccess == HT_DIR_FORBID ||
3459 	    HTDirAccess == HT_DIR_SELECTIVE) {
3460 	    if (*old_c != real_c) {
3461 		*old_c = real_c;
3462 		if (no_goto_file)
3463 		    HTUserMsg2(GOTO_XXXX_DISALLOWED, STR_FILE_URL);
3464 		else
3465 		    HTUserMsg(NOAUTH_TO_ACCESS_FILES);
3466 		HTInfoMsg(FILE_INSERT_CANCELLED);
3467 	    }
3468 	    return;
3469 	}
3470 
3471 	(void) HText_InsertFile(&links[curdoc.link]);
3472 
3473 	/*
3474 	 * TODO:
3475 	 * Move cursor "n" lines from the current line to position it on the
3476 	 * 1st line following the text that was inserted.  If the target
3477 	 * line/anchor requires us to scroll up/down, position the target in
3478 	 * the approximate center of the screen.
3479 	 *
3480 	 * [Current behavior leaves cursor on the same line relative to the
3481 	 * start of the TEXTAREA that it was on before the insertion.  This is
3482 	 * the same behavior that occurs with (my) editor, so this TODO will
3483 	 * stay unimplemented.]
3484 	 */
3485 
3486 	*refresh_screen = TRUE;
3487 
3488     } else {
3489 
3490 	HTInfoMsg(NOT_IN_TEXTAREA);
3491     }
3492 }
3493 
3494 #if defined(DIRED_SUPPORT) && defined(OK_INSTALL)
handle_LYK_INSTALL(void)3495 static void handle_LYK_INSTALL(void)
3496 {
3497     if (lynx_edit_mode && nlinks > 0 && !no_dired_support)
3498 	local_install(NULL, links[curdoc.link].lname, &newdoc.address);
3499 }
3500 #endif
3501 
3502 static const char *hexy = "0123456789ABCDEF";
3503 
3504 #define HEX(n) hexy[(n) & 0xf]
3505 /*
3506  * URL-encode a parameter which can then be appended to a URI.
3507  * RFC-3986 lists reserved characters, which should be encoded.
3508  */
urlencode(char * str)3509 static char *urlencode(char *str)
3510 {
3511     char *result = NULL;
3512     char *ptr;
3513     int ch;
3514 
3515     if (str != NULL) {
3516 	result = malloc(strlen(str) * 3 + 1);
3517 	ptr = result;
3518 
3519 	assert(result);
3520 
3521 	while ((ch = UCH(*str++)) != 0) {
3522 	    if (ch == ' ') {
3523 		*ptr = '+';
3524 		ptr++;
3525 	    } else if (ch > 127 ||
3526 		       strchr(":/?#[]@!$&'()*+,;=", ch) != 0) {
3527 		*ptr++ = '%';
3528 		*ptr++ = HEX(ch >> 4);
3529 		*ptr++ = HEX(ch);
3530 	    } else {
3531 		*ptr++ = (char) ch;
3532 	    }
3533 	}
3534 	*ptr = '\0';
3535     }
3536 
3537     return result;
3538 }
3539 
3540 /*
3541  * Fill in "%s" marker(s) in the url_template by prompting the user for the
3542  * values.
3543  */
check_JUMP_param(char ** url_template)3544 static BOOLEAN check_JUMP_param(char **url_template)
3545 {
3546     int param = 1;
3547     char *subs;
3548     char *result = *url_template;
3549     char *encoded = NULL;
3550     int code = TRUE;
3551     bstring *input = NULL;
3552 
3553     CTRACE((tfp, "check_JUMP_param: %s\n", result));
3554 
3555     while ((subs = strstr(result, "%s")) != 0) {
3556 	char prompt[MAX_LINE];
3557 	RecallType recall = NORECALL;
3558 
3559 	CTRACE((tfp, "Prompt for query param%d: %s\n", param, result));
3560 
3561 	sprintf(prompt, gettext("Query parameter %d: "), param++);
3562 	statusline(prompt);
3563 	BStrCopy0(input, "");
3564 
3565 	if (encoded)
3566 	    FREE(encoded);
3567 
3568 	if (LYgetBString(&input, VISIBLE, 0, recall) < 0) {
3569 	    /*
3570 	     * cancelled via ^G
3571 	     */
3572 	    HTInfoMsg(CANCELLED);
3573 	    code = FALSE;
3574 	    break;
3575 	} else if ((encoded = urlencode(input->str)) != '\0') {
3576 	    int subs_at = (int) (subs - result);
3577 	    int fill_in = (int) strlen(encoded) - 2;
3578 	    size_t have = strlen(result);
3579 	    size_t want = strlen(encoded) + have - 1;
3580 	    int n;
3581 	    char *update = realloc(result, want + 1);
3582 
3583 	    if (update == 0) {
3584 		HTInfoMsg(NOT_ENOUGH_MEMORY);
3585 		code = FALSE;
3586 		break;
3587 	    }
3588 
3589 	    CTRACE((tfp, "  reply: %s\n", input->str));
3590 	    CTRACE((tfp, "  coded: %s\n", encoded));
3591 
3592 	    result = update;
3593 	    result[want] = '\0';
3594 	    for (n = (int) want; (n - fill_in) >= subs_at; --n) {
3595 		result[n] = result[n - fill_in];
3596 	    }
3597 	    for (n = subs_at; encoded[n - subs_at] != '\0'; ++n) {
3598 		result[n] = encoded[n - subs_at];
3599 	    }
3600 	    CTRACE((tfp, "  subst: %s\n", result));
3601 	} else {
3602 	    HTInfoMsg(CANCELLED);
3603 	    code = FALSE;
3604 	    break;
3605 	}
3606     }
3607     BStrFree(input);
3608     FREE(encoded);
3609     *url_template = result;
3610     return (BOOLEAN) code;
3611 }
3612 
fill_JUMP_Params(char ** addressp)3613 static void fill_JUMP_Params(char **addressp)
3614 {
3615     if (LYJumpFileURL) {
3616 	check_JUMP_param(addressp);
3617     }
3618 }
3619 
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)3620 static BOOLEAN handle_LYK_JUMP(int c,
3621 			       bstring **user_input,
3622 			       char **old_user_input GCC_UNUSED,
3623 			       RecallType * recall GCC_UNUSED,
3624 			       BOOLEAN *FirstURLRecall GCC_UNUSED,
3625 			       int *URLNum GCC_UNUSED,
3626 			       int *URLTotal GCC_UNUSED,
3627 			       int *ch GCC_UNUSED,
3628 			       int *old_c,
3629 			       int real_c)
3630 {
3631     char *ret;
3632 
3633     if (no_jump || JThead == NULL) {
3634 	if (*old_c != real_c) {
3635 	    *old_c = real_c;
3636 	    if (no_jump)
3637 		HTUserMsg(JUMP_DISALLOWED);
3638 	    else
3639 		HTUserMsg(NO_JUMPFILE);
3640 	}
3641     } else {
3642 	LYJumpFileURL = TRUE;
3643 	if ((ret = LYJump(c)) != NULL) {
3644 #ifdef PERMIT_GOTO_FROM_JUMP
3645 	    if (!strncasecomp(ret, "Go ", 3)) {
3646 		LYJumpFileURL = FALSE;
3647 		StrAllocCopy(*old_user_input, (*user_input)->str);
3648 		*URLTotal = (Goto_URLs ? HTList_count(Goto_URLs) : 0);
3649 		*recall = ((*URLTotal >= 1) ? RECALL_URL : NORECALL);
3650 		*URLNum = *URLTotal;
3651 		*FirstURLRecall = TRUE;
3652 		if (!strcasecomp(ret, "Go :")) {
3653 		    if (recall) {
3654 			*ch = UPARROW;
3655 			return TRUE;
3656 		    }
3657 		    FREE(*old_user_input);
3658 		    HTUserMsg(NO_RANDOM_URLS_YET);
3659 		    return FALSE;
3660 		}
3661 		ret = HTParse((ret + 3), startfile, PARSE_ALL);
3662 		BStrCopy0((*user_input), ret);
3663 		FREE(ret);
3664 		return TRUE;
3665 	    }
3666 #endif /* PERMIT_GOTO_FROM_JUMP */
3667 	    ret = HTParse(ret, startfile, PARSE_ALL);
3668 	    if (!LYTrimStartfile(ret)) {
3669 		LYRemoveBlanks((*user_input)->str);
3670 	    }
3671 	    if (check_JUMP_param(&ret)) {
3672 		set_address(&newdoc, ret);
3673 		StrAllocCopy(lynxjumpfile, ret);
3674 		LYFreePostData(&newdoc);
3675 		FREE(newdoc.bookmark);
3676 		newdoc.isHEAD = FALSE;
3677 		newdoc.safe = FALSE;
3678 		newdoc.internal_link = FALSE;
3679 		LYUserSpecifiedURL = TRUE;
3680 	    }
3681 	    FREE(ret);
3682 	} else {
3683 	    LYJumpFileURL = FALSE;
3684 	}
3685     }
3686     return FALSE;
3687 }
3688 
handle_LYK_KEYMAP(BOOLEAN * vi_keys_flag,BOOLEAN * emacs_keys_flag,int * old_c,int real_c)3689 static void handle_LYK_KEYMAP(BOOLEAN *vi_keys_flag,
3690 			      BOOLEAN *emacs_keys_flag,
3691 			      int *old_c,
3692 			      int real_c)
3693 {
3694     if (*old_c != real_c) {
3695 	*old_c = real_c;
3696 	set_address(&newdoc, STR_LYNXKEYMAP);
3697 	StrAllocCopy(newdoc.title, CURRENT_KEYMAP_TITLE);
3698 	LYFreePostData(&newdoc);
3699 	FREE(newdoc.bookmark);
3700 	newdoc.isHEAD = FALSE;
3701 	newdoc.safe = FALSE;
3702 	newdoc.internal_link = FALSE;
3703 	/*
3704 	 * If vi_keys changed, the keymap did too, so force no cache, and reset
3705 	 * the flag.  - FM
3706 	 */
3707 	if (*vi_keys_flag != vi_keys ||
3708 	    *emacs_keys_flag != emacs_keys) {
3709 	    LYforce_no_cache = TRUE;
3710 	    *vi_keys_flag = vi_keys;
3711 	    *emacs_keys_flag = emacs_keys;
3712 	}
3713 #if defined(DIRED_SUPPORT) && defined(OK_OVERRIDE)
3714 	/*
3715 	 * Remember whether we are in dired menu so we can display the right
3716 	 * keymap.
3717 	 */
3718 	if (!no_dired_support) {
3719 	    prev_lynx_edit_mode = lynx_edit_mode;
3720 	}
3721 #endif /* DIRED_SUPPORT && OK_OVERRIDE */
3722 	LYforce_no_cache = TRUE;
3723     }
3724 }
3725 
handle_LYK_LAST_LINK(void)3726 static void handle_LYK_LAST_LINK(void)
3727 {
3728     int i = curdoc.link;
3729 
3730     for (;;) {
3731 	if (++i >= nlinks
3732 	    || links[i].ly != links[curdoc.link].ly) {
3733 	    set_curdoc_link(i - 1);
3734 	    break;
3735 	}
3736     }
3737 }
3738 
handle_LYK_LEFT_LINK(void)3739 static void handle_LYK_LEFT_LINK(void)
3740 {
3741     if (curdoc.link > 0 &&
3742 	links[curdoc.link].ly == links[curdoc.link - 1].ly) {
3743 	set_curdoc_link(curdoc.link - 1);
3744     }
3745 }
3746 
handle_LYK_LIST(int * cmd)3747 static BOOLEAN handle_LYK_LIST(int *cmd)
3748 {
3749     /*
3750      * Don't do if already viewing list page.
3751      */
3752     if (!strcmp(NonNull(curdoc.title), LIST_PAGE_TITLE) &&
3753 	LYIsUIPage(curdoc.address, UIP_LIST_PAGE)) {
3754 	/*
3755 	 * Already viewing list page, so get out.
3756 	 */
3757 	*cmd = LYK_PREV_DOC;
3758 	return TRUE;
3759     }
3760 
3761     /*
3762      * Print list page to file.
3763      */
3764     if (showlist(&newdoc, TRUE) < 0)
3765 	return FALSE;
3766     StrAllocCopy(newdoc.title, LIST_PAGE_TITLE);
3767     /*
3768      * showlist will set newdoc's other fields.  It may leave post_data intact
3769      * so the list can be used to follow internal links in the current document
3770      * even if it is a POST response.  - kw
3771      */
3772 
3773     if (LYValidate || check_realm) {
3774 	LYPermitURL = TRUE;
3775 	StrAllocCopy(lynxlistfile, newdoc.address);
3776     }
3777     return FALSE;
3778 }
3779 
handle_LYK_MAIN_MENU(int * old_c,int real_c)3780 static void handle_LYK_MAIN_MENU(int *old_c,
3781 				 int real_c)
3782 {
3783     /*
3784      * If its already the homepage then don't reload it.
3785      */
3786     if (!STREQ(curdoc.address, homepage)) {
3787 
3788 	if (HTConfirmDefault(CONFIRM_MAIN_SCREEN, NO) == YES) {
3789 	    set_address(&newdoc, homepage);
3790 	    StrAllocCopy(newdoc.title, gettext("Entry into main screen"));
3791 	    LYFreePostData(&newdoc);
3792 	    FREE(newdoc.bookmark);
3793 	    newdoc.isHEAD = FALSE;
3794 	    newdoc.safe = FALSE;
3795 	    newdoc.internal_link = FALSE;
3796 	    LYhighlight(FALSE, curdoc.link, prev_target->str);
3797 #ifdef DIRED_SUPPORT
3798 	    if (lynx_edit_mode) {
3799 		DIRED_UNCACHE_2;
3800 	    }
3801 #endif /* DIRED_SUPPORT */
3802 	}
3803     } else {
3804 	if (*old_c != real_c) {
3805 	    *old_c = real_c;
3806 	    HTUserMsg(IN_MAIN_SCREEN);
3807 	}
3808     }
3809 }
3810 
handle_LYK_MINIMAL(void)3811 static void handle_LYK_MINIMAL(void)
3812 {
3813     if (!historical_comments) {
3814 #ifdef USE_SOURCE_CACHE
3815 	if (!HTcan_reparse_document()) {
3816 #endif
3817 	    /*
3818 	     * Check if this is a reply from a POST, and if so, seek
3819 	     * confirmation of reload if the safe element is not set.  - FM
3820 	     */
3821 	    if ((curdoc.post_data != NULL &&
3822 		 curdoc.safe != TRUE) &&
3823 		confirm_post_resub(curdoc.address, NULL, 0, 0) == FALSE) {
3824 		HTInfoMsg(WILL_NOT_RELOAD_DOC);
3825 	    } else {
3826 		HText_setNoCache(HTMainText);
3827 		move_address(&newdoc, &curdoc);
3828 		newdoc.line = curdoc.line;
3829 		newdoc.link = curdoc.link;
3830 	    }
3831 #ifdef USE_SOURCE_CACHE
3832 	}			/* end if no bypass */
3833 #endif
3834     }
3835     minimal_comments = (BOOLEAN) !minimal_comments;
3836     if (!historical_comments) {
3837 	HTAlert(minimal_comments ?
3838 		MINIMAL_ON_IN_EFFECT : MINIMAL_OFF_VALID_ON);
3839     } else {
3840 	HTAlert(minimal_comments ?
3841 		MINIMAL_ON_BUT_HISTORICAL : MINIMAL_OFF_HISTORICAL_ON);
3842     }
3843 #ifdef USE_SOURCE_CACHE
3844     (void) reparse_document();
3845 #endif
3846     return;
3847 }
3848 
3849 #if defined(DIRED_SUPPORT)
handle_LYK_MODIFY(BOOLEAN * refresh_screen)3850 static void handle_LYK_MODIFY(BOOLEAN *refresh_screen)
3851 {
3852     if (lynx_edit_mode && nlinks > 0 && !no_dired_support) {
3853 	int ret;
3854 
3855 	ret = local_modify(&curdoc, &newdoc.address);
3856 	if (ret == PERMIT_FORM_RESULT) {	/* Permit form thrown up */
3857 	    *refresh_screen = TRUE;
3858 	} else if (ret) {
3859 	    DIRED_UNCACHE_1;
3860 	    move_address(&newdoc, &curdoc);
3861 	    LYFreePostData(&newdoc);
3862 	    FREE(newdoc.bookmark);
3863 	    newdoc.isHEAD = FALSE;
3864 	    newdoc.safe = FALSE;
3865 	    newdoc.internal_link = FALSE;
3866 	    newdoc.line = curdoc.line;
3867 	    newdoc.link = curdoc.link;
3868 	    LYclear();
3869 	}
3870     }
3871 }
3872 #endif /* DIRED_SUPPORT */
3873 
3874 #ifdef EXP_NESTED_TABLES
handle_LYK_NESTED_TABLES(int * cmd)3875 static BOOLEAN handle_LYK_NESTED_TABLES(int *cmd)
3876 {
3877     nested_tables = (BOOLEAN) !nested_tables;
3878     HTUserMsg(nested_tables ? NESTED_TABLES_ON : NESTED_TABLES_OFF);
3879     return reparse_or_reload(cmd);
3880 }
3881 #endif
3882 
handle_LYK_OPTIONS(int * cmd,BOOLEAN * refresh_screen)3883 static BOOLEAN handle_LYK_OPTIONS(int *cmd,
3884 				  BOOLEAN *refresh_screen)
3885 {
3886 #ifndef NO_OPTION_MENU
3887     if (!LYUseFormsOptions) {
3888 	BOOLEAN LYUseDefaultRawMode_flag = LYUseDefaultRawMode;
3889 	BOOLEAN LYSelectPopups_flag = LYSelectPopups;
3890 	BOOLEAN verbose_img_flag = verbose_img;
3891 	BOOLEAN keypad_mode_flag = (BOOL) keypad_mode;
3892 	BOOLEAN show_dotfiles_flag = show_dotfiles;
3893 	BOOLEAN user_mode_flag = (BOOL) user_mode;
3894 	int CurrentAssumeCharSet_flag = UCLYhndl_for_unspec;
3895 	int CurrentCharSet_flag = current_char_set;
3896 	int HTfileSortMethod_flag = HTfileSortMethod;
3897 	char *CurrentUserAgent = NULL;
3898 	char *CurrentNegoLanguage = NULL;
3899 	char *CurrentNegoCharset = NULL;
3900 
3901 	StrAllocCopy(CurrentUserAgent, NonNull(LYUserAgent));
3902 	StrAllocCopy(CurrentNegoLanguage, NonNull(language));
3903 	StrAllocCopy(CurrentNegoCharset, NonNull(pref_charset));
3904 
3905 	LYoptions(); /** do the old-style options stuff **/
3906 
3907 	if (keypad_mode_flag != keypad_mode ||
3908 	    (user_mode_flag != user_mode &&
3909 	     (user_mode_flag == NOVICE_MODE ||
3910 	      user_mode == NOVICE_MODE)) ||
3911 	    (((HTfileSortMethod_flag != HTfileSortMethod) ||
3912 	      (show_dotfiles_flag != show_dotfiles)) &&
3913 	     (isFILE_URL(curdoc.address) ||
3914 	      isFTP_URL(curdoc.address))) ||
3915 	    CurrentCharSet_flag != current_char_set ||
3916 	    CurrentAssumeCharSet_flag != UCLYhndl_for_unspec ||
3917 	    verbose_img_flag != verbose_img ||
3918 	    LYUseDefaultRawMode_flag != LYUseDefaultRawMode ||
3919 	    LYSelectPopups_flag != LYSelectPopups ||
3920 	    ((strcmp(CurrentUserAgent, NonNull(LYUserAgent)) ||
3921 	      strcmp(CurrentNegoLanguage, NonNull(language)) ||
3922 	      strcmp(CurrentNegoCharset, NonNull(pref_charset))) &&
3923 	     (!StrNCmp(curdoc.address, "http", 4) ||
3924 	      isLYNXCGI(curdoc.address)))) {
3925 
3926 	    BOOLEAN canreparse_post = FALSE;
3927 
3928 	    /*
3929 	     * Check if this is a reply from a POST, and if so, seek
3930 	     * confirmation of reload if the safe element is not set.  - FM
3931 	     */
3932 	    if ((curdoc.post_data != NULL &&
3933 		 curdoc.safe != TRUE) &&
3934 #ifdef USE_SOURCE_CACHE
3935 		(!(canreparse_post = HTcan_reparse_document())) &&
3936 #endif
3937 		confirm_post_resub(curdoc.address, curdoc.title,
3938 				   2, 1) == FALSE) {
3939 		HTInfoMsg(WILL_NOT_RELOAD_DOC);
3940 	    } else {
3941 		copy_address(&newdoc, &curdoc);
3942 		if (((strcmp(CurrentUserAgent, NonNull(LYUserAgent)) ||
3943 		      strcmp(CurrentNegoLanguage, NonNull(language)) ||
3944 		      strcmp(CurrentNegoCharset, NonNull(pref_charset))) &&
3945 		     (StrNCmp(curdoc.address, "http", 4) == 0 ||
3946 		      !isLYNXCGI(curdoc.address) == 0))) {
3947 		    /*
3948 		     * An option has changed which may influence content
3949 		     * negotiation, and the resource is from a http or https or
3950 		     * lynxcgi URL (the only protocols which currently do
3951 		     * anything with this information).  Set reloading = TRUE
3952 		     * so that proxy caches will be flushed, which is necessary
3953 		     * until the time when all proxies understand HTTP 1.1
3954 		     * Vary:  and all Servers properly use it...  Treat like
3955 		     * case LYK_RELOAD (see comments there).  - KW
3956 		     */
3957 		    reloading = TRUE;
3958 		}
3959 		if (HTisDocumentSource()) {
3960 		    srcmode_for_next_retrieval(1);
3961 		}
3962 #ifdef USE_SOURCE_CACHE
3963 		if (reloading == FALSE) {
3964 		    /* one more attempt to be smart enough: */
3965 		    if (reparse_document()) {
3966 			FREE(CurrentUserAgent);
3967 			FREE(CurrentNegoLanguage);
3968 			FREE(CurrentNegoCharset);
3969 			return FALSE;
3970 		    }
3971 		}
3972 #endif
3973 		if (canreparse_post &&
3974 		    confirm_post_resub(curdoc.address, curdoc.title,
3975 				       2, 1) == FALSE) {
3976 		    if (HTisDocumentSource()) {
3977 			srcmode_for_next_retrieval(0);
3978 		    }
3979 		    FREE(CurrentUserAgent);
3980 		    FREE(CurrentNegoLanguage);
3981 		    FREE(CurrentNegoCharset);
3982 		    return FALSE;
3983 		}
3984 
3985 		HEAD_request = HTLoadedDocumentIsHEAD();
3986 		HText_setNoCache(HTMainText);
3987 		newdoc.line = curdoc.line;
3988 		newdoc.link = curdoc.link;
3989 		LYforce_no_cache = TRUE;
3990 		free_address(&curdoc);	/* So it doesn't get pushed. */
3991 	    }
3992 	}
3993 	FREE(CurrentUserAgent);
3994 	FREE(CurrentNegoLanguage);
3995 	FREE(CurrentNegoCharset);
3996 	*refresh_screen = TRUE;	/* to repaint screen */
3997 	return FALSE;
3998     }				/* end if !LYUseFormsOptions */
3999 #endif /* !NO_OPTION_MENU */
4000 #ifndef NO_OPTION_FORMS
4001     /*
4002      * Generally stolen from LYK_COOKIE_JAR.  Options menu handling is
4003      * done in postoptions(), called from getfile() currently.
4004      *
4005      * postoptions() is also responsible for reloading the document
4006      * before the 'options menu' but only when (a few) important
4007      * options were changed.
4008      *
4009      * It is critical that post_data is freed here since the
4010      * submission of changed options is done via the same protocol as
4011      * LYNXOPTIONS:
4012      */
4013     /*
4014      * Don't do if already viewing options page.
4015      */
4016     if (!LYIsUIPage(curdoc.address, UIP_OPTIONS_MENU)) {
4017 
4018 	set_address(&newdoc, LYNXOPTIONS_PAGE("/"));
4019 	LYFreePostData(&newdoc);
4020 	FREE(newdoc.bookmark);
4021 	newdoc.isHEAD = FALSE;
4022 	newdoc.safe = FALSE;
4023 	newdoc.internal_link = FALSE;
4024 	LYforce_no_cache = TRUE;
4025 	/* change to 'if (check_realm && !LYValidate)' and
4026 	   make change near top of getfile to forbid
4027 	   using forms options menu with -validate:  - kw */
4028 	if (LYValidate || check_realm) {
4029 	    LYPermitURL = TRUE;
4030 	}
4031     } else {
4032 	/*
4033 	 * If already in the options menu, get out.
4034 	 */
4035 	*cmd = LYK_PREV_DOC;
4036 	return TRUE;
4037     }
4038 #endif /* !NO_OPTION_FORMS */
4039     return FALSE;
4040 }
4041 
handle_NEXT_DOC(void)4042 static void handle_NEXT_DOC(void)
4043 {
4044     if (LYhist_next(&curdoc, &newdoc)) {
4045 	free_address(&curdoc);	/* avoid push */
4046 	return;
4047     }
4048     HTUserMsg(gettext("No next document present"));
4049 }
4050 
handle_LYK_NEXT_LINK(int c,int * old_c,int real_c)4051 static void handle_LYK_NEXT_LINK(int c,
4052 				 int *old_c,
4053 				 int real_c)
4054 {
4055     if (curdoc.link < nlinks - 1) {	/* next link */
4056 	LYhighlight(FALSE, curdoc.link, prev_target->str);
4057 #ifdef FASTTAB
4058 	/*
4059 	 * Move to different textarea if TAB in textarea.
4060 	 */
4061 	if (LinkIsTextarea(curdoc.link) &&
4062 	    c == '\t') {
4063 	    int thisgroup = links[curdoc.link].l_form->number;
4064 	    char *thisname = links[curdoc.link].l_form->name;
4065 
4066 	    do
4067 		curdoc.link++;
4068 	    while ((curdoc.link < nlinks - 1) &&
4069 		   LinkIsTextarea(curdoc.link) &&
4070 		   links[curdoc.link].l_form->number == thisgroup &&
4071 		   sametext(links[curdoc.link].l_form->name, thisname));
4072 	} else {
4073 	    curdoc.link++;
4074 	}
4075 #else
4076 	curdoc.link++;
4077 #endif /* FASTTAB */
4078 	/*
4079 	 * At the bottom of list and there is only one page.  Move to the top
4080 	 * link on the page.
4081 	 */
4082     } else if (!more_text && LYGetNewline() == 1 && curdoc.link == nlinks - 1) {
4083 	set_curdoc_link(0);
4084 
4085     } else if (more_text) {	/* next page */
4086 	LYChgNewline(display_lines);
4087     } else if (*old_c != real_c) {
4088 	*old_c = real_c;
4089 	HTInfoMsg(ALREADY_AT_END);
4090     }
4091 }
4092 
handle_LYK_NEXT_PAGE(int * old_c,int real_c)4093 static void handle_LYK_NEXT_PAGE(int *old_c,
4094 				 int real_c)
4095 {
4096     if (more_text) {
4097 	LYChgNewline(display_lines);
4098     } else if (curdoc.link < nlinks - 1) {
4099 	set_curdoc_link(nlinks - 1);
4100     } else if (*old_c != real_c) {
4101 	*old_c = real_c;
4102 	HTInfoMsg(ALREADY_AT_END);
4103     }
4104 }
4105 
handle_LYK_NOCACHE(int * old_c,int real_c)4106 static BOOLEAN handle_LYK_NOCACHE(int *old_c,
4107 				  int real_c)
4108 {
4109     if (nlinks > 0) {
4110 	if (links[curdoc.link].type == WWW_FORM_LINK_TYPE &&
4111 	    links[curdoc.link].l_form->type != F_SUBMIT_TYPE &&
4112 	    links[curdoc.link].l_form->type != F_IMAGE_SUBMIT_TYPE &&
4113 	    links[curdoc.link].l_form->type != F_TEXT_SUBMIT_TYPE) {
4114 	    if (*old_c != real_c) {
4115 		*old_c = real_c;
4116 		HTUserMsg(NOT_ON_SUBMIT_OR_LINK);
4117 	    }
4118 	    return FALSE;
4119 	} else {
4120 	    LYforce_no_cache = TRUE;
4121 	    reloading = TRUE;
4122 	}
4123     }
4124     return TRUE;
4125 }
4126 
handle_LYK_PREV_LINK(int * arrowup,int * old_c,int real_c)4127 static void handle_LYK_PREV_LINK(int *arrowup,
4128 				 int *old_c,
4129 				 int real_c)
4130 {
4131     if (curdoc.link > 0) {	/* previous link */
4132 	set_curdoc_link(curdoc.link - 1);
4133 
4134     } else if (!more_text &&
4135 	       curdoc.link == 0 && LYGetNewline() == 1) {	/* at the top of list */
4136 	/*
4137 	 * If there is only one page of data and the user goes off the top,
4138 	 * just move the cursor to last link on the page.
4139 	 */
4140 	set_curdoc_link(nlinks - 1);
4141 
4142     } else if (curdoc.line > 1) {	/* previous page */
4143 	/*
4144 	 * Go back to the previous page.
4145 	 */
4146 	int scrollamount = (LYGetNewline() > display_lines
4147 			    ? display_lines
4148 			    : LYGetNewline() - 1);
4149 
4150 	LYChgNewline(-scrollamount);
4151 	if (scrollamount < display_lines &&
4152 	    nlinks > 0 && curdoc.link == 0 &&
4153 	    links[0].ly - 1 + scrollamount <= display_lines) {
4154 	    newdoc.link = HText_LinksInLines(HTMainText,
4155 					     1,
4156 					     scrollamount) - 1;
4157 	} else {
4158 	    *arrowup = TRUE;
4159 	}
4160 
4161     } else if (*old_c != real_c) {
4162 	*old_c = real_c;
4163 	HTInfoMsg(ALREADY_AT_BEGIN);
4164     }
4165 }
4166 
4167 #define nhist_1 (nhist - 1)	/* workaround for indent */
4168 
handle_PREV_DOC(int * cmd,int * old_c,int real_c)4169 static int handle_PREV_DOC(int *cmd,
4170 			   int *old_c,
4171 			   int real_c)
4172 {
4173     if (nhist > 0) {		/* if there is anything to go back to */
4174 	/*
4175 	 * Check if the previous document is a reply from a POST, and if so,
4176 	 * seek confirmation of resubmission if the safe element is not set and
4177 	 * the document is not still in the cache or LYresubmit_posts is set.
4178 	 * If not confirmed and it is not the startfile, pop it so we go to the
4179 	 * yet previous document, until we're OK or reach the startfile.  If we
4180 	 * reach the startfile and its not OK or we don't get confirmation,
4181 	 * cancel.  - FM
4182 	 */
4183 	DocAddress WWWDoc;
4184 	HTParentAnchor *tmpanchor;
4185 	BOOLEAN conf = FALSE, first = TRUE;
4186 
4187 	HTLastConfirmCancelled();	/* reset flag */
4188 	while (nhist > 0) {
4189 	    conf = FALSE;
4190 	    if (HDOC(nhist_1).post_data == NULL) {
4191 		break;
4192 	    }
4193 	    WWWDoc.address = HDOC(nhist_1).address;
4194 	    WWWDoc.post_data = HDOC(nhist_1).post_data;
4195 	    WWWDoc.post_content_type =
4196 		HDOC(nhist_1).post_content_type;
4197 	    WWWDoc.bookmark = HDOC(nhist_1).bookmark;
4198 	    WWWDoc.isHEAD = HDOC(nhist_1).isHEAD;
4199 	    WWWDoc.safe = HDOC(nhist_1).safe;
4200 	    tmpanchor = HTAnchor_findAddress(&WWWDoc);
4201 	    if (HTAnchor_safe(tmpanchor)) {
4202 		break;
4203 	    }
4204 	    if ((HTAnchor_document(tmpanchor) == NULL &&
4205 		 (isLYNXIMGMAP(WWWDoc.address) ||
4206 		  (conf = confirm_post_resub(WWWDoc.address,
4207 					     HDOC(nhist_1).title,
4208 					     0, 0))
4209 		  == FALSE)) ||
4210 		((LYresubmit_posts && !conf &&
4211 		  (NONINTERNAL_OR_PHYS_DIFFERENT((DocInfo *) &history[(nhist_1)],
4212 						 &curdoc) ||
4213 		   NONINTERNAL_OR_PHYS_DIFFERENT((DocInfo *) &history[(nhist_1)],
4214 						 &newdoc))) &&
4215 		 !confirm_post_resub(WWWDoc.address,
4216 				     HDOC(nhist_1).title,
4217 				     2, 2))) {
4218 		if (HTLastConfirmCancelled()) {
4219 		    if (!first && curdoc.internal_link)
4220 			free_address(&curdoc);
4221 		    *cmd = LYK_DO_NOTHING;
4222 		    return 2;
4223 		}
4224 		if (nhist == 1) {
4225 		    HTInfoMsg(CANCELLED);
4226 		    *old_c = 0;
4227 		    *cmd = LYK_DO_NOTHING;
4228 		    return 2;
4229 		} else {
4230 		    HTUserMsg2(WWW_SKIP_MESSAGE, WWWDoc.address);
4231 		    do {	/* Should be LYhist_prev when _next supports */
4232 			LYpop(&curdoc);		/* skipping of forms */
4233 		    } while (nhist > 1
4234 			     && !are_different((DocInfo *) &history[nhist_1],
4235 					       &curdoc));
4236 		    first = FALSE;	/* have popped at least one */
4237 		    continue;
4238 		}
4239 	    } else {
4240 		/*
4241 		 * Break from loop; if user just confirmed to load again
4242 		 * because document wasn't in cache, set LYforce_no_cache to
4243 		 * avoid unnecessary repeat question down the road.  - kw
4244 		 */
4245 		if (conf)
4246 		    LYforce_no_cache = TRUE;
4247 		break;
4248 	    }
4249 	}
4250 
4251 	if (!first)
4252 	    curdoc.internal_link = FALSE;
4253 
4254 	/*
4255 	 * Set newdoc.address to empty to pop a file.
4256 	 */
4257 	LYhist_prev_register(&curdoc);	/* Why not call _prev instead of zeroing address?  */
4258 	free_address(&newdoc);
4259 #ifdef DIRED_SUPPORT
4260 	if (lynx_edit_mode) {
4261 	    DIRED_UNCACHE_2;
4262 	}
4263 #endif /* DIRED_SUPPORT */
4264     } else if (child_lynx == TRUE) {
4265 	return (1);		/* exit on left arrow in main screen */
4266 
4267     } else if (*old_c != real_c) {
4268 	*old_c = real_c;
4269 	HTUserMsg(ALREADY_AT_FIRST);
4270     }
4271     return 0;
4272 }
4273 
handle_LYK_PREV_PAGE(int * old_c,int real_c)4274 static void handle_LYK_PREV_PAGE(int *old_c,
4275 				 int real_c)
4276 {
4277     if (LYGetNewline() > 1) {
4278 	LYChgNewline(-display_lines);
4279     } else if (curdoc.link > 0) {
4280 	set_curdoc_link(0);
4281     } else if (*old_c != real_c) {
4282 	*old_c = real_c;
4283 	HTInfoMsg(ALREADY_AT_BEGIN);
4284     }
4285 }
4286 
handle_LYK_PRINT(BOOLEAN * ForcePush,int * old_c,int real_c)4287 static void handle_LYK_PRINT(BOOLEAN *ForcePush,
4288 			     int *old_c,
4289 			     int real_c)
4290 {
4291     if (LYValidate) {
4292 	if (*old_c != real_c) {
4293 	    *old_c = real_c;
4294 	    HTUserMsg(PRINT_DISABLED);
4295 	}
4296 	return;
4297     }
4298 
4299     /*
4300      * Don't do if already viewing print options page.
4301      */
4302     if (!LYIsUIPage(curdoc.address, UIP_PRINT_OPTIONS)
4303 	&& print_options(&newdoc.address,
4304 			 curdoc.address, HText_getNumOfLines()) >= 0) {
4305 	LYRegisterUIPage(newdoc.address, UIP_PRINT_OPTIONS);
4306 	StrAllocCopy(newdoc.title, PRINT_OPTIONS_TITLE);
4307 	LYFreePostData(&newdoc);
4308 	FREE(newdoc.bookmark);
4309 	newdoc.isHEAD = FALSE;
4310 	newdoc.safe = FALSE;
4311 	*ForcePush = TRUE;	/* see LYpush() and print_options() */
4312 	if (check_realm)
4313 	    LYPermitURL = TRUE;
4314     }
4315 }
4316 
handle_LYK_QUIT(void)4317 static BOOLEAN handle_LYK_QUIT(void)
4318 {
4319     int c;
4320 
4321     if (LYQuitDefaultYes == TRUE) {
4322 	c = HTConfirmDefault(REALLY_QUIT, YES);
4323     } else {
4324 	c = HTConfirmDefault(REALLY_QUIT, NO);
4325     }
4326     if (LYQuitDefaultYes == TRUE) {
4327 	if (c != NO) {
4328 	    return (TRUE);
4329 	} else {
4330 	    HTInfoMsg(NO_CANCEL);
4331 	}
4332     } else if (c == YES) {
4333 	return (TRUE);
4334     } else {
4335 	HTInfoMsg(NO_CANCEL);
4336     }
4337     return FALSE;
4338 }
4339 
handle_LYK_RAW_TOGGLE(int * cmd)4340 static BOOLEAN handle_LYK_RAW_TOGGLE(int *cmd)
4341 {
4342     if (HTLoadedDocumentCharset()) {
4343 	HTUserMsg(gettext("charset for this document specified explicitly, sorry..."));
4344 	return FALSE;
4345     } else {
4346 	LYUseDefaultRawMode = (BOOL) !LYUseDefaultRawMode;
4347 	HTUserMsg(LYRawMode ? RAWMODE_OFF : RAWMODE_ON);
4348 	HTMLSetCharacterHandling(current_char_set);
4349 	return reparse_or_reload(cmd);
4350     }
4351 }
4352 
handle_LYK_RELOAD(int real_cmd)4353 static void handle_LYK_RELOAD(int real_cmd)
4354 {
4355     /*
4356      * Check if this is a reply from a POST, and if so,
4357      * seek confirmation if the safe element is not set.  - FM
4358      */
4359     if ((curdoc.post_data != NULL &&
4360 	 curdoc.safe != TRUE) &&
4361 	HTConfirm(CONFIRM_POST_RESUBMISSION) == FALSE) {
4362 	HTInfoMsg(CANCELLED);
4363 	return;
4364     }
4365 
4366     /*
4367      * Check to see if should reload source, or load html
4368      */
4369 
4370     if (HTisDocumentSource()) {
4371 	if ((forced_UCLYhdnl = HTMainText_Get_UCLYhndl()) >= 0)
4372 	    force_old_UCLYhndl_on_reload = TRUE;
4373 	srcmode_for_next_retrieval(1);
4374     }
4375 
4376     HEAD_request = HTLoadedDocumentIsHEAD();
4377     HText_setNoCache(HTMainText);
4378     /*
4379      * Do assume the reloaded document will be the same.  - FM
4380      *
4381      * (I don't remember all the reasons why we couldn't assume this.  As the
4382      * problems show up, we'll try to fix them, or add warnings.  - FM)
4383      */
4384     newdoc.line = curdoc.line;
4385     newdoc.link = curdoc.link;
4386     free_address(&curdoc);	/* so it doesn't get pushed */
4387 #ifdef VMS
4388     lynx_force_repaint();
4389 #endif /* VMS */
4390     /*
4391      * Reload should force a cache refresh on a proxy.  -- Ari L.
4392      * <luotonen@dxcern.cern.ch>
4393      *
4394      * -- but only if this was really a reload requested by the user, not if we
4395      * jumped here to handle reloading for INLINE_TOGGLE, IMAGE_TOGGLE,
4396      * RAW_TOGGLE, etc.  - KW
4397      */
4398     if (real_cmd == LYK_RELOAD)
4399 	reloading = REAL_RELOAD;
4400 
4401     return;
4402 }
4403 
4404 #ifdef DIRED_SUPPORT
handle_LYK_REMOVE(BOOLEAN * refresh_screen)4405 static void handle_LYK_REMOVE(BOOLEAN *refresh_screen)
4406 {
4407     if (lynx_edit_mode && nlinks > 0 && !no_dired_support) {
4408 	int linkno = curdoc.link;	/* may be changed in local_remove - kw */
4409 
4410 	local_remove(&curdoc);
4411 	if (LYAutoUncacheDirLists >= 1)
4412 	    do_cleanup_after_delete();
4413 	else if (curdoc.link != linkno)
4414 	    *refresh_screen = TRUE;
4415     }
4416 }
4417 #endif /* DIRED_SUPPORT */
4418 
handle_LYK_RIGHT_LINK(void)4419 static void handle_LYK_RIGHT_LINK(void)
4420 {
4421     if (curdoc.link < nlinks - 1 &&
4422 	links[curdoc.link].ly == links[curdoc.link + 1].ly) {
4423 	set_curdoc_link(curdoc.link + 1);
4424     }
4425 }
4426 
handle_LYK_SHELL(BOOLEAN * refresh_screen,int * old_c,int real_c)4427 static void handle_LYK_SHELL(BOOLEAN *refresh_screen,
4428 			     int *old_c,
4429 			     int real_c)
4430 {
4431     if (!no_shell) {
4432 	stop_curses();
4433 	printf("%s\r\n", SPAWNING_MSG);
4434 #if defined(__CYGWIN__)
4435 	/* handling "exec $SHELL" does not work if $SHELL is null */
4436 	if (LYGetEnv("SHELL") == NULL) {
4437 	    Cygwin_Shell();
4438 	} else
4439 #endif
4440 	{
4441 	    static char *shell = NULL;
4442 
4443 	    if (shell == 0)
4444 		StrAllocCopy(shell, LYSysShell());
4445 	    LYSystem(shell);
4446 	}
4447 	start_curses();
4448 	*refresh_screen = TRUE;	/* for an HText_pageDisplay() */
4449     } else {
4450 	if (*old_c != real_c) {
4451 	    *old_c = real_c;
4452 	    HTUserMsg(SPAWNING_DISABLED);
4453 	}
4454     }
4455 }
4456 
handle_LYK_SOFT_DQUOTES(void)4457 static void handle_LYK_SOFT_DQUOTES(void)
4458 {
4459 #ifdef USE_SOURCE_CACHE
4460     if (!HTcan_reparse_document()) {
4461 #endif
4462 	/*
4463 	 * Check if this is a reply from a POST, and if so, seek confirmation
4464 	 * of reload if the safe element is not set.  - FM
4465 	 */
4466 	if ((curdoc.post_data != NULL &&
4467 	     curdoc.safe != TRUE) &&
4468 	    confirm_post_resub(curdoc.address, NULL, 1, 1) == FALSE) {
4469 	    HTInfoMsg(WILL_NOT_RELOAD_DOC);
4470 	} else {
4471 	    HText_setNoCache(HTMainText);
4472 	    move_address(&newdoc, &curdoc);
4473 	    newdoc.line = curdoc.line;
4474 	    newdoc.link = curdoc.link;
4475 	}
4476 #ifdef USE_SOURCE_CACHE
4477     }				/* end if no bypass */
4478 #endif
4479     soft_dquotes = (BOOLEAN) !soft_dquotes;
4480     HTUserMsg(soft_dquotes ?
4481 	      SOFT_DOUBLE_QUOTE_ON : SOFT_DOUBLE_QUOTE_OFF);
4482 #ifdef USE_SOURCE_CACHE
4483     (void) reparse_document();
4484 #endif
4485     return;
4486 }
4487 
4488 #define GetAnchorNumber(link) \
4489 			((nlinks > 0 && link >= 0) \
4490     			 ? links[link].anchor_number \
4491 			 : -1)
4492 #define GetAnchorLineNo(link) \
4493 			((nlinks > 0 && link >= 0) \
4494     			 ? links[link].anchor_line_num \
4495 			 : -1)
4496 
4497 /*
4498  * Adjust the top-of-screen line number for the new document if the redisplayed
4499  * screen would not show the given link-number.
4500  */
4501 #ifdef USE_SOURCE_CACHE
wrap_reparse_document(void)4502 static int wrap_reparse_document(void)
4503 {
4504     int result;
4505     int anchor_number = GetAnchorNumber(curdoc.link);
4506     int old_line_num = HText_getAbsLineNumber(HTMainText, anchor_number);
4507     int old_from_top = old_line_num - LYGetNewline() + 1;
4508 
4509     /* get the offset for the current anchor */
4510     int old_offset = ((nlinks > 0 && curdoc.link >= 0)
4511 		      ? links[curdoc.link].sgml_offset
4512 		      : -1);
4513 
4514     CTRACE((tfp, "original anchor %d, topline %d, link %d, offset %d\n",
4515 	    anchor_number, old_line_num, curdoc.link, old_offset));
4516 
4517     /* reparse the document (producing a new anchor list) */
4518     result = reparse_document();
4519 
4520     /* readjust top-line and link-number */
4521     if (result && old_offset >= 0) {
4522 	int new_anchor = HText_closestAnchor(HTMainText, old_offset);
4523 	int new_lineno = HText_getAbsLineNumber(HTMainText, new_anchor);
4524 	int top_lineno;
4525 
4526 	CTRACE((tfp, "old anchor %d -> new anchor %d\n", anchor_number, new_anchor));
4527 
4528 	if (new_lineno - old_from_top < 0)
4529 	    old_from_top = new_lineno;
4530 
4531 	/* Newline and newdoc.line are 1-based,
4532 	 * but 0-based lines are simpler to work with.
4533 	 */
4534 	top_lineno = HText_getPreferredTopLine(HTMainText, new_lineno -
4535 					       old_from_top) + 1;
4536 	CTRACE((tfp, "preferred top %d\n", top_lineno));
4537 
4538 	if (top_lineno != LYGetNewline()) {
4539 	    LYSetNewline(top_lineno);
4540 	    newdoc.link = HText_anchorRelativeTo(HTMainText, top_lineno - 1, new_anchor);
4541 	    curdoc.link = newdoc.link;
4542 	    CTRACE((tfp,
4543 		    "adjusted anchor %d, topline %d, link %d, offset %d\n",
4544 		    new_anchor,
4545 		    top_lineno,
4546 		    curdoc.link,
4547 		    HText_locateAnchor(HTMainText, new_anchor)));
4548 	} else {
4549 	    newdoc.link = curdoc.link;
4550 	}
4551     }
4552     return result;
4553 }
4554 #endif /* USE_SOURCE_CACHE */
4555 
handle_LYK_SOURCE(char ** ownerS_address_p)4556 static void handle_LYK_SOURCE(char **ownerS_address_p)
4557 {
4558 #ifdef USE_SOURCE_CACHE
4559     BOOLEAN canreparse_post = FALSE;
4560 #endif
4561 
4562     /*
4563      * Check if this is a reply from a POST, and if so,
4564      * seek confirmation if the safe element is not set.  - FM
4565      */
4566     if ((curdoc.post_data != NULL &&
4567 	 curdoc.safe != TRUE) &&
4568 #ifdef USE_SOURCE_CACHE
4569 	(!(canreparse_post = HTcan_reparse_document())) &&
4570 #endif
4571 	(curdoc.isHEAD ? HTConfirm(CONFIRM_POST_RESUBMISSION) :
4572 	 confirm_post_resub(curdoc.address, curdoc.title, 1, 1)) == FALSE) {
4573 	HTInfoMsg(CANCELLED);
4574 	return;
4575     }
4576 
4577     if (HTisDocumentSource()) {
4578 	srcmode_for_next_retrieval(-1);
4579     } else {
4580 	if (HText_getOwner())
4581 	    StrAllocCopy(*ownerS_address_p, HText_getOwner());
4582 	LYUCPushAssumed(HTMainAnchor);
4583 	srcmode_for_next_retrieval(1);
4584     }
4585 
4586 #ifdef USE_SOURCE_CACHE
4587     if (wrap_reparse_document()) {
4588 	/*
4589 	 * These normally get cleaned up after getfile() returns;
4590 	 * since we're not calling getfile(), we have to clean them
4591 	 * up ourselves.  -dsb
4592 	 */
4593 	HTOutputFormat = WWW_PRESENT;
4594 #ifdef USE_PRETTYSRC
4595 	if (psrc_view)
4596 	    HTMark_asSource();
4597 	psrc_view = FALSE;
4598 #endif
4599 	FREE(*ownerS_address_p);	/* not used with source_cache */
4600 	LYUCPopAssumed();	/* probably a right place here */
4601 	HTMLSetCharacterHandling(current_char_set);	/* restore now */
4602 
4603 	return;
4604     } else if (canreparse_post) {
4605 	srcmode_for_next_retrieval(0);
4606 	LYUCPopAssumed();	/* probably a right place here */
4607 	return;
4608     }
4609 #endif
4610 
4611     if (curdoc.title)
4612 	StrAllocCopy(newdoc.title, curdoc.title);
4613 
4614     free_address(&curdoc);	/* so it doesn't get pushed */
4615     LYforce_no_cache = TRUE;
4616 }
4617 
handle_LYK_SWITCH_DTD(void)4618 static void handle_LYK_SWITCH_DTD(void)
4619 {
4620 #ifdef USE_SOURCE_CACHE
4621     BOOLEAN canreparse = FALSE;
4622 
4623     if (!(canreparse = HTcan_reparse_document())) {
4624 #endif
4625 	/*
4626 	 * Check if this is a reply from a POST, and if so,
4627 	 * seek confirmation of reload if the safe element
4628 	 * is not set.  - FM, kw
4629 	 */
4630 	if ((curdoc.post_data != NULL &&
4631 	     curdoc.safe != TRUE) &&
4632 	    confirm_post_resub(curdoc.address, NULL, 1, 1) == FALSE) {
4633 	    HTInfoMsg(WILL_NOT_RELOAD_DOC);
4634 	} else {
4635 	    /*
4636 	     * If currently viewing preparsed source, switching to the other
4637 	     * DTD parsing may show source differences, so stay in source view
4638 	     * - kw
4639 	     */
4640 
4641 	    /* NOTE: this conditional can be considered incorrect -
4642 	       current behaviour - when viewing source and
4643 	       LYPreparsedSource==TRUE, pressing ^V will toggle parser mode
4644 	       AND switch back from the source view to presentation view.-HV
4645 	     */
4646 	    if (HTisDocumentSource() && LYPreparsedSource) {
4647 		srcmode_for_next_retrieval(1);
4648 	    }
4649 	    HText_setNoCache(HTMainText);
4650 	    move_address(&newdoc, &curdoc);
4651 	    newdoc.line = curdoc.line;
4652 	    newdoc.link = curdoc.link;
4653 	}
4654 #ifdef USE_SOURCE_CACHE
4655     }				/* end if no bypass */
4656 #endif
4657     Old_DTD = !Old_DTD;
4658     HTSwitchDTD(!Old_DTD);
4659     HTUserMsg(Old_DTD ? USING_DTD_0 : USING_DTD_1);
4660 #ifdef USE_SOURCE_CACHE
4661     if (canreparse) {
4662 	if (HTisDocumentSource() && LYPreparsedSource) {
4663 	    srcmode_for_next_retrieval(1);
4664 	}
4665 	if (!reparse_document()) {
4666 	    srcmode_for_next_retrieval(0);
4667 	}
4668     }
4669 #endif
4670     return;
4671 }
4672 
4673 #ifdef DIRED_SUPPORT
handle_LYK_TAG_LINK(void)4674 static void handle_LYK_TAG_LINK(void)
4675 {
4676     if (lynx_edit_mode && nlinks > 0 && !no_dired_support) {
4677 	if (!strcmp(LYGetHiliteStr(curdoc.link, 0), ".."))
4678 	    return;		/* Never tag the parent directory */
4679 	if (dir_list_style == MIXED_STYLE) {
4680 	    if (!strcmp(LYGetHiliteStr(curdoc.link, 0), "../"))
4681 		return;
4682 	} else if (!StrNCmp(LYGetHiliteStr(curdoc.link, 0), "Up to ", 6))
4683 	    return;
4684 	{
4685 	    /*
4686 	     * HTList-based management of tag list, see LYLocal.c - KW
4687 	     */
4688 	    HTList *t1 = tagged;
4689 	    char *tagname = NULL;
4690 	    BOOLEAN found = FALSE;
4691 
4692 	    while ((tagname = (char *) HTList_nextObject(t1)) != NULL) {
4693 		if (!strcmp(links[curdoc.link].lname, tagname)) {
4694 		    found = TRUE;
4695 		    HTList_removeObject(tagged, tagname);
4696 		    FREE(tagname);
4697 		    tagflag(FALSE, curdoc.link);
4698 		    break;
4699 		}
4700 	    }
4701 	    if (!found) {
4702 		if (tagged == NULL)
4703 		    tagged = HTList_new();
4704 		tagname = NULL;
4705 		StrAllocCopy(tagname, links[curdoc.link].lname);
4706 		HTList_addObject(tagged, tagname);
4707 		tagflag(TRUE, curdoc.link);
4708 	    }
4709 	}
4710 	if (curdoc.link < nlinks - 1) {
4711 	    set_curdoc_link(curdoc.link + 1);
4712 	} else if (!more_text && LYGetNewline() == 1 && curdoc.link == nlinks
4713 		   - 1) {
4714 	    set_curdoc_link(0);
4715 	} else if (more_text) {	/* next page */
4716 	    LYChgNewline(display_lines);
4717 	}
4718     }
4719 }
4720 #endif /* DIRED_SUPPORT */
4721 
handle_LYK_TOGGLE_HELP(void)4722 static void handle_LYK_TOGGLE_HELP(void)
4723 {
4724     if (user_mode == NOVICE_MODE) {
4725 	toggle_novice_line();
4726 	noviceline(more_text);
4727     }
4728 }
4729 
handle_LYK_TOOLBAR(BOOLEAN * try_internal,BOOLEAN * force_load,int * old_c,int real_c)4730 static void handle_LYK_TOOLBAR(BOOLEAN *try_internal,
4731 			       BOOLEAN *force_load,
4732 			       int *old_c,
4733 			       int real_c)
4734 {
4735     char *cp;
4736     char *toolbar = NULL;
4737 
4738     if (!HText_hasToolbar(HTMainText)) {
4739 	if (*old_c != real_c) {
4740 	    *old_c = real_c;
4741 	    HTUserMsg(NO_TOOLBAR);
4742 	}
4743     } else if (*old_c != real_c) {
4744 	*old_c = real_c;
4745 	cp = trimPoundSelector(curdoc.address);
4746 	HTSprintf0(&toolbar, "%s#%s", curdoc.address, LYToolbarName);
4747 	restorePoundSelector(cp);
4748 	set_address(&newdoc, toolbar);
4749 	FREE(toolbar);
4750 	*try_internal = TRUE;
4751 	*force_load = TRUE;	/* force MainLoop to reload */
4752     }
4753 }
4754 
handle_LYK_TRACE_LOG(BOOLEAN * trace_flag_ptr)4755 static void handle_LYK_TRACE_LOG(BOOLEAN *trace_flag_ptr)
4756 {
4757 #ifndef NO_LYNX_TRACE
4758     /*
4759      * Check whether we've started a TRACE log in this session.  - FM
4760      */
4761     if (LYTraceLogFP == NULL) {
4762 	HTUserMsg(NO_TRACELOG_STARTED);
4763 	return;
4764     }
4765 
4766     /*
4767      * Don't do if already viewing the TRACE log.  - FM
4768      */
4769     if (LYIsUIPage(curdoc.address, UIP_TRACELOG))
4770 	return;
4771 
4772     /*
4773      * If TRACE mode is on, turn it off during this fetch of the TRACE log, so
4774      * we don't enter stuff about this fetch, and set a flag for turning it
4775      * back on when we return to this loop.  Note that we'll miss any messages
4776      * about memory exhaustion if it should occur.  It seems unlikely that
4777      * anything else bad might happen, but if it does, we'll miss messages
4778      * about that too.  We also fflush(), close, and open it again, to make
4779      * sure all stderr messages thus far will be in the log.  - FM
4780      */
4781     if (!LYReopenTracelog(trace_flag_ptr))
4782 	return;
4783 
4784     LYLocalFileToURL(&(newdoc.address), LYTraceLogPath);
4785     LYRegisterUIPage(newdoc.address, UIP_TRACELOG);
4786     StrAllocCopy(newdoc.title, LYNX_TRACELOG_TITLE);
4787     LYFreePostData(&newdoc);
4788     FREE(newdoc.bookmark);
4789     newdoc.isHEAD = FALSE;
4790     newdoc.safe = FALSE;
4791     newdoc.internal_link = FALSE;
4792     if (LYValidate || check_realm) {
4793 	LYPermitURL = TRUE;
4794     }
4795     LYforce_no_cache = TRUE;
4796 #else
4797     HTUserMsg(TRACE_DISABLED);
4798 #endif /* NO_LYNX_TRACE */
4799 }
4800 
4801 #ifdef DIRED_SUPPORT
handle_LYK_UPLOAD(void)4802 static void handle_LYK_UPLOAD(void)
4803 {
4804     /*
4805      * Don't do if already viewing upload options page.
4806      */
4807     if (LYIsUIPage(curdoc.address, UIP_UPLOAD_OPTIONS))
4808 	return;
4809 
4810     if (lynx_edit_mode && !no_dired_support) {
4811 	LYUpload_options(&(newdoc.address), curdoc.address);
4812 	StrAllocCopy(newdoc.title, UPLOAD_OPTIONS_TITLE);
4813 	LYFreePostData(&newdoc);
4814 	FREE(newdoc.bookmark);
4815 	newdoc.isHEAD = FALSE;
4816 	newdoc.safe = FALSE;
4817 	newdoc.internal_link = FALSE;
4818 	/*
4819 	 * Uncache the current listing so that it will be updated to included
4820 	 * the uploaded file if placed in the current directory.  - FM
4821 	 */
4822 	DIRED_UNCACHE_1;
4823     }
4824 }
4825 #endif /* DIRED_SUPPORT */
4826 
handle_LYK_UP_xxx(int * arrowup,int * old_c,int real_c,int scroll_by)4827 static void handle_LYK_UP_xxx(int *arrowup,
4828 			      int *old_c,
4829 			      int real_c,
4830 			      int scroll_by)
4831 {
4832     if (LYGetNewline() > 1) {
4833 	if (LYGetNewline() - scroll_by < 1)
4834 	    scroll_by = LYGetNewline() - 1;
4835 	LYChgNewline(-scroll_by);
4836 	if (nlinks > 0 && curdoc.link > -1) {
4837 	    if (links[curdoc.link].ly + scroll_by <= display_lines) {
4838 		newdoc.link = curdoc.link +
4839 		    HText_LinksInLines(HTMainText,
4840 				       LYGetNewline(),
4841 				       scroll_by);
4842 	    } else {
4843 		*arrowup = TRUE;
4844 	    }
4845 	}
4846     } else if (*old_c != real_c) {
4847 	*old_c = real_c;
4848 	HTInfoMsg(ALREADY_AT_BEGIN);
4849     }
4850 }
4851 
handle_LYK_UP_HALF(int * arrowup,int * old_c,int real_c)4852 static void handle_LYK_UP_HALF(int *arrowup,
4853 			       int *old_c,
4854 			       int real_c)
4855 {
4856     handle_LYK_UP_xxx(arrowup, old_c, real_c, display_lines / 2);
4857 }
4858 
handle_LYK_UP_LINK(int * follow_col,int * arrowup,int * old_c,int real_c)4859 static void handle_LYK_UP_LINK(int *follow_col,
4860 			       int *arrowup,
4861 			       int *old_c,
4862 			       int real_c)
4863 {
4864     if (curdoc.link > 0 &&
4865 	(links[0].ly != links[curdoc.link].ly ||
4866 	 !HText_LinksInLines(HTMainText, 1, LYGetNewline() - 1))) {
4867 	/* more links before this on screen, and first of them on
4868 	   a different line or no previous links before this screen? */
4869 	int newlink;
4870 
4871 	if (*follow_col == -1) {
4872 	    const char *text = LYGetHiliteStr(curdoc.link, 0);
4873 
4874 	    *follow_col = links[curdoc.link].lx;
4875 
4876 	    if (text != NULL)
4877 		*follow_col += (int) strlen(text) / 2;
4878 	}
4879 
4880 	newlink = find_link_near_col(*follow_col, -1);
4881 	if (newlink > -1) {
4882 	    set_curdoc_link(newlink);
4883 	} else if (*old_c != real_c) {
4884 	    *old_c = real_c;
4885 	    HTUserMsg(NO_LINKS_ABOVE);
4886 	}
4887 
4888     } else if (curdoc.line > 1 && LYGetNewline() > 1) {		/* previous page */
4889 	int scrollamount = (LYGetNewline() > display_lines
4890 			    ? display_lines
4891 			    : LYGetNewline() - 1);
4892 
4893 	LYChgNewline(-scrollamount);
4894 	if (scrollamount < display_lines &&
4895 	    nlinks > 0 && curdoc.link > -1 &&
4896 	    links[0].ly - 1 + scrollamount <= display_lines) {
4897 	    newdoc.link = HText_LinksInLines(HTMainText,
4898 					     1,
4899 					     scrollamount) - 1;
4900 	} else {
4901 	    *arrowup = TRUE;
4902 	}
4903 
4904     } else if (*old_c != real_c) {
4905 	*old_c = real_c;
4906 	HTInfoMsg(ALREADY_AT_BEGIN);
4907     }
4908 }
4909 
handle_LYK_UP_TWO(int * arrowup,int * old_c,int real_c)4910 static void handle_LYK_UP_TWO(int *arrowup,
4911 			      int *old_c,
4912 			      int real_c)
4913 {
4914     handle_LYK_UP_xxx(arrowup, old_c, real_c, 2);
4915 }
4916 
handle_LYK_VIEW_BOOKMARK(BOOLEAN * refresh_screen,int * old_c,int real_c)4917 static void handle_LYK_VIEW_BOOKMARK(BOOLEAN *refresh_screen,
4918 				     int *old_c,
4919 				     int real_c)
4920 {
4921     const char *cp;
4922 
4923     if (LYValidate) {
4924 	if (*old_c != real_c) {
4925 	    *old_c = real_c;
4926 	    HTUserMsg(BOOKMARKS_DISABLED);
4927 	}
4928 	return;
4929     }
4930 
4931     /*
4932      * See if a bookmark exists.  If it does replace newdoc.address with its
4933      * name.
4934      */
4935     if ((cp = get_bookmark_filename(&newdoc.address)) != NULL) {
4936 	if (*cp == '\0' || !strcmp(cp, " ") ||
4937 	    !strcmp(curdoc.address, newdoc.address)) {
4938 	    if (LYMultiBookmarks != MBM_OFF)
4939 		*refresh_screen = TRUE;
4940 	    return;
4941 	}
4942 #ifdef KANJI_CODE_OVERRIDE
4943 	if (HTCJK == JAPANESE) {
4944 	    last_kcode = NOKANJI;	/* AUTO */
4945 	}
4946 #endif
4947 	LYforce_no_cache = TRUE;	/*force the document to be reloaded */
4948 	StrAllocCopy(newdoc.title, BOOKMARK_TITLE);
4949 	StrAllocCopy(newdoc.bookmark, BookmarkPage);
4950 	LYFreePostData(&newdoc);
4951 	newdoc.isHEAD = FALSE;
4952 	newdoc.safe = FALSE;
4953 	newdoc.internal_link = FALSE;
4954     } else {
4955 	if (*old_c != real_c) {
4956 	    *old_c = real_c;
4957 	    LYMBM_statusline(BOOKMARKS_NOT_OPEN);
4958 	    LYSleepAlert();
4959 	    if (LYMultiBookmarks != MBM_OFF) {
4960 		*refresh_screen = TRUE;
4961 	    }
4962 	}
4963     }
4964 }
4965 
handle_LYK_VLINKS(int * cmd,BOOLEAN * newdoc_link_is_absolute)4966 static BOOLEAN handle_LYK_VLINKS(int *cmd,
4967 				 BOOLEAN *newdoc_link_is_absolute)
4968 {
4969     int c;
4970 
4971     if (LYIsUIPage(curdoc.address, UIP_VLINKS)) {
4972 	/*
4973 	 * Already viewing visited links page, so get out.
4974 	 */
4975 	*cmd = LYK_PREV_DOC;
4976 	return TRUE;
4977     }
4978 
4979     /*
4980      * Print visited links page to file.
4981      */
4982     c = LYShowVisitedLinks(&newdoc.address);
4983     if (c < 0) {
4984 	HTUserMsg(VISITED_LINKS_EMPTY);
4985 	return FALSE;
4986     }
4987     StrAllocCopy(newdoc.title, VISITED_LINKS_TITLE);
4988     LYFreePostData(&newdoc);
4989     FREE(newdoc.bookmark);
4990     newdoc.isHEAD = FALSE;
4991     newdoc.safe = FALSE;
4992     newdoc.internal_link = FALSE;
4993     if (c > 0) {
4994 	/* Select a correct link. */
4995 	*newdoc_link_is_absolute = TRUE;
4996 	newdoc.link = c - 1;
4997     }
4998     if (LYValidate || check_realm) {
4999 	LYPermitURL = TRUE;
5000 	StrAllocCopy(lynxlinksfile, newdoc.address);
5001     }
5002     return FALSE;
5003 }
5004 
handle_LYK_WHEREIS(int cmd,BOOLEAN * refresh_screen)5005 void handle_LYK_WHEREIS(int cmd,
5006 			BOOLEAN *refresh_screen)
5007 {
5008     BOOLEAN have_target_onscreen = (BOOLEAN) (!isBEmpty(prev_target) &&
5009 					      HText_pageHasPrevTarget());
5010     BOOL found;
5011     int oldcur = curdoc.link;	/* temporarily remember */
5012     char *remember_old_target = NULL;
5013 
5014     if (have_target_onscreen)
5015 	StrAllocCopy(remember_old_target, prev_target->str);
5016     else
5017 	StrAllocCopy(remember_old_target, "");
5018 
5019     if (cmd == LYK_WHEREIS) {
5020 	/*
5021 	 * Reset prev_target to force prompting for a new search string and to
5022 	 * turn off highlighting if no search string is entered by the user.
5023 	 */
5024 	BStrCopy0(prev_target, "");
5025     }
5026     found = textsearch(&curdoc, &prev_target,
5027 		       (cmd == LYK_WHEREIS)
5028 		       ? 0
5029 		       : ((cmd == LYK_NEXT)
5030 			  ? 1
5031 			  : -1));
5032 
5033     /*
5034      * Force a redraw to ensure highlighting of hits even when found on the
5035      * same page, or clearing of highlighting if the default search string was
5036      * erased without replacement.  - FM
5037      */
5038     /*
5039      * Well let's try to avoid it at least in a few cases
5040      * where it is not needed. - kw
5041      */
5042     if (www_search_result >= 0 && www_search_result != curdoc.line) {
5043 	*refresh_screen = TRUE;	/* doesn't really matter */
5044     } else if (!found) {
5045 	*refresh_screen = have_target_onscreen;
5046     } else if (!have_target_onscreen && found) {
5047 	*refresh_screen = TRUE;
5048     } else if (www_search_result == curdoc.line &&
5049 	       curdoc.link == oldcur &&
5050 	       curdoc.link >= 0 && nlinks > 0 &&
5051 	       links[curdoc.link].ly >= (display_lines / 3)) {
5052 	*refresh_screen = TRUE;
5053     } else if ((LYcase_sensitive && 0 != strcmp(prev_target->str,
5054 						remember_old_target)) ||
5055 	       (!LYcase_sensitive && 0 != strcasecomp8(prev_target->str,
5056 						       remember_old_target))) {
5057 	*refresh_screen = TRUE;
5058     }
5059     FREE(remember_old_target);
5060 }
5061 
5062 /*
5063  * Get a number from the user and follow that link number.
5064  */
handle_LYK_digit(int c,BOOLEAN * force_load,int * old_c,int real_c,BOOLEAN * try_internal GCC_UNUSED)5065 static void handle_LYK_digit(int c,
5066 			     BOOLEAN *force_load,
5067 			     int *old_c,
5068 			     int real_c,
5069 			     BOOLEAN *try_internal GCC_UNUSED)
5070 {
5071     int lindx = ((nlinks > 0) ? curdoc.link : 0);
5072     int number;
5073     char *temp = NULL;
5074 
5075     /* pass cur line num for use in follow_link_number()
5076      * Note: Current line may not equal links[cur].line
5077      */
5078     number = curdoc.line;
5079     switch (follow_link_number(c, lindx, &newdoc, &number)) {
5080     case DO_LINK_STUFF:
5081 	/*
5082 	 * Follow a normal link.
5083 	 */
5084 	set_address(&newdoc, links[lindx].lname);
5085 	StrAllocCopy(newdoc.title, LYGetHiliteStr(lindx, 0));
5086 	/*
5087 	 * For internal links, retain POST content if present.  If we are on
5088 	 * the List Page, prevent pushing it on the history stack.  Otherwise
5089 	 * set try_internal to signal that the top of the loop should attempt
5090 	 * to reposition directly, without calling getfile.  - kw
5091 	 */
5092 	if (track_internal_links) {
5093 	    if (links[lindx].type == WWW_INTERN_LINK_TYPE) {
5094 		LYinternal_flag = TRUE;
5095 		newdoc.internal_link = TRUE;
5096 		if (LYIsListpageTitle(NonNull(curdoc.title)) &&
5097 		    (LYIsUIPage(curdoc.address, UIP_LIST_PAGE) ||
5098 		     LYIsUIPage(curdoc.address, UIP_ADDRLIST_PAGE))) {
5099 		    if (check_history()) {
5100 			LYinternal_flag = TRUE;
5101 		    } else {
5102 			HTLastConfirmCancelled();	/* reset flag */
5103 			if (!confirm_post_resub(newdoc.address,
5104 						newdoc.title,
5105 						((LYresubmit_posts &&
5106 						  HText_POSTReplyLoaded(&newdoc))
5107 						 ? 1
5108 						 : 2),
5109 						2)) {
5110 			    if (HTLastConfirmCancelled() ||
5111 				(LYresubmit_posts &&
5112 				 !HText_POSTReplyLoaded(&newdoc))) {
5113 				/* cancel the whole thing */
5114 				LYforce_no_cache = FALSE;
5115 				reloading = FALSE;
5116 				copy_address(&newdoc, &curdoc);
5117 				StrAllocCopy(newdoc.title, curdoc.title);
5118 				newdoc.internal_link = curdoc.internal_link;
5119 				HTInfoMsg(CANCELLED);
5120 				if (nlinks > 0)
5121 				    HText_pageDisplay(curdoc.line, prev_target->str);
5122 				break;
5123 			    } else if (LYresubmit_posts) {
5124 				/* If LYresubmit_posts is set, and the
5125 				   answer was No, and we have a cached
5126 				   copy, then use it. - kw */
5127 				LYforce_no_cache = FALSE;
5128 			    } else {
5129 				/* if No, but not ^C or ^G, drop
5130 				 * the post data.  Maybe the link
5131 				 * wasn't meant to be internal after
5132 				 * all, here we can recover from that
5133 				 * assumption. - kw */
5134 				LYFreePostData(&newdoc);
5135 				newdoc.internal_link = FALSE;
5136 				HTAlert(DISCARDING_POST_DATA);
5137 			    }
5138 			}
5139 		    }
5140 		    /*
5141 		     * Don't push the List Page if we follow an internal link given
5142 		     * by it.  - kw
5143 		     */
5144 		    free_address(&curdoc);
5145 		} else
5146 		    *try_internal = TRUE;
5147 		if (!(LYresubmit_posts && newdoc.post_data))
5148 		    LYinternal_flag = TRUE;
5149 		*force_load = TRUE;
5150 		break;
5151 	    } else {
5152 		/*
5153 		 * Free POST content if not an internal link.  - kw
5154 		 */
5155 		LYFreePostData(&newdoc);
5156 	    }
5157 	}
5158 	/*
5159 	 * Might be an anchor in the same doc from a POST form.  If so, don't
5160 	 * free the content.  -- FM
5161 	 */
5162 	if (are_different(&curdoc, &newdoc)) {
5163 	    LYFreePostData(&newdoc);
5164 	    FREE(newdoc.bookmark);
5165 	    newdoc.isHEAD = FALSE;
5166 	    newdoc.safe = FALSE;
5167 	    if (isLYNXMESSAGES(newdoc.address))
5168 		LYforce_no_cache = TRUE;
5169 	}
5170 	newdoc.internal_link = FALSE;
5171 	*force_load = TRUE;	/* force MainLoop to reload */
5172 	break;
5173 
5174     case DO_GOTOLINK_STUFF:
5175 	/*
5176 	 * Position on a normal link, don't follow it.  - KW
5177 	 */
5178 	LYSetNewline(newdoc.line);
5179 	newdoc.line = 1;
5180 	if (LYGetNewline() == curdoc.line) {
5181 	    /*
5182 	     * It's a link in the current page.  - FM
5183 	     */
5184 	    if (nlinks > 0 && curdoc.link > -1) {
5185 		if (curdoc.link == newdoc.link) {
5186 		    /*
5187 		     * It's the current link, and presumably reflects a typo in
5188 		     * the statusline entry, so issue a statusline message for
5189 		     * the typo-prone users (like me 8-).  - FM
5190 		     */
5191 		    HTSprintf0(&temp, LINK_ALREADY_CURRENT, number);
5192 		    HTUserMsg(temp);
5193 		    FREE(temp);
5194 		} else {
5195 		    /*
5196 		     * It's a different link on this page,
5197 		     */
5198 		    set_curdoc_link(newdoc.link);
5199 		    newdoc.link = 0;
5200 		}
5201 	    }
5202 	}
5203 	break;			/* nothing more to do */
5204 
5205     case DO_GOTOPAGE_STUFF:
5206 	/*
5207 	 * Position on a page in this document.  - FM
5208 	 */
5209 	LYSetNewline(newdoc.line);
5210 	newdoc.line = 1;
5211 	if (LYGetNewline() == curdoc.line) {
5212 	    /*
5213 	     * It's the current page, so issue a statusline message for the
5214 	     * typo-prone users (like me 8-).  - FM
5215 	     */
5216 	    if (LYGetNewline() <= 1) {
5217 		HTInfoMsg(ALREADY_AT_BEGIN);
5218 	    } else if (!more_text) {
5219 		HTInfoMsg(ALREADY_AT_END);
5220 	    } else {
5221 		HTSprintf0(&temp, ALREADY_AT_PAGE, number);
5222 		HTUserMsg(temp);
5223 		FREE(temp);
5224 	    }
5225 	}
5226 	break;
5227 
5228     case PRINT_ERROR:
5229 	*old_c = real_c;
5230 	HTUserMsg(BAD_LINK_NUM_ENTERED);
5231 	break;
5232     }
5233     return;
5234 }
5235 
5236 #ifdef SUPPORT_CHDIR
5237 
5238 /* original implementation by VH */
handle_LYK_CHDIR(void)5239 void handle_LYK_CHDIR(void)
5240 {
5241     static bstring *buf = NULL;
5242     char *p = NULL;
5243 
5244     if (no_chdir) {
5245 	HTUserMsg(CHDIR_DISABLED);
5246 	return;
5247     }
5248 
5249     _statusline(gettext("cd to:"));
5250     if (LYgetBString(&buf, VISIBLE, 0, NORECALL) < 0 || isBEmpty(buf)) {
5251 	HTInfoMsg(CANCELLED);
5252 	return;
5253     }
5254 
5255     if (LYIsTilde(buf->str[0]) &&
5256 	(LYIsPathSep(buf->str[1]) || buf->str[1] == '\0')) {
5257 	HTSprintf0(&p, "%s%s", Home_Dir(), buf->str + 1);
5258     } else {
5259 	StrAllocCopy(p, buf->str);
5260     }
5261 
5262     CTRACE((tfp, "changing directory to '%s'\n", p));
5263     if (chdir(p)) {
5264 	switch (errno) {
5265 	case EACCES:
5266 	    HTInfoMsg(COULD_NOT_ACCESS_DIR);
5267 	    break;
5268 	case ENOENT:
5269 	    HTInfoMsg(gettext("No such directory"));
5270 	    break;
5271 	case ENOTDIR:
5272 	    HTInfoMsg(gettext("A component of path is not a directory"));
5273 	    break;
5274 	default:
5275 	    HTInfoMsg(gettext("failed to change directory"));
5276 	    break;
5277 	}
5278     } else {
5279 #ifdef DIRED_SUPPORT
5280 	/*if in dired, load content of other directory */
5281 	if (!no_dired_support
5282 	    && (lynx_edit_mode || (LYIsUIPage(curdoc.address, UIP_DIRED_MENU)))) {
5283 	    char buf2[LY_MAXPATH];
5284 	    char *addr = NULL;
5285 
5286 	    Current_Dir(buf2);
5287 	    LYLocalFileToURL(&addr, buf2);
5288 
5289 	    newdoc.address = addr;
5290 	    newdoc.isHEAD = FALSE;
5291 	    StrAllocCopy(newdoc.title, gettext("A URL specified by the user"));
5292 	    LYFreePostData(&newdoc);
5293 	    FREE(newdoc.bookmark);
5294 	    newdoc.safe = FALSE;
5295 	    newdoc.internal_link = FALSE;
5296 	    /**force_load = TRUE;*/
5297 	    if (lynx_edit_mode) {
5298 		DIRED_UNCACHE_2;
5299 	    }
5300 	} else
5301 #endif
5302 	    HTInfoMsg(OPERATION_DONE);
5303     }
5304     FREE(p);
5305 }
5306 
handle_LYK_PWD(void)5307 static void handle_LYK_PWD(void)
5308 {
5309     char buffer[LY_MAXPATH];
5310     int save_secs = InfoSecs;
5311     BOOLEAN save_wait = no_pause;
5312 
5313     if (Secs2SECS(save_secs) < 1)
5314 	InfoSecs = SECS2Secs(1);
5315     no_pause = FALSE;
5316 
5317     HTInfoMsg(Current_Dir(buffer));
5318 
5319     InfoSecs = save_secs;
5320     no_pause = save_wait;
5321 }
5322 #endif
5323 
5324 #ifdef USE_CURSES_PADS
5325 /*
5326  * Having jumps larger than this is counter-productive.  Indeed, it is natural
5327  * to expect that when the relevant text appears, one would "overshoot" and
5328  * would scroll 3-4 extra full screens.  When going back, the "accumulation"
5329  * logic would again start moving in full screens, so one would overshoot
5330  * again, etc.
5331  *
5332  * Going back, one can fix it in 28 keypresses. The relevant text will appear
5333  * on the screen soon enough for the key-repeat to become not that important,
5334  * and we are still moving in smaller steps than when we overshot.  Since key
5335  * repeat is not important, even if we overshoot again, it is going to be by 30
5336  * steps, which is easy to fix by reversing the direction again.
5337  */
repeat_to_delta(int n)5338 static int repeat_to_delta(int n)
5339 {
5340     int threshold = LYcols / 3;
5341 
5342     while (threshold > 0) {
5343 	if (n >= threshold) {
5344 	    n = threshold;
5345 	    break;
5346 	}
5347 	threshold = (threshold * 2) / 3;
5348     }
5349     return n;
5350 }
5351 
handle_LYK_SHIFT_LEFT(BOOLEAN * flag,int count)5352 static void handle_LYK_SHIFT_LEFT(BOOLEAN *flag, int count)
5353 {
5354     if (!LYwideLines) {
5355 	HTAlert(SHIFT_VS_LINEWRAP);
5356 	return;
5357     }
5358     if (LYshiftWin > 0) {
5359 	LYshiftWin -= repeat_to_delta(count);
5360 	*flag = TRUE;
5361     }
5362     if (LYshiftWin < 0)
5363 	LYshiftWin = 0;
5364 }
5365 
handle_LYK_SHIFT_RIGHT(BOOLEAN * flag,int count)5366 static void handle_LYK_SHIFT_RIGHT(BOOLEAN *flag, int count)
5367 {
5368     if (!LYwideLines) {
5369 	HTAlert(SHIFT_VS_LINEWRAP);
5370 	return;
5371     }
5372     LYshiftWin += repeat_to_delta(count);
5373     *flag = TRUE;
5374 }
5375 
handle_LYK_LINEWRAP_TOGGLE(int * cmd,BOOLEAN * flag)5376 static BOOLEAN handle_LYK_LINEWRAP_TOGGLE(int *cmd,
5377 					  BOOLEAN *flag)
5378 {
5379     static const char *choices[] =
5380     {
5381 	"Try to fit screen width",
5382 	"No line wrap in columns",
5383 	"Wrap columns at screen width",
5384 	"Wrap columns at 3/4 screen width",
5385 	"Wrap columns at 2/3 screen width",
5386 	"Wrap columns at 1/2 screen width",
5387 	"Wrap columns at 1/3 screen width",
5388 	"Wrap columns at 1/4 screen width",
5389 	NULL
5390     };
5391     static int wrap[] =
5392     {
5393 	0,
5394 	0,
5395 	12,			/* In units of 1/12 */
5396 	9,
5397 	8,
5398 	6,
5399 	4,
5400 	3
5401     };
5402     int c;
5403     int code = FALSE;
5404 
5405     CTRACE((tfp, "Entering handle_LYK_LINEWRAP_TOGGLE\n"));
5406     if (LYwin != stdscr) {
5407 	/* Somehow the mouse is over the number instead of being over the
5408 	   name, so we decrease x. */
5409 	c = LYChoosePopup(!LYwideLines,
5410 			  LYlines / 2 - 2,
5411 			  LYcolLimit / 2 - 6,
5412 			  choices, (int) TABLESIZE(choices) - 1,
5413 			  FALSE, TRUE);
5414 	/*
5415 	 * LYhandlePopupList() wasn't really meant to be used outside of
5416 	 * old-style Options menu processing.  One result of mis-using it here
5417 	 * is that we have to deal with side-effects regarding SIGINT signal
5418 	 * handler and the term_options global variable.  - kw
5419 	 */
5420 	if (!term_options) {
5421 	    CTRACE((tfp,
5422 		    "...setting LYwideLines %d, LYtableCols %d (have %d and %d)\n",
5423 		    c, wrap[c],
5424 		    LYwideLines,
5425 		    LYtableCols));
5426 
5427 	    LYwideLines = c;
5428 	    LYtableCols = wrap[c];
5429 
5430 	    if (LYwideLines == 0)
5431 		LYshiftWin = 0;
5432 	    *flag = TRUE;
5433 	    HTUserMsg(LYwideLines ? LINEWRAP_OFF : LINEWRAP_ON);
5434 	    code = reparse_or_reload(cmd);
5435 	}
5436     }
5437     return (BOOLEAN) code;
5438 }
5439 #endif
5440 
5441 #ifdef USE_MAXSCREEN_TOGGLE
handle_LYK_MAXSCREEN_TOGGLE(int * cmd)5442 static BOOLEAN handle_LYK_MAXSCREEN_TOGGLE(int *cmd)
5443 {
5444     static int flag = 0;
5445 
5446     CTRACE((tfp, "Entering handle_LYK_MAXSCREEN_TOGGLE\n"));
5447     if (flag) {
5448 	CTRACE((tfp, "Calling recoverWindowSize()\n"));
5449 	recoverWindowSize();
5450 	flag = 0;
5451     } else {
5452 	CTRACE((tfp, "Calling maxmizeWindowSize()\n"));
5453 	maxmizeWindowSize();
5454 	flag = 1;
5455     }
5456     return reparse_or_reload(cmd);
5457 }
5458 #endif
5459 
5460 #ifdef LY_FIND_LEAKS
5461 #define CleanupMainLoop() \
5462  	BStrFree(prev_target); \
5463  	BStrFree(user_input_buffer)
5464 #else
5465 #define CleanupMainLoop()	/* nothing */
5466 #endif
5467 
5468 /*
5469  * Here's where we do all the work.
5470  * mainloop is basically just a big switch dependent on the users input.  I
5471  * have tried to offload most of the work done here to procedures to make it
5472  * more modular, but this procedure still does a lot of variable manipulation.
5473  * This needs some work to make it neater.  - Lou Moutilli
5474  *					(memoir from the original Lynx - FM)
5475  */
mainloop(void)5476 int mainloop(void)
5477 {
5478 #if defined(WIN_EX)		/* 1997/10/08 (Wed) 14:52:06 */
5479     char sjis_buff[MAX_LINE];
5480     char temp_buff[sizeof(sjis_buff) * 4];
5481 #endif
5482     int c = 0;
5483     int real_c = 0;
5484     int old_c = 0;
5485     int pending_form_c = -1;
5486     int cmd = LYK_DO_NOTHING, real_cmd = LYK_DO_NOTHING;
5487     int getresult;
5488     int arrowup = FALSE, show_help = FALSE;
5489     bstring *user_input_buffer = NULL;
5490     const char *cshelpfile = NULL;
5491     BOOLEAN first_file = TRUE;
5492     BOOLEAN popped_doc = FALSE;
5493     BOOLEAN refresh_screen = FALSE;
5494     BOOLEAN force_load = FALSE;
5495     BOOLEAN try_internal = FALSE;
5496     BOOLEAN crawl_ok = FALSE;
5497     BOOLEAN vi_keys_flag = vi_keys;
5498     BOOLEAN emacs_keys_flag = emacs_keys;
5499     BOOLEAN trace_mode_flag = FALSE;
5500     BOOLEAN forced_HTML_mode = LYforce_HTML_mode;
5501     char cfile[128];
5502     FILE *cfp;
5503     char *cp;
5504     int ch = 0;
5505     RecallType recall = NORECALL;
5506     int URLTotal = 0;
5507     int URLNum;
5508     BOOLEAN FirstURLRecall = TRUE;
5509     char *temp = NULL;
5510     BOOLEAN ForcePush = FALSE;
5511     BOOLEAN override_LYresubmit_posts = FALSE;
5512     BOOLEAN newdoc_link_is_absolute = FALSE;
5513     BOOLEAN curlink_is_editable;
5514     BOOLEAN use_last_tfpos;
5515     unsigned int len;
5516     int i;
5517     int follow_col = -1, key_count = 0, last_key = 0;
5518     int tmpNewline;
5519     DocInfo tmpDocInfo;
5520 
5521     /* "internal" means "within the same document, with certainty".  It includes a
5522      * space so it cannot conflict with any (valid) "TYPE" attributes on A
5523      * elements.  [According to which DTD, anyway??] - kw
5524      */
5525     HTInternalLink = HTAtom_for("internal link");	/* init, used as const */
5526 
5527 #ifndef WWW_SOURCE
5528     WWW_SOURCE = HTAtom_for("www/source");	/* init, used as const */
5529 #endif
5530 
5531     /*
5532      * curdoc.address contains the name of the file that is currently open.
5533      * newdoc.address contains the name of the file that will soon be
5534      *                opened if it exits.
5535      * prev_target    contains the last search string the user searched for.
5536      * newdoc.title   contains the link name that the user last chose to get
5537      *                into the current link (file).
5538      */
5539     /* initialize some variables */
5540     newdoc.address = NULL;
5541     newdoc.title = NULL;
5542     newdoc.post_data = NULL;
5543     newdoc.post_content_type = NULL;
5544     newdoc.bookmark = NULL;
5545     newdoc.internal_link = FALSE;
5546     curdoc.address = NULL;
5547     curdoc.title = NULL;
5548     curdoc.post_data = NULL;
5549     curdoc.post_content_type = NULL;
5550     curdoc.bookmark = NULL;
5551     curdoc.internal_link = FALSE;
5552 #ifdef USE_COLOR_STYLE
5553     curdoc.style = NULL;
5554     newdoc.style = NULL;
5555 #endif
5556 #ifndef USE_SESSIONS
5557     nhist = 0;
5558 #endif
5559     BStrCopy0(user_input_buffer, "");
5560     BStrCopy0(prev_target, "");
5561 #ifdef LY_FIND_LEAKS
5562     atexit(free_mainloop_variables);
5563 #endif
5564   initialize:
5565     set_address(&newdoc, startfile);
5566     StrAllocCopy(startrealm, startfile);
5567     StrAllocCopy(newdoc.title, gettext("Entry into main screen"));
5568     newdoc.isHEAD = FALSE;
5569     newdoc.safe = FALSE;
5570     newdoc.line = 1;
5571     newdoc.link = 0;
5572 
5573 #ifdef USE_SLANG
5574     if (TRACE && LYCursesON) {
5575 	LYaddstr("\n");
5576 	LYrefresh();
5577     }
5578 #endif /* USE_SLANG */
5579     CTRACE((tfp, "Entering mainloop, startfile=%s\n", startfile));
5580 
5581     if (form_post_data) {
5582 	BStrCopy0(newdoc.post_data, form_post_data);
5583 	StrAllocCopy(newdoc.post_content_type,
5584 		     "application/x-www-form-urlencoded");
5585     } else if (form_get_data) {
5586 	StrAllocCat(newdoc.address, form_get_data);
5587     }
5588 
5589     if (bookmark_start) {
5590 	if (LYValidate) {
5591 	    HTAlert(BOOKMARKS_DISABLED);
5592 	    bookmark_start = FALSE;
5593 	    goto initialize;
5594 	} else if (traversal) {
5595 	    HTAlert(BOOKMARKS_NOT_TRAVERSED);
5596 	    traversal = FALSE;
5597 	    crawl = FALSE;
5598 	    bookmark_start = FALSE;
5599 	    goto initialize;
5600 	} else {
5601 	    const char *cp1;
5602 
5603 	    /*
5604 	     * See if a bookmark page exists.  If it does, replace
5605 	     * newdoc.address with its name
5606 	     */
5607 	    if ((cp1 = get_bookmark_filename(&newdoc.address)) != NULL &&
5608 		*cp1 != '\0' && strcmp(cp1, " ")) {
5609 		StrAllocCopy(newdoc.title, BOOKMARK_TITLE);
5610 		StrAllocCopy(newdoc.bookmark, BookmarkPage);
5611 		StrAllocCopy(startrealm, newdoc.address);
5612 		LYFreePostData(&newdoc);
5613 		newdoc.isHEAD = FALSE;
5614 		newdoc.safe = FALSE;
5615 		CTRACE((tfp, "Using bookmarks=%s\n", newdoc.address));
5616 	    } else {
5617 		HTUserMsg(BOOKMARKS_NOT_OPEN);
5618 		bookmark_start = FALSE;
5619 		goto initialize;
5620 	    }
5621 	}
5622     }
5623 
5624     FREE(form_post_data);
5625     FREE(form_get_data);
5626 
5627     LYSetDisplayLines();
5628 
5629     while (TRUE) {
5630 #ifdef USE_COLOR_STYLE
5631 	if (curdoc.style != NULL)
5632 	    force_load = TRUE;
5633 #endif
5634 	/*
5635 	 * If newdoc.address is different from curdoc.address then we need to
5636 	 * go out and find and load newdoc.address.
5637 	 */
5638 	if (LYforce_no_cache || force_load ||
5639 	    are_different(&curdoc, &newdoc)) {
5640 
5641 	    force_load = FALSE;	/* done */
5642 	    if (TRACE && LYCursesON) {
5643 		LYHideCursor();	/* make sure cursor is down */
5644 #ifdef USE_SLANG
5645 		LYaddstr("\n");
5646 #endif /* USE_SLANG */
5647 		LYrefresh();
5648 	    }
5649 	  try_again:
5650 	    /*
5651 	     * Push the old file onto the history stack if we have a current
5652 	     * doc and a new address.  - FM
5653 	     */
5654 	    if (curdoc.address && newdoc.address) {
5655 		/*
5656 		 * Don't actually push if this is a LYNXDOWNLOAD URL, because
5657 		 * that returns NORMAL even if it fails due to a spoof attempt
5658 		 * or file access problem, and we set the newdoc structure
5659 		 * elements to the curdoc structure elements under case NORMAL.
5660 		 * - FM
5661 		 */
5662 		if (!isLYNXDOWNLOAD(newdoc.address)) {
5663 		    LYpush(&curdoc, ForcePush);
5664 		}
5665 	    } else if (!newdoc.address) {
5666 		/*
5667 		 * If newdoc.address is empty then pop a file and load it.  -
5668 		 * FM
5669 		 */
5670 		LYhist_prev(&newdoc);
5671 		popped_doc = TRUE;
5672 
5673 		/*
5674 		 * If curdoc had been reached via an internal
5675 		 * (fragment) link from what we now have just
5676 		 * popped into newdoc, then override non-caching in
5677 		 * all cases. - kw
5678 		 */
5679 		if (track_internal_links &&
5680 		    curdoc.internal_link &&
5681 		    !are_phys_different(&curdoc, &newdoc)) {
5682 		    LYinternal_flag = TRUE;
5683 		    LYoverride_no_cache = TRUE;
5684 		    LYforce_no_cache = FALSE;
5685 		    try_internal = TRUE;
5686 		} else {
5687 		    /*
5688 		     * Force a no_cache override unless it's a bookmark file,
5689 		     * or it has POST content and LYresubmit_posts is set
5690 		     * without safe also set, and we are not going to another
5691 		     * position in the current document or restoring the
5692 		     * previous document due to a NOT_FOUND or NULLFILE return
5693 		     * value from getfile().  - FM
5694 		     */
5695 		    if ((newdoc.bookmark != NULL) ||
5696 			(newdoc.post_data != NULL &&
5697 			 !newdoc.safe &&
5698 			 LYresubmit_posts &&
5699 			 !override_LYresubmit_posts &&
5700 			 NO_INTERNAL_OR_DIFFERENT(&curdoc, &newdoc))) {
5701 			LYoverride_no_cache = FALSE;
5702 		    } else {
5703 			LYoverride_no_cache = TRUE;
5704 		    }
5705 		}
5706 	    }
5707 	    override_LYresubmit_posts = FALSE;
5708 
5709 	    if (HEAD_request) {
5710 		/*
5711 		 * Make SURE this is an appropriate request.  - FM
5712 		 */
5713 		if (newdoc.address) {
5714 		    if (LYCanDoHEAD(newdoc.address) == TRUE) {
5715 			newdoc.isHEAD = TRUE;
5716 		    } else if (isLYNXIMGMAP(newdoc.address)) {
5717 			if (LYCanDoHEAD(newdoc.address + LEN_LYNXIMGMAP) == TRUE) {
5718 			    StrAllocCopy(temp, newdoc.address + LEN_LYNXIMGMAP);
5719 			    free_address(&newdoc);
5720 			    newdoc.address = temp;
5721 			    newdoc.isHEAD = TRUE;
5722 			    temp = NULL;
5723 			}
5724 		    }
5725 		}
5726 		try_internal = FALSE;
5727 		HEAD_request = FALSE;
5728 	    }
5729 
5730 	    /*
5731 	     * If we're getting the TRACE log and it's not new, check whether
5732 	     * its HText structure has been dumped, and if so, fflush() and
5733 	     * fclose() it to ensure it's fully updated, and then fopen() it
5734 	     * again.  - FM
5735 	     */
5736 	    if (LYUseTraceLog == TRUE &&
5737 		trace_mode_flag == FALSE &&
5738 		LYTraceLogFP != NULL &&
5739 		LYIsUIPage(newdoc.address, UIP_TRACELOG)) {
5740 		DocAddress WWWDoc;
5741 		HTParentAnchor *tmpanchor;
5742 
5743 		WWWDoc.address = newdoc.address;
5744 		WWWDoc.post_data = newdoc.post_data;
5745 		WWWDoc.post_content_type = newdoc.post_content_type;
5746 		WWWDoc.bookmark = newdoc.bookmark;
5747 		WWWDoc.isHEAD = newdoc.isHEAD;
5748 		WWWDoc.safe = newdoc.safe;
5749 		tmpanchor = HTAnchor_findAddress(&WWWDoc);
5750 		if ((HText *) HTAnchor_document(tmpanchor) == NULL) {
5751 		    if (!LYReopenTracelog(&trace_mode_flag)) {
5752 			old_c = 0;
5753 			cmd = LYK_PREV_DOC;
5754 			goto new_cmd;
5755 		    }
5756 		}
5757 	    }
5758 
5759 	    LYRequestTitle = newdoc.title;
5760 	    if (newdoc.bookmark)
5761 		LYforce_HTML_mode = TRUE;
5762 	    if (LYValidate &&
5763 		startfile_ok &&
5764 		newdoc.address && startfile && homepage &&
5765 		(!strcmp(newdoc.address, startfile) ||
5766 		 !strcmp(newdoc.address, homepage))) {
5767 		LYPermitURL = TRUE;
5768 	    }
5769 
5770 	    /* reset these two variables here before getfile()
5771 	     * so they will be available in partial mode
5772 	     * (was previously implemented in case NORMAL).
5773 	     */
5774 	    BStrCopy0(prev_target, "");		/* Reset for new coming document */
5775 	    LYSetNewline(newdoc.line);	/* set for LYGetNewline() */
5776 
5777 #ifdef USE_PRETTYSRC
5778 	    psrc_first_tag = TRUE;
5779 #endif
5780 #ifdef TEXTFIELDS_MAY_NEED_ACTIVATION
5781 	    textfields_need_activation = textfields_activation_option;
5782 #endif
5783 	    FREE(LYRequestReferer);
5784 	    /*
5785 	     * Don't send Referer if we have to load a document again that we
5786 	     * got from the history stack.  We don't know any more how we
5787 	     * originally got to that page.  Using a Referer based on the
5788 	     * current HTMainText could only be right by coincidence.  - kw
5789 	     * 1999-11-01
5790 	     */
5791 	    if (popped_doc)
5792 		LYNoRefererForThis = TRUE;
5793 
5794 	    if (track_internal_links) {
5795 		if (try_internal) {
5796 		    if (newdoc.address &&
5797 			isLYNXIMGMAP(newdoc.address)) {
5798 			try_internal = FALSE;
5799 		    } else if (curdoc.address &&
5800 			       isLYNXIMGMAP(curdoc.address)) {
5801 			try_internal = FALSE;
5802 		    }
5803 		}
5804 		if (try_internal) {
5805 		    char *hashp = findPoundSelector(newdoc.address);
5806 
5807 		    if (hashp) {
5808 			HTFindPoundSelector(hashp + 1);
5809 		    }
5810 		    getresult = (HTMainText != NULL) ? NORMAL : NOT_FOUND;
5811 		    try_internal = FALSE;	/* done */
5812 		    /* fix up newdoc.address which may have been fragment-only */
5813 		    if (getresult == NORMAL && (!hashp || hashp == newdoc.address)) {
5814 			if (!hashp) {
5815 			    set_address(&newdoc, HTLoadedDocumentURL());
5816 			} else {
5817 			    StrAllocCopy(temp, HTLoadedDocumentURL());
5818 			    StrAllocCat(temp, hashp);	/* append fragment */
5819 			    set_address(&newdoc, temp);
5820 			    FREE(temp);
5821 			}
5822 		    }
5823 		} else {
5824 		    if (newdoc.internal_link && newdoc.address &&
5825 			*newdoc.address == '#' && nhist > 0) {
5826 			char *cp0;
5827 
5828 			if (isLYNXIMGMAP(HDOC(nhist_1).address))
5829 			    cp0 = HDOC(nhist_1).address + LEN_LYNXIMGMAP;
5830 			else
5831 			    cp0 = HDOC(nhist_1).address;
5832 			StrAllocCopy(temp, cp0);
5833 			(void) trimPoundSelector(temp);
5834 			StrAllocCat(temp, newdoc.address);
5835 			free_address(&newdoc);
5836 			newdoc.address = temp;
5837 			temp = NULL;
5838 		    }
5839 		    tmpDocInfo = newdoc;
5840 		    tmpNewline = -1;
5841 		    fill_JUMP_Params(&newdoc.address);
5842 		    getresult = getfile(&newdoc, &tmpNewline);
5843 		    if (!reloading && !popped_doc && (tmpNewline >= 0)) {
5844 			LYSetNewline(tmpNewline);
5845 		    } else {
5846 			newdoc.link = tmpDocInfo.link;
5847 		    }
5848 		}
5849 	    } else {
5850 		tmpDocInfo = newdoc;
5851 		tmpNewline = -1;
5852 		fill_JUMP_Params(&newdoc.address);
5853 		getresult = getfile(&newdoc, &tmpNewline);
5854 		if (!reloading && !popped_doc && (tmpNewline >= 0)) {
5855 		    LYSetNewline(tmpNewline);
5856 		} else {
5857 		    newdoc.link = tmpDocInfo.link;
5858 		}
5859 	    }
5860 
5861 #ifdef INACTIVE_INPUT_STYLE_VH
5862 	    textinput_redrawn = FALSE;	/* for sure */
5863 #endif
5864 
5865 	    switch (getresult) {
5866 
5867 	    case NOT_FOUND:
5868 		/*
5869 		 * OK!  can't find the file, so it must not be around now.  Do
5870 		 * any error logging, if appropriate.
5871 		 */
5872 		LYoverride_no_cache = FALSE;	/* Was TRUE if popped. - FM */
5873 		LYinternal_flag = FALSE;	/* Reset to default. - kw */
5874 		turn_trace_back_on(&trace_mode_flag);
5875 		if (!first_file && !LYCancelledFetch) {
5876 		    /*
5877 		     * Do error mail sending and/or traversal stuff.  Note that
5878 		     * the links[] elements may not be valid at this point, if
5879 		     * we did call HTuncache_current_document!  This should not
5880 		     * have happened for traversal, but for sending error mail
5881 		     * check that HTMainText exists for this reason.  - kw
5882 		     */
5883 		    if (error_logging && nhist > 0 && !popped_doc &&
5884 			!LYUserSpecifiedURL &&
5885 			HTMainText &&
5886 			nlinks > 0 && curdoc.link < nlinks &&
5887 			!isLYNXHIST(NonNull(newdoc.address)) &&
5888 #ifdef USE_CACHEJAR
5889 			!isLYNXCACHE(NonNull(newdoc.address)) &&
5890 #endif
5891 			!isLYNXCOOKIE(NonNull(newdoc.address))) {
5892 			char *mail_owner = NULL;
5893 
5894 			if (owner_address && isMAILTO_URL(owner_address)) {
5895 			    mail_owner = owner_address + LEN_MAILTO_URL;
5896 			}
5897 			/*
5898 			 * Email a bad link message to the owner of the
5899 			 * document, or to ALERTMAIL if defined, but NOT to
5900 			 * lynx-dev (it is rejected in mailmsg).  - FM, kw
5901 			 */
5902 #ifndef ALERTMAIL
5903 			if (mail_owner)
5904 #endif
5905 			    mailmsg(curdoc.link,
5906 				    mail_owner,
5907 				    HDOC(nhist_1).address,
5908 				    HDOC(nhist_1).title);
5909 		    }
5910 		    if (traversal) {
5911 			FILE *ofp;
5912 
5913 			if ((ofp = LYAppendToTxtFile(TRAVERSE_ERRORS)) == NULL) {
5914 			    if ((ofp = LYNewTxtFile(TRAVERSE_ERRORS)) == NULL) {
5915 				perror(NOOPEN_TRAV_ERR_FILE);
5916 				exit_immediately(EXIT_FAILURE);
5917 			    }
5918 			}
5919 			if (nhist > 0) {
5920 			    fprintf(ofp,
5921 				    "%s %s\tin %s\n",
5922 				    popped_doc ?
5923 				    newdoc.address : links[curdoc.link].lname,
5924 				    links[curdoc.link].target,
5925 				    HDOC(nhist_1).address);
5926 			} else {
5927 			    fprintf(ofp,
5928 				    "%s %s\t\n",
5929 				    popped_doc ?
5930 				    newdoc.address : links[curdoc.link].lname,
5931 				    links[curdoc.link].target);
5932 			}
5933 			LYCloseOutput(ofp);
5934 		    }
5935 		}
5936 
5937 		/*
5938 		 * Fall through to do the NULL stuff and reload the old file,
5939 		 * unless the first file wasn't found or has gone missing.
5940 		 */
5941 		if (!nhist) {
5942 		    /*
5943 		     * If nhist = 0 then it must be the first file.
5944 		     */
5945 		    CleanupMainLoop();
5946 		    exit_immediately_with_error_message(NOT_FOUND, first_file);
5947 		    return (EXIT_FAILURE);
5948 		}
5949 		/* FALLTHRU */
5950 
5951 	    case NULLFILE:
5952 		/*
5953 		 * Not supposed to return any file.
5954 		 */
5955 		LYoverride_no_cache = FALSE;	/* Was TRUE if popped. - FM */
5956 		popped_doc = FALSE;	/* Was TRUE if popped. - FM */
5957 		LYinternal_flag = FALSE;	/* Reset to default. - kw */
5958 		turn_trace_back_on(&trace_mode_flag);
5959 		free_address(&newdoc);	/* to pop last doc */
5960 		FREE(newdoc.bookmark);
5961 		LYJumpFileURL = FALSE;
5962 		reloading = FALSE;
5963 		LYPermitURL = FALSE;
5964 		LYCancelledFetch = FALSE;
5965 		ForcePush = FALSE;
5966 		LYforce_HTML_mode = FALSE;
5967 		force_old_UCLYhndl_on_reload = FALSE;
5968 		if (traversal) {
5969 		    crawl_ok = FALSE;
5970 		    if (traversal_link_to_add) {
5971 			/*
5972 			 * It's a binary file, or the fetch attempt failed.
5973 			 * Add it to TRAVERSE_REJECT_FILE so we don't try again
5974 			 * in this run.
5975 			 */
5976 			if (!lookup_reject(traversal_link_to_add)) {
5977 			    add_to_reject_list(traversal_link_to_add);
5978 			}
5979 			FREE(traversal_link_to_add);
5980 		    }
5981 		}
5982 		/*
5983 		 * Make sure the first file was found and has not gone missing.
5984 		 */
5985 		if (!nhist) {
5986 		    /*
5987 		     * If nhist = 0 then it must be the first file.
5988 		     */
5989 		    if (first_file && homepage &&
5990 			!LYSameFilename(homepage, startfile)) {
5991 			/*
5992 			 * Couldn't return to the first file but there is a
5993 			 * homepage we can use instead.  Useful for when the
5994 			 * first URL causes a program to be invoked.  - GL
5995 			 *
5996 			 * But first make sure homepage is different from
5997 			 * startfile (above), then make it the same (below) so
5998 			 * we don't enter an infinite getfile() loop on on
5999 			 * failures to find the files.  - FM
6000 			 */
6001 			set_address(&newdoc, homepage);
6002 			LYFreePostData(&newdoc);
6003 			FREE(newdoc.bookmark);
6004 			StrAllocCopy(startfile, homepage);
6005 			newdoc.isHEAD = FALSE;
6006 			newdoc.safe = FALSE;
6007 			newdoc.internal_link = FALSE;
6008 			goto try_again;
6009 		    } else {
6010 			CleanupMainLoop();
6011 			exit_immediately_with_error_message(NULLFILE, first_file);
6012 			return (EXIT_FAILURE);
6013 		    }
6014 		}
6015 
6016 		/*
6017 		 * If we're going to pop from history because getfile didn't
6018 		 * succeed, reset LYforce_no_cache first.  This would have been
6019 		 * done in HTAccess.c if the request got that far, but the URL
6020 		 * may have been handled or rejected in getfile without taking
6021 		 * care of that.  - kw
6022 		 */
6023 		LYforce_no_cache = FALSE;
6024 		/*
6025 		 * Retrieval of a newdoc just failed, and just going to
6026 		 * try_again would pop the next doc from history and try to get
6027 		 * it without further questions.  This may not be the right
6028 		 * thing to do if we have POST data, so fake a PREV_DOC key if
6029 		 * it seems that some prompting should be done.  This doesn't
6030 		 * affect the traversal logic, since with traversal POST data
6031 		 * can never occur.  - kw
6032 		 */
6033 		if (HDOC(nhist - 1).post_data &&
6034 		    !HDOC(nhist - 1).safe) {
6035 		    if (HText_POSTReplyLoaded((DocInfo *) &history[(nhist_1)])) {
6036 			override_LYresubmit_posts = TRUE;
6037 			goto try_again;
6038 		    }
6039 		    /* Set newdoc fields, just in case the PREV_DOC gets
6040 		     * cancelled.  - kw
6041 		     */
6042 		    if (!curdoc.address) {
6043 			set_address(&newdoc, HTLoadedDocumentURL());
6044 			StrAllocCopy(newdoc.title, HTLoadedDocumentTitle());
6045 			if (HTMainAnchor
6046 			    && HTMainAnchor->post_data) {
6047 			    BStrCopy(newdoc.post_data,
6048 				     HTMainAnchor->post_data);
6049 			    StrAllocCopy(newdoc.post_content_type,
6050 					 HTMainAnchor->post_content_type);
6051 			} else {
6052 			    BStrFree(newdoc.post_data);
6053 			}
6054 			newdoc.isHEAD = HTLoadedDocumentIsHEAD();
6055 			newdoc.safe = HTLoadedDocumentIsSafe();
6056 			newdoc.internal_link = FALSE;
6057 		    } else {
6058 			copy_address(&newdoc, &curdoc);
6059 			StrAllocCopy(newdoc.title, curdoc.title);
6060 			BStrCopy(newdoc.post_data, curdoc.post_data);
6061 			StrAllocCopy(newdoc.post_content_type,
6062 				     curdoc.post_content_type);
6063 			newdoc.isHEAD = curdoc.isHEAD;
6064 			newdoc.safe = curdoc.safe;
6065 			newdoc.internal_link = curdoc.internal_link;
6066 			newdoc.line = curdoc.line;
6067 			newdoc.link = curdoc.link;
6068 		    }
6069 		    cmd = LYK_PREV_DOC;
6070 		    goto new_cmd;
6071 		}
6072 		override_LYresubmit_posts = TRUE;
6073 		goto try_again;
6074 
6075 	    case NORMAL:
6076 		/*
6077 		 * Marvelously, we got the document!
6078 		 */
6079 		LYoverride_no_cache = FALSE;	/* Was TRUE if popped. - FM */
6080 		LYinternal_flag = FALSE;	/* Reset to default. - kw */
6081 		turn_trace_back_on(&trace_mode_flag);
6082 
6083 		/*
6084 		 * If it's the first file and we're interactive, check whether
6085 		 * it's a bookmark file which was not accessed via the -book
6086 		 * switch.  - FM
6087 		 */
6088 		if (((first_file == TRUE) &&
6089 		     (dump_output_immediately == FALSE) &&
6090 		     isEmpty(newdoc.bookmark)) &&
6091 		    ((LYisLocalFile(newdoc.address) == TRUE) &&
6092 		     !(strcmp(NonNull(HText_getTitle()),
6093 			      BOOKMARK_TITLE))) &&
6094 		    (temp = HTParse(newdoc.address, "",
6095 				    PARSE_PATH + PARSE_PUNCTUATION)) != NULL) {
6096 		    const char *name = wwwName(Home_Dir());
6097 
6098 		    len = (unsigned) strlen(name);
6099 #ifdef VMS
6100 		    if (!strncasecomp(temp, name, len) &&
6101 			strlen(temp) > len)
6102 #else
6103 		    if (!StrNCmp(temp, name, len) &&
6104 			strlen(temp) > len)
6105 #endif /* VMS */
6106 		    {
6107 			/*
6108 			 * We're interactive and this might be a bookmark file
6109 			 * entered as a startfile rather than invoked via
6110 			 * -book.  Check if it's in our bookmark file list, and
6111 			 * if so, reload if with the relevant bookmark elements
6112 			 * set.  - FM
6113 			 */
6114 			cp = NULL;
6115 			if (temp[len] == '/') {
6116 			    if (strchr(&temp[(len + 1)], '/')) {
6117 				HTSprintf0(&cp, ".%s", &temp[len]);
6118 			    } else {
6119 				StrAllocCopy(cp, &temp[(len + 1)]);
6120 			    }
6121 			} else {
6122 			    StrAllocCopy(cp, &temp[len]);
6123 			}
6124 			for (i = 0; i <= MBM_V_MAXFILES; i++) {
6125 			    if (MBM_A_subbookmark[i] &&
6126 				LYSameFilename(cp, MBM_A_subbookmark[i])) {
6127 				StrAllocCopy(BookmarkPage,
6128 					     MBM_A_subbookmark[i]);
6129 				break;
6130 			    }
6131 			}
6132 			FREE(cp);
6133 			if (i <= MBM_V_MAXFILES) {
6134 			    FREE(temp);
6135 			    if (LYValidate) {
6136 				HTAlert(BOOKMARKS_DISABLED);
6137 				CleanupMainLoop();
6138 				return (EXIT_FAILURE);
6139 			    }
6140 			    if ((temp = HTParse(newdoc.address, "",
6141 						PARSE_ACCESS + PARSE_HOST + PARSE_PUNCTUATION))) {
6142 				set_address(&newdoc, temp);
6143 				HTuncache_current_document();
6144 				free_address(&curdoc);
6145 				StrAllocCat(newdoc.address,
6146 					    wwwName(Home_Dir()));
6147 				StrAllocCat(newdoc.address, "/");
6148 				StrAllocCat(newdoc.address,
6149 					    (StrNCmp(BookmarkPage, "./", 2) ?
6150 					     BookmarkPage :
6151 					     (BookmarkPage + 2)));
6152 				StrAllocCopy(newdoc.title, BOOKMARK_TITLE);
6153 				StrAllocCopy(newdoc.bookmark, BookmarkPage);
6154 #ifdef USE_COLOR_STYLE
6155 				if (curdoc.style)
6156 				    StrAllocCopy(newdoc.style, curdoc.style);
6157 #endif
6158 				StrAllocCopy(startrealm, newdoc.address);
6159 				LYFreePostData(&newdoc);
6160 				newdoc.isHEAD = FALSE;
6161 				newdoc.safe = FALSE;
6162 				FREE(temp);
6163 				if (!strcmp(homepage, startfile))
6164 				    StrAllocCopy(homepage, newdoc.address);
6165 				StrAllocCopy(startfile, newdoc.address);
6166 				CTRACE((tfp, "Reloading as bookmarks=%s\n",
6167 					newdoc.address));
6168 				goto try_again;
6169 			    }
6170 			}
6171 		    }
6172 		    cp = NULL;
6173 		}
6174 		FREE(temp);
6175 
6176 		if (traversal) {
6177 		    /*
6178 		     * During traversal build up lists of all links traversed.
6179 		     * Traversal mode is a special feature for traversing http
6180 		     * links in the web.
6181 		     */
6182 		    if (traversal_link_to_add) {
6183 			/*
6184 			 * Add the address we sought to TRAVERSE_FILE.
6185 			 */
6186 			if (!lookup_link(traversal_link_to_add))
6187 			    add_to_table(traversal_link_to_add);
6188 			FREE(traversal_link_to_add);
6189 		    }
6190 		    if (curdoc.address && curdoc.title &&
6191 			!isLYNXIMGMAP(curdoc.address))
6192 			/*
6193 			 * Add the address we got to TRAVERSE_FOUND_FILE.
6194 			 */
6195 			add_to_traverse_list(curdoc.address, curdoc.title);
6196 		}
6197 
6198 		/*
6199 		 * If this was a LYNXDOWNLOAD, we still have curdoc, not a
6200 		 * newdoc, so reset the address, title and positioning
6201 		 * elements.  - FM
6202 		 */
6203 		if (newdoc.address && curdoc.address &&
6204 		    isLYNXDOWNLOAD(newdoc.address)) {
6205 		    copy_address(&newdoc, &curdoc);
6206 		    StrAllocCopy(newdoc.title, NonNull(curdoc.title));
6207 		    StrAllocCopy(newdoc.bookmark, curdoc.bookmark);
6208 		    newdoc.line = curdoc.line;
6209 		    newdoc.link = curdoc.link;
6210 		    newdoc.internal_link = FALSE;	/* can't be true. - kw */
6211 		}
6212 
6213 		/*
6214 		 * Set Newline to the saved line.  It contains the line the
6215 		 * user was on if s/he has been in the file before, or it is 1
6216 		 * if this is a new file.
6217 		 *
6218 		 * We already set Newline before getfile() and probably update
6219 		 * it explicitly if popping from the history stack via LYpop()
6220 		 * or LYpop_num() within getfile() cycle.
6221 		 *
6222 		 * In partial mode, Newline was probably updated in
6223 		 * LYMainLoop_pageDisplay() if user scrolled the document while
6224 		 * loading.  Incremental loading stage already closed in
6225 		 * HT*Copy().
6226 		 */
6227 #ifdef DISP_PARTIAL
6228 		/* Newline = newdoc.line; */
6229 		display_partial = FALSE;	/* for sure, LYNXfoo:/ may be a problem */
6230 #else
6231 		/* Should not be needed either if we remove "DISP_PARTIAL" from
6232 		 * LYHistory.c, but lets leave it as an important comment for
6233 		 * now.
6234 		 */
6235 		/* Newline = newdoc.line; */
6236 #endif
6237 
6238 		/*
6239 		 * If we are going to a target line or the first page of a
6240 		 * popped document, override any www_search line result.
6241 		 */
6242 		if (LYGetNewline() > 1 || popped_doc == TRUE)
6243 		    www_search_result = -1;
6244 
6245 		/*
6246 		 * Make sure curdoc.line will not be equal to Newline, so we
6247 		 * get a redraw.
6248 		 */
6249 		curdoc.line = -1;
6250 		break;
6251 	    }			/* end switch */
6252 
6253 	    if (TRACE) {
6254 		if (!LYTraceLogFP || trace_mode_flag) {
6255 		    LYSleepAlert();	/* allow me to look at the results */
6256 		}
6257 	    }
6258 
6259 	    /*
6260 	     * Set the files the same.
6261 	     */
6262 	    copy_address(&curdoc, &newdoc);
6263 	    BStrCopy(curdoc.post_data, newdoc.post_data);
6264 	    StrAllocCopy(curdoc.post_content_type, newdoc.post_content_type);
6265 	    StrAllocCopy(curdoc.bookmark, newdoc.bookmark);
6266 #ifdef USE_COLOR_STYLE
6267 	    StrAllocCopy(curdoc.style, HText_getStyle());
6268 	    if (curdoc.style != NULL)
6269 		style_readFromFile(curdoc.style);
6270 #endif
6271 	    curdoc.isHEAD = newdoc.isHEAD;
6272 	    curdoc.internal_link = newdoc.internal_link;
6273 
6274 	    /*
6275 	     * Set the remaining document elements and add to the visited links
6276 	     * list.  - FM
6277 	     */
6278 	    if (ownerS_address != NULL) {
6279 #ifndef USE_PRETTYSRC
6280 		if (HTOutputFormat == WWW_SOURCE && !HText_getOwner())
6281 #else
6282 		if ((LYpsrc ? psrc_view : HTOutputFormat == WWW_SOURCE)
6283 		    && !HText_getOwner())
6284 #endif
6285 		    HText_setMainTextOwner(ownerS_address);
6286 		FREE(ownerS_address);
6287 	    }
6288 	    if (HText_getTitle()) {
6289 		StrAllocCopy(curdoc.title, HText_getTitle());
6290 	    } else if (!dump_output_immediately) {
6291 		StrAllocCopy(curdoc.title, newdoc.title);
6292 	    }
6293 	    StrAllocCopy(owner_address, HText_getOwner());
6294 	    curdoc.safe = HTLoadedDocumentIsSafe();
6295 	    if (!dump_output_immediately) {
6296 		LYAddVisitedLink(&curdoc);
6297 	    }
6298 
6299 	    /*
6300 	     * Reset WWW present mode so that if we were getting the source, we
6301 	     * get rendered HTML from now on.
6302 	     */
6303 	    HTOutputFormat = WWW_PRESENT;
6304 #ifdef USE_PRETTYSRC
6305 	    psrc_view = FALSE;
6306 #endif
6307 
6308 	    HTMLSetCharacterHandling(current_char_set);		/* restore, for sure? */
6309 
6310 	    /*
6311 	     * Reset all of the other relevant flags.  - FM
6312 	     */
6313 	    LYUserSpecifiedURL = FALSE;		/* only set for gotos and jumps */
6314 	    LYJumpFileURL = FALSE;	/* only set for jumps */
6315 	    LYNoRefererForThis = FALSE;		/* always reset on return here */
6316 	    reloading = FALSE;	/* set for RELOAD and NOCACHE keys */
6317 	    HEAD_request = FALSE;	/* only set for HEAD requests */
6318 	    LYPermitURL = FALSE;	/* only for LYValidate or check_realm */
6319 	    ForcePush = FALSE;	/* only set for some PRINT requests. */
6320 	    LYforce_HTML_mode = FALSE;
6321 	    force_old_UCLYhndl_on_reload = FALSE;
6322 	    popped_doc = FALSE;
6323 	    pending_form_c = -1;
6324 
6325 	}
6326 	/* end if (LYforce_no_cache || force_load || are_different(...)) */
6327 	if (dump_output_immediately) {
6328 	    if (crawl) {
6329 		print_crawl_to_fd(stdout, curdoc.address, curdoc.title);
6330 	    } else if (!dump_links_only) {
6331 		print_wwwfile_to_fd(stdout, FALSE, FALSE);
6332 	    }
6333 	    CleanupMainLoop();
6334 	    return ((dump_server_status >= 400) ? EXIT_FAILURE : EXIT_SUCCESS);
6335 	}
6336 
6337 	/*
6338 	 * If the recent_sizechange variable is set to TRUE then the window
6339 	 * size changed recently.
6340 	 */
6341 	if (recent_sizechange) {
6342 	    /*
6343 	     * First we need to make sure the display library - curses, slang,
6344 	     * whatever - gets notified about the change, and gets a chance to
6345 	     * update external structures appropriately.  Hopefully the
6346 	     * stop_curses()/start_curses() sequence achieves this, at least if
6347 	     * the display library has a way to get the new screen size from
6348 	     * the OS.
6349 	     *
6350 	     * However, at least for ncurses, the update of the internal
6351 	     * structures will come still too late - the changed screen size is
6352 	     * detected in doupdate(), which would only be called (indirectly
6353 	     * through the HText_pageDisplay below) after the WINDOW structures
6354 	     * are already filled based on the old size.  So we notify the
6355 	     * ncurses library directly here.  - kw
6356 	     */
6357 #if defined(NCURSES) && defined(HAVE_RESIZETERM) && defined(HAVE_WRESIZE)
6358 	    resizeterm(LYlines, LYcols);
6359 	    wresize(LYwin, LYlines, LYcols);
6360 #else
6361 #if 0				/* defined(PDCURSES) && defined(HAVE_XCURSES) */
6362 	    resize_term(LYlines, LYcols);
6363 	    if (LYwin != 0)
6364 		LYwin = resize_window(LYwin, LYlines, LYcols);
6365 	    refresh();
6366 #else
6367 	    stop_curses();
6368 	    start_curses();
6369 	    LYclear();
6370 #endif
6371 #endif
6372 	    refresh_screen = TRUE;	/* to force a redraw */
6373 	    if (HTMainText)	/* to REALLY force it... - kw */
6374 		HText_setStale(HTMainText);
6375 	    recent_sizechange = FALSE;
6376 
6377 	    LYSetDisplayLines();
6378 	}
6379 
6380 	if (www_search_result != -1) {
6381 	    /*
6382 	     * This was a WWW search, set the line to the result of the search.
6383 	     */
6384 	    LYSetNewline(www_search_result);
6385 	    www_search_result = -1;	/* reset */
6386 	}
6387 
6388 	if (first_file == TRUE) {
6389 	    /*
6390 	     * We can never again have the first file.
6391 	     */
6392 	    first_file = FALSE;
6393 
6394 	    /*
6395 	     * Set the startrealm, and deal as best we can with preserving
6396 	     * forced HTML mode for a local startfile.  - FM
6397 	     */
6398 	    temp = HTParse(curdoc.address, "",
6399 			   PARSE_ACCESS + PARSE_HOST + PARSE_PUNCTUATION);
6400 	    if (isEmpty(temp)) {
6401 		StrAllocCopy(startrealm, NO_NOTHING);
6402 	    } else {
6403 		StrAllocCopy(startrealm, temp);
6404 		FREE(temp);
6405 		if (!(temp = HTParse(curdoc.address, "",
6406 				     PARSE_PATH + PARSE_PUNCTUATION))) {
6407 		    LYAddHtmlSep(&startrealm);
6408 		} else {
6409 		    if (forced_HTML_mode &&
6410 			!dump_output_immediately &&
6411 			!curdoc.bookmark &&
6412 			isFILE_URL(curdoc.address) &&
6413 			strlen(temp) > 1) {
6414 			/*
6415 			 * We forced HTML for a local startfile which is not a
6416 			 * bookmark file and has a path of at least two
6417 			 * letters.  If it doesn't have a suffix mapped to
6418 			 * text/html, we'll set the entire path (including the
6419 			 * lead slash) as a "suffix" mapped to text/html to
6420 			 * ensure it is always treated as an HTML source file.
6421 			 * We are counting on a tail match to this full path
6422 			 * for some other URL fetched during the session having
6423 			 * too low a probability to worry about, but it could
6424 			 * happen.  - FM
6425 			 */
6426 			HTAtom *encoding;
6427 
6428 			if (HTFileFormat(temp, &encoding, NULL) != WWW_HTML) {
6429 			    HTSetSuffix(temp, "text/html", "8bit", 1.0);
6430 			}
6431 		    }
6432 		    if ((cp = strrchr(temp, '/')) != NULL) {
6433 			*(cp + 1) = '\0';
6434 			StrAllocCat(startrealm, temp);
6435 		    }
6436 		}
6437 	    }
6438 	    FREE(temp);
6439 	    CTRACE((tfp, "Starting realm is '%s'\n\n", startrealm));
6440 	    if (traversal) {
6441 		/*
6442 		 * Set up the crawl output stuff.
6443 		 */
6444 		if (curdoc.address && !lookup_link(curdoc.address)) {
6445 		    if (!isLYNXIMGMAP(curdoc.address))
6446 			crawl_ok = TRUE;
6447 		    add_to_table(curdoc.address);
6448 		}
6449 		/*
6450 		 * Set up the traversal_host comparison string.
6451 		 */
6452 		if (StrNCmp((curdoc.address ? curdoc.address : "NULL"),
6453 			    "http", 4)) {
6454 		    StrAllocCopy(traversal_host, NO_NOTHING);
6455 		} else if (check_realm) {
6456 		    StrAllocCopy(traversal_host, startrealm);
6457 		} else {
6458 		    temp = HTParse(curdoc.address, "",
6459 				   PARSE_ACCESS + PARSE_HOST + PARSE_PUNCTUATION);
6460 		    if (isEmpty(temp)) {
6461 			StrAllocCopy(traversal_host, NO_NOTHING);
6462 		    } else {
6463 			StrAllocCopy(traversal_host, temp);
6464 			LYAddHtmlSep(&traversal_host);
6465 		    }
6466 		    FREE(temp);
6467 		}
6468 		CTRACE((tfp, "Traversal host is '%s'\n\n", traversal_host));
6469 	    }
6470 	    if (startfile) {
6471 		/*
6472 		 * If homepage was not equated to startfile, make the homepage
6473 		 * URL the first goto entry.  - FM
6474 		 */
6475 		if (homepage && strcmp(startfile, homepage))
6476 		    HTAddGotoURL(homepage);
6477 		/*
6478 		 * If we are not starting up with startfile (e.g., had -book),
6479 		 * or if we are using the startfile and it has no POST content,
6480 		 * make the startfile URL a goto entry.  - FM
6481 		 */
6482 		if (strcmp(startfile, newdoc.address) ||
6483 		    newdoc.post_data == NULL)
6484 		    HTAddGotoURL(startfile);
6485 	    }
6486 	    if (TRACE) {
6487 		refresh_screen = TRUE;
6488 		if (!LYTraceLogFP || trace_mode_flag) {
6489 		    LYSleepAlert();
6490 		}
6491 	    }
6492 	}
6493 #ifdef USE_SOURCE_CACHE
6494 	/*
6495 	 * If the parse settings have changed since this HText was
6496 	 * generated, we need to reparse and redraw it.  -dsb
6497 	 *
6498 	 * Should be configured to avoid shock for experienced lynx users.
6499 	 * Currently enabled for cached sources only.
6500 	 */
6501 	if (HTdocument_settings_changed()) {
6502 	    if (HTcan_reparse_document()) {
6503 		HTInfoMsg(gettext("Reparsing document under current settings..."));
6504 		reparse_document();
6505 	    } else {
6506 		/*
6507 		 * Urk.  I have no idea how to recover from a failure here.
6508 		 * At a guess, I'll try reloading.  -dsb
6509 		 */
6510 		/*  currently disabled ***
6511 		   HTUserMsg(gettext("Reparsing document under current settings..."));
6512 		   cmd = LYK_RELOAD;
6513 		   goto new_cmd;
6514 		 */
6515 	    }
6516 	}
6517 
6518 	if (from_source_cache) {
6519 	    from_source_cache = FALSE;	/* reset */
6520 	    curdoc.line = -1;	/* so curdoc.line != Newline, see below */
6521 	}
6522 #endif
6523 
6524 	/*
6525 	 * If the curdoc.line is different than Newline then there must have
6526 	 * been a change since last update.  Run HText_pageDisplay() to create
6527 	 * a fresh screen of text output.
6528 	 *
6529 	 * If we got new HTMainText go this way.  All display_partial calls
6530 	 * ends here for final redraw.
6531 	 */
6532 	if (curdoc.line != LYGetNewline()) {
6533 #ifdef INACTIVE_INPUT_STYLE_VH
6534 	    textinput_redrawn = FALSE;
6535 #endif
6536 
6537 	    refresh_screen = FALSE;
6538 
6539 	    HText_pageDisplay(LYGetNewline(), prev_target->str);
6540 
6541 #ifdef DIRED_SUPPORT
6542 	    if (lynx_edit_mode && nlinks > 0 && !HTList_isEmpty(tagged))
6543 		showtags(tagged);
6544 #endif /* DIRED_SUPPORT */
6545 
6546 	    /*
6547 	     * Check if there is more info below this page.
6548 	     */
6549 	    more_text = HText_canScrollDown();
6550 
6551 	    if (newdoc.link < 0)
6552 		goto_line(LYGetNewline());
6553 	    LYSetNewline(HText_getTopOfScreen() + 1);
6554 	    curdoc.line = LYGetNewline();
6555 
6556 	    if (curdoc.title == NULL) {
6557 		/*
6558 		 * If we don't yet have a title, try to get it, or set to that
6559 		 * for newdoc.title.  - FM
6560 		 */
6561 		if (HText_getTitle()) {
6562 		    StrAllocCopy(curdoc.title, HText_getTitle());
6563 		} else {
6564 		    StrAllocCopy(curdoc.title, newdoc.title);
6565 		}
6566 	    }
6567 
6568 	    /*
6569 	     * If the request is to highlight a link which is counted from the
6570 	     * start of document, correct the link number:
6571 	     */
6572 	    if (newdoc_link_is_absolute) {
6573 		newdoc_link_is_absolute = FALSE;
6574 		if (curdoc.line > 1)
6575 		    newdoc.link -= HText_LinksInLines(HTMainText, 1,
6576 						      curdoc.line - 1);
6577 	    }
6578 
6579 	    if (arrowup) {
6580 		/*
6581 		 * arrowup is set if we just came up from a page below.
6582 		 */
6583 		curdoc.link = nlinks - 1;
6584 		arrowup = FALSE;
6585 	    } else {
6586 		curdoc.link = newdoc.link;
6587 		if (curdoc.link >= nlinks) {
6588 		    curdoc.link = nlinks - 1;
6589 		} else if (curdoc.link < 0 && nlinks > 0) {
6590 		    /*
6591 		     * We may have popped a doc (possibly in local_dired) which
6592 		     * didn't have any links when it was pushed, but does have
6593 		     * links now (e.g., a file was created).  Code below
6594 		     * assumes that curdoc.link is valid and that
6595 		     * (curdoc.link==-1) only occurs if (nlinks==0) is true.  -
6596 		     * KW
6597 		     */
6598 		    curdoc.link = 0;
6599 		}
6600 	    }
6601 
6602 	    show_help = FALSE;	/* reset */
6603 	    newdoc.line = 1;
6604 	    newdoc.link = 0;
6605 	    curdoc.line = LYGetNewline();	/* set */
6606 	} else if (newdoc.link < 0) {
6607 	    newdoc.link = 0;	/* ...just in case getfile set this */
6608 	}
6609 
6610 	/*
6611 	 * Refresh the screen if necessary.
6612 	 */
6613 	if (refresh_screen) {
6614 #if defined(FANCY_CURSES) || defined (USE_SLANG)
6615 	    if (enable_scrollback) {
6616 		LYclear();
6617 	    } else {
6618 		LYerase();
6619 	    }
6620 #else
6621 	    LYclear();
6622 #endif /* FANCY_CURSES || USE_SLANG */
6623 	    HText_pageDisplay(LYGetNewline(), prev_target->str);
6624 
6625 #ifdef DIRED_SUPPORT
6626 	    if (lynx_edit_mode && nlinks > 0 && !HTList_isEmpty(tagged))
6627 		showtags(tagged);
6628 #endif /* DIRED_SUPPORT */
6629 
6630 	    /*
6631 	     * Check if there is more info below this page.
6632 	     */
6633 	    more_text = HText_canScrollDown();
6634 
6635 	    /*
6636 	     * Adjust curdoc.link as above; nlinks may have changed, if the
6637 	     * refresh_screen flag was set as a result of a size change.  Code
6638 	     * below assumes that curdoc.link is valid and that
6639 	     * (curdoc.link==-1) only occurs if (nlinks==0) is true.  - kw
6640 	     */
6641 	    if (curdoc.link >= nlinks) {
6642 		curdoc.link = nlinks - 1;
6643 	    } else if (curdoc.link < 0 && nlinks > 0) {
6644 		curdoc.link = 0;
6645 	    }
6646 
6647 	    if (user_mode == NOVICE_MODE)
6648 		noviceline(more_text);	/* print help message */
6649 	    refresh_screen = FALSE;
6650 
6651 	}
6652 
6653 	curlink_is_editable = (BOOLEAN)
6654 	    (nlinks > 0 &&
6655 	     LinkIsTextLike(curdoc.link));
6656 
6657 	use_last_tfpos = (BOOLEAN)
6658 	    (curlink_is_editable &&
6659 	     (real_cmd == LYK_LPOS_PREV_LINK ||
6660 	      real_cmd == LYK_LPOS_NEXT_LINK));
6661 
6662 #ifdef TEXTFIELDS_MAY_NEED_ACTIVATION
6663 	if (!textfields_need_activation)
6664 	    textinput_activated = TRUE;
6665 #endif
6666 
6667 #if defined(WIN_EX)		/* 1997/10/08 (Wed) 14:52:06 */
6668 	if (nlinks > 0) {
6669 	    char *p = "LYNX (unknown link type)";
6670 
6671 	    /* Show the URL & kanji code . */
6672 	    if (strlen(links[curdoc.link].lname) == 0) {
6673 
6674 		if (links[curdoc.link].type == WWW_FORM_LINK_TYPE) {
6675 
6676 		    switch (links[curdoc.link].l_form->type) {
6677 		    case F_TEXT_SUBMIT_TYPE:
6678 		    case F_SUBMIT_TYPE:
6679 		    case F_IMAGE_SUBMIT_TYPE:
6680 			p = "[SUBMIT]";
6681 			break;
6682 		    case F_PASSWORD_TYPE:
6683 			p = "Password";
6684 			break;
6685 		    case F_OPTION_LIST_TYPE:
6686 			p = "Option list";
6687 			break;
6688 		    case F_CHECKBOX_TYPE:
6689 			p = "Check box";
6690 			break;
6691 		    case F_RADIO_TYPE:
6692 			p = "[Radio]";
6693 			break;
6694 		    case F_RESET_TYPE:
6695 			p = "[Reset]";
6696 			break;
6697 		    case F_TEXT_TYPE:
6698 			p = "Text input";
6699 			break;
6700 		    case F_TEXTAREA_TYPE:
6701 			p = "Text input lines";
6702 			break;
6703 		    default:
6704 			break;
6705 		    }
6706 		    set_ws_title(p);
6707 		}
6708 	    } else {
6709 		if (user_mode == ADVANCED_MODE) {
6710 		    p = curdoc.title;
6711 		} else {
6712 		    p = links[curdoc.link].lname;
6713 		}
6714 
6715 		if (strlen(p) < (sizeof(sjis_buff) / 10)) {
6716 		    strcpy(temp_buff, p);
6717 		    if (strchr(temp_buff, '%')) {
6718 			HTUnEscape(temp_buff);
6719 		    }
6720 		    str_sjis(sjis_buff, temp_buff);
6721 		    set_ws_title(LYElideString(sjis_buff, 10));
6722 		}
6723 	    }
6724 	} else {
6725 	    if (strlen(curdoc.address) < sizeof(temp_buff) - 1) {
6726 		if (user_mode == ADVANCED_MODE) {
6727 		    str_sjis(temp_buff, curdoc.title);
6728 		} else {
6729 		    strcpy(temp_buff, curdoc.address);
6730 		}
6731 		set_ws_title(HTUnEscape(temp_buff));
6732 	    }
6733 	}
6734 #endif /* WIN_EX */
6735 
6736 	/*
6737 	 * Report unread or new mail, if appropriate.
6738 	 */
6739 	if (check_mail && !no_mail)
6740 	    LYCheckMail();
6741 
6742 	/*
6743 	 * If help is not on the screen, then put a message on the screen to
6744 	 * tell the user other misc info.
6745 	 */
6746 	if (!show_help) {
6747 	    show_main_statusline(links[curdoc.link],
6748 				 ((curlink_is_editable &&
6749 				   textinput_activated)
6750 				  ? FOR_INPUT
6751 				  : FOR_PANEL));
6752 	} else {
6753 	    show_help = FALSE;
6754 	}
6755 
6756 	if (nlinks > 0) {
6757 	    /*
6758 	     * Highlight current link, unless it is an active text input field.
6759 	     */
6760 	    if (!curlink_is_editable) {
6761 		LYhighlight(TRUE, curdoc.link, prev_target->str);
6762 #ifndef INACTIVE_INPUT_STYLE_VH
6763 	    } else if (!textinput_activated) {
6764 		LYhighlight(TRUE, curdoc.link, prev_target->str);
6765 #endif
6766 	    }
6767 	}
6768 
6769 	if (traversal) {
6770 	    /*
6771 	     * Don't go interactively into forms, or accept keystrokes from the
6772 	     * user
6773 	     */
6774 	    if (crawl && crawl_ok) {
6775 		crawl_ok = FALSE;
6776 #ifdef FNAMES_8_3
6777 		sprintf(cfile, "lnk%05d.dat", crawl_count);
6778 #else
6779 		sprintf(cfile, "lnk%08d.dat", crawl_count);
6780 #endif /* FNAMES_8_3 */
6781 		crawl_count = crawl_count + 1;
6782 		if ((cfp = LYNewTxtFile(cfile)) != NULL) {
6783 		    print_crawl_to_fd(cfp, curdoc.address, curdoc.title);
6784 		    LYCloseOutput(cfp);
6785 		} else {
6786 #ifdef UNIX
6787 		    FILE *fp = (dump_output_immediately
6788 				? stderr
6789 				: stdout);
6790 
6791 #else
6792 		    FILE *fp = stdout;
6793 #endif
6794 		    if (!dump_output_immediately)
6795 			cleanup();
6796 		    fprintf(fp,
6797 			    gettext("Fatal error - could not open output file %s\n"),
6798 			    cfile);
6799 		    CleanupMainLoop();
6800 		    if (!dump_output_immediately) {
6801 			exit_immediately(EXIT_FAILURE);
6802 		    }
6803 		    return (EXIT_FAILURE);
6804 		}
6805 	    }
6806 	} else {
6807 	    /*
6808 	     * Normal, non-traversal handling.
6809 	     */
6810 	    if (curlink_is_editable &&
6811 		(textinput_activated || pending_form_c != -1)) {
6812 		if (pending_form_c != -1) {
6813 		    real_c = pending_form_c;
6814 		    pending_form_c = -1;
6815 		} else {
6816 		    /*
6817 		     * Replace novice lines if in NOVICE_MODE.
6818 		     */
6819 		    if (user_mode == NOVICE_MODE) {
6820 			form_noviceline(FormIsReadonly(links[curdoc.link].l_form));
6821 		    }
6822 		    real_c = change_form_link(curdoc.link,
6823 					      &newdoc, &refresh_screen,
6824 					      use_last_tfpos, FALSE);
6825 		}
6826 #ifdef TEXTFIELDS_MAY_NEED_ACTIVATION
6827 		if (textfields_need_activation)
6828 		    textinput_activated = FALSE;
6829 #ifdef INACTIVE_INPUT_STYLE_VH
6830 		textinput_redrawn = FALSE;
6831 #endif
6832 #endif
6833 
6834 		c = (real_c == LKC_DONE) ? DO_NOTHING : LKC_TO_C(real_c);
6835 		if (c != DO_NOTHING &&
6836 		    peek_mouse_link() != -1 && peek_mouse_link() != -2)
6837 		    old_c = 0;
6838 		if (peek_mouse_link() >= 0 &&
6839 		    LKC_TO_LAC(keymap, real_c) != LYK_CHANGE_LINK) {
6840 		    do_change_link();
6841 		    if ((c == '\n' || c == '\r') &&
6842 			LinkIsTextLike(curdoc.link) &&
6843 			!textfields_need_activation) {
6844 			c = DO_NOTHING;
6845 		    }
6846 #ifdef TEXTFIELDS_MAY_NEED_ACTIVATION
6847 		} else if (LinkIsTextarea(curdoc.link)
6848 			   && textfields_need_activation
6849 			   && !FormIsReadonly(links[curdoc.link].l_form)
6850 			   && peek_mouse_link() < 0 &&
6851 			   (((LKC_TO_LAC(keymap, real_c) == LYK_NEXT_LINK ||
6852 #ifdef TEXTAREA_AUTOGROW
6853 			      LKC_TO_LAC(keymap, real_c) == LYK_ACTIVATE ||
6854 #endif
6855 			      LKC_TO_LAC(keymap, real_c) == LYK_LPOS_NEXT_LINK ||
6856 			      LKC_TO_LAC(keymap, real_c) == LYK_DOWN_LINK) &&
6857 			     ((curdoc.link < nlinks - 1 &&
6858 			       LinkIsTextarea(curdoc.link + 1)
6859 			       && (links[curdoc.link].l_form->number ==
6860 				   links[curdoc.link + 1].l_form->number)
6861 			       && strcmp(links[curdoc.link].l_form->name,
6862 					 links[curdoc.link + 1].l_form->name)
6863 			       == 0) ||
6864 			      (curdoc.link == nlinks - 1 && more_text &&
6865 			       HText_TAHasMoreLines(curdoc.link, 1)))) ||
6866 			    ((LKC_TO_LAC(keymap, real_c) == LYK_PREV_LINK ||
6867 			      LKC_TO_LAC(keymap, real_c) == LYK_LPOS_PREV_LINK ||
6868 			      LKC_TO_LAC(keymap, real_c) == LYK_UP_LINK) &&
6869 			     ((curdoc.link > 0 &&
6870 			       LinkIsTextarea(curdoc.link - 1)
6871 			       && (links[curdoc.link].l_form->number ==
6872 				   links[curdoc.link - 1].l_form->number) &&
6873 			       strcmp(links[curdoc.link].l_form->name,
6874 				      links[curdoc.link - 1].l_form->name) == 0)
6875 			      || (curdoc.link == 0 && curdoc.line > 1 &&
6876 				  HText_TAHasMoreLines(curdoc.link, -1)))))) {
6877 		    textinput_activated = TRUE;
6878 #ifdef TEXTAREA_AUTOGROW
6879 		    if ((c == '\n' || c == '\r') &&
6880 			LKC_TO_LAC(keymap, real_c) == LYK_ACTIVATE)
6881 			c = LAC_TO_LKC0(LYK_NEXT_LINK);
6882 #endif /* TEXTAREA_AUTOGROW */
6883 #endif /* TEXTFIELDS_MAY_NEED_ACTIVATION */
6884 		} else
6885 		    switch (c) {
6886 		    case '\n':
6887 		    case '\r':
6888 #ifdef TEXTAREA_AUTOGROW
6889 			/*
6890 			 * If on the bottom line of a TEXTAREA, and the user
6891 			 * hit the ENTER key, we add a new line/anchor
6892 			 * automatically, positioning the cursor on it.
6893 			 *
6894 			 * If at the bottom of the screen, we effectively
6895 			 * perform an LYK_DOWN_HALF-like operation, then move
6896 			 * down to the new line we just added.  --KED 02/14/99
6897 			 *
6898 			 * [There is some redundancy and non-standard
6899 			 * indentation in the monster-if() below.  This is
6900 			 * intentional ...  to try and improve the
6901 			 * "readability" (such as it is).  Caveat emptor to
6902 			 * anyone trying to change it.]
6903 			 */
6904 			if (LinkIsTextarea(curdoc.link)
6905 			    && ((curdoc.link == nlinks - 1 &&
6906 				 !(more_text &&
6907 				   HText_TAHasMoreLines(curdoc.link, 1)))
6908 				||
6909 				((curdoc.link < nlinks - 1) &&
6910 				 !LinkIsTextarea(curdoc.link + 1))
6911 				||
6912 				((curdoc.link < nlinks - 1) &&
6913 				 (LinkIsTextarea(curdoc.link + 1)
6914 				  && ((links[curdoc.link].l_form->number !=
6915 				       links[curdoc.link + 1].l_form->number) ||
6916 				      (strcmp(links[curdoc.link].l_form->name,
6917 					      links[curdoc.link + 1].l_form->name)
6918 				       != 0)))))) {
6919 
6920 			    HText_ExpandTextarea(&links[curdoc.link], 1);
6921 
6922 			    if (links[curdoc.link].ly < display_lines) {
6923 				refresh_screen = TRUE;
6924 			    } else {
6925 				LYChgNewline(display_lines / 2);
6926 				if (nlinks > 0 && curdoc.link > -1 &&
6927 				    links[curdoc.link].ly > display_lines / 2) {
6928 				    newdoc.link = curdoc.link;
6929 				    for (i = 0;
6930 					 links[i].ly <= (display_lines / 2);
6931 					 i++)
6932 					--newdoc.link;
6933 				    newdoc.link++;
6934 				}
6935 			    }
6936 #ifdef TEXTFIELDS_MAY_NEED_ACTIVATION
6937 			    if (textfields_need_activation) {
6938 				textinput_activated = TRUE;
6939 				textfields_need_activation = textfields_activation_option;
6940 #ifdef INACTIVE_INPUT_STYLE_VH
6941 				textinput_redrawn = TRUE;
6942 #endif
6943 			    };
6944 #endif
6945 
6946 			}
6947 #endif /* TEXTAREA_AUTOGROW */
6948 
6949 			/*
6950 			 * Make return in input field (if it was returned by
6951 			 * change_form_link) act as LYK_NEXT_LINK, independent
6952 			 * of what key (if any) is mapped to LYK_NEXT_LINK.  -
6953 			 * kw
6954 			 */
6955 			c = LAC_TO_LKC0(LYK_NEXT_LINK);
6956 			break;
6957 		    default:
6958 
6959 			if (old_c != c && old_c != real_c && c != real_c)
6960 			    real_c = c;
6961 		    }
6962 	    } else {
6963 #if defined(TEXTFIELDS_MAY_NEED_ACTIVATION) && defined(INACTIVE_INPUT_STYLE_VH)
6964 		if (curlink_is_editable && !textinput_redrawn) {
6965 		    /*draw the text entry, but don't activate it */
6966 		    textinput_redrawn = TRUE;
6967 		    change_form_link_ex(curdoc.link,
6968 					&newdoc, &refresh_screen,
6969 					use_last_tfpos, FALSE, TRUE);
6970 		    if (LYShowCursor) {
6971 			LYmove(links[curdoc.link].ly,
6972 			       ((links[curdoc.link].lx > 0) ?
6973 				(links[curdoc.link].lx - 1) : 0));
6974 		    } else {
6975 			LYHideCursor();
6976 		    }
6977 		}
6978 #endif /* TEXTFIELDS_MAY_NEED_ACTIVATION && INACTIVE_INPUT_STYLE_VH */
6979 		/*
6980 		 * Get a keystroke from the user.  Save the last keystroke to
6981 		 * avoid redundant error reporting.
6982 		 */
6983 		real_c = c = LYgetch();		/* get user input */
6984 
6985 		if (c != last_key)
6986 		    key_count = 0;
6987 		key_count++;
6988 		last_key = c;
6989 #ifndef VMS
6990 		if (c == 3) {	/* ^C */
6991 		    /*
6992 		     * This shouldn't happen.  We'll try to deal with whatever
6993 		     * bug caused it.  - FM
6994 		     */
6995 		    signal(SIGINT, cleanup_sig);
6996 		    old_c = 0;
6997 		    cmd = LYK_QUIT;
6998 		    goto new_cmd;
6999 		}
7000 #endif /* !VMS */
7001 		if (LKC_HAS_ESC_MOD(c) && EditBinding(c) != LYE_FORM_PASS) {
7002 		    /*
7003 		     * If ESC + <key> was read (and not recognized as a
7004 		     * terminal escape sequence for another key), ignore the
7005 		     * ESC modifier and act on <key> only if the line editor
7006 		     * binding would have passed the same ESC-modified
7007 		     * lynxkeycode back to us if it had been pressed in a text
7008 		     * input field.  Otherwise set interesting part so that it
7009 		     * will map to 0, to prevent that ESC + <key> acts like
7010 		     * <key>, which might be unexpected.  - kw
7011 		     */
7012 		    c = (c & ~LKC_MASK) | LAC_TO_LKC(0);
7013 		}
7014 		if (old_c != real_c) {
7015 		    old_c = 0;
7016 		}
7017 	    }
7018 	}
7019 
7020 #ifdef VMS
7021 	if (HadVMSInterrupt) {
7022 	    HadVMSInterrupt = FALSE;
7023 	    c = DO_NOTHING;
7024 	}
7025 #else
7026 	if (recent_sizechange) {
7027 	    if (c <= 0)
7028 		c = DO_NOTHING;
7029 	}
7030 #endif /* VMS */
7031 
7032       new_keyboard_input:
7033 	/*
7034 	 * A goto point for new input without going back through the getch()
7035 	 * loop.
7036 	 */
7037 	if (traversal) {
7038 	    if ((c = DoTraversal(c, &crawl_ok)) < 0) {
7039 		CleanupMainLoop();
7040 		return (EXIT_FAILURE);
7041 	    }
7042 	}
7043 	/* traversal */
7044 #ifdef WIN_EX
7045 	if (c == DO_NOTHING)
7046 	    cmd = LYK_DO_NOTHING;
7047 	else
7048 #endif
7049 	    cmd = LKC_TO_LAC(keymap, c);	/* adds 1 to map EOF to 0 */
7050 
7051 #if defined(DIRED_SUPPORT) && defined(OK_OVERRIDE)
7052 	if (lynx_edit_mode && !no_dired_support && LKC_TO_LAC(key_override, c))
7053 	    cmd = LKC_TO_LAC(key_override, c);
7054 #endif /* DIRED_SUPPORT && OK_OVERRIDE */
7055 
7056 	real_cmd = cmd;
7057 
7058 	/*
7059 	 * A goto point for new input without going back through the getch()
7060 	 * loop.
7061 	 */
7062       new_cmd:
7063 
7064 	force_old_UCLYhndl_on_reload = FALSE;
7065 	CTRACE_FLUSH(tfp);
7066 
7067 	if (cmd != LYK_UP_LINK && cmd != LYK_DOWN_LINK)
7068 	    follow_col = -1;
7069 
7070 	CTRACE((tfp, "Handling key as %s\n",
7071 		((LYKeycodeToKcmd((LYKeymapCode) cmd) != 0)
7072 		 ? LYKeycodeToKcmd((LYKeymapCode) cmd)->name
7073 		 : "unknown")));
7074 	switch (cmd) {
7075 	case -1:
7076 	    HTUserMsg(COMMAND_UNKNOWN);
7077 	    break;
7078 	case 0:		/* unmapped character */
7079 	default:
7080 	    if (curdoc.link >= 0 && curdoc.link < nlinks &&
7081 		LinkIsTextLike(curdoc.link)) {
7082 
7083 #ifdef TEXTFIELDS_MAY_NEED_ACTIVATION
7084 		if (textfields_need_activation) {
7085 		    show_main_statusline(links[curdoc.link], FOR_PANEL);
7086 #ifdef INACTIVE_INPUT_STYLE_VH
7087 		    textinput_redrawn = FALSE;
7088 #endif
7089 		} else
7090 #endif
7091 		    show_main_statusline(links[curdoc.link], FOR_INPUT);
7092 	    } else if (more_text) {
7093 		HTInfoMsg(MOREHELP);
7094 	    } else {
7095 		HTInfoMsg(HELP);
7096 	    }
7097 	    show_help = TRUE;
7098 
7099 	    if (TRACE) {
7100 		sprintf(cfile, "%d", c);
7101 		LYaddstr(cfile);	/* show the user input */
7102 		cfile[0] = '\0';
7103 	    }
7104 	    break;
7105 
7106 	case LYK_COMMAND:
7107 	    cmd = handle_LYK_COMMAND(&user_input_buffer);
7108 	    goto new_cmd;
7109 
7110 	case LYK_INTERRUPT:
7111 	    /*
7112 	     * No network transmission to interrupt - 'til we multithread.
7113 	     */
7114 	    break;
7115 
7116 	case LYK_F_LINK_NUM:
7117 	    c = '\0';
7118 	    /* FALLTHRU */
7119 	case LYK_1:		/* FALLTHRU */
7120 	case LYK_2:		/* FALLTHRU */
7121 	case LYK_3:		/* FALLTHRU */
7122 	case LYK_4:		/* FALLTHRU */
7123 	case LYK_5:		/* FALLTHRU */
7124 	case LYK_6:		/* FALLTHRU */
7125 	case LYK_7:		/* FALLTHRU */
7126 	case LYK_8:		/* FALLTHRU */
7127 	case LYK_9:
7128 	    handle_LYK_digit(c, &force_load, &old_c, real_c, &try_internal);
7129 	    break;
7130 
7131 	case LYK_SOURCE:	/* toggle view source mode */
7132 	    handle_LYK_SOURCE(&ownerS_address);
7133 	    break;
7134 
7135 	case LYK_CHG_CENTER:	/* ^Q */
7136 
7137 	    if (no_table_center) {
7138 		no_table_center = FALSE;
7139 		HTInfoMsg(gettext("TABLE center enable."));
7140 	    } else {
7141 		no_table_center = TRUE;
7142 		HTInfoMsg(gettext("TABLE center disable."));
7143 	    }
7144 	    /* FALLTHRU */
7145 
7146 	case LYK_RELOAD:	/* control-R to reload and refresh */
7147 	    handle_LYK_RELOAD(real_cmd);
7148 	    break;
7149 
7150 	case LYK_HISTORICAL:	/* toggle 'historical' comments parsing */
7151 	    handle_LYK_HISTORICAL();
7152 	    break;
7153 
7154 	case LYK_MINIMAL:	/* toggle 'minimal' comments parsing */
7155 	    handle_LYK_MINIMAL();
7156 	    break;
7157 
7158 	case LYK_SOFT_DQUOTES:
7159 	    handle_LYK_SOFT_DQUOTES();
7160 	    break;
7161 
7162 	case LYK_SWITCH_DTD:
7163 	    handle_LYK_SWITCH_DTD();
7164 	    break;
7165 
7166 	case LYK_QUIT:		/* quit */
7167 	    if (handle_LYK_QUIT()) {
7168 		CleanupMainLoop();
7169 		return (EXIT_SUCCESS);
7170 	    }
7171 	    break;
7172 
7173 	case LYK_ABORT:	/* don't ask the user about quitting */
7174 	    CleanupMainLoop();
7175 	    return (EXIT_SUCCESS);
7176 
7177 	case LYK_NEXT_PAGE:	/* next page */
7178 	    handle_LYK_NEXT_PAGE(&old_c, real_c);
7179 	    break;
7180 
7181 	case LYK_PREV_PAGE:	/* page up */
7182 	    handle_LYK_PREV_PAGE(&old_c, real_c);
7183 	    break;
7184 
7185 	case LYK_UP_TWO:
7186 	    handle_LYK_UP_TWO(&arrowup, &old_c, real_c);
7187 	    break;
7188 
7189 	case LYK_DOWN_TWO:
7190 	    handle_LYK_DOWN_TWO(&old_c, real_c);
7191 	    break;
7192 
7193 	case LYK_UP_HALF:
7194 	    handle_LYK_UP_HALF(&arrowup, &old_c, real_c);
7195 	    break;
7196 
7197 	case LYK_DOWN_HALF:
7198 	    handle_LYK_DOWN_HALF(&old_c, real_c);
7199 	    break;
7200 
7201 #ifdef CAN_CUT_AND_PASTE
7202 	case LYK_TO_CLIPBOARD:	/* ^S */
7203 	    {
7204 		char *s;
7205 		int ch2;
7206 
7207 		/* The logic resembles one of ADD_BOOKMARK */
7208 		if (nlinks > 0 && links[curdoc.link].lname
7209 		    && links[curdoc.link].type != WWW_FORM_LINK_TYPE) {
7210 		    /* Makes sense to copy a link */
7211 		    _statusline("Copy D)ocument's or L)ink's URL to clipboard or C)ancel?");
7212 		    ch2 = LYgetch_single();
7213 		    if (ch2 == 'D')
7214 			s = curdoc.address;
7215 		    else if (ch2 == 'C')
7216 			break;
7217 		    else
7218 			s = links[curdoc.link].lname;
7219 		} else
7220 		    s = curdoc.address;
7221 		if (isEmpty(s))
7222 		    HTInfoMsg(gettext("Current URL is empty."));
7223 		if (put_clip(s))
7224 		    HTInfoMsg(gettext("Copy to clipboard failed."));
7225 		else if (s == curdoc.address)
7226 		    HTInfoMsg(gettext("Document URL put to clipboard."));
7227 		else
7228 		    HTInfoMsg(gettext("Link URL put to clipboard."));
7229 	    }
7230 	    break;
7231 
7232 	case LYK_PASTE_URL:
7233 	    if (no_goto && !LYValidate) {	/*  Go to not allowed. - FM */
7234 		HTUserMsg(GOTO_DISALLOWED);
7235 	    } else {
7236 		unsigned char *s = (unsigned char *) get_clip_grab(), *e, *t;
7237 		char *buf;
7238 		int len2;
7239 
7240 		if (!s)
7241 		    break;
7242 		len2 = (int) strlen((const char *) s);
7243 		e = s + len2;
7244 		while (s < e && strchr(" \t\n\r", *s))
7245 		    s++;
7246 		while (s < e && strchr(" \t\n\r", e[-1]))
7247 		    e--;
7248 		if (s[0] == '<' && e > s && e[-1] == '>') {
7249 		    s++;
7250 		    e--;
7251 		    if (!strncasecomp((const char *) s, "URL:", 4))
7252 			s += 4;
7253 		}
7254 		if (s >= e) {
7255 		    HTInfoMsg(gettext("No URL in the clipboard."));
7256 		    break;
7257 		}
7258 		len = (unsigned) (e - s + 1);
7259 		if (len < MAX_LINE)
7260 		    len = MAX_LINE;	/* Required for do_check_goto_URL() */
7261 		buf = typeMallocn(char, len);
7262 
7263 		LYStrNCpy(buf, (const char *) s, (e - s));
7264 		t = (unsigned char *) buf;
7265 
7266 		while (s < e) {
7267 		    if (strchr(" \t\n\r", *s)) {
7268 			int nl2 = 0;	/* Keep whitespace without NL - file names! */
7269 			unsigned char *s1 = s;
7270 
7271 			while (strchr(" \t\n\r", *s)) {
7272 			    if (!nl2 && *s == '\n')
7273 				nl2 = 1;
7274 			    s++;
7275 			}
7276 			if (!nl2) {
7277 			    while (s1 < s) {
7278 				if (*s1 != '\r' && *s1 != '\n')
7279 				    *t = *s1;
7280 				t++, s1++;
7281 			    }
7282 			}
7283 		    } else
7284 			*t++ = *s++;
7285 		}
7286 		*t = '\0';
7287 		get_clip_release();
7288 		BStrCopy0(user_input_buffer, buf);
7289 		do_check_goto_URL(&user_input_buffer, &temp, &force_load);
7290 		free(buf);
7291 	    }
7292 	    break;
7293 #endif
7294 
7295 #ifdef KANJI_CODE_OVERRIDE
7296 	case LYK_CHG_KCODE:
7297 	    if (LYRawMode && (HTCJK == JAPANESE)) {
7298 		switch (last_kcode) {
7299 		case NOKANJI:
7300 		    last_kcode = SJIS;
7301 		    break;
7302 		case SJIS:
7303 		    last_kcode = EUC;
7304 		    break;
7305 		case EUC:
7306 		    last_kcode = NOKANJI;
7307 		    break;
7308 		default:
7309 		    break;
7310 		}
7311 	    }
7312 	    LYmove(0, 0);
7313 	    lynx_start_title_color();
7314 	    LYaddstr(str_kcode(last_kcode));
7315 	    lynx_stop_title_color();
7316 
7317 	    break;
7318 #endif
7319 
7320 	case LYK_REFRESH:
7321 	    refresh_screen = TRUE;
7322 	    lynx_force_repaint();
7323 	    break;
7324 
7325 	case LYK_HOME:
7326 	    if (curdoc.line > 1) {
7327 		LYSetNewline(1);
7328 	    } else {
7329 		cmd = LYK_PREV_PAGE;
7330 		goto new_cmd;
7331 	    }
7332 	    break;
7333 
7334 	case LYK_END:
7335 	    i = HText_getNumOfLines() - display_lines + 2;
7336 	    if (i >= 1 && LYGetNewline() != i) {
7337 		LYSetNewline(i);	/* go to end of file */
7338 		arrowup = TRUE;	/* position on last link */
7339 	    } else {
7340 		cmd = LYK_NEXT_PAGE;
7341 		goto new_cmd;
7342 	    }
7343 	    break;
7344 
7345 	case LYK_FIRST_LINK:
7346 	    handle_LYK_FIRST_LINK();
7347 	    break;
7348 
7349 	case LYK_LAST_LINK:
7350 	    handle_LYK_LAST_LINK();
7351 	    break;
7352 
7353 	case LYK_PREV_LINK:
7354 	case LYK_LPOS_PREV_LINK:
7355 	    handle_LYK_PREV_LINK(&arrowup, &old_c, real_c);
7356 	    break;
7357 
7358 	case LYK_NEXT_LINK:
7359 	case LYK_LPOS_NEXT_LINK:
7360 	    handle_LYK_NEXT_LINK(c, &old_c, real_c);
7361 	    break;
7362 
7363 	case LYK_FASTFORW_LINK:
7364 	    handle_LYK_FASTFORW_LINK(&old_c, real_c);
7365 	    break;
7366 
7367 	case LYK_FASTBACKW_LINK:
7368 	    if (handle_LYK_FASTBACKW_LINK(&cmd, &old_c, real_c))
7369 		goto new_cmd;
7370 	    break;
7371 
7372 	case LYK_UP_LINK:
7373 	    handle_LYK_UP_LINK(&follow_col, &arrowup, &old_c, real_c);
7374 	    break;
7375 
7376 	case LYK_DOWN_LINK:
7377 	    handle_LYK_DOWN_LINK(&follow_col, &old_c, real_c);
7378 	    break;
7379 
7380 	case LYK_CHANGE_LINK:
7381 	    do_change_link();
7382 #if defined(TEXTFIELDS_MAY_NEED_ACTIVATION) && defined(INACTIVE_INPUT_STYLE_VH)
7383 	    if (textfields_need_activation)
7384 		textinput_redrawn = FALSE;
7385 #endif /* TEXTFIELDS_MAY_NEED_ACTIVATION && INACTIVE_INPUT_STYLE_VH */
7386 	    break;
7387 
7388 	case LYK_RIGHT_LINK:
7389 	    handle_LYK_RIGHT_LINK();
7390 	    break;
7391 
7392 	case LYK_LEFT_LINK:
7393 	    handle_LYK_LEFT_LINK();
7394 	    break;
7395 
7396 	case LYK_COOKIE_JAR:	/* show the cookie jar */
7397 	    if (handle_LYK_COOKIE_JAR(&cmd))
7398 		goto new_cmd;
7399 	    break;
7400 
7401 #ifdef USE_CACHEJAR
7402 	case LYK_CACHE_JAR:	/* show the cache jar */
7403 	    if (handle_LYK_CACHE_JAR(&cmd))
7404 		goto new_cmd;
7405 	    break;
7406 #endif
7407 
7408 	case LYK_HISTORY:	/* show the history page */
7409 	    if (handle_LYK_HISTORY(ForcePush))
7410 		break;
7411 
7412 	    /* FALLTHRU */
7413 	case LYK_PREV_DOC:	/* back up a level */
7414 	    switch (handle_PREV_DOC(&cmd, &old_c, real_c)) {
7415 	    case 1:
7416 		CleanupMainLoop();
7417 		return (EXIT_SUCCESS);
7418 	    case 2:
7419 		goto new_cmd;
7420 	    }
7421 	    break;
7422 
7423 	case LYK_NEXT_DOC:	/* undo back up a level */
7424 	    handle_NEXT_DOC();
7425 	    break;
7426 
7427 	case LYK_NOCACHE:	/* Force submission of form or link with no-cache */
7428 	    if (!handle_LYK_NOCACHE(&old_c, real_c))
7429 		break;
7430 
7431 	    /* FALLTHRU */
7432 	case LYK_ACTIVATE:	/* follow a link */
7433 	case LYK_MOUSE_SUBMIT:	/* follow a link, submit TEXT_SUBMIT input */
7434 	    switch (handle_LYK_ACTIVATE(&c,
7435 					cmd,
7436 					&try_internal,
7437 					&refresh_screen,
7438 					&force_load,
7439 					real_cmd)) {
7440 	    case 1:
7441 		continue;
7442 	    case 2:
7443 		goto new_keyboard_input;
7444 	    case 3:
7445 		pending_form_c = c;
7446 		break;
7447 	    }
7448 	    break;
7449 
7450 	case LYK_SUBMIT:
7451 	    handle_LYK_SUBMIT(curdoc.link, &newdoc, &refresh_screen);
7452 	    break;
7453 
7454 	case LYK_RESET:
7455 	    handle_LYK_RESET(curdoc.link, &refresh_screen);
7456 	    break;
7457 
7458 	case LYK_ELGOTO:	/* edit URL of current link and go to it  */
7459 	    if (handle_LYK_ELGOTO(&ch, &user_input_buffer, &temp, &old_c, real_c))
7460 		do_check_goto_URL(&user_input_buffer, &temp, &force_load);
7461 	    break;
7462 
7463 	case LYK_ECGOTO:	/* edit current URL and go to to it     */
7464 	    if (handle_LYK_ECGOTO(&ch, &user_input_buffer, &temp, &old_c, real_c))
7465 		do_check_goto_URL(&user_input_buffer, &temp, &force_load);
7466 	    break;
7467 
7468 	case LYK_GOTO:		/* 'g' to goto a random URL  */
7469 	    if (handle_LYK_GOTO(&ch, &user_input_buffer, &temp, &recall,
7470 				&URLTotal, &URLNum, &FirstURLRecall, &old_c,
7471 				real_c)) {
7472 		if (do_check_recall(ch, &user_input_buffer, &temp, URLTotal,
7473 				    &URLNum, recall, &FirstURLRecall))
7474 		    do_check_goto_URL(&user_input_buffer, &temp, &force_load);
7475 	    }
7476 	    break;
7477 
7478 	case LYK_DWIMHELP:	/* show context-dependent help file */
7479 	    handle_LYK_DWIMHELP(&cshelpfile);
7480 	    /* FALLTHRU */
7481 
7482 	case LYK_HELP:		/* show help file */
7483 	    handle_LYK_HELP(&cshelpfile);
7484 	    break;
7485 
7486 	case LYK_INDEX:	/* index file */
7487 	    handle_LYK_INDEX(&old_c, real_c);
7488 	    break;
7489 
7490 	case LYK_MAIN_MENU:	/* return to main screen */
7491 	    handle_LYK_MAIN_MENU(&old_c, real_c);
7492 	    break;
7493 
7494 #ifdef EXP_NESTED_TABLES
7495 	case LYK_NESTED_TABLES:
7496 	    if (handle_LYK_NESTED_TABLES(&cmd))
7497 		goto new_cmd;
7498 	    break;
7499 #endif
7500 	case LYK_OPTIONS:	/* options screen */
7501 	    if (handle_LYK_OPTIONS(&cmd, &refresh_screen))
7502 		goto new_cmd;
7503 	    break;
7504 
7505 	case LYK_INDEX_SEARCH:	/* search for a user string */
7506 	    handle_LYK_INDEX_SEARCH(&force_load, ForcePush, &old_c, real_c);
7507 	    break;
7508 
7509 	case LYK_WHEREIS:	/* search within the document */
7510 	case LYK_NEXT:		/* find the next occurrence in the document */
7511 	case LYK_PREV:		/* find the previous occurrence in the document */
7512 	    handle_LYK_WHEREIS(cmd, &refresh_screen);
7513 	    break;
7514 
7515 	case LYK_COMMENT:	/* reply by mail */
7516 	    handle_LYK_COMMENT(&refresh_screen, &owner_address, &old_c, real_c);
7517 	    break;
7518 
7519 #ifdef DIRED_SUPPORT
7520 	case LYK_TAG_LINK:	/* tag or untag the current link */
7521 	    handle_LYK_TAG_LINK();
7522 	    break;
7523 
7524 	case LYK_MODIFY:	/* rename a file or directory */
7525 	    handle_LYK_MODIFY(&refresh_screen);
7526 	    break;
7527 
7528 	case LYK_CREATE:	/* create a new file or directory */
7529 	    handle_LYK_CREATE();
7530 	    break;
7531 #endif /* DIRED_SUPPORT */
7532 
7533 	case LYK_DWIMEDIT:	/* context-dependent edit */
7534 	    switch (handle_LYK_DWIMEDIT(&cmd, &old_c, real_c)) {
7535 	    case 1:
7536 		continue;
7537 	    case 2:
7538 		goto new_cmd;
7539 	    }
7540 	    /* FALLTHRU */
7541 
7542 	case LYK_EDIT:		/* edit */
7543 	    handle_LYK_EDIT(&old_c, real_c);
7544 	    break;
7545 
7546 	case LYK_DEL_BOOKMARK:	/* remove a bookmark file link */
7547 	    handle_LYK_DEL_BOOKMARK(&refresh_screen, &old_c, real_c);
7548 	    break;
7549 
7550 #ifdef DIRED_SUPPORT
7551 	case LYK_REMOVE:	/* remove files and directories */
7552 	    handle_LYK_REMOVE(&refresh_screen);
7553 	    break;
7554 #endif /* DIRED_SUPPORT */
7555 
7556 #if defined(DIRED_SUPPORT) && defined(OK_INSTALL)
7557 	case LYK_INSTALL:	/* install a file into system area */
7558 	    handle_LYK_INSTALL();
7559 	    break;
7560 #endif /* DIRED_SUPPORT && OK_INSTALL */
7561 
7562 	case LYK_INFO:		/* show document info */
7563 	    if (handle_LYK_INFO(&cmd))
7564 		goto new_cmd;
7565 	    break;
7566 
7567 	case LYK_EDIT_TEXTAREA:	/* use external editor on a TEXTAREA - KED */
7568 	    handle_LYK_EDIT_TEXTAREA(&refresh_screen, &old_c, real_c);
7569 	    break;
7570 
7571 	case LYK_GROW_TEXTAREA:	/* add new lines to bottom of TEXTAREA - KED */
7572 	    handle_LYK_GROW_TEXTAREA(&refresh_screen);
7573 	    break;
7574 
7575 	case LYK_INSERT_FILE:	/* insert file in TEXTAREA, above cursor - KED */
7576 	    handle_LYK_INSERT_FILE(&refresh_screen, &old_c, real_c);
7577 	    break;
7578 
7579 	case LYK_PRINT:	/* print the file */
7580 	    handle_LYK_PRINT(&ForcePush, &old_c, real_c);
7581 	    break;
7582 
7583 	case LYK_LIST:		/* list links in the current document */
7584 	    if (handle_LYK_LIST(&cmd))
7585 		goto new_cmd;
7586 	    break;
7587 
7588 #ifdef USE_ADDRLIST_PAGE
7589 	case LYK_ADDRLIST:	/* always list URLs (only) */
7590 	    if (handle_LYK_ADDRLIST(&cmd))
7591 		goto new_cmd;
7592 	    break;
7593 #endif /* USE_ADDRLIST_PAGE */
7594 
7595 	case LYK_VLINKS:	/* list links visited during the current session */
7596 	    if (handle_LYK_VLINKS(&cmd, &newdoc_link_is_absolute))
7597 		goto new_cmd;
7598 	    break;
7599 
7600 	case LYK_TOOLBAR:	/* go to Toolbar or Banner in current document */
7601 	    handle_LYK_TOOLBAR(&try_internal, &force_load, &old_c, real_c);
7602 	    break;
7603 
7604 #if defined(DIRED_SUPPORT) || defined(VMS)
7605 	case LYK_DIRED_MENU:	/* provide full file management menu */
7606 	    handle_LYK_DIRED_MENU(&refresh_screen, &old_c, real_c);
7607 	    break;
7608 #endif /* DIRED_SUPPORT || VMS */
7609 
7610 #ifdef USE_EXTERNALS
7611 	case LYK_EXTERN_LINK:	/* use external program on url */
7612 	    handle_LYK_EXTERN_LINK(&refresh_screen);
7613 	    break;
7614 	case LYK_EXTERN_PAGE:	/* use external program on current page */
7615 	    handle_LYK_EXTERN_PAGE(&refresh_screen);
7616 	    break;
7617 #endif /* USE_EXTERNALS */
7618 
7619 	case LYK_ADD_BOOKMARK:	/* add link to bookmark file */
7620 	    handle_LYK_ADD_BOOKMARK(&refresh_screen, &old_c, real_c);
7621 	    break;
7622 
7623 	case LYK_VIEW_BOOKMARK:	/* v to view home page */
7624 	    handle_LYK_VIEW_BOOKMARK(&refresh_screen, &old_c, real_c);
7625 	    break;
7626 
7627 	case LYK_SHELL:	/* (!) shell escape */
7628 	    handle_LYK_SHELL(&refresh_screen, &old_c, real_c);
7629 	    break;
7630 
7631 	case LYK_DOWNLOAD:
7632 	    switch (handle_LYK_DOWNLOAD(&cmd, &old_c, real_c)) {
7633 	    case 1:
7634 		continue;
7635 	    case 2:
7636 		goto new_cmd;
7637 	    }
7638 	    break;
7639 
7640 #ifdef DIRED_SUPPORT
7641 	case LYK_UPLOAD:
7642 	    handle_LYK_UPLOAD();
7643 	    break;
7644 #endif /* DIRED_SUPPORT */
7645 
7646 	case LYK_TRACE_TOGGLE:	/*  Toggle TRACE mode. */
7647 	    handle_LYK_TRACE_TOGGLE();
7648 	    break;
7649 
7650 	case LYK_TRACE_LOG:	/*  View TRACE log. */
7651 	    handle_LYK_TRACE_LOG(&trace_mode_flag);
7652 	    break;
7653 
7654 	case LYK_IMAGE_TOGGLE:
7655 	    if (handle_LYK_IMAGE_TOGGLE(&cmd))
7656 		goto new_cmd;
7657 	    break;
7658 
7659 	case LYK_INLINE_TOGGLE:
7660 	    if (handle_LYK_INLINE_TOGGLE(&cmd))
7661 		goto new_cmd;
7662 	    break;
7663 
7664 	case LYK_RAW_TOGGLE:
7665 	    if (handle_LYK_RAW_TOGGLE(&cmd))
7666 		goto new_cmd;
7667 	    break;
7668 
7669 	case LYK_HEAD:
7670 	    if (handle_LYK_HEAD(&cmd))
7671 		goto new_cmd;
7672 	    break;
7673 
7674 	case LYK_TOGGLE_HELP:
7675 	    handle_LYK_TOGGLE_HELP();
7676 	    break;
7677 
7678 	case LYK_KEYMAP:
7679 	    handle_LYK_KEYMAP(&vi_keys_flag, &emacs_keys_flag, &old_c, real_c);
7680 	    break;
7681 
7682 	case LYK_JUMP:
7683 	    if (handle_LYK_JUMP(c, &user_input_buffer, &temp, &recall,
7684 				&FirstURLRecall, &URLNum, &URLTotal, &ch,
7685 				&old_c, real_c)) {
7686 		if (do_check_recall(ch, &user_input_buffer, &temp, URLTotal,
7687 				    &URLNum, recall, &FirstURLRecall))
7688 		    do_check_goto_URL(&user_input_buffer, &temp, &force_load);
7689 	    }
7690 	    break;
7691 
7692 	case LYK_CLEAR_AUTH:
7693 	    handle_LYK_CLEAR_AUTH(&old_c, real_c);
7694 	    break;
7695 
7696 	case LYK_DO_NOTHING:	/* pretty self explanatory */
7697 	    break;
7698 #ifdef SUPPORT_CHDIR
7699 	case LYK_CHDIR:
7700 	    handle_LYK_CHDIR();
7701 	    break;
7702 	case LYK_PWD:
7703 	    handle_LYK_PWD();
7704 	    break;
7705 #endif
7706 #ifdef USE_CURSES_PADS
7707 	case LYK_SHIFT_LEFT:
7708 	    handle_LYK_SHIFT_LEFT(&refresh_screen, key_count);
7709 	    break;
7710 	case LYK_SHIFT_RIGHT:
7711 	    handle_LYK_SHIFT_RIGHT(&refresh_screen, key_count);
7712 	    break;
7713 	case LYK_LINEWRAP_TOGGLE:
7714 	    if (handle_LYK_LINEWRAP_TOGGLE(&cmd, &refresh_screen))
7715 		goto new_cmd;
7716 	    break;
7717 #endif
7718 
7719 #ifdef USE_MAXSCREEN_TOGGLE
7720 	case LYK_MAXSCREEN_TOGGLE:
7721 	    if (handle_LYK_MAXSCREEN_TOGGLE(&cmd))
7722 		goto new_cmd;
7723 	    break;
7724 #endif
7725 	}			/* end of BIG switch */
7726     }
7727 }
7728 
are_different(DocInfo * doc1,DocInfo * doc2)7729 static int are_different(DocInfo *doc1, DocInfo *doc2)
7730 {
7731     char *cp1, *cp2;
7732 
7733     /*
7734      * Do we have two addresses?
7735      */
7736     if (!doc1->address || !doc2->address)
7737 	return (TRUE);
7738 
7739     /*
7740      * Do they differ in the type of request?
7741      */
7742     if (doc1->isHEAD != doc2->isHEAD)
7743 	return (TRUE);
7744 
7745     /*
7746      * See if the addresses are different, making sure we're not tripped up by
7747      * multiple anchors in the the same document from a POST form.  -- FM
7748      */
7749     cp1 = trimPoundSelector(doc1->address);
7750     cp2 = trimPoundSelector(doc2->address);
7751     /*
7752      * Are the base addresses different?
7753      */
7754     if (strcmp(doc1->address, doc2->address)) {
7755 	restorePoundSelector(cp1);
7756 	restorePoundSelector(cp2);
7757 	return (TRUE);
7758     }
7759     restorePoundSelector(cp1);
7760     restorePoundSelector(cp2);
7761 
7762     /*
7763      * Do the docs have different contents?
7764      */
7765     if (doc1->post_data) {
7766 	if (doc2->post_data) {
7767 	    if (!BINEQ(doc1->post_data, doc2->post_data))
7768 		return (TRUE);
7769 	} else
7770 	    return (TRUE);
7771     } else if (doc2->post_data)
7772 	return (TRUE);
7773 
7774     /*
7775      * We'll assume the two documents in fact are the same.
7776      */
7777     return (FALSE);
7778 }
7779 
7780 /* This determines whether two docs are _physically_ different,
7781  * meaning they are "from different files". - kw
7782  */
are_phys_different(DocInfo * doc1,DocInfo * doc2)7783 static int are_phys_different(DocInfo *doc1, DocInfo *doc2)
7784 {
7785     char *cp1, *cp2, *ap1 = doc1->address, *ap2 = doc2->address;
7786 
7787     /*
7788      * Do we have two addresses?
7789      */
7790     if (!doc1->address || !doc2->address)
7791 	return (TRUE);
7792 
7793     /*
7794      * Do they differ in the type of request?
7795      */
7796     if (doc1->isHEAD != doc2->isHEAD)
7797 	return (TRUE);
7798 
7799     /*
7800      * Skip over possible LYNXIMGMAP parts. - kw
7801      */
7802     if (isLYNXIMGMAP(doc1->address))
7803 	ap1 += LEN_LYNXIMGMAP;
7804     if (isLYNXIMGMAP(doc2->address))
7805 	ap2 += LEN_LYNXIMGMAP;
7806     /*
7807      * If there isn't any real URL in doc2->address, but maybe just
7808      * a fragment, doc2 is assumed to be an internal reference in
7809      * the same physical document, so return FALSE. - kw
7810      */
7811     if (*ap2 == '\0' || *ap2 == '#')
7812 	return (FALSE);
7813 
7814     /*
7815      * See if the addresses are different, making sure we're not tripped up by
7816      * multiple anchors in the the same document from a POST form.  -- FM
7817      */
7818     cp1 = trimPoundSelector(doc1->address);
7819     cp2 = trimPoundSelector(doc2->address);
7820     /*
7821      * Are the base addresses different?
7822      */
7823     if (strcmp(ap1, ap2)) {
7824 	restorePoundSelector(cp1);
7825 	restorePoundSelector(cp2);
7826 	return (TRUE);
7827     }
7828     restorePoundSelector(cp1);
7829     restorePoundSelector(cp2);
7830 
7831     /*
7832      * Do the docs have different contents?
7833      */
7834     if (doc1->post_data) {
7835 	if (doc2->post_data) {
7836 	    if (!BINEQ(doc1->post_data, doc2->post_data))
7837 		return (TRUE);
7838 	} else
7839 	    return (TRUE);
7840     } else if (doc2->post_data)
7841 	return (TRUE);
7842 
7843     /*
7844      * We'll assume the two documents in fact are the same.
7845      */
7846     return (FALSE);
7847 }
7848 
7849 /*
7850  * Utility for freeing the list of goto URLs.  - FM
7851  */
7852 #ifdef LY_FIND_LEAKS
HTGotoURLs_free(void)7853 static void HTGotoURLs_free(void)
7854 {
7855     LYFreeStringList(Goto_URLs);
7856     Goto_URLs = NULL;
7857 }
7858 #endif
7859 
7860 /*
7861  * Utility for listing Goto URLs, making any repeated URLs the most current in
7862  * the list.  - FM
7863  */
HTAddGotoURL(char * url)7864 void HTAddGotoURL(char *url)
7865 {
7866     char *copy = NULL;
7867     char *old;
7868     HTList *cur;
7869 
7870     if (isEmpty(url))
7871 	return;
7872 
7873     CTRACE((tfp, "HTAddGotoURL %s\n", url));
7874     StrAllocCopy(copy, url);
7875 
7876     if (!Goto_URLs) {
7877 	Goto_URLs = HTList_new();
7878 #ifdef LY_FIND_LEAKS
7879 	atexit(HTGotoURLs_free);
7880 #endif
7881 	HTList_addObject(Goto_URLs, copy);
7882 	return;
7883     }
7884 
7885     cur = Goto_URLs;
7886     while (NULL != (old = (char *) HTList_nextObject(cur))) {
7887 	if (!strcmp(old, copy)) {
7888 	    HTList_removeObject(Goto_URLs, old);
7889 	    FREE(old);
7890 	    break;
7891 	}
7892     }
7893     HTList_addObject(Goto_URLs, copy);
7894 
7895     return;
7896 }
7897 
7898 /*
7899  * When help is not on the screen, put a message on the screen to tell the user
7900  * other misc info.
7901  */
show_main_statusline(const LinkInfo curlink,int for_what)7902 static void show_main_statusline(const LinkInfo curlink,
7903 				 int for_what)
7904 {
7905     /*
7906      * Make sure form novice lines are replaced.
7907      */
7908     if (user_mode == NOVICE_MODE && for_what != FOR_INPUT) {
7909 	noviceline(more_text);
7910     }
7911 
7912     if (HTisDocumentSource()) {
7913 	/*
7914 	 * Currently displaying HTML source.
7915 	 */
7916 	_statusline(SOURCE_HELP);
7917 
7918 	/*
7919 	 * If we are in forms mode then explicitly tell the user what each kind
7920 	 * of link is.
7921 	 */
7922 #ifdef INDICATE_FORMS_MODE_FOR_ALL_LINKS_ON_PAGE
7923     } else if (lynx_mode == FORMS_LYNX_MODE && nlinks > 0) {
7924 #else
7925 #ifdef NORMAL_NON_FORM_LINK_STATUSLINES_FOR_ALL_USER_MODES
7926     } else if (lynx_mode == FORMS_LYNX_MODE && nlinks > 0 &&
7927 	       !(curlink.type & WWW_LINK_TYPE)) {
7928 #else
7929     } else if (lynx_mode == FORMS_LYNX_MODE && nlinks > 0 &&
7930 	       !(user_mode == ADVANCED_MODE &&
7931 		 (curlink.type & WWW_LINK_TYPE))) {
7932 #endif /* NORMAL_NON_FORM_LINK_STATUSLINES_FOR_ALL_USER_MODES */
7933 #endif /* INDICATE_FORMS_MODE_FOR_ALL_LINKS_ON_PAGE */
7934 	if (curlink.type == WWW_FORM_LINK_TYPE) {
7935 	    show_formlink_statusline(curlink.l_form, for_what);
7936 	} else {
7937 	    statusline(NORMAL_LINK_MESSAGE);
7938 	}
7939 
7940 	/*
7941 	 * Let them know if it's an index -- very rare.
7942 	 */
7943 	if (is_www_index) {
7944 	    const char *indx = gettext("-index-");
7945 
7946 	    LYmove(LYlines - 1, LYcolLimit - (int) strlen(indx));
7947 	    lynx_start_reverse();
7948 	    LYaddstr(indx);
7949 	    lynx_stop_reverse();
7950 	}
7951 
7952     } else if (user_mode == ADVANCED_MODE && nlinks > 0) {
7953 	/*
7954 	 * Show the URL or, for some internal links, the fragment
7955 	 */
7956 	char *cp = NULL;
7957 
7958 	if (curlink.type == WWW_INTERN_LINK_TYPE &&
7959 	    !isLYNXIMGMAP(curlink.lname)) {
7960 	    cp = findPoundSelector(curlink.lname);
7961 	}
7962 	if (!cp)
7963 	    cp = curlink.lname;
7964 	status_link(cp, more_text, is_www_index);
7965     } else if (is_www_index && more_text) {
7966 	char buf[128];
7967 
7968 	sprintf(buf, WWW_INDEX_MORE_MESSAGE, key_for_func(LYK_INDEX_SEARCH));
7969 	_statusline(buf);
7970     } else if (is_www_index) {
7971 	char buf[128];
7972 
7973 	sprintf(buf, WWW_INDEX_MESSAGE, key_for_func(LYK_INDEX_SEARCH));
7974 	_statusline(buf);
7975     } else if (more_text) {
7976 	if (user_mode == NOVICE_MODE)
7977 	    _statusline(MORE);
7978 	else
7979 	    _statusline(MOREHELP);
7980     } else {
7981 	_statusline(HELP);
7982     }
7983 
7984     /* turn off cursor since now it's probably on statusline -HV */
7985     /* But not if LYShowCursor is on.  -show_cursor may be used as a
7986      * workaround to avoid putting the cursor in the last position, for
7987      * curses implementations or terminals that cannot deal with that
7988      * correctly. - kw */
7989     if (!LYShowCursor) {
7990 	LYHideCursor();
7991     }
7992 }
7993 
7994 /*
7995  * Public function for redrawing the statusline appropriate for the selected
7996  * link.  It should only be called at times when curdoc.link, nlinks, and the
7997  * links[] array are valid.  - kw
7998  */
repaint_main_statusline(int for_what)7999 void repaint_main_statusline(int for_what)
8000 {
8001     if (curdoc.link >= 0 && curdoc.link < nlinks)
8002 	show_main_statusline(links[curdoc.link], for_what);
8003 }
8004 
form_noviceline(int disabled)8005 static void form_noviceline(int disabled)
8006 {
8007     LYmove(LYlines - 2, 0);
8008     LYclrtoeol();
8009     if (!disabled) {
8010 	LYaddstr(FORM_NOVICELINE_ONE);
8011     }
8012     LYmove(LYlines - 1, 0);
8013     LYclrtoeol();
8014     if (disabled)
8015 	return;
8016     if (EditBinding(FROMASCII('\025')) == LYE_ERASE) {
8017 	LYaddstr(FORM_NOVICELINE_TWO);
8018     } else if (EditBinding(FROMASCII('\025')) == LYE_DELBL) {
8019 	LYaddstr(FORM_NOVICELINE_TWO_DELBL);
8020     } else {
8021 	char *temp = NULL;
8022 	char *erasekey = fmt_keys(LYKeyForEditAction(LYE_ERASE), -1);
8023 
8024 	if (erasekey) {
8025 	    HTSprintf0(&temp, FORM_NOVICELINE_TWO_VAR, erasekey);
8026 	} else {
8027 	    erasekey = fmt_keys(LYKeyForEditAction(LYE_DELBL), -1);
8028 	    if (erasekey)
8029 		HTSprintf0(&temp,
8030 			   FORM_NOVICELINE_TWO_DELBL_VAR, erasekey);
8031 	}
8032 	if (temp) {
8033 	    LYaddstr(temp);
8034 	    FREE(temp);
8035 	}
8036 	FREE(erasekey);
8037     }
8038 }
8039 
exit_immediately_with_error_message(int state,int first_file)8040 static void exit_immediately_with_error_message(int state, int first_file)
8041 {
8042     char *buf = 0;
8043     char *buf2 = 0;
8044 
8045     if (first_file) {
8046 	/* print statusline messages as a hint, if any */
8047 	LYstatusline_messages_on_exit(&buf2);
8048     }
8049 
8050     if (state == NOT_FOUND) {
8051 	HTSprintf0(&buf, "%s\n%s %s\n",
8052 		   NonNull(buf2),
8053 		   gettext("lynx: Can't access startfile"),
8054 	/*
8055 	 * hack: if we fail in HTAccess.c
8056 	 * avoid duplicating URL, oh.
8057 	 */
8058 		   (buf2 && strstr(buf2, gettext("Can't Access"))) ?
8059 		   "" : startfile);
8060     }
8061 
8062     if (state == NULLFILE) {
8063 	HTSprintf0(&buf, "%s\n%s\n%s\n",
8064 		   NonNull(buf2),
8065 		   gettext("lynx: Start file could not be found or is not text/html or text/plain"),
8066 		   gettext("      Exiting..."));
8067     }
8068 
8069     FREE(buf2);
8070 
8071     if (!dump_output_immediately)
8072 	cleanup();
8073 
8074     if (buf != 0) {
8075 #ifdef UNIX
8076 	if (dump_output_immediately) {
8077 	    fputs(buf, stderr);
8078 	} else
8079 #endif /* UNIX */
8080 	{
8081 	    SetOutputMode(O_TEXT);
8082 	    fputs(buf, stdout);
8083 	    SetOutputMode(O_BINARY);
8084 	}
8085 
8086 	FREE(buf);
8087     }
8088 
8089     if (!dump_output_immediately) {
8090 	exit_immediately(EXIT_FAILURE);
8091     }
8092     /* else: return(EXIT_FAILURE) in mainloop */
8093 }
8094 
status_link(char * curlink_name,int show_more,int show_indx)8095 static void status_link(char *curlink_name,
8096 			int show_more,
8097 			int show_indx)
8098 {
8099 #define MAX_STATUS (LYcolLimit - 1)
8100 #define MIN_STATUS 0
8101     char format[MAX_LINE];
8102     int prefix = 0;
8103     int length;
8104 
8105     *format = 0;
8106     if (show_more && !nomore) {
8107 	sprintf(format, "%.*s ",
8108 		(int) (sizeof(format) - 2),
8109 		gettext("-more-"));
8110 	prefix = (int) strlen(format);
8111     }
8112     if (show_indx) {
8113 	sprintf(format + prefix, "%.*s ",
8114 		((int) sizeof(format) - prefix - 2),
8115 		gettext("-index-"));
8116     }
8117     prefix = (int) strlen(format);
8118     length = (int) strlen(curlink_name);
8119 
8120     if (prefix > MAX_STATUS || prefix >= MAX_LINE - 1) {
8121 	_user_message("%s", format);	/* no room for url */
8122     } else {
8123 	sprintf(format + prefix, "%%.%ds", MAX_STATUS - prefix);
8124 
8125 	if ((length + prefix > MAX_STATUS) && long_url_ok) {
8126 	    char *buf = NULL;
8127 	    int cut_from_pos;
8128 	    int cut_to_pos;
8129 	    int n;
8130 
8131 	    StrAllocCopy(buf, curlink_name);
8132 	    /*
8133 	     * Scan to find the final leaf of the URL.  Ignore trailing '/'.
8134 	     */
8135 	    for (cut_to_pos = length - 2;
8136 		 (cut_to_pos > 0) && (buf[cut_to_pos] != '/');
8137 		 cut_to_pos--) ;
8138 	    /*
8139 	     * Jump back to the next leaf to remove.
8140 	     */
8141 	    for (cut_from_pos = cut_to_pos - 4;
8142 		 (cut_from_pos > 0) && ((buf[cut_from_pos] != '/')
8143 					|| ((prefix + cut_from_pos
8144 					     + 4
8145 					     + (length - cut_to_pos)) >= MAX_STATUS));
8146 		 cut_from_pos--) ;
8147 	    /*
8148 	     * Replace some leaves to '...', if possible, and put the final
8149 	     * leaf at the end.  We assume that one can recognize the link from
8150 	     * at least MIN_STATUS characters.
8151 	     */
8152 	    if (cut_from_pos > MIN_STATUS) {
8153 		for (n = 1; n <= 3; n++)
8154 		    buf[cut_from_pos + n] = '.';
8155 		for (n = 0; cut_to_pos + n <= length; n++)
8156 		    buf[cut_from_pos + 4 + n] = buf[cut_to_pos + n];
8157 	    }
8158 	    _user_message(format, buf);
8159 	    CTRACE((tfp, "lastline = %s\n", buf));	/* don't forget to erase me */
8160 	    FREE(buf);
8161 	} else {		/* show (possibly truncated) url */
8162 	    _user_message(format, curlink_name);
8163 	}
8164     }
8165 }
8166 
LYDownLoadAddress(void)8167 const char *LYDownLoadAddress(void)
8168 {
8169     return NonNull(newdoc.address);
8170 }
8171