xref: /freebsd/contrib/less/command.c (revision 884a2a699669ec61e2366e3e358342dbc94be24a)
1 /* $FreeBSD$ */
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 
25 extern int erase_char, erase2_char, kill_char;
26 extern int sigs;
27 extern int quit_if_one_screen;
28 extern int squished;
29 extern int sc_width;
30 extern int sc_height;
31 extern int swindow;
32 extern int jump_sline;
33 extern int quitting;
34 extern int wscroll;
35 extern int top_scroll;
36 extern int ignore_eoi;
37 extern int secure;
38 extern int hshift;
39 extern int show_attn;
40 extern int less_is_more;
41 extern char *every_first_cmd;
42 extern char *curr_altfilename;
43 extern char version[];
44 extern struct scrpos initial_scrpos;
45 extern IFILE curr_ifile;
46 extern void constant *ml_search;
47 extern void constant *ml_examine;
48 #if SHELL_ESCAPE || PIPEC
49 extern void constant *ml_shell;
50 #endif
51 #if EDITOR
52 extern char *editor;
53 extern char *editproto;
54 #endif
55 extern int screen_trashed;	/* The screen has been overwritten */
56 extern int shift_count;
57 extern int oldbot;
58 extern int forw_prompt;
59 
60 #if SHELL_ESCAPE
61 static char *shellcmd = NULL;	/* For holding last shell command for "!!" */
62 #endif
63 static int mca;			/* The multicharacter command (action) */
64 static int search_type;		/* The previous type of search */
65 static LINENUM number;		/* The number typed by the user */
66 static long fraction;		/* The fractional part of the number */
67 static struct loption *curropt;
68 static int opt_lower;
69 static int optflag;
70 static int optgetname;
71 static POSITION bottompos;
72 static int save_hshift;
73 #if PIPEC
74 static char pipec;
75 #endif
76 
77 struct ungot {
78 	struct ungot *ug_next;
79 	char ug_char;
80 };
81 static struct ungot* ungot = NULL;
82 static int unget_end = 0;
83 
84 static 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
92 cmd_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
105 start_mca(action, prompt, mlist, cmdflags)
106 	int action;
107 	char *prompt;
108 	void *mlist;
109 	int cmdflags;
110 {
111 	mca = action;
112 	clear_bot();
113 	clear_cmd();
114 	cmd_putstr(prompt);
115 	set_mlist(mlist, cmdflags);
116 }
117 
118 	public int
119 in_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
128 mca_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
170 mca_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
204 exec_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
294 is_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
304 mca_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
356 mca_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
406 mca_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
472 mca_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
529 mca_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
632 clear_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
647 make_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
687 prompt()
688 {
689 	register char *p;
690 
691 	if (ungot != NULL)
692 	{
693 		/*
694 		 * No prompt necessary if commands are from
695 		 * ungotten chars rather than from the user.
696 		 */
697 		return;
698 	}
699 
700 	/*
701 	 * Make sure the screen is displayed.
702 	 */
703 	make_display();
704 	bottompos = position(BOTTOM_PLUS_ONE);
705 
706 	/*
707 	 * If we've hit EOF on the last file and the -E flag is set, quit.
708 	 */
709 	if (get_quit_at_eof() == OPT_ONPLUS &&
710 	    eof_displayed() && !(ch_getflags() & CH_HELPFILE) &&
711 	    next_ifile(curr_ifile) == NULL_IFILE)
712 		quit(QUIT_OK);
713 
714 	/*
715 	 * If the entire file is displayed and the -F flag is set, quit.
716 	 */
717 	if (quit_if_one_screen &&
718 	    entire_file_displayed() && !(ch_getflags() & CH_HELPFILE) &&
719 	    next_ifile(curr_ifile) == NULL_IFILE)
720 		quit(QUIT_OK);
721 
722 #if MSDOS_COMPILER==WIN32C
723 	/*
724 	 * In Win32, display the file name in the window title.
725 	 */
726 	if (!(ch_getflags() & CH_HELPFILE))
727 		SetConsoleTitle(pr_expand("Less?f - %f.", 0));
728 #endif
729 	/*
730 	 * Select the proper prompt and display it.
731 	 */
732 	/*
733 	 * If the previous action was a forward movement,
734 	 * don't clear the bottom line of the display;
735 	 * just print the prompt since the forward movement guarantees
736 	 * that we're in the right position to display the prompt.
737 	 * Clearing the line could cause a problem: for example, if the last
738 	 * line displayed ended at the right screen edge without a newline,
739 	 * then clearing would clear the last displayed line rather than
740 	 * the prompt line.
741 	 */
742 	if (!forw_prompt)
743 		clear_bot();
744 	clear_cmd();
745 	forw_prompt = 0;
746 	p = pr_string();
747 	if (is_filtering())
748 		putstr("& ");
749 	if (p == NULL || *p == '\0')
750 		putchr(':');
751 	else
752 	{
753 		at_enter(AT_STANDOUT);
754 		putstr(p);
755 		at_exit();
756 	}
757 	clear_eol();
758 }
759 
760 /*
761  * Display the less version message.
762  */
763 	public void
764 dispversion()
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
779 getcc()
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
841 ungetcc(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 }
850 
851 /*
852  * Unget a whole string of command characters.
853  * The next sequence of getcc()'s will return this string.
854  */
855 	public void
856 ungetsc(s)
857 	char *s;
858 {
859 	register char *p;
860 
861 	for (p = s + strlen(s) - 1;  p >= s;  p--)
862 		ungetcc(*p);
863 }
864 
865 /*
866  * Search for a pattern, possibly in multiple files.
867  * If SRCH_FIRST_FILE is set, begin searching at the first file.
868  * If SRCH_PAST_EOF is set, continue the search thru multiple files.
869  */
870 	static void
871 multi_search(pattern, n)
872 	char *pattern;
873 	int n;
874 {
875 	register int nomore;
876 	IFILE save_ifile;
877 	int changed_file;
878 
879 	changed_file = 0;
880 	save_ifile = save_curr_ifile();
881 
882 	if (search_type & SRCH_FIRST_FILE)
883 	{
884 		/*
885 		 * Start at the first (or last) file
886 		 * in the command line list.
887 		 */
888 		if (search_type & SRCH_FORW)
889 			nomore = edit_first();
890 		else
891 			nomore = edit_last();
892 		if (nomore)
893 		{
894 			unsave_ifile(save_ifile);
895 			return;
896 		}
897 		changed_file = 1;
898 		search_type &= ~SRCH_FIRST_FILE;
899 	}
900 
901 	for (;;)
902 	{
903 		n = search(search_type, pattern, n);
904 		/*
905 		 * The SRCH_NO_MOVE flag doesn't "stick": it gets cleared
906 		 * after being used once.  This allows "n" to work after
907 		 * using a /@@ search.
908 		 */
909 		search_type &= ~SRCH_NO_MOVE;
910 		if (n == 0)
911 		{
912 			/*
913 			 * Found it.
914 			 */
915 			unsave_ifile(save_ifile);
916 			return;
917 		}
918 
919 		if (n < 0)
920 			/*
921 			 * Some kind of error in the search.
922 			 * Error message has been printed by search().
923 			 */
924 			break;
925 
926 		if ((search_type & SRCH_PAST_EOF) == 0)
927 			/*
928 			 * We didn't find a match, but we're
929 			 * supposed to search only one file.
930 			 */
931 			break;
932 		/*
933 		 * Move on to the next file.
934 		 */
935 		if (search_type & SRCH_FORW)
936 			nomore = edit_next(1);
937 		else
938 			nomore = edit_prev(1);
939 		if (nomore)
940 			break;
941 		changed_file = 1;
942 	}
943 
944 	/*
945 	 * Didn't find it.
946 	 * Print an error message if we haven't already.
947 	 */
948 	if (n > 0)
949 		error("Pattern not found", NULL_PARG);
950 
951 	if (changed_file)
952 	{
953 		/*
954 		 * Restore the file we were originally viewing.
955 		 */
956 		reedit_ifile(save_ifile);
957 	} else
958 	{
959 		unsave_ifile(save_ifile);
960 	}
961 }
962 
963 /*
964  * Main command processor.
965  * Accept and execute commands until a quit command.
966  */
967 	public void
968 commands()
969 {
970 	register int c;
971 	register int action;
972 	register char *cbuf;
973 	int newaction;
974 	int save_search_type;
975 	char *extra;
976 	char tbuf[2];
977 	PARG parg;
978 	IFILE old_ifile;
979 	IFILE new_ifile;
980 	char *tagfile;
981 
982 	search_type = SRCH_FORW;
983 	wscroll = (sc_height + 1) / 2;
984 	newaction = A_NOACTION;
985 
986 	for (;;)
987 	{
988 		mca = 0;
989 		cmd_accept();
990 		number = 0;
991 		curropt = NULL;
992 
993 		/*
994 		 * See if any signals need processing.
995 		 */
996 		if (sigs)
997 		{
998 			psignals();
999 			if (quitting)
1000 				quit(QUIT_SAVED_STATUS);
1001 		}
1002 
1003 		/*
1004 		 * See if window size changed, for systems that don't
1005 		 * generate SIGWINCH.
1006 		 */
1007 		check_winch();
1008 
1009 		/*
1010 		 * Display prompt and accept a character.
1011 		 */
1012 		cmd_reset();
1013 		prompt();
1014 		if (sigs)
1015 			continue;
1016 		if (newaction == A_NOACTION)
1017 			c = getcc();
1018 
1019 	again:
1020 		if (sigs)
1021 			continue;
1022 
1023 		if (newaction != A_NOACTION)
1024 		{
1025 			action = newaction;
1026 			newaction = A_NOACTION;
1027 		} else
1028 		{
1029 			/*
1030 			 * If we are in a multicharacter command, call mca_char.
1031 			 * Otherwise we call fcmd_decode to determine the
1032 			 * action to be performed.
1033 			 */
1034 			if (mca)
1035 				switch (mca_char(c))
1036 				{
1037 				case MCA_MORE:
1038 					/*
1039 					 * Need another character.
1040 					 */
1041 					c = getcc();
1042 					goto again;
1043 				case MCA_DONE:
1044 					/*
1045 					 * Command has been handled by mca_char.
1046 					 * Start clean with a prompt.
1047 					 */
1048 					continue;
1049 				case NO_MCA:
1050 					/*
1051 					 * Not a multi-char command
1052 					 * (at least, not anymore).
1053 					 */
1054 					break;
1055 				}
1056 
1057 			/*
1058 			 * Decode the command character and decide what to do.
1059 			 */
1060 			if (mca)
1061 			{
1062 				/*
1063 				 * We're in a multichar command.
1064 				 * Add the character to the command buffer
1065 				 * and display it on the screen.
1066 				 * If the user backspaces past the start
1067 				 * of the line, abort the command.
1068 				 */
1069 				if (cmd_char(c) == CC_QUIT || len_cmdbuf() == 0)
1070 					continue;
1071 				cbuf = get_cmdbuf();
1072 			} else
1073 			{
1074 				/*
1075 				 * Don't use cmd_char if we're starting fresh
1076 				 * at the beginning of a command, because we
1077 				 * don't want to echo the command until we know
1078 				 * it is a multichar command.  We also don't
1079 				 * want erase_char/kill_char to be treated
1080 				 * as line editing characters.
1081 				 */
1082 				tbuf[0] = c;
1083 				tbuf[1] = '\0';
1084 				cbuf = tbuf;
1085 			}
1086 			extra = NULL;
1087 			action = fcmd_decode(cbuf, &extra);
1088 			/*
1089 			 * If an "extra" string was returned,
1090 			 * process it as a string of command characters.
1091 			 */
1092 			if (extra != NULL)
1093 				ungetsc(extra);
1094 		}
1095 		/*
1096 		 * Clear the cmdbuf string.
1097 		 * (But not if we're in the prefix of a command,
1098 		 * because the partial command string is kept there.)
1099 		 */
1100 		if (action != A_PREFIX)
1101 			cmd_reset();
1102 
1103 		switch (action)
1104 		{
1105 		case A_DIGIT:
1106 			/*
1107 			 * First digit of a number.
1108 			 */
1109 			start_mca(A_DIGIT, ":", (void*)NULL, CF_QUIT_ON_ERASE);
1110 			goto again;
1111 
1112 		case A_F_WINDOW:
1113 			/*
1114 			 * Forward one window (and set the window size).
1115 			 */
1116 			if (number > 0)
1117 				swindow = (int) number;
1118 			/* FALLTHRU */
1119 		case A_F_SCREEN:
1120 			/*
1121 			 * Forward one screen.
1122 			 */
1123 			if (number <= 0)
1124 				number = get_swindow();
1125 			cmd_exec();
1126 			if (show_attn)
1127 				set_attnpos(bottompos);
1128 			forward((int) number, 0, 1);
1129 			break;
1130 
1131 		case A_B_WINDOW:
1132 			/*
1133 			 * Backward one window (and set the window size).
1134 			 */
1135 			if (number > 0)
1136 				swindow = (int) number;
1137 			/* FALLTHRU */
1138 		case A_B_SCREEN:
1139 			/*
1140 			 * Backward one screen.
1141 			 */
1142 			if (number <= 0)
1143 				number = get_swindow();
1144 			cmd_exec();
1145 			backward((int) number, 0, 1);
1146 			break;
1147 
1148 		case A_F_LINE:
1149 			/*
1150 			 * Forward N (default 1) line.
1151 			 */
1152 			if (number <= 0)
1153 				number = 1;
1154 			cmd_exec();
1155 			if (show_attn == OPT_ONPLUS && number > 1)
1156 				set_attnpos(bottompos);
1157 			forward((int) number, 0, 0);
1158 			break;
1159 
1160 		case A_B_LINE:
1161 			/*
1162 			 * Backward N (default 1) line.
1163 			 */
1164 			if (number <= 0)
1165 				number = 1;
1166 			cmd_exec();
1167 			backward((int) number, 0, 0);
1168 			break;
1169 
1170 		case A_FF_LINE:
1171 			/*
1172 			 * Force forward N (default 1) line.
1173 			 */
1174 			if (number <= 0)
1175 				number = 1;
1176 			cmd_exec();
1177 			if (show_attn == OPT_ONPLUS && number > 1)
1178 				set_attnpos(bottompos);
1179 			forward((int) number, 1, 0);
1180 			break;
1181 
1182 		case A_BF_LINE:
1183 			/*
1184 			 * Force backward N (default 1) line.
1185 			 */
1186 			if (number <= 0)
1187 				number = 1;
1188 			cmd_exec();
1189 			backward((int) number, 1, 0);
1190 			break;
1191 
1192 		case A_FF_SCREEN:
1193 			/*
1194 			 * Force forward one screen.
1195 			 */
1196 			if (number <= 0)
1197 				number = get_swindow();
1198 			cmd_exec();
1199 			if (show_attn == OPT_ONPLUS)
1200 				set_attnpos(bottompos);
1201 			forward((int) number, 1, 0);
1202 			break;
1203 
1204 		case A_F_FOREVER:
1205 			/*
1206 			 * Forward forever, ignoring EOF.
1207 			 */
1208 			if (ch_getflags() & CH_HELPFILE)
1209 				break;
1210 			cmd_exec();
1211 			jump_forw();
1212 			ignore_eoi = 1;
1213 			while (!sigs)
1214 			{
1215 				make_display();
1216 				forward(1, 0, 0);
1217 			}
1218 			ignore_eoi = 0;
1219 			/*
1220 			 * This gets us back in "F mode" after processing
1221 			 * a non-abort signal (e.g. window-change).
1222 			 */
1223 			if (sigs && !ABORT_SIGS())
1224 				newaction = A_F_FOREVER;
1225 			break;
1226 
1227 		case A_F_SCROLL:
1228 			/*
1229 			 * Forward N lines
1230 			 * (default same as last 'd' or 'u' command).
1231 			 */
1232 			if (number > 0)
1233 				wscroll = (int) number;
1234 			cmd_exec();
1235 			if (show_attn == OPT_ONPLUS)
1236 				set_attnpos(bottompos);
1237 			forward(wscroll, 0, 0);
1238 			break;
1239 
1240 		case A_B_SCROLL:
1241 			/*
1242 			 * Forward N lines
1243 			 * (default same as last 'd' or 'u' command).
1244 			 */
1245 			if (number > 0)
1246 				wscroll = (int) number;
1247 			cmd_exec();
1248 			backward(wscroll, 0, 0);
1249 			break;
1250 
1251 		case A_FREPAINT:
1252 			/*
1253 			 * Flush buffers, then repaint screen.
1254 			 * Don't flush the buffers on a pipe!
1255 			 */
1256 			clear_buffers();
1257 			/* FALLTHRU */
1258 		case A_REPAINT:
1259 			/*
1260 			 * Repaint screen.
1261 			 */
1262 			cmd_exec();
1263 			repaint();
1264 			break;
1265 
1266 		case A_GOLINE:
1267 			/*
1268 			 * Go to line N, default beginning of file.
1269 			 */
1270 			if (number <= 0)
1271 				number = 1;
1272 			cmd_exec();
1273 			jump_back(number);
1274 			break;
1275 
1276 		case A_PERCENT:
1277 			/*
1278 			 * Go to a specified percentage into the file.
1279 			 */
1280 			if (number < 0)
1281 			{
1282 				number = 0;
1283 				fraction = 0;
1284 			}
1285 			if (number > 100)
1286 			{
1287 				number = 100;
1288 				fraction = 0;
1289 			}
1290 			cmd_exec();
1291 			jump_percent((int) number, fraction);
1292 			break;
1293 
1294 		case A_GOEND:
1295 			/*
1296 			 * Go to line N, default end of file.
1297 			 */
1298 			cmd_exec();
1299 			if (number <= 0)
1300 				jump_forw();
1301 			else
1302 				jump_back(number);
1303 			break;
1304 
1305 		case A_GOPOS:
1306 			/*
1307 			 * Go to a specified byte position in the file.
1308 			 */
1309 			cmd_exec();
1310 			if (number < 0)
1311 				number = 0;
1312 			jump_line_loc((POSITION) number, jump_sline);
1313 			break;
1314 
1315 		case A_STAT:
1316 			/*
1317 			 * Print file name, etc.
1318 			 */
1319 			if (ch_getflags() & CH_HELPFILE)
1320 				break;
1321 			cmd_exec();
1322 			parg.p_string = eq_message();
1323 			error("%s", &parg);
1324 			break;
1325 
1326 		case A_VERSION:
1327 			/*
1328 			 * Print version number, without the "@(#)".
1329 			 */
1330 			cmd_exec();
1331 			dispversion();
1332 			break;
1333 
1334 		case A_QUIT:
1335 			/*
1336 			 * Exit.
1337 			 */
1338 			if (curr_ifile != NULL_IFILE &&
1339 			    ch_getflags() & CH_HELPFILE)
1340 			{
1341 				/*
1342 				 * Quit while viewing the help file
1343 				 * just means return to viewing the
1344 				 * previous file.
1345 				 */
1346 				hshift = save_hshift;
1347 				if (edit_prev(1) == 0)
1348 					break;
1349 			}
1350 			if (extra != NULL)
1351 				quit(*extra);
1352 			quit(QUIT_OK);
1353 			break;
1354 
1355 /*
1356  * Define abbreviation for a commonly used sequence below.
1357  */
1358 #define	DO_SEARCH() \
1359 			if (number <= 0) number = 1;	\
1360 			mca_search();			\
1361 			cmd_exec();			\
1362 			multi_search((char *)NULL, (int) number);
1363 
1364 
1365 		case A_F_SEARCH:
1366 			/*
1367 			 * Search forward for a pattern.
1368 			 * Get the first char of the pattern.
1369 			 */
1370 			search_type = SRCH_FORW;
1371 			if (number <= 0)
1372 				number = 1;
1373 			mca_search();
1374 			c = getcc();
1375 			goto again;
1376 
1377 		case A_B_SEARCH:
1378 			/*
1379 			 * Search backward for a pattern.
1380 			 * Get the first char of the pattern.
1381 			 */
1382 			search_type = SRCH_BACK;
1383 			if (number <= 0)
1384 				number = 1;
1385 			mca_search();
1386 			c = getcc();
1387 			goto again;
1388 
1389 		case A_FILTER:
1390 #if HILITE_SEARCH
1391 			search_type = SRCH_FORW | SRCH_FILTER;
1392 			mca_search();
1393 			c = getcc();
1394 			goto again;
1395 #else
1396 			error("Command not available", NULL_PARG);
1397 			break;
1398 #endif
1399 
1400 		case A_AGAIN_SEARCH:
1401 			/*
1402 			 * Repeat previous search.
1403 			 */
1404 			DO_SEARCH();
1405 			break;
1406 
1407 		case A_T_AGAIN_SEARCH:
1408 			/*
1409 			 * Repeat previous search, multiple files.
1410 			 */
1411 			search_type |= SRCH_PAST_EOF;
1412 			DO_SEARCH();
1413 			break;
1414 
1415 		case A_REVERSE_SEARCH:
1416 			/*
1417 			 * Repeat previous search, in reverse direction.
1418 			 */
1419 			save_search_type = search_type;
1420 			search_type = SRCH_REVERSE(search_type);
1421 			DO_SEARCH();
1422 			search_type = save_search_type;
1423 			break;
1424 
1425 		case A_T_REVERSE_SEARCH:
1426 			/*
1427 			 * Repeat previous search,
1428 			 * multiple files in reverse direction.
1429 			 */
1430 			save_search_type = search_type;
1431 			search_type = SRCH_REVERSE(search_type);
1432 			search_type |= SRCH_PAST_EOF;
1433 			DO_SEARCH();
1434 			search_type = save_search_type;
1435 			break;
1436 
1437 		case A_UNDO_SEARCH:
1438 			undo_search();
1439 			break;
1440 
1441 		case A_HELP:
1442 			/*
1443 			 * Help.
1444 			 */
1445 			if (ch_getflags() & CH_HELPFILE)
1446 				break;
1447 			cmd_exec();
1448 			save_hshift = hshift;
1449 			hshift = 0;
1450 			(void) edit(FAKE_HELPFILE);
1451 			break;
1452 
1453 		case A_EXAMINE:
1454 #if EXAMINE
1455 			/*
1456 			 * Edit a new file.  Get the filename.
1457 			 */
1458 			if (secure)
1459 			{
1460 				error("Command not available", NULL_PARG);
1461 				break;
1462 			}
1463 			start_mca(A_EXAMINE, "Examine: ", ml_examine, 0);
1464 			c = getcc();
1465 			goto again;
1466 #else
1467 			error("Command not available", NULL_PARG);
1468 			break;
1469 #endif
1470 
1471 		case A_VISUAL:
1472 			/*
1473 			 * Invoke an editor on the input file.
1474 			 */
1475 #if EDITOR
1476 			if (secure)
1477 			{
1478 				error("Command not available", NULL_PARG);
1479 				break;
1480 			}
1481 			if (ch_getflags() & CH_HELPFILE)
1482 				break;
1483 			if (strcmp(get_filename(curr_ifile), "-") == 0)
1484 			{
1485 				error("Cannot edit standard input", NULL_PARG);
1486 				break;
1487 			}
1488 			if (curr_altfilename != NULL)
1489 			{
1490 				error("WARNING: This file was viewed via LESSOPEN",
1491 					NULL_PARG);
1492 			}
1493 			start_mca(A_SHELL, "!", ml_shell, 0);
1494 			/*
1495 			 * Expand the editor prototype string
1496 			 * and pass it to the system to execute.
1497 			 * (Make sure the screen is displayed so the
1498 			 * expansion of "+%lm" works.)
1499 			 */
1500 			make_display();
1501 			cmd_exec();
1502 			lsystem(pr_expand(editproto, 0), (char*)NULL);
1503 			break;
1504 #else
1505 			error("Command not available", NULL_PARG);
1506 			break;
1507 #endif
1508 
1509 		case A_NEXT_FILE:
1510 			/*
1511 			 * Examine next file.
1512 			 */
1513 #if TAGS
1514 			if (ntags())
1515 			{
1516 				error("No next file", NULL_PARG);
1517 				break;
1518 			}
1519 #endif
1520 			if (number <= 0)
1521 				number = 1;
1522 			if (edit_next((int) number))
1523 			{
1524 				if (get_quit_at_eof() && eof_displayed() &&
1525 				    !(ch_getflags() & CH_HELPFILE))
1526 					quit(QUIT_OK);
1527 				parg.p_string = (number > 1) ? "(N-th) " : "";
1528 				error("No %snext file", &parg);
1529 			}
1530 			break;
1531 
1532 		case A_PREV_FILE:
1533 			/*
1534 			 * Examine previous file.
1535 			 */
1536 #if TAGS
1537 			if (ntags())
1538 			{
1539 				error("No previous file", NULL_PARG);
1540 				break;
1541 			}
1542 #endif
1543 			if (number <= 0)
1544 				number = 1;
1545 			if (edit_prev((int) number))
1546 			{
1547 				parg.p_string = (number > 1) ? "(N-th) " : "";
1548 				error("No %sprevious file", &parg);
1549 			}
1550 			break;
1551 
1552 		case A_NEXT_TAG:
1553 #if TAGS
1554 			if (number <= 0)
1555 				number = 1;
1556 			tagfile = nexttag((int) number);
1557 			if (tagfile == NULL)
1558 			{
1559 				error("No next tag", NULL_PARG);
1560 				break;
1561 			}
1562 			if (edit(tagfile) == 0)
1563 			{
1564 				POSITION pos = tagsearch();
1565 				if (pos != NULL_POSITION)
1566 					jump_loc(pos, jump_sline);
1567 			}
1568 #else
1569 			error("Command not available", NULL_PARG);
1570 #endif
1571 			break;
1572 
1573 		case A_PREV_TAG:
1574 #if TAGS
1575 			if (number <= 0)
1576 				number = 1;
1577 			tagfile = prevtag((int) number);
1578 			if (tagfile == NULL)
1579 			{
1580 				error("No previous tag", NULL_PARG);
1581 				break;
1582 			}
1583 			if (edit(tagfile) == 0)
1584 			{
1585 				POSITION pos = tagsearch();
1586 				if (pos != NULL_POSITION)
1587 					jump_loc(pos, jump_sline);
1588 			}
1589 #else
1590 			error("Command not available", NULL_PARG);
1591 #endif
1592 			break;
1593 
1594 		case A_INDEX_FILE:
1595 			/*
1596 			 * Examine a particular file.
1597 			 */
1598 			if (number <= 0)
1599 				number = 1;
1600 			if (edit_index((int) number))
1601 				error("No such file", NULL_PARG);
1602 			break;
1603 
1604 		case A_REMOVE_FILE:
1605 			if (ch_getflags() & CH_HELPFILE)
1606 				break;
1607 			old_ifile = curr_ifile;
1608 			new_ifile = getoff_ifile(curr_ifile);
1609 			if (new_ifile == NULL_IFILE)
1610 			{
1611 				bell();
1612 				break;
1613 			}
1614 			if (edit_ifile(new_ifile) != 0)
1615 			{
1616 				reedit_ifile(old_ifile);
1617 				break;
1618 			}
1619 			del_ifile(old_ifile);
1620 			break;
1621 
1622 		case A_OPT_TOGGLE:
1623 			optflag = OPT_TOGGLE;
1624 			optgetname = FALSE;
1625 			mca_opt_toggle();
1626 			c = getcc();
1627 			goto again;
1628 
1629 		case A_DISP_OPTION:
1630 			/*
1631 			 * Report a flag setting.
1632 			 */
1633 			optflag = OPT_NO_TOGGLE;
1634 			optgetname = FALSE;
1635 			mca_opt_toggle();
1636 			c = getcc();
1637 			goto again;
1638 
1639 		case A_FIRSTCMD:
1640 			/*
1641 			 * Set an initial command for new files.
1642 			 */
1643 			start_mca(A_FIRSTCMD, "+", (void*)NULL, 0);
1644 			c = getcc();
1645 			goto again;
1646 
1647 		case A_SHELL:
1648 			/*
1649 			 * Shell escape.
1650 			 */
1651 #if SHELL_ESCAPE
1652 			if (secure)
1653 			{
1654 				error("Command not available", NULL_PARG);
1655 				break;
1656 			}
1657 			start_mca(A_SHELL, "!", ml_shell, 0);
1658 			c = getcc();
1659 			goto again;
1660 #else
1661 			error("Command not available", NULL_PARG);
1662 			break;
1663 #endif
1664 
1665 		case A_SETMARK:
1666 			/*
1667 			 * Set a mark.
1668 			 */
1669 			if (ch_getflags() & CH_HELPFILE)
1670 				break;
1671 			start_mca(A_SETMARK, "mark: ", (void*)NULL, 0);
1672 			c = getcc();
1673 			if (c == erase_char || c == erase2_char ||
1674 			    c == kill_char || c == '\n' || c == '\r')
1675 				break;
1676 			setmark(c);
1677 			break;
1678 
1679 		case A_GOMARK:
1680 			/*
1681 			 * Go to a mark.
1682 			 */
1683 			start_mca(A_GOMARK, "goto mark: ", (void*)NULL, 0);
1684 			c = getcc();
1685 			if (c == erase_char || c == erase2_char ||
1686 			    c == kill_char || c == '\n' || c == '\r')
1687 				break;
1688 			cmd_exec();
1689 			gomark(c);
1690 			break;
1691 
1692 		case A_PIPE:
1693 #if PIPEC
1694 			if (secure)
1695 			{
1696 				error("Command not available", NULL_PARG);
1697 				break;
1698 			}
1699 			start_mca(A_PIPE, "|mark: ", (void*)NULL, 0);
1700 			c = getcc();
1701 			if (c == erase_char || c == erase2_char || c == kill_char)
1702 				break;
1703 			if (c == '\n' || c == '\r')
1704 				c = '.';
1705 			if (badmark(c))
1706 				break;
1707 			pipec = c;
1708 			start_mca(A_PIPE, "!", ml_shell, 0);
1709 			c = getcc();
1710 			goto again;
1711 #else
1712 			error("Command not available", NULL_PARG);
1713 			break;
1714 #endif
1715 
1716 		case A_B_BRACKET:
1717 		case A_F_BRACKET:
1718 			start_mca(action, "Brackets: ", (void*)NULL, 0);
1719 			c = getcc();
1720 			goto again;
1721 
1722 		case A_LSHIFT:
1723 			if (number > 0)
1724 				shift_count = number;
1725 			else
1726 				number = (shift_count > 0) ?
1727 					shift_count : sc_width / 2;
1728 			if (number > hshift)
1729 				number = hshift;
1730 			hshift -= number;
1731 			screen_trashed = 1;
1732 			break;
1733 
1734 		case A_RSHIFT:
1735 			if (number > 0)
1736 				shift_count = number;
1737 			else
1738 				number = (shift_count > 0) ?
1739 					shift_count : sc_width / 2;
1740 			hshift += number;
1741 			screen_trashed = 1;
1742 			break;
1743 
1744 		case A_PREFIX:
1745 			/*
1746 			 * The command is incomplete (more chars are needed).
1747 			 * Display the current char, so the user knows
1748 			 * what's going on, and get another character.
1749 			 */
1750 			if (mca != A_PREFIX)
1751 			{
1752 				cmd_reset();
1753 				start_mca(A_PREFIX, " ", (void*)NULL,
1754 					CF_QUIT_ON_ERASE);
1755 				(void) cmd_char(c);
1756 			}
1757 			c = getcc();
1758 			goto again;
1759 
1760 		case A_NOACTION:
1761 			break;
1762 
1763 		default:
1764 			bell();
1765 			break;
1766 		}
1767 	}
1768 }
1769