command.c revision 229811
1/* $FreeBSD: stable/9/contrib/less/command.c 229811 2012-01-08 13:51:09Z dim $ */
2/*
3 * Copyright (C) 1984-2011  Mark Nudelman
4 *
5 * You may distribute under the terms of either the GNU General Public
6 * License or the Less License, as specified in the README file.
7 *
8 * For more information about less, or for information on how to
9 * contact the author, see the README file.
10 */
11
12
13/*
14 * User-level command processor.
15 */
16
17#include "less.h"
18#if MSDOS_COMPILER==WIN32C
19#include <windows.h>
20#endif
21#include "position.h"
22#include "option.h"
23#include "cmd.h"
24
25extern int erase_char, erase2_char, kill_char;
26extern int sigs;
27extern int quit_if_one_screen;
28extern int squished;
29extern int sc_width;
30extern int sc_height;
31extern int swindow;
32extern int jump_sline;
33extern int quitting;
34extern int wscroll;
35extern int top_scroll;
36extern int ignore_eoi;
37extern int secure;
38extern int hshift;
39extern int show_attn;
40extern int less_is_more;
41extern char *every_first_cmd;
42extern char *curr_altfilename;
43extern char version[];
44extern struct scrpos initial_scrpos;
45extern IFILE curr_ifile;
46extern void constant *ml_search;
47extern 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	char *prompt;
108	void constant *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 char constant *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 * Main command processor.
966 * Accept and execute commands until a quit command.
967 */
968	public void
969commands()
970{
971	register int c;
972	register int action;
973	register char *cbuf;
974	int newaction;
975	int save_search_type;
976	char *extra;
977	char tbuf[2];
978	PARG parg;
979	IFILE old_ifile;
980	IFILE new_ifile;
981	char *tagfile;
982
983	search_type = SRCH_FORW;
984	wscroll = (sc_height + 1) / 2;
985	newaction = A_NOACTION;
986
987	for (;;)
988	{
989		mca = 0;
990		cmd_accept();
991		number = 0;
992		curropt = NULL;
993
994		/*
995		 * See if any signals need processing.
996		 */
997		if (sigs)
998		{
999			psignals();
1000			if (quitting)
1001				quit(QUIT_SAVED_STATUS);
1002		}
1003
1004		/*
1005		 * See if window size changed, for systems that don't
1006		 * generate SIGWINCH.
1007		 */
1008		check_winch();
1009
1010		/*
1011		 * Display prompt and accept a character.
1012		 */
1013		cmd_reset();
1014		prompt();
1015		if (sigs)
1016			continue;
1017		if (newaction == A_NOACTION)
1018			c = getcc();
1019
1020	again:
1021		if (sigs)
1022			continue;
1023
1024		if (newaction != A_NOACTION)
1025		{
1026			action = newaction;
1027			newaction = A_NOACTION;
1028		} else
1029		{
1030			/*
1031			 * If we are in a multicharacter command, call mca_char.
1032			 * Otherwise we call fcmd_decode to determine the
1033			 * action to be performed.
1034			 */
1035			if (mca)
1036				switch (mca_char(c))
1037				{
1038				case MCA_MORE:
1039					/*
1040					 * Need another character.
1041					 */
1042					c = getcc();
1043					goto again;
1044				case MCA_DONE:
1045					/*
1046					 * Command has been handled by mca_char.
1047					 * Start clean with a prompt.
1048					 */
1049					continue;
1050				case NO_MCA:
1051					/*
1052					 * Not a multi-char command
1053					 * (at least, not anymore).
1054					 */
1055					break;
1056				}
1057
1058			/*
1059			 * Decode the command character and decide what to do.
1060			 */
1061			if (mca)
1062			{
1063				/*
1064				 * We're in a multichar command.
1065				 * Add the character to the command buffer
1066				 * and display it on the screen.
1067				 * If the user backspaces past the start
1068				 * of the line, abort the command.
1069				 */
1070				if (cmd_char(c) == CC_QUIT || len_cmdbuf() == 0)
1071					continue;
1072				cbuf = get_cmdbuf();
1073			} else
1074			{
1075				/*
1076				 * Don't use cmd_char if we're starting fresh
1077				 * at the beginning of a command, because we
1078				 * don't want to echo the command until we know
1079				 * it is a multichar command.  We also don't
1080				 * want erase_char/kill_char to be treated
1081				 * as line editing characters.
1082				 */
1083				tbuf[0] = c;
1084				tbuf[1] = '\0';
1085				cbuf = tbuf;
1086			}
1087			extra = NULL;
1088			action = fcmd_decode(cbuf, &extra);
1089			/*
1090			 * If an "extra" string was returned,
1091			 * process it as a string of command characters.
1092			 */
1093			if (extra != NULL)
1094				ungetsc(extra);
1095		}
1096		/*
1097		 * Clear the cmdbuf string.
1098		 * (But not if we're in the prefix of a command,
1099		 * because the partial command string is kept there.)
1100		 */
1101		if (action != A_PREFIX)
1102			cmd_reset();
1103
1104		switch (action)
1105		{
1106		case A_DIGIT:
1107			/*
1108			 * First digit of a number.
1109			 */
1110			start_mca(A_DIGIT, ":", (void*)NULL, CF_QUIT_ON_ERASE);
1111			goto again;
1112
1113		case A_F_WINDOW:
1114			/*
1115			 * Forward one window (and set the window size).
1116			 */
1117			if (number > 0)
1118				swindow = (int) number;
1119			/* FALLTHRU */
1120		case A_F_SCREEN:
1121			/*
1122			 * Forward one screen.
1123			 */
1124			if (number <= 0)
1125				number = get_swindow();
1126			cmd_exec();
1127			if (show_attn)
1128				set_attnpos(bottompos);
1129			forward((int) number, 0, 1);
1130			break;
1131
1132		case A_B_WINDOW:
1133			/*
1134			 * Backward one window (and set the window size).
1135			 */
1136			if (number > 0)
1137				swindow = (int) number;
1138			/* FALLTHRU */
1139		case A_B_SCREEN:
1140			/*
1141			 * Backward one screen.
1142			 */
1143			if (number <= 0)
1144				number = get_swindow();
1145			cmd_exec();
1146			backward((int) number, 0, 1);
1147			break;
1148
1149		case A_F_LINE:
1150			/*
1151			 * Forward N (default 1) line.
1152			 */
1153			if (number <= 0)
1154				number = 1;
1155			cmd_exec();
1156			if (show_attn == OPT_ONPLUS && number > 1)
1157				set_attnpos(bottompos);
1158			forward((int) number, 0, 0);
1159			break;
1160
1161		case A_B_LINE:
1162			/*
1163			 * Backward N (default 1) line.
1164			 */
1165			if (number <= 0)
1166				number = 1;
1167			cmd_exec();
1168			backward((int) number, 0, 0);
1169			break;
1170
1171		case A_FF_LINE:
1172			/*
1173			 * Force forward N (default 1) line.
1174			 */
1175			if (number <= 0)
1176				number = 1;
1177			cmd_exec();
1178			if (show_attn == OPT_ONPLUS && number > 1)
1179				set_attnpos(bottompos);
1180			forward((int) number, 1, 0);
1181			break;
1182
1183		case A_BF_LINE:
1184			/*
1185			 * Force backward N (default 1) line.
1186			 */
1187			if (number <= 0)
1188				number = 1;
1189			cmd_exec();
1190			backward((int) number, 1, 0);
1191			break;
1192
1193		case A_FF_SCREEN:
1194			/*
1195			 * Force forward one screen.
1196			 */
1197			if (number <= 0)
1198				number = get_swindow();
1199			cmd_exec();
1200			if (show_attn == OPT_ONPLUS)
1201				set_attnpos(bottompos);
1202			forward((int) number, 1, 0);
1203			break;
1204
1205		case A_F_FOREVER:
1206			/*
1207			 * Forward forever, ignoring EOF.
1208			 */
1209			if (ch_getflags() & CH_HELPFILE)
1210				break;
1211			cmd_exec();
1212			jump_forw();
1213			ignore_eoi = 1;
1214			while (!sigs)
1215			{
1216				make_display();
1217				forward(1, 0, 0);
1218			}
1219			ignore_eoi = 0;
1220			/*
1221			 * This gets us back in "F mode" after processing
1222			 * a non-abort signal (e.g. window-change).
1223			 */
1224			if (sigs && !ABORT_SIGS())
1225				newaction = A_F_FOREVER;
1226			break;
1227
1228		case A_F_SCROLL:
1229			/*
1230			 * Forward N lines
1231			 * (default same as last 'd' or 'u' command).
1232			 */
1233			if (number > 0)
1234				wscroll = (int) number;
1235			cmd_exec();
1236			if (show_attn == OPT_ONPLUS)
1237				set_attnpos(bottompos);
1238			forward(wscroll, 0, 0);
1239			break;
1240
1241		case A_B_SCROLL:
1242			/*
1243			 * Forward N lines
1244			 * (default same as last 'd' or 'u' command).
1245			 */
1246			if (number > 0)
1247				wscroll = (int) number;
1248			cmd_exec();
1249			backward(wscroll, 0, 0);
1250			break;
1251
1252		case A_FREPAINT:
1253			/*
1254			 * Flush buffers, then repaint screen.
1255			 * Don't flush the buffers on a pipe!
1256			 */
1257			clear_buffers();
1258			/* FALLTHRU */
1259		case A_REPAINT:
1260			/*
1261			 * Repaint screen.
1262			 */
1263			cmd_exec();
1264			repaint();
1265			break;
1266
1267		case A_GOLINE:
1268			/*
1269			 * Go to line N, default beginning of file.
1270			 */
1271			if (number <= 0)
1272				number = 1;
1273			cmd_exec();
1274			jump_back(number);
1275			break;
1276
1277		case A_PERCENT:
1278			/*
1279			 * Go to a specified percentage into the file.
1280			 */
1281			if (number < 0)
1282			{
1283				number = 0;
1284				fraction = 0;
1285			}
1286			if (number > 100)
1287			{
1288				number = 100;
1289				fraction = 0;
1290			}
1291			cmd_exec();
1292			jump_percent((int) number, fraction);
1293			break;
1294
1295		case A_GOEND:
1296			/*
1297			 * Go to line N, default end of file.
1298			 */
1299			cmd_exec();
1300			if (number <= 0)
1301				jump_forw();
1302			else
1303				jump_back(number);
1304			break;
1305
1306		case A_GOPOS:
1307			/*
1308			 * Go to a specified byte position in the file.
1309			 */
1310			cmd_exec();
1311			if (number < 0)
1312				number = 0;
1313			jump_line_loc((POSITION) number, jump_sline);
1314			break;
1315
1316		case A_STAT:
1317			/*
1318			 * Print file name, etc.
1319			 */
1320			if (ch_getflags() & CH_HELPFILE)
1321				break;
1322			cmd_exec();
1323			parg.p_string = eq_message();
1324			error("%s", &parg);
1325			break;
1326
1327		case A_VERSION:
1328			/*
1329			 * Print version number, without the "@(#)".
1330			 */
1331			cmd_exec();
1332			dispversion();
1333			break;
1334
1335		case A_QUIT:
1336			/*
1337			 * Exit.
1338			 */
1339			if (curr_ifile != NULL_IFILE &&
1340			    ch_getflags() & CH_HELPFILE)
1341			{
1342				/*
1343				 * Quit while viewing the help file
1344				 * just means return to viewing the
1345				 * previous file.
1346				 */
1347				hshift = save_hshift;
1348				if (edit_prev(1) == 0)
1349					break;
1350			}
1351			if (extra != NULL)
1352				quit(*extra);
1353			quit(QUIT_OK);
1354			break;
1355
1356/*
1357 * Define abbreviation for a commonly used sequence below.
1358 */
1359#define	DO_SEARCH() \
1360			if (number <= 0) number = 1;	\
1361			mca_search();			\
1362			cmd_exec();			\
1363			multi_search((char *)NULL, (int) number);
1364
1365
1366		case A_F_SEARCH:
1367			/*
1368			 * Search forward for a pattern.
1369			 * Get the first char of the pattern.
1370			 */
1371			search_type = SRCH_FORW;
1372			if (number <= 0)
1373				number = 1;
1374			mca_search();
1375			c = getcc();
1376			goto again;
1377
1378		case A_B_SEARCH:
1379			/*
1380			 * Search backward for a pattern.
1381			 * Get the first char of the pattern.
1382			 */
1383			search_type = SRCH_BACK;
1384			if (number <= 0)
1385				number = 1;
1386			mca_search();
1387			c = getcc();
1388			goto again;
1389
1390		case A_FILTER:
1391#if HILITE_SEARCH
1392			search_type = SRCH_FORW | SRCH_FILTER;
1393			mca_search();
1394			c = getcc();
1395			goto again;
1396#else
1397			error("Command not available", NULL_PARG);
1398			break;
1399#endif
1400
1401		case A_AGAIN_SEARCH:
1402			/*
1403			 * Repeat previous search.
1404			 */
1405			DO_SEARCH();
1406			break;
1407
1408		case A_T_AGAIN_SEARCH:
1409			/*
1410			 * Repeat previous search, multiple files.
1411			 */
1412			search_type |= SRCH_PAST_EOF;
1413			DO_SEARCH();
1414			break;
1415
1416		case A_REVERSE_SEARCH:
1417			/*
1418			 * Repeat previous search, in reverse direction.
1419			 */
1420			save_search_type = search_type;
1421			search_type = SRCH_REVERSE(search_type);
1422			DO_SEARCH();
1423			search_type = save_search_type;
1424			break;
1425
1426		case A_T_REVERSE_SEARCH:
1427			/*
1428			 * Repeat previous search,
1429			 * multiple files in reverse direction.
1430			 */
1431			save_search_type = search_type;
1432			search_type = SRCH_REVERSE(search_type);
1433			search_type |= SRCH_PAST_EOF;
1434			DO_SEARCH();
1435			search_type = save_search_type;
1436			break;
1437
1438		case A_UNDO_SEARCH:
1439			undo_search();
1440			break;
1441
1442		case A_HELP:
1443			/*
1444			 * Help.
1445			 */
1446			if (ch_getflags() & CH_HELPFILE)
1447				break;
1448			cmd_exec();
1449			save_hshift = hshift;
1450			hshift = 0;
1451			(void) edit(FAKE_HELPFILE);
1452			break;
1453
1454		case A_EXAMINE:
1455#if EXAMINE
1456			/*
1457			 * Edit a new file.  Get the filename.
1458			 */
1459			if (secure)
1460			{
1461				error("Command not available", NULL_PARG);
1462				break;
1463			}
1464			start_mca(A_EXAMINE, "Examine: ", ml_examine, 0);
1465			c = getcc();
1466			goto again;
1467#else
1468			error("Command not available", NULL_PARG);
1469			break;
1470#endif
1471
1472		case A_VISUAL:
1473			/*
1474			 * Invoke an editor on the input file.
1475			 */
1476#if EDITOR
1477			if (secure)
1478			{
1479				error("Command not available", NULL_PARG);
1480				break;
1481			}
1482			if (ch_getflags() & CH_HELPFILE)
1483				break;
1484			if (strcmp(get_filename(curr_ifile), "-") == 0)
1485			{
1486				error("Cannot edit standard input", NULL_PARG);
1487				break;
1488			}
1489			if (curr_altfilename != NULL)
1490			{
1491				error("WARNING: This file was viewed via LESSOPEN",
1492					NULL_PARG);
1493			}
1494			start_mca(A_SHELL, "!", ml_shell, 0);
1495			/*
1496			 * Expand the editor prototype string
1497			 * and pass it to the system to execute.
1498			 * (Make sure the screen is displayed so the
1499			 * expansion of "+%lm" works.)
1500			 */
1501			make_display();
1502			cmd_exec();
1503			lsystem(pr_expand(editproto, 0), (char*)NULL);
1504			break;
1505#else
1506			error("Command not available", NULL_PARG);
1507			break;
1508#endif
1509
1510		case A_NEXT_FILE:
1511			/*
1512			 * Examine next file.
1513			 */
1514#if TAGS
1515			if (ntags())
1516			{
1517				error("No next file", NULL_PARG);
1518				break;
1519			}
1520#endif
1521			if (number <= 0)
1522				number = 1;
1523			if (edit_next((int) number))
1524			{
1525				if (get_quit_at_eof() && eof_displayed() &&
1526				    !(ch_getflags() & CH_HELPFILE))
1527					quit(QUIT_OK);
1528				parg.p_string = (number > 1) ? "(N-th) " : "";
1529				error("No %snext file", &parg);
1530			}
1531			break;
1532
1533		case A_PREV_FILE:
1534			/*
1535			 * Examine previous file.
1536			 */
1537#if TAGS
1538			if (ntags())
1539			{
1540				error("No previous file", NULL_PARG);
1541				break;
1542			}
1543#endif
1544			if (number <= 0)
1545				number = 1;
1546			if (edit_prev((int) number))
1547			{
1548				parg.p_string = (number > 1) ? "(N-th) " : "";
1549				error("No %sprevious file", &parg);
1550			}
1551			break;
1552
1553		case A_NEXT_TAG:
1554#if TAGS
1555			if (number <= 0)
1556				number = 1;
1557			tagfile = nexttag((int) number);
1558			if (tagfile == NULL)
1559			{
1560				error("No next tag", NULL_PARG);
1561				break;
1562			}
1563			if (edit(tagfile) == 0)
1564			{
1565				POSITION pos = tagsearch();
1566				if (pos != NULL_POSITION)
1567					jump_loc(pos, jump_sline);
1568			}
1569#else
1570			error("Command not available", NULL_PARG);
1571#endif
1572			break;
1573
1574		case A_PREV_TAG:
1575#if TAGS
1576			if (number <= 0)
1577				number = 1;
1578			tagfile = prevtag((int) number);
1579			if (tagfile == NULL)
1580			{
1581				error("No previous tag", NULL_PARG);
1582				break;
1583			}
1584			if (edit(tagfile) == 0)
1585			{
1586				POSITION pos = tagsearch();
1587				if (pos != NULL_POSITION)
1588					jump_loc(pos, jump_sline);
1589			}
1590#else
1591			error("Command not available", NULL_PARG);
1592#endif
1593			break;
1594
1595		case A_INDEX_FILE:
1596			/*
1597			 * Examine a particular file.
1598			 */
1599			if (number <= 0)
1600				number = 1;
1601			if (edit_index((int) number))
1602				error("No such file", NULL_PARG);
1603			break;
1604
1605		case A_REMOVE_FILE:
1606			if (ch_getflags() & CH_HELPFILE)
1607				break;
1608			old_ifile = curr_ifile;
1609			new_ifile = getoff_ifile(curr_ifile);
1610			if (new_ifile == NULL_IFILE)
1611			{
1612				bell();
1613				break;
1614			}
1615			if (edit_ifile(new_ifile) != 0)
1616			{
1617				reedit_ifile(old_ifile);
1618				break;
1619			}
1620			del_ifile(old_ifile);
1621			break;
1622
1623		case A_OPT_TOGGLE:
1624			optflag = OPT_TOGGLE;
1625			optgetname = FALSE;
1626			mca_opt_toggle();
1627			c = getcc();
1628			goto again;
1629
1630		case A_DISP_OPTION:
1631			/*
1632			 * Report a flag setting.
1633			 */
1634			optflag = OPT_NO_TOGGLE;
1635			optgetname = FALSE;
1636			mca_opt_toggle();
1637			c = getcc();
1638			goto again;
1639
1640		case A_FIRSTCMD:
1641			/*
1642			 * Set an initial command for new files.
1643			 */
1644			start_mca(A_FIRSTCMD, "+", (void*)NULL, 0);
1645			c = getcc();
1646			goto again;
1647
1648		case A_SHELL:
1649			/*
1650			 * Shell escape.
1651			 */
1652#if SHELL_ESCAPE
1653			if (secure)
1654			{
1655				error("Command not available", NULL_PARG);
1656				break;
1657			}
1658			start_mca(A_SHELL, "!", ml_shell, 0);
1659			c = getcc();
1660			goto again;
1661#else
1662			error("Command not available", NULL_PARG);
1663			break;
1664#endif
1665
1666		case A_SETMARK:
1667			/*
1668			 * Set a mark.
1669			 */
1670			if (ch_getflags() & CH_HELPFILE)
1671				break;
1672			start_mca(A_SETMARK, "mark: ", (void*)NULL, 0);
1673			c = getcc();
1674			if (c == erase_char || c == erase2_char ||
1675			    c == kill_char || c == '\n' || c == '\r')
1676				break;
1677			setmark(c);
1678			break;
1679
1680		case A_GOMARK:
1681			/*
1682			 * Go to a mark.
1683			 */
1684			start_mca(A_GOMARK, "goto mark: ", (void*)NULL, 0);
1685			c = getcc();
1686			if (c == erase_char || c == erase2_char ||
1687			    c == kill_char || c == '\n' || c == '\r')
1688				break;
1689			cmd_exec();
1690			gomark(c);
1691			break;
1692
1693		case A_PIPE:
1694#if PIPEC
1695			if (secure)
1696			{
1697				error("Command not available", NULL_PARG);
1698				break;
1699			}
1700			start_mca(A_PIPE, "|mark: ", (void*)NULL, 0);
1701			c = getcc();
1702			if (c == erase_char || c == erase2_char || c == kill_char)
1703				break;
1704			if (c == '\n' || c == '\r')
1705				c = '.';
1706			if (badmark(c))
1707				break;
1708			pipec = c;
1709			start_mca(A_PIPE, "!", ml_shell, 0);
1710			c = getcc();
1711			goto again;
1712#else
1713			error("Command not available", NULL_PARG);
1714			break;
1715#endif
1716
1717		case A_B_BRACKET:
1718		case A_F_BRACKET:
1719			start_mca(action, "Brackets: ", (void*)NULL, 0);
1720			c = getcc();
1721			goto again;
1722
1723		case A_LSHIFT:
1724			if (number > 0)
1725				shift_count = number;
1726			else
1727				number = (shift_count > 0) ?
1728					shift_count : sc_width / 2;
1729			if (number > hshift)
1730				number = hshift;
1731			hshift -= number;
1732			screen_trashed = 1;
1733			break;
1734
1735		case A_RSHIFT:
1736			if (number > 0)
1737				shift_count = number;
1738			else
1739				number = (shift_count > 0) ?
1740					shift_count : sc_width / 2;
1741			hshift += number;
1742			screen_trashed = 1;
1743			break;
1744
1745		case A_PREFIX:
1746			/*
1747			 * The command is incomplete (more chars are needed).
1748			 * Display the current char, so the user knows
1749			 * what's going on, and get another character.
1750			 */
1751			if (mca != A_PREFIX)
1752			{
1753				cmd_reset();
1754				start_mca(A_PREFIX, " ", (void*)NULL,
1755					CF_QUIT_ON_ERASE);
1756				(void) cmd_char(c);
1757			}
1758			c = getcc();
1759			goto again;
1760
1761		case A_NOACTION:
1762			break;
1763
1764		default:
1765			bell();
1766			break;
1767		}
1768	}
1769}
1770