160812Sps/* $FreeBSD$ */
260786Sps/*
3240121Sdelphij * Copyright (C) 1984-2012  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 *
8240121Sdelphij * For more information, see the README file.
960786Sps */
1060786Sps
1160786Sps
1260786Sps/*
1360786Sps * User-level command processor.
1460786Sps */
1560786Sps
1660786Sps#include "less.h"
1789022Sps#if MSDOS_COMPILER==WIN32C
1889022Sps#include <windows.h>
1989022Sps#endif
2060786Sps#include "position.h"
2160786Sps#include "option.h"
2260786Sps#include "cmd.h"
2360786Sps
24161478Sdelphijextern int erase_char, erase2_char, kill_char;
2560786Spsextern int sigs;
2660786Spsextern int quit_if_one_screen;
2760786Spsextern int squished;
2860786Spsextern int sc_width;
2960786Spsextern int sc_height;
3060786Spsextern int swindow;
3160786Spsextern int jump_sline;
3260786Spsextern int quitting;
3360786Spsextern int wscroll;
3460786Spsextern int top_scroll;
3560786Spsextern int ignore_eoi;
3660786Spsextern int secure;
3760786Spsextern int hshift;
3860786Spsextern int show_attn;
39170259Sdelphijextern int less_is_more;
40240121Sdelphijextern POSITION highest_hilite;
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
6060786Sps#if SHELL_ESCAPE
6160786Spsstatic char *shellcmd = NULL;	/* For holding last shell command for "!!" */
6260786Sps#endif
6360786Spsstatic int mca;			/* The multicharacter command (action) */
6460786Spsstatic int search_type;		/* The previous type of search */
65128348Stjrstatic LINENUM number;		/* The number typed by the user */
66170259Sdelphijstatic long fraction;		/* The fractional part of the number */
67221715Sdelphijstatic struct loption *curropt;
68221715Sdelphijstatic int opt_lower;
6960786Spsstatic int optflag;
7060786Spsstatic int optgetname;
7160786Spsstatic POSITION bottompos;
72161478Sdelphijstatic int save_hshift;
7360786Sps#if PIPEC
7460786Spsstatic char pipec;
7560786Sps#endif
7660786Sps
77221715Sdelphijstruct ungot {
78221715Sdelphij	struct ungot *ug_next;
79221715Sdelphij	char ug_char;
80221715Sdelphij};
81221715Sdelphijstatic struct ungot* ungot = NULL;
82221715Sdelphijstatic int unget_end = 0;
83221715Sdelphij
8460786Spsstatic void multi_search();
8560786Sps
8660786Sps/*
87170259Sdelphij * Move the cursor to start of prompt line before executing a command.
8860786Sps * This looks nicer if the command takes a long time before
8960786Sps * updating the screen.
9060786Sps */
9160786Sps	static void
9260786Spscmd_exec()
9360786Sps{
94191930Sdelphij#if HILITE_SEARCH
9560786Sps	clear_attn();
96191930Sdelphij#endif
97170967Sdelphij	clear_bot();
9860786Sps	flush();
9960786Sps}
10060786Sps
10160786Sps/*
10260786Sps * Set up the display to start a new multi-character command.
10360786Sps */
10460786Sps	static void
10560786Spsstart_mca(action, prompt, mlist, cmdflags)
10660786Sps	int action;
107240121Sdelphij	constant char *prompt;
108240121Sdelphij	constant void *mlist;
10960786Sps	int cmdflags;
11060786Sps{
11160786Sps	mca = action;
112170259Sdelphij	clear_bot();
11360786Sps	clear_cmd();
11460786Sps	cmd_putstr(prompt);
11560786Sps	set_mlist(mlist, cmdflags);
11660786Sps}
11760786Sps
11860786Sps	public int
11960786Spsin_mca()
12060786Sps{
12160786Sps	return (mca != 0 && mca != A_PREFIX);
12260786Sps}
12360786Sps
12460786Sps/*
12560786Sps * Set up the display to start a new search command.
12660786Sps */
12760786Sps	static void
12860786Spsmca_search()
12960786Sps{
130191930Sdelphij#if HILITE_SEARCH
131191930Sdelphij	if (search_type & SRCH_FILTER)
132191930Sdelphij		mca = A_FILTER;
133191930Sdelphij	else
134191930Sdelphij#endif
13560786Sps	if (search_type & SRCH_FORW)
13660786Sps		mca = A_F_SEARCH;
13760786Sps	else
13860786Sps		mca = A_B_SEARCH;
13960786Sps
140170259Sdelphij	clear_bot();
14160786Sps	clear_cmd();
14260786Sps
14360786Sps	if (search_type & SRCH_NO_MATCH)
14460786Sps		cmd_putstr("Non-match ");
14560786Sps	if (search_type & SRCH_FIRST_FILE)
14660786Sps		cmd_putstr("First-file ");
14760786Sps	if (search_type & SRCH_PAST_EOF)
14860786Sps		cmd_putstr("EOF-ignore ");
14960786Sps	if (search_type & SRCH_NO_MOVE)
15060786Sps		cmd_putstr("Keep-pos ");
15160786Sps	if (search_type & SRCH_NO_REGEX)
15260786Sps		cmd_putstr("Regex-off ");
15360786Sps
154191930Sdelphij#if HILITE_SEARCH
155191930Sdelphij	if (search_type & SRCH_FILTER)
156191930Sdelphij		cmd_putstr("&/");
157191930Sdelphij	else
158191930Sdelphij#endif
15960786Sps	if (search_type & SRCH_FORW)
16060786Sps		cmd_putstr("/");
16160786Sps	else
16260786Sps		cmd_putstr("?");
16360786Sps	set_mlist(ml_search, 0);
16460786Sps}
16560786Sps
16660786Sps/*
16760786Sps * Set up the display to start a new toggle-option command.
16860786Sps */
16960786Sps	static void
17060786Spsmca_opt_toggle()
17160786Sps{
17260786Sps	int no_prompt;
17360786Sps	int flag;
17460786Sps	char *dash;
17560786Sps
17660786Sps	no_prompt = (optflag & OPT_NO_PROMPT);
17760786Sps	flag = (optflag & ~OPT_NO_PROMPT);
17860786Sps	dash = (flag == OPT_NO_TOGGLE) ? "_" : "-";
17960786Sps
18060786Sps	mca = A_OPT_TOGGLE;
181170259Sdelphij	clear_bot();
18260786Sps	clear_cmd();
18360786Sps	cmd_putstr(dash);
18460786Sps	if (optgetname)
18560786Sps		cmd_putstr(dash);
18660786Sps	if (no_prompt)
18760786Sps		cmd_putstr("(P)");
18860786Sps	switch (flag)
18960786Sps	{
19060786Sps	case OPT_UNSET:
19160786Sps		cmd_putstr("+");
19260786Sps		break;
19360786Sps	case OPT_SET:
19460786Sps		cmd_putstr("!");
19560786Sps		break;
19660786Sps	}
19760786Sps	set_mlist(NULL, 0);
19860786Sps}
19960786Sps
20060786Sps/*
20160786Sps * Execute a multicharacter command.
20260786Sps */
20360786Sps	static void
20460786Spsexec_mca()
20560786Sps{
20660786Sps	register char *cbuf;
20760786Sps
20860786Sps	cmd_exec();
20960786Sps	cbuf = get_cmdbuf();
21060786Sps
21160786Sps	switch (mca)
21260786Sps	{
21360786Sps	case A_F_SEARCH:
21460786Sps	case A_B_SEARCH:
215128348Stjr		multi_search(cbuf, (int) number);
21660786Sps		break;
217191930Sdelphij#if HILITE_SEARCH
218191930Sdelphij	case A_FILTER:
219191930Sdelphij		search_type ^= SRCH_NO_MATCH;
220191930Sdelphij		set_filter_pattern(cbuf, search_type);
221191930Sdelphij		break;
222191930Sdelphij#endif
22360786Sps	case A_FIRSTCMD:
22460786Sps		/*
22560786Sps		 * Skip leading spaces or + signs in the string.
22660786Sps		 */
22760786Sps		while (*cbuf == '+' || *cbuf == ' ')
22860786Sps			cbuf++;
22960786Sps		if (every_first_cmd != NULL)
23060786Sps			free(every_first_cmd);
23160786Sps		if (*cbuf == '\0')
23260786Sps			every_first_cmd = NULL;
23360786Sps		else
23460786Sps			every_first_cmd = save(cbuf);
23560786Sps		break;
23660786Sps	case A_OPT_TOGGLE:
237221715Sdelphij		toggle_option(curropt, opt_lower, cbuf, optflag);
238221715Sdelphij		curropt = NULL;
23960786Sps		break;
24060786Sps	case A_F_BRACKET:
241128348Stjr		match_brac(cbuf[0], cbuf[1], 1, (int) number);
24260786Sps		break;
24360786Sps	case A_B_BRACKET:
244128348Stjr		match_brac(cbuf[1], cbuf[0], 0, (int) number);
24560786Sps		break;
24660786Sps#if EXAMINE
24760786Sps	case A_EXAMINE:
24860786Sps		if (secure)
24960786Sps			break;
25060786Sps		edit_list(cbuf);
251128348Stjr#if TAGS
25289022Sps		/* If tag structure is loaded then clean it up. */
25389022Sps		cleantags();
254128348Stjr#endif
25560786Sps		break;
25660786Sps#endif
25760786Sps#if SHELL_ESCAPE
25860786Sps	case A_SHELL:
25960786Sps		/*
26060786Sps		 * !! just uses whatever is in shellcmd.
26160786Sps		 * Otherwise, copy cmdbuf to shellcmd,
26260786Sps		 * expanding any special characters ("%" or "#").
26360786Sps		 */
26460786Sps		if (*cbuf != '!')
26560786Sps		{
26660786Sps			if (shellcmd != NULL)
26760786Sps				free(shellcmd);
26860786Sps			shellcmd = fexpand(cbuf);
26960786Sps		}
27060786Sps
27160786Sps		if (secure)
27260786Sps			break;
27360786Sps		if (shellcmd == NULL)
27460786Sps			lsystem("", "!done");
27560786Sps		else
27660786Sps			lsystem(shellcmd, "!done");
27760786Sps		break;
27860786Sps#endif
27960786Sps#if PIPEC
28060786Sps	case A_PIPE:
28160786Sps		if (secure)
28260786Sps			break;
28360786Sps		(void) pipe_mark(pipec, cbuf);
28460786Sps		error("|done", NULL_PARG);
28560786Sps		break;
28660786Sps#endif
28760786Sps	}
28860786Sps}
28960786Sps
29060786Sps/*
291221715Sdelphij * Is a character an erase or kill char?
29260786Sps */
29360786Sps	static int
294221715Sdelphijis_erase_char(c)
29560786Sps	int c;
29660786Sps{
297221715Sdelphij	return (c == erase_char || c == erase2_char || c == kill_char);
298221715Sdelphij}
299221715Sdelphij
300221715Sdelphij/*
301221715Sdelphij * Handle the first char of an option (after the initial dash).
302221715Sdelphij */
303221715Sdelphij	static int
304221715Sdelphijmca_opt_first_char(c)
305222906Sdelphij    int c;
306221715Sdelphij{
307221715Sdelphij	int flag = (optflag & ~OPT_NO_PROMPT);
308221715Sdelphij	if (flag == OPT_NO_TOGGLE)
309221715Sdelphij	{
310221715Sdelphij		switch (c)
311221715Sdelphij		{
312221715Sdelphij		case '_':
313221715Sdelphij			/* "__" = long option name. */
314221715Sdelphij			optgetname = TRUE;
315221715Sdelphij			mca_opt_toggle();
316221715Sdelphij			return (MCA_MORE);
317221715Sdelphij		}
318221715Sdelphij	} else
319221715Sdelphij	{
320221715Sdelphij		switch (c)
321221715Sdelphij		{
322221715Sdelphij		case '+':
323221715Sdelphij			/* "-+" = UNSET. */
324221715Sdelphij			optflag = (flag == OPT_UNSET) ?
325221715Sdelphij				OPT_TOGGLE : OPT_UNSET;
326221715Sdelphij			mca_opt_toggle();
327221715Sdelphij			return (MCA_MORE);
328221715Sdelphij		case '!':
329221715Sdelphij			/* "-!" = SET */
330221715Sdelphij			optflag = (flag == OPT_SET) ?
331221715Sdelphij				OPT_TOGGLE : OPT_SET;
332221715Sdelphij			mca_opt_toggle();
333221715Sdelphij			return (MCA_MORE);
334221715Sdelphij		case CONTROL('P'):
335221715Sdelphij			optflag ^= OPT_NO_PROMPT;
336221715Sdelphij			mca_opt_toggle();
337221715Sdelphij			return (MCA_MORE);
338221715Sdelphij		case '-':
339221715Sdelphij			/* "--" = long option name. */
340221715Sdelphij			optgetname = TRUE;
341221715Sdelphij			mca_opt_toggle();
342221715Sdelphij			return (MCA_MORE);
343221715Sdelphij		}
344221715Sdelphij	}
345221715Sdelphij	/* Char was not handled here. */
346221715Sdelphij	return (NO_MCA);
347221715Sdelphij}
348221715Sdelphij
349221715Sdelphij/*
350221715Sdelphij * Add a char to a long option name.
351221715Sdelphij * See if we've got a match for an option name yet.
352221715Sdelphij * If so, display the complete name and stop
353221715Sdelphij * accepting chars until user hits RETURN.
354221715Sdelphij */
355221715Sdelphij	static int
356221715Sdelphijmca_opt_nonfirst_char(c)
357221715Sdelphij	int c;
358221715Sdelphij{
35960786Sps	char *p;
360221715Sdelphij	char *oname;
361221715Sdelphij
362221715Sdelphij	if (curropt != NULL)
363221715Sdelphij	{
364221715Sdelphij		/*
365221715Sdelphij		 * Already have a match for the name.
366221715Sdelphij		 * Don't accept anything but erase/kill.
367221715Sdelphij		 */
368221715Sdelphij		if (is_erase_char(c))
369221715Sdelphij			return (MCA_DONE);
370221715Sdelphij		return (MCA_MORE);
371221715Sdelphij	}
372221715Sdelphij	/*
373221715Sdelphij	 * Add char to cmd buffer and try to match
374221715Sdelphij	 * the option name.
375221715Sdelphij	 */
376221715Sdelphij	if (cmd_char(c) == CC_QUIT)
377221715Sdelphij		return (MCA_DONE);
378221715Sdelphij	p = get_cmdbuf();
379221715Sdelphij	opt_lower = ASCII_IS_LOWER(p[0]);
380221715Sdelphij	curropt = findopt_name(&p, &oname, NULL);
381221715Sdelphij	if (curropt != NULL)
382221715Sdelphij	{
383221715Sdelphij		/*
384221715Sdelphij		 * Got a match.
385221715Sdelphij		 * Remember the option and
386221715Sdelphij		 * display the full option name.
387221715Sdelphij		 */
388221715Sdelphij		cmd_reset();
389221715Sdelphij		mca_opt_toggle();
390221715Sdelphij		for (p = oname;  *p != '\0';  p++)
391221715Sdelphij		{
392221715Sdelphij			c = *p;
393221715Sdelphij			if (!opt_lower && ASCII_IS_LOWER(c))
394221715Sdelphij				c = ASCII_TO_UPPER(c);
395221715Sdelphij			if (cmd_char(c) != CC_OK)
396221715Sdelphij				return (MCA_DONE);
397221715Sdelphij		}
398221715Sdelphij	}
399221715Sdelphij	return (MCA_MORE);
400221715Sdelphij}
401221715Sdelphij
402221715Sdelphij/*
403221715Sdelphij * Handle a char of an option toggle command.
404221715Sdelphij */
405221715Sdelphij	static int
406221715Sdelphijmca_opt_char(c)
407221715Sdelphij	int c;
408221715Sdelphij{
40960786Sps	PARG parg;
41060786Sps
411221715Sdelphij	/*
412221715Sdelphij	 * This may be a short option (single char),
413221715Sdelphij	 * or one char of a long option name,
414221715Sdelphij	 * or one char of the option parameter.
415221715Sdelphij	 */
416221715Sdelphij	if (curropt == NULL && len_cmdbuf() == 0)
417221715Sdelphij	{
418221715Sdelphij		int ret = mca_opt_first_char(c);
419221715Sdelphij		if (ret != NO_MCA)
420221715Sdelphij			return (ret);
421221715Sdelphij	}
422221715Sdelphij	if (optgetname)
423221715Sdelphij	{
424221715Sdelphij		/* We're getting a long option name.  */
425221715Sdelphij		if (c != '\n' && c != '\r')
426221715Sdelphij			return (mca_opt_nonfirst_char(c));
427221715Sdelphij		if (curropt == NULL)
428221715Sdelphij		{
429221715Sdelphij			parg.p_string = get_cmdbuf();
430221715Sdelphij			error("There is no --%s option", &parg);
431221715Sdelphij			return (MCA_DONE);
432221715Sdelphij		}
433221715Sdelphij		optgetname = FALSE;
434221715Sdelphij		cmd_reset();
435221715Sdelphij	} else
436221715Sdelphij	{
437221715Sdelphij		if (is_erase_char(c))
438221715Sdelphij			return (NO_MCA);
439221715Sdelphij		if (curropt != NULL)
440221715Sdelphij			/* We're getting the option parameter. */
441221715Sdelphij			return (NO_MCA);
442221715Sdelphij		curropt = findopt(c);
443221715Sdelphij		if (curropt == NULL)
444221715Sdelphij		{
445221715Sdelphij			parg.p_string = propt(c);
446221715Sdelphij			error("There is no %s option", &parg);
447221715Sdelphij			return (MCA_DONE);
448221715Sdelphij		}
449221715Sdelphij	}
450221715Sdelphij	/*
451221715Sdelphij	 * If the option which was entered does not take a
452221715Sdelphij	 * parameter, toggle the option immediately,
453221715Sdelphij	 * so user doesn't have to hit RETURN.
454221715Sdelphij	 */
455221715Sdelphij	if ((optflag & ~OPT_NO_PROMPT) != OPT_TOGGLE ||
456221715Sdelphij	    !opt_has_param(curropt))
457221715Sdelphij	{
458221715Sdelphij		toggle_option(curropt, ASCII_IS_LOWER(c), "", optflag);
459221715Sdelphij		return (MCA_DONE);
460221715Sdelphij	}
461221715Sdelphij	/*
462221715Sdelphij	 * Display a prompt appropriate for the option parameter.
463221715Sdelphij	 */
464221715Sdelphij	start_mca(A_OPT_TOGGLE, opt_prompt(curropt), (void*)NULL, 0);
465221715Sdelphij	return (MCA_MORE);
466221715Sdelphij}
467221715Sdelphij
468221715Sdelphij/*
469221715Sdelphij * Handle a char of a search command.
470221715Sdelphij */
471221715Sdelphij	static int
472221715Sdelphijmca_search_char(c)
473221715Sdelphij	int c;
474221715Sdelphij{
475221715Sdelphij	int flag = 0;
476221715Sdelphij
477221715Sdelphij	/*
478221715Sdelphij	 * Certain characters as the first char of
479221715Sdelphij	 * the pattern have special meaning:
480221715Sdelphij	 *	!  Toggle the NO_MATCH flag
481221715Sdelphij	 *	*  Toggle the PAST_EOF flag
482221715Sdelphij	 *	@  Toggle the FIRST_FILE flag
483221715Sdelphij	 */
484221715Sdelphij	if (len_cmdbuf() > 0)
485221715Sdelphij		return (NO_MCA);
486221715Sdelphij
487221715Sdelphij	switch (c)
488221715Sdelphij	{
489221715Sdelphij	case '*':
490221715Sdelphij		if (less_is_more)
491221715Sdelphij			break;
492221715Sdelphij	case CONTROL('E'): /* ignore END of file */
493221715Sdelphij		if (mca != A_FILTER)
494221715Sdelphij			flag = SRCH_PAST_EOF;
495221715Sdelphij		break;
496221715Sdelphij	case '@':
497221715Sdelphij		if (less_is_more)
498221715Sdelphij			break;
499221715Sdelphij	case CONTROL('F'): /* FIRST file */
500221715Sdelphij		if (mca != A_FILTER)
501221715Sdelphij			flag = SRCH_FIRST_FILE;
502221715Sdelphij		break;
503221715Sdelphij	case CONTROL('K'): /* KEEP position */
504221715Sdelphij		if (mca != A_FILTER)
505221715Sdelphij			flag = SRCH_NO_MOVE;
506221715Sdelphij		break;
507221715Sdelphij	case CONTROL('R'): /* Don't use REGULAR EXPRESSIONS */
508221715Sdelphij		flag = SRCH_NO_REGEX;
509221715Sdelphij		break;
510221715Sdelphij	case CONTROL('N'): /* NOT match */
511221715Sdelphij	case '!':
512221715Sdelphij		flag = SRCH_NO_MATCH;
513221715Sdelphij		break;
514221715Sdelphij	}
515221715Sdelphij
516221715Sdelphij	if (flag != 0)
517221715Sdelphij	{
518221715Sdelphij		search_type ^= flag;
519221715Sdelphij		mca_search();
520221715Sdelphij		return (MCA_MORE);
521221715Sdelphij	}
522221715Sdelphij	return (NO_MCA);
523221715Sdelphij}
524221715Sdelphij
525221715Sdelphij/*
526221715Sdelphij * Handle a character of a multi-character command.
527221715Sdelphij */
528221715Sdelphij	static int
529221715Sdelphijmca_char(c)
530221715Sdelphij	int c;
531221715Sdelphij{
532221715Sdelphij	int ret;
533221715Sdelphij
53460786Sps	switch (mca)
53560786Sps	{
53660786Sps	case 0:
53760786Sps		/*
538221715Sdelphij		 * We're not in a multicharacter command.
53960786Sps		 */
54060786Sps		return (NO_MCA);
54160786Sps
54260786Sps	case A_PREFIX:
54360786Sps		/*
54460786Sps		 * In the prefix of a command.
54560786Sps		 * This not considered a multichar command
54660786Sps		 * (even tho it uses cmdbuf, etc.).
54760786Sps		 * It is handled in the commands() switch.
54860786Sps		 */
54960786Sps		return (NO_MCA);
55060786Sps
55160786Sps	case A_DIGIT:
55260786Sps		/*
55360786Sps		 * Entering digits of a number.
55460786Sps		 * Terminated by a non-digit.
55560786Sps		 */
556170259Sdelphij		if (!((c >= '0' && c <= '9') || c == '.') &&
55789022Sps		  editchar(c, EC_PEEK|EC_NOHISTORY|EC_NOCOMPLETE|EC_NORIGHTLEFT) == A_INVALID)
55860786Sps		{
55960786Sps			/*
56060786Sps			 * Not part of the number.
561221715Sdelphij			 * End the number and treat this char
562221715Sdelphij			 * as a normal command character.
56360786Sps			 */
564170259Sdelphij			number = cmd_int(&fraction);
56560786Sps			mca = 0;
56660786Sps			cmd_accept();
56760786Sps			return (NO_MCA);
56860786Sps		}
56960786Sps		break;
57060786Sps
57160786Sps	case A_OPT_TOGGLE:
572221715Sdelphij		ret = mca_opt_char(c);
573221715Sdelphij		if (ret != NO_MCA)
574221715Sdelphij			return (ret);
575221715Sdelphij		break;
57660786Sps
57760786Sps	case A_F_SEARCH:
57860786Sps	case A_B_SEARCH:
579191930Sdelphij	case A_FILTER:
580221715Sdelphij		ret = mca_search_char(c);
581221715Sdelphij		if (ret != NO_MCA)
582221715Sdelphij			return (ret);
583221715Sdelphij		break;
58460786Sps
585221715Sdelphij	default:
586221715Sdelphij		/* Other multicharacter command. */
58760786Sps		break;
58860786Sps	}
58960786Sps
59060786Sps	/*
591221715Sdelphij	 * The multichar command is terminated by a newline.
59260786Sps	 */
59360786Sps	if (c == '\n' || c == '\r')
59460786Sps	{
59560786Sps		/*
59660786Sps		 * Execute the command.
59760786Sps		 */
59860786Sps		exec_mca();
59960786Sps		return (MCA_DONE);
60060786Sps	}
60160786Sps
60260786Sps	/*
60360786Sps	 * Append the char to the command buffer.
60460786Sps	 */
60560786Sps	if (cmd_char(c) == CC_QUIT)
60660786Sps		/*
60760786Sps		 * Abort the multi-char command.
60860786Sps		 */
60960786Sps		return (MCA_DONE);
61060786Sps
61160786Sps	if ((mca == A_F_BRACKET || mca == A_B_BRACKET) && len_cmdbuf() >= 2)
61260786Sps	{
61360786Sps		/*
61460786Sps		 * Special case for the bracket-matching commands.
61560786Sps		 * Execute the command after getting exactly two
61660786Sps		 * characters from the user.
61760786Sps		 */
61860786Sps		exec_mca();
61960786Sps		return (MCA_DONE);
62060786Sps	}
62160786Sps
62260786Sps	/*
62360786Sps	 * Need another character.
62460786Sps	 */
62560786Sps	return (MCA_MORE);
62660786Sps}
62760786Sps
62860786Sps/*
629173685Sdelphij * Discard any buffered file data.
630173685Sdelphij */
631173685Sdelphij	static void
632173685Sdelphijclear_buffers()
633173685Sdelphij{
634173685Sdelphij	if (!(ch_getflags() & CH_CANSEEK))
635173685Sdelphij		return;
636173685Sdelphij	ch_flush();
637173685Sdelphij	clr_linenum();
638173685Sdelphij#if HILITE_SEARCH
639173685Sdelphij	clr_hilite();
640173685Sdelphij#endif
641173685Sdelphij}
642173685Sdelphij
643173685Sdelphij/*
64460786Sps * Make sure the screen is displayed.
64560786Sps */
64660786Sps	static void
64760786Spsmake_display()
64860786Sps{
64960786Sps	/*
65060786Sps	 * If nothing is displayed yet, display starting from initial_scrpos.
65160786Sps	 */
65260786Sps	if (empty_screen())
65360786Sps	{
65460786Sps		if (initial_scrpos.pos == NULL_POSITION)
65560786Sps			/*
65660786Sps			 * {{ Maybe this should be:
65760786Sps			 *    jump_loc(ch_zero(), jump_sline);
65860786Sps			 *    but this behavior seems rather unexpected
65960786Sps			 *    on the first screen. }}
66060786Sps			 */
66160786Sps			jump_loc(ch_zero(), 1);
66260786Sps		else
66360786Sps			jump_loc(initial_scrpos.pos, initial_scrpos.ln);
66460786Sps	} else if (screen_trashed)
66560786Sps	{
666173685Sdelphij		int save_top_scroll = top_scroll;
667173685Sdelphij		int save_ignore_eoi = ignore_eoi;
66860786Sps		top_scroll = 1;
669173685Sdelphij		ignore_eoi = 0;
670173685Sdelphij		if (screen_trashed == 2)
671173685Sdelphij		{
672173685Sdelphij			/* Special case used by ignore_eoi: re-open the input file
673173685Sdelphij			 * and jump to the end of the file. */
674173685Sdelphij			reopen_curr_ifile();
675173685Sdelphij			jump_forw();
676173685Sdelphij		}
67760786Sps		repaint();
67860786Sps		top_scroll = save_top_scroll;
679173685Sdelphij		ignore_eoi = save_ignore_eoi;
68060786Sps	}
68160786Sps}
68260786Sps
68360786Sps/*
68460786Sps * Display the appropriate prompt.
68560786Sps */
68660786Sps	static void
68760786Spsprompt()
68860786Sps{
689240121Sdelphij	register constant char *p;
69060786Sps
691221715Sdelphij	if (ungot != NULL)
69260786Sps	{
69360786Sps		/*
69460786Sps		 * No prompt necessary if commands are from
69560786Sps		 * ungotten chars rather than from the user.
69660786Sps		 */
69760786Sps		return;
69860786Sps	}
69960786Sps
70060786Sps	/*
70160786Sps	 * Make sure the screen is displayed.
70260786Sps	 */
70360786Sps	make_display();
70460786Sps	bottompos = position(BOTTOM_PLUS_ONE);
70560786Sps
70660786Sps	/*
707191930Sdelphij	 * If we've hit EOF on the last file and the -E flag is set, quit.
70860786Sps	 */
709191930Sdelphij	if (get_quit_at_eof() == OPT_ONPLUS &&
710191930Sdelphij	    eof_displayed() && !(ch_getflags() & CH_HELPFILE) &&
71160786Sps	    next_ifile(curr_ifile) == NULL_IFILE)
71260786Sps		quit(QUIT_OK);
713191930Sdelphij
71460786Sps	/*
715191930Sdelphij	 * If the entire file is displayed and the -F flag is set, quit.
71660786Sps	 */
717191930Sdelphij	if (quit_if_one_screen &&
718191930Sdelphij	    entire_file_displayed() && !(ch_getflags() & CH_HELPFILE) &&
71960786Sps	    next_ifile(curr_ifile) == NULL_IFILE)
72060786Sps		quit(QUIT_OK);
72160786Sps
72289022Sps#if MSDOS_COMPILER==WIN32C
72389022Sps	/*
72489022Sps	 * In Win32, display the file name in the window title.
72589022Sps	 */
72689022Sps	if (!(ch_getflags() & CH_HELPFILE))
72789022Sps		SetConsoleTitle(pr_expand("Less?f - %f.", 0));
72889022Sps#endif
72960786Sps	/*
73060786Sps	 * Select the proper prompt and display it.
73160786Sps	 */
732170259Sdelphij	/*
733170259Sdelphij	 * If the previous action was a forward movement,
734170259Sdelphij	 * don't clear the bottom line of the display;
735170259Sdelphij	 * just print the prompt since the forward movement guarantees
736170259Sdelphij	 * that we're in the right position to display the prompt.
737170259Sdelphij	 * Clearing the line could cause a problem: for example, if the last
738170259Sdelphij	 * line displayed ended at the right screen edge without a newline,
739170259Sdelphij	 * then clearing would clear the last displayed line rather than
740170259Sdelphij	 * the prompt line.
741170259Sdelphij	 */
742170259Sdelphij	if (!forw_prompt)
743170259Sdelphij		clear_bot();
74460786Sps	clear_cmd();
745170259Sdelphij	forw_prompt = 0;
74660786Sps	p = pr_string();
747191930Sdelphij	if (is_filtering())
748191930Sdelphij		putstr("& ");
749161478Sdelphij	if (p == NULL || *p == '\0')
75060786Sps		putchr(':');
75160786Sps	else
75260786Sps	{
753161478Sdelphij		at_enter(AT_STANDOUT);
75460786Sps		putstr(p);
755161478Sdelphij		at_exit();
75660786Sps	}
757170259Sdelphij	clear_eol();
75860786Sps}
75960786Sps
76060786Sps/*
76160786Sps * Display the less version message.
76260786Sps */
76360786Sps	public void
76460786Spsdispversion()
76560786Sps{
76660786Sps	PARG parg;
76760786Sps
76860786Sps	parg.p_string = version;
76960786Sps	error("less %s", &parg);
77060786Sps}
77160786Sps
77260786Sps/*
77360786Sps * Get command character.
77460786Sps * The character normally comes from the keyboard,
77560786Sps * but may come from ungotten characters
77660786Sps * (characters previously given to ungetcc or ungetsc).
77760786Sps */
77860786Sps	public int
77960786Spsgetcc()
78060786Sps{
781221715Sdelphij	if (unget_end)
782221715Sdelphij	{
78360786Sps		/*
784221715Sdelphij		 * We have just run out of ungotten chars.
78560786Sps		 */
786221715Sdelphij		unget_end = 0;
787221715Sdelphij		if (len_cmdbuf() == 0 || !empty_screen())
788221715Sdelphij			return (getchr());
78960786Sps		/*
790221715Sdelphij		 * Command is incomplete, so try to complete it.
79160786Sps		 */
792221715Sdelphij		switch (mca)
793221715Sdelphij		{
794221715Sdelphij		case A_DIGIT:
795221715Sdelphij			/*
796221715Sdelphij			 * We have a number but no command.  Treat as #g.
797221715Sdelphij			 */
798221715Sdelphij			return ('g');
79960786Sps
800221715Sdelphij		case A_F_SEARCH:
801221715Sdelphij		case A_B_SEARCH:
802221715Sdelphij			/*
803221715Sdelphij			 * We have "/string" but no newline.  Add the \n.
804221715Sdelphij			 */
805221715Sdelphij			return ('\n');
80660786Sps
807221715Sdelphij		default:
808221715Sdelphij			/*
809221715Sdelphij			 * Some other incomplete command.  Let user complete it.
810221715Sdelphij			 */
811221715Sdelphij			return (getchr());
812221715Sdelphij		}
813221715Sdelphij	}
81460786Sps
815221715Sdelphij	if (ungot == NULL)
816221715Sdelphij	{
81760786Sps		/*
818221715Sdelphij		 * Normal case: no ungotten chars, so get one from the user.
81960786Sps		 */
82060786Sps		return (getchr());
82160786Sps	}
822221715Sdelphij
823221715Sdelphij	/*
824221715Sdelphij	 * Return the next ungotten char.
825221715Sdelphij	 */
826221715Sdelphij	{
827221715Sdelphij		struct ungot *ug = ungot;
828221715Sdelphij		char c = ug->ug_char;
829221715Sdelphij		ungot = ug->ug_next;
830221715Sdelphij		free(ug);
831221715Sdelphij		unget_end = (ungot == NULL);
832221715Sdelphij		return (c);
833221715Sdelphij	}
83460786Sps}
83560786Sps
83660786Sps/*
83760786Sps * "Unget" a command character.
83860786Sps * The next getcc() will return this character.
83960786Sps */
84060786Sps	public void
84160786Spsungetcc(c)
84260786Sps	int c;
84360786Sps{
844221715Sdelphij	struct ungot *ug = (struct ungot *) ecalloc(1, sizeof(struct ungot));
845221715Sdelphij
846221715Sdelphij	ug->ug_char = c;
847221715Sdelphij	ug->ug_next = ungot;
848221715Sdelphij	ungot = ug;
849222906Sdelphij	unget_end = 0;
85060786Sps}
85160786Sps
85260786Sps/*
85360786Sps * Unget a whole string of command characters.
85460786Sps * The next sequence of getcc()'s will return this string.
85560786Sps */
85660786Sps	public void
85760786Spsungetsc(s)
85860786Sps	char *s;
85960786Sps{
86060786Sps	register char *p;
86160786Sps
86260786Sps	for (p = s + strlen(s) - 1;  p >= s;  p--)
86360786Sps		ungetcc(*p);
86460786Sps}
86560786Sps
86660786Sps/*
86760786Sps * Search for a pattern, possibly in multiple files.
86860786Sps * If SRCH_FIRST_FILE is set, begin searching at the first file.
86960786Sps * If SRCH_PAST_EOF is set, continue the search thru multiple files.
87060786Sps */
87160786Sps	static void
87260786Spsmulti_search(pattern, n)
87360786Sps	char *pattern;
87460786Sps	int n;
87560786Sps{
87660786Sps	register int nomore;
87760786Sps	IFILE save_ifile;
87860786Sps	int changed_file;
87960786Sps
88060786Sps	changed_file = 0;
88160786Sps	save_ifile = save_curr_ifile();
88260786Sps
88360786Sps	if (search_type & SRCH_FIRST_FILE)
88460786Sps	{
88560786Sps		/*
88660786Sps		 * Start at the first (or last) file
88760786Sps		 * in the command line list.
88860786Sps		 */
88960786Sps		if (search_type & SRCH_FORW)
89060786Sps			nomore = edit_first();
89160786Sps		else
89260786Sps			nomore = edit_last();
89360786Sps		if (nomore)
89460786Sps		{
89560786Sps			unsave_ifile(save_ifile);
89660786Sps			return;
89760786Sps		}
89860786Sps		changed_file = 1;
89960786Sps		search_type &= ~SRCH_FIRST_FILE;
90060786Sps	}
90160786Sps
90260786Sps	for (;;)
90360786Sps	{
90460786Sps		n = search(search_type, pattern, n);
90560786Sps		/*
90660786Sps		 * The SRCH_NO_MOVE flag doesn't "stick": it gets cleared
90760786Sps		 * after being used once.  This allows "n" to work after
90860786Sps		 * using a /@@ search.
90960786Sps		 */
91060786Sps		search_type &= ~SRCH_NO_MOVE;
91160786Sps		if (n == 0)
91260786Sps		{
91360786Sps			/*
91460786Sps			 * Found it.
91560786Sps			 */
91660786Sps			unsave_ifile(save_ifile);
91760786Sps			return;
91860786Sps		}
91960786Sps
92060786Sps		if (n < 0)
92160786Sps			/*
92260786Sps			 * Some kind of error in the search.
92360786Sps			 * Error message has been printed by search().
92460786Sps			 */
92560786Sps			break;
92660786Sps
92760786Sps		if ((search_type & SRCH_PAST_EOF) == 0)
92860786Sps			/*
92960786Sps			 * We didn't find a match, but we're
93060786Sps			 * supposed to search only one file.
93160786Sps			 */
93260786Sps			break;
93360786Sps		/*
93460786Sps		 * Move on to the next file.
93560786Sps		 */
93660786Sps		if (search_type & SRCH_FORW)
93760786Sps			nomore = edit_next(1);
93860786Sps		else
93960786Sps			nomore = edit_prev(1);
94060786Sps		if (nomore)
94160786Sps			break;
94260786Sps		changed_file = 1;
94360786Sps	}
94460786Sps
94560786Sps	/*
94660786Sps	 * Didn't find it.
94760786Sps	 * Print an error message if we haven't already.
94860786Sps	 */
94960786Sps	if (n > 0)
95060786Sps		error("Pattern not found", NULL_PARG);
95160786Sps
95260786Sps	if (changed_file)
95360786Sps	{
95460786Sps		/*
95560786Sps		 * Restore the file we were originally viewing.
95660786Sps		 */
95760786Sps		reedit_ifile(save_ifile);
958161478Sdelphij	} else
959161478Sdelphij	{
960161478Sdelphij		unsave_ifile(save_ifile);
96160786Sps	}
96260786Sps}
96360786Sps
96460786Sps/*
965240121Sdelphij * Forward forever, or until a highlighted line appears.
966240121Sdelphij */
967240121Sdelphij	static int
968240121Sdelphijforw_loop(until_hilite)
969240121Sdelphij	int until_hilite;
970240121Sdelphij{
971240121Sdelphij	POSITION curr_len;
972240121Sdelphij
973240121Sdelphij	if (ch_getflags() & CH_HELPFILE)
974240121Sdelphij		return (A_NOACTION);
975240121Sdelphij
976240121Sdelphij	cmd_exec();
977240121Sdelphij	jump_forw();
978240121Sdelphij	curr_len = ch_length();
979240121Sdelphij	highest_hilite = until_hilite ? curr_len : NULL_POSITION;
980240121Sdelphij	ignore_eoi = 1;
981240121Sdelphij	while (!sigs)
982240121Sdelphij	{
983240121Sdelphij		if (until_hilite && highest_hilite > curr_len)
984240121Sdelphij		{
985240121Sdelphij			bell();
986240121Sdelphij			break;
987240121Sdelphij		}
988240121Sdelphij		make_display();
989240121Sdelphij		forward(1, 0, 0);
990240121Sdelphij	}
991240121Sdelphij	ignore_eoi = 0;
992240121Sdelphij	ch_set_eof();
993240121Sdelphij
994240121Sdelphij	/*
995240121Sdelphij	 * This gets us back in "F mode" after processing
996240121Sdelphij	 * a non-abort signal (e.g. window-change).
997240121Sdelphij	 */
998240121Sdelphij	if (sigs && !ABORT_SIGS())
999240121Sdelphij		return (until_hilite ? A_F_UNTIL_HILITE : A_F_FOREVER);
1000240121Sdelphij
1001240121Sdelphij	return (A_NOACTION);
1002240121Sdelphij}
1003240121Sdelphij
1004240121Sdelphij/*
100560786Sps * Main command processor.
100660786Sps * Accept and execute commands until a quit command.
100760786Sps */
100860786Sps	public void
100960786Spscommands()
101060786Sps{
101160786Sps	register int c;
101260786Sps	register int action;
101360786Sps	register char *cbuf;
101460786Sps	int newaction;
101560786Sps	int save_search_type;
101660786Sps	char *extra;
101760786Sps	char tbuf[2];
101860786Sps	PARG parg;
101960786Sps	IFILE old_ifile;
102060786Sps	IFILE new_ifile;
102189022Sps	char *tagfile;
1022240121Sdelphij	int until_hilite = 0;
102360786Sps
102460786Sps	search_type = SRCH_FORW;
102560786Sps	wscroll = (sc_height + 1) / 2;
102660786Sps	newaction = A_NOACTION;
102760786Sps
102860786Sps	for (;;)
102960786Sps	{
103060786Sps		mca = 0;
103160786Sps		cmd_accept();
103260786Sps		number = 0;
1033221715Sdelphij		curropt = NULL;
103460786Sps
103560786Sps		/*
103660786Sps		 * See if any signals need processing.
103760786Sps		 */
103860786Sps		if (sigs)
103960786Sps		{
104060786Sps			psignals();
104160786Sps			if (quitting)
104260786Sps				quit(QUIT_SAVED_STATUS);
104360786Sps		}
104460786Sps
104560786Sps		/*
104660786Sps		 * See if window size changed, for systems that don't
104760786Sps		 * generate SIGWINCH.
104860786Sps		 */
104960786Sps		check_winch();
105060786Sps
105160786Sps		/*
105260786Sps		 * Display prompt and accept a character.
105360786Sps		 */
105460786Sps		cmd_reset();
105560786Sps		prompt();
105660786Sps		if (sigs)
105760786Sps			continue;
105860786Sps		if (newaction == A_NOACTION)
105960786Sps			c = getcc();
106060786Sps
106160786Sps	again:
106260786Sps		if (sigs)
106360786Sps			continue;
106460786Sps
106560786Sps		if (newaction != A_NOACTION)
106660786Sps		{
106760786Sps			action = newaction;
106860786Sps			newaction = A_NOACTION;
106960786Sps		} else
107060786Sps		{
107160786Sps			/*
107260786Sps			 * If we are in a multicharacter command, call mca_char.
107360786Sps			 * Otherwise we call fcmd_decode to determine the
107460786Sps			 * action to be performed.
107560786Sps			 */
107660786Sps			if (mca)
107760786Sps				switch (mca_char(c))
107860786Sps				{
107960786Sps				case MCA_MORE:
108060786Sps					/*
108160786Sps					 * Need another character.
108260786Sps					 */
108360786Sps					c = getcc();
108460786Sps					goto again;
108560786Sps				case MCA_DONE:
108660786Sps					/*
108760786Sps					 * Command has been handled by mca_char.
108860786Sps					 * Start clean with a prompt.
108960786Sps					 */
109060786Sps					continue;
109160786Sps				case NO_MCA:
109260786Sps					/*
109360786Sps					 * Not a multi-char command
109460786Sps					 * (at least, not anymore).
109560786Sps					 */
109660786Sps					break;
109760786Sps				}
109860786Sps
109960786Sps			/*
110060786Sps			 * Decode the command character and decide what to do.
110160786Sps			 */
110260786Sps			if (mca)
110360786Sps			{
110460786Sps				/*
110560786Sps				 * We're in a multichar command.
110660786Sps				 * Add the character to the command buffer
110760786Sps				 * and display it on the screen.
110860786Sps				 * If the user backspaces past the start
110960786Sps				 * of the line, abort the command.
111060786Sps				 */
111160786Sps				if (cmd_char(c) == CC_QUIT || len_cmdbuf() == 0)
111260786Sps					continue;
111360786Sps				cbuf = get_cmdbuf();
111460786Sps			} else
111560786Sps			{
111660786Sps				/*
111760786Sps				 * Don't use cmd_char if we're starting fresh
111860786Sps				 * at the beginning of a command, because we
111960786Sps				 * don't want to echo the command until we know
112060786Sps				 * it is a multichar command.  We also don't
112160786Sps				 * want erase_char/kill_char to be treated
112260786Sps				 * as line editing characters.
112360786Sps				 */
112460786Sps				tbuf[0] = c;
112560786Sps				tbuf[1] = '\0';
112660786Sps				cbuf = tbuf;
112760786Sps			}
112860786Sps			extra = NULL;
112960786Sps			action = fcmd_decode(cbuf, &extra);
113060786Sps			/*
113160786Sps			 * If an "extra" string was returned,
113260786Sps			 * process it as a string of command characters.
113360786Sps			 */
113460786Sps			if (extra != NULL)
113560786Sps				ungetsc(extra);
113660786Sps		}
113760786Sps		/*
113860786Sps		 * Clear the cmdbuf string.
113960786Sps		 * (But not if we're in the prefix of a command,
114060786Sps		 * because the partial command string is kept there.)
114160786Sps		 */
114260786Sps		if (action != A_PREFIX)
114360786Sps			cmd_reset();
114460786Sps
114560786Sps		switch (action)
114660786Sps		{
114760786Sps		case A_DIGIT:
114860786Sps			/*
114960786Sps			 * First digit of a number.
115060786Sps			 */
115160786Sps			start_mca(A_DIGIT, ":", (void*)NULL, CF_QUIT_ON_ERASE);
115260786Sps			goto again;
115360786Sps
115460786Sps		case A_F_WINDOW:
115560786Sps			/*
115660786Sps			 * Forward one window (and set the window size).
115760786Sps			 */
115860786Sps			if (number > 0)
1159128348Stjr				swindow = (int) number;
116060786Sps			/* FALLTHRU */
116160786Sps		case A_F_SCREEN:
116260786Sps			/*
116360786Sps			 * Forward one screen.
116460786Sps			 */
116560786Sps			if (number <= 0)
116660786Sps				number = get_swindow();
116760786Sps			cmd_exec();
116860786Sps			if (show_attn)
116960786Sps				set_attnpos(bottompos);
1170128348Stjr			forward((int) number, 0, 1);
117160786Sps			break;
117260786Sps
117360786Sps		case A_B_WINDOW:
117460786Sps			/*
117560786Sps			 * Backward one window (and set the window size).
117660786Sps			 */
117760786Sps			if (number > 0)
1178128348Stjr				swindow = (int) number;
117960786Sps			/* FALLTHRU */
118060786Sps		case A_B_SCREEN:
118160786Sps			/*
118260786Sps			 * Backward one screen.
118360786Sps			 */
118460786Sps			if (number <= 0)
118560786Sps				number = get_swindow();
118660786Sps			cmd_exec();
1187128348Stjr			backward((int) number, 0, 1);
118860786Sps			break;
118960786Sps
119060786Sps		case A_F_LINE:
119160786Sps			/*
119260786Sps			 * Forward N (default 1) line.
119360786Sps			 */
119460786Sps			if (number <= 0)
119560786Sps				number = 1;
119660786Sps			cmd_exec();
119760786Sps			if (show_attn == OPT_ONPLUS && number > 1)
119860786Sps				set_attnpos(bottompos);
1199128348Stjr			forward((int) number, 0, 0);
120060786Sps			break;
120160786Sps
120260786Sps		case A_B_LINE:
120360786Sps			/*
120460786Sps			 * Backward N (default 1) line.
120560786Sps			 */
120660786Sps			if (number <= 0)
120760786Sps				number = 1;
120860786Sps			cmd_exec();
1209128348Stjr			backward((int) number, 0, 0);
121060786Sps			break;
121160786Sps
121260786Sps		case A_FF_LINE:
121360786Sps			/*
121460786Sps			 * Force forward N (default 1) line.
121560786Sps			 */
121660786Sps			if (number <= 0)
121760786Sps				number = 1;
121860786Sps			cmd_exec();
121960786Sps			if (show_attn == OPT_ONPLUS && number > 1)
122060786Sps				set_attnpos(bottompos);
1221128348Stjr			forward((int) number, 1, 0);
122260786Sps			break;
122360786Sps
122460786Sps		case A_BF_LINE:
122560786Sps			/*
122660786Sps			 * Force backward N (default 1) line.
122760786Sps			 */
122860786Sps			if (number <= 0)
122960786Sps				number = 1;
123060786Sps			cmd_exec();
1231128348Stjr			backward((int) number, 1, 0);
123260786Sps			break;
123360786Sps
123460786Sps		case A_FF_SCREEN:
123560786Sps			/*
123660786Sps			 * Force forward one screen.
123760786Sps			 */
123860786Sps			if (number <= 0)
123960786Sps				number = get_swindow();
124060786Sps			cmd_exec();
124160786Sps			if (show_attn == OPT_ONPLUS)
124260786Sps				set_attnpos(bottompos);
1243128348Stjr			forward((int) number, 1, 0);
124460786Sps			break;
124560786Sps
124660786Sps		case A_F_FOREVER:
124760786Sps			/*
124860786Sps			 * Forward forever, ignoring EOF.
124960786Sps			 */
1250240121Sdelphij			newaction = forw_loop(0);
125160786Sps			break;
125260786Sps
1253240121Sdelphij		case A_F_UNTIL_HILITE:
1254240121Sdelphij			newaction = forw_loop(1);
1255240121Sdelphij			break;
1256240121Sdelphij
125760786Sps		case A_F_SCROLL:
125860786Sps			/*
125960786Sps			 * Forward N lines
126060786Sps			 * (default same as last 'd' or 'u' command).
126160786Sps			 */
126260786Sps			if (number > 0)
1263128348Stjr				wscroll = (int) number;
126460786Sps			cmd_exec();
126560786Sps			if (show_attn == OPT_ONPLUS)
126660786Sps				set_attnpos(bottompos);
126760786Sps			forward(wscroll, 0, 0);
126860786Sps			break;
126960786Sps
127060786Sps		case A_B_SCROLL:
127160786Sps			/*
127260786Sps			 * Forward N lines
127360786Sps			 * (default same as last 'd' or 'u' command).
127460786Sps			 */
127560786Sps			if (number > 0)
1276128348Stjr				wscroll = (int) number;
127760786Sps			cmd_exec();
127860786Sps			backward(wscroll, 0, 0);
127960786Sps			break;
128060786Sps
128160786Sps		case A_FREPAINT:
128260786Sps			/*
128360786Sps			 * Flush buffers, then repaint screen.
128460786Sps			 * Don't flush the buffers on a pipe!
128560786Sps			 */
1286173685Sdelphij			clear_buffers();
128760786Sps			/* FALLTHRU */
128860786Sps		case A_REPAINT:
128960786Sps			/*
129060786Sps			 * Repaint screen.
129160786Sps			 */
129260786Sps			cmd_exec();
129360786Sps			repaint();
129460786Sps			break;
129560786Sps
129660786Sps		case A_GOLINE:
129760786Sps			/*
129860786Sps			 * Go to line N, default beginning of file.
129960786Sps			 */
130060786Sps			if (number <= 0)
130160786Sps				number = 1;
130260786Sps			cmd_exec();
130360786Sps			jump_back(number);
130460786Sps			break;
130560786Sps
130660786Sps		case A_PERCENT:
130760786Sps			/*
130860786Sps			 * Go to a specified percentage into the file.
130960786Sps			 */
131060786Sps			if (number < 0)
1311170259Sdelphij			{
131260786Sps				number = 0;
1313170259Sdelphij				fraction = 0;
1314170259Sdelphij			}
131560786Sps			if (number > 100)
1316170259Sdelphij			{
131760786Sps				number = 100;
1318170259Sdelphij				fraction = 0;
1319170259Sdelphij			}
132060786Sps			cmd_exec();
1321170259Sdelphij			jump_percent((int) number, fraction);
132260786Sps			break;
132360786Sps
132460786Sps		case A_GOEND:
132560786Sps			/*
132660786Sps			 * Go to line N, default end of file.
132760786Sps			 */
132860786Sps			cmd_exec();
132960786Sps			if (number <= 0)
133060786Sps				jump_forw();
133160786Sps			else
133260786Sps				jump_back(number);
133360786Sps			break;
133460786Sps
133560786Sps		case A_GOPOS:
133660786Sps			/*
133760786Sps			 * Go to a specified byte position in the file.
133860786Sps			 */
133960786Sps			cmd_exec();
134060786Sps			if (number < 0)
134160786Sps				number = 0;
1342128348Stjr			jump_line_loc((POSITION) number, jump_sline);
134360786Sps			break;
134460786Sps
134560786Sps		case A_STAT:
134660786Sps			/*
134760786Sps			 * Print file name, etc.
134860786Sps			 */
134960786Sps			if (ch_getflags() & CH_HELPFILE)
135060786Sps				break;
135160786Sps			cmd_exec();
135260786Sps			parg.p_string = eq_message();
135360786Sps			error("%s", &parg);
135460786Sps			break;
135560786Sps
135660786Sps		case A_VERSION:
135760786Sps			/*
135860786Sps			 * Print version number, without the "@(#)".
135960786Sps			 */
136060786Sps			cmd_exec();
136160786Sps			dispversion();
136260786Sps			break;
136360786Sps
136460786Sps		case A_QUIT:
136560786Sps			/*
136660786Sps			 * Exit.
136760786Sps			 */
136860786Sps			if (curr_ifile != NULL_IFILE &&
136960786Sps			    ch_getflags() & CH_HELPFILE)
137060786Sps			{
137160786Sps				/*
137260786Sps				 * Quit while viewing the help file
137360786Sps				 * just means return to viewing the
137460786Sps				 * previous file.
137560786Sps				 */
1376161478Sdelphij				hshift = save_hshift;
137760786Sps				if (edit_prev(1) == 0)
137860786Sps					break;
137960786Sps			}
138060786Sps			if (extra != NULL)
138160786Sps				quit(*extra);
138260786Sps			quit(QUIT_OK);
138360786Sps			break;
138460786Sps
138560786Sps/*
138660786Sps * Define abbreviation for a commonly used sequence below.
138760786Sps */
1388173685Sdelphij#define	DO_SEARCH() \
1389173685Sdelphij			if (number <= 0) number = 1;	\
139060786Sps			mca_search();			\
139160786Sps			cmd_exec();			\
1392128348Stjr			multi_search((char *)NULL, (int) number);
139360786Sps
139460786Sps
139560786Sps		case A_F_SEARCH:
139660786Sps			/*
139760786Sps			 * Search forward for a pattern.
139860786Sps			 * Get the first char of the pattern.
139960786Sps			 */
140060786Sps			search_type = SRCH_FORW;
140160786Sps			if (number <= 0)
140260786Sps				number = 1;
140360786Sps			mca_search();
140460786Sps			c = getcc();
140560786Sps			goto again;
140660786Sps
140760786Sps		case A_B_SEARCH:
140860786Sps			/*
140960786Sps			 * Search backward for a pattern.
141060786Sps			 * Get the first char of the pattern.
141160786Sps			 */
141260786Sps			search_type = SRCH_BACK;
141360786Sps			if (number <= 0)
141460786Sps				number = 1;
141560786Sps			mca_search();
141660786Sps			c = getcc();
141760786Sps			goto again;
141860786Sps
1419191930Sdelphij		case A_FILTER:
1420191930Sdelphij#if HILITE_SEARCH
1421191930Sdelphij			search_type = SRCH_FORW | SRCH_FILTER;
1422191930Sdelphij			mca_search();
1423191930Sdelphij			c = getcc();
1424191930Sdelphij			goto again;
1425191930Sdelphij#else
1426191930Sdelphij			error("Command not available", NULL_PARG);
1427191930Sdelphij			break;
1428191930Sdelphij#endif
1429191930Sdelphij
143060786Sps		case A_AGAIN_SEARCH:
143160786Sps			/*
143260786Sps			 * Repeat previous search.
143360786Sps			 */
143460786Sps			DO_SEARCH();
143560786Sps			break;
143660786Sps
143760786Sps		case A_T_AGAIN_SEARCH:
143860786Sps			/*
143960786Sps			 * Repeat previous search, multiple files.
144060786Sps			 */
144160786Sps			search_type |= SRCH_PAST_EOF;
144260786Sps			DO_SEARCH();
144360786Sps			break;
144460786Sps
144560786Sps		case A_REVERSE_SEARCH:
144660786Sps			/*
144760786Sps			 * Repeat previous search, in reverse direction.
144860786Sps			 */
144960786Sps			save_search_type = search_type;
145060786Sps			search_type = SRCH_REVERSE(search_type);
145160786Sps			DO_SEARCH();
145260786Sps			search_type = save_search_type;
145360786Sps			break;
145460786Sps
145560786Sps		case A_T_REVERSE_SEARCH:
145660786Sps			/*
145760786Sps			 * Repeat previous search,
145860786Sps			 * multiple files in reverse direction.
145960786Sps			 */
146060786Sps			save_search_type = search_type;
146160786Sps			search_type = SRCH_REVERSE(search_type);
146260786Sps			search_type |= SRCH_PAST_EOF;
146360786Sps			DO_SEARCH();
146460786Sps			search_type = save_search_type;
146560786Sps			break;
146660786Sps
146760786Sps		case A_UNDO_SEARCH:
146860786Sps			undo_search();
146960786Sps			break;
147060786Sps
147160786Sps		case A_HELP:
147260786Sps			/*
147360786Sps			 * Help.
147460786Sps			 */
147560786Sps			if (ch_getflags() & CH_HELPFILE)
147660786Sps				break;
147760786Sps			cmd_exec();
1478161478Sdelphij			save_hshift = hshift;
1479161478Sdelphij			hshift = 0;
148060786Sps			(void) edit(FAKE_HELPFILE);
148160786Sps			break;
148260786Sps
148360786Sps		case A_EXAMINE:
148460786Sps#if EXAMINE
148560786Sps			/*
148660786Sps			 * Edit a new file.  Get the filename.
148760786Sps			 */
148860786Sps			if (secure)
148960786Sps			{
149060786Sps				error("Command not available", NULL_PARG);
149160786Sps				break;
149260786Sps			}
149360786Sps			start_mca(A_EXAMINE, "Examine: ", ml_examine, 0);
149460786Sps			c = getcc();
149560786Sps			goto again;
149660786Sps#else
149760786Sps			error("Command not available", NULL_PARG);
149860786Sps			break;
149960786Sps#endif
150060786Sps
150160786Sps		case A_VISUAL:
150260786Sps			/*
150360786Sps			 * Invoke an editor on the input file.
150460786Sps			 */
150560786Sps#if EDITOR
150660786Sps			if (secure)
150760786Sps			{
150860786Sps				error("Command not available", NULL_PARG);
150960786Sps				break;
151060786Sps			}
151160786Sps			if (ch_getflags() & CH_HELPFILE)
151260786Sps				break;
151360786Sps			if (strcmp(get_filename(curr_ifile), "-") == 0)
151460786Sps			{
151560786Sps				error("Cannot edit standard input", NULL_PARG);
151660786Sps				break;
151760786Sps			}
151860786Sps			if (curr_altfilename != NULL)
151960786Sps			{
1520161478Sdelphij				error("WARNING: This file was viewed via LESSOPEN",
152160786Sps					NULL_PARG);
152260786Sps			}
152360786Sps			start_mca(A_SHELL, "!", ml_shell, 0);
152460786Sps			/*
152560786Sps			 * Expand the editor prototype string
152660786Sps			 * and pass it to the system to execute.
152760786Sps			 * (Make sure the screen is displayed so the
152860786Sps			 * expansion of "+%lm" works.)
152960786Sps			 */
153060786Sps			make_display();
153160786Sps			cmd_exec();
153260786Sps			lsystem(pr_expand(editproto, 0), (char*)NULL);
153360786Sps			break;
153460786Sps#else
153560786Sps			error("Command not available", NULL_PARG);
153660786Sps			break;
153760786Sps#endif
153860786Sps
153960786Sps		case A_NEXT_FILE:
154060786Sps			/*
154160786Sps			 * Examine next file.
154260786Sps			 */
1543128348Stjr#if TAGS
154489022Sps			if (ntags())
154589022Sps			{
154689022Sps				error("No next file", NULL_PARG);
154789022Sps				break;
154889022Sps			}
1549128348Stjr#endif
155060786Sps			if (number <= 0)
155160786Sps				number = 1;
1552128348Stjr			if (edit_next((int) number))
155360786Sps			{
1554191930Sdelphij				if (get_quit_at_eof() && eof_displayed() &&
155560786Sps				    !(ch_getflags() & CH_HELPFILE))
155660786Sps					quit(QUIT_OK);
155760786Sps				parg.p_string = (number > 1) ? "(N-th) " : "";
155860786Sps				error("No %snext file", &parg);
155960786Sps			}
156060786Sps			break;
156160786Sps
156260786Sps		case A_PREV_FILE:
156360786Sps			/*
156460786Sps			 * Examine previous file.
156560786Sps			 */
1566128348Stjr#if TAGS
156789022Sps			if (ntags())
156889022Sps			{
156989022Sps				error("No previous file", NULL_PARG);
157089022Sps				break;
157189022Sps			}
1572128348Stjr#endif
157360786Sps			if (number <= 0)
157460786Sps				number = 1;
1575128348Stjr			if (edit_prev((int) number))
157660786Sps			{
157760786Sps				parg.p_string = (number > 1) ? "(N-th) " : "";
157860786Sps				error("No %sprevious file", &parg);
157960786Sps			}
158060786Sps			break;
158160786Sps
158289022Sps		case A_NEXT_TAG:
1583128348Stjr#if TAGS
158489022Sps			if (number <= 0)
158589022Sps				number = 1;
1586128348Stjr			tagfile = nexttag((int) number);
158789022Sps			if (tagfile == NULL)
158889022Sps			{
158989022Sps				error("No next tag", NULL_PARG);
159089022Sps				break;
159189022Sps			}
159289022Sps			if (edit(tagfile) == 0)
159389022Sps			{
159489022Sps				POSITION pos = tagsearch();
159589022Sps				if (pos != NULL_POSITION)
159689022Sps					jump_loc(pos, jump_sline);
159789022Sps			}
1598128348Stjr#else
1599128348Stjr			error("Command not available", NULL_PARG);
1600128348Stjr#endif
160189022Sps			break;
160289022Sps
160389022Sps		case A_PREV_TAG:
1604128348Stjr#if TAGS
160589022Sps			if (number <= 0)
160689022Sps				number = 1;
1607128348Stjr			tagfile = prevtag((int) number);
160889022Sps			if (tagfile == NULL)
160989022Sps			{
161089022Sps				error("No previous tag", NULL_PARG);
161189022Sps				break;
161289022Sps			}
161389022Sps			if (edit(tagfile) == 0)
161489022Sps			{
161589022Sps				POSITION pos = tagsearch();
161689022Sps				if (pos != NULL_POSITION)
161789022Sps					jump_loc(pos, jump_sline);
161889022Sps			}
1619128348Stjr#else
1620128348Stjr			error("Command not available", NULL_PARG);
1621128348Stjr#endif
162289022Sps			break;
162389022Sps
162460786Sps		case A_INDEX_FILE:
162560786Sps			/*
162660786Sps			 * Examine a particular file.
162760786Sps			 */
162860786Sps			if (number <= 0)
162960786Sps				number = 1;
1630128348Stjr			if (edit_index((int) number))
163160786Sps				error("No such file", NULL_PARG);
163260786Sps			break;
163360786Sps
163460786Sps		case A_REMOVE_FILE:
163560786Sps			if (ch_getflags() & CH_HELPFILE)
163660786Sps				break;
163760786Sps			old_ifile = curr_ifile;
163860786Sps			new_ifile = getoff_ifile(curr_ifile);
163960786Sps			if (new_ifile == NULL_IFILE)
164060786Sps			{
164160786Sps				bell();
164260786Sps				break;
164360786Sps			}
164460786Sps			if (edit_ifile(new_ifile) != 0)
164560786Sps			{
164660786Sps				reedit_ifile(old_ifile);
164760786Sps				break;
164860786Sps			}
164960786Sps			del_ifile(old_ifile);
165060786Sps			break;
165160786Sps
165260786Sps		case A_OPT_TOGGLE:
165360786Sps			optflag = OPT_TOGGLE;
165460786Sps			optgetname = FALSE;
165560786Sps			mca_opt_toggle();
165660786Sps			c = getcc();
165760786Sps			goto again;
165860786Sps
165960786Sps		case A_DISP_OPTION:
166060786Sps			/*
166160786Sps			 * Report a flag setting.
166260786Sps			 */
166360786Sps			optflag = OPT_NO_TOGGLE;
166460786Sps			optgetname = FALSE;
166560786Sps			mca_opt_toggle();
166660786Sps			c = getcc();
166760786Sps			goto again;
166860786Sps
166960786Sps		case A_FIRSTCMD:
167060786Sps			/*
167160786Sps			 * Set an initial command for new files.
167260786Sps			 */
167360786Sps			start_mca(A_FIRSTCMD, "+", (void*)NULL, 0);
167460786Sps			c = getcc();
167560786Sps			goto again;
167660786Sps
167760786Sps		case A_SHELL:
167860786Sps			/*
167960786Sps			 * Shell escape.
168060786Sps			 */
168160786Sps#if SHELL_ESCAPE
168260786Sps			if (secure)
168360786Sps			{
168460786Sps				error("Command not available", NULL_PARG);
168560786Sps				break;
168660786Sps			}
168760786Sps			start_mca(A_SHELL, "!", ml_shell, 0);
168860786Sps			c = getcc();
168960786Sps			goto again;
169060786Sps#else
169160786Sps			error("Command not available", NULL_PARG);
169260786Sps			break;
169360786Sps#endif
169460786Sps
169560786Sps		case A_SETMARK:
169660786Sps			/*
169760786Sps			 * Set a mark.
169860786Sps			 */
169960786Sps			if (ch_getflags() & CH_HELPFILE)
170060786Sps				break;
170160786Sps			start_mca(A_SETMARK, "mark: ", (void*)NULL, 0);
170260786Sps			c = getcc();
1703161478Sdelphij			if (c == erase_char || c == erase2_char ||
1704161478Sdelphij			    c == kill_char || c == '\n' || c == '\r')
170560786Sps				break;
170660786Sps			setmark(c);
170760786Sps			break;
170860786Sps
170960786Sps		case A_GOMARK:
171060786Sps			/*
171160786Sps			 * Go to a mark.
171260786Sps			 */
171360786Sps			start_mca(A_GOMARK, "goto mark: ", (void*)NULL, 0);
171460786Sps			c = getcc();
1715161478Sdelphij			if (c == erase_char || c == erase2_char ||
1716161478Sdelphij			    c == kill_char || c == '\n' || c == '\r')
171760786Sps				break;
1718191930Sdelphij			cmd_exec();
171960786Sps			gomark(c);
172060786Sps			break;
172160786Sps
172260786Sps		case A_PIPE:
172360786Sps#if PIPEC
172460786Sps			if (secure)
172560786Sps			{
172660786Sps				error("Command not available", NULL_PARG);
172760786Sps				break;
172860786Sps			}
172960786Sps			start_mca(A_PIPE, "|mark: ", (void*)NULL, 0);
173060786Sps			c = getcc();
1731161478Sdelphij			if (c == erase_char || c == erase2_char || c == kill_char)
173260786Sps				break;
173360786Sps			if (c == '\n' || c == '\r')
173460786Sps				c = '.';
173560786Sps			if (badmark(c))
173660786Sps				break;
173760786Sps			pipec = c;
173860786Sps			start_mca(A_PIPE, "!", ml_shell, 0);
173960786Sps			c = getcc();
174060786Sps			goto again;
174160786Sps#else
174260786Sps			error("Command not available", NULL_PARG);
174360786Sps			break;
174460786Sps#endif
174560786Sps
174660786Sps		case A_B_BRACKET:
174760786Sps		case A_F_BRACKET:
174860786Sps			start_mca(action, "Brackets: ", (void*)NULL, 0);
174960786Sps			c = getcc();
175060786Sps			goto again;
175160786Sps
175260786Sps		case A_LSHIFT:
175389022Sps			if (number > 0)
175489022Sps				shift_count = number;
175589022Sps			else
175663131Sps				number = (shift_count > 0) ?
175763131Sps					shift_count : sc_width / 2;
175860786Sps			if (number > hshift)
175960786Sps				number = hshift;
176060786Sps			hshift -= number;
176160786Sps			screen_trashed = 1;
176260786Sps			break;
176360786Sps
176460786Sps		case A_RSHIFT:
176589022Sps			if (number > 0)
176689022Sps				shift_count = number;
176789022Sps			else
176863131Sps				number = (shift_count > 0) ?
176963131Sps					shift_count : sc_width / 2;
177060786Sps			hshift += number;
177160786Sps			screen_trashed = 1;
177260786Sps			break;
177360786Sps
177460786Sps		case A_PREFIX:
177560786Sps			/*
177660786Sps			 * The command is incomplete (more chars are needed).
177760786Sps			 * Display the current char, so the user knows
177860786Sps			 * what's going on, and get another character.
177960786Sps			 */
178060786Sps			if (mca != A_PREFIX)
178160786Sps			{
178260786Sps				cmd_reset();
178360786Sps				start_mca(A_PREFIX, " ", (void*)NULL,
178460786Sps					CF_QUIT_ON_ERASE);
178560786Sps				(void) cmd_char(c);
178660786Sps			}
178760786Sps			c = getcc();
178860786Sps			goto again;
178960786Sps
179060786Sps		case A_NOACTION:
179160786Sps			break;
179260786Sps
179360786Sps		default:
179460786Sps			bell();
179560786Sps			break;
179660786Sps		}
179760786Sps	}
179860786Sps}
1799