1117395Skan/* $FreeBSD$ */
2132718Skan/*
3117395Skan * Copyright (C) 1984-2012  Mark Nudelman
4117395Skan *
5132718Skan * You may distribute under the terms of either the GNU General Public
6117395Skan * License or the Less License, as specified in the README file.
7132718Skan *
8132718Skan * For more information, see the README file.
9132718Skan */
10132718Skan
11117395Skan
12132718Skan/*
13132718Skan * User-level command processor.
14132718Skan */
15132718Skan
16117395Skan#include "less.h"
17132718Skan#if MSDOS_COMPILER==WIN32C
18132718Skan#include <windows.h>
19169689Skan#endif
20169689Skan#include "position.h"
21117395Skan#include "option.h"
22132718Skan#include "cmd.h"
23117395Skan
24117395Skanextern int erase_char, erase2_char, kill_char;
25132718Skanextern int sigs;
26117395Skanextern int quit_if_one_screen;
27117395Skanextern int squished;
28132718Skanextern int sc_width;
29117395Skanextern int sc_height;
30117395Skanextern int swindow;
31132718Skanextern int jump_sline;
32117395Skanextern int quitting;
33117395Skanextern int wscroll;
34117395Skanextern int top_scroll;
35117395Skanextern int ignore_eoi;
36117395Skanextern int secure;
37117395Skanextern int hshift;
38117395Skanextern int show_attn;
39117395Skanextern int less_is_more;
40132718Skanextern POSITION highest_hilite;
41117395Skanextern char *every_first_cmd;
42117395Skanextern char *curr_altfilename;
43132718Skanextern char version[];
44117395Skanextern struct scrpos initial_scrpos;
45117395Skanextern IFILE curr_ifile;
46132718Skanextern void constant *ml_search;
47117395Skanextern void constant *ml_examine;
48#if SHELL_ESCAPE || PIPEC
49extern void constant *ml_shell;
50#endif
51#if EDITOR
52extern char *editor;
53extern char *editproto;
54#endif
55extern int screen_trashed;	/* The screen has been overwritten */
56extern int shift_count;
57extern int oldbot;
58extern int forw_prompt;
59
60#if SHELL_ESCAPE
61static char *shellcmd = NULL;	/* For holding last shell command for "!!" */
62#endif
63static int mca;			/* The multicharacter command (action) */
64static int search_type;		/* The previous type of search */
65static LINENUM number;		/* The number typed by the user */
66static long fraction;		/* The fractional part of the number */
67static struct loption *curropt;
68static int opt_lower;
69static int optflag;
70static int optgetname;
71static POSITION bottompos;
72static int save_hshift;
73#if PIPEC
74static char pipec;
75#endif
76
77struct ungot {
78	struct ungot *ug_next;
79	char ug_char;
80};
81static struct ungot* ungot = NULL;
82static int unget_end = 0;
83
84static void multi_search();
85
86/*
87 * Move the cursor to start of prompt line before executing a command.
88 * This looks nicer if the command takes a long time before
89 * updating the screen.
90 */
91	static void
92cmd_exec()
93{
94#if HILITE_SEARCH
95	clear_attn();
96#endif
97	clear_bot();
98	flush();
99}
100
101/*
102 * Set up the display to start a new multi-character command.
103 */
104	static void
105start_mca(action, prompt, mlist, cmdflags)
106	int action;
107	constant char *prompt;
108	constant void *mlist;
109	int cmdflags;
110{
111	mca = action;
112	clear_bot();
113	clear_cmd();
114	cmd_putstr(prompt);
115	set_mlist(mlist, cmdflags);
116}
117
118	public int
119in_mca()
120{
121	return (mca != 0 && mca != A_PREFIX);
122}
123
124/*
125 * Set up the display to start a new search command.
126 */
127	static void
128mca_search()
129{
130#if HILITE_SEARCH
131	if (search_type & SRCH_FILTER)
132		mca = A_FILTER;
133	else
134#endif
135	if (search_type & SRCH_FORW)
136		mca = A_F_SEARCH;
137	else
138		mca = A_B_SEARCH;
139
140	clear_bot();
141	clear_cmd();
142
143	if (search_type & SRCH_NO_MATCH)
144		cmd_putstr("Non-match ");
145	if (search_type & SRCH_FIRST_FILE)
146		cmd_putstr("First-file ");
147	if (search_type & SRCH_PAST_EOF)
148		cmd_putstr("EOF-ignore ");
149	if (search_type & SRCH_NO_MOVE)
150		cmd_putstr("Keep-pos ");
151	if (search_type & SRCH_NO_REGEX)
152		cmd_putstr("Regex-off ");
153
154#if HILITE_SEARCH
155	if (search_type & SRCH_FILTER)
156		cmd_putstr("&/");
157	else
158#endif
159	if (search_type & SRCH_FORW)
160		cmd_putstr("/");
161	else
162		cmd_putstr("?");
163	set_mlist(ml_search, 0);
164}
165
166/*
167 * Set up the display to start a new toggle-option command.
168 */
169	static void
170mca_opt_toggle()
171{
172	int no_prompt;
173	int flag;
174	char *dash;
175
176	no_prompt = (optflag & OPT_NO_PROMPT);
177	flag = (optflag & ~OPT_NO_PROMPT);
178	dash = (flag == OPT_NO_TOGGLE) ? "_" : "-";
179
180	mca = A_OPT_TOGGLE;
181	clear_bot();
182	clear_cmd();
183	cmd_putstr(dash);
184	if (optgetname)
185		cmd_putstr(dash);
186	if (no_prompt)
187		cmd_putstr("(P)");
188	switch (flag)
189	{
190	case OPT_UNSET:
191		cmd_putstr("+");
192		break;
193	case OPT_SET:
194		cmd_putstr("!");
195		break;
196	}
197	set_mlist(NULL, 0);
198}
199
200/*
201 * Execute a multicharacter command.
202 */
203	static void
204exec_mca()
205{
206	register char *cbuf;
207
208	cmd_exec();
209	cbuf = get_cmdbuf();
210
211	switch (mca)
212	{
213	case A_F_SEARCH:
214	case A_B_SEARCH:
215		multi_search(cbuf, (int) number);
216		break;
217#if HILITE_SEARCH
218	case A_FILTER:
219		search_type ^= SRCH_NO_MATCH;
220		set_filter_pattern(cbuf, search_type);
221		break;
222#endif
223	case A_FIRSTCMD:
224		/*
225		 * Skip leading spaces or + signs in the string.
226		 */
227		while (*cbuf == '+' || *cbuf == ' ')
228			cbuf++;
229		if (every_first_cmd != NULL)
230			free(every_first_cmd);
231		if (*cbuf == '\0')
232			every_first_cmd = NULL;
233		else
234			every_first_cmd = save(cbuf);
235		break;
236	case A_OPT_TOGGLE:
237		toggle_option(curropt, opt_lower, cbuf, optflag);
238		curropt = NULL;
239		break;
240	case A_F_BRACKET:
241		match_brac(cbuf[0], cbuf[1], 1, (int) number);
242		break;
243	case A_B_BRACKET:
244		match_brac(cbuf[1], cbuf[0], 0, (int) number);
245		break;
246#if EXAMINE
247	case A_EXAMINE:
248		if (secure)
249			break;
250		edit_list(cbuf);
251#if TAGS
252		/* If tag structure is loaded then clean it up. */
253		cleantags();
254#endif
255		break;
256#endif
257#if SHELL_ESCAPE
258	case A_SHELL:
259		/*
260		 * !! just uses whatever is in shellcmd.
261		 * Otherwise, copy cmdbuf to shellcmd,
262		 * expanding any special characters ("%" or "#").
263		 */
264		if (*cbuf != '!')
265		{
266			if (shellcmd != NULL)
267				free(shellcmd);
268			shellcmd = fexpand(cbuf);
269		}
270
271		if (secure)
272			break;
273		if (shellcmd == NULL)
274			lsystem("", "!done");
275		else
276			lsystem(shellcmd, "!done");
277		break;
278#endif
279#if PIPEC
280	case A_PIPE:
281		if (secure)
282			break;
283		(void) pipe_mark(pipec, cbuf);
284		error("|done", NULL_PARG);
285		break;
286#endif
287	}
288}
289
290/*
291 * Is a character an erase or kill char?
292 */
293	static int
294is_erase_char(c)
295	int c;
296{
297	return (c == erase_char || c == erase2_char || c == kill_char);
298}
299
300/*
301 * Handle the first char of an option (after the initial dash).
302 */
303	static int
304mca_opt_first_char(c)
305    int c;
306{
307	int flag = (optflag & ~OPT_NO_PROMPT);
308	if (flag == OPT_NO_TOGGLE)
309	{
310		switch (c)
311		{
312		case '_':
313			/* "__" = long option name. */
314			optgetname = TRUE;
315			mca_opt_toggle();
316			return (MCA_MORE);
317		}
318	} else
319	{
320		switch (c)
321		{
322		case '+':
323			/* "-+" = UNSET. */
324			optflag = (flag == OPT_UNSET) ?
325				OPT_TOGGLE : OPT_UNSET;
326			mca_opt_toggle();
327			return (MCA_MORE);
328		case '!':
329			/* "-!" = SET */
330			optflag = (flag == OPT_SET) ?
331				OPT_TOGGLE : OPT_SET;
332			mca_opt_toggle();
333			return (MCA_MORE);
334		case CONTROL('P'):
335			optflag ^= OPT_NO_PROMPT;
336			mca_opt_toggle();
337			return (MCA_MORE);
338		case '-':
339			/* "--" = long option name. */
340			optgetname = TRUE;
341			mca_opt_toggle();
342			return (MCA_MORE);
343		}
344	}
345	/* Char was not handled here. */
346	return (NO_MCA);
347}
348
349/*
350 * Add a char to a long option name.
351 * See if we've got a match for an option name yet.
352 * If so, display the complete name and stop
353 * accepting chars until user hits RETURN.
354 */
355	static int
356mca_opt_nonfirst_char(c)
357	int c;
358{
359	char *p;
360	char *oname;
361
362	if (curropt != NULL)
363	{
364		/*
365		 * Already have a match for the name.
366		 * Don't accept anything but erase/kill.
367		 */
368		if (is_erase_char(c))
369			return (MCA_DONE);
370		return (MCA_MORE);
371	}
372	/*
373	 * Add char to cmd buffer and try to match
374	 * the option name.
375	 */
376	if (cmd_char(c) == CC_QUIT)
377		return (MCA_DONE);
378	p = get_cmdbuf();
379	opt_lower = ASCII_IS_LOWER(p[0]);
380	curropt = findopt_name(&p, &oname, NULL);
381	if (curropt != NULL)
382	{
383		/*
384		 * Got a match.
385		 * Remember the option and
386		 * display the full option name.
387		 */
388		cmd_reset();
389		mca_opt_toggle();
390		for (p = oname;  *p != '\0';  p++)
391		{
392			c = *p;
393			if (!opt_lower && ASCII_IS_LOWER(c))
394				c = ASCII_TO_UPPER(c);
395			if (cmd_char(c) != CC_OK)
396				return (MCA_DONE);
397		}
398	}
399	return (MCA_MORE);
400}
401
402/*
403 * Handle a char of an option toggle command.
404 */
405	static int
406mca_opt_char(c)
407	int c;
408{
409	PARG parg;
410
411	/*
412	 * This may be a short option (single char),
413	 * or one char of a long option name,
414	 * or one char of the option parameter.
415	 */
416	if (curropt == NULL && len_cmdbuf() == 0)
417	{
418		int ret = mca_opt_first_char(c);
419		if (ret != NO_MCA)
420			return (ret);
421	}
422	if (optgetname)
423	{
424		/* We're getting a long option name.  */
425		if (c != '\n' && c != '\r')
426			return (mca_opt_nonfirst_char(c));
427		if (curropt == NULL)
428		{
429			parg.p_string = get_cmdbuf();
430			error("There is no --%s option", &parg);
431			return (MCA_DONE);
432		}
433		optgetname = FALSE;
434		cmd_reset();
435	} else
436	{
437		if (is_erase_char(c))
438			return (NO_MCA);
439		if (curropt != NULL)
440			/* We're getting the option parameter. */
441			return (NO_MCA);
442		curropt = findopt(c);
443		if (curropt == NULL)
444		{
445			parg.p_string = propt(c);
446			error("There is no %s option", &parg);
447			return (MCA_DONE);
448		}
449	}
450	/*
451	 * If the option which was entered does not take a
452	 * parameter, toggle the option immediately,
453	 * so user doesn't have to hit RETURN.
454	 */
455	if ((optflag & ~OPT_NO_PROMPT) != OPT_TOGGLE ||
456	    !opt_has_param(curropt))
457	{
458		toggle_option(curropt, ASCII_IS_LOWER(c), "", optflag);
459		return (MCA_DONE);
460	}
461	/*
462	 * Display a prompt appropriate for the option parameter.
463	 */
464	start_mca(A_OPT_TOGGLE, opt_prompt(curropt), (void*)NULL, 0);
465	return (MCA_MORE);
466}
467
468/*
469 * Handle a char of a search command.
470 */
471	static int
472mca_search_char(c)
473	int c;
474{
475	int flag = 0;
476
477	/*
478	 * Certain characters as the first char of
479	 * the pattern have special meaning:
480	 *	!  Toggle the NO_MATCH flag
481	 *	*  Toggle the PAST_EOF flag
482	 *	@  Toggle the FIRST_FILE flag
483	 */
484	if (len_cmdbuf() > 0)
485		return (NO_MCA);
486
487	switch (c)
488	{
489	case '*':
490		if (less_is_more)
491			break;
492	case CONTROL('E'): /* ignore END of file */
493		if (mca != A_FILTER)
494			flag = SRCH_PAST_EOF;
495		break;
496	case '@':
497		if (less_is_more)
498			break;
499	case CONTROL('F'): /* FIRST file */
500		if (mca != A_FILTER)
501			flag = SRCH_FIRST_FILE;
502		break;
503	case CONTROL('K'): /* KEEP position */
504		if (mca != A_FILTER)
505			flag = SRCH_NO_MOVE;
506		break;
507	case CONTROL('R'): /* Don't use REGULAR EXPRESSIONS */
508		flag = SRCH_NO_REGEX;
509		break;
510	case CONTROL('N'): /* NOT match */
511	case '!':
512		flag = SRCH_NO_MATCH;
513		break;
514	}
515
516	if (flag != 0)
517	{
518		search_type ^= flag;
519		mca_search();
520		return (MCA_MORE);
521	}
522	return (NO_MCA);
523}
524
525/*
526 * Handle a character of a multi-character command.
527 */
528	static int
529mca_char(c)
530	int c;
531{
532	int ret;
533
534	switch (mca)
535	{
536	case 0:
537		/*
538		 * We're not in a multicharacter command.
539		 */
540		return (NO_MCA);
541
542	case A_PREFIX:
543		/*
544		 * In the prefix of a command.
545		 * This not considered a multichar command
546		 * (even tho it uses cmdbuf, etc.).
547		 * It is handled in the commands() switch.
548		 */
549		return (NO_MCA);
550
551	case A_DIGIT:
552		/*
553		 * Entering digits of a number.
554		 * Terminated by a non-digit.
555		 */
556		if (!((c >= '0' && c <= '9') || c == '.') &&
557		  editchar(c, EC_PEEK|EC_NOHISTORY|EC_NOCOMPLETE|EC_NORIGHTLEFT) == A_INVALID)
558		{
559			/*
560			 * Not part of the number.
561			 * End the number and treat this char
562			 * as a normal command character.
563			 */
564			number = cmd_int(&fraction);
565			mca = 0;
566			cmd_accept();
567			return (NO_MCA);
568		}
569		break;
570
571	case A_OPT_TOGGLE:
572		ret = mca_opt_char(c);
573		if (ret != NO_MCA)
574			return (ret);
575		break;
576
577	case A_F_SEARCH:
578	case A_B_SEARCH:
579	case A_FILTER:
580		ret = mca_search_char(c);
581		if (ret != NO_MCA)
582			return (ret);
583		break;
584
585	default:
586		/* Other multicharacter command. */
587		break;
588	}
589
590	/*
591	 * The multichar command is terminated by a newline.
592	 */
593	if (c == '\n' || c == '\r')
594	{
595		/*
596		 * Execute the command.
597		 */
598		exec_mca();
599		return (MCA_DONE);
600	}
601
602	/*
603	 * Append the char to the command buffer.
604	 */
605	if (cmd_char(c) == CC_QUIT)
606		/*
607		 * Abort the multi-char command.
608		 */
609		return (MCA_DONE);
610
611	if ((mca == A_F_BRACKET || mca == A_B_BRACKET) && len_cmdbuf() >= 2)
612	{
613		/*
614		 * Special case for the bracket-matching commands.
615		 * Execute the command after getting exactly two
616		 * characters from the user.
617		 */
618		exec_mca();
619		return (MCA_DONE);
620	}
621
622	/*
623	 * Need another character.
624	 */
625	return (MCA_MORE);
626}
627
628/*
629 * Discard any buffered file data.
630 */
631	static void
632clear_buffers()
633{
634	if (!(ch_getflags() & CH_CANSEEK))
635		return;
636	ch_flush();
637	clr_linenum();
638#if HILITE_SEARCH
639	clr_hilite();
640#endif
641}
642
643/*
644 * Make sure the screen is displayed.
645 */
646	static void
647make_display()
648{
649	/*
650	 * If nothing is displayed yet, display starting from initial_scrpos.
651	 */
652	if (empty_screen())
653	{
654		if (initial_scrpos.pos == NULL_POSITION)
655			/*
656			 * {{ Maybe this should be:
657			 *    jump_loc(ch_zero(), jump_sline);
658			 *    but this behavior seems rather unexpected
659			 *    on the first screen. }}
660			 */
661			jump_loc(ch_zero(), 1);
662		else
663			jump_loc(initial_scrpos.pos, initial_scrpos.ln);
664	} else if (screen_trashed)
665	{
666		int save_top_scroll = top_scroll;
667		int save_ignore_eoi = ignore_eoi;
668		top_scroll = 1;
669		ignore_eoi = 0;
670		if (screen_trashed == 2)
671		{
672			/* Special case used by ignore_eoi: re-open the input file
673			 * and jump to the end of the file. */
674			reopen_curr_ifile();
675			jump_forw();
676		}
677		repaint();
678		top_scroll = save_top_scroll;
679		ignore_eoi = save_ignore_eoi;
680	}
681}
682
683/*
684 * Display the appropriate prompt.
685 */
686	static void
687prompt()
688{
689	register constant char *p;
690
691	if (ungot != NULL)
692	{
693		/*
694		 * No prompt necessary if commands are from
695		 * ungotten chars rather than from the user.
696		 */
697		return;
698	}
699
700	/*
701	 * Make sure the screen is displayed.
702	 */
703	make_display();
704	bottompos = position(BOTTOM_PLUS_ONE);
705
706	/*
707	 * If we've hit EOF on the last file and the -E flag is set, quit.
708	 */
709	if (get_quit_at_eof() == OPT_ONPLUS &&
710	    eof_displayed() && !(ch_getflags() & CH_HELPFILE) &&
711	    next_ifile(curr_ifile) == NULL_IFILE)
712		quit(QUIT_OK);
713
714	/*
715	 * If the entire file is displayed and the -F flag is set, quit.
716	 */
717	if (quit_if_one_screen &&
718	    entire_file_displayed() && !(ch_getflags() & CH_HELPFILE) &&
719	    next_ifile(curr_ifile) == NULL_IFILE)
720		quit(QUIT_OK);
721
722#if MSDOS_COMPILER==WIN32C
723	/*
724	 * In Win32, display the file name in the window title.
725	 */
726	if (!(ch_getflags() & CH_HELPFILE))
727		SetConsoleTitle(pr_expand("Less?f - %f.", 0));
728#endif
729	/*
730	 * Select the proper prompt and display it.
731	 */
732	/*
733	 * If the previous action was a forward movement,
734	 * don't clear the bottom line of the display;
735	 * just print the prompt since the forward movement guarantees
736	 * that we're in the right position to display the prompt.
737	 * Clearing the line could cause a problem: for example, if the last
738	 * line displayed ended at the right screen edge without a newline,
739	 * then clearing would clear the last displayed line rather than
740	 * the prompt line.
741	 */
742	if (!forw_prompt)
743		clear_bot();
744	clear_cmd();
745	forw_prompt = 0;
746	p = pr_string();
747	if (is_filtering())
748		putstr("& ");
749	if (p == NULL || *p == '\0')
750		putchr(':');
751	else
752	{
753		at_enter(AT_STANDOUT);
754		putstr(p);
755		at_exit();
756	}
757	clear_eol();
758}
759
760/*
761 * Display the less version message.
762 */
763	public void
764dispversion()
765{
766	PARG parg;
767
768	parg.p_string = version;
769	error("less %s", &parg);
770}
771
772/*
773 * Get command character.
774 * The character normally comes from the keyboard,
775 * but may come from ungotten characters
776 * (characters previously given to ungetcc or ungetsc).
777 */
778	public int
779getcc()
780{
781	if (unget_end)
782	{
783		/*
784		 * We have just run out of ungotten chars.
785		 */
786		unget_end = 0;
787		if (len_cmdbuf() == 0 || !empty_screen())
788			return (getchr());
789		/*
790		 * Command is incomplete, so try to complete it.
791		 */
792		switch (mca)
793		{
794		case A_DIGIT:
795			/*
796			 * We have a number but no command.  Treat as #g.
797			 */
798			return ('g');
799
800		case A_F_SEARCH:
801		case A_B_SEARCH:
802			/*
803			 * We have "/string" but no newline.  Add the \n.
804			 */
805			return ('\n');
806
807		default:
808			/*
809			 * Some other incomplete command.  Let user complete it.
810			 */
811			return (getchr());
812		}
813	}
814
815	if (ungot == NULL)
816	{
817		/*
818		 * Normal case: no ungotten chars, so get one from the user.
819		 */
820		return (getchr());
821	}
822
823	/*
824	 * Return the next ungotten char.
825	 */
826	{
827		struct ungot *ug = ungot;
828		char c = ug->ug_char;
829		ungot = ug->ug_next;
830		free(ug);
831		unget_end = (ungot == NULL);
832		return (c);
833	}
834}
835
836/*
837 * "Unget" a command character.
838 * The next getcc() will return this character.
839 */
840	public void
841ungetcc(c)
842	int c;
843{
844	struct ungot *ug = (struct ungot *) ecalloc(1, sizeof(struct ungot));
845
846	ug->ug_char = c;
847	ug->ug_next = ungot;
848	ungot = ug;
849	unget_end = 0;
850}
851
852/*
853 * Unget a whole string of command characters.
854 * The next sequence of getcc()'s will return this string.
855 */
856	public void
857ungetsc(s)
858	char *s;
859{
860	register char *p;
861
862	for (p = s + strlen(s) - 1;  p >= s;  p--)
863		ungetcc(*p);
864}
865
866/*
867 * Search for a pattern, possibly in multiple files.
868 * If SRCH_FIRST_FILE is set, begin searching at the first file.
869 * If SRCH_PAST_EOF is set, continue the search thru multiple files.
870 */
871	static void
872multi_search(pattern, n)
873	char *pattern;
874	int n;
875{
876	register int nomore;
877	IFILE save_ifile;
878	int changed_file;
879
880	changed_file = 0;
881	save_ifile = save_curr_ifile();
882
883	if (search_type & SRCH_FIRST_FILE)
884	{
885		/*
886		 * Start at the first (or last) file
887		 * in the command line list.
888		 */
889		if (search_type & SRCH_FORW)
890			nomore = edit_first();
891		else
892			nomore = edit_last();
893		if (nomore)
894		{
895			unsave_ifile(save_ifile);
896			return;
897		}
898		changed_file = 1;
899		search_type &= ~SRCH_FIRST_FILE;
900	}
901
902	for (;;)
903	{
904		n = search(search_type, pattern, n);
905		/*
906		 * The SRCH_NO_MOVE flag doesn't "stick": it gets cleared
907		 * after being used once.  This allows "n" to work after
908		 * using a /@@ search.
909		 */
910		search_type &= ~SRCH_NO_MOVE;
911		if (n == 0)
912		{
913			/*
914			 * Found it.
915			 */
916			unsave_ifile(save_ifile);
917			return;
918		}
919
920		if (n < 0)
921			/*
922			 * Some kind of error in the search.
923			 * Error message has been printed by search().
924			 */
925			break;
926
927		if ((search_type & SRCH_PAST_EOF) == 0)
928			/*
929			 * We didn't find a match, but we're
930			 * supposed to search only one file.
931			 */
932			break;
933		/*
934		 * Move on to the next file.
935		 */
936		if (search_type & SRCH_FORW)
937			nomore = edit_next(1);
938		else
939			nomore = edit_prev(1);
940		if (nomore)
941			break;
942		changed_file = 1;
943	}
944
945	/*
946	 * Didn't find it.
947	 * Print an error message if we haven't already.
948	 */
949	if (n > 0)
950		error("Pattern not found", NULL_PARG);
951
952	if (changed_file)
953	{
954		/*
955		 * Restore the file we were originally viewing.
956		 */
957		reedit_ifile(save_ifile);
958	} else
959	{
960		unsave_ifile(save_ifile);
961	}
962}
963
964/*
965 * Forward forever, or until a highlighted line appears.
966 */
967	static int
968forw_loop(until_hilite)
969	int until_hilite;
970{
971	POSITION curr_len;
972
973	if (ch_getflags() & CH_HELPFILE)
974		return (A_NOACTION);
975
976	cmd_exec();
977	jump_forw();
978	curr_len = ch_length();
979	highest_hilite = until_hilite ? curr_len : NULL_POSITION;
980	ignore_eoi = 1;
981	while (!sigs)
982	{
983		if (until_hilite && highest_hilite > curr_len)
984		{
985			bell();
986			break;
987		}
988		make_display();
989		forward(1, 0, 0);
990	}
991	ignore_eoi = 0;
992	ch_set_eof();
993
994	/*
995	 * This gets us back in "F mode" after processing
996	 * a non-abort signal (e.g. window-change).
997	 */
998	if (sigs && !ABORT_SIGS())
999		return (until_hilite ? A_F_UNTIL_HILITE : A_F_FOREVER);
1000
1001	return (A_NOACTION);
1002}
1003
1004/*
1005 * Main command processor.
1006 * Accept and execute commands until a quit command.
1007 */
1008	public void
1009commands()
1010{
1011	register int c;
1012	register int action;
1013	register char *cbuf;
1014	int newaction;
1015	int save_search_type;
1016	char *extra;
1017	char tbuf[2];
1018	PARG parg;
1019	IFILE old_ifile;
1020	IFILE new_ifile;
1021	char *tagfile;
1022	int until_hilite = 0;
1023
1024	search_type = SRCH_FORW;
1025	wscroll = (sc_height + 1) / 2;
1026	newaction = A_NOACTION;
1027
1028	for (;;)
1029	{
1030		mca = 0;
1031		cmd_accept();
1032		number = 0;
1033		curropt = NULL;
1034
1035		/*
1036		 * See if any signals need processing.
1037		 */
1038		if (sigs)
1039		{
1040			psignals();
1041			if (quitting)
1042				quit(QUIT_SAVED_STATUS);
1043		}
1044
1045		/*
1046		 * See if window size changed, for systems that don't
1047		 * generate SIGWINCH.
1048		 */
1049		check_winch();
1050
1051		/*
1052		 * Display prompt and accept a character.
1053		 */
1054		cmd_reset();
1055		prompt();
1056		if (sigs)
1057			continue;
1058		if (newaction == A_NOACTION)
1059			c = getcc();
1060
1061	again:
1062		if (sigs)
1063			continue;
1064
1065		if (newaction != A_NOACTION)
1066		{
1067			action = newaction;
1068			newaction = A_NOACTION;
1069		} else
1070		{
1071			/*
1072			 * If we are in a multicharacter command, call mca_char.
1073			 * Otherwise we call fcmd_decode to determine the
1074			 * action to be performed.
1075			 */
1076			if (mca)
1077				switch (mca_char(c))
1078				{
1079				case MCA_MORE:
1080					/*
1081					 * Need another character.
1082					 */
1083					c = getcc();
1084					goto again;
1085				case MCA_DONE:
1086					/*
1087					 * Command has been handled by mca_char.
1088					 * Start clean with a prompt.
1089					 */
1090					continue;
1091				case NO_MCA:
1092					/*
1093					 * Not a multi-char command
1094					 * (at least, not anymore).
1095					 */
1096					break;
1097				}
1098
1099			/*
1100			 * Decode the command character and decide what to do.
1101			 */
1102			if (mca)
1103			{
1104				/*
1105				 * We're in a multichar command.
1106				 * Add the character to the command buffer
1107				 * and display it on the screen.
1108				 * If the user backspaces past the start
1109				 * of the line, abort the command.
1110				 */
1111				if (cmd_char(c) == CC_QUIT || len_cmdbuf() == 0)
1112					continue;
1113				cbuf = get_cmdbuf();
1114			} else
1115			{
1116				/*
1117				 * Don't use cmd_char if we're starting fresh
1118				 * at the beginning of a command, because we
1119				 * don't want to echo the command until we know
1120				 * it is a multichar command.  We also don't
1121				 * want erase_char/kill_char to be treated
1122				 * as line editing characters.
1123				 */
1124				tbuf[0] = c;
1125				tbuf[1] = '\0';
1126				cbuf = tbuf;
1127			}
1128			extra = NULL;
1129			action = fcmd_decode(cbuf, &extra);
1130			/*
1131			 * If an "extra" string was returned,
1132			 * process it as a string of command characters.
1133			 */
1134			if (extra != NULL)
1135				ungetsc(extra);
1136		}
1137		/*
1138		 * Clear the cmdbuf string.
1139		 * (But not if we're in the prefix of a command,
1140		 * because the partial command string is kept there.)
1141		 */
1142		if (action != A_PREFIX)
1143			cmd_reset();
1144
1145		switch (action)
1146		{
1147		case A_DIGIT:
1148			/*
1149			 * First digit of a number.
1150			 */
1151			start_mca(A_DIGIT, ":", (void*)NULL, CF_QUIT_ON_ERASE);
1152			goto again;
1153
1154		case A_F_WINDOW:
1155			/*
1156			 * Forward one window (and set the window size).
1157			 */
1158			if (number > 0)
1159				swindow = (int) number;
1160			/* FALLTHRU */
1161		case A_F_SCREEN:
1162			/*
1163			 * Forward one screen.
1164			 */
1165			if (number <= 0)
1166				number = get_swindow();
1167			cmd_exec();
1168			if (show_attn)
1169				set_attnpos(bottompos);
1170			forward((int) number, 0, 1);
1171			break;
1172
1173		case A_B_WINDOW:
1174			/*
1175			 * Backward one window (and set the window size).
1176			 */
1177			if (number > 0)
1178				swindow = (int) number;
1179			/* FALLTHRU */
1180		case A_B_SCREEN:
1181			/*
1182			 * Backward one screen.
1183			 */
1184			if (number <= 0)
1185				number = get_swindow();
1186			cmd_exec();
1187			backward((int) number, 0, 1);
1188			break;
1189
1190		case A_F_LINE:
1191			/*
1192			 * Forward N (default 1) line.
1193			 */
1194			if (number <= 0)
1195				number = 1;
1196			cmd_exec();
1197			if (show_attn == OPT_ONPLUS && number > 1)
1198				set_attnpos(bottompos);
1199			forward((int) number, 0, 0);
1200			break;
1201
1202		case A_B_LINE:
1203			/*
1204			 * Backward N (default 1) line.
1205			 */
1206			if (number <= 0)
1207				number = 1;
1208			cmd_exec();
1209			backward((int) number, 0, 0);
1210			break;
1211
1212		case A_FF_LINE:
1213			/*
1214			 * Force forward N (default 1) line.
1215			 */
1216			if (number <= 0)
1217				number = 1;
1218			cmd_exec();
1219			if (show_attn == OPT_ONPLUS && number > 1)
1220				set_attnpos(bottompos);
1221			forward((int) number, 1, 0);
1222			break;
1223
1224		case A_BF_LINE:
1225			/*
1226			 * Force backward N (default 1) line.
1227			 */
1228			if (number <= 0)
1229				number = 1;
1230			cmd_exec();
1231			backward((int) number, 1, 0);
1232			break;
1233
1234		case A_FF_SCREEN:
1235			/*
1236			 * Force forward one screen.
1237			 */
1238			if (number <= 0)
1239				number = get_swindow();
1240			cmd_exec();
1241			if (show_attn == OPT_ONPLUS)
1242				set_attnpos(bottompos);
1243			forward((int) number, 1, 0);
1244			break;
1245
1246		case A_F_FOREVER:
1247			/*
1248			 * Forward forever, ignoring EOF.
1249			 */
1250			newaction = forw_loop(0);
1251			break;
1252
1253		case A_F_UNTIL_HILITE:
1254			newaction = forw_loop(1);
1255			break;
1256
1257		case A_F_SCROLL:
1258			/*
1259			 * Forward N lines
1260			 * (default same as last 'd' or 'u' command).
1261			 */
1262			if (number > 0)
1263				wscroll = (int) number;
1264			cmd_exec();
1265			if (show_attn == OPT_ONPLUS)
1266				set_attnpos(bottompos);
1267			forward(wscroll, 0, 0);
1268			break;
1269
1270		case A_B_SCROLL:
1271			/*
1272			 * Forward N lines
1273			 * (default same as last 'd' or 'u' command).
1274			 */
1275			if (number > 0)
1276				wscroll = (int) number;
1277			cmd_exec();
1278			backward(wscroll, 0, 0);
1279			break;
1280
1281		case A_FREPAINT:
1282			/*
1283			 * Flush buffers, then repaint screen.
1284			 * Don't flush the buffers on a pipe!
1285			 */
1286			clear_buffers();
1287			/* FALLTHRU */
1288		case A_REPAINT:
1289			/*
1290			 * Repaint screen.
1291			 */
1292			cmd_exec();
1293			repaint();
1294			break;
1295
1296		case A_GOLINE:
1297			/*
1298			 * Go to line N, default beginning of file.
1299			 */
1300			if (number <= 0)
1301				number = 1;
1302			cmd_exec();
1303			jump_back(number);
1304			break;
1305
1306		case A_PERCENT:
1307			/*
1308			 * Go to a specified percentage into the file.
1309			 */
1310			if (number < 0)
1311			{
1312				number = 0;
1313				fraction = 0;
1314			}
1315			if (number > 100)
1316			{
1317				number = 100;
1318				fraction = 0;
1319			}
1320			cmd_exec();
1321			jump_percent((int) number, fraction);
1322			break;
1323
1324		case A_GOEND:
1325			/*
1326			 * Go to line N, default end of file.
1327			 */
1328			cmd_exec();
1329			if (number <= 0)
1330				jump_forw();
1331			else
1332				jump_back(number);
1333			break;
1334
1335		case A_GOPOS:
1336			/*
1337			 * Go to a specified byte position in the file.
1338			 */
1339			cmd_exec();
1340			if (number < 0)
1341				number = 0;
1342			jump_line_loc((POSITION) number, jump_sline);
1343			break;
1344
1345		case A_STAT:
1346			/*
1347			 * Print file name, etc.
1348			 */
1349			if (ch_getflags() & CH_HELPFILE)
1350				break;
1351			cmd_exec();
1352			parg.p_string = eq_message();
1353			error("%s", &parg);
1354			break;
1355
1356		case A_VERSION:
1357			/*
1358			 * Print version number, without the "@(#)".
1359			 */
1360			cmd_exec();
1361			dispversion();
1362			break;
1363
1364		case A_QUIT:
1365			/*
1366			 * Exit.
1367			 */
1368			if (curr_ifile != NULL_IFILE &&
1369			    ch_getflags() & CH_HELPFILE)
1370			{
1371				/*
1372				 * Quit while viewing the help file
1373				 * just means return to viewing the
1374				 * previous file.
1375				 */
1376				hshift = save_hshift;
1377				if (edit_prev(1) == 0)
1378					break;
1379			}
1380			if (extra != NULL)
1381				quit(*extra);
1382			quit(QUIT_OK);
1383			break;
1384
1385/*
1386 * Define abbreviation for a commonly used sequence below.
1387 */
1388#define	DO_SEARCH() \
1389			if (number <= 0) number = 1;	\
1390			mca_search();			\
1391			cmd_exec();			\
1392			multi_search((char *)NULL, (int) number);
1393
1394
1395		case A_F_SEARCH:
1396			/*
1397			 * Search forward for a pattern.
1398			 * Get the first char of the pattern.
1399			 */
1400			search_type = SRCH_FORW;
1401			if (number <= 0)
1402				number = 1;
1403			mca_search();
1404			c = getcc();
1405			goto again;
1406
1407		case A_B_SEARCH:
1408			/*
1409			 * Search backward for a pattern.
1410			 * Get the first char of the pattern.
1411			 */
1412			search_type = SRCH_BACK;
1413			if (number <= 0)
1414				number = 1;
1415			mca_search();
1416			c = getcc();
1417			goto again;
1418
1419		case A_FILTER:
1420#if HILITE_SEARCH
1421			search_type = SRCH_FORW | SRCH_FILTER;
1422			mca_search();
1423			c = getcc();
1424			goto again;
1425#else
1426			error("Command not available", NULL_PARG);
1427			break;
1428#endif
1429
1430		case A_AGAIN_SEARCH:
1431			/*
1432			 * Repeat previous search.
1433			 */
1434			DO_SEARCH();
1435			break;
1436
1437		case A_T_AGAIN_SEARCH:
1438			/*
1439			 * Repeat previous search, multiple files.
1440			 */
1441			search_type |= SRCH_PAST_EOF;
1442			DO_SEARCH();
1443			break;
1444
1445		case A_REVERSE_SEARCH:
1446			/*
1447			 * Repeat previous search, in reverse direction.
1448			 */
1449			save_search_type = search_type;
1450			search_type = SRCH_REVERSE(search_type);
1451			DO_SEARCH();
1452			search_type = save_search_type;
1453			break;
1454
1455		case A_T_REVERSE_SEARCH:
1456			/*
1457			 * Repeat previous search,
1458			 * multiple files in reverse direction.
1459			 */
1460			save_search_type = search_type;
1461			search_type = SRCH_REVERSE(search_type);
1462			search_type |= SRCH_PAST_EOF;
1463			DO_SEARCH();
1464			search_type = save_search_type;
1465			break;
1466
1467		case A_UNDO_SEARCH:
1468			undo_search();
1469			break;
1470
1471		case A_HELP:
1472			/*
1473			 * Help.
1474			 */
1475			if (ch_getflags() & CH_HELPFILE)
1476				break;
1477			cmd_exec();
1478			save_hshift = hshift;
1479			hshift = 0;
1480			(void) edit(FAKE_HELPFILE);
1481			break;
1482
1483		case A_EXAMINE:
1484#if EXAMINE
1485			/*
1486			 * Edit a new file.  Get the filename.
1487			 */
1488			if (secure)
1489			{
1490				error("Command not available", NULL_PARG);
1491				break;
1492			}
1493			start_mca(A_EXAMINE, "Examine: ", ml_examine, 0);
1494			c = getcc();
1495			goto again;
1496#else
1497			error("Command not available", NULL_PARG);
1498			break;
1499#endif
1500
1501		case A_VISUAL:
1502			/*
1503			 * Invoke an editor on the input file.
1504			 */
1505#if EDITOR
1506			if (secure)
1507			{
1508				error("Command not available", NULL_PARG);
1509				break;
1510			}
1511			if (ch_getflags() & CH_HELPFILE)
1512				break;
1513			if (strcmp(get_filename(curr_ifile), "-") == 0)
1514			{
1515				error("Cannot edit standard input", NULL_PARG);
1516				break;
1517			}
1518			if (curr_altfilename != NULL)
1519			{
1520				error("WARNING: This file was viewed via LESSOPEN",
1521					NULL_PARG);
1522			}
1523			start_mca(A_SHELL, "!", ml_shell, 0);
1524			/*
1525			 * Expand the editor prototype string
1526			 * and pass it to the system to execute.
1527			 * (Make sure the screen is displayed so the
1528			 * expansion of "+%lm" works.)
1529			 */
1530			make_display();
1531			cmd_exec();
1532			lsystem(pr_expand(editproto, 0), (char*)NULL);
1533			break;
1534#else
1535			error("Command not available", NULL_PARG);
1536			break;
1537#endif
1538
1539		case A_NEXT_FILE:
1540			/*
1541			 * Examine next file.
1542			 */
1543#if TAGS
1544			if (ntags())
1545			{
1546				error("No next file", NULL_PARG);
1547				break;
1548			}
1549#endif
1550			if (number <= 0)
1551				number = 1;
1552			if (edit_next((int) number))
1553			{
1554				if (get_quit_at_eof() && eof_displayed() &&
1555				    !(ch_getflags() & CH_HELPFILE))
1556					quit(QUIT_OK);
1557				parg.p_string = (number > 1) ? "(N-th) " : "";
1558				error("No %snext file", &parg);
1559			}
1560			break;
1561
1562		case A_PREV_FILE:
1563			/*
1564			 * Examine previous file.
1565			 */
1566#if TAGS
1567			if (ntags())
1568			{
1569				error("No previous file", NULL_PARG);
1570				break;
1571			}
1572#endif
1573			if (number <= 0)
1574				number = 1;
1575			if (edit_prev((int) number))
1576			{
1577				parg.p_string = (number > 1) ? "(N-th) " : "";
1578				error("No %sprevious file", &parg);
1579			}
1580			break;
1581
1582		case A_NEXT_TAG:
1583#if TAGS
1584			if (number <= 0)
1585				number = 1;
1586			tagfile = nexttag((int) number);
1587			if (tagfile == NULL)
1588			{
1589				error("No next tag", NULL_PARG);
1590				break;
1591			}
1592			if (edit(tagfile) == 0)
1593			{
1594				POSITION pos = tagsearch();
1595				if (pos != NULL_POSITION)
1596					jump_loc(pos, jump_sline);
1597			}
1598#else
1599			error("Command not available", NULL_PARG);
1600#endif
1601			break;
1602
1603		case A_PREV_TAG:
1604#if TAGS
1605			if (number <= 0)
1606				number = 1;
1607			tagfile = prevtag((int) number);
1608			if (tagfile == NULL)
1609			{
1610				error("No previous tag", NULL_PARG);
1611				break;
1612			}
1613			if (edit(tagfile) == 0)
1614			{
1615				POSITION pos = tagsearch();
1616				if (pos != NULL_POSITION)
1617					jump_loc(pos, jump_sline);
1618			}
1619#else
1620			error("Command not available", NULL_PARG);
1621#endif
1622			break;
1623
1624		case A_INDEX_FILE:
1625			/*
1626			 * Examine a particular file.
1627			 */
1628			if (number <= 0)
1629				number = 1;
1630			if (edit_index((int) number))
1631				error("No such file", NULL_PARG);
1632			break;
1633
1634		case A_REMOVE_FILE:
1635			if (ch_getflags() & CH_HELPFILE)
1636				break;
1637			old_ifile = curr_ifile;
1638			new_ifile = getoff_ifile(curr_ifile);
1639			if (new_ifile == NULL_IFILE)
1640			{
1641				bell();
1642				break;
1643			}
1644			if (edit_ifile(new_ifile) != 0)
1645			{
1646				reedit_ifile(old_ifile);
1647				break;
1648			}
1649			del_ifile(old_ifile);
1650			break;
1651
1652		case A_OPT_TOGGLE:
1653			optflag = OPT_TOGGLE;
1654			optgetname = FALSE;
1655			mca_opt_toggle();
1656			c = getcc();
1657			goto again;
1658
1659		case A_DISP_OPTION:
1660			/*
1661			 * Report a flag setting.
1662			 */
1663			optflag = OPT_NO_TOGGLE;
1664			optgetname = FALSE;
1665			mca_opt_toggle();
1666			c = getcc();
1667			goto again;
1668
1669		case A_FIRSTCMD:
1670			/*
1671			 * Set an initial command for new files.
1672			 */
1673			start_mca(A_FIRSTCMD, "+", (void*)NULL, 0);
1674			c = getcc();
1675			goto again;
1676
1677		case A_SHELL:
1678			/*
1679			 * Shell escape.
1680			 */
1681#if SHELL_ESCAPE
1682			if (secure)
1683			{
1684				error("Command not available", NULL_PARG);
1685				break;
1686			}
1687			start_mca(A_SHELL, "!", ml_shell, 0);
1688			c = getcc();
1689			goto again;
1690#else
1691			error("Command not available", NULL_PARG);
1692			break;
1693#endif
1694
1695		case A_SETMARK:
1696			/*
1697			 * Set a mark.
1698			 */
1699			if (ch_getflags() & CH_HELPFILE)
1700				break;
1701			start_mca(A_SETMARK, "mark: ", (void*)NULL, 0);
1702			c = getcc();
1703			if (c == erase_char || c == erase2_char ||
1704			    c == kill_char || c == '\n' || c == '\r')
1705				break;
1706			setmark(c);
1707			break;
1708
1709		case A_GOMARK:
1710			/*
1711			 * Go to a mark.
1712			 */
1713			start_mca(A_GOMARK, "goto mark: ", (void*)NULL, 0);
1714			c = getcc();
1715			if (c == erase_char || c == erase2_char ||
1716			    c == kill_char || c == '\n' || c == '\r')
1717				break;
1718			cmd_exec();
1719			gomark(c);
1720			break;
1721
1722		case A_PIPE:
1723#if PIPEC
1724			if (secure)
1725			{
1726				error("Command not available", NULL_PARG);
1727				break;
1728			}
1729			start_mca(A_PIPE, "|mark: ", (void*)NULL, 0);
1730			c = getcc();
1731			if (c == erase_char || c == erase2_char || c == kill_char)
1732				break;
1733			if (c == '\n' || c == '\r')
1734				c = '.';
1735			if (badmark(c))
1736				break;
1737			pipec = c;
1738			start_mca(A_PIPE, "!", ml_shell, 0);
1739			c = getcc();
1740			goto again;
1741#else
1742			error("Command not available", NULL_PARG);
1743			break;
1744#endif
1745
1746		case A_B_BRACKET:
1747		case A_F_BRACKET:
1748			start_mca(action, "Brackets: ", (void*)NULL, 0);
1749			c = getcc();
1750			goto again;
1751
1752		case A_LSHIFT:
1753			if (number > 0)
1754				shift_count = number;
1755			else
1756				number = (shift_count > 0) ?
1757					shift_count : sc_width / 2;
1758			if (number > hshift)
1759				number = hshift;
1760			hshift -= number;
1761			screen_trashed = 1;
1762			break;
1763
1764		case A_RSHIFT:
1765			if (number > 0)
1766				shift_count = number;
1767			else
1768				number = (shift_count > 0) ?
1769					shift_count : sc_width / 2;
1770			hshift += number;
1771			screen_trashed = 1;
1772			break;
1773
1774		case A_PREFIX:
1775			/*
1776			 * The command is incomplete (more chars are needed).
1777			 * Display the current char, so the user knows
1778			 * what's going on, and get another character.
1779			 */
1780			if (mca != A_PREFIX)
1781			{
1782				cmd_reset();
1783				start_mca(A_PREFIX, " ", (void*)NULL,
1784					CF_QUIT_ON_ERASE);
1785				(void) cmd_char(c);
1786			}
1787			c = getcc();
1788			goto again;
1789
1790		case A_NOACTION:
1791			break;
1792
1793		default:
1794			bell();
1795			break;
1796		}
1797	}
1798}
1799