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