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