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