xref: /freebsd/contrib/less/command.c (revision 10b59a9b4add0320d52c15ce057dd697261e7dfc)
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 	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
857 ungetsc(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
872 multi_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
969 commands()
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