command.c revision 63131
1112158Sdas/* $FreeBSD: head/contrib/less/command.c 63131 2000-07-14 09:57:37Z ps $ */
2112158Sdas/*
3112158Sdas * Copyright (C) 1984-2000  Mark Nudelman
4112158Sdas *
5112158Sdas * You may distribute under the terms of either the GNU General Public
6112158Sdas * License or the Less License, as specified in the README file.
7112158Sdas *
8112158Sdas * For more information about less, or for information on how to
9112158Sdas * contact the author, see the README file.
10112158Sdas */
11112158Sdas
12112158Sdas
13112158Sdas/*
14112158Sdas * User-level command processor.
15112158Sdas */
16112158Sdas
17112158Sdas#include "less.h"
18112158Sdas#include "position.h"
19112158Sdas#include "option.h"
20112158Sdas#include "cmd.h"
21112158Sdas
22112158Sdasextern int erase_char, kill_char;
23112158Sdasextern int sigs;
24112158Sdasextern int quit_at_eof;
25112158Sdasextern int quit_if_one_screen;
26112158Sdasextern int squished;
27112158Sdasextern int hit_eof;
28112158Sdasextern int sc_width;
29165743Sdasextern int sc_height;
30165743Sdasextern int swindow;
31112158Sdasextern int jump_sline;
32112158Sdasextern int quitting;
33112158Sdasextern int wscroll;
34112158Sdasextern int top_scroll;
35112158Sdasextern int ignore_eoi;
36112158Sdasextern int secure;
37187808Sdasextern int hshift;
38112158Sdasextern int show_attn;
39187808Sdasextern int more_mode;
40112158Sdasextern char *every_first_cmd;
41112158Sdasextern char *curr_altfilename;
42112158Sdasextern char version[];
43112158Sdasextern struct scrpos initial_scrpos;
44112158Sdasextern IFILE curr_ifile;
45112158Sdasextern void constant *ml_search;
46112158Sdasextern void constant *ml_examine;
47112158Sdas#if SHELL_ESCAPE || PIPEC
48112158Sdasextern void constant *ml_shell;
49112158Sdas#endif
50112158Sdas#if EDITOR
51112158Sdasextern char *editor;
52112158Sdasextern char *editproto;
53112158Sdas#endif
54112158Sdasextern int screen_trashed;	/* The screen has been overwritten */
55112158Sdasextern int shift_count;
56112158Sdas
57112158Sdasstatic char ungot[UNGOT_SIZE];
58112158Sdasstatic char *ungotp = NULL;
59112158Sdas#if SHELL_ESCAPE
60112158Sdasstatic char *shellcmd = NULL;	/* For holding last shell command for "!!" */
61112158Sdas#endif
62112158Sdasstatic int mca;			/* The multicharacter command (action) */
63187808Sdasstatic int search_type;		/* The previous type of search */
64112158Sdasstatic int number;		/* The number typed by the user */
65112158Sdasstatic char optchar;
66187808Sdasstatic int optflag;
67112158Sdasstatic int optgetname;
68112158Sdasstatic POSITION bottompos;
69112158Sdas#if PIPEC
70112158Sdasstatic char pipec;
71112158Sdas#endif
72112158Sdas
73112158Sdasstatic void multi_search();
74112158Sdas
75112158Sdas/*
76112158Sdas * Move the cursor to lower left before executing a command.
77112158Sdas * This looks nicer if the command takes a long time before
78112158Sdas * updating the screen.
79112158Sdas */
80219557Sdas	static void
81112158Sdascmd_exec()
82112158Sdas{
83219557Sdas	clear_attn();
84219557Sdas	lower_left();
85112158Sdas	flush();
86112158Sdas}
87112158Sdas
88219557Sdas/*
89112158Sdas * Set up the display to start a new multi-character command.
90219557Sdas */
91112158Sdas	static void
92112158Sdasstart_mca(action, prompt, mlist, cmdflags)
93112158Sdas	int action;
94219557Sdas	char *prompt;
95112158Sdas	void *mlist;
96219557Sdas	int cmdflags;
97112158Sdas{
98112158Sdas	mca = action;
99112158Sdas	clear_cmd();
100219557Sdas	cmd_putstr(prompt);
101112158Sdas	set_mlist(mlist, cmdflags);
102112158Sdas}
103219557Sdas
104112158Sdas	public int
105112158Sdasin_mca()
106219557Sdas{
107112158Sdas	return (mca != 0 && mca != A_PREFIX);
108112158Sdas}
109112158Sdas
110112158Sdas/*
111112158Sdas * Set up the display to start a new search command.
112112158Sdas */
113112158Sdas	static void
114112158Sdasmca_search()
115112158Sdas{
116112158Sdas	if (search_type & SRCH_FORW)
117112158Sdas		mca = A_F_SEARCH;
118112158Sdas	else
119112158Sdas		mca = A_B_SEARCH;
120112158Sdas
121112158Sdas	clear_cmd();
122112158Sdas
123112158Sdas	if (search_type & SRCH_NO_MATCH)
124112158Sdas		cmd_putstr("Non-match ");
125112158Sdas	if (search_type & SRCH_FIRST_FILE)
126112158Sdas		cmd_putstr("First-file ");
127112158Sdas	if (search_type & SRCH_PAST_EOF)
128112158Sdas		cmd_putstr("EOF-ignore ");
129112158Sdas	if (search_type & SRCH_NO_MOVE)
130112158Sdas		cmd_putstr("Keep-pos ");
131112158Sdas	if (search_type & SRCH_NO_REGEX)
132112158Sdas		cmd_putstr("Regex-off ");
133112158Sdas
134112158Sdas	if (search_type & SRCH_FORW)
135112158Sdas		cmd_putstr("/");
136112158Sdas	else
137112158Sdas		cmd_putstr("?");
138112158Sdas	set_mlist(ml_search, 0);
139112158Sdas}
140112158Sdas
141112158Sdas/*
142112158Sdas * Set up the display to start a new toggle-option command.
143112158Sdas */
144112158Sdas	static void
145112158Sdasmca_opt_toggle()
146112158Sdas{
147112158Sdas	int no_prompt;
148112158Sdas	int flag;
149112158Sdas	char *dash;
150112158Sdas
151112158Sdas	no_prompt = (optflag & OPT_NO_PROMPT);
152112158Sdas	flag = (optflag & ~OPT_NO_PROMPT);
153112158Sdas	dash = (flag == OPT_NO_TOGGLE) ? "_" : "-";
154112158Sdas
155112158Sdas	mca = A_OPT_TOGGLE;
156112158Sdas	clear_cmd();
157112158Sdas	cmd_putstr(dash);
158112158Sdas	if (optgetname)
159112158Sdas		cmd_putstr(dash);
160112158Sdas	if (no_prompt)
161112158Sdas		cmd_putstr("(P)");
162112158Sdas	switch (flag)
163112158Sdas	{
164112158Sdas	case OPT_UNSET:
165112158Sdas		cmd_putstr("+");
166112158Sdas		break;
167112158Sdas	case OPT_SET:
168112158Sdas		cmd_putstr("!");
169112158Sdas		break;
170112158Sdas	}
171112158Sdas	set_mlist(NULL, 0);
172112158Sdas}
173112158Sdas
174112158Sdas/*
175112158Sdas * Execute a multicharacter command.
176112158Sdas */
177112158Sdas	static void
178112158Sdasexec_mca()
179112158Sdas{
180112158Sdas	register char *cbuf;
181112158Sdas
182112158Sdas	cmd_exec();
183112158Sdas	cbuf = get_cmdbuf();
184112158Sdas
185112158Sdas	switch (mca)
186112158Sdas	{
187112158Sdas	case A_F_SEARCH:
188112158Sdas	case A_B_SEARCH:
189112158Sdas		multi_search(cbuf, number);
190112158Sdas		break;
191112158Sdas	case A_FIRSTCMD:
192		/*
193		 * Skip leading spaces or + signs in the string.
194		 */
195		while (*cbuf == '+' || *cbuf == ' ')
196			cbuf++;
197		if (every_first_cmd != NULL)
198			free(every_first_cmd);
199		if (*cbuf == '\0')
200			every_first_cmd = NULL;
201		else
202			every_first_cmd = save(cbuf);
203		break;
204	case A_OPT_TOGGLE:
205		toggle_option(optchar, cbuf, optflag);
206		optchar = '\0';
207		break;
208	case A_F_BRACKET:
209		match_brac(cbuf[0], cbuf[1], 1, number);
210		break;
211	case A_B_BRACKET:
212		match_brac(cbuf[1], cbuf[0], 0, number);
213		break;
214#if EXAMINE
215	case A_EXAMINE:
216		if (secure)
217			break;
218		edit_list(cbuf);
219		break;
220#endif
221#if SHELL_ESCAPE
222	case A_SHELL:
223		/*
224		 * !! just uses whatever is in shellcmd.
225		 * Otherwise, copy cmdbuf to shellcmd,
226		 * expanding any special characters ("%" or "#").
227		 */
228		if (*cbuf != '!')
229		{
230			if (shellcmd != NULL)
231				free(shellcmd);
232			shellcmd = fexpand(cbuf);
233		}
234
235		if (secure)
236			break;
237		if (shellcmd == NULL)
238			lsystem("", "!done");
239		else
240			lsystem(shellcmd, "!done");
241		break;
242#endif
243#if PIPEC
244	case A_PIPE:
245		if (secure)
246			break;
247		(void) pipe_mark(pipec, cbuf);
248		error("|done", NULL_PARG);
249		break;
250#endif
251	}
252}
253
254/*
255 * Add a character to a multi-character command.
256 */
257	static int
258mca_char(c)
259	int c;
260{
261	char *p;
262	int flag;
263	char buf[3];
264	PARG parg;
265
266	switch (mca)
267	{
268	case 0:
269		/*
270		 * Not in a multicharacter command.
271		 */
272		return (NO_MCA);
273
274	case A_PREFIX:
275		/*
276		 * In the prefix of a command.
277		 * This not considered a multichar command
278		 * (even tho it uses cmdbuf, etc.).
279		 * It is handled in the commands() switch.
280		 */
281		return (NO_MCA);
282
283	case A_DIGIT:
284		/*
285		 * Entering digits of a number.
286		 * Terminated by a non-digit.
287		 */
288		if ((c < '0' || c > '9') &&
289		  editchar(c, EC_PEEK|EC_NOHISTORY|EC_NOCOMPLETE) == A_INVALID)
290		{
291			/*
292			 * Not part of the number.
293			 * Treat as a normal command character.
294			 */
295			number = cmd_int();
296			mca = 0;
297			cmd_accept();
298			return (NO_MCA);
299		}
300		break;
301
302	case A_OPT_TOGGLE:
303		/*
304		 * Special case for the TOGGLE_OPTION command.
305		 * If the option letter which was entered is a
306		 * single-char option, execute the command immediately,
307		 * so user doesn't have to hit RETURN.
308		 * If the first char is + or -, this indicates
309		 * OPT_UNSET or OPT_SET respectively, instead of OPT_TOGGLE.
310		 * "--" begins inputting a long option name.
311		 */
312		if (optchar == '\0' && len_cmdbuf() == 0)
313		{
314			flag = (optflag & ~OPT_NO_PROMPT);
315			if (flag == OPT_NO_TOGGLE)
316			{
317				switch (c)
318				{
319				case '_':
320					/* "__" = long option name. */
321					optgetname = TRUE;
322					mca_opt_toggle();
323					return (MCA_MORE);
324				}
325			} else
326			{
327				switch (c)
328				{
329				case '+':
330					/* "-+" = UNSET. */
331					optflag = (flag == OPT_UNSET) ?
332						OPT_TOGGLE : OPT_UNSET;
333					mca_opt_toggle();
334					return (MCA_MORE);
335				case '!':
336					/* "-!" = SET */
337					optflag = (flag == OPT_SET) ?
338						OPT_TOGGLE : OPT_SET;
339					mca_opt_toggle();
340					return (MCA_MORE);
341				case CONTROL('P'):
342					optflag ^= OPT_NO_PROMPT;
343					mca_opt_toggle();
344					return (MCA_MORE);
345				case '-':
346					/* "--" = long option name. */
347					optgetname = TRUE;
348					mca_opt_toggle();
349					return (MCA_MORE);
350				}
351			}
352		}
353		if (optgetname)
354		{
355			/*
356			 * We're getting a long option name.
357			 * See if we've matched an option name yet.
358			 * If so, display the complete name and stop
359			 * accepting chars until user hits RETURN.
360			 */
361			struct option *o;
362			char *oname;
363			int lc;
364
365			if (c == '\n' || c == '\r')
366			{
367				/*
368				 * When the user hits RETURN, make sure
369				 * we've matched an option name, then
370				 * pretend he just entered the equivalent
371				 * option letter.
372				 */
373				if (optchar == '\0')
374				{
375					parg.p_string = get_cmdbuf();
376					error("There is no --%s option", &parg);
377					return (MCA_DONE);
378				}
379				optgetname = FALSE;
380				cmd_reset();
381				c = optchar;
382			} else
383			{
384				if (optchar != '\0')
385				{
386					/*
387					 * Already have a match for the name.
388					 * Don't accept anything but erase/kill.
389					 */
390					if (c == erase_char || c == kill_char)
391						return (MCA_DONE);
392					return (MCA_MORE);
393				}
394				/*
395				 * Add char to cmd buffer and try to match
396				 * the option name.
397				 */
398				if (cmd_char(c) == CC_QUIT)
399					return (MCA_DONE);
400				p = get_cmdbuf();
401				lc = islower(p[0]);
402				o = findopt_name(&p, &oname, NULL);
403				if (o != NULL)
404				{
405					/*
406					 * Got a match.
407					 * Remember the option letter and
408					 * display the full option name.
409					 */
410					optchar = o->oletter;
411					if (!lc && islower(optchar))
412						optchar = toupper(optchar);
413					cmd_reset();
414					mca_opt_toggle();
415					for (p = oname;  *p != '\0';  p++)
416					{
417						c = *p;
418						if (!lc && islower(c))
419							c = toupper(c);
420						if (cmd_char(c) != CC_OK)
421							return (MCA_DONE);
422					}
423				}
424				return (MCA_MORE);
425			}
426		} else
427		{
428			if (c == erase_char || c == kill_char)
429				break;
430			if (optchar != '\0')
431				/* We already have the option letter. */
432				break;
433		}
434
435		optchar = c;
436		if ((optflag & ~OPT_NO_PROMPT) != OPT_TOGGLE ||
437		    single_char_option(c))
438		{
439			toggle_option(c, "", optflag);
440			return (MCA_DONE);
441		}
442		/*
443		 * Display a prompt appropriate for the option letter.
444		 */
445		if ((p = opt_prompt(c)) == NULL)
446		{
447			buf[0] = '-';
448			buf[1] = c;
449			buf[2] = '\0';
450			p = buf;
451		}
452		start_mca(A_OPT_TOGGLE, p, (void*)NULL, 0);
453		return (MCA_MORE);
454
455	case A_F_SEARCH:
456	case A_B_SEARCH:
457		/*
458		 * Special case for search commands.
459		 * Certain characters as the first char of
460		 * the pattern have special meaning:
461		 *	!  Toggle the NO_MATCH flag
462		 *	*  Toggle the PAST_EOF flag
463		 *	@  Toggle the FIRST_FILE flag
464		 */
465		if (len_cmdbuf() > 0)
466			/*
467			 * Only works for the first char of the pattern.
468			 */
469			break;
470
471		flag = 0;
472		switch (c)
473		{
474		case '*':
475			if (more_mode)
476				break;
477		case CONTROL('E'): /* ignore END of file */
478			flag = SRCH_PAST_EOF;
479			break;
480		case '@':
481			if (more_mode)
482				break;
483		case CONTROL('F'): /* FIRST file */
484			flag = SRCH_FIRST_FILE;
485			break;
486		case CONTROL('K'): /* KEEP position */
487			flag = SRCH_NO_MOVE;
488			break;
489		case CONTROL('R'): /* Don't use REGULAR EXPRESSIONS */
490			flag = SRCH_NO_REGEX;
491			break;
492		case CONTROL('N'): /* NOT match */
493		case '!':
494			flag = SRCH_NO_MATCH;
495			break;
496		}
497		if (flag != 0)
498		{
499			search_type ^= flag;
500			mca_search();
501			return (MCA_MORE);
502		}
503		break;
504	}
505
506	/*
507	 * Any other multicharacter command
508	 * is terminated by a newline.
509	 */
510	if (c == '\n' || c == '\r')
511	{
512		/*
513		 * Execute the command.
514		 */
515		exec_mca();
516		return (MCA_DONE);
517	}
518
519	/*
520	 * Append the char to the command buffer.
521	 */
522	if (cmd_char(c) == CC_QUIT)
523		/*
524		 * Abort the multi-char command.
525		 */
526		return (MCA_DONE);
527
528	if ((mca == A_F_BRACKET || mca == A_B_BRACKET) && len_cmdbuf() >= 2)
529	{
530		/*
531		 * Special case for the bracket-matching commands.
532		 * Execute the command after getting exactly two
533		 * characters from the user.
534		 */
535		exec_mca();
536		return (MCA_DONE);
537	}
538
539	/*
540	 * Need another character.
541	 */
542	return (MCA_MORE);
543}
544
545/*
546 * Make sure the screen is displayed.
547 */
548	static void
549make_display()
550{
551	/*
552	 * If nothing is displayed yet, display starting from initial_scrpos.
553	 */
554	if (empty_screen())
555	{
556		if (initial_scrpos.pos == NULL_POSITION)
557			/*
558			 * {{ Maybe this should be:
559			 *    jump_loc(ch_zero(), jump_sline);
560			 *    but this behavior seems rather unexpected
561			 *    on the first screen. }}
562			 */
563			jump_loc(ch_zero(), 1);
564		else
565			jump_loc(initial_scrpos.pos, initial_scrpos.ln);
566	} else if (screen_trashed)
567	{
568		int save_top_scroll;
569		save_top_scroll = top_scroll;
570		top_scroll = 1;
571		repaint();
572		top_scroll = save_top_scroll;
573	}
574}
575
576/*
577 * Display the appropriate prompt.
578 */
579	static void
580prompt()
581{
582	register char *p;
583
584	if (ungotp != NULL && ungotp > ungot)
585	{
586		/*
587		 * No prompt necessary if commands are from
588		 * ungotten chars rather than from the user.
589		 */
590		return;
591	}
592
593	/*
594	 * Make sure the screen is displayed.
595	 */
596	make_display();
597	bottompos = position(BOTTOM_PLUS_ONE);
598
599	/*
600	 * If the -E flag is set and we've hit EOF on the last file, quit.
601	 */
602	if ((quit_at_eof == OPT_ONPLUS || quit_if_one_screen) &&
603	    hit_eof && !(ch_getflags() & CH_HELPFILE) &&
604	    next_ifile(curr_ifile) == NULL_IFILE)
605		quit(QUIT_OK);
606	quit_if_one_screen = FALSE;
607#if 0 /* This doesn't work well because some "te"s clear the screen. */
608	/*
609	 * If the -e flag is set and we've hit EOF on the last file,
610	 * and the file is squished (shorter than the screen), quit.
611	 */
612	if (quit_at_eof && squished &&
613	    next_ifile(curr_ifile) == NULL_IFILE)
614		quit(QUIT_OK);
615#endif
616
617	/*
618	 * Select the proper prompt and display it.
619	 */
620	clear_cmd();
621	p = pr_string();
622	if (p == NULL)
623		putchr(':');
624	else
625	{
626		so_enter();
627		putstr(p);
628		so_exit();
629	}
630}
631
632/*
633 * Display the less version message.
634 */
635	public void
636dispversion()
637{
638	PARG parg;
639
640	parg.p_string = version;
641	error("less %s", &parg);
642}
643
644/*
645 * Get command character.
646 * The character normally comes from the keyboard,
647 * but may come from ungotten characters
648 * (characters previously given to ungetcc or ungetsc).
649 */
650	public int
651getcc()
652{
653	if (ungotp == NULL)
654		/*
655		 * Normal case: no ungotten chars, so get one from the user.
656		 */
657		return (getchr());
658
659	if (ungotp > ungot)
660		/*
661		 * Return the next ungotten char.
662		 */
663		return (*--ungotp);
664
665	/*
666	 * We have just run out of ungotten chars.
667	 */
668	ungotp = NULL;
669	if (len_cmdbuf() == 0 || !empty_screen())
670		return (getchr());
671	/*
672	 * Command is incomplete, so try to complete it.
673	 */
674	switch (mca)
675	{
676	case A_DIGIT:
677		/*
678		 * We have a number but no command.  Treat as #g.
679		 */
680		return ('g');
681
682	case A_F_SEARCH:
683	case A_B_SEARCH:
684		/*
685		 * We have "/string" but no newline.  Add the \n.
686		 */
687		return ('\n');
688
689	default:
690		/*
691		 * Some other incomplete command.  Let user complete it.
692		 */
693		return (getchr());
694	}
695}
696
697/*
698 * "Unget" a command character.
699 * The next getcc() will return this character.
700 */
701	public void
702ungetcc(c)
703	int c;
704{
705	if (ungotp == NULL)
706		ungotp = ungot;
707	if (ungotp >= ungot + sizeof(ungot))
708	{
709		error("ungetcc overflow", NULL_PARG);
710		quit(QUIT_ERROR);
711	}
712	*ungotp++ = c;
713}
714
715/*
716 * Unget a whole string of command characters.
717 * The next sequence of getcc()'s will return this string.
718 */
719	public void
720ungetsc(s)
721	char *s;
722{
723	register char *p;
724
725	for (p = s + strlen(s) - 1;  p >= s;  p--)
726		ungetcc(*p);
727}
728
729/*
730 * Search for a pattern, possibly in multiple files.
731 * If SRCH_FIRST_FILE is set, begin searching at the first file.
732 * If SRCH_PAST_EOF is set, continue the search thru multiple files.
733 */
734	static void
735multi_search(pattern, n)
736	char *pattern;
737	int n;
738{
739	register int nomore;
740	IFILE save_ifile;
741	int changed_file;
742
743	changed_file = 0;
744	save_ifile = save_curr_ifile();
745
746	if (search_type & SRCH_FIRST_FILE)
747	{
748		/*
749		 * Start at the first (or last) file
750		 * in the command line list.
751		 */
752		if (search_type & SRCH_FORW)
753			nomore = edit_first();
754		else
755			nomore = edit_last();
756		if (nomore)
757		{
758			unsave_ifile(save_ifile);
759			return;
760		}
761		changed_file = 1;
762		search_type &= ~SRCH_FIRST_FILE;
763	}
764
765	for (;;)
766	{
767		n = search(search_type, pattern, n);
768		/*
769		 * The SRCH_NO_MOVE flag doesn't "stick": it gets cleared
770		 * after being used once.  This allows "n" to work after
771		 * using a /@@ search.
772		 */
773		search_type &= ~SRCH_NO_MOVE;
774		if (n == 0)
775		{
776			/*
777			 * Found it.
778			 */
779			unsave_ifile(save_ifile);
780			return;
781		}
782
783		if (n < 0)
784			/*
785			 * Some kind of error in the search.
786			 * Error message has been printed by search().
787			 */
788			break;
789
790		if ((search_type & SRCH_PAST_EOF) == 0)
791			/*
792			 * We didn't find a match, but we're
793			 * supposed to search only one file.
794			 */
795			break;
796		/*
797		 * Move on to the next file.
798		 */
799		if (search_type & SRCH_FORW)
800			nomore = edit_next(1);
801		else
802			nomore = edit_prev(1);
803		if (nomore)
804			break;
805		changed_file = 1;
806	}
807
808	/*
809	 * Didn't find it.
810	 * Print an error message if we haven't already.
811	 */
812	if (n > 0)
813		error("Pattern not found", NULL_PARG);
814
815	if (changed_file)
816	{
817		/*
818		 * Restore the file we were originally viewing.
819		 */
820		reedit_ifile(save_ifile);
821	}
822}
823
824/*
825 * Main command processor.
826 * Accept and execute commands until a quit command.
827 */
828	public void
829commands()
830{
831	register int c;
832	register int action;
833	register char *cbuf;
834	int newaction;
835	int save_search_type;
836	char *extra;
837	char tbuf[2];
838	PARG parg;
839	IFILE old_ifile;
840	IFILE new_ifile;
841
842	search_type = SRCH_FORW;
843	wscroll = (sc_height + 1) / 2;
844	newaction = A_NOACTION;
845
846	for (;;)
847	{
848		mca = 0;
849		cmd_accept();
850		number = 0;
851		optchar = '\0';
852
853		/*
854		 * See if any signals need processing.
855		 */
856		if (sigs)
857		{
858			psignals();
859			if (quitting)
860				quit(QUIT_SAVED_STATUS);
861		}
862
863		/*
864		 * See if window size changed, for systems that don't
865		 * generate SIGWINCH.
866		 */
867		check_winch();
868
869		/*
870		 * Display prompt and accept a character.
871		 */
872		cmd_reset();
873		prompt();
874		if (sigs)
875			continue;
876		if (newaction == A_NOACTION)
877			c = getcc();
878
879	again:
880		if (sigs)
881			continue;
882
883		if (newaction != A_NOACTION)
884		{
885			action = newaction;
886			newaction = A_NOACTION;
887		} else
888		{
889			/*
890			 * If we are in a multicharacter command, call mca_char.
891			 * Otherwise we call fcmd_decode to determine the
892			 * action to be performed.
893			 */
894			if (mca)
895				switch (mca_char(c))
896				{
897				case MCA_MORE:
898					/*
899					 * Need another character.
900					 */
901					c = getcc();
902					goto again;
903				case MCA_DONE:
904					/*
905					 * Command has been handled by mca_char.
906					 * Start clean with a prompt.
907					 */
908					continue;
909				case NO_MCA:
910					/*
911					 * Not a multi-char command
912					 * (at least, not anymore).
913					 */
914					break;
915				}
916
917			/*
918			 * Decode the command character and decide what to do.
919			 */
920			if (mca)
921			{
922				/*
923				 * We're in a multichar command.
924				 * Add the character to the command buffer
925				 * and display it on the screen.
926				 * If the user backspaces past the start
927				 * of the line, abort the command.
928				 */
929				if (cmd_char(c) == CC_QUIT || len_cmdbuf() == 0)
930					continue;
931				cbuf = get_cmdbuf();
932			} else
933			{
934				/*
935				 * Don't use cmd_char if we're starting fresh
936				 * at the beginning of a command, because we
937				 * don't want to echo the command until we know
938				 * it is a multichar command.  We also don't
939				 * want erase_char/kill_char to be treated
940				 * as line editing characters.
941				 */
942				tbuf[0] = c;
943				tbuf[1] = '\0';
944				cbuf = tbuf;
945			}
946			extra = NULL;
947			action = fcmd_decode(cbuf, &extra);
948			/*
949			 * If an "extra" string was returned,
950			 * process it as a string of command characters.
951			 */
952			if (extra != NULL)
953				ungetsc(extra);
954		}
955		/*
956		 * Clear the cmdbuf string.
957		 * (But not if we're in the prefix of a command,
958		 * because the partial command string is kept there.)
959		 */
960		if (action != A_PREFIX)
961			cmd_reset();
962
963		switch (action)
964		{
965		case A_DIGIT:
966			/*
967			 * First digit of a number.
968			 */
969			start_mca(A_DIGIT, ":", (void*)NULL, CF_QUIT_ON_ERASE);
970			goto again;
971
972		case A_F_WINDOW:
973			/*
974			 * Forward one window (and set the window size).
975			 */
976			if (number > 0)
977				swindow = number;
978			/* FALLTHRU */
979		case A_F_SCREEN:
980			/*
981			 * Forward one screen.
982			 */
983			if (number <= 0)
984				number = get_swindow();
985			cmd_exec();
986			if (show_attn)
987				set_attnpos(bottompos);
988			forward(number, 0, 1);
989			break;
990
991		case A_B_WINDOW:
992			/*
993			 * Backward one window (and set the window size).
994			 */
995			if (number > 0)
996				swindow = number;
997			/* FALLTHRU */
998		case A_B_SCREEN:
999			/*
1000			 * Backward one screen.
1001			 */
1002			if (number <= 0)
1003				number = get_swindow();
1004			cmd_exec();
1005			backward(number, 0, 1);
1006			break;
1007
1008		case A_F_LINE:
1009			/*
1010			 * Forward N (default 1) line.
1011			 */
1012			if (number <= 0)
1013				number = 1;
1014			cmd_exec();
1015			if (show_attn == OPT_ONPLUS && number > 1)
1016				set_attnpos(bottompos);
1017			forward(number, 0, 0);
1018			break;
1019
1020		case A_B_LINE:
1021			/*
1022			 * Backward N (default 1) line.
1023			 */
1024			if (number <= 0)
1025				number = 1;
1026			cmd_exec();
1027			backward(number, 0, 0);
1028			break;
1029
1030		case A_FF_LINE:
1031			/*
1032			 * Force forward N (default 1) line.
1033			 */
1034			if (number <= 0)
1035				number = 1;
1036			cmd_exec();
1037			if (show_attn == OPT_ONPLUS && number > 1)
1038				set_attnpos(bottompos);
1039			forward(number, 1, 0);
1040			break;
1041
1042		case A_BF_LINE:
1043			/*
1044			 * Force backward N (default 1) line.
1045			 */
1046			if (number <= 0)
1047				number = 1;
1048			cmd_exec();
1049			backward(number, 1, 0);
1050			break;
1051
1052		case A_FF_SCREEN:
1053			/*
1054			 * Force forward one screen.
1055			 */
1056			if (number <= 0)
1057				number = get_swindow();
1058			cmd_exec();
1059			if (show_attn == OPT_ONPLUS)
1060				set_attnpos(bottompos);
1061			forward(number, 1, 0);
1062			break;
1063
1064		case A_F_FOREVER:
1065			/*
1066			 * Forward forever, ignoring EOF.
1067			 */
1068			if (ch_getflags() & CH_HELPFILE)
1069				break;
1070			cmd_exec();
1071			jump_forw();
1072			ignore_eoi = 1;
1073			hit_eof = 0;
1074			while (!sigs)
1075				forward(1, 0, 0);
1076			ignore_eoi = 0;
1077			/*
1078			 * This gets us back in "F mode" after processing
1079			 * a non-abort signal (e.g. window-change).
1080			 */
1081			if (sigs && !ABORT_SIGS())
1082				newaction = A_F_FOREVER;
1083			break;
1084
1085		case A_F_SCROLL:
1086			/*
1087			 * Forward N lines
1088			 * (default same as last 'd' or 'u' command).
1089			 */
1090			if (number > 0)
1091				wscroll = number;
1092			cmd_exec();
1093			if (show_attn == OPT_ONPLUS)
1094				set_attnpos(bottompos);
1095			forward(wscroll, 0, 0);
1096			break;
1097
1098		case A_B_SCROLL:
1099			/*
1100			 * Forward N lines
1101			 * (default same as last 'd' or 'u' command).
1102			 */
1103			if (number > 0)
1104				wscroll = number;
1105			cmd_exec();
1106			backward(wscroll, 0, 0);
1107			break;
1108
1109		case A_FREPAINT:
1110			/*
1111			 * Flush buffers, then repaint screen.
1112			 * Don't flush the buffers on a pipe!
1113			 */
1114			if (ch_getflags() & CH_CANSEEK)
1115			{
1116				ch_flush();
1117				clr_linenum();
1118#if HILITE_SEARCH
1119				clr_hilite();
1120#endif
1121			}
1122			/* FALLTHRU */
1123		case A_REPAINT:
1124			/*
1125			 * Repaint screen.
1126			 */
1127			cmd_exec();
1128			repaint();
1129			break;
1130
1131		case A_GOLINE:
1132			/*
1133			 * Go to line N, default beginning of file.
1134			 */
1135			if (number <= 0)
1136				number = 1;
1137			cmd_exec();
1138			jump_back(number);
1139			break;
1140
1141		case A_PERCENT:
1142			/*
1143			 * Go to a specified percentage into the file.
1144			 */
1145			if (number < 0)
1146				number = 0;
1147			if (number > 100)
1148				number = 100;
1149			cmd_exec();
1150			jump_percent(number);
1151			break;
1152
1153		case A_GOEND:
1154			/*
1155			 * Go to line N, default end of file.
1156			 */
1157			cmd_exec();
1158			if (number <= 0)
1159				jump_forw();
1160			else
1161				jump_back(number);
1162			break;
1163
1164		case A_GOPOS:
1165			/*
1166			 * Go to a specified byte position in the file.
1167			 */
1168			cmd_exec();
1169			if (number < 0)
1170				number = 0;
1171			jump_line_loc((POSITION)number, jump_sline);
1172			break;
1173
1174		case A_STAT:
1175			/*
1176			 * Print file name, etc.
1177			 */
1178			if (ch_getflags() & CH_HELPFILE)
1179				break;
1180			cmd_exec();
1181			parg.p_string = eq_message();
1182			error("%s", &parg);
1183			break;
1184
1185		case A_VERSION:
1186			/*
1187			 * Print version number, without the "@(#)".
1188			 */
1189			cmd_exec();
1190			dispversion();
1191			break;
1192
1193		case A_QUIT:
1194			/*
1195			 * Exit.
1196			 */
1197			if (curr_ifile != NULL_IFILE &&
1198			    ch_getflags() & CH_HELPFILE)
1199			{
1200				/*
1201				 * Quit while viewing the help file
1202				 * just means return to viewing the
1203				 * previous file.
1204				 */
1205				if (edit_prev(1) == 0)
1206					break;
1207			}
1208			if (extra != NULL)
1209				quit(*extra);
1210			quit(QUIT_OK);
1211			break;
1212
1213/*
1214 * Define abbreviation for a commonly used sequence below.
1215 */
1216#define	DO_SEARCH()	if (number <= 0) number = 1;	\
1217			mca_search();			\
1218			cmd_exec();			\
1219			multi_search((char *)NULL, number);
1220
1221
1222		case A_F_SEARCH:
1223			/*
1224			 * Search forward for a pattern.
1225			 * Get the first char of the pattern.
1226			 */
1227			search_type = SRCH_FORW;
1228			if (number <= 0)
1229				number = 1;
1230			mca_search();
1231			c = getcc();
1232			goto again;
1233
1234		case A_B_SEARCH:
1235			/*
1236			 * Search backward for a pattern.
1237			 * Get the first char of the pattern.
1238			 */
1239			search_type = SRCH_BACK;
1240			if (number <= 0)
1241				number = 1;
1242			mca_search();
1243			c = getcc();
1244			goto again;
1245
1246		case A_AGAIN_SEARCH:
1247			/*
1248			 * Repeat previous search.
1249			 */
1250			DO_SEARCH();
1251			break;
1252
1253		case A_T_AGAIN_SEARCH:
1254			/*
1255			 * Repeat previous search, multiple files.
1256			 */
1257			search_type |= SRCH_PAST_EOF;
1258			DO_SEARCH();
1259			break;
1260
1261		case A_REVERSE_SEARCH:
1262			/*
1263			 * Repeat previous search, in reverse direction.
1264			 */
1265			save_search_type = search_type;
1266			search_type = SRCH_REVERSE(search_type);
1267			DO_SEARCH();
1268			search_type = save_search_type;
1269			break;
1270
1271		case A_T_REVERSE_SEARCH:
1272			/*
1273			 * Repeat previous search,
1274			 * multiple files in reverse direction.
1275			 */
1276			save_search_type = search_type;
1277			search_type = SRCH_REVERSE(search_type);
1278			search_type |= SRCH_PAST_EOF;
1279			DO_SEARCH();
1280			search_type = save_search_type;
1281			break;
1282
1283		case A_UNDO_SEARCH:
1284			undo_search();
1285			break;
1286
1287		case A_HELP:
1288			/*
1289			 * Help.
1290			 */
1291			if (ch_getflags() & CH_HELPFILE)
1292				break;
1293			cmd_exec();
1294			(void) edit(FAKE_HELPFILE);
1295			break;
1296
1297		case A_EXAMINE:
1298#if EXAMINE
1299			/*
1300			 * Edit a new file.  Get the filename.
1301			 */
1302			if (secure)
1303			{
1304				error("Command not available", NULL_PARG);
1305				break;
1306			}
1307			start_mca(A_EXAMINE, "Examine: ", ml_examine, 0);
1308			c = getcc();
1309			goto again;
1310#else
1311			error("Command not available", NULL_PARG);
1312			break;
1313#endif
1314
1315		case A_VISUAL:
1316			/*
1317			 * Invoke an editor on the input file.
1318			 */
1319#if EDITOR
1320			if (secure)
1321			{
1322				error("Command not available", NULL_PARG);
1323				break;
1324			}
1325			if (ch_getflags() & CH_HELPFILE)
1326				break;
1327			if (strcmp(get_filename(curr_ifile), "-") == 0)
1328			{
1329				error("Cannot edit standard input", NULL_PARG);
1330				break;
1331			}
1332			if (curr_altfilename != NULL)
1333			{
1334				error("Cannot edit file processed with LESSOPEN",
1335					NULL_PARG);
1336				break;
1337			}
1338			start_mca(A_SHELL, "!", ml_shell, 0);
1339			/*
1340			 * Expand the editor prototype string
1341			 * and pass it to the system to execute.
1342			 * (Make sure the screen is displayed so the
1343			 * expansion of "+%lm" works.)
1344			 */
1345			make_display();
1346			cmd_exec();
1347			lsystem(pr_expand(editproto, 0), (char*)NULL);
1348			break;
1349#else
1350			error("Command not available", NULL_PARG);
1351			break;
1352#endif
1353
1354		case A_NEXT_FILE:
1355			/*
1356			 * Examine next file.
1357			 */
1358			if (number <= 0)
1359				number = 1;
1360			if (edit_next(number))
1361			{
1362				if (quit_at_eof && hit_eof &&
1363				    !(ch_getflags() & CH_HELPFILE))
1364					quit(QUIT_OK);
1365				parg.p_string = (number > 1) ? "(N-th) " : "";
1366				error("No %snext file", &parg);
1367			}
1368			break;
1369
1370		case A_PREV_FILE:
1371			/*
1372			 * Examine previous file.
1373			 */
1374			if (number <= 0)
1375				number = 1;
1376			if (edit_prev(number))
1377			{
1378				parg.p_string = (number > 1) ? "(N-th) " : "";
1379				error("No %sprevious file", &parg);
1380			}
1381			break;
1382
1383		case A_INDEX_FILE:
1384			/*
1385			 * Examine a particular file.
1386			 */
1387			if (number <= 0)
1388				number = 1;
1389			if (edit_index(number))
1390				error("No such file", NULL_PARG);
1391			break;
1392
1393		case A_REMOVE_FILE:
1394			if (ch_getflags() & CH_HELPFILE)
1395				break;
1396			old_ifile = curr_ifile;
1397			new_ifile = getoff_ifile(curr_ifile);
1398			if (new_ifile == NULL_IFILE)
1399			{
1400				bell();
1401				break;
1402			}
1403			if (edit_ifile(new_ifile) != 0)
1404			{
1405				reedit_ifile(old_ifile);
1406				break;
1407			}
1408			del_ifile(old_ifile);
1409			break;
1410
1411		case A_OPT_TOGGLE:
1412			optflag = OPT_TOGGLE;
1413			optgetname = FALSE;
1414			mca_opt_toggle();
1415			c = getcc();
1416			goto again;
1417
1418		case A_DISP_OPTION:
1419			/*
1420			 * Report a flag setting.
1421			 */
1422			optflag = OPT_NO_TOGGLE;
1423			optgetname = FALSE;
1424			mca_opt_toggle();
1425			c = getcc();
1426			goto again;
1427
1428		case A_FIRSTCMD:
1429			/*
1430			 * Set an initial command for new files.
1431			 */
1432			start_mca(A_FIRSTCMD, "+", (void*)NULL, 0);
1433			c = getcc();
1434			goto again;
1435
1436		case A_SHELL:
1437			/*
1438			 * Shell escape.
1439			 */
1440#if SHELL_ESCAPE
1441			if (secure)
1442			{
1443				error("Command not available", NULL_PARG);
1444				break;
1445			}
1446			start_mca(A_SHELL, "!", ml_shell, 0);
1447			c = getcc();
1448			goto again;
1449#else
1450			error("Command not available", NULL_PARG);
1451			break;
1452#endif
1453
1454		case A_SETMARK:
1455			/*
1456			 * Set a mark.
1457			 */
1458			if (ch_getflags() & CH_HELPFILE)
1459				break;
1460			start_mca(A_SETMARK, "mark: ", (void*)NULL, 0);
1461			c = getcc();
1462			if (c == erase_char || c == kill_char ||
1463			    c == '\n' || c == '\r')
1464				break;
1465			setmark(c);
1466			break;
1467
1468		case A_GOMARK:
1469			/*
1470			 * Go to a mark.
1471			 */
1472			start_mca(A_GOMARK, "goto mark: ", (void*)NULL, 0);
1473			c = getcc();
1474			if (c == erase_char || c == kill_char ||
1475			    c == '\n' || c == '\r')
1476				break;
1477			gomark(c);
1478			break;
1479
1480		case A_PIPE:
1481#if PIPEC
1482			if (secure)
1483			{
1484				error("Command not available", NULL_PARG);
1485				break;
1486			}
1487			start_mca(A_PIPE, "|mark: ", (void*)NULL, 0);
1488			c = getcc();
1489			if (c == erase_char || c == kill_char)
1490				break;
1491			if (c == '\n' || c == '\r')
1492				c = '.';
1493			if (badmark(c))
1494				break;
1495			pipec = c;
1496			start_mca(A_PIPE, "!", ml_shell, 0);
1497			c = getcc();
1498			goto again;
1499#else
1500			error("Command not available", NULL_PARG);
1501			break;
1502#endif
1503
1504		case A_B_BRACKET:
1505		case A_F_BRACKET:
1506			start_mca(action, "Brackets: ", (void*)NULL, 0);
1507			c = getcc();
1508			goto again;
1509
1510		case A_LSHIFT:
1511			if (number <= 0)
1512				number = (shift_count > 0) ?
1513					shift_count : sc_width / 2;
1514			if (number > hshift)
1515				number = hshift;
1516			hshift -= number;
1517			screen_trashed = 1;
1518			break;
1519
1520		case A_RSHIFT:
1521			if (number <= 0)
1522				number = (shift_count > 0) ?
1523					shift_count : sc_width / 2;
1524			hshift += number;
1525			screen_trashed = 1;
1526			break;
1527
1528		case A_PREFIX:
1529			/*
1530			 * The command is incomplete (more chars are needed).
1531			 * Display the current char, so the user knows
1532			 * what's going on, and get another character.
1533			 */
1534			if (mca != A_PREFIX)
1535			{
1536				cmd_reset();
1537				start_mca(A_PREFIX, " ", (void*)NULL,
1538					CF_QUIT_ON_ERASE);
1539				(void) cmd_char(c);
1540			}
1541			c = getcc();
1542			goto again;
1543
1544		case A_NOACTION:
1545			break;
1546
1547		default:
1548			bell();
1549			break;
1550		}
1551	}
1552}
1553