xref: /freebsd/contrib/less/command.c (revision f4b37ed0f8b307b1f3f0f630ca725d68f1dff30d)
1 /* $FreeBSD$ */
2 /*
3  * Copyright (C) 1984-2012  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 show_attn;
39 extern int less_is_more;
40 extern POSITION highest_hilite;
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 	constant char *prompt;
108 	constant 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 constant 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  * Forward forever, or until a highlighted line appears.
966  */
967 	static int
968 forw_loop(until_hilite)
969 	int until_hilite;
970 {
971 	POSITION curr_len;
972 
973 	if (ch_getflags() & CH_HELPFILE)
974 		return (A_NOACTION);
975 
976 	cmd_exec();
977 	jump_forw();
978 	curr_len = ch_length();
979 	highest_hilite = until_hilite ? curr_len : NULL_POSITION;
980 	ignore_eoi = 1;
981 	while (!sigs)
982 	{
983 		if (until_hilite && highest_hilite > curr_len)
984 		{
985 			bell();
986 			break;
987 		}
988 		make_display();
989 		forward(1, 0, 0);
990 	}
991 	ignore_eoi = 0;
992 	ch_set_eof();
993 
994 	/*
995 	 * This gets us back in "F mode" after processing
996 	 * a non-abort signal (e.g. window-change).
997 	 */
998 	if (sigs && !ABORT_SIGS())
999 		return (until_hilite ? A_F_UNTIL_HILITE : A_F_FOREVER);
1000 
1001 	return (A_NOACTION);
1002 }
1003 
1004 /*
1005  * Main command processor.
1006  * Accept and execute commands until a quit command.
1007  */
1008 	public void
1009 commands()
1010 {
1011 	register int c;
1012 	register int action;
1013 	register char *cbuf;
1014 	int newaction;
1015 	int save_search_type;
1016 	char *extra;
1017 	char tbuf[2];
1018 	PARG parg;
1019 	IFILE old_ifile;
1020 	IFILE new_ifile;
1021 	char *tagfile;
1022 	int until_hilite = 0;
1023 
1024 	search_type = SRCH_FORW;
1025 	wscroll = (sc_height + 1) / 2;
1026 	newaction = A_NOACTION;
1027 
1028 	for (;;)
1029 	{
1030 		mca = 0;
1031 		cmd_accept();
1032 		number = 0;
1033 		curropt = NULL;
1034 
1035 		/*
1036 		 * See if any signals need processing.
1037 		 */
1038 		if (sigs)
1039 		{
1040 			psignals();
1041 			if (quitting)
1042 				quit(QUIT_SAVED_STATUS);
1043 		}
1044 
1045 		/*
1046 		 * See if window size changed, for systems that don't
1047 		 * generate SIGWINCH.
1048 		 */
1049 		check_winch();
1050 
1051 		/*
1052 		 * Display prompt and accept a character.
1053 		 */
1054 		cmd_reset();
1055 		prompt();
1056 		if (sigs)
1057 			continue;
1058 		if (newaction == A_NOACTION)
1059 			c = getcc();
1060 
1061 	again:
1062 		if (sigs)
1063 			continue;
1064 
1065 		if (newaction != A_NOACTION)
1066 		{
1067 			action = newaction;
1068 			newaction = A_NOACTION;
1069 		} else
1070 		{
1071 			/*
1072 			 * If we are in a multicharacter command, call mca_char.
1073 			 * Otherwise we call fcmd_decode to determine the
1074 			 * action to be performed.
1075 			 */
1076 			if (mca)
1077 				switch (mca_char(c))
1078 				{
1079 				case MCA_MORE:
1080 					/*
1081 					 * Need another character.
1082 					 */
1083 					c = getcc();
1084 					goto again;
1085 				case MCA_DONE:
1086 					/*
1087 					 * Command has been handled by mca_char.
1088 					 * Start clean with a prompt.
1089 					 */
1090 					continue;
1091 				case NO_MCA:
1092 					/*
1093 					 * Not a multi-char command
1094 					 * (at least, not anymore).
1095 					 */
1096 					break;
1097 				}
1098 
1099 			/*
1100 			 * Decode the command character and decide what to do.
1101 			 */
1102 			if (mca)
1103 			{
1104 				/*
1105 				 * We're in a multichar command.
1106 				 * Add the character to the command buffer
1107 				 * and display it on the screen.
1108 				 * If the user backspaces past the start
1109 				 * of the line, abort the command.
1110 				 */
1111 				if (cmd_char(c) == CC_QUIT || len_cmdbuf() == 0)
1112 					continue;
1113 				cbuf = get_cmdbuf();
1114 			} else
1115 			{
1116 				/*
1117 				 * Don't use cmd_char if we're starting fresh
1118 				 * at the beginning of a command, because we
1119 				 * don't want to echo the command until we know
1120 				 * it is a multichar command.  We also don't
1121 				 * want erase_char/kill_char to be treated
1122 				 * as line editing characters.
1123 				 */
1124 				tbuf[0] = c;
1125 				tbuf[1] = '\0';
1126 				cbuf = tbuf;
1127 			}
1128 			extra = NULL;
1129 			action = fcmd_decode(cbuf, &extra);
1130 			/*
1131 			 * If an "extra" string was returned,
1132 			 * process it as a string of command characters.
1133 			 */
1134 			if (extra != NULL)
1135 				ungetsc(extra);
1136 		}
1137 		/*
1138 		 * Clear the cmdbuf string.
1139 		 * (But not if we're in the prefix of a command,
1140 		 * because the partial command string is kept there.)
1141 		 */
1142 		if (action != A_PREFIX)
1143 			cmd_reset();
1144 
1145 		switch (action)
1146 		{
1147 		case A_DIGIT:
1148 			/*
1149 			 * First digit of a number.
1150 			 */
1151 			start_mca(A_DIGIT, ":", (void*)NULL, CF_QUIT_ON_ERASE);
1152 			goto again;
1153 
1154 		case A_F_WINDOW:
1155 			/*
1156 			 * Forward one window (and set the window size).
1157 			 */
1158 			if (number > 0)
1159 				swindow = (int) number;
1160 			/* FALLTHRU */
1161 		case A_F_SCREEN:
1162 			/*
1163 			 * Forward one screen.
1164 			 */
1165 			if (number <= 0)
1166 				number = get_swindow();
1167 			cmd_exec();
1168 			if (show_attn)
1169 				set_attnpos(bottompos);
1170 			forward((int) number, 0, 1);
1171 			break;
1172 
1173 		case A_B_WINDOW:
1174 			/*
1175 			 * Backward one window (and set the window size).
1176 			 */
1177 			if (number > 0)
1178 				swindow = (int) number;
1179 			/* FALLTHRU */
1180 		case A_B_SCREEN:
1181 			/*
1182 			 * Backward one screen.
1183 			 */
1184 			if (number <= 0)
1185 				number = get_swindow();
1186 			cmd_exec();
1187 			backward((int) number, 0, 1);
1188 			break;
1189 
1190 		case A_F_LINE:
1191 			/*
1192 			 * Forward N (default 1) line.
1193 			 */
1194 			if (number <= 0)
1195 				number = 1;
1196 			cmd_exec();
1197 			if (show_attn == OPT_ONPLUS && number > 1)
1198 				set_attnpos(bottompos);
1199 			forward((int) number, 0, 0);
1200 			break;
1201 
1202 		case A_B_LINE:
1203 			/*
1204 			 * Backward N (default 1) line.
1205 			 */
1206 			if (number <= 0)
1207 				number = 1;
1208 			cmd_exec();
1209 			backward((int) number, 0, 0);
1210 			break;
1211 
1212 		case A_FF_LINE:
1213 			/*
1214 			 * Force forward N (default 1) line.
1215 			 */
1216 			if (number <= 0)
1217 				number = 1;
1218 			cmd_exec();
1219 			if (show_attn == OPT_ONPLUS && number > 1)
1220 				set_attnpos(bottompos);
1221 			forward((int) number, 1, 0);
1222 			break;
1223 
1224 		case A_BF_LINE:
1225 			/*
1226 			 * Force backward N (default 1) line.
1227 			 */
1228 			if (number <= 0)
1229 				number = 1;
1230 			cmd_exec();
1231 			backward((int) number, 1, 0);
1232 			break;
1233 
1234 		case A_FF_SCREEN:
1235 			/*
1236 			 * Force forward one screen.
1237 			 */
1238 			if (number <= 0)
1239 				number = get_swindow();
1240 			cmd_exec();
1241 			if (show_attn == OPT_ONPLUS)
1242 				set_attnpos(bottompos);
1243 			forward((int) number, 1, 0);
1244 			break;
1245 
1246 		case A_F_FOREVER:
1247 			/*
1248 			 * Forward forever, ignoring EOF.
1249 			 */
1250 			newaction = forw_loop(0);
1251 			break;
1252 
1253 		case A_F_UNTIL_HILITE:
1254 			newaction = forw_loop(1);
1255 			break;
1256 
1257 		case A_F_SCROLL:
1258 			/*
1259 			 * Forward N lines
1260 			 * (default same as last 'd' or 'u' command).
1261 			 */
1262 			if (number > 0)
1263 				wscroll = (int) number;
1264 			cmd_exec();
1265 			if (show_attn == OPT_ONPLUS)
1266 				set_attnpos(bottompos);
1267 			forward(wscroll, 0, 0);
1268 			break;
1269 
1270 		case A_B_SCROLL:
1271 			/*
1272 			 * Forward N lines
1273 			 * (default same as last 'd' or 'u' command).
1274 			 */
1275 			if (number > 0)
1276 				wscroll = (int) number;
1277 			cmd_exec();
1278 			backward(wscroll, 0, 0);
1279 			break;
1280 
1281 		case A_FREPAINT:
1282 			/*
1283 			 * Flush buffers, then repaint screen.
1284 			 * Don't flush the buffers on a pipe!
1285 			 */
1286 			clear_buffers();
1287 			/* FALLTHRU */
1288 		case A_REPAINT:
1289 			/*
1290 			 * Repaint screen.
1291 			 */
1292 			cmd_exec();
1293 			repaint();
1294 			break;
1295 
1296 		case A_GOLINE:
1297 			/*
1298 			 * Go to line N, default beginning of file.
1299 			 */
1300 			if (number <= 0)
1301 				number = 1;
1302 			cmd_exec();
1303 			jump_back(number);
1304 			break;
1305 
1306 		case A_PERCENT:
1307 			/*
1308 			 * Go to a specified percentage into the file.
1309 			 */
1310 			if (number < 0)
1311 			{
1312 				number = 0;
1313 				fraction = 0;
1314 			}
1315 			if (number > 100)
1316 			{
1317 				number = 100;
1318 				fraction = 0;
1319 			}
1320 			cmd_exec();
1321 			jump_percent((int) number, fraction);
1322 			break;
1323 
1324 		case A_GOEND:
1325 			/*
1326 			 * Go to line N, default end of file.
1327 			 */
1328 			cmd_exec();
1329 			if (number <= 0)
1330 				jump_forw();
1331 			else
1332 				jump_back(number);
1333 			break;
1334 
1335 		case A_GOPOS:
1336 			/*
1337 			 * Go to a specified byte position in the file.
1338 			 */
1339 			cmd_exec();
1340 			if (number < 0)
1341 				number = 0;
1342 			jump_line_loc((POSITION) number, jump_sline);
1343 			break;
1344 
1345 		case A_STAT:
1346 			/*
1347 			 * Print file name, etc.
1348 			 */
1349 			if (ch_getflags() & CH_HELPFILE)
1350 				break;
1351 			cmd_exec();
1352 			parg.p_string = eq_message();
1353 			error("%s", &parg);
1354 			break;
1355 
1356 		case A_VERSION:
1357 			/*
1358 			 * Print version number, without the "@(#)".
1359 			 */
1360 			cmd_exec();
1361 			dispversion();
1362 			break;
1363 
1364 		case A_QUIT:
1365 			/*
1366 			 * Exit.
1367 			 */
1368 			if (curr_ifile != NULL_IFILE &&
1369 			    ch_getflags() & CH_HELPFILE)
1370 			{
1371 				/*
1372 				 * Quit while viewing the help file
1373 				 * just means return to viewing the
1374 				 * previous file.
1375 				 */
1376 				hshift = save_hshift;
1377 				if (edit_prev(1) == 0)
1378 					break;
1379 			}
1380 			if (extra != NULL)
1381 				quit(*extra);
1382 			quit(QUIT_OK);
1383 			break;
1384 
1385 /*
1386  * Define abbreviation for a commonly used sequence below.
1387  */
1388 #define	DO_SEARCH() \
1389 			if (number <= 0) number = 1;	\
1390 			mca_search();			\
1391 			cmd_exec();			\
1392 			multi_search((char *)NULL, (int) number);
1393 
1394 
1395 		case A_F_SEARCH:
1396 			/*
1397 			 * Search forward for a pattern.
1398 			 * Get the first char of the pattern.
1399 			 */
1400 			search_type = SRCH_FORW;
1401 			if (number <= 0)
1402 				number = 1;
1403 			mca_search();
1404 			c = getcc();
1405 			goto again;
1406 
1407 		case A_B_SEARCH:
1408 			/*
1409 			 * Search backward for a pattern.
1410 			 * Get the first char of the pattern.
1411 			 */
1412 			search_type = SRCH_BACK;
1413 			if (number <= 0)
1414 				number = 1;
1415 			mca_search();
1416 			c = getcc();
1417 			goto again;
1418 
1419 		case A_FILTER:
1420 #if HILITE_SEARCH
1421 			search_type = SRCH_FORW | SRCH_FILTER;
1422 			mca_search();
1423 			c = getcc();
1424 			goto again;
1425 #else
1426 			error("Command not available", NULL_PARG);
1427 			break;
1428 #endif
1429 
1430 		case A_AGAIN_SEARCH:
1431 			/*
1432 			 * Repeat previous search.
1433 			 */
1434 			DO_SEARCH();
1435 			break;
1436 
1437 		case A_T_AGAIN_SEARCH:
1438 			/*
1439 			 * Repeat previous search, multiple files.
1440 			 */
1441 			search_type |= SRCH_PAST_EOF;
1442 			DO_SEARCH();
1443 			break;
1444 
1445 		case A_REVERSE_SEARCH:
1446 			/*
1447 			 * Repeat previous search, in reverse direction.
1448 			 */
1449 			save_search_type = search_type;
1450 			search_type = SRCH_REVERSE(search_type);
1451 			DO_SEARCH();
1452 			search_type = save_search_type;
1453 			break;
1454 
1455 		case A_T_REVERSE_SEARCH:
1456 			/*
1457 			 * Repeat previous search,
1458 			 * multiple files in reverse direction.
1459 			 */
1460 			save_search_type = search_type;
1461 			search_type = SRCH_REVERSE(search_type);
1462 			search_type |= SRCH_PAST_EOF;
1463 			DO_SEARCH();
1464 			search_type = save_search_type;
1465 			break;
1466 
1467 		case A_UNDO_SEARCH:
1468 			undo_search();
1469 			break;
1470 
1471 		case A_HELP:
1472 			/*
1473 			 * Help.
1474 			 */
1475 			if (ch_getflags() & CH_HELPFILE)
1476 				break;
1477 			cmd_exec();
1478 			save_hshift = hshift;
1479 			hshift = 0;
1480 			(void) edit(FAKE_HELPFILE);
1481 			break;
1482 
1483 		case A_EXAMINE:
1484 #if EXAMINE
1485 			/*
1486 			 * Edit a new file.  Get the filename.
1487 			 */
1488 			if (secure)
1489 			{
1490 				error("Command not available", NULL_PARG);
1491 				break;
1492 			}
1493 			start_mca(A_EXAMINE, "Examine: ", ml_examine, 0);
1494 			c = getcc();
1495 			goto again;
1496 #else
1497 			error("Command not available", NULL_PARG);
1498 			break;
1499 #endif
1500 
1501 		case A_VISUAL:
1502 			/*
1503 			 * Invoke an editor on the input file.
1504 			 */
1505 #if EDITOR
1506 			if (secure)
1507 			{
1508 				error("Command not available", NULL_PARG);
1509 				break;
1510 			}
1511 			if (ch_getflags() & CH_HELPFILE)
1512 				break;
1513 			if (strcmp(get_filename(curr_ifile), "-") == 0)
1514 			{
1515 				error("Cannot edit standard input", NULL_PARG);
1516 				break;
1517 			}
1518 			if (curr_altfilename != NULL)
1519 			{
1520 				error("WARNING: This file was viewed via LESSOPEN",
1521 					NULL_PARG);
1522 			}
1523 			start_mca(A_SHELL, "!", ml_shell, 0);
1524 			/*
1525 			 * Expand the editor prototype string
1526 			 * and pass it to the system to execute.
1527 			 * (Make sure the screen is displayed so the
1528 			 * expansion of "+%lm" works.)
1529 			 */
1530 			make_display();
1531 			cmd_exec();
1532 			lsystem(pr_expand(editproto, 0), (char*)NULL);
1533 			break;
1534 #else
1535 			error("Command not available", NULL_PARG);
1536 			break;
1537 #endif
1538 
1539 		case A_NEXT_FILE:
1540 			/*
1541 			 * Examine next file.
1542 			 */
1543 #if TAGS
1544 			if (ntags())
1545 			{
1546 				error("No next file", NULL_PARG);
1547 				break;
1548 			}
1549 #endif
1550 			if (number <= 0)
1551 				number = 1;
1552 			if (edit_next((int) number))
1553 			{
1554 				if (get_quit_at_eof() && eof_displayed() &&
1555 				    !(ch_getflags() & CH_HELPFILE))
1556 					quit(QUIT_OK);
1557 				parg.p_string = (number > 1) ? "(N-th) " : "";
1558 				error("No %snext file", &parg);
1559 			}
1560 			break;
1561 
1562 		case A_PREV_FILE:
1563 			/*
1564 			 * Examine previous file.
1565 			 */
1566 #if TAGS
1567 			if (ntags())
1568 			{
1569 				error("No previous file", NULL_PARG);
1570 				break;
1571 			}
1572 #endif
1573 			if (number <= 0)
1574 				number = 1;
1575 			if (edit_prev((int) number))
1576 			{
1577 				parg.p_string = (number > 1) ? "(N-th) " : "";
1578 				error("No %sprevious file", &parg);
1579 			}
1580 			break;
1581 
1582 		case A_NEXT_TAG:
1583 #if TAGS
1584 			if (number <= 0)
1585 				number = 1;
1586 			tagfile = nexttag((int) number);
1587 			if (tagfile == NULL)
1588 			{
1589 				error("No next tag", NULL_PARG);
1590 				break;
1591 			}
1592 			if (edit(tagfile) == 0)
1593 			{
1594 				POSITION pos = tagsearch();
1595 				if (pos != NULL_POSITION)
1596 					jump_loc(pos, jump_sline);
1597 			}
1598 #else
1599 			error("Command not available", NULL_PARG);
1600 #endif
1601 			break;
1602 
1603 		case A_PREV_TAG:
1604 #if TAGS
1605 			if (number <= 0)
1606 				number = 1;
1607 			tagfile = prevtag((int) number);
1608 			if (tagfile == NULL)
1609 			{
1610 				error("No previous tag", NULL_PARG);
1611 				break;
1612 			}
1613 			if (edit(tagfile) == 0)
1614 			{
1615 				POSITION pos = tagsearch();
1616 				if (pos != NULL_POSITION)
1617 					jump_loc(pos, jump_sline);
1618 			}
1619 #else
1620 			error("Command not available", NULL_PARG);
1621 #endif
1622 			break;
1623 
1624 		case A_INDEX_FILE:
1625 			/*
1626 			 * Examine a particular file.
1627 			 */
1628 			if (number <= 0)
1629 				number = 1;
1630 			if (edit_index((int) number))
1631 				error("No such file", NULL_PARG);
1632 			break;
1633 
1634 		case A_REMOVE_FILE:
1635 			if (ch_getflags() & CH_HELPFILE)
1636 				break;
1637 			old_ifile = curr_ifile;
1638 			new_ifile = getoff_ifile(curr_ifile);
1639 			if (new_ifile == NULL_IFILE)
1640 			{
1641 				bell();
1642 				break;
1643 			}
1644 			if (edit_ifile(new_ifile) != 0)
1645 			{
1646 				reedit_ifile(old_ifile);
1647 				break;
1648 			}
1649 			del_ifile(old_ifile);
1650 			break;
1651 
1652 		case A_OPT_TOGGLE:
1653 			optflag = OPT_TOGGLE;
1654 			optgetname = FALSE;
1655 			mca_opt_toggle();
1656 			c = getcc();
1657 			goto again;
1658 
1659 		case A_DISP_OPTION:
1660 			/*
1661 			 * Report a flag setting.
1662 			 */
1663 			optflag = OPT_NO_TOGGLE;
1664 			optgetname = FALSE;
1665 			mca_opt_toggle();
1666 			c = getcc();
1667 			goto again;
1668 
1669 		case A_FIRSTCMD:
1670 			/*
1671 			 * Set an initial command for new files.
1672 			 */
1673 			start_mca(A_FIRSTCMD, "+", (void*)NULL, 0);
1674 			c = getcc();
1675 			goto again;
1676 
1677 		case A_SHELL:
1678 			/*
1679 			 * Shell escape.
1680 			 */
1681 #if SHELL_ESCAPE
1682 			if (secure)
1683 			{
1684 				error("Command not available", NULL_PARG);
1685 				break;
1686 			}
1687 			start_mca(A_SHELL, "!", ml_shell, 0);
1688 			c = getcc();
1689 			goto again;
1690 #else
1691 			error("Command not available", NULL_PARG);
1692 			break;
1693 #endif
1694 
1695 		case A_SETMARK:
1696 			/*
1697 			 * Set a mark.
1698 			 */
1699 			if (ch_getflags() & CH_HELPFILE)
1700 				break;
1701 			start_mca(A_SETMARK, "mark: ", (void*)NULL, 0);
1702 			c = getcc();
1703 			if (c == erase_char || c == erase2_char ||
1704 			    c == kill_char || c == '\n' || c == '\r')
1705 				break;
1706 			setmark(c);
1707 			break;
1708 
1709 		case A_GOMARK:
1710 			/*
1711 			 * Go to a mark.
1712 			 */
1713 			start_mca(A_GOMARK, "goto mark: ", (void*)NULL, 0);
1714 			c = getcc();
1715 			if (c == erase_char || c == erase2_char ||
1716 			    c == kill_char || c == '\n' || c == '\r')
1717 				break;
1718 			cmd_exec();
1719 			gomark(c);
1720 			break;
1721 
1722 		case A_PIPE:
1723 #if PIPEC
1724 			if (secure)
1725 			{
1726 				error("Command not available", NULL_PARG);
1727 				break;
1728 			}
1729 			start_mca(A_PIPE, "|mark: ", (void*)NULL, 0);
1730 			c = getcc();
1731 			if (c == erase_char || c == erase2_char || c == kill_char)
1732 				break;
1733 			if (c == '\n' || c == '\r')
1734 				c = '.';
1735 			if (badmark(c))
1736 				break;
1737 			pipec = c;
1738 			start_mca(A_PIPE, "!", ml_shell, 0);
1739 			c = getcc();
1740 			goto again;
1741 #else
1742 			error("Command not available", NULL_PARG);
1743 			break;
1744 #endif
1745 
1746 		case A_B_BRACKET:
1747 		case A_F_BRACKET:
1748 			start_mca(action, "Brackets: ", (void*)NULL, 0);
1749 			c = getcc();
1750 			goto again;
1751 
1752 		case A_LSHIFT:
1753 			if (number > 0)
1754 				shift_count = number;
1755 			else
1756 				number = (shift_count > 0) ?
1757 					shift_count : sc_width / 2;
1758 			if (number > hshift)
1759 				number = hshift;
1760 			hshift -= number;
1761 			screen_trashed = 1;
1762 			break;
1763 
1764 		case A_RSHIFT:
1765 			if (number > 0)
1766 				shift_count = number;
1767 			else
1768 				number = (shift_count > 0) ?
1769 					shift_count : sc_width / 2;
1770 			hshift += number;
1771 			screen_trashed = 1;
1772 			break;
1773 
1774 		case A_PREFIX:
1775 			/*
1776 			 * The command is incomplete (more chars are needed).
1777 			 * Display the current char, so the user knows
1778 			 * what's going on, and get another character.
1779 			 */
1780 			if (mca != A_PREFIX)
1781 			{
1782 				cmd_reset();
1783 				start_mca(A_PREFIX, " ", (void*)NULL,
1784 					CF_QUIT_ON_ERASE);
1785 				(void) cmd_char(c);
1786 			}
1787 			c = getcc();
1788 			goto again;
1789 
1790 		case A_NOACTION:
1791 			break;
1792 
1793 		default:
1794 			bell();
1795 			break;
1796 		}
1797 	}
1798 }
1799