OpenSolaris_b135/cmd/vi/port/ex_cmds.c

Compare this file to the similar file:
Show the results in this format:

/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License (the "License").
 * You may not use this file except in compliance with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */
/*
 * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

/*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
/*	  All Rights Reserved  	*/


/* Copyright (c) 1981 Regents of the University of California */

#pragma ident	"%Z%%M%	%I%	%E% SMI"

#include "ex.h"
#include "ex_argv.h"
#include "ex_temp.h"
#include "ex_tty.h"
#include "ex_vis.h"

bool	pflag, nflag;
int	poffset;

#define	nochng()	lchng = chng


/*
 * Main loop for command mode command decoding.
 * A few commands are executed here, but main function
 * is to strip command addresses, do a little address oriented
 * processing and call command routines to do the real work.
 */
extern unsigned char *Version;
void
commands(noprompt, exitoneof)
	bool noprompt, exitoneof;
{
	line *addr;
	int c;
	int lchng;
	int given;
	int seensemi;
	int cnt;
	bool hadpr;
	bool gotfile;
#ifdef XPG4
	int d;
#endif /* XPG4 */
	unsigned char *vgetpass();

	resetflav();
	nochng();
	for (;;) {
		if (!firstpat)
			laste = 0;
		/*
		 * If dot at last command
		 * ended up at zero, advance to one if there is a such.
		 */
		if (dot <= zero) {
			dot = zero;
			if (dol > zero)
				dot = one;
		}
		shudclob = 0;

		/*
		 * If autoprint or trailing print flags,
		 * print the line at the specified offset
		 * before the next command.
		 */
		if ((pflag || lchng != chng && value(vi_AUTOPRINT) &&
		    !inglobal && !inopen && endline) || poffset != 0) {
			pflag = 0;
			nochng();
			if (dol != zero) {
				addr1 = addr2 = dot + poffset;
				poffset = 0;
				if (addr1 < one || addr1 > dol)
					error(value(vi_TERSE) ?
					    gettext("Offset out-of-bounds") :
					    gettext("Offset after command "
						"too large"));
				dot = addr1;
				setdot1();

				goto print;
			}
		}
		nochng();

		/*
		 * Print prompt if appropriate.
		 * If not in global flush output first to prevent
		 * going into pfast mode unreasonably.
		 */
		if (inglobal == 0) {
			flush();
			if (!hush && value(vi_PROMPT) && !globp &&
			    !noprompt && endline) {
				putchar(':');
				hadpr = 1;
			}
			TSYNC();
		}

		/*
		 * Gobble up the address.
		 * Degenerate addresses yield ".".
		 */
		addr2 = 0;
		given = seensemi = 0;
		do {
			addr1 = addr2;
			addr = address(0);
			c = getcd();
			if (addr == 0) {
				if (c == ',' || c == ';')
					addr = dot;
				else if (addr1 != 0) {
					addr2 = dot;
					break;
				} else
					break;
			}
			addr2 = addr;
			given++;
			if (c == ';') {
				c = ',';
				dot = addr;
				seensemi = 1;
			}
		} while (c == ',');

		if (c == '%') {
			/* %: same as 1,$ */
			addr1 = one;
			addr2 = dol;
			given = 2;
			c = getchar();
		}
		if (addr1 == 0)
			addr1 = addr2;

		/*
		 * eat multiple colons
		 */
		while (c == ':')
			c = getchar();
		/*
		 * Set command name for special character commands.
		 */
		tailspec(c);

		/*
		 * If called via : escape from open or visual, limit
		 * the set of available commands here to save work below.
		 */
		if (inopen) {
			if (c == '\n' || c == '\r' ||
			    c == CTRL('d') || c == EOF) {
				if (addr2)
					dot = addr2;
				if (c == EOF)
					return;
				continue;
			}
			if (any(c, "o"))
notinvis:
				tailprim(Command, 1, 1);
		}
		switch (c) {

		case 'a':

			switch (peekchar()) {
			case 'b':
/* abbreviate */
				tail("abbreviate");
				setnoaddr();
				mapcmd(0, 1);
				anyabbrs = 1;
				continue;
			case 'r':
/* args */
				tail("args");
				setnoaddr();
				eol();
				pargs();
				continue;
			}

/* append */
			if (inopen)
				goto notinvis;
			tail("append");
			setdot();
			aiflag = exclam();
			donewline();
			vmacchng(0);
			deletenone();
			setin(addr2);
			inappend = 1;
			(void) append(gettty, addr2);
			inappend = 0;
			nochng();
			continue;

		case 'c':
			switch (peekchar()) {

/* copy */
			case 'o':
				tail("copy");
				vmacchng(0);
				vi_move();
				continue;

/* crypt */
			case 'r':
				tail("crypt");
				crflag = -1;
			ent_crypt:
				setnoaddr();
				xflag = 1;
				if (permflag)
					(void) crypt_close(perm);
				permflag = 1;
				if ((kflag = run_setkey(perm,
				    (key = vgetpass(
					gettext("Enter key:"))))) == -1) {
					xflag = 0;
					kflag = 0;
					crflag = 0;
					smerror(gettext("Encryption facility "
						    "not available\n"));
				}
				if (kflag == 0)
					crflag = 0;
				continue;

/* cd */
			case 'd':
				tail("cd");
				goto changdir;

/* chdir */
			case 'h':
				ignchar();
				if (peekchar() == 'd') {
					unsigned char *p;
					tail2of("chdir");
changdir:
					if (savedfile[0] == '/' ||
					    !value(vi_WARN))
						(void) exclam();
					else
						(void) quickly();
					if (skipend()) {
						p = (unsigned char *)
						    getenv("HOME");
						if (p == NULL)
							error(gettext(
								"Home directory"
								    /*CSTYLED*/
								    " unknown"));
					} else
						getone(), p = file;
					eol();
					if (chdir((char *)p) < 0)
						filioerr(p);
					if (savedfile[0] != '/')
						edited = 0;
					continue;
				}
				if (inopen)
					tailprim((unsigned char *)"change",
					    2, 1);
				tail2of("change");
				break;

			default:
				if (inopen)
					goto notinvis;
				tail("change");
				break;
			}
/* change */
			aiflag = exclam();
#ifdef XPG4ONLY
			setcount2();
			donewline();
#else /* XPG6 and Solaris */
			setCNL();
#endif /* XPG4ONLY */
			vmacchng(0);
			setin(addr1);
			(void) delete(0);
			inappend = 1;
			if (append(gettty, addr1 - 1) == 0) {
#ifdef XPG4
				/*
				 * P2003.2/D9:5.10.7.2.4, p. 646,
				 * assertion 214(A). If nothing changed,
				 * set dot to the line preceding the lines
				 * to be changed.
				 */
				dot = addr1 - 1;
#else /* XPG4 */
				dot = addr1;
#endif /* XPG4 */
				if (dot > dol)
					dot = dol;
			}
			inappend = 0;
			nochng();
			continue;

/* delete */
		case 'd':
			/*
			 * Caution: dp and dl have special meaning already.
			 */
			tail("delete");
			c = cmdreg();
#ifdef XPG4ONLY
			setcount2();
			donewline();
#else /* XPG6 and Solaris */
			setCNL();
#endif /* XPG4ONLY */
			vmacchng(0);
			if (c)
				(void) YANKreg(c);
			(void) delete(0);
			appendnone();
			continue;

/* edit */
/* ex */
		case 'e':
			if (crflag == 2 || crflag == -2)
				crflag = -1;
			tail(peekchar() == 'x' ? "ex" : "edit");
editcmd:
			if (!exclam() && chng)
				c = 'E';
			gotfile = 0;
			if (c == 'E') {
				if (inopen && !value(vi_AUTOWRITE)) {
					filename(c);
					gotfile = 1;
				}
				ungetchar(lastchar());
				if (!exclam()) {
					ckaw();
					if (chng && dol > zero) {
						xchng = 0;
						error(value(vi_TERSE) ?
						    gettext("No write") :
						    gettext("No write since "
							"last change (:%s! "
							"overrides)"),
						    Command);
					}
				}

			}
			if (gotfile == 0)
				filename(c);
			setnoaddr();
doecmd:
			init();
			addr2 = zero;
			laste++;
			sync();
			rop(c);
			nochng();
			continue;

/* file */
		case 'f':
			tail("file");
			setnoaddr();
			filename(c);
			noonl();
/*
 *			synctmp();
 */
			continue;

/* global */
		case 'g':
			tail("global");
			global(!exclam());
			nochng();
			continue;

/* insert */
		case 'i':
			if (inopen)
				goto notinvis;
			tail("insert");
			setdot();
			nonzero();
			aiflag = exclam();
			donewline();
			vmacchng(0);
			deletenone();
			setin(addr2);
			inappend = 1;
			(void) append(gettty, addr2 - 1);
			inappend = 0;
			if (dot == zero && dol > zero)
				dot = one;
			nochng();
			continue;

/* join */
		case 'j':
			tail("join");
			c = exclam();
			setcount();
			nonzero();
			donewline();
			vmacchng(0);
#ifdef XPG4ONLY
			/*
			 * if no count was specified, addr1 == addr2. if only
			 * 1 range arg was specified, inc addr2 to allow
			 * joining of the next line.
			 */
			if (given < 2 && (addr1 == addr2) && (addr2 != dol))
				addr2++;

#else /* XPG6 and Solaris */
			if (given < 2 && addr2 != dol)
				addr2++;
#endif /* XPG4ONLY */
			(void) join(c);
			continue;

/* k */
		case 'k':
casek:
			pastwh();
			c = getchar();
			if (endcmd(c))
				serror((vi_TERSE) ?
				    (unsigned char *)gettext("Mark what?") :
				    (unsigned char *)
				    gettext("%s requires following "
				    "letter"), Command);
			donewline();
			if (!islower(c))
				error((vi_TERSE) ? gettext("Bad mark") :
					gettext("Mark must specify a letter"));
			setdot();
			nonzero();
			names[c - 'a'] = *addr2 &~ 01;
			anymarks = 1;
			continue;

/* list */
		case 'l':
			tail("list");
#ifdef XPG4ONLY
			setcount2();
			donewline();
#else /* XPG6 and Solaris */
			setCNL();
#endif /* XPG4ONLY */
			(void) setlist(1);
			pflag = 0;
			goto print;

		case 'm':
			if (peekchar() == 'a') {
				ignchar();
				if (peekchar() == 'p') {
/* map */
					tail2of("map");
					setnoaddr();
					mapcmd(0, 0);
					continue;
				}
/* mark */
				tail2of("mark");
				goto casek;
			}
/* move */
			tail("move");
			vmacchng(0);
			vi_move();
			continue;

		case 'n':
			if (peekchar() == 'u') {
				tail("number");
				goto numberit;
			}
/* next */
			tail("next");
			setnoaddr();
			if (!exclam()) {
				ckaw();
				if (chng && dol > zero) {
					xchng = 0;
					error(value(vi_TERSE) ?
					    gettext("No write") :
					    gettext("No write since last "
						"change (:%s! overrides)"),
					    Command);
				}
			}

			if (getargs())
				makargs();
			next();
			c = 'e';
			filename(c);
			goto doecmd;

/* open */
		case 'o':
			tail("open");
			oop();
			pflag = 0;
			nochng();
			continue;

		case 'p':
		case 'P':
			switch (peekchar()) {
#ifdef TAG_STACK
/* pop */
			case 'o':
				tail("pop");
				poptag(exclam());
				if (!inopen)
					lchng = chng - 1;
				else
					nochng();
				continue;
#endif

/* put */
			case 'u':
				tail("put");
				setdot();
				c = cmdreg();
				eol();
				vmacchng(0);
				if (c)
					(void) putreg(c);
				else
					(void) put();
				continue;

			case 'r':
				ignchar();
				if (peekchar() == 'e') {
/* preserve */
					tail2of("preserve");
					eol();
					if (preserve() == 0)
						error(gettext(
						    "Preserve failed!"));
					else {
#ifdef XPG4
						/*
						 * error() incs errcnt. this is
						 * misleading here; and a
						 * violation of POSIX. so call
						 * noerror() instead.
						 * this is for assertion ex:222.
						 */
						noerror(
						    gettext("File preserved."));

#else /* XPG4 */
						error(
						    gettext("File preserved."));
#endif /* XPG4 */
					}
				}
				tail2of("print");
				break;

			default:
				tail("print");
				break;
			}
/* print */
			setCNL();
			pflag = 0;
print:
			nonzero();
			if (clear_screen && span() > lines) {
				flush1();
				vclear();
			}
			/*
			 * poffset is nonzero if trailing + or - flags
			 * were given, and in that case we need to
			 * adjust dot before printing a line.
			 */
			if (poffset == 0)
				plines(addr1, addr2, 1);
			else
				dot = addr2;
			continue;

/* quit */
		case 'q':
			tail("quit");
			setnoaddr();
			c = quickly();
			eol();
			if (!c)
quit:
				if (nomore())
					continue;
			if (inopen) {
				vgoto(WECHO, 0);
				if (!ateopr())
					vnfl();
				else {
					tostop();
				}
				flush();
				setty(normf);
				ixlatctl(1);
			}
			cleanup(1);
			exit(errcnt);

		case 'r':
			if (peekchar() == 'e') {
				ignchar();
				switch (peekchar()) {

/* rewind */
				case 'w':
					tail2of("rewind");
					setnoaddr();
					if (!exclam()) {
						ckaw();
						if (chng && dol > zero)
							error((vi_TERSE) ?
							    /*CSTYLED*/
							    gettext("No write") :
							    gettext("No write "
								"since last "
								"change (:rewi"
								/*CSTYLED*/
								"nd! overrides)"));
					}
					eol();
					erewind();
					next();
					c = 'e';
					ungetchar(lastchar());
					filename(c);
					goto doecmd;

/* recover */
				case 'c':
					tail2of("recover");
					setnoaddr();
					c = 'e';
					if (!exclam() && chng)
						c = 'E';
					filename(c);
					if (c == 'E') {
						ungetchar(lastchar());
						(void) quickly();
					}
					init();
					addr2 = zero;
					laste++;
					sync();
					recover();
					rop2();
					revocer();
					if (status == 0)
						rop3(c);
					if (dol != zero)
						change();
					nochng();
					continue;
				}
				tail2of("read");
			} else
				tail("read");
/* read */
			if (crflag == 2 || crflag == -2)
			/* restore crflag for new input text */
				crflag = -1;
			if (savedfile[0] == 0 && dol == zero)
				c = 'e';
			pastwh();
			vmacchng(0);
			if (peekchar() == '!') {
				setdot();
				ignchar();
				unix0(0, 1);
				(void) vi_filter(0);
				continue;
			}
			filename(c);
			rop(c);
			nochng();
			if (inopen && endline && addr1 > zero && addr1 < dol)
				dot = addr1 + 1;
			continue;

		case 's':
			switch (peekchar()) {
			/*
			 * Caution: 2nd char cannot be c, g, or r
			 * because these have meaning to substitute.
			 */

/* set */
			case 'e':
				tail("set");
				setnoaddr();
				set();
				continue;

/* shell */
			case 'h':
				tail("shell");
				setNAEOL();
				vnfl();
				putpad((unsigned char *)exit_ca_mode);
				flush();
				resetterm();
				unixwt(1, unixex("-i", (char *)0, 0, 0));
				vcontin(0);
				continue;

/* source */
			case 'o':
#ifdef notdef
				if (inopen)
					goto notinvis;
#endif
				tail("source");
				setnoaddr();
				getone();
				eol();
				source(file, 0);
				continue;
#ifdef SIGTSTP
/* stop, suspend */
			case 't':
				tail("stop");
				goto suspend;
			case 'u':
#ifdef XPG4
				/*
				 * for POSIX, "su" with no other distinguishing
				 * characteristics, maps to "s". Re. P1003.D11,
				 * 5.10.7.3.
				 *
				 * so, unless the "su" is followed by a "s" or
				 * a "!", we assume that the user means "s".
				 */
				switch (d = peekchar()) {
				case 's':
				case '!':
#endif /* XPG4 */
					tail("suspend");
suspend:
					c = exclam();
					eol();
					if (!c)
						ckaw();
					onsusp(0);
					continue;
#ifdef XPG4
				}
#endif /* XPG4 */
#endif

			}
			/* fall into ... */

/* & */
/* ~ */
/* substitute */
		case '&':
		case '~':
			Command = (unsigned char *)"substitute";
			if (c == 's')
				tail(Command);
			vmacchng(0);
			if (!substitute(c))
				pflag = 0;
			continue;

/* t */
		case 't':
			if (peekchar() == 'a') {
				tagflg = 1; /* :tag command */
				tail("tag");
				tagfind(exclam());
				tagflg = 0;
				if (!inopen)
					lchng = chng - 1;
				else
					nochng();
				continue;
			}
			tail("t");
			vmacchng(0);
			vi_move();
			continue;

		case 'u':
			if (peekchar() == 'n') {
				ignchar();
				switch (peekchar()) {
/* unmap */
				case 'm':
					tail2of("unmap");
					setnoaddr();
					mapcmd(1, 0);
					continue;
/* unabbreviate */
				case 'a':
					tail2of("unabbreviate");
					setnoaddr();
					mapcmd(1, 1);
					anyabbrs = 1;
					continue;
				}
/* undo */
				tail2of("undo");
			} else
				tail("undo");
			setnoaddr();
			markDOT();
			c = exclam();
			donewline();
			undo(c);
			continue;

		case 'v':
			switch (peekchar()) {

			case 'e':
/* version */
				tail("version");
				setNAEOL();
				viprintf("%s", Version);
				noonl();
				continue;

/* visual */
			case 'i':
				tail("visual");
				if (inopen) {
					c = 'e';
					goto editcmd;
				}
				vop();
				pflag = 0;
				nochng();
				continue;
			}
/* v */
			tail("v");
			global(0);
			nochng();
			continue;

/* write */
		case 'w':
			c = peekchar();
			tail(c == 'q' ? "wq" : "write");
wq:
			if (skipwh() && peekchar() == '!') {
				pofix();
				ignchar();
				setall();
				unix0(0, 1);
				(void) vi_filter(1);
			} else {
				setall();
				if (c == 'q')
					write_quit = 1;
				else
					write_quit = 0;
				wop(1);
				nochng();
			}
			if (c == 'q')
				goto quit;
			continue;
/* X: crypt */
		case 'X':
			crflag = -1; /* determine if file is encrypted */
			goto ent_crypt;

		case 'C':
			crflag = 1;  /* assume files read in are encrypted */
			goto ent_crypt;

/* xit */
		case 'x':
			tail("xit");
			if (!chng)
				goto quit;
			c = 'q';
			goto wq;

/* yank */
		case 'y':
			tail("yank");
			c = cmdreg();
#ifdef XPG4ONLY
			setcount2();
#else /* XPG6 and Solaris */
			setcount();
#endif /* XPG4ONLY */
			eol();
			vmacchng(0);
			if (c)
				(void) YANKreg(c);
			else
				(void) yank();
			continue;

/* z */
		case 'z':
			zop(0);
			pflag = 0;
			continue;

/* * */
/* @ */
		case '*':
		case '@':
			c = getchar();
			if (c == '\n' || c == '\r')
				ungetchar(c);
			if (any(c, "@*\n\r"))
				c = lastmac;
			if (isupper(c))
				c = tolower(c);
			if (!islower(c))
				error(gettext("Bad register"));
			donewline();
			setdot();
			cmdmac(c);
			continue;

/* | */
		case '|':
			endline = 0;
			goto caseline;

/* \n */
		case '\n':
			endline = 1;
caseline:
			notempty();
			if (addr2 == 0) {
				if (cursor_up != NOSTR && c == '\n' &&
				    !inglobal)
					c = CTRL('k');
				if (inglobal)
					addr1 = addr2 = dot;
				else {
					if (dot == dol)
						error((vi_TERSE) ?
						    gettext("At EOF") :
						    gettext("At end-of-file"));
					addr1 = addr2 = dot + 1;
				}
			}
			setdot();
			nonzero();
			if (seensemi)
				addr1 = addr2;
			getline(*addr1);
			if (c == CTRL('k')) {
				flush1();
				destline--;
				if (hadpr)
					shudclob = 1;
			}
			plines(addr1, addr2, 1);
			continue;

/* " */
		case '"':
			comment();
			continue;

/* # */
		case '#':
numberit:
			setCNL();
			(void) setnumb(1);
			pflag = 0;
			goto print;

/* = */
		case '=':
			donewline();
			setall();
			if (inglobal == 2)
				pofix();
			viprintf("%d", lineno(addr2));
			noonl();
			continue;

/* ! */
		case '!':
			if (addr2 != 0) {
				vmacchng(0);
				unix0(0, 1);
				setdot();
				(void) vi_filter(2);
			} else {
				unix0(1, 1);
				pofix();
				putpad((unsigned char *)exit_ca_mode);
				flush();
				resetterm();
				if (!tagflg) {
					unixwt(1, unixex("-c", uxb, 0, 0));
				} else {
					error(gettext("Invalid tags file:"
					    " contains shell escape"));
				}
				vclrech(1);	/* vcontin(0); */
				nochng();
			}
			continue;

/* < */
/* > */
		case '<':
		case '>':
			for (cnt = 1; peekchar() == c; cnt++)
				ignchar();
			setCNL();
			vmacchng(0);
			shift(c, cnt);
			continue;

/* ^D */
/* EOF */
		case CTRL('d'):
		case EOF:
			if (exitoneof) {
				if (addr2 != 0)
					dot = addr2;
				return;
			}
			if (!isatty(0)) {
				if (intty)
					/*
					 * Chtty sys call at UCB may cause a
					 * input which was a tty to suddenly be
					 * turned into /dev/null.
					 */
					onhup(0);
				return;
			}
			if (addr2 != 0) {
				setlastchar('\n');
				putnl();
			}
			if (dol == zero) {
				if (addr2 == 0)
					putnl();
				notempty();
			}
			ungetchar(EOF);
			zop(hadpr);
			continue;
		default:
			if (!isalpha(c) || !isascii(c))
				break;
			ungetchar(c);
			tailprim((unsigned char *)"", 0, 0);
		}
		ungetchar(c);
		{
			int length;
			char multic[MULTI_BYTE_MAX];
			wchar_t wchar;
			length = _mbftowc(multic, &wchar, getchar, &peekc);
			if (length < 0)
				length = -length;
			multic[length] = '\0';
			error((vi_TERSE) ? gettext("What?") :
				gettext("Unknown command character '%s'"),
			    multic);
		}
	}
}