command.c revision 191930
160812Sps/* $FreeBSD: head/contrib/less/command.c 191930 2009-05-09 01:35:27Z delphij $ */
260786Sps/*
3191930Sdelphij * Copyright (C) 1984-2008  Mark Nudelman
460786Sps *
560786Sps * You may distribute under the terms of either the GNU General Public
660786Sps * License or the Less License, as specified in the README file.
760786Sps *
860786Sps * For more information about less, or for information on how to
960786Sps * contact the author, see the README file.
1060786Sps */
1160786Sps
1260786Sps
1360786Sps/*
1460786Sps * User-level command processor.
1560786Sps */
1660786Sps
1760786Sps#include "less.h"
1889022Sps#if MSDOS_COMPILER==WIN32C
1989022Sps#include <windows.h>
2089022Sps#endif
2160786Sps#include "position.h"
2260786Sps#include "option.h"
2360786Sps#include "cmd.h"
2460786Sps
25161478Sdelphijextern int erase_char, erase2_char, kill_char;
2660786Spsextern int sigs;
2760786Spsextern int quit_if_one_screen;
2860786Spsextern int squished;
2960786Spsextern int sc_width;
3060786Spsextern int sc_height;
3160786Spsextern int swindow;
3260786Spsextern int jump_sline;
3360786Spsextern int quitting;
3460786Spsextern int wscroll;
3560786Spsextern int top_scroll;
3660786Spsextern int ignore_eoi;
3760786Spsextern int secure;
3860786Spsextern int hshift;
3960786Spsextern int show_attn;
40170259Sdelphijextern int less_is_more;
4160786Spsextern char *every_first_cmd;
4260786Spsextern char *curr_altfilename;
4360786Spsextern char version[];
4460786Spsextern struct scrpos initial_scrpos;
4560786Spsextern IFILE curr_ifile;
4660786Spsextern void constant *ml_search;
4760786Spsextern void constant *ml_examine;
4860786Sps#if SHELL_ESCAPE || PIPEC
4960786Spsextern void constant *ml_shell;
5060786Sps#endif
5160786Sps#if EDITOR
5260786Spsextern char *editor;
5360786Spsextern char *editproto;
5460786Sps#endif
5560786Spsextern int screen_trashed;	/* The screen has been overwritten */
5663131Spsextern int shift_count;
57170259Sdelphijextern int oldbot;
58170259Sdelphijextern int forw_prompt;
5960786Sps
6060786Spsstatic char ungot[UNGOT_SIZE];
6160786Spsstatic char *ungotp = NULL;
6260786Sps#if SHELL_ESCAPE
6360786Spsstatic char *shellcmd = NULL;	/* For holding last shell command for "!!" */
6460786Sps#endif
6560786Spsstatic int mca;			/* The multicharacter command (action) */
6660786Spsstatic int search_type;		/* The previous type of search */
67128348Stjrstatic LINENUM number;		/* The number typed by the user */
68170259Sdelphijstatic long fraction;		/* The fractional part of the number */
6960786Spsstatic char optchar;
7060786Spsstatic int optflag;
7160786Spsstatic int optgetname;
7260786Spsstatic POSITION bottompos;
73161478Sdelphijstatic int save_hshift;
7460786Sps#if PIPEC
7560786Spsstatic char pipec;
7660786Sps#endif
7760786Sps
7860786Spsstatic void multi_search();
7960786Sps
8060786Sps/*
81170259Sdelphij * Move the cursor to start of prompt line before executing a command.
8260786Sps * This looks nicer if the command takes a long time before
8360786Sps * updating the screen.
8460786Sps */
8560786Sps	static void
8660786Spscmd_exec()
8760786Sps{
88191930Sdelphij#if HILITE_SEARCH
8960786Sps	clear_attn();
90191930Sdelphij#endif
91170967Sdelphij	clear_bot();
9260786Sps	flush();
9360786Sps}
9460786Sps
9560786Sps/*
9660786Sps * Set up the display to start a new multi-character command.
9760786Sps */
9860786Sps	static void
9960786Spsstart_mca(action, prompt, mlist, cmdflags)
10060786Sps	int action;
10160786Sps	char *prompt;
10260786Sps	void *mlist;
10360786Sps	int cmdflags;
10460786Sps{
10560786Sps	mca = action;
106170259Sdelphij	clear_bot();
10760786Sps	clear_cmd();
10860786Sps	cmd_putstr(prompt);
10960786Sps	set_mlist(mlist, cmdflags);
11060786Sps}
11160786Sps
11260786Sps	public int
11360786Spsin_mca()
11460786Sps{
11560786Sps	return (mca != 0 && mca != A_PREFIX);
11660786Sps}
11760786Sps
11860786Sps/*
11960786Sps * Set up the display to start a new search command.
12060786Sps */
12160786Sps	static void
12260786Spsmca_search()
12360786Sps{
124191930Sdelphij#if HILITE_SEARCH
125191930Sdelphij	if (search_type & SRCH_FILTER)
126191930Sdelphij		mca = A_FILTER;
127191930Sdelphij	else
128191930Sdelphij#endif
12960786Sps	if (search_type & SRCH_FORW)
13060786Sps		mca = A_F_SEARCH;
13160786Sps	else
13260786Sps		mca = A_B_SEARCH;
13360786Sps
134170259Sdelphij	clear_bot();
13560786Sps	clear_cmd();
13660786Sps
13760786Sps	if (search_type & SRCH_NO_MATCH)
13860786Sps		cmd_putstr("Non-match ");
13960786Sps	if (search_type & SRCH_FIRST_FILE)
14060786Sps		cmd_putstr("First-file ");
14160786Sps	if (search_type & SRCH_PAST_EOF)
14260786Sps		cmd_putstr("EOF-ignore ");
14360786Sps	if (search_type & SRCH_NO_MOVE)
14460786Sps		cmd_putstr("Keep-pos ");
14560786Sps	if (search_type & SRCH_NO_REGEX)
14660786Sps		cmd_putstr("Regex-off ");
14760786Sps
148191930Sdelphij#if HILITE_SEARCH
149191930Sdelphij	if (search_type & SRCH_FILTER)
150191930Sdelphij		cmd_putstr("&/");
151191930Sdelphij	else
152191930Sdelphij#endif
15360786Sps	if (search_type & SRCH_FORW)
15460786Sps		cmd_putstr("/");
15560786Sps	else
15660786Sps		cmd_putstr("?");
15760786Sps	set_mlist(ml_search, 0);
15860786Sps}
15960786Sps
16060786Sps/*
16160786Sps * Set up the display to start a new toggle-option command.
16260786Sps */
16360786Sps	static void
16460786Spsmca_opt_toggle()
16560786Sps{
16660786Sps	int no_prompt;
16760786Sps	int flag;
16860786Sps	char *dash;
16960786Sps
17060786Sps	no_prompt = (optflag & OPT_NO_PROMPT);
17160786Sps	flag = (optflag & ~OPT_NO_PROMPT);
17260786Sps	dash = (flag == OPT_NO_TOGGLE) ? "_" : "-";
17360786Sps
17460786Sps	mca = A_OPT_TOGGLE;
175170259Sdelphij	clear_bot();
17660786Sps	clear_cmd();
17760786Sps	cmd_putstr(dash);
17860786Sps	if (optgetname)
17960786Sps		cmd_putstr(dash);
18060786Sps	if (no_prompt)
18160786Sps		cmd_putstr("(P)");
18260786Sps	switch (flag)
18360786Sps	{
18460786Sps	case OPT_UNSET:
18560786Sps		cmd_putstr("+");
18660786Sps		break;
18760786Sps	case OPT_SET:
18860786Sps		cmd_putstr("!");
18960786Sps		break;
19060786Sps	}
19160786Sps	set_mlist(NULL, 0);
19260786Sps}
19360786Sps
19460786Sps/*
19560786Sps * Execute a multicharacter command.
19660786Sps */
19760786Sps	static void
19860786Spsexec_mca()
19960786Sps{
20060786Sps	register char *cbuf;
20160786Sps
20260786Sps	cmd_exec();
20360786Sps	cbuf = get_cmdbuf();
20460786Sps
20560786Sps	switch (mca)
20660786Sps	{
20760786Sps	case A_F_SEARCH:
20860786Sps	case A_B_SEARCH:
209128348Stjr		multi_search(cbuf, (int) number);
21060786Sps		break;
211191930Sdelphij#if HILITE_SEARCH
212191930Sdelphij	case A_FILTER:
213191930Sdelphij		search_type ^= SRCH_NO_MATCH;
214191930Sdelphij		set_filter_pattern(cbuf, search_type);
215191930Sdelphij		break;
216191930Sdelphij#endif
21760786Sps	case A_FIRSTCMD:
21860786Sps		/*
21960786Sps		 * Skip leading spaces or + signs in the string.
22060786Sps		 */
22160786Sps		while (*cbuf == '+' || *cbuf == ' ')
22260786Sps			cbuf++;
22360786Sps		if (every_first_cmd != NULL)
22460786Sps			free(every_first_cmd);
22560786Sps		if (*cbuf == '\0')
22660786Sps			every_first_cmd = NULL;
22760786Sps		else
22860786Sps			every_first_cmd = save(cbuf);
22960786Sps		break;
23060786Sps	case A_OPT_TOGGLE:
23160786Sps		toggle_option(optchar, cbuf, optflag);
23260786Sps		optchar = '\0';
23360786Sps		break;
23460786Sps	case A_F_BRACKET:
235128348Stjr		match_brac(cbuf[0], cbuf[1], 1, (int) number);
23660786Sps		break;
23760786Sps	case A_B_BRACKET:
238128348Stjr		match_brac(cbuf[1], cbuf[0], 0, (int) number);
23960786Sps		break;
24060786Sps#if EXAMINE
24160786Sps	case A_EXAMINE:
24260786Sps		if (secure)
24360786Sps			break;
24460786Sps		edit_list(cbuf);
245128348Stjr#if TAGS
24689022Sps		/* If tag structure is loaded then clean it up. */
24789022Sps		cleantags();
248128348Stjr#endif
24960786Sps		break;
25060786Sps#endif
25160786Sps#if SHELL_ESCAPE
25260786Sps	case A_SHELL:
25360786Sps		/*
25460786Sps		 * !! just uses whatever is in shellcmd.
25560786Sps		 * Otherwise, copy cmdbuf to shellcmd,
25660786Sps		 * expanding any special characters ("%" or "#").
25760786Sps		 */
25860786Sps		if (*cbuf != '!')
25960786Sps		{
26060786Sps			if (shellcmd != NULL)
26160786Sps				free(shellcmd);
26260786Sps			shellcmd = fexpand(cbuf);
26360786Sps		}
26460786Sps
26560786Sps		if (secure)
26660786Sps			break;
26760786Sps		if (shellcmd == NULL)
26860786Sps			lsystem("", "!done");
26960786Sps		else
27060786Sps			lsystem(shellcmd, "!done");
27160786Sps		break;
27260786Sps#endif
27360786Sps#if PIPEC
27460786Sps	case A_PIPE:
27560786Sps		if (secure)
27660786Sps			break;
27760786Sps		(void) pipe_mark(pipec, cbuf);
27860786Sps		error("|done", NULL_PARG);
27960786Sps		break;
28060786Sps#endif
28160786Sps	}
28260786Sps}
28360786Sps
28460786Sps/*
28560786Sps * Add a character to a multi-character command.
28660786Sps */
28760786Sps	static int
28860786Spsmca_char(c)
28960786Sps	int c;
29060786Sps{
29160786Sps	char *p;
29260786Sps	int flag;
29360786Sps	char buf[3];
29460786Sps	PARG parg;
29560786Sps
29660786Sps	switch (mca)
29760786Sps	{
29860786Sps	case 0:
29960786Sps		/*
30060786Sps		 * Not in a multicharacter command.
30160786Sps		 */
30260786Sps		return (NO_MCA);
30360786Sps
30460786Sps	case A_PREFIX:
30560786Sps		/*
30660786Sps		 * In the prefix of a command.
30760786Sps		 * This not considered a multichar command
30860786Sps		 * (even tho it uses cmdbuf, etc.).
30960786Sps		 * It is handled in the commands() switch.
31060786Sps		 */
31160786Sps		return (NO_MCA);
31260786Sps
31360786Sps	case A_DIGIT:
31460786Sps		/*
31560786Sps		 * Entering digits of a number.
31660786Sps		 * Terminated by a non-digit.
31760786Sps		 */
318170259Sdelphij		if (!((c >= '0' && c <= '9') || c == '.') &&
31989022Sps		  editchar(c, EC_PEEK|EC_NOHISTORY|EC_NOCOMPLETE|EC_NORIGHTLEFT) == A_INVALID)
32060786Sps		{
32160786Sps			/*
32260786Sps			 * Not part of the number.
32360786Sps			 * Treat as a normal command character.
32460786Sps			 */
325170259Sdelphij			number = cmd_int(&fraction);
32660786Sps			mca = 0;
32760786Sps			cmd_accept();
32860786Sps			return (NO_MCA);
32960786Sps		}
33060786Sps		break;
33160786Sps
33260786Sps	case A_OPT_TOGGLE:
33360786Sps		/*
33460786Sps		 * Special case for the TOGGLE_OPTION command.
33560786Sps		 * If the option letter which was entered is a
33660786Sps		 * single-char option, execute the command immediately,
33760786Sps		 * so user doesn't have to hit RETURN.
33860786Sps		 * If the first char is + or -, this indicates
33960786Sps		 * OPT_UNSET or OPT_SET respectively, instead of OPT_TOGGLE.
34060786Sps		 * "--" begins inputting a long option name.
34160786Sps		 */
34260786Sps		if (optchar == '\0' && len_cmdbuf() == 0)
34360786Sps		{
34460786Sps			flag = (optflag & ~OPT_NO_PROMPT);
34560786Sps			if (flag == OPT_NO_TOGGLE)
34660786Sps			{
34760786Sps				switch (c)
34860786Sps				{
34960786Sps				case '_':
35060786Sps					/* "__" = long option name. */
35160786Sps					optgetname = TRUE;
35260786Sps					mca_opt_toggle();
35360786Sps					return (MCA_MORE);
35460786Sps				}
35560786Sps			} else
35660786Sps			{
35760786Sps				switch (c)
35860786Sps				{
35960786Sps				case '+':
36060786Sps					/* "-+" = UNSET. */
36160786Sps					optflag = (flag == OPT_UNSET) ?
36260786Sps						OPT_TOGGLE : OPT_UNSET;
36360786Sps					mca_opt_toggle();
36460786Sps					return (MCA_MORE);
36560786Sps				case '!':
36660786Sps					/* "-!" = SET */
36760786Sps					optflag = (flag == OPT_SET) ?
36860786Sps						OPT_TOGGLE : OPT_SET;
36960786Sps					mca_opt_toggle();
37060786Sps					return (MCA_MORE);
37160786Sps				case CONTROL('P'):
37260786Sps					optflag ^= OPT_NO_PROMPT;
37360786Sps					mca_opt_toggle();
37460786Sps					return (MCA_MORE);
37560786Sps				case '-':
37660786Sps					/* "--" = long option name. */
37760786Sps					optgetname = TRUE;
37860786Sps					mca_opt_toggle();
37960786Sps					return (MCA_MORE);
38060786Sps				}
38160786Sps			}
38260786Sps		}
38360786Sps		if (optgetname)
38460786Sps		{
38560786Sps			/*
38660786Sps			 * We're getting a long option name.
38760786Sps			 * See if we've matched an option name yet.
38860786Sps			 * If so, display the complete name and stop
38960786Sps			 * accepting chars until user hits RETURN.
39060786Sps			 */
391128348Stjr			struct loption *o;
39260786Sps			char *oname;
39360786Sps			int lc;
39460786Sps
39560786Sps			if (c == '\n' || c == '\r')
39660786Sps			{
39760786Sps				/*
39860786Sps				 * When the user hits RETURN, make sure
39960786Sps				 * we've matched an option name, then
40060786Sps				 * pretend he just entered the equivalent
40160786Sps				 * option letter.
40260786Sps				 */
40360786Sps				if (optchar == '\0')
40460786Sps				{
40560786Sps					parg.p_string = get_cmdbuf();
40660786Sps					error("There is no --%s option", &parg);
40760786Sps					return (MCA_DONE);
40860786Sps				}
40960786Sps				optgetname = FALSE;
41060786Sps				cmd_reset();
41160786Sps				c = optchar;
41260786Sps			} else
41360786Sps			{
41460786Sps				if (optchar != '\0')
41560786Sps				{
41660786Sps					/*
41760786Sps					 * Already have a match for the name.
41860786Sps					 * Don't accept anything but erase/kill.
41960786Sps					 */
420161478Sdelphij					if (c == erase_char ||
421161478Sdelphij					    c == erase2_char ||
422161478Sdelphij					    c == kill_char)
42360786Sps						return (MCA_DONE);
42460786Sps					return (MCA_MORE);
42560786Sps				}
42660786Sps				/*
42760786Sps				 * Add char to cmd buffer and try to match
42860786Sps				 * the option name.
42960786Sps				 */
43060786Sps				if (cmd_char(c) == CC_QUIT)
43160786Sps					return (MCA_DONE);
43260786Sps				p = get_cmdbuf();
433161478Sdelphij				lc = ASCII_IS_LOWER(p[0]);
43460786Sps				o = findopt_name(&p, &oname, NULL);
43560786Sps				if (o != NULL)
43660786Sps				{
43760786Sps					/*
43860786Sps					 * Got a match.
43960786Sps					 * Remember the option letter and
44060786Sps					 * display the full option name.
44160786Sps					 */
44260786Sps					optchar = o->oletter;
443161478Sdelphij					if (!lc && ASCII_IS_LOWER(optchar))
444161478Sdelphij						optchar = ASCII_TO_UPPER(optchar);
44560786Sps					cmd_reset();
44660786Sps					mca_opt_toggle();
44760786Sps					for (p = oname;  *p != '\0';  p++)
44860786Sps					{
44960786Sps						c = *p;
450161478Sdelphij						if (!lc && ASCII_IS_LOWER(c))
451161478Sdelphij							c = ASCII_TO_UPPER(c);
45260786Sps						if (cmd_char(c) != CC_OK)
45360786Sps							return (MCA_DONE);
45460786Sps					}
45560786Sps				}
45660786Sps				return (MCA_MORE);
45760786Sps			}
45860786Sps		} else
45960786Sps		{
460161478Sdelphij			if (c == erase_char || c == erase2_char || c == kill_char)
46160786Sps				break;
46260786Sps			if (optchar != '\0')
46360786Sps				/* We already have the option letter. */
46460786Sps				break;
46560786Sps		}
46660786Sps
46760786Sps		optchar = c;
46860786Sps		if ((optflag & ~OPT_NO_PROMPT) != OPT_TOGGLE ||
46960786Sps		    single_char_option(c))
47060786Sps		{
47160786Sps			toggle_option(c, "", optflag);
47260786Sps			return (MCA_DONE);
47360786Sps		}
47460786Sps		/*
47560786Sps		 * Display a prompt appropriate for the option letter.
47660786Sps		 */
47760786Sps		if ((p = opt_prompt(c)) == NULL)
47860786Sps		{
47960786Sps			buf[0] = '-';
48060786Sps			buf[1] = c;
48160786Sps			buf[2] = '\0';
48260786Sps			p = buf;
48360786Sps		}
48460786Sps		start_mca(A_OPT_TOGGLE, p, (void*)NULL, 0);
48560786Sps		return (MCA_MORE);
48660786Sps
48760786Sps	case A_F_SEARCH:
48860786Sps	case A_B_SEARCH:
489191930Sdelphij	case A_FILTER:
49060786Sps		/*
49160786Sps		 * Special case for search commands.
49260786Sps		 * Certain characters as the first char of
49360786Sps		 * the pattern have special meaning:
49460786Sps		 *	!  Toggle the NO_MATCH flag
49560786Sps		 *	*  Toggle the PAST_EOF flag
49660786Sps		 *	@  Toggle the FIRST_FILE flag
49760786Sps		 */
49860786Sps		if (len_cmdbuf() > 0)
49960786Sps			/*
50060786Sps			 * Only works for the first char of the pattern.
50160786Sps			 */
50260786Sps			break;
50360786Sps
50460786Sps		flag = 0;
50560786Sps		switch (c)
50660786Sps		{
50760812Sps		case '*':
508170259Sdelphij			if (less_is_more)
50960812Sps				break;
51060786Sps		case CONTROL('E'): /* ignore END of file */
511191930Sdelphij			if (mca != A_FILTER)
512191930Sdelphij				flag = SRCH_PAST_EOF;
51360786Sps			break;
51460812Sps		case '@':
515170259Sdelphij			if (less_is_more)
51660812Sps				break;
51760786Sps		case CONTROL('F'): /* FIRST file */
518191930Sdelphij			if (mca != A_FILTER)
519191930Sdelphij				flag = SRCH_FIRST_FILE;
52060786Sps			break;
52160786Sps		case CONTROL('K'): /* KEEP position */
522191930Sdelphij			if (mca != A_FILTER)
523191930Sdelphij				flag = SRCH_NO_MOVE;
52460786Sps			break;
52560786Sps		case CONTROL('R'): /* Don't use REGULAR EXPRESSIONS */
52660786Sps			flag = SRCH_NO_REGEX;
52760786Sps			break;
52860786Sps		case CONTROL('N'): /* NOT match */
52960786Sps		case '!':
53060786Sps			flag = SRCH_NO_MATCH;
53160786Sps			break;
53260786Sps		}
53360786Sps		if (flag != 0)
53460786Sps		{
53560786Sps			search_type ^= flag;
53660786Sps			mca_search();
53760786Sps			return (MCA_MORE);
53860786Sps		}
53960786Sps		break;
54060786Sps	}
54160786Sps
54260786Sps	/*
54360786Sps	 * Any other multicharacter command
54460786Sps	 * is terminated by a newline.
54560786Sps	 */
54660786Sps	if (c == '\n' || c == '\r')
54760786Sps	{
54860786Sps		/*
54960786Sps		 * Execute the command.
55060786Sps		 */
55160786Sps		exec_mca();
55260786Sps		return (MCA_DONE);
55360786Sps	}
55460786Sps
55560786Sps	/*
55660786Sps	 * Append the char to the command buffer.
55760786Sps	 */
55860786Sps	if (cmd_char(c) == CC_QUIT)
55960786Sps		/*
56060786Sps		 * Abort the multi-char command.
56160786Sps		 */
56260786Sps		return (MCA_DONE);
56360786Sps
56460786Sps	if ((mca == A_F_BRACKET || mca == A_B_BRACKET) && len_cmdbuf() >= 2)
56560786Sps	{
56660786Sps		/*
56760786Sps		 * Special case for the bracket-matching commands.
56860786Sps		 * Execute the command after getting exactly two
56960786Sps		 * characters from the user.
57060786Sps		 */
57160786Sps		exec_mca();
57260786Sps		return (MCA_DONE);
57360786Sps	}
57460786Sps
57560786Sps	/*
57660786Sps	 * Need another character.
57760786Sps	 */
57860786Sps	return (MCA_MORE);
57960786Sps}
58060786Sps
58160786Sps/*
582173685Sdelphij * Discard any buffered file data.
583173685Sdelphij */
584173685Sdelphij	static void
585173685Sdelphijclear_buffers()
586173685Sdelphij{
587173685Sdelphij	if (!(ch_getflags() & CH_CANSEEK))
588173685Sdelphij		return;
589173685Sdelphij	ch_flush();
590173685Sdelphij	clr_linenum();
591173685Sdelphij#if HILITE_SEARCH
592173685Sdelphij	clr_hilite();
593173685Sdelphij#endif
594173685Sdelphij}
595173685Sdelphij
596173685Sdelphij/*
59760786Sps * Make sure the screen is displayed.
59860786Sps */
59960786Sps	static void
60060786Spsmake_display()
60160786Sps{
60260786Sps	/*
60360786Sps	 * If nothing is displayed yet, display starting from initial_scrpos.
60460786Sps	 */
60560786Sps	if (empty_screen())
60660786Sps	{
60760786Sps		if (initial_scrpos.pos == NULL_POSITION)
60860786Sps			/*
60960786Sps			 * {{ Maybe this should be:
61060786Sps			 *    jump_loc(ch_zero(), jump_sline);
61160786Sps			 *    but this behavior seems rather unexpected
61260786Sps			 *    on the first screen. }}
61360786Sps			 */
61460786Sps			jump_loc(ch_zero(), 1);
61560786Sps		else
61660786Sps			jump_loc(initial_scrpos.pos, initial_scrpos.ln);
61760786Sps	} else if (screen_trashed)
61860786Sps	{
619173685Sdelphij		int save_top_scroll = top_scroll;
620173685Sdelphij		int save_ignore_eoi = ignore_eoi;
62160786Sps		top_scroll = 1;
622173685Sdelphij		ignore_eoi = 0;
623173685Sdelphij		if (screen_trashed == 2)
624173685Sdelphij		{
625173685Sdelphij			/* Special case used by ignore_eoi: re-open the input file
626173685Sdelphij			 * and jump to the end of the file. */
627173685Sdelphij			reopen_curr_ifile();
628173685Sdelphij			jump_forw();
629173685Sdelphij		}
63060786Sps		repaint();
63160786Sps		top_scroll = save_top_scroll;
632173685Sdelphij		ignore_eoi = save_ignore_eoi;
63360786Sps	}
63460786Sps}
63560786Sps
63660786Sps/*
63760786Sps * Display the appropriate prompt.
63860786Sps */
63960786Sps	static void
64060786Spsprompt()
64160786Sps{
64260786Sps	register char *p;
64360786Sps
64460786Sps	if (ungotp != NULL && ungotp > ungot)
64560786Sps	{
64660786Sps		/*
64760786Sps		 * No prompt necessary if commands are from
64860786Sps		 * ungotten chars rather than from the user.
64960786Sps		 */
65060786Sps		return;
65160786Sps	}
65260786Sps
65360786Sps	/*
65460786Sps	 * Make sure the screen is displayed.
65560786Sps	 */
65660786Sps	make_display();
65760786Sps	bottompos = position(BOTTOM_PLUS_ONE);
65860786Sps
65960786Sps	/*
660191930Sdelphij	 * If we've hit EOF on the last file and the -E flag is set, quit.
66160786Sps	 */
662191930Sdelphij	if (get_quit_at_eof() == OPT_ONPLUS &&
663191930Sdelphij	    eof_displayed() && !(ch_getflags() & CH_HELPFILE) &&
66460786Sps	    next_ifile(curr_ifile) == NULL_IFILE)
66560786Sps		quit(QUIT_OK);
666191930Sdelphij
66760786Sps	/*
668191930Sdelphij	 * If the entire file is displayed and the -F flag is set, quit.
66960786Sps	 */
670191930Sdelphij	if (quit_if_one_screen &&
671191930Sdelphij	    entire_file_displayed() && !(ch_getflags() & CH_HELPFILE) &&
67260786Sps	    next_ifile(curr_ifile) == NULL_IFILE)
67360786Sps		quit(QUIT_OK);
67460786Sps
67589022Sps#if MSDOS_COMPILER==WIN32C
67689022Sps	/*
67789022Sps	 * In Win32, display the file name in the window title.
67889022Sps	 */
67989022Sps	if (!(ch_getflags() & CH_HELPFILE))
68089022Sps		SetConsoleTitle(pr_expand("Less?f - %f.", 0));
68189022Sps#endif
68260786Sps	/*
68360786Sps	 * Select the proper prompt and display it.
68460786Sps	 */
685170259Sdelphij	/*
686170259Sdelphij	 * If the previous action was a forward movement,
687170259Sdelphij	 * don't clear the bottom line of the display;
688170259Sdelphij	 * just print the prompt since the forward movement guarantees
689170259Sdelphij	 * that we're in the right position to display the prompt.
690170259Sdelphij	 * Clearing the line could cause a problem: for example, if the last
691170259Sdelphij	 * line displayed ended at the right screen edge without a newline,
692170259Sdelphij	 * then clearing would clear the last displayed line rather than
693170259Sdelphij	 * the prompt line.
694170259Sdelphij	 */
695170259Sdelphij	if (!forw_prompt)
696170259Sdelphij		clear_bot();
69760786Sps	clear_cmd();
698170259Sdelphij	forw_prompt = 0;
69960786Sps	p = pr_string();
700191930Sdelphij	if (is_filtering())
701191930Sdelphij		putstr("& ");
702161478Sdelphij	if (p == NULL || *p == '\0')
70360786Sps		putchr(':');
70460786Sps	else
70560786Sps	{
706161478Sdelphij		at_enter(AT_STANDOUT);
70760786Sps		putstr(p);
708161478Sdelphij		at_exit();
70960786Sps	}
710170259Sdelphij	clear_eol();
71160786Sps}
71260786Sps
71360786Sps/*
71460786Sps * Display the less version message.
71560786Sps */
71660786Sps	public void
71760786Spsdispversion()
71860786Sps{
71960786Sps	PARG parg;
72060786Sps
72160786Sps	parg.p_string = version;
72260786Sps	error("less %s", &parg);
72360786Sps}
72460786Sps
72560786Sps/*
72660786Sps * Get command character.
72760786Sps * The character normally comes from the keyboard,
72860786Sps * but may come from ungotten characters
72960786Sps * (characters previously given to ungetcc or ungetsc).
73060786Sps */
73160786Sps	public int
73260786Spsgetcc()
73360786Sps{
73460786Sps	if (ungotp == NULL)
73560786Sps		/*
73660786Sps		 * Normal case: no ungotten chars, so get one from the user.
73760786Sps		 */
73860786Sps		return (getchr());
73960786Sps
74060786Sps	if (ungotp > ungot)
74160786Sps		/*
74260786Sps		 * Return the next ungotten char.
74360786Sps		 */
74460786Sps		return (*--ungotp);
74560786Sps
74660786Sps	/*
74760786Sps	 * We have just run out of ungotten chars.
74860786Sps	 */
74960786Sps	ungotp = NULL;
75060786Sps	if (len_cmdbuf() == 0 || !empty_screen())
75160786Sps		return (getchr());
75260786Sps	/*
75360786Sps	 * Command is incomplete, so try to complete it.
75460786Sps	 */
75560786Sps	switch (mca)
75660786Sps	{
75760786Sps	case A_DIGIT:
75860786Sps		/*
75960786Sps		 * We have a number but no command.  Treat as #g.
76060786Sps		 */
76160786Sps		return ('g');
76260786Sps
76360786Sps	case A_F_SEARCH:
76460786Sps	case A_B_SEARCH:
76560786Sps		/*
76660786Sps		 * We have "/string" but no newline.  Add the \n.
76760786Sps		 */
76860786Sps		return ('\n');
76960786Sps
77060786Sps	default:
77160786Sps		/*
77260786Sps		 * Some other incomplete command.  Let user complete it.
77360786Sps		 */
77460786Sps		return (getchr());
77560786Sps	}
77660786Sps}
77760786Sps
77860786Sps/*
77960786Sps * "Unget" a command character.
78060786Sps * The next getcc() will return this character.
78160786Sps */
78260786Sps	public void
78360786Spsungetcc(c)
78460786Sps	int c;
78560786Sps{
78660786Sps	if (ungotp == NULL)
78760786Sps		ungotp = ungot;
78860786Sps	if (ungotp >= ungot + sizeof(ungot))
78960786Sps	{
79060786Sps		error("ungetcc overflow", NULL_PARG);
79160786Sps		quit(QUIT_ERROR);
79260786Sps	}
79360786Sps	*ungotp++ = c;
79460786Sps}
79560786Sps
79660786Sps/*
79760786Sps * Unget a whole string of command characters.
79860786Sps * The next sequence of getcc()'s will return this string.
79960786Sps */
80060786Sps	public void
80160786Spsungetsc(s)
80260786Sps	char *s;
80360786Sps{
80460786Sps	register char *p;
80560786Sps
80660786Sps	for (p = s + strlen(s) - 1;  p >= s;  p--)
80760786Sps		ungetcc(*p);
80860786Sps}
80960786Sps
81060786Sps/*
81160786Sps * Search for a pattern, possibly in multiple files.
81260786Sps * If SRCH_FIRST_FILE is set, begin searching at the first file.
81360786Sps * If SRCH_PAST_EOF is set, continue the search thru multiple files.
81460786Sps */
81560786Sps	static void
81660786Spsmulti_search(pattern, n)
81760786Sps	char *pattern;
81860786Sps	int n;
81960786Sps{
82060786Sps	register int nomore;
82160786Sps	IFILE save_ifile;
82260786Sps	int changed_file;
82360786Sps
82460786Sps	changed_file = 0;
82560786Sps	save_ifile = save_curr_ifile();
82660786Sps
82760786Sps	if (search_type & SRCH_FIRST_FILE)
82860786Sps	{
82960786Sps		/*
83060786Sps		 * Start at the first (or last) file
83160786Sps		 * in the command line list.
83260786Sps		 */
83360786Sps		if (search_type & SRCH_FORW)
83460786Sps			nomore = edit_first();
83560786Sps		else
83660786Sps			nomore = edit_last();
83760786Sps		if (nomore)
83860786Sps		{
83960786Sps			unsave_ifile(save_ifile);
84060786Sps			return;
84160786Sps		}
84260786Sps		changed_file = 1;
84360786Sps		search_type &= ~SRCH_FIRST_FILE;
84460786Sps	}
84560786Sps
84660786Sps	for (;;)
84760786Sps	{
84860786Sps		n = search(search_type, pattern, n);
84960786Sps		/*
85060786Sps		 * The SRCH_NO_MOVE flag doesn't "stick": it gets cleared
85160786Sps		 * after being used once.  This allows "n" to work after
85260786Sps		 * using a /@@ search.
85360786Sps		 */
85460786Sps		search_type &= ~SRCH_NO_MOVE;
85560786Sps		if (n == 0)
85660786Sps		{
85760786Sps			/*
85860786Sps			 * Found it.
85960786Sps			 */
86060786Sps			unsave_ifile(save_ifile);
86160786Sps			return;
86260786Sps		}
86360786Sps
86460786Sps		if (n < 0)
86560786Sps			/*
86660786Sps			 * Some kind of error in the search.
86760786Sps			 * Error message has been printed by search().
86860786Sps			 */
86960786Sps			break;
87060786Sps
87160786Sps		if ((search_type & SRCH_PAST_EOF) == 0)
87260786Sps			/*
87360786Sps			 * We didn't find a match, but we're
87460786Sps			 * supposed to search only one file.
87560786Sps			 */
87660786Sps			break;
87760786Sps		/*
87860786Sps		 * Move on to the next file.
87960786Sps		 */
88060786Sps		if (search_type & SRCH_FORW)
88160786Sps			nomore = edit_next(1);
88260786Sps		else
88360786Sps			nomore = edit_prev(1);
88460786Sps		if (nomore)
88560786Sps			break;
88660786Sps		changed_file = 1;
88760786Sps	}
88860786Sps
88960786Sps	/*
89060786Sps	 * Didn't find it.
89160786Sps	 * Print an error message if we haven't already.
89260786Sps	 */
89360786Sps	if (n > 0)
89460786Sps		error("Pattern not found", NULL_PARG);
89560786Sps
89660786Sps	if (changed_file)
89760786Sps	{
89860786Sps		/*
89960786Sps		 * Restore the file we were originally viewing.
90060786Sps		 */
90160786Sps		reedit_ifile(save_ifile);
902161478Sdelphij	} else
903161478Sdelphij	{
904161478Sdelphij		unsave_ifile(save_ifile);
90560786Sps	}
90660786Sps}
90760786Sps
90860786Sps/*
90960786Sps * Main command processor.
91060786Sps * Accept and execute commands until a quit command.
91160786Sps */
91260786Sps	public void
91360786Spscommands()
91460786Sps{
91560786Sps	register int c;
91660786Sps	register int action;
91760786Sps	register char *cbuf;
91860786Sps	int newaction;
91960786Sps	int save_search_type;
92060786Sps	char *extra;
92160786Sps	char tbuf[2];
92260786Sps	PARG parg;
92360786Sps	IFILE old_ifile;
92460786Sps	IFILE new_ifile;
92589022Sps	char *tagfile;
92660786Sps
92760786Sps	search_type = SRCH_FORW;
92860786Sps	wscroll = (sc_height + 1) / 2;
92960786Sps	newaction = A_NOACTION;
93060786Sps
93160786Sps	for (;;)
93260786Sps	{
93360786Sps		mca = 0;
93460786Sps		cmd_accept();
93560786Sps		number = 0;
93660786Sps		optchar = '\0';
93760786Sps
93860786Sps		/*
93960786Sps		 * See if any signals need processing.
94060786Sps		 */
94160786Sps		if (sigs)
94260786Sps		{
94360786Sps			psignals();
94460786Sps			if (quitting)
94560786Sps				quit(QUIT_SAVED_STATUS);
94660786Sps		}
94760786Sps
94860786Sps		/*
94960786Sps		 * See if window size changed, for systems that don't
95060786Sps		 * generate SIGWINCH.
95160786Sps		 */
95260786Sps		check_winch();
95360786Sps
95460786Sps		/*
95560786Sps		 * Display prompt and accept a character.
95660786Sps		 */
95760786Sps		cmd_reset();
95860786Sps		prompt();
95960786Sps		if (sigs)
96060786Sps			continue;
96160786Sps		if (newaction == A_NOACTION)
96260786Sps			c = getcc();
96360786Sps
96460786Sps	again:
96560786Sps		if (sigs)
96660786Sps			continue;
96760786Sps
96860786Sps		if (newaction != A_NOACTION)
96960786Sps		{
97060786Sps			action = newaction;
97160786Sps			newaction = A_NOACTION;
97260786Sps		} else
97360786Sps		{
97460786Sps			/*
97560786Sps			 * If we are in a multicharacter command, call mca_char.
97660786Sps			 * Otherwise we call fcmd_decode to determine the
97760786Sps			 * action to be performed.
97860786Sps			 */
97960786Sps			if (mca)
98060786Sps				switch (mca_char(c))
98160786Sps				{
98260786Sps				case MCA_MORE:
98360786Sps					/*
98460786Sps					 * Need another character.
98560786Sps					 */
98660786Sps					c = getcc();
98760786Sps					goto again;
98860786Sps				case MCA_DONE:
98960786Sps					/*
99060786Sps					 * Command has been handled by mca_char.
99160786Sps					 * Start clean with a prompt.
99260786Sps					 */
99360786Sps					continue;
99460786Sps				case NO_MCA:
99560786Sps					/*
99660786Sps					 * Not a multi-char command
99760786Sps					 * (at least, not anymore).
99860786Sps					 */
99960786Sps					break;
100060786Sps				}
100160786Sps
100260786Sps			/*
100360786Sps			 * Decode the command character and decide what to do.
100460786Sps			 */
100560786Sps			if (mca)
100660786Sps			{
100760786Sps				/*
100860786Sps				 * We're in a multichar command.
100960786Sps				 * Add the character to the command buffer
101060786Sps				 * and display it on the screen.
101160786Sps				 * If the user backspaces past the start
101260786Sps				 * of the line, abort the command.
101360786Sps				 */
101460786Sps				if (cmd_char(c) == CC_QUIT || len_cmdbuf() == 0)
101560786Sps					continue;
101660786Sps				cbuf = get_cmdbuf();
101760786Sps			} else
101860786Sps			{
101960786Sps				/*
102060786Sps				 * Don't use cmd_char if we're starting fresh
102160786Sps				 * at the beginning of a command, because we
102260786Sps				 * don't want to echo the command until we know
102360786Sps				 * it is a multichar command.  We also don't
102460786Sps				 * want erase_char/kill_char to be treated
102560786Sps				 * as line editing characters.
102660786Sps				 */
102760786Sps				tbuf[0] = c;
102860786Sps				tbuf[1] = '\0';
102960786Sps				cbuf = tbuf;
103060786Sps			}
103160786Sps			extra = NULL;
103260786Sps			action = fcmd_decode(cbuf, &extra);
103360786Sps			/*
103460786Sps			 * If an "extra" string was returned,
103560786Sps			 * process it as a string of command characters.
103660786Sps			 */
103760786Sps			if (extra != NULL)
103860786Sps				ungetsc(extra);
103960786Sps		}
104060786Sps		/*
104160786Sps		 * Clear the cmdbuf string.
104260786Sps		 * (But not if we're in the prefix of a command,
104360786Sps		 * because the partial command string is kept there.)
104460786Sps		 */
104560786Sps		if (action != A_PREFIX)
104660786Sps			cmd_reset();
104760786Sps
104860786Sps		switch (action)
104960786Sps		{
105060786Sps		case A_DIGIT:
105160786Sps			/*
105260786Sps			 * First digit of a number.
105360786Sps			 */
105460786Sps			start_mca(A_DIGIT, ":", (void*)NULL, CF_QUIT_ON_ERASE);
105560786Sps			goto again;
105660786Sps
105760786Sps		case A_F_WINDOW:
105860786Sps			/*
105960786Sps			 * Forward one window (and set the window size).
106060786Sps			 */
106160786Sps			if (number > 0)
1062128348Stjr				swindow = (int) number;
106360786Sps			/* FALLTHRU */
106460786Sps		case A_F_SCREEN:
106560786Sps			/*
106660786Sps			 * Forward one screen.
106760786Sps			 */
106860786Sps			if (number <= 0)
106960786Sps				number = get_swindow();
107060786Sps			cmd_exec();
107160786Sps			if (show_attn)
107260786Sps				set_attnpos(bottompos);
1073128348Stjr			forward((int) number, 0, 1);
107460786Sps			break;
107560786Sps
107660786Sps		case A_B_WINDOW:
107760786Sps			/*
107860786Sps			 * Backward one window (and set the window size).
107960786Sps			 */
108060786Sps			if (number > 0)
1081128348Stjr				swindow = (int) number;
108260786Sps			/* FALLTHRU */
108360786Sps		case A_B_SCREEN:
108460786Sps			/*
108560786Sps			 * Backward one screen.
108660786Sps			 */
108760786Sps			if (number <= 0)
108860786Sps				number = get_swindow();
108960786Sps			cmd_exec();
1090128348Stjr			backward((int) number, 0, 1);
109160786Sps			break;
109260786Sps
109360786Sps		case A_F_LINE:
109460786Sps			/*
109560786Sps			 * Forward N (default 1) line.
109660786Sps			 */
109760786Sps			if (number <= 0)
109860786Sps				number = 1;
109960786Sps			cmd_exec();
110060786Sps			if (show_attn == OPT_ONPLUS && number > 1)
110160786Sps				set_attnpos(bottompos);
1102128348Stjr			forward((int) number, 0, 0);
110360786Sps			break;
110460786Sps
110560786Sps		case A_B_LINE:
110660786Sps			/*
110760786Sps			 * Backward N (default 1) line.
110860786Sps			 */
110960786Sps			if (number <= 0)
111060786Sps				number = 1;
111160786Sps			cmd_exec();
1112128348Stjr			backward((int) number, 0, 0);
111360786Sps			break;
111460786Sps
111560786Sps		case A_FF_LINE:
111660786Sps			/*
111760786Sps			 * Force forward N (default 1) line.
111860786Sps			 */
111960786Sps			if (number <= 0)
112060786Sps				number = 1;
112160786Sps			cmd_exec();
112260786Sps			if (show_attn == OPT_ONPLUS && number > 1)
112360786Sps				set_attnpos(bottompos);
1124128348Stjr			forward((int) number, 1, 0);
112560786Sps			break;
112660786Sps
112760786Sps		case A_BF_LINE:
112860786Sps			/*
112960786Sps			 * Force backward N (default 1) line.
113060786Sps			 */
113160786Sps			if (number <= 0)
113260786Sps				number = 1;
113360786Sps			cmd_exec();
1134128348Stjr			backward((int) number, 1, 0);
113560786Sps			break;
113660786Sps
113760786Sps		case A_FF_SCREEN:
113860786Sps			/*
113960786Sps			 * Force forward one screen.
114060786Sps			 */
114160786Sps			if (number <= 0)
114260786Sps				number = get_swindow();
114360786Sps			cmd_exec();
114460786Sps			if (show_attn == OPT_ONPLUS)
114560786Sps				set_attnpos(bottompos);
1146128348Stjr			forward((int) number, 1, 0);
114760786Sps			break;
114860786Sps
114960786Sps		case A_F_FOREVER:
115060786Sps			/*
115160786Sps			 * Forward forever, ignoring EOF.
115260786Sps			 */
115360786Sps			if (ch_getflags() & CH_HELPFILE)
115460786Sps				break;
115560786Sps			cmd_exec();
115660786Sps			jump_forw();
115760786Sps			ignore_eoi = 1;
115860786Sps			while (!sigs)
1159173685Sdelphij			{
1160173685Sdelphij				make_display();
116160786Sps				forward(1, 0, 0);
1162173685Sdelphij			}
116360786Sps			ignore_eoi = 0;
116460786Sps			/*
116560786Sps			 * This gets us back in "F mode" after processing
116660786Sps			 * a non-abort signal (e.g. window-change).
116760786Sps			 */
116860786Sps			if (sigs && !ABORT_SIGS())
116960786Sps				newaction = A_F_FOREVER;
117060786Sps			break;
117160786Sps
117260786Sps		case A_F_SCROLL:
117360786Sps			/*
117460786Sps			 * Forward N lines
117560786Sps			 * (default same as last 'd' or 'u' command).
117660786Sps			 */
117760786Sps			if (number > 0)
1178128348Stjr				wscroll = (int) number;
117960786Sps			cmd_exec();
118060786Sps			if (show_attn == OPT_ONPLUS)
118160786Sps				set_attnpos(bottompos);
118260786Sps			forward(wscroll, 0, 0);
118360786Sps			break;
118460786Sps
118560786Sps		case A_B_SCROLL:
118660786Sps			/*
118760786Sps			 * Forward N lines
118860786Sps			 * (default same as last 'd' or 'u' command).
118960786Sps			 */
119060786Sps			if (number > 0)
1191128348Stjr				wscroll = (int) number;
119260786Sps			cmd_exec();
119360786Sps			backward(wscroll, 0, 0);
119460786Sps			break;
119560786Sps
119660786Sps		case A_FREPAINT:
119760786Sps			/*
119860786Sps			 * Flush buffers, then repaint screen.
119960786Sps			 * Don't flush the buffers on a pipe!
120060786Sps			 */
1201173685Sdelphij			clear_buffers();
120260786Sps			/* FALLTHRU */
120360786Sps		case A_REPAINT:
120460786Sps			/*
120560786Sps			 * Repaint screen.
120660786Sps			 */
120760786Sps			cmd_exec();
120860786Sps			repaint();
120960786Sps			break;
121060786Sps
121160786Sps		case A_GOLINE:
121260786Sps			/*
121360786Sps			 * Go to line N, default beginning of file.
121460786Sps			 */
121560786Sps			if (number <= 0)
121660786Sps				number = 1;
121760786Sps			cmd_exec();
121860786Sps			jump_back(number);
121960786Sps			break;
122060786Sps
122160786Sps		case A_PERCENT:
122260786Sps			/*
122360786Sps			 * Go to a specified percentage into the file.
122460786Sps			 */
122560786Sps			if (number < 0)
1226170259Sdelphij			{
122760786Sps				number = 0;
1228170259Sdelphij				fraction = 0;
1229170259Sdelphij			}
123060786Sps			if (number > 100)
1231170259Sdelphij			{
123260786Sps				number = 100;
1233170259Sdelphij				fraction = 0;
1234170259Sdelphij			}
123560786Sps			cmd_exec();
1236170259Sdelphij			jump_percent((int) number, fraction);
123760786Sps			break;
123860786Sps
123960786Sps		case A_GOEND:
124060786Sps			/*
124160786Sps			 * Go to line N, default end of file.
124260786Sps			 */
124360786Sps			cmd_exec();
124460786Sps			if (number <= 0)
124560786Sps				jump_forw();
124660786Sps			else
124760786Sps				jump_back(number);
124860786Sps			break;
124960786Sps
125060786Sps		case A_GOPOS:
125160786Sps			/*
125260786Sps			 * Go to a specified byte position in the file.
125360786Sps			 */
125460786Sps			cmd_exec();
125560786Sps			if (number < 0)
125660786Sps				number = 0;
1257128348Stjr			jump_line_loc((POSITION) number, jump_sline);
125860786Sps			break;
125960786Sps
126060786Sps		case A_STAT:
126160786Sps			/*
126260786Sps			 * Print file name, etc.
126360786Sps			 */
126460786Sps			if (ch_getflags() & CH_HELPFILE)
126560786Sps				break;
126660786Sps			cmd_exec();
126760786Sps			parg.p_string = eq_message();
126860786Sps			error("%s", &parg);
126960786Sps			break;
127060786Sps
127160786Sps		case A_VERSION:
127260786Sps			/*
127360786Sps			 * Print version number, without the "@(#)".
127460786Sps			 */
127560786Sps			cmd_exec();
127660786Sps			dispversion();
127760786Sps			break;
127860786Sps
127960786Sps		case A_QUIT:
128060786Sps			/*
128160786Sps			 * Exit.
128260786Sps			 */
128360786Sps			if (curr_ifile != NULL_IFILE &&
128460786Sps			    ch_getflags() & CH_HELPFILE)
128560786Sps			{
128660786Sps				/*
128760786Sps				 * Quit while viewing the help file
128860786Sps				 * just means return to viewing the
128960786Sps				 * previous file.
129060786Sps				 */
1291161478Sdelphij				hshift = save_hshift;
129260786Sps				if (edit_prev(1) == 0)
129360786Sps					break;
129460786Sps			}
129560786Sps			if (extra != NULL)
129660786Sps				quit(*extra);
129760786Sps			quit(QUIT_OK);
129860786Sps			break;
129960786Sps
130060786Sps/*
130160786Sps * Define abbreviation for a commonly used sequence below.
130260786Sps */
1303173685Sdelphij#define	DO_SEARCH() \
1304173685Sdelphij			if (number <= 0) number = 1;	\
130560786Sps			mca_search();			\
130660786Sps			cmd_exec();			\
1307128348Stjr			multi_search((char *)NULL, (int) number);
130860786Sps
130960786Sps
131060786Sps		case A_F_SEARCH:
131160786Sps			/*
131260786Sps			 * Search forward for a pattern.
131360786Sps			 * Get the first char of the pattern.
131460786Sps			 */
131560786Sps			search_type = SRCH_FORW;
131660786Sps			if (number <= 0)
131760786Sps				number = 1;
131860786Sps			mca_search();
131960786Sps			c = getcc();
132060786Sps			goto again;
132160786Sps
132260786Sps		case A_B_SEARCH:
132360786Sps			/*
132460786Sps			 * Search backward for a pattern.
132560786Sps			 * Get the first char of the pattern.
132660786Sps			 */
132760786Sps			search_type = SRCH_BACK;
132860786Sps			if (number <= 0)
132960786Sps				number = 1;
133060786Sps			mca_search();
133160786Sps			c = getcc();
133260786Sps			goto again;
133360786Sps
1334191930Sdelphij		case A_FILTER:
1335191930Sdelphij#if HILITE_SEARCH
1336191930Sdelphij			search_type = SRCH_FORW | SRCH_FILTER;
1337191930Sdelphij			mca_search();
1338191930Sdelphij			c = getcc();
1339191930Sdelphij			goto again;
1340191930Sdelphij#else
1341191930Sdelphij			error("Command not available", NULL_PARG);
1342191930Sdelphij			break;
1343191930Sdelphij#endif
1344191930Sdelphij
134560786Sps		case A_AGAIN_SEARCH:
134660786Sps			/*
134760786Sps			 * Repeat previous search.
134860786Sps			 */
134960786Sps			DO_SEARCH();
135060786Sps			break;
135160786Sps
135260786Sps		case A_T_AGAIN_SEARCH:
135360786Sps			/*
135460786Sps			 * Repeat previous search, multiple files.
135560786Sps			 */
135660786Sps			search_type |= SRCH_PAST_EOF;
135760786Sps			DO_SEARCH();
135860786Sps			break;
135960786Sps
136060786Sps		case A_REVERSE_SEARCH:
136160786Sps			/*
136260786Sps			 * Repeat previous search, in reverse direction.
136360786Sps			 */
136460786Sps			save_search_type = search_type;
136560786Sps			search_type = SRCH_REVERSE(search_type);
136660786Sps			DO_SEARCH();
136760786Sps			search_type = save_search_type;
136860786Sps			break;
136960786Sps
137060786Sps		case A_T_REVERSE_SEARCH:
137160786Sps			/*
137260786Sps			 * Repeat previous search,
137360786Sps			 * multiple files in reverse direction.
137460786Sps			 */
137560786Sps			save_search_type = search_type;
137660786Sps			search_type = SRCH_REVERSE(search_type);
137760786Sps			search_type |= SRCH_PAST_EOF;
137860786Sps			DO_SEARCH();
137960786Sps			search_type = save_search_type;
138060786Sps			break;
138160786Sps
138260786Sps		case A_UNDO_SEARCH:
138360786Sps			undo_search();
138460786Sps			break;
138560786Sps
138660786Sps		case A_HELP:
138760786Sps			/*
138860786Sps			 * Help.
138960786Sps			 */
139060786Sps			if (ch_getflags() & CH_HELPFILE)
139160786Sps				break;
139260786Sps			cmd_exec();
1393161478Sdelphij			save_hshift = hshift;
1394161478Sdelphij			hshift = 0;
139560786Sps			(void) edit(FAKE_HELPFILE);
139660786Sps			break;
139760786Sps
139860786Sps		case A_EXAMINE:
139960786Sps#if EXAMINE
140060786Sps			/*
140160786Sps			 * Edit a new file.  Get the filename.
140260786Sps			 */
140360786Sps			if (secure)
140460786Sps			{
140560786Sps				error("Command not available", NULL_PARG);
140660786Sps				break;
140760786Sps			}
140860786Sps			start_mca(A_EXAMINE, "Examine: ", ml_examine, 0);
140960786Sps			c = getcc();
141060786Sps			goto again;
141160786Sps#else
141260786Sps			error("Command not available", NULL_PARG);
141360786Sps			break;
141460786Sps#endif
141560786Sps
141660786Sps		case A_VISUAL:
141760786Sps			/*
141860786Sps			 * Invoke an editor on the input file.
141960786Sps			 */
142060786Sps#if EDITOR
142160786Sps			if (secure)
142260786Sps			{
142360786Sps				error("Command not available", NULL_PARG);
142460786Sps				break;
142560786Sps			}
142660786Sps			if (ch_getflags() & CH_HELPFILE)
142760786Sps				break;
142860786Sps			if (strcmp(get_filename(curr_ifile), "-") == 0)
142960786Sps			{
143060786Sps				error("Cannot edit standard input", NULL_PARG);
143160786Sps				break;
143260786Sps			}
143360786Sps			if (curr_altfilename != NULL)
143460786Sps			{
1435161478Sdelphij				error("WARNING: This file was viewed via LESSOPEN",
143660786Sps					NULL_PARG);
143760786Sps			}
143860786Sps			start_mca(A_SHELL, "!", ml_shell, 0);
143960786Sps			/*
144060786Sps			 * Expand the editor prototype string
144160786Sps			 * and pass it to the system to execute.
144260786Sps			 * (Make sure the screen is displayed so the
144360786Sps			 * expansion of "+%lm" works.)
144460786Sps			 */
144560786Sps			make_display();
144660786Sps			cmd_exec();
144760786Sps			lsystem(pr_expand(editproto, 0), (char*)NULL);
144860786Sps			break;
144960786Sps#else
145060786Sps			error("Command not available", NULL_PARG);
145160786Sps			break;
145260786Sps#endif
145360786Sps
145460786Sps		case A_NEXT_FILE:
145560786Sps			/*
145660786Sps			 * Examine next file.
145760786Sps			 */
1458128348Stjr#if TAGS
145989022Sps			if (ntags())
146089022Sps			{
146189022Sps				error("No next file", NULL_PARG);
146289022Sps				break;
146389022Sps			}
1464128348Stjr#endif
146560786Sps			if (number <= 0)
146660786Sps				number = 1;
1467128348Stjr			if (edit_next((int) number))
146860786Sps			{
1469191930Sdelphij				if (get_quit_at_eof() && eof_displayed() &&
147060786Sps				    !(ch_getflags() & CH_HELPFILE))
147160786Sps					quit(QUIT_OK);
147260786Sps				parg.p_string = (number > 1) ? "(N-th) " : "";
147360786Sps				error("No %snext file", &parg);
147460786Sps			}
147560786Sps			break;
147660786Sps
147760786Sps		case A_PREV_FILE:
147860786Sps			/*
147960786Sps			 * Examine previous file.
148060786Sps			 */
1481128348Stjr#if TAGS
148289022Sps			if (ntags())
148389022Sps			{
148489022Sps				error("No previous file", NULL_PARG);
148589022Sps				break;
148689022Sps			}
1487128348Stjr#endif
148860786Sps			if (number <= 0)
148960786Sps				number = 1;
1490128348Stjr			if (edit_prev((int) number))
149160786Sps			{
149260786Sps				parg.p_string = (number > 1) ? "(N-th) " : "";
149360786Sps				error("No %sprevious file", &parg);
149460786Sps			}
149560786Sps			break;
149660786Sps
149789022Sps		case A_NEXT_TAG:
1498128348Stjr#if TAGS
149989022Sps			if (number <= 0)
150089022Sps				number = 1;
1501128348Stjr			tagfile = nexttag((int) number);
150289022Sps			if (tagfile == NULL)
150389022Sps			{
150489022Sps				error("No next tag", NULL_PARG);
150589022Sps				break;
150689022Sps			}
150789022Sps			if (edit(tagfile) == 0)
150889022Sps			{
150989022Sps				POSITION pos = tagsearch();
151089022Sps				if (pos != NULL_POSITION)
151189022Sps					jump_loc(pos, jump_sline);
151289022Sps			}
1513128348Stjr#else
1514128348Stjr			error("Command not available", NULL_PARG);
1515128348Stjr#endif
151689022Sps			break;
151789022Sps
151889022Sps		case A_PREV_TAG:
1519128348Stjr#if TAGS
152089022Sps			if (number <= 0)
152189022Sps				number = 1;
1522128348Stjr			tagfile = prevtag((int) number);
152389022Sps			if (tagfile == NULL)
152489022Sps			{
152589022Sps				error("No previous tag", NULL_PARG);
152689022Sps				break;
152789022Sps			}
152889022Sps			if (edit(tagfile) == 0)
152989022Sps			{
153089022Sps				POSITION pos = tagsearch();
153189022Sps				if (pos != NULL_POSITION)
153289022Sps					jump_loc(pos, jump_sline);
153389022Sps			}
1534128348Stjr#else
1535128348Stjr			error("Command not available", NULL_PARG);
1536128348Stjr#endif
153789022Sps			break;
153889022Sps
153960786Sps		case A_INDEX_FILE:
154060786Sps			/*
154160786Sps			 * Examine a particular file.
154260786Sps			 */
154360786Sps			if (number <= 0)
154460786Sps				number = 1;
1545128348Stjr			if (edit_index((int) number))
154660786Sps				error("No such file", NULL_PARG);
154760786Sps			break;
154860786Sps
154960786Sps		case A_REMOVE_FILE:
155060786Sps			if (ch_getflags() & CH_HELPFILE)
155160786Sps				break;
155260786Sps			old_ifile = curr_ifile;
155360786Sps			new_ifile = getoff_ifile(curr_ifile);
155460786Sps			if (new_ifile == NULL_IFILE)
155560786Sps			{
155660786Sps				bell();
155760786Sps				break;
155860786Sps			}
155960786Sps			if (edit_ifile(new_ifile) != 0)
156060786Sps			{
156160786Sps				reedit_ifile(old_ifile);
156260786Sps				break;
156360786Sps			}
156460786Sps			del_ifile(old_ifile);
156560786Sps			break;
156660786Sps
156760786Sps		case A_OPT_TOGGLE:
156860786Sps			optflag = OPT_TOGGLE;
156960786Sps			optgetname = FALSE;
157060786Sps			mca_opt_toggle();
157160786Sps			c = getcc();
157260786Sps			goto again;
157360786Sps
157460786Sps		case A_DISP_OPTION:
157560786Sps			/*
157660786Sps			 * Report a flag setting.
157760786Sps			 */
157860786Sps			optflag = OPT_NO_TOGGLE;
157960786Sps			optgetname = FALSE;
158060786Sps			mca_opt_toggle();
158160786Sps			c = getcc();
158260786Sps			goto again;
158360786Sps
158460786Sps		case A_FIRSTCMD:
158560786Sps			/*
158660786Sps			 * Set an initial command for new files.
158760786Sps			 */
158860786Sps			start_mca(A_FIRSTCMD, "+", (void*)NULL, 0);
158960786Sps			c = getcc();
159060786Sps			goto again;
159160786Sps
159260786Sps		case A_SHELL:
159360786Sps			/*
159460786Sps			 * Shell escape.
159560786Sps			 */
159660786Sps#if SHELL_ESCAPE
159760786Sps			if (secure)
159860786Sps			{
159960786Sps				error("Command not available", NULL_PARG);
160060786Sps				break;
160160786Sps			}
160260786Sps			start_mca(A_SHELL, "!", ml_shell, 0);
160360786Sps			c = getcc();
160460786Sps			goto again;
160560786Sps#else
160660786Sps			error("Command not available", NULL_PARG);
160760786Sps			break;
160860786Sps#endif
160960786Sps
161060786Sps		case A_SETMARK:
161160786Sps			/*
161260786Sps			 * Set a mark.
161360786Sps			 */
161460786Sps			if (ch_getflags() & CH_HELPFILE)
161560786Sps				break;
161660786Sps			start_mca(A_SETMARK, "mark: ", (void*)NULL, 0);
161760786Sps			c = getcc();
1618161478Sdelphij			if (c == erase_char || c == erase2_char ||
1619161478Sdelphij			    c == kill_char || c == '\n' || c == '\r')
162060786Sps				break;
162160786Sps			setmark(c);
162260786Sps			break;
162360786Sps
162460786Sps		case A_GOMARK:
162560786Sps			/*
162660786Sps			 * Go to a mark.
162760786Sps			 */
162860786Sps			start_mca(A_GOMARK, "goto mark: ", (void*)NULL, 0);
162960786Sps			c = getcc();
1630161478Sdelphij			if (c == erase_char || c == erase2_char ||
1631161478Sdelphij			    c == kill_char || c == '\n' || c == '\r')
163260786Sps				break;
1633191930Sdelphij			cmd_exec();
163460786Sps			gomark(c);
163560786Sps			break;
163660786Sps
163760786Sps		case A_PIPE:
163860786Sps#if PIPEC
163960786Sps			if (secure)
164060786Sps			{
164160786Sps				error("Command not available", NULL_PARG);
164260786Sps				break;
164360786Sps			}
164460786Sps			start_mca(A_PIPE, "|mark: ", (void*)NULL, 0);
164560786Sps			c = getcc();
1646161478Sdelphij			if (c == erase_char || c == erase2_char || c == kill_char)
164760786Sps				break;
164860786Sps			if (c == '\n' || c == '\r')
164960786Sps				c = '.';
165060786Sps			if (badmark(c))
165160786Sps				break;
165260786Sps			pipec = c;
165360786Sps			start_mca(A_PIPE, "!", ml_shell, 0);
165460786Sps			c = getcc();
165560786Sps			goto again;
165660786Sps#else
165760786Sps			error("Command not available", NULL_PARG);
165860786Sps			break;
165960786Sps#endif
166060786Sps
166160786Sps		case A_B_BRACKET:
166260786Sps		case A_F_BRACKET:
166360786Sps			start_mca(action, "Brackets: ", (void*)NULL, 0);
166460786Sps			c = getcc();
166560786Sps			goto again;
166660786Sps
166760786Sps		case A_LSHIFT:
166889022Sps			if (number > 0)
166989022Sps				shift_count = number;
167089022Sps			else
167163131Sps				number = (shift_count > 0) ?
167263131Sps					shift_count : sc_width / 2;
167360786Sps			if (number > hshift)
167460786Sps				number = hshift;
167560786Sps			hshift -= number;
167660786Sps			screen_trashed = 1;
167760786Sps			break;
167860786Sps
167960786Sps		case A_RSHIFT:
168089022Sps			if (number > 0)
168189022Sps				shift_count = number;
168289022Sps			else
168363131Sps				number = (shift_count > 0) ?
168463131Sps					shift_count : sc_width / 2;
168560786Sps			hshift += number;
168660786Sps			screen_trashed = 1;
168760786Sps			break;
168860786Sps
168960786Sps		case A_PREFIX:
169060786Sps			/*
169160786Sps			 * The command is incomplete (more chars are needed).
169260786Sps			 * Display the current char, so the user knows
169360786Sps			 * what's going on, and get another character.
169460786Sps			 */
169560786Sps			if (mca != A_PREFIX)
169660786Sps			{
169760786Sps				cmd_reset();
169860786Sps				start_mca(A_PREFIX, " ", (void*)NULL,
169960786Sps					CF_QUIT_ON_ERASE);
170060786Sps				(void) cmd_char(c);
170160786Sps			}
170260786Sps			c = getcc();
170360786Sps			goto again;
170460786Sps
170560786Sps		case A_NOACTION:
170660786Sps			break;
170760786Sps
170860786Sps		default:
170960786Sps			bell();
171060786Sps			break;
171160786Sps		}
171260786Sps	}
171360786Sps}
1714