BSD 4_3_Reno release
[unix-history] / usr / src / usr.bin / ex / ex_cmds.c
/*
* Copyright (c) 1980 Regents of the University of California.
* All rights reserved. The Berkeley software License Agreement
* specifies the terms and conditions for redistribution.
*/
#ifndef lint
static char *sccsid = "@(#)ex_cmds.c 7.13 (Berkeley) 6/29/90";
#endif not lint
#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.
*/
commands(noprompt, exitoneof)
bool noprompt, exitoneof;
{
register line *addr;
register int c;
register int lchng;
int given;
int seensemi;
int cnt;
bool hadpr;
resetflav();
nochng();
for (;;) {
/*
* 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(AUTOPRINT) && !inglobal && !inopen && endline) {
pflag = 0;
nochng();
if (dol != zero) {
addr1 = addr2 = dot + poffset;
if (addr1 < one || addr1 > dol)
error("Offset out-of-bounds|Offset after command too large");
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(PROMPT) && !globp && !noprompt && endline) {
ex_putchar(':');
hadpr = 1;
}
TSYNC();
}
/*
* Gobble up the address.
* Degenerate addresses yield ".".
*/
addr2 = 0;
given = seensemi = 0;
do {
addr1 = addr2;
addr = address((char *) 0);
c = getcd();
if (addr == 0)
if (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 = ex_getchar();
}
if (addr1 == 0)
addr1 = addr2;
if (c == ':')
c = ex_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();
newline();
vmacchng(0);
deletenone();
setin(addr2);
inappend = 1;
ignore(append(gettty, addr2));
inappend = 0;
nochng();
continue;
case 'c':
switch (peekchar()) {
/* copy */
case 'o':
tail("copy");
vmacchng(0);
move();
continue;
#ifdef CHDIR
/* cd */
case 'd':
tail("cd");
goto changdir;
/* chdir */
case 'h':
ignchar();
if (peekchar() == 'd') {
register char *p;
tail2of("chdir");
changdir:
if (savedfile[0] == '/' || !value(WARN))
ignore(exclam());
else
ignore(quickly());
if (skipend()) {
p = getenv("HOME");
if (p == NULL)
error("Home directory unknown");
} else
getone(), p = file;
eol();
if (chdir(p) < 0)
filioerr(p);
if (savedfile[0] != '/')
edited = 0;
continue;
}
if (inopen)
tailprim("change", 2, 1);
tail2of("change");
break;
#endif
default:
if (inopen)
goto notinvis;
tail("change");
break;
}
/* change */
aiflag = exclam();
setCNL();
vmacchng(0);
setin(addr1);
ex_delete(0);
inappend = 1;
ignore(append(gettty, addr1 - 1));
inappend = 0;
nochng();
continue;
/* delete */
case 'd':
/*
* Caution: dp and dl have special meaning already.
*/
tail("delete");
c = cmdreg();
setCNL();
vmacchng(0);
if (c)
YANKreg(c);
ex_delete(0);
appendnone();
continue;
/* edit */
/* ex */
case 'e':
tail(peekchar() == 'x' ? "ex" : "edit");
editcmd:
if (!exclam() && chng)
c = 'E';
filename(c);
if (c == 'E') {
ungetchar(lastchar());
ignore(quickly());
}
setnoaddr();
doecmd:
init();
addr2 = zero;
laste++;
ex_sync();
rop(c);
#ifdef VMUNIX
tlaste();
#endif
laste = 0;
ex_sync();
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();
newline();
vmacchng(0);
deletenone();
setin(addr2);
inappend = 1;
ignore(append(gettty, addr2 - 1));
inappend = 0;
if (dot == zero && dol > zero)
dot = one;
nochng();
continue;
/* join */
case 'j':
tail("join");
c = exclam();
setcount();
nonzero();
newline();
vmacchng(0);
if (given < 2 && addr2 != dol)
addr2++;
join(c);
continue;
/* k */
case 'k':
casek:
pastwh();
c = ex_getchar();
if (endcmd(c))
serror("Mark what?|%s requires following letter", Command);
newline();
if (!islower(c))
error("Bad mark|Mark must specify a letter");
setdot();
nonzero();
names[c - 'a'] = *addr2 &~ 01;
anymarks = 1;
continue;
/* list */
case 'l':
tail("list");
setCNL();
ignorf(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);
move();
continue;
case 'n':
if (peekchar() == 'u') {
tail("number");
goto numberit;
}
/* next */
tail("next");
setnoaddr();
ckaw();
ignore(quickly());
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()) {
/* put */
case 'u':
tail("put");
setdot();
c = cmdreg();
eol();
vmacchng(0);
if (c)
putreg(c);
else
put();
continue;
case 'r':
ignchar();
if (peekchar() == 'e') {
/* preserve */
tail2of("preserve");
eol();
if (preserve() == 0)
error("Preserve failed!");
else
error("File preserved.");
}
tail2of("print");
break;
default:
tail("print");
break;
}
/* print */
setCNL();
pflag = 0;
print:
nonzero();
if (CL && span() > LINES) {
flush1();
vclear();
}
plines(addr1, addr2, 1);
continue;
/* quit */
case 'q':
tail("quit");
setnoaddr();
c = quickly();
eol();
if (!c)
quit:
nomore();
if (inopen) {
vgoto(WECHO, 0);
if (!ateopr())
vnfl();
else {
tostop();
}
flush();
ignore(setty(normf));
}
cleanup(1);
ex_exit(0);
case 'r':
if (peekchar() == 'e') {
ignchar();
switch (peekchar()) {
/* rewind */
case 'w':
tail2of("rewind");
setnoaddr();
if (!exclam()) {
ckaw();
if (chng && dol > zero)
error("No write@since last chage (:rewind! 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());
ignore(quickly());
}
init();
addr2 = zero;
laste++;
ex_sync();
recover();
rop2();
revocer();
if (status == 0)
rop3(c);
if (dol != zero)
change();
#ifdef VMUNIX
tlaste();
#endif
laste = 0;
nochng();
continue;
}
tail2of("read");
} else
tail("read");
/* read */
if (savedfile[0] == 0 && dol == zero)
c = 'e';
pastwh();
vmacchng(0);
if (peekchar() == '!') {
setdot();
ignchar();
unix0(0);
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(TE);
flush();
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':
tail("suspend");
suspend:
c = exclam();
eol();
if (!c)
ckaw();
onsusp();
continue;
#endif
}
/* fall into ... */
/* & */
/* ~ */
/* substitute */
case '&':
case '~':
Command = "substitute";
if (c == 's')
tail(Command);
vmacchng(0);
if (!substitute(c))
pflag = 0;
continue;
/* t */
case 't':
if (peekchar() == 'a') {
tail("tag");
tagfind(exclam());
if (!inopen)
lchng = chng - 1;
else
nochng();
continue;
}
tail("t");
vmacchng(0);
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();
newline();
undo(c);
continue;
case 'v':
switch (peekchar()) {
case 'e':
/* version */
tail("version");
setNAEOL();
ex_printf("@(#) Version 3.7, 6/7/85."+5);
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);
filter(1);
} else {
setall();
wop(1);
nochng();
}
if (c == 'q')
goto quit;
continue;
/* xit */
case 'x':
tail("xit");
if (!chng)
goto quit;
c = 'q';
goto wq;
/* yank */
case 'y':
tail("yank");
c = cmdreg();
setcount();
eol();
vmacchng(0);
if (c)
YANKreg(c);
else
yank();
continue;
/* z */
case 'z':
zop(0);
pflag = 0;
continue;
/* * */
/* @ */
case '*':
case '@':
c = ex_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("Bad register");
newline();
setdot();
cmdmac(c);
continue;
/* | */
case '|':
endline = 0;
goto caseline;
/* \n */
case '\n':
endline = 1;
caseline:
notempty();
if (addr2 == 0) {
if (UP != NOSTR && c == '\n' && !inglobal)
c = CTRL('k');
if (inglobal)
addr1 = addr2 = dot;
else {
if (dot == dol)
error("At EOF|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();
ignorf(setnumb(1));
pflag = 0;
goto print;
/* = */
case '=':
newline();
setall();
if (inglobal == 2)
pofix();
ex_printf("%d", lineno(addr2));
noonl();
continue;
/* ! */
case '!':
if (addr2 != 0) {
vmacchng(0);
unix0(0);
setdot();
filter(2);
} else {
unix0(1);
pofix();
putpad(TE);
flush();
unixwt(1, unixex("-c", uxb, 0, 0));
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();
return;
}
if (addr2 != 0) {
setlastchar('\n');
putnl();
}
if (dol == zero) {
if (addr2 == 0)
putnl();
notempty();
}
ungetchar(EOF);
zop(hadpr);
continue;
default:
if (!isalpha(c))
break;
ungetchar(c);
tailprim("", 0, 0);
}
error("What?|Unknown command character '%c'", c);
}
}