xref: /openbsd/usr.bin/less/command.c (revision 133306f0)
1 /*	$OpenBSD: command.c,v 1.3 2001/01/29 01:58:00 niklas Exp $	*/
2 
3 /*
4  * Copyright (c) 1984,1985,1989,1994,1995  Mark Nudelman
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice in the documentation and/or other materials provided with
14  *    the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY
17  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE
20  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
22  * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
23  * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
24  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
25  * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
26  * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27  */
28 
29 
30 /*
31  * User-level command processor.
32  */
33 
34 #include "less.h"
35 #include "position.h"
36 #include "option.h"
37 #include "cmd.h"
38 
39 extern int erase_char, kill_char;
40 extern int sigs;
41 extern int quit_at_eof;
42 extern int hit_eof;
43 extern int sc_width;
44 extern int sc_height;
45 extern int swindow;
46 extern int jump_sline;
47 extern int quitting;
48 extern int wscroll;
49 extern int nohelp;
50 extern int top_scroll;
51 extern int ignore_eoi;
52 extern char *every_first_cmd;
53 extern char *curr_altfilename;
54 extern char version[];
55 extern struct scrpos initial_scrpos;
56 extern IFILE curr_ifile;
57 #if CMD_HISTORY
58 extern void *ml_search;
59 extern void *ml_examine;
60 #if SHELL_ESCAPE || PIPEC
61 extern void *ml_shell;
62 #endif
63 #else
64 /* No CMD_HISTORY */
65 #define	ml_search	NULL
66 #define	ml_examine	NULL
67 #define	ml_shell  	NULL
68 #endif
69 #if EDITOR
70 extern char *editor;
71 extern char *editproto;
72 #endif
73 extern int screen_trashed;	/* The screen has been overwritten */
74 extern int be_helpful;
75 
76 public int helpprompt;
77 
78 static char ungot[100];
79 static char *ungotp = NULL;
80 #if SHELL_ESCAPE
81 static char *shellcmd = NULL;	/* For holding last shell command for "!!" */
82 #endif
83 static int mca;			/* The multicharacter command (action) */
84 static int search_type;		/* The previous type of search */
85 static int number;		/* The number typed by the user */
86 static char optchar;
87 static int optflag;
88 #if PIPEC
89 static char pipec;
90 #endif
91 
92 static void multi_search();
93 
94 /*
95  * Move the cursor to lower left before executing a command.
96  * This looks nicer if the command takes a long time before
97  * updating the screen.
98  */
99 	static void
100 cmd_exec()
101 {
102 	lower_left();
103 	flush();
104 }
105 
106 /*
107  * Set up the display to start a new multi-character command.
108  */
109 	static void
110 start_mca(action, prompt, mlist)
111 	int action;
112 	char *prompt;
113 	void *mlist;
114 {
115 	mca = action;
116 	clear_bot();
117 	cmd_putstr(prompt);
118 #if CMD_HISTORY
119 	set_mlist(mlist);
120 #endif
121 }
122 
123 	public int
124 in_mca()
125 {
126 	return (mca != 0 && mca != A_PREFIX);
127 }
128 
129 /*
130  * Set up the display to start a new search command.
131  */
132 	static void
133 mca_search()
134 {
135 	if (search_type & SRCH_FORW)
136 		mca = A_F_SEARCH;
137 	else
138 		mca = A_B_SEARCH;
139 
140 	clear_bot();
141 
142 	if (search_type & SRCH_FIRST_FILE)
143 		cmd_putstr("@");
144 
145 	if (search_type & SRCH_PAST_EOF)
146 		cmd_putstr("*");
147 
148 	if (search_type & SRCH_NOMATCH)
149 		cmd_putstr("!");
150 
151 	if (search_type & SRCH_FORW)
152 		cmd_putstr("/");
153 	else
154 		cmd_putstr("?");
155 #if CMD_HISTORY
156 	set_mlist(ml_search);
157 #endif
158 }
159 
160 /*
161  * Execute a multicharacter command.
162  */
163 	static void
164 exec_mca()
165 {
166 	register char *cbuf;
167 
168 	cmd_exec();
169 	cbuf = get_cmdbuf();
170 
171 	switch (mca)
172 	{
173 	case A_F_SEARCH:
174 	case A_B_SEARCH:
175 		multi_search(cbuf, number);
176 		break;
177 	case A_FIRSTCMD:
178 		/*
179 		 * Skip leading spaces or + signs in the string.
180 		 */
181 		while (*cbuf == '+' || *cbuf == ' ')
182 			cbuf++;
183 		if (every_first_cmd != NULL)
184 			free(every_first_cmd);
185 		if (*cbuf == '\0')
186 			every_first_cmd = NULL;
187 		else
188 			every_first_cmd = save(cbuf);
189 		break;
190 	case A_OPT_TOGGLE:
191 		toggle_option(optchar, cbuf, optflag);
192 		optchar = '\0';
193 		break;
194 	case A_F_BRACKET:
195 		match_brac(cbuf[0], cbuf[1], 1, number);
196 		break;
197 	case A_B_BRACKET:
198 		match_brac(cbuf[1], cbuf[0], 0, number);
199 		break;
200 #if EXAMINE
201 	case A_EXAMINE:
202 		edit_list(cbuf);
203 		break;
204 #endif
205 #if SHELL_ESCAPE
206 	case A_SHELL:
207 		/*
208 		 * !! just uses whatever is in shellcmd.
209 		 * Otherwise, copy cmdbuf to shellcmd,
210 		 * expanding any special characters ("%" or "#").
211 		 */
212 		if (*cbuf != '!')
213 		{
214 			if (shellcmd != NULL)
215 				free(shellcmd);
216 			shellcmd = fexpand(cbuf);
217 		}
218 
219 		if (shellcmd == NULL)
220 			lsystem("");
221 		else
222 			lsystem(shellcmd);
223 		error("!done", NULL_PARG);
224 		break;
225 #endif
226 #if PIPEC
227 	case A_PIPE:
228 		(void) pipe_mark(pipec, cbuf);
229 		error("|done", NULL_PARG);
230 		break;
231 #endif
232 	}
233 }
234 
235 /*
236  * Add a character to a multi-character command.
237  */
238 	static int
239 mca_char(c)
240 	int c;
241 {
242 	char *p;
243 	int flag;
244 	char buf[3];
245 
246 	switch (mca)
247 	{
248 	case 0:
249 		/*
250 		 * Not in a multicharacter command.
251 		 */
252 		return (NO_MCA);
253 
254 	case A_PREFIX:
255 		/*
256 		 * In the prefix of a command.
257 		 * This not considered a multichar command
258 		 * (even tho it uses cmdbuf, etc.).
259 		 * It is handled in the commands() switch.
260 		 */
261 		return (NO_MCA);
262 
263 	case A_DIGIT:
264 		/*
265 		 * Entering digits of a number.
266 		 * Terminated by a non-digit.
267 		 */
268 		if ((c < '0' || c > '9') &&
269 		  editchar(c, EC_PEEK|EC_NOHISTORY|EC_NOCOMPLETE) == A_INVALID)
270 		{
271 			/*
272 			 * Not part of the number.
273 			 * Treat as a normal command character.
274 			 */
275 			number = cmd_int();
276 			mca = 0;
277 			cmd_accept();
278 			return (NO_MCA);
279 		}
280 		break;
281 
282 	case A_OPT_TOGGLE:
283 		/*
284 		 * Special case for the TOGGLE_OPTION command.
285 		 * If the option letter which was entered is a
286 		 * single-char option, execute the command immediately,
287 		 * so user doesn't have to hit RETURN.
288 		 * If the first char is + or -, this indicates
289 		 * OPT_UNSET or OPT_SET respectively, instead of OPT_TOGGLE.
290 		 */
291 		if (c == erase_char || c == kill_char)
292 			break;
293 		if (optchar != '\0' && optchar != '+' && optchar != '-')
294 			/*
295 			 * We already have the option letter.
296 			 */
297 			break;
298 		switch (c)
299 		{
300 		case '+':
301 			optflag = OPT_UNSET;
302 			break;
303 		case '-':
304 			optflag = OPT_SET;
305 			break;
306 		default:
307 			optchar = c;
308 			if (optflag != OPT_TOGGLE || single_char_option(c))
309 			{
310 				toggle_option(c, "", optflag);
311 				return (MCA_DONE);
312 			}
313 			break;
314 		}
315 		if (optchar == '+' || optchar == '-')
316 		{
317 			optchar = c;
318 			break;
319 		}
320 		/*
321 		 * Display a prompt appropriate for the option letter.
322 		 */
323 		if ((p = opt_prompt(c)) == NULL)
324 		{
325 			buf[0] = '-';
326 			buf[1] = c;
327 			buf[2] = '\0';
328 			p = buf;
329 		}
330 		start_mca(A_OPT_TOGGLE, p, (void*)NULL);
331 		return (MCA_MORE);
332 
333 	case A_F_SEARCH:
334 	case A_B_SEARCH:
335 		/*
336 		 * Special case for search commands.
337 		 * Certain characters as the first char of
338 		 * the pattern have special meaning:
339 		 *	!  Toggle the NOMATCH flag
340 		 *	*  Toggle the PAST_EOF flag
341 		 *	@  Toggle the FIRST_FILE flag
342 		 */
343 		if (len_cmdbuf() > 0)
344 			/*
345 			 * Only works for the first char of the pattern.
346 			 */
347 			break;
348 
349 		flag = 0;
350 		switch (c)
351 		{
352 		case '!':
353 			flag = SRCH_NOMATCH;
354 			break;
355 		case '@':
356 			flag = SRCH_FIRST_FILE;
357 			break;
358 		case '*':
359 			flag = SRCH_PAST_EOF;
360 			break;
361 		}
362 		if (flag != 0)
363 		{
364 			search_type ^= flag;
365 			mca_search();
366 			return (MCA_MORE);
367 		}
368 		break;
369 	}
370 
371 	/*
372 	 * Any other multicharacter command
373 	 * is terminated by a newline.
374 	 */
375 	if (c == '\n' || c == '\r')
376 	{
377 		/*
378 		 * Execute the command.
379 		 */
380 		exec_mca();
381 		return (MCA_DONE);
382 	}
383 	/*
384 	 * Append the char to the command buffer.
385 	 */
386 	if (cmd_char(c) == CC_QUIT)
387 		/*
388 		 * Abort the multi-char command.
389 		 */
390 		return (MCA_DONE);
391 
392 	if ((mca == A_F_BRACKET || mca == A_B_BRACKET) && len_cmdbuf() >= 2)
393 	{
394 		/*
395 		 * Special case for the bracket-matching commands.
396 		 * Execute the command after getting exactly two
397 		 * characters from the user.
398 		 */
399 		exec_mca();
400 		return (MCA_DONE);
401 	}
402 
403 	/*
404 	 * Need another character.
405 	 */
406 	return (MCA_MORE);
407 }
408 
409 /*
410  * Display the appropriate prompt.
411  */
412 	static void
413 prompt()
414 {
415 	register char *p;
416 
417 	if (ungotp != NULL && ungotp > ungot)
418 	{
419 		/*
420 		 * No prompt necessary if commands are from
421 		 * ungotten chars rather than from the user.
422 		 */
423 		return;
424 	}
425 
426 	/*
427 	 * If nothing is displayed yet, display starting from initial_scrpos.
428 	 */
429 	if (empty_screen())
430 	{
431 		if (initial_scrpos.pos == NULL_POSITION)
432 			/*
433 			 * {{ Maybe this should be:
434 			 *    jump_loc(ch_zero(), jump_sline);
435 			 *    but this behavior seems rather unexpected
436 			 *    on the first screen. }}
437 			 */
438 			jump_loc(ch_zero(), 1);
439 		else
440 			jump_loc(initial_scrpos.pos, initial_scrpos.ln);
441 	} else if (screen_trashed)
442 	{
443 		int save_top_scroll;
444 		save_top_scroll = top_scroll;
445 		top_scroll = 1;
446 		repaint();
447 		top_scroll = save_top_scroll;
448 	}
449 
450 	/*
451 	 * If the -E flag is set and we've hit EOF on the last file, quit.
452 	 */
453 	if (quit_at_eof == OPT_ONPLUS && hit_eof &&
454 	    next_ifile(curr_ifile) == NULL_IFILE)
455 		quit(QUIT_OK);
456 
457 	/*
458 	 * Select the proper prompt and display it.
459 	 */
460 	clear_bot();
461 	if (helpprompt) {
462 		so_enter();
463 		putstr("[Press 'h' for instructions.]");
464 		so_exit();
465 		helpprompt = 0;
466 	} else {
467 		p = pr_string();
468 		if (p == NULL)
469 			putchr(':');
470 		else
471 		{
472 			so_enter();
473 			putstr(p);
474 			if (be_helpful)
475 				putstr(" [Press space to continue, 'q' to quit.]");
476 			so_exit();
477 		}
478 	}
479 }
480 
481 	public void
482 dispversion()
483 {
484 	PARG parg;
485 
486 	parg.p_string = version;
487 	error("less  version %s", &parg);
488 }
489 
490 /*
491  * Get command character.
492  * The character normally comes from the keyboard,
493  * but may come from ungotten characters
494  * (characters previously given to ungetcc or ungetsc).
495  */
496 	public int
497 getcc()
498 {
499 	if (ungotp == NULL)
500 		/*
501 		 * Normal case: no ungotten chars, so get one from the user.
502 		 */
503 		return (getchr());
504 
505 	if (ungotp > ungot)
506 		/*
507 		 * Return the next ungotten char.
508 		 */
509 		return (*--ungotp);
510 
511 	/*
512 	 * We have just run out of ungotten chars.
513 	 */
514 	ungotp = NULL;
515 	if (len_cmdbuf() == 0 || !empty_screen())
516 		return (getchr());
517 	/*
518 	 * Command is incomplete, so try to complete it.
519 	 */
520 	switch (mca)
521 	{
522 	case A_DIGIT:
523 		/*
524 		 * We have a number but no command.  Treat as #g.
525 		 */
526 		return ('g');
527 
528 	case A_F_SEARCH:
529 	case A_B_SEARCH:
530 		/*
531 		 * We have "/string" but no newline.  Add the \n.
532 		 */
533 		return ('\n');
534 
535 	default:
536 		/*
537 		 * Some other incomplete command.  Let user complete it.
538 		 */
539 		return (getchr());
540 	}
541 }
542 
543 /*
544  * "Unget" a command character.
545  * The next getcc() will return this character.
546  */
547 	public void
548 ungetcc(c)
549 	int c;
550 {
551 	if (ungotp == NULL)
552 		ungotp = ungot;
553 	if (ungotp >= ungot + sizeof(ungot))
554 	{
555 		error("ungetcc overflow", NULL_PARG);
556 		quit(QUIT_ERROR);
557 	}
558 	*ungotp++ = c;
559 }
560 
561 /*
562  * Unget a whole string of command characters.
563  * The next sequence of getcc()'s will return this string.
564  */
565 	public void
566 ungetsc(s)
567 	char *s;
568 {
569 	register char *p;
570 
571 	for (p = s + strlen(s) - 1;  p >= s;  p--)
572 		ungetcc(*p);
573 }
574 
575 /*
576  * Search for a pattern, possibly in multiple files.
577  * If SRCH_FIRST_FILE is set, begin searching at the first file.
578  * If SRCH_PAST_EOF is set, continue the search thru multiple files.
579  */
580 	static void
581 multi_search(pattern, n)
582 	char *pattern;
583 	int n;
584 {
585 	register int nomore;
586 	IFILE save_ifile;
587 	int changed_file;
588 
589 	changed_file = 0;
590 	save_ifile = curr_ifile;
591 
592 	if (search_type & SRCH_FIRST_FILE)
593 	{
594 		/*
595 		 * Start at the first (or last) file
596 		 * in the command line list.
597 		 */
598 		if (search_type & SRCH_FORW)
599 			nomore = edit_first();
600 		else
601 			nomore = edit_last();
602 		if (nomore)
603 			return;
604 		changed_file = 1;
605 		search_type &= ~SRCH_FIRST_FILE;
606 	}
607 
608 	for (;;)
609 	{
610 		if ((n = search(search_type, pattern, n)) == 0)
611 			/*
612 			 * Found it.
613 			 */
614 			return;
615 
616 		if (n < 0)
617 			/*
618 			 * Some kind of error in the search.
619 			 * Error message has been printed by search().
620 			 */
621 			break;
622 
623 		if ((search_type & SRCH_PAST_EOF) == 0)
624 			/*
625 			 * We didn't find a match, but we're
626 			 * supposed to search only one file.
627 			 */
628 			break;
629 		/*
630 		 * Move on to the next file.
631 		 */
632 		if (search_type & SRCH_FORW)
633 			nomore = edit_next(1);
634 		else
635 			nomore = edit_prev(1);
636 		if (nomore)
637 			break;
638 		changed_file = 1;
639 	}
640 
641 	/*
642 	 * Didn't find it.
643 	 * Print an error message if we haven't already.
644 	 */
645 	if (n > 0)
646 		error("Pattern not found", NULL_PARG);
647 
648 	if (changed_file)
649 	{
650 		/*
651 		 * Restore the file we were originally viewing.
652 		 */
653 		if (edit_ifile(save_ifile))
654 			quit(QUIT_ERROR);
655 	}
656 }
657 
658 /*
659  * Main command processor.
660  * Accept and execute commands until a quit command.
661  */
662 	public void
663 commands()
664 {
665 	register int c;
666 	register int action;
667 	register char *cbuf;
668 	int save_search_type;
669 	char *s;
670 	char tbuf[2];
671 	PARG parg;
672 
673 	search_type = SRCH_FORW;
674 	wscroll = (sc_height + 1) / 2;
675 
676 	for (;;)
677 	{
678 		mca = 0;
679 		cmd_accept();
680 		number = 0;
681 		optchar = '\0';
682 
683 		/*
684 		 * See if any signals need processing.
685 		 */
686 		if (sigs)
687 		{
688 			psignals();
689 			if (quitting)
690 				quit(QUIT_SAVED_STATUS);
691 		}
692 
693 		/*
694 		 * Display prompt and accept a character.
695 		 */
696 		cmd_reset();
697 		prompt();
698 		if (sigs)
699 			continue;
700 		c = getcc();
701 
702 	again:
703 		if (sigs)
704 			continue;
705 
706 		/*
707 		 * If we are in a multicharacter command, call mca_char.
708 		 * Otherwise we call fcmd_decode to determine the
709 		 * action to be performed.
710 		 */
711 		if (mca)
712 			switch (mca_char(c))
713 			{
714 			case MCA_MORE:
715 				/*
716 				 * Need another character.
717 				 */
718 				c = getcc();
719 				goto again;
720 			case MCA_DONE:
721 				/*
722 				 * Command has been handled by mca_char.
723 				 * Start clean with a prompt.
724 				 */
725 				continue;
726 			case NO_MCA:
727 				/*
728 				 * Not a multi-char command
729 				 * (at least, not anymore).
730 				 */
731 				break;
732 			}
733 
734 		/*
735 		 * Decode the command character and decide what to do.
736 		 */
737 		if (mca)
738 		{
739 			/*
740 			 * We're in a multichar command.
741 			 * Add the character to the command buffer
742 			 * and display it on the screen.
743 			 * If the user backspaces past the start
744 			 * of the line, abort the command.
745 			 */
746 			if (cmd_char(c) == CC_QUIT || len_cmdbuf() == 0)
747 				continue;
748 			cbuf = get_cmdbuf();
749 		} else
750 		{
751 			/*
752 			 * Don't use cmd_char if we're starting fresh
753 			 * at the beginning of a command, because we
754 			 * don't want to echo the command until we know
755 			 * it is a multichar command.  We also don't
756 			 * want erase_char/kill_char to be treated
757 			 * as line editing characters.
758 			 */
759 			tbuf[0] = c;
760 			tbuf[1] = '\0';
761 			cbuf = tbuf;
762 		}
763 		s = NULL;
764 		action = fcmd_decode(cbuf, &s);
765 		/*
766 		 * If an "extra" string was returned,
767 		 * process it as a string of command characters.
768 		 */
769 		if (s != NULL)
770 			ungetsc(s);
771 		/*
772 		 * Clear the cmdbuf string.
773 		 * (But not if we're in the prefix of a command,
774 		 * because the partial command string is kept there.)
775 		 */
776 		if (action != A_PREFIX)
777 			cmd_reset();
778 
779 		switch (action)
780 		{
781 		case A_DIGIT:
782 			/*
783 			 * First digit of a number.
784 			 */
785 			start_mca(A_DIGIT, ":", (void*)NULL);
786 			goto again;
787 
788 		case A_F_WINDOW:
789 			/*
790 			 * Forward one window (and set the window size).
791 			 */
792 			if (number > 0)
793 				swindow = number;
794 			/* FALLTHRU */
795 		case A_F_SCREEN:
796 			/*
797 			 * Forward one screen.
798 			 */
799 			if (number <= 0)
800 				number = get_swindow();
801 			cmd_exec();
802 			forward(number, 0, 1);
803 			break;
804 
805 		case A_B_WINDOW:
806 			/*
807 			 * Backward one window (and set the window size).
808 			 */
809 			if (number > 0)
810 				swindow = number;
811 			/* FALLTHRU */
812 		case A_B_SCREEN:
813 			/*
814 			 * Backward one screen.
815 			 */
816 			if (number <= 0)
817 				number = get_swindow();
818 			cmd_exec();
819 			backward(number, 0, 1);
820 			break;
821 
822 		case A_F_LINE:
823 			/*
824 			 * Forward N (default 1) line.
825 			 */
826 			if (number <= 0)
827 				number = 1;
828 			cmd_exec();
829 			forward(number, 0, 0);
830 			break;
831 
832 		case A_B_LINE:
833 			/*
834 			 * Backward N (default 1) line.
835 			 */
836 			if (number <= 0)
837 				number = 1;
838 			cmd_exec();
839 			backward(number, 0, 0);
840 			break;
841 
842 		case A_FF_LINE:
843 			/*
844 			 * Force forward N (default 1) line.
845 			 */
846 			if (number <= 0)
847 				number = 1;
848 			cmd_exec();
849 			forward(number, 1, 0);
850 			break;
851 
852 		case A_BF_LINE:
853 			/*
854 			 * Force backward N (default 1) line.
855 			 */
856 			if (number <= 0)
857 				number = 1;
858 			cmd_exec();
859 			backward(number, 1, 0);
860 			break;
861 
862 		case A_F_FOREVER:
863 			/*
864 			 * Forward forever, ignoring EOF.
865 			 */
866 			cmd_exec();
867 			jump_forw();
868 			ignore_eoi = 1;
869 			hit_eof = 0;
870 			while (!ABORT_SIGS())
871 				forward(1, 0, 0);
872 			ignore_eoi = 0;
873 			break;
874 
875 		case A_F_SCROLL:
876 			/*
877 			 * Forward N lines
878 			 * (default same as last 'd' or 'u' command).
879 			 */
880 			if (number > 0)
881 				wscroll = number;
882 			cmd_exec();
883 			forward(wscroll, 0, 0);
884 			break;
885 
886 		case A_B_SCROLL:
887 			/*
888 			 * Forward N lines
889 			 * (default same as last 'd' or 'u' command).
890 			 */
891 			if (number > 0)
892 				wscroll = number;
893 			cmd_exec();
894 			backward(wscroll, 0, 0);
895 			break;
896 
897 		case A_FREPAINT:
898 			/*
899 			 * Flush buffers, then repaint screen.
900 			 * Don't flush the buffers on a pipe!
901 			 */
902 			if (ch_getflags() & CH_CANSEEK)
903 			{
904 				ch_flush();
905 				clr_linenum();
906 			}
907 			/* FALLTHRU */
908 		case A_REPAINT:
909 			/*
910 			 * Repaint screen.
911 			 */
912 			cmd_exec();
913 			repaint();
914 			break;
915 
916 		case A_GOLINE:
917 			/*
918 			 * Go to line N, default beginning of file.
919 			 */
920 			if (number <= 0)
921 				number = 1;
922 			cmd_exec();
923 			jump_back(number);
924 			break;
925 
926 		case A_PERCENT:
927 			/*
928 			 * Go to a specified percentage into the file.
929 			 */
930 			if (number < 0)
931 				number = 0;
932 			if (number > 100)
933 				number = 100;
934 			cmd_exec();
935 			jump_percent(number);
936 			break;
937 
938 		case A_GOEND:
939 			/*
940 			 * Go to line N, default end of file.
941 			 */
942 			cmd_exec();
943 			if (number <= 0)
944 				jump_forw();
945 			else
946 				jump_back(number);
947 			break;
948 
949 		case A_GOPOS:
950 			/*
951 			 * Go to a specified byte position in the file.
952 			 */
953 			cmd_exec();
954 			if (number < 0)
955 				number = 0;
956 			jump_line_loc((POSITION)number, jump_sline);
957 			break;
958 
959 		case A_STAT:
960 			/*
961 			 * Print file name, etc.
962 			 */
963 			cmd_exec();
964 			parg.p_string = eq_message();
965 			error("%s", &parg);
966 			break;
967 
968 		case A_VERSION:
969 			/*
970 			 * Print version number, without the "@(#)".
971 			 */
972 			cmd_exec();
973 			dispversion();
974 			break;
975 
976 		case A_QUIT:
977 			/*
978 			 * Exit.
979 			 */
980 			quit(QUIT_OK);
981 
982 /*
983  * Define abbreviation for a commonly used sequence below.
984  */
985 #define	DO_SEARCH()	if (number <= 0) number = 1;	\
986 			mca_search();			\
987 			cmd_exec();			\
988 			multi_search((char *)NULL, number);
989 
990 
991 		case A_F_SEARCH:
992 			/*
993 			 * Search forward for a pattern.
994 			 * Get the first char of the pattern.
995 			 */
996 			search_type = SRCH_FORW;
997 			if (number <= 0)
998 				number = 1;
999 			mca_search();
1000 			c = getcc();
1001 			goto again;
1002 
1003 		case A_B_SEARCH:
1004 			/*
1005 			 * Search backward for a pattern.
1006 			 * Get the first char of the pattern.
1007 			 */
1008 			search_type = SRCH_BACK;
1009 			if (number <= 0)
1010 				number = 1;
1011 			mca_search();
1012 			c = getcc();
1013 			goto again;
1014 
1015 		case A_AGAIN_SEARCH:
1016 			/*
1017 			 * Repeat previous search.
1018 			 */
1019 			DO_SEARCH();
1020 			break;
1021 
1022 		case A_T_AGAIN_SEARCH:
1023 			/*
1024 			 * Repeat previous search, multiple files.
1025 			 */
1026 			search_type |= SRCH_PAST_EOF;
1027 			DO_SEARCH();
1028 			break;
1029 
1030 		case A_REVERSE_SEARCH:
1031 			/*
1032 			 * Repeat previous search, in reverse direction.
1033 			 */
1034 			save_search_type = search_type;
1035 			search_type = SRCH_REVERSE(search_type);
1036 			DO_SEARCH();
1037 			search_type = save_search_type;
1038 			break;
1039 
1040 		case A_T_REVERSE_SEARCH:
1041 			/*
1042 			 * Repeat previous search,
1043 			 * multiple files in reverse direction.
1044 			 */
1045 			save_search_type = search_type;
1046 			search_type = SRCH_REVERSE(search_type);
1047 			search_type |= SRCH_PAST_EOF;
1048 			DO_SEARCH();
1049 			search_type = save_search_type;
1050 			break;
1051 
1052 		case A_UNDO_SEARCH:
1053 			undo_search();
1054 			break;
1055 
1056 		case A_HELP:
1057 			/*
1058 			 * Help.
1059 			 */
1060 			if (nohelp)
1061 			{
1062 				bell();
1063 				break;
1064 			}
1065 			clear_bot();
1066 			putstr(" help");
1067 			cmd_exec();
1068 			help(0);
1069 			break;
1070 
1071 		case A_EXAMINE:
1072 #if EXAMINE
1073 			/*
1074 			 * Edit a new file.  Get the filename.
1075 			 */
1076 			start_mca(A_EXAMINE, "Examine: ", ml_examine);
1077 			c = getcc();
1078 			goto again;
1079 #else
1080 			error("Command not available", NULL_PARG);
1081 			break;
1082 #endif
1083 
1084 		case A_VISUAL:
1085 			/*
1086 			 * Invoke an editor on the input file.
1087 			 */
1088 #if EDITOR
1089 			if (strcmp(get_filename(curr_ifile), "-") == 0)
1090 			{
1091 				error("Cannot edit standard input", NULL_PARG);
1092 				break;
1093 			}
1094 			if (curr_altfilename != NULL)
1095 			{
1096 				error("Cannot edit file processed with LESSOPEN",
1097 					NULL_PARG);
1098 				break;
1099 			}
1100 			/*
1101 			 * Expand the editor prototype string
1102 			 * and pass it to the system to execute.
1103 			 */
1104 			cmd_exec();
1105 			lsystem(pr_expand(editproto, 0));
1106 			/*
1107 			 * Re-edit the file, since data may have changed.
1108 			 * Some editors even recreate the file, so flushing
1109 			 * buffers is not sufficient.
1110 			 */
1111 			if (edit_ifile(curr_ifile))
1112 				quit(QUIT_ERROR);
1113 			break;
1114 #else
1115 			error("Command not available", NULL_PARG);
1116 			break;
1117 #endif
1118 
1119 		case A_NEXT_FILE:
1120 			/*
1121 			 * Examine next file.
1122 			 */
1123 			if (number <= 0)
1124 				number = 1;
1125 			if (edit_next(number))
1126 			{
1127 				if (quit_at_eof && hit_eof)
1128 					quit(QUIT_OK);
1129 				parg.p_string = (number > 1) ? "(N-th) " : "";
1130 				error("No %snext file", &parg);
1131 			}
1132 			break;
1133 
1134 		case A_PREV_FILE:
1135 			/*
1136 			 * Examine previous file.
1137 			 */
1138 			if (number <= 0)
1139 				number = 1;
1140 			if (edit_prev(number))
1141 			{
1142 				parg.p_string = (number > 1) ? "(N-th) " : "";
1143 				error("No %sprevious file", &parg);
1144 			}
1145 			break;
1146 
1147 		case A_INDEX_FILE:
1148 			/*
1149 			 * Examine a particular file.
1150 			 */
1151 			if (number <= 0)
1152 				number = 1;
1153 			if (edit_index(number))
1154 				error("No such file", NULL_PARG);
1155 			break;
1156 
1157 		case A_OPT_TOGGLE:
1158 			start_mca(A_OPT_TOGGLE, "-", (void*)NULL);
1159 			optflag = OPT_TOGGLE;
1160 			c = getcc();
1161 			goto again;
1162 
1163 		case A_DISP_OPTION:
1164 			/*
1165 			 * Report a flag setting.
1166 			 */
1167 			start_mca(A_DISP_OPTION, "_", (void*)NULL);
1168 			c = getcc();
1169 			if (c == erase_char || c == kill_char)
1170 				break;
1171 			toggle_option(c, "", OPT_NO_TOGGLE);
1172 			break;
1173 
1174 		case A_FIRSTCMD:
1175 			/*
1176 			 * Set an initial command for new files.
1177 			 */
1178 			start_mca(A_FIRSTCMD, "+", (void*)NULL);
1179 			c = getcc();
1180 			goto again;
1181 
1182 		case A_SHELL:
1183 			/*
1184 			 * Shell escape.
1185 			 */
1186 #if SHELL_ESCAPE
1187 			start_mca(A_SHELL, "!", ml_shell);
1188 			c = getcc();
1189 			goto again;
1190 #else
1191 			error("Command not available", NULL_PARG);
1192 			break;
1193 #endif
1194 
1195 		case A_SETMARK:
1196 			/*
1197 			 * Set a mark.
1198 			 */
1199 			start_mca(A_SETMARK, "mark: ", (void*)NULL);
1200 			c = getcc();
1201 			if (c == erase_char || c == kill_char ||
1202 			    c == '\n' || c == '\r')
1203 				break;
1204 			setmark(c);
1205 			break;
1206 
1207 		case A_GOMARK:
1208 			/*
1209 			 * Go to a mark.
1210 			 */
1211 			start_mca(A_GOMARK, "goto mark: ", (void*)NULL);
1212 			c = getcc();
1213 			if (c == erase_char || c == kill_char ||
1214 			    c == '\n' || c == '\r')
1215 				break;
1216 			gomark(c);
1217 			break;
1218 
1219 		case A_PIPE:
1220 #if PIPEC
1221 			start_mca(A_PIPE, "|mark: ", (void*)NULL);
1222 			c = getcc();
1223 			if (c == erase_char || c == kill_char)
1224 				break;
1225 			if (c == '\n' || c == '\r')
1226 				c = '.';
1227 			if (badmark(c))
1228 				break;
1229 			pipec = c;
1230 			start_mca(A_PIPE, "!", ml_shell);
1231 			c = getcc();
1232 			goto again;
1233 #else
1234 			error("Command not available", NULL_PARG);
1235 			break;
1236 #endif
1237 
1238 		case A_B_BRACKET:
1239 		case A_F_BRACKET:
1240 			start_mca(action, "Brackets: ", (void*)NULL);
1241 			c = getcc();
1242 			goto again;
1243 
1244 		case A_PREFIX:
1245 			/*
1246 			 * The command is incomplete (more chars are needed).
1247 			 * Display the current char, so the user knows
1248 			 * what's going on, and get another character.
1249 			 */
1250 			if (mca != A_PREFIX)
1251 			{
1252 				start_mca(A_PREFIX, " ", (void*)NULL);
1253 				cmd_reset();
1254 				(void) cmd_char(c);
1255 			}
1256 			c = getcc();
1257 			goto again;
1258 
1259 		case A_NOACTION:
1260 			break;
1261 
1262 		default:
1263 			if (be_helpful)
1264 				helpprompt = 1;
1265 			else
1266 				bell();
1267 			break;
1268 		}
1269 	}
1270 }
1271