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