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