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