/*
* 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 (c) 1989, 2010, Oracle and/or its affiliates. All rights reserved.
*/
/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
/* All Rights Reserved */
/* Copyright (c) 1981 Regents of the University of California */
#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;
getaline(*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);
}
}
}