"SfR Fresh" - the SfR Freeware/Shareware Archive

Member "less-424/command.c" of archive less-424.tar.gz:


As a special service "SfR Fresh" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting with prefixed line numbers. Alternatively you can here view or download the uninterpreted source code file. That can be also achieved for any archive member file by clicking within an archive contents listing on the first character of the file(path) respectively on the according byte size field.
    1 /*
    2  * Copyright (C) 1984-2008  Mark Nudelman
    3  *
    4  * You may distribute under the terms of either the GNU General Public
    5  * License or the Less License, as specified in the README file.
    6  *
    7  * For more information about less, or for information on how to
    8  * contact the author, see the README file.
    9  */
   10 
   11 
   12 /*
   13  * User-level command processor.
   14  */
   15 
   16 #include "less.h"
   17 #if MSDOS_COMPILER==WIN32C
   18 #include <windows.h>
   19 #endif
   20 #include "position.h"
   21 #include "option.h"
   22 #include "cmd.h"
   23 
   24 extern int erase_char, erase2_char, kill_char;
   25 extern int sigs;
   26 extern int quit_if_one_screen;
   27 extern int squished;
   28 extern int sc_width;
   29 extern int sc_height;
   30 extern int swindow;
   31 extern int jump_sline;
   32 extern int quitting;
   33 extern int wscroll;
   34 extern int top_scroll;
   35 extern int ignore_eoi;
   36 extern int secure;
   37 extern int hshift;
   38 extern int show_attn;
   39 extern char *every_first_cmd;
   40 extern char *curr_altfilename;
   41 extern char version[];
   42 extern struct scrpos initial_scrpos;
   43 extern IFILE curr_ifile;
   44 extern void constant *ml_search;
   45 extern void constant *ml_examine;
   46 #if SHELL_ESCAPE || PIPEC
   47 extern void constant *ml_shell;
   48 #endif
   49 #if EDITOR
   50 extern char *editor;
   51 extern char *editproto;
   52 #endif
   53 extern int screen_trashed;	/* The screen has been overwritten */
   54 extern int shift_count;
   55 extern int oldbot;
   56 extern int forw_prompt;
   57 
   58 static char ungot[UNGOT_SIZE];
   59 static char *ungotp = NULL;
   60 #if SHELL_ESCAPE
   61 static char *shellcmd = NULL;	/* For holding last shell command for "!!" */
   62 #endif
   63 static int mca;			/* The multicharacter command (action) */
   64 static int search_type;		/* The previous type of search */
   65 static LINENUM number;		/* The number typed by the user */
   66 static long fraction;		/* The fractional part of the number */
   67 static char optchar;
   68 static int optflag;
   69 static int optgetname;
   70 static POSITION bottompos;
   71 static int save_hshift;
   72 #if PIPEC
   73 static char pipec;
   74 #endif
   75 
   76 static void multi_search();
   77 
   78 /*
   79  * Move the cursor to start of prompt line before executing a command.
   80  * This looks nicer if the command takes a long time before
   81  * updating the screen.
   82  */
   83 	static void
   84 cmd_exec()
   85 {
   86 #if HILITE_SEARCH
   87 	clear_attn();
   88 #endif
   89 	clear_bot();
   90 	flush();
   91 }
   92 
   93 /*
   94  * Set up the display to start a new multi-character command.
   95  */
   96 	static void
   97 start_mca(action, prompt, mlist, cmdflags)
   98 	int action;
   99 	char *prompt;
  100 	void *mlist;
  101 	int cmdflags;
  102 {
  103 	mca = action;
  104 	clear_bot();
  105 	clear_cmd();
  106 	cmd_putstr(prompt);
  107 	set_mlist(mlist, cmdflags);
  108 }
  109 
  110 	public int
  111 in_mca()
  112 {
  113 	return (mca != 0 && mca != A_PREFIX);
  114 }
  115 
  116 /*
  117  * Set up the display to start a new search command.
  118  */
  119 	static void
  120 mca_search()
  121 {
  122 #if HILITE_SEARCH
  123 	if (search_type & SRCH_FILTER)
  124 		mca = A_FILTER;
  125 	else
  126 #endif
  127 	if (search_type & SRCH_FORW)
  128 		mca = A_F_SEARCH;
  129 	else
  130 		mca = A_B_SEARCH;
  131 
  132 	clear_bot();
  133 	clear_cmd();
  134 
  135 	if (search_type & SRCH_NO_MATCH)
  136 		cmd_putstr("Non-match ");
  137 	if (search_type & SRCH_FIRST_FILE)
  138 		cmd_putstr("First-file ");
  139 	if (search_type & SRCH_PAST_EOF)
  140 		cmd_putstr("EOF-ignore ");
  141 	if (search_type & SRCH_NO_MOVE)
  142 		cmd_putstr("Keep-pos ");
  143 	if (search_type & SRCH_NO_REGEX)
  144 		cmd_putstr("Regex-off ");
  145 
  146 #if HILITE_SEARCH
  147 	if (search_type & SRCH_FILTER)
  148 		cmd_putstr("&/");
  149 	else
  150 #endif
  151 	if (search_type & SRCH_FORW)
  152 		cmd_putstr("/");
  153 	else
  154 		cmd_putstr("?");
  155 	set_mlist(ml_search, 0);
  156 }
  157 
  158 /*
  159  * Set up the display to start a new toggle-option command.
  160  */
  161 	static void
  162 mca_opt_toggle()
  163 {
  164 	int no_prompt;
  165 	int flag;
  166 	char *dash;
  167 
  168 	no_prompt = (optflag & OPT_NO_PROMPT);
  169 	flag = (optflag & ~OPT_NO_PROMPT);
  170 	dash = (flag == OPT_NO_TOGGLE) ? "_" : "-";
  171 
  172 	mca = A_OPT_TOGGLE;
  173 	clear_bot();
  174 	clear_cmd();
  175 	cmd_putstr(dash);
  176 	if (optgetname)
  177 		cmd_putstr(dash);
  178 	if (no_prompt)
  179 		cmd_putstr("(P)");
  180 	switch (flag)
  181 	{
  182 	case OPT_UNSET:
  183 		cmd_putstr("+");
  184 		break;
  185 	case OPT_SET:
  186 		cmd_putstr("!");
  187 		break;
  188 	}
  189 	set_mlist(NULL, 0);
  190 }
  191 
  192 /*
  193  * Execute a multicharacter command.
  194  */
  195 	static void
  196 exec_mca()
  197 {
  198 	register char *cbuf;
  199 
  200 	cmd_exec();
  201 	cbuf = get_cmdbuf();
  202 
  203 	switch (mca)
  204 	{
  205 	case A_F_SEARCH:
  206 	case A_B_SEARCH:
  207 		multi_search(cbuf, (int) number);
  208 		break;
  209 #if HILITE_SEARCH
  210 	case A_FILTER:
  211 		search_type ^= SRCH_NO_MATCH;
  212 		set_filter_pattern(cbuf, search_type);
  213 		break;
  214 #endif
  215 	case A_FIRSTCMD:
  216 		/*
  217 		 * Skip leading spaces or + signs in the string.
  218 		 */
  219 		while (*cbuf == '+' || *cbuf == ' ')
  220 			cbuf++;
  221 		if (every_first_cmd != NULL)
  222 			free(every_first_cmd);
  223 		if (*cbuf == '\0')
  224 			every_first_cmd = NULL;
  225 		else
  226 			every_first_cmd = save(cbuf);
  227 		break;
  228 	case A_OPT_TOGGLE:
  229 		toggle_option(optchar, cbuf, optflag);
  230 		optchar = '\0';
  231 		break;
  232 	case A_F_BRACKET:
  233 		match_brac(cbuf[0], cbuf[1], 1, (int) number);
  234 		break;
  235 	case A_B_BRACKET:
  236 		match_brac(cbuf[1], cbuf[0], 0, (int) number);
  237 		break;
  238 #if EXAMINE
  239 	case A_EXAMINE:
  240 		if (secure)
  241 			break;
  242 		edit_list(cbuf);
  243 #if TAGS
  244 		/* If tag structure is loaded then clean it up. */
  245 		cleantags();
  246 #endif
  247 		break;
  248 #endif
  249 #if SHELL_ESCAPE
  250 	case A_SHELL:
  251 		/*
  252 		 * !! just uses whatever is in shellcmd.
  253 		 * Otherwise, copy cmdbuf to shellcmd,
  254 		 * expanding any special characters ("%" or "#").
  255 		 */
  256 		if (*cbuf != '!')
  257 		{
  258 			if (shellcmd != NULL)
  259 				free(shellcmd);
  260 			shellcmd = fexpand(cbuf);
  261 		}
  262 
  263 		if (secure)
  264 			break;
  265 		if (shellcmd == NULL)
  266 			lsystem("", "!done");
  267 		else
  268 			lsystem(shellcmd, "!done");
  269 		break;
  270 #endif
  271 #if PIPEC
  272 	case A_PIPE:
  273 		if (secure)
  274 			break;
  275 		(void) pipe_mark(pipec, cbuf);
  276 		error("|done", NULL_PARG);
  277 		break;
  278 #endif
  279 	}
  280 }
  281 
  282 /*
  283  * Add a character to a multi-character command.
  284  */
  285 	static int
  286 mca_char(c)
  287 	int c;
  288 {
  289 	char *p;
  290 	int flag;
  291 	char buf[3];
  292 	PARG parg;
  293 
  294 	switch (mca)
  295 	{
  296 	case 0:
  297 		/*
  298 		 * Not in a multicharacter command.
  299 		 */
  300 		return (NO_MCA);
  301 
  302 	case A_PREFIX:
  303 		/*
  304 		 * In the prefix of a command.
  305 		 * This not considered a multichar command
  306 		 * (even tho it uses cmdbuf, etc.).
  307 		 * It is handled in the commands() switch.
  308 		 */
  309 		return (NO_MCA);
  310 
  311 	case A_DIGIT:
  312 		/*
  313 		 * Entering digits of a number.
  314 		 * Terminated by a non-digit.
  315 		 */
  316 		if (!((c >= '0' && c <= '9') || c == '.') &&
  317 		  editchar(c, EC_PEEK|EC_NOHISTORY|EC_NOCOMPLETE|EC_NORIGHTLEFT) == A_INVALID)
  318 		{
  319 			/*
  320 			 * Not part of the number.
  321 			 * Treat as a normal command character.
  322 			 */
  323 			number = cmd_int(&fraction);
  324 			mca = 0;
  325 			cmd_accept();
  326 			return (NO_MCA);
  327 		}
  328 		break;
  329 
  330 	case A_OPT_TOGGLE:
  331 		/*
  332 		 * Special case for the TOGGLE_OPTION command.
  333 		 * If the option letter which was entered is a
  334 		 * single-char option, execute the command immediately,
  335 		 * so user doesn't have to hit RETURN.
  336 		 * If the first char is + or -, this indicates
  337 		 * OPT_UNSET or OPT_SET respectively, instead of OPT_TOGGLE.
  338 		 * "--" begins inputting a long option name.
  339 		 */
  340 		if (optchar == '\0' && len_cmdbuf() == 0)
  341 		{
  342 			flag = (optflag & ~OPT_NO_PROMPT);
  343 			if (flag == OPT_NO_TOGGLE)
  344 			{
  345 				switch (c)
  346 				{
  347 				case '_':
  348 					/* "__" = long option name. */
  349 					optgetname = TRUE;
  350 					mca_opt_toggle();
  351 					return (MCA_MORE);
  352 				}
  353 			} else
  354 			{
  355 				switch (c)
  356 				{
  357 				case '+':
  358 					/* "-+" = UNSET. */
  359 					optflag = (flag == OPT_UNSET) ?
  360 						OPT_TOGGLE : OPT_UNSET;
  361 					mca_opt_toggle();
  362 					return (MCA_MORE);
  363 				case '!':
  364 					/* "-!" = SET */
  365 					optflag = (flag == OPT_SET) ?
  366 						OPT_TOGGLE : OPT_SET;
  367 					mca_opt_toggle();
  368 					return (MCA_MORE);
  369 				case CONTROL('P'):
  370 					optflag ^= OPT_NO_PROMPT;
  371 					mca_opt_toggle();
  372 					return (MCA_MORE);
  373 				case '-':
  374 					/* "--" = long option name. */
  375 					optgetname = TRUE;
  376 					mca_opt_toggle();
  377 					return (MCA_MORE);
  378 				}
  379 			}
  380 		}
  381 		if (optgetname)
  382 		{
  383 			/*
  384 			 * We're getting a long option name.
  385 			 * See if we've matched an option name yet.
  386 			 * If so, display the complete name and stop
  387 			 * accepting chars until user hits RETURN.
  388 			 */
  389 			struct loption *o;
  390 			char *oname;
  391 			int lc;
  392 
  393 			if (c == '\n' || c == '\r')
  394 			{
  395 				/*
  396 				 * When the user hits RETURN, make sure
  397 				 * we've matched an option name, then
  398 				 * pretend he just entered the equivalent
  399 				 * option letter.
  400 				 */
  401 				if (optchar == '\0')
  402 				{
  403 					parg.p_string = get_cmdbuf();
  404 					error("There is no --%s option", &parg);
  405 					return (MCA_DONE);
  406 				}
  407 				optgetname = FALSE;
  408 				cmd_reset();
  409 				c = optchar;
  410 			} else
  411 			{
  412 				if (optchar != '\0')
  413 				{
  414 					/*
  415 					 * Already have a match for the name.
  416 					 * Don't accept anything but erase/kill.
  417 					 */
  418 					if (c == erase_char ||
  419 					    c == erase2_char ||
  420 					    c == kill_char)
  421 						return (MCA_DONE);
  422 					return (MCA_MORE);
  423 				}
  424 				/*
  425 				 * Add char to cmd buffer and try to match
  426 				 * the option name.
  427 				 */
  428 				if (cmd_char(c) == CC_QUIT)
  429 					return (MCA_DONE);
  430 				p = get_cmdbuf();
  431 				lc = ASCII_IS_LOWER(p[0]);
  432 				o = findopt_name(&p, &oname, NULL);
  433 				if (o != NULL)
  434 				{
  435 					/*
  436 					 * Got a match.
  437 					 * Remember the option letter and
  438 					 * display the full option name.
  439 					 */
  440 					optchar = o->oletter;
  441 					if (!lc && ASCII_IS_LOWER(optchar))
  442 						optchar = ASCII_TO_UPPER(optchar);
  443 					cmd_reset();
  444 					mca_opt_toggle();
  445 					for (p = oname;  *p != '\0';  p++)
  446 					{
  447 						c = *p;
  448 						if (!lc && ASCII_IS_LOWER(c))
  449 							c = ASCII_TO_UPPER(c);
  450 						if (cmd_char(c) != CC_OK)
  451 							return (MCA_DONE);
  452 					}
  453 				}
  454 				return (MCA_MORE);
  455 			}
  456 		} else
  457 		{
  458 			if (c == erase_char || c == erase2_char || c == kill_char)
  459 				break;
  460 			if (optchar != '\0')
  461 				/* We already have the option letter. */
  462 				break;
  463 		}
  464 
  465 		optchar = c;
  466 		if ((optflag & ~OPT_NO_PROMPT) != OPT_TOGGLE ||
  467 		    single_char_option(c))
  468 		{
  469 			toggle_option(c, "", optflag);
  470 			return (MCA_DONE);
  471 		}
  472 		/*
  473 		 * Display a prompt appropriate for the option letter.
  474 		 */
  475 		if ((p = opt_prompt(c)) == NULL)
  476 		{
  477 			buf[0] = '-';
  478 			buf[1] = c;
  479 			buf[2] = '\0';
  480 			p = buf;
  481 		}
  482 		start_mca(A_OPT_TOGGLE, p, (void*)NULL, 0);
  483 		return (MCA_MORE);
  484 
  485 	case A_F_SEARCH:
  486 	case A_B_SEARCH:
  487 	case A_FILTER:
  488 		/*
  489 		 * Special case for search commands.
  490 		 * Certain characters as the first char of
  491 		 * the pattern have special meaning:
  492 		 *	!  Toggle the NO_MATCH flag
  493 		 *	*  Toggle the PAST_EOF flag
  494 		 *	@  Toggle the FIRST_FILE flag
  495 		 */
  496 		if (len_cmdbuf() > 0)
  497 			/*
  498 			 * Only works for the first char of the pattern.
  499 			 */
  500 			break;
  501 
  502 		flag = 0;
  503 		switch (c)
  504 		{
  505 		case CONTROL('E'): /* ignore END of file */
  506 		case '*':
  507 			if (mca != A_FILTER)
  508 				flag = SRCH_PAST_EOF;
  509 			break;
  510 		case CONTROL('F'): /* FIRST file */
  511 		case '@':
  512 			if (mca != A_FILTER)
  513 				flag = SRCH_FIRST_FILE;
  514 			break;
  515 		case CONTROL('K'): /* KEEP position */
  516 			if (mca != A_FILTER)
  517 				flag = SRCH_NO_MOVE;
  518 			break;
  519 		case CONTROL('R'): /* Don't use REGULAR EXPRESSIONS */
  520 			flag = SRCH_NO_REGEX;
  521 			break;
  522 		case CONTROL('N'): /* NOT match */
  523 		case '!':
  524 			flag = SRCH_NO_MATCH;
  525 			break;
  526 		}
  527 		if (flag != 0)
  528 		{
  529 			search_type ^= flag;
  530 			mca_search();
  531 			return (MCA_MORE);
  532 		}
  533 		break;
  534 	}
  535 
  536 	/*
  537 	 * Any other multicharacter command
  538 	 * is terminated by a newline.
  539 	 */
  540 	if (c == '\n' || c == '\r')
  541 	{
  542 		/*
  543 		 * Execute the command.
  544 		 */
  545 		exec_mca();
  546 		return (MCA_DONE);
  547 	}
  548 
  549 	/*
  550 	 * Append the char to the command buffer.
  551 	 */
  552 	if (cmd_char(c) == CC_QUIT)
  553 		/*
  554 		 * Abort the multi-char command.
  555 		 */
  556 		return (MCA_DONE);
  557 
  558 	if ((mca == A_F_BRACKET || mca == A_B_BRACKET) && len_cmdbuf() >= 2)
  559 	{
  560 		/*
  561 		 * Special case for the bracket-matching commands.
  562 		 * Execute the command after getting exactly two
  563 		 * characters from the user.
  564 		 */
  565 		exec_mca();
  566 		return (MCA_DONE);
  567 	}
  568 
  569 	/*
  570 	 * Need another character.
  571 	 */
  572 	return (MCA_MORE);
  573 }
  574 
  575 /*
  576  * Discard any buffered file data.
  577  */
  578 	static void
  579 clear_buffers()
  580 {
  581 	if (!(ch_getflags() & CH_CANSEEK))
  582 		return;
  583 	ch_flush();
  584 	clr_linenum();
  585 #if HILITE_SEARCH
  586 	clr_hilite();
  587 #endif
  588 }
  589 
  590 /*
  591  * Make sure the screen is displayed.
  592  */
  593 	static void
  594 make_display()
  595 {
  596 	/*
  597 	 * If nothing is displayed yet, display starting from initial_scrpos.
  598 	 */
  599 	if (empty_screen())
  600 	{
  601 		if (initial_scrpos.pos == NULL_POSITION)
  602 			/*
  603 			 * {{ Maybe this should be:
  604 			 *    jump_loc(ch_zero(), jump_sline);
  605 			 *    but this behavior seems rather unexpected
  606 			 *    on the first screen. }}
  607 			 */
  608 			jump_loc(ch_zero(), 1);
  609 		else
  610 			jump_loc(initial_scrpos.pos, initial_scrpos.ln);
  611 	} else if (screen_trashed)
  612 	{
  613 		int save_top_scroll = top_scroll;
  614 		int save_ignore_eoi = ignore_eoi;
  615 		top_scroll = 1;
  616 		ignore_eoi = 0;
  617 		if (screen_trashed == 2)
  618 		{
  619 			/* Special case used by ignore_eoi: re-open the input file
  620 			 * and jump to the end of the file. */
  621 			reopen_curr_ifile();
  622 			jump_forw();
  623 		}
  624 		repaint();
  625 		top_scroll = save_top_scroll;
  626 		ignore_eoi = save_ignore_eoi;
  627 	}
  628 }
  629 
  630 /*
  631  * Display the appropriate prompt.
  632  */
  633 	static void
  634 prompt()
  635 {
  636 	register char *p;
  637 
  638 	if (ungotp != NULL && ungotp > ungot)
  639 	{
  640 		/*
  641 		 * No prompt necessary if commands are from
  642 		 * ungotten chars rather than from the user.
  643 		 */
  644 		return;
  645 	}
  646 
  647 	/*
  648 	 * Make sure the screen is displayed.
  649 	 */
  650 	make_display();
  651 	bottompos = position(BOTTOM_PLUS_ONE);
  652 
  653 	/*
  654 	 * If we've hit EOF on the last file and the -E flag is set, quit.
  655 	 */
  656 	if (get_quit_at_eof() == OPT_ONPLUS &&
  657 	    eof_displayed() && !(ch_getflags() & CH_HELPFILE) &&
  658 	    next_ifile(curr_ifile) == NULL_IFILE)
  659 		quit(QUIT_OK);
  660 
  661 	/*
  662 	 * If the entire file is displayed and the -F flag is set, quit.
  663 	 */
  664 	if (quit_if_one_screen &&
  665 	    entire_file_displayed() && !(ch_getflags() & CH_HELPFILE) &&
  666 	    next_ifile(curr_ifile) == NULL_IFILE)
  667 		quit(QUIT_OK);
  668 
  669 #if MSDOS_COMPILER==WIN32C
  670 	/*
  671 	 * In Win32, display the file name in the window title.
  672 	 */
  673 	if (!(ch_getflags() & CH_HELPFILE))
  674 		SetConsoleTitle(pr_expand("Less?f - %f.", 0));
  675 #endif
  676 	/*
  677 	 * Select the proper prompt and display it.
  678 	 */
  679 	/*
  680 	 * If the previous action was a forward movement,
  681 	 * don't clear the bottom line of the display;
  682 	 * just print the prompt since the forward movement guarantees
  683 	 * that we're in the right position to display the prompt.
  684 	 * Clearing the line could cause a problem: for example, if the last
  685 	 * line displayed ended at the right screen edge without a newline,
  686 	 * then clearing would clear the last displayed line rather than
  687 	 * the prompt line.
  688 	 */
  689 	if (!forw_prompt)
  690 		clear_bot();
  691 	clear_cmd();
  692 	forw_prompt = 0;
  693 	p = pr_string();
  694 	if (is_filtering())
  695 		putstr("& ");
  696 	if (p == NULL || *p == '\0')
  697 		putchr(':');
  698 	else
  699 	{
  700 		at_enter(AT_STANDOUT);
  701 		putstr(p);
  702 		at_exit();
  703 	}
  704 	clear_eol();
  705 }
  706 
  707 /*
  708  * Display the less version message.
  709  */
  710 	public void
  711 dispversion()
  712 {
  713 	PARG parg;
  714 
  715 	parg.p_string = version;
  716 	error("less %s", &parg);
  717 }
  718 
  719 /*
  720  * Get command character.
  721  * The character normally comes from the keyboard,
  722  * but may come from ungotten characters
  723  * (characters previously given to ungetcc or ungetsc).
  724  */
  725 	public int
  726 getcc()
  727 {
  728 	if (ungotp == NULL)
  729 		/*
  730 		 * Normal case: no ungotten chars, so get one from the user.
  731 		 */
  732 		return (getchr());
  733 
  734 	if (ungotp > ungot)
  735 		/*
  736 		 * Return the next ungotten char.
  737 		 */
  738 		return (*--ungotp);
  739 
  740 	/*
  741 	 * We have just run out of ungotten chars.
  742 	 */
  743 	ungotp = NULL;
  744 	if (len_cmdbuf() == 0 || !empty_screen())
  745 		return (getchr());
  746 	/*
  747 	 * Command is incomplete, so try to complete it.
  748 	 */
  749 	switch (mca)
  750 	{
  751 	case A_DIGIT:
  752 		/*
  753 		 * We have a number but no command.  Treat as #g.
  754 		 */
  755 		return ('g');
  756 
  757 	case A_F_SEARCH:
  758 	case A_B_SEARCH:
  759 		/*
  760 		 * We have "/string" but no newline.  Add the \n.
  761 		 */
  762 		return ('\n');
  763 
  764 	default:
  765 		/*
  766 		 * Some other incomplete command.  Let user complete it.
  767 		 */
  768 		return (getchr());
  769 	}
  770 }
  771 
  772 /*
  773  * "Unget" a command character.
  774  * The next getcc() will return this character.
  775  */
  776 	public void
  777 ungetcc(c)
  778 	int c;
  779 {
  780 	if (ungotp == NULL)
  781 		ungotp = ungot;
  782 	if (ungotp >= ungot + sizeof(ungot))
  783 	{
  784 		error("ungetcc overflow", NULL_PARG);
  785 		quit(QUIT_ERROR);
  786 	}
  787 	*ungotp++ = c;
  788 }
  789 
  790 /*
  791  * Unget a whole string of command characters.
  792  * The next sequence of getcc()'s will return this string.
  793  */
  794 	public void
  795 ungetsc(s)
  796 	char *s;
  797 {
  798 	register char *p;
  799 
  800 	for (p = s + strlen(s) - 1;  p >= s;  p--)
  801 		ungetcc(*p);
  802 }
  803 
  804 /*
  805  * Search for a pattern, possibly in multiple files.
  806  * If SRCH_FIRST_FILE is set, begin searching at the first file.
  807  * If SRCH_PAST_EOF is set, continue the search thru multiple files.
  808  */
  809 	static void
  810 multi_search(pattern, n)
  811 	char *pattern;
  812 	int n;
  813 {
  814 	register int nomore;
  815 	IFILE save_ifile;
  816 	int changed_file;
  817 
  818 	changed_file = 0;
  819 	save_ifile = save_curr_ifile();
  820 
  821 	if (search_type & SRCH_FIRST_FILE)
  822 	{
  823 		/*
  824 		 * Start at the first (or last) file
  825 		 * in the command line list.
  826 		 */
  827 		if (search_type & SRCH_FORW)
  828 			nomore = edit_first();
  829 		else
  830 			nomore = edit_last();
  831 		if (nomore)
  832 		{
  833 			unsave_ifile(save_ifile);
  834 			return;
  835 		}
  836 		changed_file = 1;
  837 		search_type &= ~SRCH_FIRST_FILE;
  838 	}
  839 
  840 	for (;;)
  841 	{
  842 		n = search(search_type, pattern, n);
  843 		/*
  844 		 * The SRCH_NO_MOVE flag doesn't "stick": it gets cleared
  845 		 * after being used once.  This allows "n" to work after
  846 		 * using a /@@ search.
  847 		 */
  848 		search_type &= ~SRCH_NO_MOVE;
  849 		if (n == 0)
  850 		{
  851 			/*
  852 			 * Found it.
  853 			 */
  854 			unsave_ifile(save_ifile);
  855 			return;
  856 		}
  857 
  858 		if (n < 0)
  859 			/*
  860 			 * Some kind of error in the search.
  861 			 * Error message has been printed by search().
  862 			 */
  863 			break;
  864 
  865 		if ((search_type & SRCH_PAST_EOF) == 0)
  866 			/*
  867 			 * We didn't find a match, but we're
  868 			 * supposed to search only one file.
  869 			 */
  870 			break;
  871 		/*
  872 		 * Move on to the next file.
  873 		 */
  874 		if (search_type & SRCH_FORW)
  875 			nomore = edit_next(1);
  876 		else
  877 			nomore = edit_prev(1);
  878 		if (nomore)
  879 			break;
  880 		changed_file = 1;
  881 	}
  882 
  883 	/*
  884 	 * Didn't find it.
  885 	 * Print an error message if we haven't already.
  886 	 */
  887 	if (n > 0)
  888 		error("Pattern not found", NULL_PARG);
  889 
  890 	if (changed_file)
  891 	{
  892 		/*
  893 		 * Restore the file we were originally viewing.
  894 		 */
  895 		reedit_ifile(save_ifile);
  896 	} else
  897 	{
  898 		unsave_ifile(save_ifile);
  899 	}
  900 }
  901 
  902 /*
  903  * Main command processor.
  904  * Accept and execute commands until a quit command.
  905  */
  906 	public void
  907 commands()
  908 {
  909 	register int c;
  910 	register int action;
  911 	register char *cbuf;
  912 	int newaction;
  913 	int save_search_type;
  914 	char *extra;
  915 	char tbuf[2];
  916 	PARG parg;
  917 	IFILE old_ifile;
  918 	IFILE new_ifile;
  919 	char *tagfile;
  920 
  921 	search_type = SRCH_FORW;
  922 	wscroll = (sc_height + 1) / 2;
  923 	newaction = A_NOACTION;
  924 
  925 	for (;;)
  926 	{
  927 		mca = 0;
  928 		cmd_accept();
  929 		number = 0;
  930 		optchar = '\0';
  931 
  932 		/*
  933 		 * See if any signals need processing.
  934 		 */
  935 		if (sigs)
  936 		{
  937 			psignals();
  938 			if (quitting)
  939 				quit(QUIT_SAVED_STATUS);
  940 		}
  941 
  942 		/*
  943 		 * See if window size changed, for systems that don't
  944 		 * generate SIGWINCH.
  945 		 */
  946 		check_winch();
  947 
  948 		/*
  949 		 * Display prompt and accept a character.
  950 		 */
  951 		cmd_reset();
  952 		prompt();
  953 		if (sigs)
  954 			continue;
  955 		if (newaction == A_NOACTION)
  956 			c = getcc();
  957 
  958 	again:
  959 		if (sigs)
  960 			continue;
  961 
  962 		if (newaction != A_NOACTION)
  963 		{
  964 			action = newaction;
  965 			newaction = A_NOACTION;
  966 		} else
  967 		{
  968 			/*
  969 			 * If we are in a multicharacter command, call mca_char.
  970 			 * Otherwise we call fcmd_decode to determine the
  971 			 * action to be performed.
  972 			 */
  973 			if (mca)
  974 				switch (mca_char(c))
  975 				{
  976 				case MCA_MORE:
  977 					/*
  978 					 * Need another character.
  979 					 */
  980 					c = getcc();
  981 					goto again;
  982 				case MCA_DONE:
  983 					/*
  984 					 * Command has been handled by mca_char.
  985 					 * Start clean with a prompt.
  986 					 */
  987 					continue;
  988 				case NO_MCA:
  989 					/*
  990 					 * Not a multi-char command
  991 					 * (at least, not anymore).
  992 					 */
  993 					break;
  994 				}
  995 
  996 			/*
  997 			 * Decode the command character and decide what to do.
  998 			 */
  999 			if (mca)
 1000 			{
 1001 				/*
 1002 				 * We're in a multichar command.
 1003 				 * Add the character to the command buffer
 1004 				 * and display it on the screen.
 1005 				 * If the user backspaces past the start
 1006 				 * of the line, abort the command.
 1007 				 */
 1008 				if (cmd_char(c) == CC_QUIT || len_cmdbuf() == 0)
 1009 					continue;
 1010 				cbuf = get_cmdbuf();
 1011 			} else
 1012 			{
 1013 				/*
 1014 				 * Don't use cmd_char if we're starting fresh
 1015 				 * at the beginning of a command, because we
 1016 				 * don't want to echo the command until we know
 1017 				 * it is a multichar command.  We also don't
 1018 				 * want erase_char/kill_char to be treated
 1019 				 * as line editing characters.
 1020 				 */
 1021 				tbuf[0] = c;
 1022 				tbuf[1] = '\0';
 1023 				cbuf = tbuf;
 1024 			}
 1025 			extra = NULL;
 1026 			action = fcmd_decode(cbuf, &extra);
 1027 			/*
 1028 			 * If an "extra" string was returned,
 1029 			 * process it as a string of command characters.
 1030 			 */
 1031 			if (extra != NULL)
 1032 				ungetsc(extra);
 1033 		}
 1034 		/*
 1035 		 * Clear the cmdbuf string.
 1036 		 * (But not if we're in the prefix of a command,
 1037 		 * because the partial command string is kept there.)
 1038 		 */
 1039 		if (action != A_PREFIX)
 1040 			cmd_reset();
 1041 
 1042 		switch (action)
 1043 		{
 1044 		case A_DIGIT:
 1045 			/*
 1046 			 * First digit of a number.
 1047 			 */
 1048 			start_mca(A_DIGIT, ":", (void*)NULL, CF_QUIT_ON_ERASE);
 1049 			goto again;
 1050 
 1051 		case A_F_WINDOW:
 1052 			/*
 1053 			 * Forward one window (and set the window size).
 1054 			 */
 1055 			if (number > 0)
 1056 				swindow = (int) number;
 1057 			/* FALLTHRU */
 1058 		case A_F_SCREEN:
 1059 			/*
 1060 			 * Forward one screen.
 1061 			 */
 1062 			if (number <= 0)
 1063 				number = get_swindow();
 1064 			cmd_exec();
 1065 			if (show_attn)
 1066 				set_attnpos(bottompos);
 1067 			forward((int) number, 0, 1);
 1068 			break;
 1069 
 1070 		case A_B_WINDOW:
 1071 			/*
 1072 			 * Backward one window (and set the window size).
 1073 			 */
 1074 			if (number > 0)
 1075 				swindow = (int) number;
 1076 			/* FALLTHRU */
 1077 		case A_B_SCREEN:
 1078 			/*
 1079 			 * Backward one screen.
 1080 			 */
 1081 			if (number <= 0)
 1082 				number = get_swindow();
 1083 			cmd_exec();
 1084 			backward((int) number, 0, 1);
 1085 			break;
 1086 
 1087 		case A_F_LINE:
 1088 			/*
 1089 			 * Forward N (default 1) line.
 1090 			 */
 1091 			if (number <= 0)
 1092 				number = 1;
 1093 			cmd_exec();
 1094 			if (show_attn == OPT_ONPLUS && number > 1)
 1095 				set_attnpos(bottompos);
 1096 			forward((int) number, 0, 0);
 1097 			break;
 1098 
 1099 		case A_B_LINE:
 1100 			/*
 1101 			 * Backward N (default 1) line.
 1102 			 */
 1103 			if (number <= 0)
 1104 				number = 1;
 1105 			cmd_exec();
 1106 			backward((int) number, 0, 0);
 1107 			break;
 1108 
 1109 		case A_FF_LINE:
 1110 			/*
 1111 			 * Force forward N (default 1) line.
 1112 			 */
 1113 			if (number <= 0)
 1114 				number = 1;
 1115 			cmd_exec();
 1116 			if (show_attn == OPT_ONPLUS && number > 1)
 1117 				set_attnpos(bottompos);
 1118 			forward((int) number, 1, 0);
 1119 			break;
 1120 
 1121 		case A_BF_LINE:
 1122 			/*
 1123 			 * Force backward N (default 1) line.
 1124 			 */
 1125 			if (number <= 0)
 1126 				number = 1;
 1127 			cmd_exec();
 1128 			backward((int) number, 1, 0);
 1129 			break;
 1130 
 1131 		case A_FF_SCREEN:
 1132 			/*
 1133 			 * Force forward one screen.
 1134 			 */
 1135 			if (number <= 0)
 1136 				number = get_swindow();
 1137 			cmd_exec();
 1138 			if (show_attn == OPT_ONPLUS)
 1139 				set_attnpos(bottompos);
 1140 			forward((int) number, 1