Subversion Repositories planix.SVN

Compare Revisions

No changes between revisions

Ignore whitespace Rev 104 → Rev 105

/ports/trunk/editors/rvi/ex_temp.c
0,0 → 1,659
/*
* Copyright (c) 1980 Regents of the University of California.
* All rights reserved. The Berkeley software License Agreement
* specifies the terms and conditions for redistribution.
*/
 
#if !defined(lint) && defined(DOSCCS)
static char *sccsid = "@(#)ex_temp.c 7.5.1.1 (Berkeley) 8/12/86";
#endif
 
#include "ex.h"
#include "ex_temp.h"
#include "ex_vis.h"
#include "ex_tty.h"
 
/*
* Editor temporary file routines.
* Very similar to those of ed, except uses 2 input buffers.
*/
#define READ 0
#define WRITE 1
 
char tfname[40];
char rfname[40];
int havetmp;
short tfile = -1;
short rfile = -1;
 
fileinit()
{
register char *p;
register int i, j;
struct stat stbuf;
 
if (tline == INCRMT * (HBLKS+2))
return;
cleanup(0);
close(tfile);
tline = INCRMT * (HBLKS+2);
blocks[0] = HBLKS;
blocks[1] = HBLKS+1;
blocks[2] = -1;
dirtcnt = 0;
iblock = -1;
iblock2 = -1;
oblock = -1;
CP(tfname, svalue(DIRECTORY));
if (stat(tfname, &stbuf)) {
dumbness:
if (setexit() == 0)
filioerr(tfname);
else
putNFL();
cleanup(1);
exit(1);
}
if ((stbuf.st_mode & S_IFMT) != S_IFDIR) {
errno = ENOTDIR;
goto dumbness;
}
ichanged = 0;
ichang2 = 0;
ignore(strcat(tfname, "/ExXXXXX"));
for (p = strend(tfname), i = 5, j = getpid(); i > 0; i--, j /= 10)
*--p = j % 10 | '0';
tfile = creat(tfname, 0600);
if (tfile < 0)
goto dumbness;
#ifdef VMUNIX
{
extern stilinc; /* see below */
stilinc = 0;
}
#endif
havetmp = 1;
close(tfile);
tfile = open(tfname, 2);
if (tfile < 0)
goto dumbness;
/* brk((char *)fendcore); */
}
 
cleanup(all)
bool all;
{
if (all) {
putpad(TE);
flush();
}
if (havetmp)
unlink(tfname);
havetmp = 0;
if (all && rfile >= 0) {
unlink(rfname);
close(rfile);
rfile = -1;
}
}
 
getline(tl)
line tl;
{
register char *bp, *lp;
register int nl;
 
lp = linebuf;
bp = getblock(tl, READ);
nl = nleft;
tl &= ~OFFMSK;
while (*lp++ = *bp++)
if (--nl == 0) {
bp = getblock(tl += INCRMT, READ);
nl = nleft;
}
}
 
putline()
{
register char *bp, *lp;
register int nl;
line tl;
 
dirtcnt++;
lp = linebuf;
change();
tl = tline;
bp = getblock(tl, WRITE);
nl = nleft;
tl &= ~OFFMSK;
while (*bp = *lp++) {
if (*bp++ == '\n') {
*--bp = 0;
linebp = lp;
break;
}
if (--nl == 0) {
bp = getblock(tl += INCRMT, WRITE);
nl = nleft;
}
}
tl = tline;
tline += (((lp - linebuf) + BNDRY - 1) >> SHFT) & 077776;
return (tl);
}
 
int read();
int write();
 
char *
getblock(atl, iof)
line atl;
int iof;
{
register int bno, off;
register char *p1, *p2;
register int n;
bno = (atl >> OFFBTS) & BLKMSK;
off = (atl << SHFT) & LBTMSK;
if (bno >= NMBLKS)
error(" Tmp file too large");
nleft = BUFSIZ - off;
if (bno == iblock) {
ichanged |= iof;
hitin2 = 0;
return (ibuff + off);
}
if (bno == iblock2) {
ichang2 |= iof;
hitin2 = 1;
return (ibuff2 + off);
}
if (bno == oblock)
return (obuff + off);
if (iof == READ) {
if (hitin2 == 0) {
if (ichang2) {
blkio(iblock2, ibuff2, write);
}
ichang2 = 0;
iblock2 = bno;
blkio(bno, ibuff2, read);
hitin2 = 1;
return (ibuff2 + off);
}
hitin2 = 0;
if (ichanged) {
blkio(iblock, ibuff, write);
}
ichanged = 0;
iblock = bno;
blkio(bno, ibuff, read);
return (ibuff + off);
}
if (oblock >= 0) {
blkio(oblock, obuff, write);
}
oblock = bno;
return (obuff + off);
}
 
#ifdef VMUNIX
#define INCORB 64
char incorb[INCORB+1][BUFSIZ];
#define pagrnd(a) ((char *)(((int)a)&~(BUFSIZ-1)))
int stilinc; /* up to here not written yet */
#endif
 
blkio(b, buf, iofcn)
short b;
char *buf;
int (*iofcn)();
{
 
#ifdef VMUNIX
if (b < INCORB) {
if (iofcn == read) {
bcopy(pagrnd(incorb[b+1]), buf, BUFSIZ);
return;
}
bcopy(buf, pagrnd(incorb[b+1]), BUFSIZ);
if (laste) {
if (b >= stilinc)
stilinc = b + 1;
return;
}
} else if (stilinc)
tflush();
#endif
lseek(tfile, (long) (unsigned) b * BUFSIZ, 0);
if ((*iofcn)(tfile, buf, BUFSIZ) != BUFSIZ)
filioerr(tfname);
}
 
#ifdef VMUNIX
tlaste()
{
 
if (stilinc)
dirtcnt = 0;
}
 
tflush()
{
int i = stilinc;
stilinc = 0;
lseek(tfile, (long) 0, 0);
if (write(tfile, pagrnd(incorb[1]), i * BUFSIZ) != (i * BUFSIZ))
filioerr(tfname);
}
#endif
 
/*
* Synchronize the state of the temporary file in case
* a crash occurs.
*/
synctmp()
{
register int cnt;
register line *a;
register short *bp;
 
#ifdef VMUNIX
if (stilinc)
return;
#endif
if (dol == zero)
return;
if (ichanged)
blkio(iblock, ibuff, write);
ichanged = 0;
if (ichang2)
blkio(iblock2, ibuff2, write);
ichang2 = 0;
if (oblock != -1)
blkio(oblock, obuff, write);
time(&H.Time);
uid = getuid();
*zero = (line) H.Time;
for (a = zero, bp = blocks; a <= dol; a += BUFSIZ / sizeof *a, bp++) {
if (*bp < 0) {
tline = (tline + OFFMSK) &~ OFFMSK;
*bp = ((tline >> OFFBTS) & BLKMSK);
if (*bp > NMBLKS)
error(" Tmp file too large");
tline += INCRMT;
oblock = *bp + 1;
bp[1] = -1;
}
lseek(tfile, (long) (unsigned) *bp * BUFSIZ, 0);
cnt = ((dol - a) + 2) * sizeof (line);
if (cnt > BUFSIZ)
cnt = BUFSIZ;
if (write(tfile, (char *) a, cnt) != cnt) {
oops:
*zero = 0;
filioerr(tfname);
}
*zero = 0;
}
flines = lineDOL();
lseek(tfile, 0l, 0);
if (write(tfile, (char *) &H, sizeof H) != sizeof H)
goto oops;
#ifdef notdef
/*
* This will insure that exrecover gets as much
* back after a crash as is absolutely possible,
* but can result in pregnant pauses between commands
* when the TSYNC call is made, so...
*/
(void) fsync(tfile);
#endif
}
 
TSYNC()
{
 
if (dirtcnt > MAXDIRT) { /* mjm: 12 --> MAXDIRT */
#ifdef VMUNIX
if (stilinc)
tflush();
#endif
dirtcnt = 0;
synctmp();
}
}
 
/*
* Named buffer routines.
* These are implemented differently than the main buffer.
* Each named buffer has a chain of blocks in the register file.
* Each block contains roughly 508 chars of text,
* and a previous and next block number. We also have information
* about which blocks came from deletes of multiple partial lines,
* e.g. deleting a sentence or a LISP object.
*
* We maintain a free map for the temp file. To free the blocks
* in a register we must read the blocks to find how they are chained
* together.
*
* BUG: The default savind of deleted lines in numbered
* buffers may be rather inefficient; it hasn't been profiled.
*/
struct strreg {
short rg_flags;
short rg_nleft;
short rg_first;
short rg_last;
} strregs[('z'-'a'+1) + ('9'-'0'+1)], *strp;
 
struct rbuf {
short rb_prev;
short rb_next;
char rb_text[BUFSIZ - 2 * sizeof (short)];
} *rbuf, KILLrbuf, putrbuf, YANKrbuf, regrbuf;
#ifdef VMUNIX
short rused[256];
#else
short rused[32];
#endif
short rnleft;
short rblock;
short rnext;
char *rbufcp;
 
regio(b, iofcn)
short b;
int (*iofcn)();
{
 
if (rfile == -1) {
CP(rfname, tfname);
*(strend(rfname) - 7) = 'R';
rfile = creat(rfname, 0600);
if (rfile < 0)
oops:
filioerr(rfname);
close(rfile);
rfile = open(rfname, 2);
if (rfile < 0)
goto oops;
}
lseek(rfile, (long) b * BUFSIZ, 0);
if ((*iofcn)(rfile, rbuf, BUFSIZ) != BUFSIZ)
goto oops;
rblock = b;
}
 
REGblk()
{
register int i, j, m;
 
for (i = 0; i < sizeof rused / sizeof rused[0]; i++) {
m = (rused[i] ^ 0177777) & 0177777;
if (i == 0)
m &= ~1;
if (m != 0) {
j = 0;
while ((m & 1) == 0)
j++, m >>= 1;
rused[i] |= (1 << j);
#ifdef RDEBUG
printf("allocating block %d\n", i * 16 + j);
#endif
return (i * 16 + j);
}
}
error("Out of register space (ugh)");
/*NOTREACHED*/
}
 
struct strreg *
mapreg(c)
register int c;
{
 
if (isupper(c))
c = tolower(c);
return (isdigit(c) ? &strregs[('z'-'a'+1)+(c-'0')] : &strregs[c-'a']);
}
 
int shread();
 
KILLreg(c)
register int c;
{
register struct strreg *sp;
 
rbuf = &KILLrbuf;
sp = mapreg(c);
rblock = sp->rg_first;
sp->rg_first = sp->rg_last = 0;
sp->rg_flags = sp->rg_nleft = 0;
while (rblock != 0) {
#ifdef RDEBUG
printf("freeing block %d\n", rblock);
#endif
rused[rblock / 16] &= ~(1 << (rblock % 16));
regio(rblock, shread);
rblock = rbuf->rb_next;
}
}
 
/*VARARGS*/
shread()
{
struct front { short a; short b; };
 
if (read(rfile, (char *) rbuf, sizeof (struct front)) == sizeof (struct front))
return (sizeof (struct rbuf));
return (0);
}
 
int getREG();
 
putreg(c)
char c;
{
register line *odot = dot;
register line *odol = dol;
register int cnt;
 
deletenone();
appendnone();
rbuf = &putrbuf;
rnleft = 0;
rblock = 0;
rnext = mapreg(c)->rg_first;
if (rnext == 0) {
if (inopen) {
splitw++;
vclean();
vgoto(WECHO, 0);
}
vreg = -1;
error("Nothing in register %c", c);
}
if (inopen && partreg(c)) {
if (!FIXUNDO) {
splitw++; vclean(); vgoto(WECHO, 0); vreg = -1;
error("Can't put partial line inside macro");
}
squish();
addr1 = addr2 = dol;
}
cnt = append(getREG, addr2);
if (inopen && partreg(c)) {
unddol = dol;
dol = odol;
dot = odot;
pragged(0);
}
killcnt(cnt);
notecnt = cnt;
}
 
partreg(c)
char c;
{
 
return (mapreg(c)->rg_flags);
}
 
notpart(c)
register int c;
{
 
if (c)
mapreg(c)->rg_flags = 0;
}
 
getREG()
{
register char *lp = linebuf;
register int c;
 
for (;;) {
if (rnleft == 0) {
if (rnext == 0)
return (EOF);
regio(rnext, read);
rnext = rbuf->rb_next;
rbufcp = rbuf->rb_text;
rnleft = sizeof rbuf->rb_text;
}
c = *rbufcp;
if (c == 0)
return (EOF);
rbufcp++, --rnleft;
if (c == '\n') {
*lp++ = 0;
return (0);
}
*lp++ = c;
}
}
 
YANKreg(c)
register int c;
{
register line *addr;
register struct strreg *sp;
char savelb[LBSIZE];
 
if (isdigit(c))
kshift();
if (islower(c))
KILLreg(c);
strp = sp = mapreg(c);
sp->rg_flags = inopen && cursor && wcursor;
rbuf = &YANKrbuf;
if (sp->rg_last) {
regio(sp->rg_last, read);
rnleft = sp->rg_nleft;
rbufcp = &rbuf->rb_text[sizeof rbuf->rb_text - rnleft];
} else {
rblock = 0;
rnleft = 0;
}
CP(savelb,linebuf);
for (addr = addr1; addr <= addr2; addr++) {
getline(*addr);
if (sp->rg_flags) {
if (addr == addr2)
*wcursor = 0;
if (addr == addr1)
strcpy(linebuf, cursor);
}
YANKline();
}
rbflush();
killed();
CP(linebuf,savelb);
}
 
kshift()
{
register int i;
 
KILLreg('9');
for (i = '8'; i >= '0'; i--)
copy(mapreg(i+1), mapreg(i), sizeof (struct strreg));
}
 
YANKline()
{
register char *lp = linebuf;
register struct rbuf *rp = rbuf;
register int c;
 
do {
c = *lp++;
if (c == 0)
c = '\n';
if (rnleft == 0) {
rp->rb_next = REGblk();
rbflush();
rblock = rp->rb_next;
rp->rb_next = 0;
rp->rb_prev = rblock;
rnleft = sizeof rp->rb_text;
rbufcp = rp->rb_text;
}
*rbufcp++ = c;
--rnleft;
} while (c != '\n');
if (rnleft)
*rbufcp = 0;
}
 
rbflush()
{
register struct strreg *sp = strp;
 
if (rblock == 0)
return;
regio(rblock, write);
if (sp->rg_first == 0)
sp->rg_first = rblock;
sp->rg_last = rblock;
sp->rg_nleft = rnleft;
}
 
/* Register c to char buffer buf of size buflen */
regbuf(c, buf, buflen)
char c;
char *buf;
int buflen;
{
register char *p, *lp;
 
rbuf = &regrbuf;
rnleft = 0;
rblock = 0;
rnext = mapreg(c)->rg_first;
if (rnext==0) {
*buf = 0;
error("Nothing in register %c",c);
}
p = buf;
while (getREG()==0) {
for (lp=linebuf; *lp;) {
if (p >= &buf[buflen])
error("Register too long@to fit in memory");
*p++ = *lp++;
}
*p++ = '\n';
}
if (partreg(c)) p--;
*p = '\0';
getDOT();
}
 
/*
* Encryption routines. These are essentially unmodified from ed.
*/
 
/ports/trunk/editors/rvi/ex_tty.c
0,0 → 1,272
/*
* Copyright (c) 1980 Regents of the University of California.
* All rights reserved. The Berkeley software License Agreement
* specifies the terms and conditions for redistribution.
*/
 
#if !defined(lint) && defined(DOSCCS)
static char *sccsid = "@(#)ex_tty.c 7.10.1 (2.11BSD GTE) 12/9/94";
#endif
 
#include "ex.h"
#include "ex_tty.h"
 
/*
* Terminal type initialization routines,
* and calculation of flags at entry or after
* a shell escape which may change them.
*/
/* short ospeed = -1; mjm: def also in tputs.c of termcap.a */
short o_speed = -1;
 
gettmode()
{
 
#ifndef USG3TTY
if (gtty(1, &tty) < 0)
return;
if (o_speed != tty.sg_ospeed)
value(SLOWOPEN) = tty.sg_ospeed < B1200;
o_speed = tty.sg_ospeed;
normf = tty.sg_flags;
GT = (tty.sg_flags & XTABS) != XTABS && !XT;
NONL = (tty.sg_flags & CRMOD) == 0;
#else
if (ioctl(1, TCGETA, &tty) < 0)
return;
if (o_speed != (tty.c_cflag & CBAUD)) /* mjm */
value(SLOWOPEN) = (tty.c_cflag & CBAUD) < B1200;
o_speed = tty.c_cflag & CBAUD;
normf = tty;
GT = (tty.c_oflag & TABDLY) != TAB3 && !XT;
NONL = (tty.c_oflag & ONLCR) == 0;
#endif
}
 
char *xPC;
char **sstrs[] = {
&AL, &BC, &BT, &CD, &CE, &CL, &CM, &xCR, &CS, &DC, &DL, &DM, &DO,
&ED, &EI, &F0, &F1, &F2, &F3, &F4, &F5, &F6, &F7, &F8, &F9,
&HO, &IC, &IM, &IP, &KD, &KE, &KH, &KL, &KR, &KS, &KU, &LL, &ND, &xNL,
&xPC, &RC, &SC, &SE, &SF, &SO, &SR, &TA, &TE, &TI, &UP, &VB, &VS, &VE,
&AL_PARM, &DL_PARM, &UP_PARM, &DOWN_PARM, &LEFT_PARM, &RIGHT_PARM
};
bool *sflags[] = {
&AM, &BS, &DA, &DB, &EO, &HC, &IN, &MI, &NC, &NS, &OS, &UL,
&XB, &XN, &XT, &XX
};
char **fkeys[10] = {
&F0, &F1, &F2, &F3, &F4, &F5, &F6, &F7, &F8, &F9
};
setterm(type)
char *type;
{
char *tgoto();
register int unknown;
char ltcbuf[TCBUFSIZE];
 
if (type[0] == 0)
type = "xx";
unknown = 0;
putpad(TE);
#ifdef NCURSES
snprintf(ltcbuf, TCBUFSIZE, "%s:", type);
#endif
if (tgetent(ltcbuf, type) != 1) {
unknown++;
CP(ltcbuf, "xx|dumb:");
}
setsize();
aoftspace = tspace;
zap();
/*
* Initialize keypad arrow keys.
*/
arrows[0].cap = KU; arrows[0].mapto = "k"; arrows[0].descr = "up";
arrows[1].cap = KD; arrows[1].mapto = "j"; arrows[1].descr = "down";
arrows[2].cap = KL; arrows[2].mapto = "h"; arrows[2].descr = "left";
arrows[3].cap = KR; arrows[3].mapto = "l"; arrows[3].descr = "right";
arrows[4].cap = KH; arrows[4].mapto = "H"; arrows[4].descr = "home";
 
/*
* Handle funny termcap capabilities
*/
if (CS && SC && RC) {
if (AL==NULL) AL="";
if (DL==NULL) DL="";
}
if (AL_PARM && AL==NULL) AL="";
if (DL_PARM && DL==NULL) DL="";
if (IC && IM==NULL) IM="";
if (IC && EI==NULL) EI="";
if (!GT) BT=NULL; /* If we can't tab, we can't backtab either */
 
#ifdef TIOCLGET
/*
* Now map users susp char to ^Z, being careful that the susp
* overrides any arrow key, but only for hackers (=new tty driver).
*/
{
static char sc[2];
int i, fnd;
 
ioctl(0, TIOCGETD, &ldisc);
if (ldisc == NTTYDISC) {
sc[0] = olttyc.t_suspc;
sc[1] = 0;
if (olttyc.t_suspc == CTRL('z')) {
for (i=0; i<=4; i++)
if (arrows[i].cap &&
arrows[i].cap[0] == CTRL('z'))
addmac(sc, NULL, NULL, arrows);
} else
addmac(sc, "\32", "susp", arrows);
}
}
#endif
 
if (CM != 0) {
if (tgoto(CM, 2, 2)[0] == 'O') /* OOPS */
CA = 0, CM = 0;
else
CA = 1, costCM = cost(tgoto(CM, 8, 10));
} else {
CA = 0, CM = 0;
}
costSR = cost(SR);
costAL = cost(AL);
costDP = cost(tgoto(DOWN_PARM, 10, 10));
costLP = cost(tgoto(LEFT_PARM, 10, 10));
costRP = cost(tgoto(RIGHT_PARM, 10, 10));
PC = xPC ? xPC[0] : 0;
aoftspace = tspace;
CP(ttytype, longname(ltcbuf, type));
/* proper strings to change tty type */
termreset();
gettmode();
value(REDRAW) = AL && DL;
value(OPTIMIZE) = !CA && !GT;
if (o_speed == B1200 && !value(REDRAW))
value(SLOWOPEN) = 1; /* see also gettmode above */
if (unknown)
serror("%s: Unknown terminal type", type);
}
 
setsize()
{
register int l, i;
struct winsize win;
 
if (ioctl(0, TIOCGWINSZ, &win) < 0) {
i = LINES = tgetnum("li");
COLUMNS = tgetnum("co");
} else {
if ((LINES = winsz.ws_row = win.ws_row) == 0)
LINES = tgetnum("li");
i = LINES;
if ((COLUMNS = winsz.ws_col = win.ws_col) == 0)
COLUMNS = tgetnum("co");
}
if (LINES <= 5)
LINES = 24;
if (LINES > TUBELINES)
LINES = TUBELINES;
l = LINES;
if (o_speed < B1200)
l = 9; /* including the message line at the bottom */
else if (o_speed < B2400)
l = 17;
if (l > LINES)
l = LINES;
if (COLUMNS <= 4)
COLUMNS = 1000;
options[WINDOW].ovalue = options[WINDOW].odefault = l - 1;
if (defwind) options[WINDOW].ovalue = defwind;
options[SCROLL].ovalue = options[SCROLL].odefault = HC ? 11 : ((l-1) / 2);
if (i <= 0)
LINES = 2;
}
 
zap()
{
register char *namp;
register bool **fp;
register char ***sp;
int flag;
char *string;
 
namp = "ambsdadbeohcinmincnsosulxbxnxtxx";
fp = sflags;
do {
flag = tgetflag(namp);
*(*fp++) = flag;
namp += 2;
} while (*namp);
namp = "albcbtcdceclcmcrcsdcdldmdoedeik0k1k2k3k4k5k6k7k8k9hoicimipkdkekhklkrkskullndnlpcrcscsesfsosrtatetiupvbvsveALDLUPDOLERI";
sp = sstrs;
do {
string = tgetstr(namp, &aoftspace);
*(*sp++) = string;
namp += 2;
} while (*namp);
}
 
char *
longname(bp, def)
register char *bp;
char *def;
{
register char *cp;
 
while (*bp && *bp != ':' && *bp != '|')
bp++;
if (*bp == '|') {
bp++;
cp = bp;
while (*cp && *cp != ':' && *cp != '|')
cp++;
*cp = 0;
return (bp);
}
return (def);
}
 
char *
fkey(i)
int i;
{
if (0 <= i && i <= 9)
return(*fkeys[i]);
else
return(NOSTR);
}
 
/*
* cost figures out how much (in characters) it costs to send the string
* str to the terminal. It takes into account padding information, as
* much as it can, for a typical case. (Right now the typical case assumes
* the number of lines affected is the size of the screen, since this is
* mainly used to decide if AL or SR is better, and this always happens
* at the top of the screen. We assume cursor motion (CM) has little
* padding, if any, required, so that case, which is really more important
* than AL vs SR, won't be really affected.)
*/
static int costnum;
cost(str)
char *str;
{
int countnum();
 
if (str == NULL || *str=='O') /* OOPS */
return 10000; /* infinity */
costnum = 0;
tputs(str, LINES, countnum);
return costnum;
}
 
/* ARGSUSED */
countnum(ch)
char ch;
{
costnum++;
}
/ports/trunk/editors/rvi/ex_tune.h
0,0 → 1,136
/*
* Copyright (c) 1980 Regents of the University of California.
* All rights reserved. The Berkeley software License Agreement
* specifies the terms and conditions for redistribution.
*
* @(#)ex_tune.h 7.8.1 (2.11BSD) 1996/10/23
*/
 
/*
* Definitions of editor parameters and limits
*/
 
/*
* Pathnames.
*/
#if 0 /* GR */
#define E_TERMCAP "/etc/termcap"
#else
#include "tcbufsize.h"
#endif
#define B_CSH "/bin/csh"
#define EXRECOVER "/usr/sbin/exrecover"
#define EXPRESERVE "/usr/sbin/expreserve"
#ifndef VMUNIX
#define EXSTRINGS "/usr/share/misc/exstrings"
#endif
 
/*
* If your system believes that tabs expand to a width other than
* 8 then your makefile should cc with -DTABS=whatever, otherwise we use 8.
*/
#ifndef TABS
#define TABS 8
#endif
 
/*
* Maximums
*
* The definition of LBSIZE should be the same as BUFSIZ (512 usually).
* Most other definitions are quite generous.
*/
/* FNSIZE is also defined in expreserve.c */
#define FNSIZE 128 /* File name size */
#ifdef VMUNIX
#ifndef BIGMEM
#define LBSIZE 1024
#define ESIZE 512
#define CRSIZE 1024
#else
#define LBSIZE 4096
#define ESIZE 512
#define CRSIZE 4096
#endif
#else /* !VMUNIX */
#ifdef u370
#define LBSIZE 4096
#define ESIZE 512
#define CRSIZE 4096
#else
#define LBSIZE 512 /* Line length */
#define ESIZE 128 /* Size of compiled re */
#define CRSIZE 512
#endif
#endif
#define RHSSIZE 256 /* Size of rhs of substitute */
#define NBRA 9 /* Number of re \( \) pairs */
#define TAGSIZE 128 /* Tag length */
#define ONMSZ 64 /* Option name size */
#define GBSIZE 256 /* Buffer size */
#define UXBSIZE 128 /* Unix command buffer size */
#define VBSIZE 128 /* Partial line max size in visual */
/* LBLKS is also defined in expreserve.c */
#ifndef VMUNIX
#define LBLKS 125 /* Line pointer blocks in temp file */
#define HBLKS 1 /* struct header fits in BUFSIZ*HBLKS */
#else
#define LBLKS 900
#define HBLKS 2
#endif
#define MAXDIRT 12 /* Max dirtcnt before sync tfile */
#if 0 /* GR */
#define TCBUFSIZE 1024 /* Max entry size in termcap, see
also termlib and termcap */
#endif
 
/*
* Except on VMUNIX, these are a ridiculously small due to the
* lousy arglist processing implementation which fixes core
* proportional to them. Argv (and hence NARGS) is really unnecessary,
* and argument character space not needed except when
* arguments exist. Argument lists should be saved before the "zero"
* of the incore line information and could then
* be reasonably large.
*/
#undef NCARGS
#ifndef VMUNIX
#define NARGS 100 /* Maximum number of names in "next" */
#define NCARGS LBSIZE /* Maximum arglist chars in "next" */
#else
#define NCARGS 5120
#define NARGS (NCARGS/6)
#endif
 
/*
* Note: because the routine "alloca" is not portable, TUBESIZE
* bytes are allocated on the stack each time you go into visual
* and then never freed by the system. Thus if you have no terminals
* which are larger than 24 * 80 you may well want to make TUBESIZE
* smaller. TUBECOLS should stay at 160 since this defines the maximum
* length of opening on hardcopies and allows two lines of open on
* terminals like adm3's (glass tty's) where it switches to pseudo
* hardcopy mode when a line gets longer than 80 characters.
*/
#ifndef VMUNIX
#define TUBELINES 70 /* Number of screen lines for visual */
#define TUBECOLS 160 /* Number of screen columns for visual */
#define TUBESIZE 6000 /* Maximum screen size for visual */
#else
#define TUBELINES 70
#define TUBECOLS 160
#define TUBESIZE 7000 /* 70 * 100 */
#endif
 
/*
* Output column (and line) are set to this value on cursor addressible
* terminals when we lose track of the cursor to force cursor
* addressing to occur.
*/
#define UKCOL -20 /* Prototype unknown column */
 
/*
* Attention is the interrupt character (normally 0177 -- delete).
* Quit is the quit signal (normally FS -- control-\) and quits open/visual.
*/
#define ATTN (-2) /* mjm: (char) ?? */
#define QUIT ('\\' & 037)
/ports/trunk/editors/rvi/ex_v.c
0,0 → 1,450
/*
* Copyright (c) 1980 Regents of the University of California.
* All rights reserved. The Berkeley software License Agreement
* specifies the terms and conditions for redistribution.
*/
 
#if !defined(lint) && defined(DOSCCS)
static char *sccsid = "@(#)ex_v.c 7.8.1 (2.11BSD GTE) 12/9/94";
#endif
 
#include "ex.h"
#include "ex_re.h"
#include "ex_tty.h"
#include "ex_vis.h"
 
/*
* Entry points to open and visual from command mode processor.
* The open/visual code breaks down roughly as follows:
*
* ex_v.c entry points, checking of terminal characteristics
*
* ex_vadj.c logical screen control, use of intelligent operations
* insert/delete line and coordination with screen image;
* updating of screen after changes.
*
* ex_vget.c input of single keys and reading of input lines
* from the echo area, handling of memory for repeated
* commands and small saved texts from inserts and partline
* deletes, notification of multi line changes in the echo
* area.
*
* ex_vmain.c main command decoding, some command processing.
*
* ex_voperate.c decoding of operator/operand sequences and
* contextual scans, implementation of word motions.
*
* ex_vops.c major operator interfaces, undos, motions, deletes,
* changes, opening new lines, shifts, replacements and yanks
* coordinating logical and physical changes.
*
* ex_vops2.c subroutines for operator interfaces in ex_vops.c,
* insert mode, read input line processing at lowest level.
*
* ex_vops3.c structured motion definitions of ( ) { } and [ ] operators,
* indent for lisp routines, () and {} balancing.
*
* ex_vput.c output routines, clearing, physical mapping of logical cursor
* positioning, cursor motions, handling of insert character
* and delete character functions of intelligent and unintelligent
* terminals, visual mode tracing routines (for debugging),
* control of screen image and its updating.
*
* ex_vwind.c window level control of display, forward and backward rolls,
* absolute motions, contextual displays, line depth determination
*/
 
jmp_buf venv;
int winch();
 
/*
* Enter open mode
*/
#ifdef u370
#ifndef BIT8
char atube[TUBESIZE+LBSIZE];
#else
short atube[TUBESIZE+LBSIZE];
#endif
#endif
oop()
{
register char *ic;
#ifndef u370
#ifndef BIT8
char atube[TUBESIZE + LBSIZE];
#else
short atube[TUBESIZE + LBSIZE];
#endif
#endif
ttymode f; /* mjm: was register */
int resize;
 
if (resize = setjmp(venv)) {
setsize();
initev = (char *)0;
inopen = 0;
addr1 = addr2 = dot;
}
(void)signal(SIGWINCH, winch);
ovbeg();
if (peekchar() == '/') {
ignore(compile(getchar(), 1));
savere(scanre);
if (execute(0, dot) == 0)
error("Fail|Pattern not found on addressed line");
ic = loc1;
if (ic > linebuf && *ic == 0)
ic--;
} else {
getDOT();
ic = vskipwh(linebuf);
}
newline();
 
/*
* If overstrike then have to HARDOPEN
* else if can move cursor up off current line can use CRTOPEN (~~vi1)
* otherwise (ugh) have to use ONEOPEN (like adm3)
*/
if (OS && !EO)
bastate = HARDOPEN;
else if (CA || UP)
bastate = CRTOPEN;
else
bastate = ONEOPEN;
setwind();
 
/*
* To avoid bombing on glass-crt's when the line is too long
* pretend that such terminals are 160 columns wide.
* If a line is too wide for display, we will dynamically
* switch to hardcopy open mode.
*/
if (state != CRTOPEN)
WCOLS = TUBECOLS;
if (!inglobal)
savevis();
vok(atube);
if (state != CRTOPEN)
COLUMNS = WCOLS;
Outchar = vputchar;
f = ostart();
if (state == CRTOPEN) {
if (outcol == UKCOL)
outcol = 0;
vmoveitup(1, 1);
} else
outline = destline = WBOT;
vshow(dot, NOLINE);
vnline(ic);
vmain();
if (state != CRTOPEN)
vclean();
Command = "open";
ovend(f);
(void)signal(SIGWINCH, SIG_DFL);
}
 
ovbeg()
{
 
if (!value(OPEN))
error("Can't use open/visual unless open option is set");
if (inopen)
error("Recursive open/visual not allowed");
Vlines = lineDOL();
fixzero();
setdot();
pastwh();
dot = addr2;
}
 
ovend(f)
ttymode f;
{
 
splitw++;
vgoto(WECHO, 0);
vclreol();
vgoto(WECHO, 0);
holdcm = 0;
splitw = 0;
ostop(f);
setoutt();
undvis();
COLUMNS = OCOLUMNS;
inopen = 0;
flusho();
netchHAD(Vlines);
}
 
/*
* Enter visual mode
*/
vop()
{
register int c;
#ifndef u370
char atube[TUBESIZE + LBSIZE];
#endif
ttymode f; /* mjm: was register */
int resize;
 
if (!CA && UP == NOSTR) {
if (initev) {
toopen:
merror("[Using open mode]");
putNFL();
oop();
return;
}
error("Visual needs addressible cursor or upline capability");
}
if (OS && !EO) {
if (initev)
goto toopen;
error("Can't use visual on a terminal which overstrikes");
}
if (!CL) {
if (initev)
goto toopen;
error("Visual requires clear screen capability");
}
if (NS && !SF) {
if (initev)
goto toopen;
error("Visual requires scrolling");
}
if (resize = setjmp(venv)) {
setsize();
initev = (char *)0;
inopen = 0;
addr1 = addr2 = dot;
}
(void)signal(SIGWINCH, winch);
ovbeg();
bastate = VISUAL;
c = 0;
if (any(peekchar(), "+-^."))
c = getchar();
pastwh();
vsetsiz(isdigit(peekchar()) ? getnum() : value(WINDOW));
setwind();
newline();
vok(atube);
if (!inglobal)
savevis();
Outchar = vputchar;
vmoving = 0;
f = ostart();
if (initev == 0) {
vcontext(dot, c);
vnline(NOSTR);
}
vmain();
Command = "visual";
ovend(f);
(void)signal(SIGWINCH, SIG_DFL);
}
 
/*
* Hack to allow entry to visual with
* empty buffer since routines internally
* demand at least one line.
*/
fixzero()
{
 
if (dol == zero) {
register bool ochng = chng;
 
vdoappend("");
if (!ochng)
sync();
addr1 = addr2 = one;
} else if (addr2 == zero)
addr2 = one;
}
 
/*
* Save lines before visual between unddol and truedol.
* Accomplish this by throwing away current [unddol,truedol]
* and then saving all the lines in the buffer and moving
* unddol back to dol. Don't do this if in a global.
*
* If you do
* g/xxx/vi.
* and then do a
* :e xxxx
* at some point, and then quit from the visual and undo
* you get the old file back. Somewhat weird.
*/
savevis()
{
 
if (inglobal)
return;
truedol = unddol;
saveall();
unddol = dol;
undkind = UNDNONE;
}
 
/*
* Restore a sensible state after a visual/open, moving the saved
* stuff back to [unddol,dol], and killing the partial line kill indicators.
*/
undvis()
{
 
if (ruptible)
signal(SIGINT, onintr);
squish();
pkill[0] = pkill[1] = 0;
unddol = truedol;
unddel = zero;
undap1 = one;
undap2 = dol + 1;
undkind = UNDALL;
if (undadot <= zero || undadot > dol)
undadot = zero+1;
}
 
/*
* Set the window parameters based on the base state bastate
* and the available buffer space.
*/
setwind()
{
 
WCOLS = COLUMNS;
switch (bastate) {
 
case ONEOPEN:
if (AM)
WCOLS--;
/* fall into ... */
 
case HARDOPEN:
basWTOP = WTOP = WBOT = WECHO = 0;
ZERO = 0;
holdcm++;
break;
 
case CRTOPEN:
basWTOP = LINES - 2;
/* fall into */
 
case VISUAL:
ZERO = LINES - TUBESIZE / WCOLS;
if (ZERO < 0)
ZERO = 0;
if (ZERO > basWTOP)
error("Screen too large for internal buffer");
WTOP = basWTOP; WBOT = LINES - 2; WECHO = LINES - 1;
break;
}
state = bastate;
basWLINES = WLINES = WBOT - WTOP + 1;
}
 
/*
* Can we hack an open/visual on this terminal?
* If so, then divide the screen buffer up into lines,
* and initialize a bunch of state variables before we start.
*/
vok(atube)
#ifndef BIT8
register char *atube;
#else
register short *atube;
#endif
{
register int i;
 
if (WCOLS == 1000)
serror("Don't know enough about your terminal to use %s", Command);
if (WCOLS > TUBECOLS)
error("Terminal too wide");
if (WLINES >= TUBELINES || WCOLS * (WECHO - ZERO + 1) > TUBESIZE)
error("Screen too large");
 
vtube0 = atube;
vclrbyte(atube, WCOLS * (WECHO - ZERO + 1));
for (i = 0; i < ZERO; i++)
#ifndef BIT8
vtube[i] = (char *) 0;
#else
vtube[i] = (short *) 0;
#endif
for (; i <= WECHO; i++)
vtube[i] = atube, atube += WCOLS;
for (; i < TUBELINES; i++)
#ifndef BIT8
vtube[i] = (char *) 0;
#else
vtube[i] = (short *) 0;
#endif
vutmp = atube;
vundkind = VNONE;
vUNDdot = 0;
OCOLUMNS = COLUMNS;
inopen = 1;
#ifdef CBREAK
signal(SIGINT, vintr);
#endif
vmoving = 0;
splitw = 0;
doomed = 0;
holdupd = 0;
Peekkey = 0;
vcnt = vcline = 0;
if (vSCROLL == 0)
vSCROLL = (value(WINDOW)+1)/2; /* round up so dft=6,11 */
}
 
#ifdef CBREAK
vintr()
{
extern jmp_buf readbuf;
extern int doingread;
 
signal(SIGINT, vintr);
if (vcatch)
onintr();
ungetkey(ATTN);
draino();
if (doingread) {
doingread = 0;
longjmp(readbuf, 1);
}
}
#endif
 
/*
* Set the size of the screen to size lines, to take effect the
* next time the screen is redrawn.
*/
vsetsiz(size)
int size;
{
register int b;
 
if (bastate != VISUAL)
return;
b = LINES - 1 - size;
if (b >= LINES - 1)
b = LINES - 2;
if (b < 0)
b = 0;
basWTOP = b;
basWLINES = WBOT - b + 1;
}
 
winch()
{
vsave();
setty(normf);
longjmp(venv, 1);
}
/ports/trunk/editors/rvi/ex_vars.h
0,0 → 1,52
/*
* Copyright (c) 1980 Regents of the University of California.
* All rights reserved. The Berkeley software License Agreement
* specifies the terms and conditions for redistribution.
*
* @(#)ex_vars.h 7.5 (Berkeley) 8/29/85
*/
 
#define AUTOINDENT 0
#define AUTOPRINT 1
#define AUTOWRITE 2
#define BEAUTIFY 3
#define DIRECTORY 4
#define EDCOMPATIBLE 5
#define ERRORBELLS 6
#define HARDTABS 7
#define IGNORECASE 8
#define LISP 9
#define LIST 10
#define MAGIC 11
#define MESG 12
#define MODELINE 13
#define NUMBER 14
#define OPEN 15
#define OPTIMIZE 16
#define PARAGRAPHS 17
#define PROMPT 18
#define READONLY 19
#define REDRAW 20
#define REMAP 21
#define REPORT 22
#define SCROLL 23
#define SECTIONS 24
#define SHELL 25
#define SHIFTWIDTH 26
#define SHOWMATCH 27
#define SLOWOPEN 28
#define SOURCEANY 29
#define TABSTOP 30
#define TAGLENGTH 31
#define TAGS 32
#define TERM 33
#define TERSE 34
#define TIMEOUT 35
#define TTYTYPE 36
#define WARN 37
#define WINDOW 38
#define WRAPSCAN 39
#define WRAPMARGIN 40
#define WRITEANY 41
 
#define NOPTS 42
/ports/trunk/editors/rvi/ex_vmain.c
0,0 → 1,1399
/*
* Copyright (c) 1980 Regents of the University of California.
* All rights reserved. The Berkeley software License Agreement
* specifies the terms and conditions for redistribution.
*/
 
#if !defined(lint) && defined(DOSCCS)
static char *sccsid = "@(#)ex_vmain.c 7.7 (Berkeley) 6/7/85";
#endif
 
#include "ex.h"
#include "ex_tty.h"
#include "ex_vis.h"
 
#ifdef BIT8
short *ss_strcpy(dest, src)
short *dest;
const short *src;
{
unsigned int i = 0;
 
do {
dest[i] = src[i];
} while (src[i++] != '\0');
return dest;
}
 
short *sc_strcpy(dest, src)
short *dest;
const char *src;
{
unsigned int i = 0;
 
do {
dest[i] = src[i];
} while (src[i++] != '\0');
return dest;
}
 
char *cs_strcpy(dest, src)
char *dest;
const short *src;
{
unsigned int i = 0;
 
do {
dest[i] = src[i];
} while (src[i++] != '\0');
return dest;
}
 
unsigned int s_strlen(s)
const short *s;
{
unsigned int i = 0;
 
while (s[i] != '\0')
i++;
return i;
}
 
short *sc_strcat(dest, src)
short *dest;
const char *src;
{
return sc_strcpy(dest + s_strlen((const short*)dest), src);
}
#endif
 
/*
* This is the main routine for visual.
* We here decode the count and possible named buffer specification
* preceding a command and interpret a few of the commands.
* Commands which involve a target (i.e. an operator) are decoded
* in the routine operate in ex_voperate.c.
*/
 
#define forbid(a) { if (a) goto fonfon; }
 
vmain()
{
register int c, cnt, i;
#ifndef BIT8
char esave[TUBECOLS];
char *oglobp;
#else
short esave[TUBECOLS];
short *oglobp;
#endif
short d;
line *addr;
int ind, nlput;
int shouldpo = 0;
int onumber, olist, (*OPline)(), (*OPutchar)();
 
vch_mac = VC_NOTINMAC;
 
/*
* If we started as a vi command (on the command line)
* then go process initial commands (recover, next or tag).
*/
if (initev) {
oglobp = globp;
globp = initev;
hadcnt = cnt = 0;
i = tchng;
addr = dot;
goto doinit;
}
 
/*
* NB:
*
* The current line is always in the line buffer linebuf,
* and the cursor at the position cursor. You should do
* a vsave() before moving off the line to make sure the disk
* copy is updated if it has changed, and a getDOT() to get
* the line back if you mung linebuf. The motion
* routines in ex_vwind.c handle most of this.
*/
for (;;) {
/*
* Decode a visual command.
* First sync the temp file if there has been a reasonable
* amount of change. Clear state for decoding of next
* command.
*/
TSYNC();
vglobp = 0;
vreg = 0;
hold = 0;
seenprompt = 1;
wcursor = 0;
Xhadcnt = hadcnt = 0;
Xcnt = cnt = 1;
splitw = 0;
if (i = holdupd) {
if (state == VISUAL)
ignore(peekkey());
holdupd = 0;
/*
if (LINE(0) < ZERO) {
vclear();
vcnt = 0;
i = 3;
}
*/
if (state != VISUAL) {
vcnt = 0;
vsave();
vrepaint(cursor);
} else if (i == 3)
vredraw(WTOP);
else
vsync(WTOP);
vfixcurs();
}
 
/*
* Gobble up counts and named buffer specifications.
*/
for (;;) {
looptop:
#ifdef MDEBUG
if (trace)
fprintf(trace, "pc=%c",peekkey());
#endif
if (isdigit(peekkey()) && peekkey() != '0') {
hadcnt = 1;
cnt = vgetcnt();
forbid (cnt <= 0);
}
if (peekkey() != '"')
break;
ignore(getkey()), c = getkey();
/*
* Buffer names be letters or digits.
* But not '0' as that is the source of
* an 'empty' named buffer spec in the routine
* kshift (see ex_temp.c).
*/
forbid (c == '0' || !isalpha(c) && !isdigit(c));
vreg = c;
}
reread:
/*
* Come to reread from below after some macro expansions.
* The call to map allows use of function key pads
* by performing a terminal dependent mapping of inputs.
*/
#ifdef MDEBUG
if (trace)
fprintf(trace,"pcb=%c,",peekkey());
#endif
op = getkey();
maphopcnt = 0;
do {
/*
* Keep mapping the char as long as it changes.
* This allows for double mappings, e.g., q to #,
* #1 to something else.
*/
c = op;
op = map(c,arrows);
#ifdef MDEBUG
if (trace)
fprintf(trace,"pca=%c,",c);
#endif
/*
* Maybe the mapped to char is a count. If so, we have
* to go back to the "for" to interpret it. Likewise
* for a buffer name.
*/
if ((isdigit(c) && c!='0') || c == '"') {
ungetkey(c);
goto looptop;
}
if (!value(REMAP)) {
c = op;
break;
}
if (++maphopcnt > 256)
error("Infinite macro loop");
} while (c != op);
 
/*
* Begin to build an image of this command for possible
* later repeat in the buffer workcmd. It will be copied
* to lastcmd by the routine setLAST
* if/when completely specified.
*/
lastcp = workcmd;
if (!vglobp)
*lastcp++ = c;
 
/*
* First level command decode.
*/
switch (c) {
 
/*
* ^L Clear screen e.g. after transmission error.
*/
 
/*
* ^R Retype screen, getting rid of @ lines.
* If in open, equivalent to ^L.
* On terminals where the right arrow key sends
* ^L we make ^R act like ^L, since there is no
* way to get ^L. These terminals (adm31, tvi)
* are intelligent so ^R is useless. Soroc
* will probably foul this up, but nobody has
* one of them.
*/
case CTRL('l'):
case CTRL('r'):
if (c == CTRL('l') || (KR && *KR==CTRL('l'))) {
vclear();
vdirty(0, vcnt);
}
if (state != VISUAL) {
/*
* Get a clean line, throw away the
* memory of what is displayed now,
* and move back onto the current line.
*/
vclean();
vcnt = 0;
vmoveto(dot, cursor, 0);
continue;
}
vredraw(WTOP);
/*
* Weird glitch -- when we enter visual
* in a very small window we may end up with
* no lines on the screen because the line
* at the top is too long. This forces the screen
* to be expanded to make room for it (after
* we have printed @'s ick showing we goofed).
*/
if (vcnt == 0)
vrepaint(cursor);
vfixcurs();
continue;
 
/*
* $ Escape just cancels the current command
* with a little feedback.
*/
case ESCAPE:
beep();
continue;
 
/*
* @ Macros. Bring in the macro and put it
* in vmacbuf, point vglobp there and punt.
*/
case '@':
c = getesc();
if (c == 0)
continue;
if (c == '@')
c = lastmac;
if (isupper(c))
c = tolower(c);
forbid(!islower(c));
lastmac = c;
vsave();
CATCH
char tmpbuf[BUFSIZ];
 
regbuf(c,tmpbuf,sizeof(vmacbuf));
macpush(tmpbuf, 1);
ONERR
lastmac = 0;
splitw = 0;
getDOT();
vrepaint(cursor);
continue;
ENDCATCH
vmacp = vmacbuf;
goto reread;
 
/*
* . Repeat the last (modifying) open/visual command.
*/
case '.':
/*
* Check that there was a last command, and
* take its count and named buffer unless they
* were given anew. Special case if last command
* referenced a numeric named buffer -- increment
* the number and go to a named buffer again.
* This allows a sequence like "1pu.u.u...
* to successively look for stuff in the kill chain
* much as one does in EMACS with C-Y and M-Y.
*/
forbid (lastcmd[0] == 0);
if (hadcnt)
lastcnt = cnt;
if (vreg)
lastreg = vreg;
else if (isdigit(lastreg) && lastreg < '9')
lastreg++;
vreg = lastreg;
cnt = lastcnt;
hadcnt = lasthad;
vglobp = lastcmd;
goto reread;
 
/*
* ^U Scroll up. A count sticks around for
* future scrolls as the scroll amount.
* Attempt to hold the indentation from the
* top of the screen (in logical lines).
*
* BUG: A ^U near the bottom of the screen
* on a dumb terminal (which can't roll back)
* causes the screen to be cleared and then
* redrawn almost as it was. In this case
* one should simply move the cursor.
*/
case CTRL('u'):
if (hadcnt)
vSCROLL = cnt;
cnt = vSCROLL;
if (state == VISUAL)
ind = vcline, cnt += ind;
else
ind = 0;
vmoving = 0;
vup(cnt, ind, 1);
vnline(NOSTR);
continue;
 
/*
* ^D Scroll down. Like scroll up.
*/
case CTRL('d'):
#ifdef TRACE
if (trace)
fprintf(trace, "before vdown in ^D, dot=%d, wdot=%d, dol=%d\n", lineno(dot), lineno(wdot), lineno(dol));
#endif
if (hadcnt)
vSCROLL = cnt;
cnt = vSCROLL;
if (state == VISUAL)
ind = vcnt - vcline - 1, cnt += ind;
else
ind = 0;
vmoving = 0;
vdown(cnt, ind, 1);
#ifdef TRACE
if (trace)
fprintf(trace, "before vnline in ^D, dot=%d, wdot=%d, dol=%d\n", lineno(dot), lineno(wdot), lineno(dol));
#endif
vnline(NOSTR);
#ifdef TRACE
if (trace)
fprintf(trace, "after vnline in ^D, dot=%d, wdot=%d, dol=%d\n", lineno(dot), lineno(wdot), lineno(dol));
#endif
continue;
 
/*
* ^E Glitch the screen down (one) line.
* Cursor left on same line in file.
*/
case CTRL('e'):
if (state != VISUAL)
continue;
if (!hadcnt)
cnt = 1;
/* Bottom line of file already on screen */
forbid(lineDOL()-lineDOT() <= vcnt-1-vcline);
ind = vcnt - vcline - 1 + cnt;
vdown(ind, ind, 1);
vnline(cursor);
continue;
 
/*
* ^Y Like ^E but up
*/
case CTRL('y'):
if (state != VISUAL)
continue;
if (!hadcnt)
cnt = 1;
forbid(lineDOT()-1<=vcline); /* line 1 already there */
ind = vcline + cnt;
vup(ind, ind, 1);
vnline(cursor);
continue;
 
 
/*
* m Mark position in mark register given
* by following letter. Return is
* accomplished via ' or `; former
* to beginning of line where mark
* was set, latter to column where marked.
*/
case 'm':
/*
* Getesc is generally used when a character
* is read as a latter part of a command
* to allow one to hit rubout/escape to cancel
* what you have typed so far. These characters
* are mapped to 0 by the subroutine.
*/
c = getesc();
if (c == 0)
continue;
 
/*
* Markreg checks that argument is a letter
* and also maps ' and ` to the end of the range
* to allow '' or `` to reference the previous
* context mark.
*/
c = markreg(c);
forbid (c == 0);
vsave();
names[c - 'a'] = (*dot &~ 01);
ncols[c - 'a'] = cursor;
anymarks = 1;
continue;
 
/*
* ^F Window forwards, with 2 lines of continuity.
* Count repeats.
*/
case CTRL('f'):
vsave();
if (vcnt > 2) {
addr = dot + (vcnt - vcline) - 2 + (cnt-1)*basWLINES;
forbid(addr > dol);
dot = addr;
vcnt = vcline = 0;
}
vzop(0, 0, '+');
continue;
 
/*
* ^B Window backwards, with 2 lines of continuity.
* Inverse of ^F.
*/
case CTRL('b'):
vsave();
if (one + vcline != dot && vcnt > 2) {
addr = dot - vcline + 2 - (cnt-1)*basWLINES;
forbid (addr <= zero);
dot = addr;
vcnt = vcline = 0;
}
vzop(0, 0, '^');
continue;
 
/*
* z Screen adjustment, taking a following character:
* z<CR> current line to top
* z<NL> like z<CR>
* z- current line to bottom
* also z+, z^ like ^F and ^B.
* A preceding count is line to use rather
* than current line. A count between z and
* specifier character changes the screen size
* for the redraw.
*
*/
case 'z':
if (state == VISUAL) {
i = vgetcnt();
if (i > 0)
vsetsiz(i);
c = getesc();
if (c == 0)
continue;
}
vsave();
vzop(hadcnt, cnt, c);
continue;
 
/*
* Y Yank lines, abbreviation for y_ or yy.
* Yanked lines can be put later if no
* changes intervene, or can be put in named
* buffers and put anytime in this session.
*/
case 'Y':
ungetkey('_');
c = 'y';
break;
 
/*
* J Join lines, 2 by default. Count is number
* of lines to join (no join operator sorry.)
*/
case 'J':
forbid (dot == dol);
if (cnt == 1)
cnt = 2;
if (cnt > (i = dol - dot + 1))
cnt = i;
vsave();
vmacchng(1);
setLAST();
cursor = strend(linebuf);
vremote(cnt, join, 0);
notenam = "join";
vmoving = 0;
killU();
vreplace(vcline, cnt, 1);
if (!*cursor && cursor > linebuf)
cursor--;
if (notecnt == 2)
notecnt = 0;
vrepaint(cursor);
continue;
 
/*
* S Substitute text for whole lines, abbrev for c_.
* Count is number of lines to change.
*/
case 'S':
ungetkey('_');
c = 'c';
break;
 
/*
* O Create a new line above current and accept new
* input text, to an escape, there.
* A count specifies, for dumb terminals when
* slowopen is not set, the number of physical
* line space to open on the screen.
*
* o Like O, but opens lines below.
*/
case 'O':
case 'o':
vmacchng(1);
voOpen(c, cnt);
continue;
 
/*
* C Change text to end of line, short for c$.
*/
case 'C':
if (*cursor) {
ungetkey('$'), c = 'c';
break;
}
goto appnd;
 
/*
* ~ Switch case of letter under cursor
*/
case '~':
{
char mbuf[4];
setLAST();
mbuf[0] = 'r';
mbuf[1] = *cursor;
mbuf[2] = cursor[1]==0 ? 0 : ' ';
mbuf[3] = 0;
if (isalpha(mbuf[1]))
mbuf[1] ^= ' '; /* toggle the case */
macpush(mbuf, 1);
}
continue;
 
 
/*
* A Append at end of line, short for $a.
*/
case 'A':
operate('$', 1);
appnd:
c = 'a';
/* fall into ... */
 
/*
* a Appends text after cursor. Text can continue
* through arbitrary number of lines.
*/
case 'a':
if (*cursor) {
if (state == HARDOPEN)
putchar(*cursor);
cursor++;
}
goto insrt;
 
/*
* I Insert at beginning of whitespace of line,
* short for ^i.
*/
case 'I':
operate('^', 1);
c = 'i';
/* fall into ... */
 
/*
* R Replace characters, one for one, by input
* (logically), like repeated r commands.
*
* BUG: This is like the typeover mode of many other
* editors, and is only rarely useful. Its
* implementation is a hack in a low level
* routine and it doesn't work very well, e.g.
* you can't move around within a R, etc.
*/
case 'R':
/* fall into... */
 
/*
* i Insert text to an escape in the buffer.
* Text is arbitrary. This command reminds of
* the i command in bare teco.
*/
case 'i':
insrt:
/*
* Common code for all the insertion commands.
* Save for redo, position cursor, prepare for append
* at command and in visual undo. Note that nothing
* is doomed, unless R when all is, and save the
* current line in a the undo temporary buffer.
*/
vmacchng(1);
setLAST();
vcursat(cursor);
prepapp();
vnoapp();
doomed = c == 'R' ? 10000 : 0;
if(FIXUNDO)
vundkind = VCHNG;
vmoving = 0;
#ifndef BIT8
CP(vutmp, linebuf);
#else
sc_strcpy(vutmp, linebuf);
#endif
 
/*
* If this is a repeated command, then suppress
* fake insert mode on dumb terminals which looks
* ridiculous and wastes lots of time even at 9600B.
*/
if (vglobp)
hold = HOLDQIK;
vappend(c, cnt, 0);
continue;
 
/*
* ^? An attention, normally a ^?, just beeps.
* If you are a vi command within ex, then
* two ATTN's will drop you back to command mode.
*/
case ATTN:
beep();
if (initev || peekkey() != ATTN)
continue;
/* fall into... */
 
/*
* ^\ A quit always gets command mode.
*/
case QUIT:
/*
* Have to be careful if we were called
* g/xxx/vi
* since a return will just start up again.
* So we simulate an interrupt.
*/
if (inglobal)
onintr();
/* fall into... */
 
#ifdef notdef
/*
* q Quit back to command mode, unless called as
* vi on command line in which case dont do it
*/
case 'q': /* quit */
if (initev) {
vsave();
CATCH
error("Q gets ex command mode, :q leaves vi");
ENDCATCH
splitw = 0;
getDOT();
vrepaint(cursor);
continue;
}
#endif
/* fall into... */
 
/*
* Q Is like q, but always gets to command mode
* even if command line invocation was as vi.
*/
case 'Q':
vsave();
/*
* If we are in the middle of a macro, throw away
* the rest and fix up undo.
* This code copied from getbr().
*/
if (vmacp) {
vmacp = 0;
if (inopen == -1) /* don't screw up undo for esc esc */
vundkind = VMANY;
inopen = 1; /* restore old setting now that macro done */
}
return;
 
 
/*
* ZZ Like :x
*/
case 'Z':
forbid(getkey() != 'Z');
oglobp = globp;
#ifndef BIT8
globp = "x";
#else
globp[0] = 'x', globp[1] = '\0';
#endif
vclrech(0);
goto gogo;
/*
* P Put back text before cursor or before current
* line. If text was whole lines goes back
* as whole lines. If part of a single line
* or parts of whole lines splits up current
* line to form many new lines.
* May specify a named buffer, or the delete
* saving buffers 1-9.
*
* p Like P but after rather than before.
*/
case 'P':
case 'p':
vmoving = 0;
#ifdef notdef
forbid (!vreg && value(UNDOMACRO) && inopen < 0);
#endif
/*
* If previous delete was partial line, use an
* append or insert to put it back so as to
* use insert mode on intelligent terminals.
*/
if (!vreg && DEL[0]) {
forbid ((DEL[0] & (QUOTE|TRIM)) == OVERBUF);
vglobp = DEL;
ungetkey(c == 'p' ? 'a' : 'i');
goto reread;
}
 
/*
* If a register wasn't specified, then make
* sure there is something to put back.
*/
forbid (!vreg && unddol == dol);
/*
* If we just did a macro the whole buffer is in
* the undo save area. We don't want to put THAT.
*/
forbid (vundkind == VMANY && undkind==UNDALL);
vsave();
vmacchng(1);
setLAST();
i = 0;
if (vreg && partreg(vreg) || !vreg && pkill[0]) {
/*
* Restoring multiple lines which were partial
* lines; will leave cursor in middle
* of line after shoving restored text in to
* split the current line.
*/
i++;
if (c == 'p' && *cursor)
cursor++;
} else {
/*
* In whole line case, have to back up dot
* for P; also want to clear cursor so
* cursor will eventually be positioned
* at the beginning of the first put line.
*/
cursor = 0;
if (c == 'P') {
dot--, vcline--;
c = 'p';
}
}
killU();
 
/*
* The call to putreg can potentially
* bomb since there may be nothing in a named buffer.
* We thus put a catch in here. If we didn't and
* there was an error we would end up in command mode.
*/
addr = dol; /* old dol */
CATCH
vremote(1, vreg ? putreg : put, vreg);
ONERR
if (vreg == -1) {
splitw = 0;
if (op == 'P')
dot++, vcline++;
goto pfixup;
}
ENDCATCH
splitw = 0;
nlput = dol - addr + 1;
if (!i) {
/*
* Increment undap1, undap2 to make up
* for their incorrect initialization in the
* routine vremote before calling put/putreg.
*/
if (FIXUNDO)
undap1++, undap2++;
vcline++;
nlput--;
 
/*
* After a put want current line first line,
* and dot was made the last line put in code
* run so far. This is why we increment vcline
* above and decrease dot here.
*/
dot -= nlput - 1;
}
#ifdef TRACE
if (trace)
fprintf(trace, "vreplace(%d, %d, %d), undap1=%d, undap2=%d, dot=%d\n", vcline, i, nlput, lineno(undap1), lineno(undap2), lineno(dot));
#endif
vreplace(vcline, i, nlput);
if (state != VISUAL) {
/*
* Special case in open mode.
* Force action on the screen when a single
* line is put even if it is identical to
* the current line, e.g. on YP; otherwise
* you can't tell anything happened.
*/
vjumpto(dot, cursor, '.');
continue;
}
pfixup:
vrepaint(cursor);
vfixcurs();
continue;
 
/*
* ^^ Return to previous file.
* Like a :e #, and thus can be used after a
* "No Write" diagnostic.
*/
case CTRL('^'):
forbid (hadcnt);
vsave();
ckaw();
oglobp = globp;
if (value(AUTOWRITE))
#ifndef BIT8
globp = "e! #";
else
globp = "e #";
#else
sc_strcpy(globp, "e! #");
else
sc_strcpy(globp, "e #");
#endif
goto gogo;
 
/*
* ^] Takes word after cursor as tag, and then does
* tag command. Read ``go right to''.
*/
case CTRL(']'):
grabtag();
oglobp = globp;
#ifndef BIT8
globp = "tag";
#else
sc_strcpy(globp, "tag");
#endif
goto gogo;
 
/*
* & Like :&
*/
case '&':
oglobp = globp;
#ifndef BIT8
globp = "&";
#else
globp[0] = '&', globp[1] = '\0';
#endif
goto gogo;
/*
* ^G Bring up a status line at the bottom of
* the screen, like a :file command.
*
* BUG: Was ^S but doesn't work in cbreak mode
*/
case CTRL('g'):
oglobp = globp;
#ifndef BIT8
globp = "file";
#else
sc_strcpy(globp, "file");
#endif
gogo:
addr = dot;
vsave();
goto doinit;
 
#ifdef SIGTSTP
/*
* ^Z: suspend editor session and temporarily return
* to shell. Only works with Berkeley/IIASA process
* control in kernel.
*/
case CTRL('z'):
forbid(dosusp == 0 || !ldisc);
vsave();
oglobp = globp;
#ifndef BIT8
globp = "stop";
#else
sc_strcpy(globp, "stop");
#endif
goto gogo;
#endif
 
/*
* : Read a command from the echo area and
* execute it in command mode.
*/
case ':':
forbid (hadcnt);
vsave();
i = tchng;
addr = dot;
if (readecho(c)) {
esave[0] = 0;
goto fixup;
}
getDOT();
#ifndef BIT8
/*
* Use the visual undo buffer to store the global
* string for command mode, since it is idle right now.
*/
oglobp = globp; strcpy(vutmp, genbuf+1); globp = vutmp;
#else
oglobp = globp; sc_strcpy(vutmp, genbuf+1);
globp = vutmp;
#endif
doinit:
esave[0] = 0;
fixech();
 
/*
* Have to finagle around not to lose last
* character after this command (when run from ex
* command mode). This is clumsy.
*/
d = peekc; ungetchar(0);
if (shouldpo) {
/*
* So after a "Hit return..." ":", we do
* another "Hit return..." the next time
*/
pofix();
shouldpo = 0;
}
CATCH
/*
* Save old values of options so we can
* notice when they change; switch into
* cooked mode so we are interruptible.
*/
onumber = value(NUMBER);
olist = value(LIST);
OPline = Pline;
OPutchar = Putchar;
#ifndef CBREAK
vcook();
#endif
commands(1, 1);
if (dot == zero && dol > zero)
dot = one;
#ifndef CBREAK
vraw();
#endif
ONERR
#ifndef CBREAK
vraw();
#endif
copy(esave, vtube[WECHO], TUBECOLS);
ENDCATCH
fixol();
Pline = OPline;
Putchar = OPutchar;
ungetchar(d);
globp = oglobp;
 
/*
* If we ended up with no lines in the buffer, make
* a line, and don't consider the buffer changed.
*/
if (dot == zero) {
fixzero();
sync();
}
splitw = 0;
 
/*
* Special case: did list/number options change?
*/
if (onumber != value(NUMBER))
setnumb(value(NUMBER));
if (olist != value(LIST))
setlist(value(LIST));
 
fixup:
/*
* If a change occurred, other than
* a write which clears changes, then
* we should allow an undo even if .
* didn't move.
*
* BUG: You can make this wrong by
* tricking around with multiple commands
* on one line of : escape, and including
* a write command there, but its not
* worth worrying about.
*/
if (FIXUNDO && tchng && tchng != i)
vundkind = VMANY, cursor = 0;
 
/*
* If we are about to do another :, hold off
* updating of screen.
*/
if (vcnt < 0 && Peekkey == ':') {
getDOT();
shouldpo = 1;
continue;
}
shouldpo = 0;
 
/*
* In the case where the file being edited is
* new; e.g. if the initial state hasn't been
* saved yet, then do so now.
*/
if (unddol == truedol) {
vundkind = VNONE;
Vlines = lineDOL();
if (!inglobal)
savevis();
addr = zero;
vcnt = 0;
if (esave[0] == 0)
copy(esave, vtube[WECHO], TUBECOLS);
}
 
/*
* If the current line moved reset the cursor position.
*/
if (dot != addr) {
vmoving = 0;
cursor = 0;
}
 
/*
* If current line is not on screen or if we are
* in open mode and . moved, then redraw.
*/
i = vcline + (dot - addr);
if (i < 0 || i >= vcnt && i >= -vcnt || state != VISUAL && dot != addr) {
if (state == CRTOPEN)
vup1();
if (vcnt > 0)
vcnt = 0;
vjumpto(dot, (char *) 0, '.');
} else {
/*
* Current line IS on screen.
* If we did a [Hit return...] then
* restore vcnt and clear screen if in visual
*/
vcline = i;
if (vcnt < 0) {
vcnt = -vcnt;
if (state == VISUAL)
vclear();
else if (state == CRTOPEN) {
vcnt = 0;
}
}
 
/*
* Limit max value of vcnt based on $
*/
i = vcline + lineDOL() - lineDOT() + 1;
if (i < vcnt)
vcnt = i;
/*
* Dirty and repaint.
*/
vdirty(0, LINES);
vrepaint(cursor);
}
 
/*
* If in visual, put back the echo area
* if it was clobberred.
*/
if (state == VISUAL) {
int sdc = destcol, sdl = destline;
 
splitw++;
vigoto(WECHO, 0);
for (i = 0; i < TUBECOLS - 1; i++) {
if (esave[i] == 0)
break;
vputchar(esave[i]);
}
splitw = 0;
vgoto(sdl, sdc);
}
continue;
 
/*
* u undo the last changing command.
*/
case 'u':
vundo(1);
continue;
 
/*
* U restore current line to initial state.
*/
case 'U':
vUndo();
continue;
 
fonfon:
beep();
vmacp = 0;
inopen = 1; /* might have been -1 */
continue;
}
 
/*
* Rest of commands are decoded by the operate
* routine.
*/
operate(c, cnt);
}
}
 
/*
* Grab the word after the cursor so we can look for it as a tag.
*/
grabtag()
{
register char *cp, *dp;
 
cp = vpastwh(cursor);
if (*cp) {
dp = lasttag;
do {
if (dp < &lasttag[sizeof lasttag - 2])
*dp++ = *cp;
cp++;
} while (isalpha(*cp) || isdigit(*cp) || *cp == '_'
#ifdef LISPCODE
|| (value(LISP) && *cp == '-')
#endif LISPCODE
);
*dp++ = 0;
}
}
 
/*
* Before appending lines, set up addr1 and
* the command mode undo information.
*/
prepapp()
{
 
addr1 = dot;
deletenone();
addr1++;
appendnone();
}
 
/*
* Execute function f with the address bounds addr1
* and addr2 surrounding cnt lines starting at dot.
*/
vremote(cnt, f, arg)
int cnt, (*f)(), arg;
{
register int oing = inglobal;
 
addr1 = dot;
addr2 = dot + cnt - 1;
inglobal = 0;
if (FIXUNDO)
undap1 = undap2 = dot;
(*f)(arg);
inglobal = oing;
if (FIXUNDO)
vundkind = VMANY;
vmcurs = 0;
}
 
/*
* Save the current contents of linebuf, if it has changed.
*/
vsave()
{
char temp[LBSIZE];
 
CP(temp, linebuf);
if (FIXUNDO && vundkind == VCHNG || vundkind == VCAPU) {
/*
* If the undo state is saved in the temporary buffer
* vutmp, then we sync this into the temp file so that
* we will be able to undo even after we have moved off
* the line. It would be possible to associate a line
* with vutmp but we assume that vutmp is only associated
* with line dot (e.g. in case ':') above, so beware.
*/
prepapp();
#ifndef BIT8
strcLIN(vutmp);
#else
cs_strcpy(linebuf, vutmp);
#endif
putmark(dot);
vremote(1, yank, 0);
vundkind = VMCHNG;
notecnt = 0;
undkind = UNDCHANGE;
}
/*
* Get the line out of the temp file and do nothing if it hasn't
* changed. This may seem like a loss, but the line will
* almost always be in a read buffer so this may well avoid disk i/o.
*/
getDOT();
if (strcmp(linebuf, temp) == 0)
return;
strcLIN(temp);
putmark(dot);
}
 
#undef forbid
#define forbid(a) if (a) { beep(); return; }
 
/*
* Do a z operation.
* Code here is rather long, and very uninteresting.
*/
vzop(hadcnt, cnt, c)
bool hadcnt;
int cnt;
register int c;
{
register line *addr;
 
if (state != VISUAL) {
/*
* Z from open; always like a z=.
* This code is a mess and should be cleaned up.
*/
vmoveitup(1, 1);
vgoto(outline, 0);
ostop(normf);
setoutt();
addr2 = dot;
vclear();
destline = WECHO;
zop2(Xhadcnt ? Xcnt : value(WINDOW) - 1, '=');
if (state == CRTOPEN)
putnl();
putNFL();
termreset();
Outchar = vputchar;
ignore(ostart());
vcnt = 0;
outline = destline = 0;
vjumpto(dot, cursor, 0);
return;
}
if (hadcnt) {
addr = zero + cnt;
if (addr < one)
addr = one;
if (addr > dol)
addr = dol;
markit(addr);
} else
switch (c) {
 
case '+':
addr = dot + vcnt - vcline;
break;
 
case '^':
addr = dot - vcline - 1;
forbid (addr < one);
c = '-';
break;
 
default:
addr = dot;
break;
}
switch (c) {
 
case '.':
case '-':
break;
 
case '^':
forbid (addr <= one);
break;
 
case '+':
forbid (addr >= dol);
/* fall into ... */
 
case CR:
case NL:
c = CR;
break;
 
default:
beep();
return;
}
vmoving = 0;
vjumpto(addr, NOSTR, c);
}
/ports/trunk/editors/rvi/ex_vops2.c
0,0 → 1,927
/*
* Copyright (c) 1980 Regents of the University of California.
* All rights reserved. The Berkeley software License Agreement
* specifies the terms and conditions for redistribution.
*/
 
#if !defined(lint) && defined(DOSCCS)
static char *sccsid = "@(#)ex_vops2.c 6.8 (Berkeley) 6/7/85";
#endif
 
#include "ex.h"
#include "ex_tty.h"
#include "ex_vis.h"
 
/*
* Low level routines for operations sequences,
* and mostly, insert mode (and a subroutine
* to read an input line, including in the echo area.)
*/
extern char *vUA1, *vUA2; /* mjm: extern; also in ex_vops.c */
extern char *vUD1, *vUD2; /* mjm: extern; also in ex_vops.c */
 
/*
* Obleeperate characters in hardcopy
* open with \'s.
*/
bleep(i, cp)
register int i;
char *cp;
{
 
i -= column(cp);
do
#ifndef ISO
putchar('\\' | QUOTE);
#else
putchar('\\');
#endif
while (--i >= 0);
rubble = 1;
}
 
/*
* Common code for middle part of delete
* and change operating on parts of lines.
*/
vdcMID()
{
register char *cp;
 
squish();
setLAST();
if (FIXUNDO)
#ifndef BIT8
vundkind = VCHNG, CP(vutmp, linebuf);
#else
vundkind = VCHNG, sc_strcpy(vutmp, linebuf);
#endif
if (wcursor < cursor)
cp = wcursor, wcursor = cursor, cursor = cp;
vUD1 = vUA1 = vUA2 = cursor; vUD2 = wcursor;
return (column(wcursor - 1));
}
 
/*
* Take text from linebuf and stick it
* in the VBSIZE buffer BUF. Used to save
* deleted text of part of line.
*/
takeout(BUF)
char *BUF;
{
register char *cp;
 
if (wcursor < linebuf)
wcursor = linebuf;
if (cursor == wcursor) {
beep();
return;
}
if (wcursor < cursor) {
cp = wcursor;
wcursor = cursor;
cursor = cp;
}
setBUF(BUF);
if ((BUF[0] & (QUOTE|TRIM)) == OVERBUF)
beep();
}
 
/*
* Are we at the end of the printed representation of the
* line? Used internally in hardcopy open.
*/
ateopr()
{
register int i, c;
#ifndef BIT8
register char *cp = vtube[destline] + destcol;
#else
register short *cp = vtube[destline] + destcol;
#endif
 
for (i = WCOLS - destcol; i > 0; i--) {
c = *cp++;
if (c == 0)
return (1);
#ifdef ISO
if ((c & QUOTE) && !niso(c))
return (0);
#endif
if (c != ' ' && (c & QUOTE) == 0)
return (0);
}
return (1);
}
 
/*
* Append.
*
* This routine handles the top level append, doing work
* as each new line comes in, and arranging repeatability.
* It also handles append with repeat counts, and calculation
* of autoindents for new lines.
*/
bool vaifirst;
bool gobbled;
char *ogcursor;
 
vappend(ch, cnt, indent)
int ch; /* mjm: char --> int */
int cnt, indent;
{
register int i;
register char *gcursor;
bool escape;
int repcnt, savedoomed;
short oldhold = hold;
long oldmask;
 
/*
* Before a move in hardopen when the line is dirty
* or we are in the middle of the printed representation,
* we retype the line to the left of the cursor so the
* insert looks clean.
*/
if (ch != 'o' && state == HARDOPEN && (rubble || !ateopr())) {
rubble = 1;
gcursor = cursor;
i = *gcursor;
*gcursor = ' ';
wcursor = gcursor;
vmove();
*gcursor = i;
}
vaifirst = indent == 0;
 
/*
* Handle replace character by (eventually)
* limiting the number of input characters allowed
* in the vgetline routine.
*/
if (ch == 'r')
repcnt = 2;
else
repcnt = 0;
 
/*
* If an autoindent is specified, then
* generate a mixture of blanks to tabs to implement
* it and place the cursor after the indent.
* Text read by the vgetline routine will be placed in genbuf,
* so the indent is generated there.
*/
if (value(AUTOINDENT) && indent != 0) {
gcursor = genindent(indent);
*gcursor = 0;
vgotoCL(qcolumn(cursor - 1, genbuf));
} else {
gcursor = genbuf;
*gcursor = 0;
if (ch == 'o')
vfixcurs();
}
 
/*
* Prepare for undo. Pointers delimit inserted portion of line.
*/
vUA1 = vUA2 = cursor;
 
/*
* If we are not in a repeated command and a ^@ comes in
* then this means the previous inserted text.
* If there is none or it was too long to be saved,
* then beep() and also arrange to undo any damage done
* so far (e.g. if we are a change.)
*/
if ((vglobp && *vglobp == 0) || peekbr()) {
if ((INS[0] & (QUOTE|TRIM)) == OVERBUF) {
beep();
if (!splitw)
ungetkey('u');
doomed = 0;
hold = oldhold;
return;
}
/*
* Unread input from INS.
* An escape will be generated at end of string.
* Hold off n^^2 type update on dumb terminals.
*/
vglobp = INS;
hold |= HOLDQIK;
} else if (vglobp == 0)
/*
* Not a repeated command, get
* a new inserted text for repeat.
*/
INS[0] = 0;
 
/*
* For wrapmargin to hack away second space after a '.'
* when the first space caused a line break we keep
* track that this happened in gobblebl, which says
* to gobble up a blank silently.
*/
gobblebl = 0;
 
oldmask = sigblock(sigmask(SIGWINCH));
/*
* Text gathering loop.
* New text goes into genbuf starting at gcursor.
* cursor preserves place in linebuf where text will eventually go.
*/
if (*cursor == 0 || state == CRTOPEN)
hold |= HOLDROL;
for (;;) {
if (ch == 'r' && repcnt == 0)
escape = 0;
else {
gcursor = vgetline(repcnt, gcursor, &escape, ch);
 
/*
* After an append, stick information
* about the ^D's and ^^D's and 0^D's in
* the repeated text buffer so repeated
* inserts of stuff indented with ^D as backtab's
* can work.
*/
if (HADUP)
addtext("^");
else if (HADZERO)
addtext("0");
while (CDCNT > 0)
addtext("\204"), CDCNT--;
if (gobbled)
addtext(" ");
addtext(ogcursor);
}
repcnt = 0;
 
/*
* Smash the generated and preexisting indents together
* and generate one cleanly made out of tabs and spaces
* if we are using autoindent.
*/
if (!vaifirst && value(AUTOINDENT)) {
i = fixindent(indent);
if (!HADUP)
indent = i;
gcursor = strend(genbuf);
}
 
/*
* Limit the repetition count based on maximum
* possible line length; do output implied
* by further count (> 1) and cons up the new line
* in linebuf.
*/
cnt = vmaxrep(ch, cnt);
CP(gcursor + 1, cursor);
do {
CP(cursor, genbuf);
if (cnt > 1) {
int oldhold = hold;
 
Outchar = vinschar;
hold |= HOLDQIK;
printf("%s", genbuf);
hold = oldhold;
Outchar = vputchar;
}
cursor += gcursor - genbuf;
} while (--cnt > 0);
endim();
vUA2 = cursor;
if (escape != '\n')
CP(cursor, gcursor + 1);
 
/*
* If doomed characters remain, clobber them,
* and reopen the line to get the display exact.
*/
if (state != HARDOPEN) {
DEPTH(vcline) = 0;
savedoomed = doomed;
if (doomed > 0) {
register int cind = cindent();
 
physdc(cind, cind + doomed);
doomed = 0;
}
i = vreopen(LINE(vcline), lineDOT(), vcline);
#ifdef TRACE
if (trace)
fprintf(trace, "restoring doomed from %d to %d\n", doomed, savedoomed);
#endif
if (ch == 'R')
doomed = savedoomed;
}
 
/*
* All done unless we are continuing on to another line.
*/
if (escape != '\n')
break;
 
/*
* Set up for the new line.
* First save the current line, then construct a new
* first image for the continuation line consisting
* of any new autoindent plus the pushed ahead text.
*/
killU();
addtext(gobblebl ? " " : "\n");
vsave();
cnt = 1;
if (value(AUTOINDENT)) {
#ifdef LISPCODE
if (value(LISP))
indent = lindent(dot + 1);
else
#endif
if (!HADUP && vaifirst)
indent = whitecnt(linebuf);
vaifirst = 0;
strcLIN(vpastwh(gcursor + 1));
gcursor = genindent(indent);
*gcursor = 0;
if (gcursor + strlen(linebuf) > &genbuf[LBSIZE - 2])
gcursor = genbuf;
CP(gcursor, linebuf);
} else {
CP(genbuf, gcursor + 1);
gcursor = genbuf;
}
 
/*
* If we started out as a single line operation and are now
* turning into a multi-line change, then we had better yank
* out dot before it changes so that undo will work
* correctly later.
*/
if (FIXUNDO && vundkind == VCHNG) {
vremote(1, yank, 0);
undap1--;
}
 
/*
* Now do the append of the new line in the buffer,
* and update the display. If slowopen
* we don't do very much.
*/
vdoappend(genbuf);
vundkind = VMANYINS;
vcline++;
if (state != VISUAL)
vshow(dot, NOLINE);
else {
i += LINE(vcline - 1);
vopen(dot, i);
if (value(SLOWOPEN))
vscrap();
else
vsync1(LINE(vcline));
}
strcLIN(gcursor);
*gcursor = 0;
cursor = linebuf;
vgotoCL(qcolumn(cursor - 1, genbuf));
}
 
/*
* All done with insertion, position the cursor
* and sync the screen.
*/
hold = oldhold;
if (cursor > linebuf)
cursor--;
if (state != HARDOPEN)
vsyncCL();
else if (cursor > linebuf)
back1();
doomed = 0;
wcursor = cursor;
vmove();
(void)sigsetmask(oldmask);
}
 
/*
* Subroutine for vgetline to back up a single character position,
* backwards around end of lines (vgoto can't hack columns which are
* less than 0 in general).
*/
back1()
{
 
vgoto(destline - 1, WCOLS + destcol - 1);
}
 
/*
* Get a line into genbuf after gcursor.
* Cnt limits the number of input characters
* accepted and is used for handling the replace
* single character command. Aescaped is the location
* where we stick a termination indicator (whether we
* ended with an ESCAPE or a newline/return.
*
* We do erase-kill type processing here and also
* are careful about the way we do this so that it is
* repeatable. (I.e. so that your kill doesn't happen,
* when you repeat an insert if it was escaped with \ the
* first time you did it. commch is the command character
* involved, including the prompt for readline.
*/
char *
vgetline(cnt, gcursor, aescaped, commch)
int cnt;
register char *gcursor;
bool *aescaped;
char commch;
{
register int c, ch;
register char *cp;
int x, y, iwhite, backsl=0;
char *iglobp;
char cstr[2];
int (*OO)() = Outchar;
 
/*
* Clear the output state and counters
* for autoindent backwards motion (counts of ^D, etc.)
* Remember how much white space at beginning of line so
* as not to allow backspace over autoindent.
*/
*aescaped = 0;
ogcursor = gcursor;
flusho();
CDCNT = 0;
HADUP = 0;
HADZERO = 0;
gobbled = 0;
iwhite = whitecnt(genbuf);
iglobp = vglobp;
 
/*
* Carefully avoid using vinschar in the echo area.
*/
if (splitw)
Outchar = vputchar;
else {
Outchar = vinschar;
vprepins();
}
for (;;) {
backsl = 0;
if (gobblebl)
gobblebl--;
if (cnt != 0) {
cnt--;
if (cnt == 0)
goto vadone;
}
c = getkey();
if (c != ATTN)
c &= (QUOTE|TRIM);
ch = c;
maphopcnt = 0;
if (vglobp == 0 && Peekkey == 0 && commch != 'r')
while ((ch = map(c, immacs)) != c) {
c = ch;
if (!value(REMAP))
break;
if (++maphopcnt > 256)
error("Infinite macro loop");
}
if (!iglobp) {
 
/*
* Erase-kill type processing.
* Only happens if we were not reading
* from untyped input when we started.
* Map users erase to ^H, kill to -1 for switch.
*/
#ifndef USG3TTY
if (c == tty.sg_erase)
c = CTRL('h');
else if (c == tty.sg_kill)
c = -1;
#else
if (c == tty.c_cc[VERASE])
c = CTRL('h');
else if (c == tty.c_cc[VKILL])
c = -1;
#endif
switch (c) {
 
/*
* ^? Interrupt drops you back to visual
* command mode with an unread interrupt
* still in the input buffer.
*
* ^\ Quit does the same as interrupt.
* If you are a ex command rather than
* a vi command this will drop you
* back to command mode for sure.
*/
case ATTN:
case QUIT:
ungetkey(c);
goto vadone;
 
/*
* ^H Backs up a character in the input.
*
* BUG: Can't back around line boundaries.
* This is hard because stuff has
* already been saved for repeat.
*/
case CTRL('h'):
bakchar:
cp = gcursor - 1;
if (cp < ogcursor) {
if (splitw) {
/*
* Backspacing over readecho
* prompt. Pretend delete but
* don't beep.
*/
ungetkey(c);
goto vadone;
}
beep();
continue;
}
goto vbackup;
 
/*
* ^W Back up a white/non-white word.
*/
case CTRL('w'):
wdkind = 1;
for (cp = gcursor; cp > ogcursor && isspace(cp[-1]); cp--)
continue;
for (c = wordch(cp - 1);
cp > ogcursor && wordof(c, cp - 1); cp--)
continue;
goto vbackup;
 
/*
* users kill Kill input on this line, back to
* the autoindent.
*/
case -1:
cp = ogcursor;
vbackup:
if (cp == gcursor) {
beep();
continue;
}
endim();
*cp = 0;
c = cindent();
vgotoCL(qcolumn(cursor - 1, genbuf));
if (doomed >= 0)
doomed += c - cindent();
gcursor = cp;
continue;
 
/*
* \ Followed by erase or kill
* maps to just the erase or kill.
*/
case '\\':
x = destcol, y = destline;
putchar('\\');
vcsync();
c = getkey();
#ifndef USG3TTY
if (c == tty.sg_erase || c == tty.sg_kill)
#else
if (c == tty.c_cc[VERASE]
|| c == tty.c_cc[VKILL])
#endif
{
vgoto(y, x);
if (doomed >= 0)
doomed++;
goto def;
}
ungetkey(c), c = '\\';
backsl = 1;
break;
 
/*
* ^Q Super quote following character
* Only ^@ is verboten (trapped at
* a lower level) and \n forces a line
* split so doesn't really go in.
*
* ^V Synonym for ^Q
*/
case CTRL('q'):
case CTRL('v'):
x = destcol, y = destline;
putchar('^');
vgoto(y, x);
c = getkey();
#ifdef TIOCSETC
if (c == ATTN)
c = nttyc.t_intrc;
#endif
if (c != NL) {
if (doomed >= 0)
doomed++;
goto def;
}
break;
}
}
 
/*
* If we get a blank not in the echo area
* consider splitting the window in the wrapmargin.
*/
if (c != NL && !splitw) {
if (c == ' ' && gobblebl) {
gobbled = 1;
continue;
}
if (value(WRAPMARGIN) &&
(outcol >= OCOLUMNS - value(WRAPMARGIN) ||
backsl && outcol==0) &&
commch != 'r') {
/*
* At end of word and hit wrapmargin.
* Move the word to next line and keep going.
*/
wdkind = 1;
*gcursor++ = c;
if (backsl)
*gcursor++ = getkey();
*gcursor = 0;
/*
* Find end of previous word if we are past it.
*/
for (cp=gcursor; cp>ogcursor && isspace(cp[-1]); cp--)
;
if (outcol+(backsl?OCOLUMNS:0) - (gcursor-cp) >= OCOLUMNS - value(WRAPMARGIN)) {
/*
* Find beginning of previous word.
*/
for (; cp>ogcursor && !isspace(cp[-1]); cp--)
;
if (cp <= ogcursor) {
/*
* There is a single word that
* is too long to fit. Just
* let it pass, but beep for
* each new letter to warn
* the luser.
*/
c = *--gcursor;
*gcursor = 0;
beep();
goto dontbreak;
}
/*
* Save it for next line.
*/
macpush(cp, 0);
cp--;
}
macpush("\n", 0);
/*
* Erase white space before the word.
*/
while (cp > ogcursor && isspace(cp[-1]))
cp--; /* skip blank */
gobblebl = 3;
goto vbackup;
}
dontbreak:;
}
 
/*
* Word abbreviation mode.
*/
cstr[0] = c;
if (anyabbrs && gcursor > ogcursor && !wordch(cstr) && wordch(gcursor-1)) {
int wdtype, abno;
 
cstr[1] = 0;
wdkind = 1;
cp = gcursor - 1;
for (wdtype = wordch(cp - 1);
cp > ogcursor && wordof(wdtype, cp - 1); cp--)
;
*gcursor = 0;
for (abno=0; abbrevs[abno].mapto; abno++) {
if (eq(cp, abbrevs[abno].cap)) {
macpush(cstr, 0);
macpush(abbrevs[abno].mapto);
goto vbackup;
}
}
}
 
switch (c) {
 
/*
* ^M Except in repeat maps to \n.
*/
case CR:
if (vglobp)
goto def;
c = '\n';
/* presto chango ... */
 
/*
* \n Start new line.
*/
case NL:
*aescaped = c;
goto vadone;
 
/*
* escape End insert unless repeat and more to repeat.
*/
case ESCAPE:
if (lastvgk)
goto def;
goto vadone;
 
/*
* ^D Backtab.
* ^T Software forward tab.
*
* Unless in repeat where this means these
* were superquoted in.
*/
case CTRL('d'):
case CTRL('t'):
if (vglobp)
goto def;
/* fall into ... */
 
/*
* ^D|QUOTE Is a backtab (in a repeated command).
*/
case CTRL('d') | QUOTE:
*gcursor = 0;
cp = vpastwh(genbuf);
c = whitecnt(genbuf);
if (ch == CTRL('t')) {
/*
* ^t just generates new indent replacing
* current white space rounded up to soft
* tab stop increment.
*/
if (cp != gcursor)
/*
* BUG: Don't hack ^T except
* right after initial
* white space.
*/
continue;
cp = genindent(iwhite = backtab(c + value(SHIFTWIDTH) + 1));
ogcursor = cp;
goto vbackup;
}
/*
* ^D works only if we are at the (end of) the
* generated autoindent. We count the ^D for repeat
* purposes.
*/
if (c == iwhite && c != 0)
if (cp == gcursor) {
iwhite = backtab(c);
CDCNT++;
ogcursor = cp = genindent(iwhite);
goto vbackup;
} else if (&cp[1] == gcursor &&
(*cp == '^' || *cp == '0')) {
/*
* ^^D moves to margin, then back
* to current indent on next line.
*
* 0^D moves to margin and then
* stays there.
*/
HADZERO = *cp == '0';
ogcursor = cp = genbuf;
HADUP = 1 - HADZERO;
CDCNT = 1;
endim();
back1();
vputchar(' ');
goto vbackup;
}
if (vglobp && vglobp - iglobp >= 2 &&
(vglobp[-2] == '^' || vglobp[-2] == '0')
&& gcursor == ogcursor + 1)
goto bakchar;
continue;
 
default:
/*
* Possibly discard control inputs.
*/
if (!vglobp && junk(c)) {
beep();
continue;
}
def:
if (!backsl) {
int cnt;
putchar(c);
flush();
}
if (gcursor > &genbuf[LBSIZE - 2])
error("Line too long");
#ifndef ISO
*gcursor++ = c & TRIM;
#else
if (niso(c))
*gcursor++ = c & TRIM;
else
*gcursor++ = c;
#endif
vcsync();
if (value(SHOWMATCH) && !iglobp)
if (c == ')' || c == '}')
lsmatch(gcursor);
continue;
}
}
vadone:
*gcursor = 0;
if (Outchar != termchar)
Outchar = OO;
endim();
return (gcursor);
}
 
int vgetsplit();
char *vsplitpt;
 
/*
* Append the line in buffer at lp
* to the buffer after dot.
*/
vdoappend(lp)
char *lp;
{
register int oing = inglobal;
 
vsplitpt = lp;
inglobal = 1;
ignore(append(vgetsplit, dot));
inglobal = oing;
}
 
/*
* Subroutine for vdoappend to pass to append.
*/
vgetsplit()
{
 
if (vsplitpt == 0)
return (EOF);
strcLIN(vsplitpt);
vsplitpt = 0;
return (0);
}
 
/*
* Vmaxrep determines the maximum repetitition factor
* allowed that will yield total line length less than
* LBSIZE characters and also does hacks for the R command.
*/
vmaxrep(ch, cnt)
char ch;
register int cnt;
{
register int len, replen;
 
if (cnt > LBSIZE - 2)
cnt = LBSIZE - 2;
replen = strlen(genbuf);
if (ch == 'R') {
len = strlen(cursor);
if (replen < len)
len = replen;
CP(cursor, cursor + len);
vUD2 += len;
}
len = strlen(linebuf);
if (len + cnt * replen <= LBSIZE - 2)
return (cnt);
cnt = (LBSIZE - 2 - len) / replen;
if (cnt == 0) {
vsave();
error("Line too long");
}
return (cnt);
}
/ports/trunk/editors/rvi/ex_vwind.c
0,0 → 1,469
/*
* Copyright (c) 1980 Regents of the University of California.
* All rights reserved. The Berkeley software License Agreement
* specifies the terms and conditions for redistribution.
*/
 
#if !defined(lint) && defined(DOSCCS)
static char *sccsid = "@(#)ex_vwind.c 7.3 (Berkeley) 6/7/85";
#endif
 
#include "ex.h"
#include "ex_tty.h"
#include "ex_vis.h"
 
/*
* Routines to adjust the window, showing specified lines
* in certain positions on the screen, and scrolling in both
* directions. Code here is very dependent on mode (open versus visual).
*/
 
/*
* Move in a nonlocal way to line addr.
* If it isn't on screen put it in specified context.
* New position for cursor is curs.
* Like most routines here, we vsave().
*/
vmoveto(addr, curs, context)
register line *addr;
char *curs;
char context;
{
 
markit(addr);
vsave();
vjumpto(addr, curs, context);
}
 
/*
* Vjumpto is like vmoveto, but doesn't mark previous
* context or save linebuf as current line.
*/
vjumpto(addr, curs, context)
register line *addr;
char *curs;
char context;
{
 
noteit(0);
if (context != 0)
vcontext(addr, context);
else
vshow(addr, NOLINE);
noteit(1);
vnline(curs);
}
 
/*
* Go up or down cnt (negative is up) to new position curs.
*/
vupdown(cnt, curs)
register int cnt;
char *curs;
{
 
if (cnt > 0)
vdown(cnt, 0, 0);
else if (cnt < 0)
vup(-cnt, 0, 0);
if (vcnt == 0)
vrepaint(curs);
else
vnline(curs);
}
 
/*
* Go up cnt lines, afterwards preferring to be ind
* logical lines from the top of the screen.
* If scroll, then we MUST use a scroll.
* Otherwise clear and redraw if motion is far.
*/
vup(cnt, ind, scroll)
register int cnt, ind;
bool scroll;
{
register int i, tot;
 
if (dot == one) {
beep();
return;
}
vsave();
i = lineDOT() - 1;
if (cnt > i) {
ind -= cnt - i;
if (ind < 0)
ind = 0;
cnt = i;
}
if (!scroll && cnt <= vcline) {
vshow(dot - cnt, NOLINE);
return;
}
cnt -= vcline, dot -= vcline, vcline = 0;
if (hold & HOLDWIG)
goto contxt;
if (state == VISUAL && !AL && !SR &&
cnt <= WTOP - ZERO && vfit(dot - cnt, cnt) <= WTOP - ZERO)
goto okr;
tot = WECHO - ZERO;
if (state != VISUAL || (!AL && !SR) || (!scroll && (cnt > tot || vfit(dot - cnt, cnt) > tot / 3 + 1))) {
if (ind > basWLINES / 2)
ind = basWLINES / 3;
contxt:
vcontext(dot + ind - cnt, '.');
return;
}
okr:
vrollR(cnt);
if (scroll) {
vcline += ind, dot += ind;
if (vcline >= vcnt)
dot -= vcline - vcnt + 1, vcline = vcnt - 1;
getDOT();
}
}
 
/*
* Like vup, but scrolling down.
*/
vdown(cnt, ind, scroll)
register int cnt, ind;
bool scroll;
{
register int i, tot;
 
if (dot == dol) {
beep();
return;
}
vsave();
i = dol - dot;
if (cnt > i) {
ind -= cnt - i;
if (ind < 0)
ind = 0;
cnt = i;
}
i = vcnt - vcline - 1;
if (!scroll && cnt <= i) {
vshow(dot + cnt, NOLINE);
return;
}
cnt -= i, dot += i, vcline += i;
if (hold & HOLDWIG)
goto dcontxt;
if (!scroll) {
tot = WECHO - ZERO;
if (state != VISUAL || cnt - tot > 0 || vfit(dot, cnt) > tot / 3 + 1) {
dcontxt:
vcontext(dot + cnt, '.');
return;
}
}
if (cnt > 0)
vroll(cnt);
if (state == VISUAL && scroll) {
vcline -= ind, dot -= ind;
if (vcline < 0)
dot -= vcline, vcline = 0;
getDOT();
}
}
 
/*
* Show line addr in context where on the screen.
* Work here is in determining new top line implied by
* this placement of line addr, since we always draw from the top.
*/
vcontext(addr, where)
register line *addr;
char where;
{
register line *top;
 
getline(*addr);
if (state != VISUAL)
top = addr;
else switch (where) {
 
case '^':
addr = vback(addr, basWLINES - vdepth());
getline(*addr);
/* fall into ... */
 
case '-':
top = vback(addr, basWLINES - vdepth());
getline(*addr);
break;
 
case '.':
top = vback(addr, basWLINES / 2 - vdepth());
getline(*addr);
break;
 
default:
top = addr;
break;
}
if (state == ONEOPEN && LINE(0) == WBOT)
vup1();
vcnt = vcline = 0;
vclean();
if (state == CRTOPEN)
vup1();
vshow(addr, top);
}
 
/*
* Get a clean line. If we are in a hard open
* we may be able to reuse the line we are on
* if it is blank. This is a real win.
*/
vclean()
{
 
if (state != VISUAL && state != CRTOPEN) {
destcol = 0;
if (!ateopr())
vup1();
vcnt = 0;
}
}
 
/*
* Show line addr with the specified top line on the screen.
* Top may be 0; in this case have vcontext compute the top
* (and call us recursively). Eventually, we clear the screen
* (or its open mode equivalent) and redraw.
*/
vshow(addr, top)
line *addr, *top;
{
#ifndef CBREAK
register bool fried = 0;
#endif
register int cnt = addr - dot;
register int i = vcline + cnt;
short oldhold = hold;
 
if (state != HARDOPEN && state != ONEOPEN && i >= 0 && i < vcnt) {
dot = addr;
getDOT();
vcline = i;
return;
}
if (state != VISUAL) {
dot = addr;
vopen(dot, WBOT);
return;
}
if (top == 0) {
vcontext(addr, '.');
return;
}
dot = top;
#ifndef CBREAK
if (vcookit(2))
fried++, vcook();
#endif
oldhold = hold;
hold |= HOLDAT;
vclear();
vreset(0);
vredraw(WTOP);
/* error if vcline >= vcnt ! */
vcline = addr - top;
dot = addr;
getDOT();
hold = oldhold;
vsync(LASTLINE);
#ifndef CBREAK
if (fried)
flusho(), vraw();
#endif
}
 
/*
* reset the state.
* If inecho then leave us at the beginning of the echo
* area; we are called this way in the middle of a :e escape
* from visual, e.g.
*/
vreset(inecho)
bool inecho;
{
 
vcnt = vcline = 0;
WTOP = basWTOP;
WLINES = basWLINES;
if (inecho)
splitw = 1, vgoto(WECHO, 0);
}
 
/*
* Starting from which line preceding tp uses almost (but not more
* than) cnt physical lines?
*/
line *
vback(tp, cnt)
register int cnt;
register line *tp;
{
register int d;
 
if (cnt > 0)
for (; tp > one; tp--) {
getline(tp[-1]);
d = vdepth();
if (d > cnt)
break;
cnt -= d;
}
return (tp);
}
 
/*
* How much scrolling will it take to roll cnt lines starting at tp?
*/
vfit(tp, cnt)
register line *tp;
int cnt;
{
register int j;
 
j = 0;
while (cnt > 0) {
cnt--;
getline(tp[cnt]);
j += vdepth();
}
if (tp > dot)
j -= WBOT - LASTLINE;
return (j);
}
 
/*
* Roll cnt lines onto the screen.
*/
vroll(cnt)
register int cnt;
{
#ifndef CBREAK
register bool fried = 0;
#endif
short oldhold = hold;
 
#ifdef ADEBUG
if (trace)
tfixnl(), fprintf(trace, "vroll(%d)\n", cnt);
#endif
if (state != VISUAL)
hold |= HOLDAT|HOLDROL;
if (WBOT == WECHO) {
vcnt = 0;
if (state == ONEOPEN)
vup1();
}
#ifndef CBREAK
if (vcookit(cnt))
fried++, vcook();
#endif
for (; cnt > 0 && Peekkey != ATTN; cnt--) {
dot++, vcline++;
vopen(dot, LASTLINE);
vscrap();
}
hold = oldhold;
if (state == HARDOPEN)
sethard();
vsyncCL();
#ifndef CBREAK
if (fried)
flusho(), vraw();
#endif
}
 
/*
* Roll backwards (scroll up).
*/
vrollR(cnt)
register int cnt;
{
register bool fried = 0;
short oldhold = hold;
 
#ifdef ADEBUG
if (trace)
tfixnl(), fprintf(trace, "vrollR(%d), dot=%d\n", cnt, lineDOT());
#endif
#ifndef CBREAK
if (vcookit(cnt))
fried++, vcook();
#endif
if (WBOT == WECHO)
vcnt = 0;
heldech = 0;
hold |= HOLDAT|HOLDECH;
for (; cnt > 0 && Peekkey != ATTN; cnt--) {
dot--;
vopen(dot, WTOP);
vscrap();
}
hold = oldhold;
if (heldech)
vclrech(0);
vsync(LINE(vcnt-1));
#ifndef CBREAK
if (fried)
flusho(), vraw();
#endif
}
 
/*
* Go into cooked mode (allow interrupts) during
* a scroll if we are at less than 1200 baud and not
* a 'vi' command, of if we are in a 'vi' command and the
* scroll is more than 2 full screens.
*
* BUG: An interrupt during a scroll in this way
* dumps to command mode.
*/
vcookit(cnt)
register int cnt;
{
 
return (cnt > 1 && (o_speed < B1200 && !initev || cnt > LINES * 2));
}
 
/*
* Determine displayed depth of current line.
*/
vdepth()
{
register int d;
 
d = (column(NOSTR) + WCOLS - 1 + (Putchar == listchar) + IN) / WCOLS;
#ifdef ADEBUG
if (trace)
tfixnl(), fprintf(trace, "vdepth returns %d\n", d == 0 ? 1 : d);
#endif
return (d == 0 ? 1 : d);
}
 
/*
* Move onto a new line, with cursor at position curs.
*/
vnline(curs)
char *curs;
{
 
if (curs)
wcursor = curs;
else if (vmoving)
wcursor = vfindcol(vmovcol);
else
wcursor = vskipwh(linebuf);
cursor = linebuf;
vmove();
}
/ports/trunk/editors/rvi/makeoptions
0,0 → 1,54
#
# Copyright (c) 1980 Regents of the University of California.
# All rights reserved. The Berkeley software License Agreement
# specifies the terms and conditions for redistribution.
#
# @(#)makeoptions 6.4 (Berkeley) 5/31/85
#
 
#
# remake options -- this isn't necessary unless you add/delete options
#
onintr ifintr
cat < ex_data.c > /tmp/$$.c
ex - /tmp/$$.c <<'%'
g/^#include/d
w
q
'%'
cc -E $* /tmp/$$.c >/tmp/foo.c
ex - /tmp/foo.c <<'X'
" delete all preprocessor output (# line, etc)
g/^# /d
set sh=/bin/csh
" delete junk (all but data lines)
g/^[ ]*$/d
1,/option options/d
/}/-1,$d
" get rid of all of line but option name
1,$s/[ ]*"//
1,$s/".*//
" begin kludge since options start at 0 but cat -n starts at 1
" move first to end and later move it back and renumber
1m$
%!cat -n
$t0
1s/[0-9][0-9]*/0/
" end kludge
" make #define lines
1,$s/[ ]*\([0-9][0-9]*\)[ ]*\(.*\)/#define \U\2\L \1/
" filter through expand to make it line up nice
%!expand -8\,24
" blank line and number of options.
$i
 
.
$s/e[ ].*[ ]/e NOPTS /
0a
/* sccs id @(#) ex_vars.h @(#)makeoptions 6.4 5/31/85 */
.
w! ex_vars.h
q
'X'
ifintr:
rm /tmp/foo.c
/ports/trunk/editors/rvi/tcbufsize.h
0,0 → 1,9
/*
* Define the size of the termcap buffer here.
* If using an external termcap, this should match it's buffer size.
* If using the termcap in this archive, this determines the
* maximum size of a termcap entry.
*/
 
#define TCBUFSIZE 8192
#define E_TERMCAP "/etc/termcap"
/ports/trunk/editors/rvi/termcap/tc2.c
0,0 → 1,57
/*
* Copyright (c) 1980 Regents of the University of California.
* All rights reserved. The Berkeley software License Agreement
* specifies the terms and conditions for redistribution.
*/
 
#if !defined(lint) && !defined(NOSCCS)
static char sccsid[] = "@(#)tc2.c 5.1 (Berkeley) 6/5/85";
#endif not lint
 
/*
* tc2 [term]
* Dummy program to test out termlib.
* Commands are "tcc\n" where t is type (s for string, f for flag,
* or n for number) and cc is the name of the capability.
*/
#include <stdio.h>
char buf[1024];
char *getenv(), *tgetstr();
 
main(argc, argv) char **argv; {
char *p, *q;
int rc;
char b[3], c;
char area[200];
 
if (argc < 2)
p = getenv("TERM");
else
p = argv[1];
rc = tgetent(buf,p);
for (;;) {
c = getchar();
if (c < 0)
exit(0);
b[0] = getchar();
if (b[0] < ' ')
exit(0);
b[1] = getchar();
b[2] = 0;
getchar();
switch(c) {
case 'f':
printf("%s: %d\n",b,tgetflag(b));
break;
case 'n':
printf("%s: %d\n",b,tgetnum(b));
break;
case 's':
q = area;
printf("%s: %s\n",b,tgetstr(b,&q));
break;
default:
exit(0);
}
}
}
/ports/trunk/editors/rvi/ex_vget.c
0,0 → 1,688
/*
* Copyright (c) 1980 Regents of the University of California.
* All rights reserved. The Berkeley software License Agreement
* specifies the terms and conditions for redistribution.
*/
 
#if !defined(lint) && defined(DOSCCS)
static char *sccsid = "@(#)ex_vget.c 6.8.1 (2.11BSD GTE) 12/9/94";
#endif
 
#include "ex.h"
#include "ex_tty.h"
#include "ex_vis.h"
 
/*
* Input routines for open/visual.
* We handle reading from the echo area here as well as notification on
* large changes which appears in the echo area.
*/
 
/*
* Return the key.
*/
ungetkey(c)
int c; /* mjm: char --> int */
{
 
if (Peekkey != ATTN)
Peekkey = c;
}
 
/*
* Return a keystroke, but never a ^@.
*/
getkey()
{
register int c; /* mjm: char --> int */
 
do {
c = getbr();
if (c==0)
beep();
} while (c == 0);
return (c);
}
 
/*
* Tell whether next keystroke would be a ^@.
*/
peekbr()
{
 
Peekkey = getbr();
return (Peekkey == 0);
}
 
short precbksl;
jmp_buf readbuf;
int doingread = 0;
 
/*
* Get a keystroke, including a ^@.
* If an key was returned with ungetkey, that
* comes back first. Next comes unread input (e.g.
* from repeating commands with .), and finally new
* keystrokes.
*/
getbr()
{
char ch;
register int c, d;
register char *colp;
int cnt;
#ifndef NCURSES
#define BEEHIVE
#endif
#ifdef BEEHIVE
static char Peek2key;
#endif
extern short slevel, ttyindes;
 
getATTN:
if (Peekkey) {
c = Peekkey;
Peekkey = 0;
return (c);
}
#ifdef BEEHIVE
if (Peek2key) {
c = Peek2key;
Peek2key = 0;
return (c);
}
#endif
if (vglobp) {
if (*vglobp)
return (lastvgk = *vglobp++);
lastvgk = 0;
return (ESCAPE);
}
if (vmacp) {
if (*vmacp)
return(*vmacp++);
/* End of a macro or set of nested macros */
vmacp = 0;
if (inopen == -1) /* don't screw up undo for esc esc */
vundkind = VMANY;
inopen = 1; /* restore old setting now that macro done */
vch_mac = VC_NOTINMAC;
}
flusho();
again:
if (setjmp(readbuf))
goto getATTN;
doingread = 1;
c = read(slevel == 0 ? 0 : ttyindes, &ch, 1);
doingread = 0;
if (c != 1) {
if (errno == EINTR)
goto getATTN;
error("Input read error");
}
#ifndef ISO
c = ch & TRIM;
#else
if (niso(ch))
c = ch & TRIM;
else
c = ch;
#endif
#ifdef BEEHIVE
if (XB && slevel==0 && c == ESCAPE) {
if (read(0, &Peek2key, 1) != 1)
goto getATTN;
#ifdef ISO
if (niso(Peek2key))
#endif
Peek2key &= TRIM;
switch (Peek2key) {
case 'C': /* SPOW mode sometimes sends \EC for space */
c = ' ';
Peek2key = 0;
break;
case 'q': /* f2 -> ^C */
c = CTRL('c');
Peek2key = 0;
break;
case 'p': /* f1 -> esc */
Peek2key = 0;
break;
}
}
#endif
 
#ifdef TRACE
if (trace) {
if (!techoin) {
tfixnl();
techoin = 1;
fprintf(trace, "*** Input: ");
}
tracec(c);
}
#endif
lastvgk = 0;
return (c);
}
 
/*
* Get a key, but if a delete, quit or attention
* is typed return 0 so we will abort a partial command.
*/
getesc()
{
register int c;
 
c = getkey();
switch (c) {
 
case CTRL('v'):
case CTRL('q'):
c = getkey();
return (c);
 
case ATTN:
case QUIT:
ungetkey(c);
return (0);
 
case ESCAPE:
return (0);
}
return (c);
}
 
/*
* Peek at the next keystroke.
*/
peekkey()
{
 
Peekkey = getkey();
return (Peekkey);
}
 
/*
* Read a line from the echo area, with single character prompt c.
* A return value of 1 means the user blewit or blewit away.
*/
readecho(c)
char c;
{
register char *sc = cursor;
register int (*OP)();
bool waste;
register int OPeek;
 
if (WBOT == WECHO)
vclean();
else
vclrech(0);
splitw++;
vgoto(WECHO, 0);
putchar(c);
vclreol();
vgoto(WECHO, 1);
cursor = linebuf; linebuf[0] = 0; genbuf[0] = c;
if (peekbr()) {
if (!INS[0] || (INS[0] & (QUOTE|TRIM)) == OVERBUF)
goto blewit;
vglobp = INS;
}
OP = Pline; Pline = normline;
ignore(vgetline(0, genbuf + 1, &waste, c));
if (Outchar == termchar)
putchar('\n');
vscrap();
Pline = OP;
if (Peekkey != ATTN && Peekkey != QUIT && Peekkey != CTRL('h')) {
cursor = sc;
vclreol();
return (0);
}
blewit:
OPeek = Peekkey==CTRL('h') ? 0 : Peekkey; Peekkey = 0;
splitw = 0;
vclean();
vshow(dot, NOLINE);
vnline(sc);
Peekkey = OPeek;
return (1);
}
 
/*
* A complete command has been defined for
* the purposes of repeat, so copy it from
* the working to the previous command buffer.
*/
setLAST()
{
 
if (vglobp || vmacp)
return;
lastreg = vreg;
lasthad = Xhadcnt;
lastcnt = Xcnt;
*lastcp = 0;
CP(lastcmd, workcmd);
}
 
/*
* Gather up some more text from an insert.
* If the insertion buffer oveflows, then destroy
* the repeatability of the insert.
*/
addtext(cp)
char *cp;
{
 
if (vglobp)
return;
#ifndef BIT8
addto(INS, cp);
if ((INS[0] & (QUOTE|TRIM)) == OVERBUF)
#else
if (addto(INS, cp) != 0)
#endif
lastcmd[0] = 0;
}
 
setDEL()
{
 
setBUF(DEL);
}
 
/*
* Put text from cursor upto wcursor in BUF.
*/
setBUF(BUF)
#ifndef BIT8
register char *BUF;
#else
register short *BUF;
#endif
{
register int c;
register char *wp = wcursor;
 
c = *wp;
*wp = 0;
BUF[0] = 0;
addto(BUF, cursor);
*wp = c;
}
 
#ifdef BIT8
int
#endif
addto(buf, str)
#ifndef BIT8
register char *buf, *str;
#else
register short *buf;
register char *str;
#endif
{
 
if ((buf[0] & (QUOTE|TRIM)) == OVERBUF)
#ifndef BIT8
return;
#else
return 1;
#endif
#ifndef BIT8
if (strlen(buf) + strlen(str) + 1 >= VBSIZE) {
buf[0] = OVERBUF;
return;
#else
if (s_strlen(buf) + strlen(str) + 1 >= VBSIZE) {
return 1;
#endif
}
#ifndef BIT8
ignore(strcat(buf, str));
#else
sc_strcat(buf, str);
return 0;
#endif
}
 
/*
* Note a change affecting a lot of lines, or non-visible
* lines. If the parameter must is set, then we only want
* to do this for open modes now; return and save for later
* notification in visual.
*/
noteit(must)
bool must;
{
register int sdl = destline, sdc = destcol;
 
if (notecnt < 2 || !must && state == VISUAL)
return (0);
splitw++;
if (WBOT == WECHO)
vmoveitup(1, 1);
vigoto(WECHO, 0);
printf("%d %sline", notecnt, notesgn);
if (notecnt > 1)
putchar('s');
if (*notenam) {
printf(" %s", notenam);
if (*(strend(notenam) - 1) != 'e')
putchar('e');
putchar('d');
}
vclreol();
notecnt = 0;
if (state != VISUAL)
vcnt = vcline = 0;
splitw = 0;
if (state == ONEOPEN || state == CRTOPEN)
vup1();
destline = sdl; destcol = sdc;
return (1);
}
 
/*
* Rrrrringgggggg.
* If possible, use flash (VB).
*/
beep()
{
 
if (VB)
vputp(VB, 0);
else
vputc(CTRL('g'));
}
 
/*
* Map the command input character c,
* for keypads and labelled keys which do cursor
* motions. I.e. on an adm3a we might map ^K to ^P.
* DM1520 for example has a lot of mappable characters.
*/
 
map(c,maps)
register int c;
register struct maps *maps;
{
register int d;
register char *p, *q;
char b[10]; /* Assumption: no keypad sends string longer than 10 */
 
/*
* Mapping for special keys on the terminal only.
* BUG: if there's a long sequence and it matches
* some chars and then misses, we lose some chars.
*
* For this to work, some conditions must be met.
* 1) Keypad sends SHORT (2 or 3 char) strings
* 2) All strings sent are same length & similar
* 3) The user is unlikely to type the first few chars of
* one of these strings very fast.
* Note: some code has been fixed up since the above was laid out,
* so conditions 1 & 2 are probably not required anymore.
* However, this hasn't been tested with any first char
* that means anything else except escape.
*/
#ifdef MDEBUG
if (trace)
fprintf(trace,"map(%c): ",c);
#endif
/*
* If c==0, the char came from getesc typing escape. Pass it through
* unchanged. 0 messes up the following code anyway.
*/
if (c==0)
return(0);
 
b[0] = c;
b[1] = 0;
for (d=0; maps[d].mapto; d++) {
#ifdef MDEBUG
if (trace)
fprintf(trace,"\ntry '%s', ",maps[d].cap);
#endif
if (p = maps[d].cap) {
for (q=b; *p; p++, q++) {
#ifdef MDEBUG
if (trace)
fprintf(trace,"q->b[%d], ",q-b);
#endif
if (*q==0) {
/*
* Is there another char waiting?
*
* This test is oversimplified, but
* should work mostly. It handles the
* case where we get an ESCAPE that
* wasn't part of a keypad string.
*/
if ((c=='#' ? peekkey() : fastpeekkey()) == 0) {
#ifdef MDEBUG
if (trace)
fprintf(trace,"fpk=0: will return '%c'",c);
#endif
/*
* Nothing waiting. Push back
* what we peeked at & return
* failure (c).
*
* We want to be able to undo
* commands, but it's nonsense
* to undo part of an insertion
* so if in input mode don't.
*/
#ifdef MDEBUG
if (trace)
fprintf(trace, "Call macpush, b %d %d %d\n", b[0], b[1], b[2]);
#endif
macpush(&b[1],maps == arrows);
#ifdef MDEBUG
if (trace)
fprintf(trace, "return %d\n", c);
#endif
return(c);
}
*q = getkey();
q[1] = 0;
}
if (*p != *q)
goto contin;
}
macpush(maps[d].mapto,maps == arrows);
c = getkey();
#ifdef MDEBUG
if (trace)
fprintf(trace,"Success: push(%s), return %c",maps[d].mapto, c);
#endif
return(c); /* first char of map string */
contin:;
}
}
#ifdef MDEBUG
if (trace)
fprintf(trace,"Fail: push(%s), return %c", &b[1], c);
#endif
macpush(&b[1],0);
return(c);
}
 
/*
* Push st onto the front of vmacp. This is tricky because we have to
* worry about where vmacp was previously pointing. We also have to
* check for overflow (which is typically from a recursive macro)
* Finally we have to set a flag so the whole thing can be undone.
* canundo is 1 iff we want to be able to undo the macro. This
* is false for, for example, pushing back lookahead from fastpeekkey(),
* since otherwise two fast escapes can clobber our undo.
*/
macpush(st, canundo)
char *st;
int canundo;
{
char tmpbuf[BUFSIZ];
 
if (st==0 || *st==0)
return;
#ifdef MDEBUG
if (trace)
fprintf(trace, "macpush(%s), canundo=%d\n",st,canundo);
#endif
if ((vmacp ? strlen(vmacp) : 0) + strlen(st) > BUFSIZ)
error("Macro too long@ - maybe recursive?");
if (vmacp) {
strcpy(tmpbuf, vmacp);
if (!FIXUNDO)
canundo = 0; /* can't undo inside a macro anyway */
}
strcpy(vmacbuf, st);
if (vmacp)
strcat(vmacbuf, tmpbuf);
vmacp = vmacbuf;
/* arrange to be able to undo the whole macro */
if (canundo) {
#ifdef notdef
otchng = tchng;
vsave();
saveall();
inopen = -1; /* no need to save since it had to be 1 or -1 before */
vundkind = VMANY;
#endif
vch_mac = VC_NOCHANGE;
}
}
 
#ifdef TRACE
visdump(s)
char *s;
{
register int i;
 
if (!trace) return;
 
fprintf(trace, "\n%s: basWTOP=%d, basWLINES=%d, WTOP=%d, WBOT=%d, WLINES=%d, WCOLS=%d, WECHO=%d\n",
s, basWTOP, basWLINES, WTOP, WBOT, WLINES, WCOLS, WECHO);
fprintf(trace, " vcnt=%d, vcline=%d, cursor=%d, wcursor=%d, wdot=%d\n",
vcnt, vcline, cursor-linebuf, wcursor-linebuf, wdot-zero);
for (i=0; i<TUBELINES; i++)
if (vtube[i] && *vtube[i])
fprintf(trace, "%d: '%s'\n", i, vtube[i]);
tvliny();
}
 
vudump(s)
char *s;
{
register line *p;
char savelb[1024];
 
if (!trace) return;
 
fprintf(trace, "\n%s: undkind=%d, vundkind=%d, unddel=%d, undap1=%d, undap2=%d,\n",
s, undkind, vundkind, lineno(unddel), lineno(undap1), lineno(undap2));
fprintf(trace, " undadot=%d, dot=%d, dol=%d, unddol=%d, truedol=%d\n",
lineno(undadot), lineno(dot), lineno(dol), lineno(unddol), lineno(truedol));
fprintf(trace, " [\n");
CP(savelb, linebuf);
fprintf(trace, "linebuf = '%s'\n", linebuf);
for (p=zero+1; p<=truedol; p++) {
fprintf(trace, "%o ", *p);
getline(*p);
fprintf(trace, "'%s'\n", linebuf);
}
fprintf(trace, "]\n");
CP(linebuf, savelb);
}
#endif
 
/*
* Get a count from the keyed input stream.
* A zero count is indistinguishable from no count.
*/
vgetcnt()
{
register int c, cnt;
 
cnt = 0;
for (;;) {
c = getkey();
if (!isdigit(c))
break;
cnt *= 10, cnt += c - '0';
}
ungetkey(c);
Xhadcnt = 1;
Xcnt = cnt;
return(cnt);
}
 
/*
* fastpeekkey is just like peekkey but insists the character come in
* fast (within 1 second). This will succeed if it is the 2nd char of
* a machine generated sequence (such as a function pad from an escape
* flavor terminal) but fail for a human hitting escape then waiting.
*/
fastpeekkey()
{
int trapalarm();
int (*Oint)();
register int c;
 
/*
* If the user has set notimeout, we wait forever for a key.
* If we are in a macro we do too, but since it's already
* buffered internally it will return immediately.
* In other cases we force this to die in 1 second.
* This is pretty reliable (VMUNIX rounds it to .5 - 1.5 secs,
* but UNIX truncates it to 0 - 1 secs) but due to system delays
* there are times when arrow keys or very fast typing get counted
* as separate. notimeout is provided for people who dislike such
* nondeterminism.
*/
#ifdef MDEBUG
if (trace)
fprintf(trace,"\nfastpeekkey: ",c);
#endif
Oint = signal(SIGINT, trapalarm);
if (value(TIMEOUT) && inopen >= 0) {
signal(SIGALRM, trapalarm);
#ifdef MDEBUG
alarm(10);
if (trace)
fprintf(trace, "set alarm ");
#else
alarm(1);
#endif
}
CATCH
c = peekkey();
#ifdef MDEBUG
if (trace)
fprintf(trace,"[OK]",c);
#endif
alarm(0);
ONERR
c = 0;
#ifdef MDEBUG
if (trace)
fprintf(trace,"[TIMEOUT]",c);
#endif
ENDCATCH
#ifdef MDEBUG
if (trace)
fprintf(trace,"[fpk:%o]",c);
#endif
signal(SIGINT,Oint);
return(c);
}
 
trapalarm() {
alarm(0);
if (vcatch)
longjmp(vreslab,1);
}
/ports/trunk/editors/rvi/ex_voper.c
0,0 → 1,878
/*
* Copyright (c) 1980 Regents of the University of California.
* All rights reserved. The Berkeley software License Agreement
* specifies the terms and conditions for redistribution.
*/
 
#if !defined(lint) && defined(DOSCCS)
static char *sccsid = "@(#)ex_voper.c 7.4 (Berkeley) 6/7/85";
#endif
 
#include "ex.h"
#include "ex_tty.h"
#include "ex_vis.h"
 
#define blank() isspace(wcursor[0])
#define forbid(a) if (a) goto errlab;
 
char vscandir[2] = { '/', 0 };
 
/*
* Decode an operator/operand type command.
* Eventually we switch to an operator subroutine in ex_vops.c.
* The work here is setting up a function variable to point
* to the routine we want, and manipulation of the variables
* wcursor and wdot, which mark the other end of the affected
* area. If wdot is zero, then the current line is the other end,
* and if wcursor is zero, then the first non-blank location of the
* other line is implied.
*/
operate(c, cnt)
register int c, cnt;
{
register int i;
int (*moveop)(), (*deleteop)();
register int (*opf)();
bool subop = 0;
char *oglobp, *ocurs;
register line *addr;
line *odot;
static char lastFKND, lastFCHR;
short d;
 
moveop = vmove, deleteop = vdelete;
wcursor = cursor;
wdot = NOLINE;
notecnt = 0;
dir = 1;
switch (c) {
 
/*
* d delete operator.
*/
case 'd':
moveop = vdelete;
deleteop = beep;
break;
 
/*
* s substitute characters, like c\040, i.e. change space.
*/
case 's':
ungetkey(' ');
subop++;
/* fall into ... */
 
/*
* c Change operator.
*/
case 'c':
if (c == 'c' && workcmd[0] == 'C' || workcmd[0] == 'S')
subop++;
moveop = vchange;
deleteop = beep;
break;
 
/*
* ! Filter through a UNIX command.
*/
case '!':
moveop = vfilter;
deleteop = beep;
break;
 
/*
* y Yank operator. Place specified text so that it
* can be put back with p/P. Also yanks to named buffers.
*/
case 'y':
moveop = vyankit;
deleteop = beep;
break;
 
/*
* = Reformat operator (for LISP).
*/
#ifdef LISPCODE
case '=':
forbid(!value(LISP));
/* fall into ... */
#endif
 
/*
* > Right shift operator.
* < Left shift operator.
*/
case '<':
case '>':
moveop = vshftop;
deleteop = beep;
break;
 
/*
* r Replace character under cursor with single following
* character.
*/
case 'r':
vmacchng(1);
vrep(cnt);
return;
 
default:
goto nocount;
}
vmacchng(1);
/*
* Had an operator, so accept another count.
* Multiply counts together.
*/
if (isdigit(peekkey()) && peekkey() != '0') {
cnt *= vgetcnt();
Xcnt = cnt;
forbid (cnt <= 0);
}
 
/*
* Get next character, mapping it and saving as
* part of command for repeat.
*/
c = map(getesc(),arrows);
if (c == 0)
return;
if (!subop)
*lastcp++ = c;
nocount:
opf = moveop;
switch (c) {
 
/*
* b Back up a word.
* B Back up a word, liberal definition.
*/
case 'b':
case 'B':
dir = -1;
/* fall into ... */
 
/*
* w Forward a word.
* W Forward a word, liberal definition.
*/
case 'W':
case 'w':
wdkind = c & ' ';
forbid(lfind(2, cnt, opf, 0) < 0);
vmoving = 0;
break;
 
/*
* E to end of following blank/nonblank word
*/
case 'E':
wdkind = 0;
goto ein;
 
/*
* e To end of following word.
*/
case 'e':
wdkind = 1;
ein:
forbid(lfind(3, cnt - 1, opf, 0) < 0);
vmoving = 0;
break;
 
/*
* ( Back an s-expression.
*/
case '(':
dir = -1;
/* fall into... */
 
/*
* ) Forward an s-expression.
*/
case ')':
forbid(lfind(0, cnt, opf, (line *) 0) < 0);
markDOT();
break;
 
/*
* { Back an s-expression, but don't stop on atoms.
* In text mode, a paragraph. For C, a balanced set
* of {}'s.
*/
case '{':
dir = -1;
/* fall into... */
 
/*
* } Forward an s-expression, but don't stop on atoms.
* In text mode, back paragraph. For C, back a balanced
* set of {}'s.
*/
case '}':
forbid(lfind(1, cnt, opf, (line *) 0) < 0);
markDOT();
break;
 
/*
* % To matching () or {}. If not at ( or { scan for
* first such after cursor on this line.
*/
case '%':
vsave();
i = lmatchp((line *) 0);
#ifdef TRACE
if (trace)
fprintf(trace, "after lmatchp in %, dot=%d, wdot=%d, dol=%d\n", lineno(dot), lineno(wdot), lineno(dol));
#endif
getDOT();
forbid(!i);
if (opf != vmove)
if (dir > 0)
wcursor++;
else
cursor++;
else
markDOT();
vmoving = 0;
break;
 
/*
* [ Back to beginning of defun, i.e. an ( in column 1.
* For text, back to a section macro.
* For C, back to a { in column 1 (~~ beg of function.)
*/
case '[':
dir = -1;
/* fall into ... */
 
/*
* ] Forward to next defun, i.e. a ( in column 1.
* For text, forward section.
* For C, forward to a } in column 1 (if delete or such)
* or if a move to a { in column 1.
*/
case ']':
if (!vglobp)
forbid(getkey() != c);
forbid (Xhadcnt);
vsave();
i = lbrack(c, opf);
getDOT();
forbid(!i);
markDOT();
if (o_speed > B300)
hold |= HOLDWIG;
break;
 
/*
* , Invert last find with f F t or T, like inverse
* of ;.
*/
case ',':
forbid (lastFKND == 0);
c = isupper(lastFKND) ? tolower(lastFKND) : toupper(lastFKND);
i = lastFCHR;
if (vglobp == 0)
vglobp = "";
subop++;
goto nocount;
 
/*
* 0 To beginning of real line.
*/
case '0':
wcursor = linebuf;
vmoving = 0;
break;
 
/*
* ; Repeat last find with f F t or T.
*/
case ';':
forbid (lastFKND == 0);
c = lastFKND;
i = lastFCHR;
subop++;
goto nocount;
 
/*
* F Find single character before cursor in current line.
* T Like F, but stops before character.
*/
case 'F': /* inverted find */
case 'T':
dir = -1;
/* fall into ... */
 
/*
* f Find single character following cursor in current line.
* t Like f, but stope before character.
*/
case 'f': /* find */
case 't':
if (!subop) {
i = getesc();
if (i == 0)
return;
*lastcp++ = i;
}
if (vglobp == 0)
lastFKND = c, lastFCHR = i;
for (; cnt > 0; cnt--)
forbid (find(i) == 0);
vmoving = 0;
switch (c) {
 
case 'T':
wcursor++;
break;
 
case 't':
wcursor--;
case 'f':
fixup:
if (moveop != vmove)
wcursor++;
break;
}
break;
 
/*
* | Find specified print column in current line.
*/
case '|':
if (Pline == numbline)
cnt += 8;
vmovcol = cnt;
vmoving = 1;
wcursor = vfindcol(cnt);
break;
 
/*
* ^ To beginning of non-white space on line.
*/
case '^':
wcursor = vskipwh(linebuf);
vmoving = 0;
break;
 
/*
* $ To end of line.
*/
case '$':
if (opf == vmove) {
vmoving = 1;
vmovcol = 20000;
} else
vmoving = 0;
if (cnt > 1) {
if (opf == vmove) {
wcursor = 0;
cnt--;
} else
wcursor = linebuf;
/* This is wrong at EOF */
wdot = dot + cnt;
break;
}
if (linebuf[0]) {
wcursor = strend(linebuf) - 1;
goto fixup;
}
wcursor = linebuf;
break;
 
/*
* h Back a character.
* ^H Back a character.
*/
case 'h':
case CTRL('h'):
dir = -1;
/* fall into ... */
 
/*
* space Forward a character.
*/
case 'l':
case ' ':
forbid (margin() || opf == vmove && edge());
while (cnt > 0 && !margin())
wcursor += dir, cnt--;
if (margin() && opf == vmove || wcursor < linebuf)
wcursor -= dir;
vmoving = 0;
break;
 
/*
* D Delete to end of line, short for d$.
*/
case 'D':
cnt = INF;
goto deleteit;
 
/*
* X Delete character before cursor.
*/
case 'X':
dir = -1;
/* fall into ... */
deleteit:
/*
* x Delete character at cursor, leaving cursor where it is.
*/
case 'x':
if (margin())
goto errlab;
vmacchng(1);
while (cnt > 0 && !margin())
wcursor += dir, cnt--;
opf = deleteop;
vmoving = 0;
break;
 
default:
/*
* Stuttered operators are equivalent to the operator on
* a line, thus turn dd into d_.
*/
if (opf == vmove || c != workcmd[0]) {
errlab:
beep();
vmacp = 0;
return;
}
/* fall into ... */
 
/*
* _ Target for a line or group of lines.
* Stuttering is more convenient; this is mostly
* for aesthetics.
*/
case '_':
wdot = dot + cnt - 1;
vmoving = 0;
wcursor = 0;
break;
 
/*
* H To first, home line on screen.
* Count is for count'th line rather than first.
*/
case 'H':
wdot = (dot - vcline) + cnt - 1;
if (opf == vmove)
markit(wdot);
vmoving = 0;
wcursor = 0;
break;
 
/*
* - Backwards lines, to first non-white character.
*/
case '-':
wdot = dot - cnt;
vmoving = 0;
wcursor = 0;
break;
 
/*
* ^P To previous line same column. Ridiculous on the
* console of the VAX since it puts console in LSI mode.
*/
case 'k':
case CTRL('p'):
wdot = dot - cnt;
if (vmoving == 0)
vmoving = 1, vmovcol = column(cursor);
wcursor = 0;
break;
 
/*
* L To last line on screen, or count'th line from the
* bottom.
*/
case 'L':
wdot = dot + vcnt - vcline - cnt;
if (opf == vmove)
markit(wdot);
vmoving = 0;
wcursor = 0;
break;
 
/*
* M To the middle of the screen.
*/
case 'M':
wdot = dot + ((vcnt + 1) / 2) - vcline - 1;
if (opf == vmove)
markit(wdot);
vmoving = 0;
wcursor = 0;
break;
 
/*
* + Forward line, to first non-white.
*
* CR Convenient synonym for +.
*/
case '+':
case CR:
wdot = dot + cnt;
vmoving = 0;
wcursor = 0;
break;
 
/*
* ^N To next line, same column if possible.
*
* LF Linefeed is a convenient synonym for ^N.
*/
case CTRL('n'):
case 'j':
case NL:
wdot = dot + cnt;
if (vmoving == 0)
vmoving = 1, vmovcol = column(cursor);
wcursor = 0;
break;
 
/*
* n Search to next match of current pattern.
*/
case 'n':
vglobp = vscandir;
c = *vglobp++;
goto nocount;
 
/*
* N Like n but in reverse direction.
*/
case 'N':
vglobp = vscandir[0] == '/' ? "?" : "/";
c = *vglobp++;
goto nocount;
 
/*
* ' Return to line specified by following mark,
* first white position on line.
*
* ` Return to marked line at remembered column.
*/
case '\'':
case '`':
d = c;
c = getesc();
if (c == 0)
return;
c = markreg(c);
forbid (c == 0);
wdot = getmark(c);
forbid (wdot == NOLINE);
forbid (Xhadcnt);
vmoving = 0;
wcursor = d == '`' ? ncols[c - 'a'] : 0;
if (opf == vmove && (wdot != dot || (d == '`' && wcursor != cursor)))
markDOT();
if (wcursor) {
vsave();
getline(*wdot);
if (wcursor > strend(linebuf))
wcursor = 0;
getDOT();
}
if (o_speed > B300)
hold |= HOLDWIG;
break;
 
/*
* G Goto count'th line, or last line if no count
* given.
*/
case 'G':
if (!Xhadcnt)
cnt = lineDOL();
wdot = zero + cnt;
forbid (wdot < one || wdot > dol);
if (opf == vmove)
markit(wdot);
vmoving = 0;
wcursor = 0;
break;
 
/*
* / Scan forward for following re.
* ? Scan backward for following re.
*/
case '/':
case '?':
forbid (Xhadcnt);
vsave();
ocurs = cursor;
odot = dot;
wcursor = 0;
if (readecho(c))
return;
if (!vglobp)
vscandir[0] = genbuf[0];
#ifndef BIT8
oglobp = globp; CP(vutmp, genbuf); globp = vutmp;
#else
oglobp = globp; CP((char*)vutmp, genbuf);
globp = (char*)vutmp;
#endif
d = peekc;
fromsemi:
ungetchar(0);
fixech();
CATCH
#ifndef CBREAK
/*
* Lose typeahead (ick).
*/
vcook();
#endif
addr = address(cursor);
#ifndef CBREAK
vraw();
#endif
ONERR
#ifndef CBREAK
vraw();
#endif
slerr:
globp = oglobp;
dot = odot;
cursor = ocurs;
ungetchar(d);
splitw = 0;
vclean();
vjumpto(dot, ocurs, 0);
return;
ENDCATCH
if (globp == 0)
globp = "";
else if (peekc)
--globp;
if (*globp == ';') {
/* /foo/;/bar/ */
globp++;
dot = addr;
cursor = loc1;
goto fromsemi;
}
dot = odot;
ungetchar(d);
c = 0;
if (*globp == 'z')
globp++, c = '\n';
if (any(*globp, "^+-."))
c = *globp++;
i = 0;
while (isdigit(*globp))
i = i * 10 + *globp++ - '0';
if (any(*globp, "^+-."))
c = *globp++;
if (*globp) {
/* random junk after the pattern */
beep();
goto slerr;
}
globp = oglobp;
splitw = 0;
vmoving = 0;
wcursor = loc1;
if (i != 0)
vsetsiz(i);
if (opf == vmove) {
if (state == ONEOPEN || state == HARDOPEN)
outline = destline = WBOT;
if (addr != dot || loc1 != cursor)
markDOT();
if (loc1 > linebuf && *loc1 == 0)
loc1--;
if (c)
vjumpto(addr, loc1, c);
else {
vmoving = 0;
if (loc1) {
vmoving++;
vmovcol = column(loc1);
}
getDOT();
if (state == CRTOPEN && addr != dot)
vup1();
vupdown(addr - dot, NOSTR);
}
return;
}
lastcp[-1] = 'n';
getDOT();
wdot = addr;
break;
}
/*
* Apply.
*/
if (vreg && wdot == 0)
wdot = dot;
(*opf)(c);
wdot = NOLINE;
}
 
/*
* Find single character c, in direction dir from cursor.
*/
find(c)
char c;
{
 
for(;;) {
if (edge())
return (0);
wcursor += dir;
if (*wcursor == c)
return (1);
}
}
 
/*
* Do a word motion with operator op, and cnt more words
* to go after this.
*/
word(op, cnt)
register int (*op)();
int cnt;
{
register int which;
register char *iwc;
register line *iwdot = wdot;
 
if (dir == 1) {
iwc = wcursor;
which = wordch(wcursor);
while (wordof(which, wcursor)) {
if (cnt == 1 && op != vmove && wcursor[1] == 0) {
wcursor++;
break;
}
if (!lnext())
return (0);
if (wcursor == linebuf)
break;
}
/* Unless last segment of a change skip blanks */
if (op != vchange || cnt > 1)
while (!margin() && blank())
wcursor++;
else
if (wcursor == iwc && iwdot == wdot && *iwc)
wcursor++;
if (op == vmove && margin())
wcursor--;
} else {
if (!lnext())
return (0);
while (blank())
if (!lnext())
return (0);
if (!margin()) {
which = wordch(wcursor);
while (!margin() && wordof(which, wcursor))
wcursor--;
}
if (wcursor < linebuf || !wordof(which, wcursor))
wcursor++;
}
return (1);
}
 
/*
* To end of word, with operator op and cnt more motions
* remaining after this.
*/
eend(op)
register int (*op)();
{
register int which;
 
if (!lnext())
return;
while (blank())
if (!lnext())
return;
which = wordch(wcursor);
while (wordof(which, wcursor)) {
if (wcursor[1] == 0) {
wcursor++;
break;
}
if (!lnext())
return;
}
if (op != vchange && op != vdelete && wcursor > linebuf)
wcursor--;
}
 
/*
* Wordof tells whether the character at *wc is in a word of
* kind which (blank/nonblank words are 0, conservative words 1).
*/
wordof(which, wc)
char which;
register char *wc;
{
 
if (isspace(*wc))
return (0);
return (!wdkind || wordch(wc) == which);
}
 
/*
* Wordch tells whether character at *wc is a word character
* i.e. an alfa, digit, or underscore.
*/
wordch(wc)
char *wc;
{
register int c;
 
c = wc[0];
return (isalpha(c) || isdigit(c) || c == '_'
#ifdef ISO
/*
* We consider all ISO chars except fpr
* no-break-space as word characters since
* we cannot know better.
*/
|| ((c & QUOTE) && !niso(c)
&& ((unsigned char)c) != 240)
#endif
);
}
 
/*
* Edge tells when we hit the last character in the current line.
*/
edge()
{
 
if (linebuf[0] == 0)
return (1);
if (dir == 1)
return (wcursor[1] == 0);
else
return (wcursor == linebuf);
}
 
/*
* Margin tells us when we have fallen off the end of the line.
*/
margin()
{
 
return (wcursor < linebuf || wcursor[0] == 0);
}
/ports/trunk/editors/rvi/ex_vops3.c
0,0 → 1,560
/*
* Copyright (c) 1980 Regents of the University of California.
* All rights reserved. The Berkeley software License Agreement
* specifies the terms and conditions for redistribution.
*/
 
#if !defined(lint) && defined(DOSCCS)
static char *sccsid = "@(#)ex_vops3.c 7.3 (Berkeley) 6/7/85";
#endif
 
#include "ex.h"
#include "ex_tty.h"
#include "ex_vis.h"
 
/*
* Routines to handle structure.
* Operations supported are:
* ( ) { } [ ]
*
* These cover: LISP TEXT
* ( ) s-exprs sentences
* { } list at same paragraphs
* [ ] defuns sections
*
* { and } for C used to attempt to do something with matching {}'s, but
* I couldn't find definitions which worked intuitively very well, so I
* scrapped this.
*
* The code here is very hard to understand.
*/
line *llimit;
int (*lf)();
 
#ifdef LISPCODE
int lindent();
#endif
 
bool wasend;
 
/*
* Find over structure, repeated count times.
* Don't go past line limit. F is the operation to
* be performed eventually. If pastatom then the user said {}
* rather than (), implying past atoms in a list (or a paragraph
* rather than a sentence.
*/
lfind(pastatom, cnt, f, limit)
bool pastatom;
int cnt, (*f)();
line *limit;
{
register int c;
register int rc = 0;
char save[LBSIZE];
 
/*
* Initialize, saving the current line buffer state
* and computing the limit; a 0 argument means
* directional end of file.
*/
wasend = 0;
lf = f;
strcpy(save, linebuf);
if (limit == 0)
limit = dir < 0 ? one : dol;
llimit = limit;
wdot = dot;
wcursor = cursor;
 
if (pastatom >= 2) {
while (cnt > 0 && word(f, cnt))
cnt--;
if (pastatom == 3)
eend(f);
if (dot == wdot) {
wdot = 0;
if (cursor == wcursor)
rc = -1;
}
}
#ifdef LISPCODE
else if (!value(LISP)) {
#else
else {
#endif
char *icurs;
line *idot;
 
if (linebuf[0] == 0) {
do
if (!lnext())
goto ret;
while (linebuf[0] == 0);
if (dir > 0) {
wdot--;
linebuf[0] = 0;
wcursor = linebuf;
/*
* If looking for sentence, next line
* starts one.
*/
if (!pastatom) {
icurs = wcursor;
idot = wdot;
goto begin;
}
}
}
icurs = wcursor;
idot = wdot;
 
/*
* Advance so as to not find same thing again.
*/
if (dir > 0) {
if (!lnext()) {
rc = -1;
goto ret;
}
} else
ignore(lskipa1(""));
 
/*
* Count times find end of sentence/paragraph.
*/
begin:
for (;;) {
while (!endsent(pastatom))
if (!lnext())
goto ret;
if (!pastatom || wcursor == linebuf && endPS())
if (--cnt <= 0)
break;
if (linebuf[0] == 0) {
do
if (!lnext())
goto ret;
while (linebuf[0] == 0);
} else
if (!lnext())
goto ret;
}
 
/*
* If going backwards, and didn't hit the end of the buffer,
* then reverse direction.
*/
if (dir < 0 && (wdot != llimit || wcursor != linebuf)) {
dir = 1;
llimit = dot;
/*
* Empty line needs special treatement.
* If moved to it from other than begining of next line,
* then a sentence starts on next line.
*/
if (linebuf[0] == 0 && !pastatom &&
(wdot != dot - 1 || cursor != linebuf)) {
lnext();
goto ret;
}
}
 
/*
* If we are not at a section/paragraph division,
* advance to next.
*/
if (wcursor == icurs && wdot == idot || wcursor != linebuf || !endPS())
ignore(lskipa1(""));
}
#ifdef LISPCODE
else {
c = *wcursor;
/*
* Startup by skipping if at a ( going left or a ) going
* right to keep from getting stuck immediately.
*/
if (dir < 0 && c == '(' || dir > 0 && c == ')') {
if (!lnext()) {
rc = -1;
goto ret;
}
}
/*
* Now chew up repitition count. Each time around
* if at the beginning of an s-exp (going forwards)
* or the end of an s-exp (going backwards)
* skip the s-exp. If not at beg/end resp, then stop
* if we hit a higher level paren, else skip an atom,
* counting it unless pastatom.
*/
while (cnt > 0) {
c = *wcursor;
if (dir < 0 && c == ')' || dir > 0 && c == '(') {
if (!lskipbal("()"))
goto ret;
/*
* Unless this is the last time going
* backwards, skip past the matching paren
* so we don't think it is a higher level paren.
*/
if (dir < 0 && cnt == 1)
goto ret;
if (!lnext() || !ltosolid())
goto ret;
--cnt;
} else if (dir < 0 && c == '(' || dir > 0 && c == ')')
/* Found a higher level paren */
goto ret;
else {
if (!lskipatom())
goto ret;
if (!pastatom)
--cnt;
}
}
}
#endif
ret:
strcLIN(save);
return (rc);
}
 
/*
* Is this the end of a sentence?
*/
endsent(pastatom)
bool pastatom;
{
register char *cp = wcursor;
register int c, d;
 
/*
* If this is the beginning of a line, then
* check for the end of a paragraph or section.
*/
if (cp == linebuf)
return (endPS());
 
/*
* Sentences end with . ! ? not at the beginning
* of the line, and must be either at the end of the line,
* or followed by 2 spaces. Any number of intervening ) ] ' "
* characters are allowed.
*/
if (!any(c = *cp, ".!?"))
goto tryps;
do
if ((d = *++cp) == 0)
return (1);
while (any(d, ")]'"));
if (*cp == 0 || *cp++ == ' ' && *cp == ' ')
return (1);
tryps:
if (cp[1] == 0)
return (endPS());
return (0);
}
 
/*
* End of paragraphs/sections are respective
* macros as well as blank lines and form feeds.
*/
endPS()
{
 
return (linebuf[0] == 0 ||
isa(svalue(PARAGRAPHS)) || isa(svalue(SECTIONS)));
}
 
#ifdef LISPCODE
lindent(addr)
line *addr;
{
register int i;
char *swcurs = wcursor;
line *swdot = wdot;
 
again:
if (addr > one) {
register char *cp;
register int cnt = 0;
 
addr--;
getline(*addr);
for (cp = linebuf; *cp; cp++)
if (*cp == '(')
cnt++;
else if (*cp == ')')
cnt--;
cp = vpastwh(linebuf);
if (*cp == 0)
goto again;
if (cnt == 0)
return (whitecnt(linebuf));
addr++;
}
wcursor = linebuf;
linebuf[0] = 0;
wdot = addr;
dir = -1;
llimit = one;
lf = lindent;
if (!lskipbal("()"))
i = 0;
else if (wcursor == linebuf)
i = 2;
else {
register char *wp = wcursor;
 
dir = 1;
llimit = wdot;
if (!lnext() || !ltosolid() || !lskipatom()) {
wcursor = wp;
i = 1;
} else
i = 0;
i += column(wcursor) - 1;
if (!inopen)
i--;
}
wdot = swdot;
wcursor = swcurs;
return (i);
}
#endif
 
lmatchp(addr)
line *addr;
{
register int i;
register char *parens, *cp;
 
for (cp = cursor; !any(*cp, "({[)}]");)
if (*cp++ == 0)
return (0);
lf = 0;
parens = any(*cp, "()") ? "()" : any(*cp, "[]") ? "[]" : "{}";
if (*cp == parens[1]) {
dir = -1;
llimit = one;
} else {
dir = 1;
llimit = dol;
}
if (addr)
llimit = addr;
if (splitw)
llimit = dot;
wcursor = cp;
wdot = dot;
i = lskipbal(parens);
return (i);
}
 
lsmatch(cp)
char *cp;
{
char save[LBSIZE];
register char *sp = save;
register char *scurs = cursor;
 
wcursor = cp;
strcpy(sp, linebuf);
*wcursor = 0;
strcpy(cursor, genbuf);
cursor = strend(linebuf) - 1;
if (lmatchp(dot - vcline)) {
register int i = insmode;
register int c = outcol;
register int l = outline;
 
if (!MI)
endim();
vgoto(splitw ? WECHO : LINE(wdot - llimit), column(wcursor) - 1);
flush();
#ifndef __linux__
sleep(1);
#else
usleep(500000);
#endif
vgoto(l, c);
if (i)
goim();
}
else {
strcLIN(sp);
strcpy(scurs, genbuf);
if (!lmatchp((line *) 0))
beep();
}
strcLIN(sp);
wdot = 0;
wcursor = 0;
cursor = scurs;
}
 
ltosolid()
{
 
return (ltosol1("()"));
}
 
ltosol1(parens)
register char *parens;
{
register char *cp;
 
if (*parens && !*wcursor && !lnext())
return (0);
while (isspace(*wcursor) || (*wcursor == 0 && *parens))
if (!lnext())
return (0);
if (any(*wcursor, parens) || dir > 0)
return (1);
for (cp = wcursor; cp > linebuf; cp--)
if (isspace(cp[-1]) || any(cp[-1], parens))
break;
wcursor = cp;
return (1);
}
 
lskipbal(parens)
register char *parens;
{
register int level = dir;
register int c;
 
do {
if (!lnext()) {
wdot = NOLINE;
return (0);
}
c = *wcursor;
if (c == parens[1])
level--;
else if (c == parens[0])
level++;
} while (level);
return (1);
}
 
lskipatom()
{
 
return (lskipa1("()"));
}
 
lskipa1(parens)
register char *parens;
{
register int c;
 
for (;;) {
if (dir < 0 && wcursor == linebuf) {
if (!lnext())
return (0);
break;
}
c = *wcursor;
if (c && (isspace(c) || any(c, parens)))
break;
if (!lnext())
return (0);
if (dir > 0 && wcursor == linebuf)
break;
}
return (ltosol1(parens));
}
 
lnext()
{
 
if (dir > 0) {
if (*wcursor)
wcursor++;
if (*wcursor)
return (1);
if (wdot >= llimit) {
if (lf == vmove && wcursor > linebuf)
wcursor--;
return (0);
}
wdot++;
getline(*wdot);
wcursor = linebuf;
return (1);
} else {
--wcursor;
if (wcursor >= linebuf)
return (1);
#ifdef LISPCODE
if (lf == lindent && linebuf[0] == '(')
llimit = wdot;
#endif
if (wdot <= llimit) {
wcursor = linebuf;
return (0);
}
wdot--;
getline(*wdot);
wcursor = linebuf[0] == 0 ? linebuf : strend(linebuf) - 1;
return (1);
}
}
 
lbrack(c, f)
register int c;
int (*f)();
{
register line *addr;
 
addr = dot;
for (;;) {
addr += dir;
if (addr < one || addr > dol) {
addr -= dir;
break;
}
getline(*addr);
if (linebuf[0] == '{' ||
#ifdef LISPCODE
value(LISP) && linebuf[0] == '(' ||
#endif
isa(svalue(SECTIONS))) {
if (c == ']' && f != vmove) {
addr--;
getline(*addr);
}
break;
}
if (c == ']' && f != vmove && linebuf[0] == '}')
break;
}
if (addr == dot)
return (0);
if (f != vmove)
wcursor = c == ']' ? strend(linebuf) : linebuf;
else
wcursor = 0;
wdot = addr;
vmoving = 0;
return (1);
}
 
isa(cp)
register char *cp;
{
 
if (linebuf[0] != '.')
return (0);
for (; cp[0] && cp[1]; cp += 2)
if (linebuf[1] == cp[0]) {
if (linebuf[2] == cp[1])
return (1);
if (linebuf[2] == 0 && cp[1] == ' ')
return (1);
}
return (0);
}
/ports/trunk/editors/rvi/expreserve.c
0,0 → 1,398
/*
* Copyright (c) 1980 Regents of the University of California.
* All rights reserved. The Berkeley software License Agreement
* specifies the terms and conditions for redistribution.
*/
 
#if !defined(lint) && defined(DOSCCS)
char *copyright =
"@(#) Copyright (c) 1980 Regents of the University of California.\n\
All rights reserved.\n";
 
static char *sccsid = "@(#)expreserve.c 7.13.2 (2.11BSD GTE) 1996/10/26";
#endif
 
#include <stdio.h>
#include <ctype.h>
#include <sys/param.h>
#include <sys/stat.h>
#include <sys/dir.h>
#include <pwd.h>
#include "uparm.h"
 
#define TMP "/tmp"
 
#ifdef VMUNIX
#define HBLKS 2
#else
#define HBLKS 1
#endif
 
char xstr[1]; /* make loader happy */
 
/*
* Expreserve - preserve a file in /usr/preserve
* Bill Joy UCB November 13, 1977
*
* This routine is very naive - it doesn't remove anything from
* /usr/preserve... this may mean that we leave
* stuff there... the danger in doing anything with /usr/preserve
* is that the clock may be screwed up and we may get confused.
*
* We are called in two ways - first from the editor with no argumentss
* and the standard input open on the temp file. Second with an argument
* to preserve the entire contents of /tmp (root only).
*
* BUG: should do something about preserving Rx... (register contents)
* temporaries.
*/
 
#ifndef VMUNIX
#define LBLKS 125
#else
#define LBLKS 900
#endif
#define FNSIZE 128
 
struct header {
time_t Time; /* Time temp file last updated */
int Uid; /* This users identity */
#ifndef VMUNIX
short Flines; /* Number of lines in file */
#else
int Flines;
#endif
char Savedfile[FNSIZE]; /* The current file name */
short Blocks[LBLKS]; /* Blocks where line pointers stashed */
} H;
 
#ifdef lint
#define ignore(a) Ignore(a)
#define ignorl(a) Ignorl(a)
#else
#define ignore(a) a
#define ignorl(a) a
#endif
 
struct passwd *getpwuid();
off_t lseek();
FILE *popen();
 
#define eq(a, b) strcmp(a, b) == 0
 
main(argc)
int argc;
{
register DIR *tf;
struct direct *dirent;
struct stat stbuf;
 
/*
* If only one argument, then preserve the standard input.
*/
if (argc == 1) {
if (copyout((char *) 0))
exit(1);
exit(0);
}
 
/*
* If not super user, then can only preserve standard input.
*/
if (getuid()) {
fprintf(stderr, "NOT super user\n");
exit(1);
}
 
/*
* ... else preserve all the stuff in /tmp, removing
* it as we go.
*/
if (chdir(TMP) < 0) {
perror(TMP);
exit(1);
}
 
tf = opendir(".");
if (tf == NULL) {
perror(TMP);
exit(1);
}
while ((dirent = readdir(tf)) != NULL) {
/* Ex temporaries must begin with Ex. */
if (dirent->d_name[0] != 'E' || dirent->d_name[1] != 'x')
continue;
if (stat(dirent->d_name, &stbuf))
continue;
if ((stbuf.st_mode & S_IFMT) != S_IFREG)
continue;
/*
* Save the bastard.
*/
ignore(copyout(dirent->d_name));
}
closedir(tf);
exit(0);
}
 
char pattern[] = "/usr/preserve/Exaa`XXXXX";
 
/*
* Copy file name into /usr/preserve/...
* If name is (char *) 0, then do the standard input.
* We make some checks on the input to make sure it is
* really an editor temporary, generate a name for the
* file (this is the slowest thing since we must stat
* to find a unique name), and finally copy the file.
*/
copyout(name)
char *name;
{
int i;
static int reenter;
char buf[BUFSIZ];
 
/*
* The first time we put in the digits of our
* process number at the end of the pattern.
*/
if (reenter == 0) {
mkdigits(pattern);
reenter++;
}
 
/*
* If a file name was given, make it the standard
* input if possible.
*/
if (name != 0) {
ignore(close(0));
/*
* Need read/write access for arcane reasons
* (see below).
*/
if (open(name, 2) < 0)
return (-1);
}
 
/*
* Get the header block.
*/
ignorl(lseek(0, 0l, 0));
if (read(0, (char *) &H, sizeof H) != sizeof H) {
format:
if (name == 0)
fprintf(stderr, "Buffer format error\t");
return (-1);
}
 
/*
* Consistency checsks so we don't copy out garbage.
*/
if (H.Flines < 0) {
#ifdef DEBUG
fprintf(stderr, "Negative number of lines\n");
#endif
goto format;
}
if (H.Blocks[0] != HBLKS || H.Blocks[1] != HBLKS+1) {
#ifdef DEBUG
fprintf(stderr, "Blocks %d %d\n", H.Blocks[0], H.Blocks[1]);
#endif
goto format;
}
if (name == 0 && H.Uid != getuid()) {
#ifdef DEBUG
fprintf(stderr, "Wrong user-id\n");
#endif
goto format;
}
if (lseek(0, 0l, 0)) {
#ifdef DEBUG
fprintf(stderr, "Negative number of lines\n");
#endif
goto format;
}
 
/*
* If no name was assigned to the file, then give it the name
* LOST, by putting this in the header.
*/
if (H.Savedfile[0] == 0) {
strcpy(H.Savedfile, "LOST");
ignore(write(0, (char *) &H, sizeof H));
H.Savedfile[0] = 0;
lseek(0, 0l, 0);
}
 
/*
* File is good. Get a name and create a file for the copy.
*/
mknext(pattern);
ignore(close(1));
if (creat(pattern, 0600) < 0) {
if (name == 0)
perror(pattern);
return (1);
}
 
/*
* Make the target be owned by the owner of the file.
*/
ignore(chown(pattern, H.Uid, 0));
 
/*
* Copy the file.
*/
for (;;) {
i = read(0, buf, BUFSIZ);
if (i < 0) {
if (name)
perror("Buffer read error");
ignore(unlink(pattern));
return (-1);
}
if (i == 0) {
if (name)
ignore(unlink(name));
notify(H.Uid, H.Savedfile, (int) name, H.Time);
return (0);
}
if (write(1, buf, i) != i) {
if (name == 0)
perror(pattern);
unlink(pattern);
return (-1);
}
}
}
 
/*
* Blast the last 5 characters of cp to be the process number.
*/
mkdigits(cp)
char *cp;
{
register int i, j;
 
for (i = getpid(), j = 5, cp += strlen(cp); j > 0; i /= 10, j--)
*--cp = i % 10 | '0';
}
 
/*
* Make the name in cp be unique by clobbering up to
* three alphabetic characters into a sequence of the form 'aab', 'aac', etc.
* Mktemp gets weird names too quickly to be useful here.
*/
mknext(cp)
char *cp;
{
char *dcp;
struct stat stb;
 
dcp = cp + strlen(cp) - 1;
while (isdigit(*dcp))
dcp--;
whoops:
if (dcp[0] == 'z') {
dcp[0] = 'a';
if (dcp[-1] == 'z') {
dcp[-1] = 'a';
if (dcp[-2] == 'z')
fprintf(stderr, "Can't find a name\t");
dcp[-2]++;
} else
dcp[-1]++;
} else
dcp[0]++;
if (stat(cp, &stb) == 0)
goto whoops;
}
 
/*
* Notify user uid that his file fname has been saved.
*/
notify(uid, fname, flag, time)
int uid;
char *fname;
time_t time;
{
struct passwd *pp = getpwuid(uid);
register FILE *mf;
char cmd[BUFSIZ];
char hostname[128];
char croak[128];
char *timestamp, *ctime();
 
if (pp == NULL)
return;
gethostname(hostname, sizeof(hostname));
timestamp = ctime(&time);
timestamp[16] = 0; /* blast from seconds on */
sprintf(cmd, "/bin/mail %s", pp->pw_name);
setuid(getuid());
mf = popen(cmd, "w");
if (mf == NULL)
return;
setbuf(mf, cmd);
/*
* flag says how the editor croaked:
* "the editor was killed" is perhaps still not an ideal
* error message. Usually, either it was forcably terminated
* or the phone was hung up, but we don't know which.
*/
sprintf(croak, flag
? "the system went down"
: "the editor was killed");
if (fname[0] == 0) {
fname = "LOST";
fprintf(mf,
"Subject: editor saved ``LOST''\n");
fprintf(mf,
"You were editing a file without a name\n");
fprintf(mf,
"at <%s> on the machine ``%s'' when %s.\n", timestamp, hostname, croak);
fprintf(mf,
"Since the file had no name, it has been named \"LOST\".\n");
} else {
fprintf(mf,
"Subject: editor saved ``%s''\n", fname);
fprintf(mf,
"You were editing the file \"%s\"\n", fname);
fprintf(mf,
"at <%s> on the machine ``%s''\n", timestamp, hostname);
fprintf(mf,
"when %s.\n", croak);
}
fprintf(mf,
"\nYou can retrieve most of your changes to this file\n");
fprintf(mf,
"using the \"recover\" command of the editor.\n");
fprintf(mf,
"An easy way to do this is to give the command \"vi -r %s\".\n", fname);
fprintf(mf,
"This method also works using \"ex\" and \"edit\".\n");
pclose(mf);
}
 
/*
* people making love
* never exactly the same
* just like a snowflake
*/
 
#ifdef lint
Ignore(a)
int a;
{
 
a = a;
}
 
Ignorl(a)
long a;
{
 
a = a;
}
#endif
/ports/trunk/editors/rvi/mkstr.c
0,0 → 1,281
/*
* 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
char copyright[] =
"@(#) Copyright (c) 1980 Regents of the University of California.\n\
All rights reserved.\n";
#endif not lint
 
#ifndef lint
static char sccsid[] = "@(#)mkstr.c 5.1 (Berkeley) 5/31/85";
#endif not lint
 
#include <stdio.h>
#include <string.h>
 
#define ungetchar(c) ungetc(c, stdin)
 
long ftell();
char *calloc();
/*
* mkstr - create a string error message file by massaging C source
*
* Bill Joy UCB August 1977
*
* Modified March 1978 to hash old messages to be able to recompile
* without addding messages to the message file (usually)
*
* Based on an earlier program conceived by Bill Joy and Chuck Haley
*
* Program to create a string error message file
* from a group of C programs. Arguments are the name
* of the file where the strings are to be placed, the
* prefix of the new files where the processed source text
* is to be placed, and the files to be processed.
*
* The program looks for 'error("' in the source stream.
* Whenever it finds this, the following characters from the '"'
* to a '"' are replaced by 'seekpt' where seekpt is a
* pointer into the error message file.
* If the '(' is not immediately followed by a '"' no change occurs.
*
* The optional '-' causes strings to be added at the end of the
* existing error message file for recompilation of single routines.
*/
 
 
FILE *mesgread, *mesgwrite;
char *progname;
char usagestr[] = "usage: %s [ - ] mesgfile prefix file ...\n";
char name[100], *np;
 
main(argc, argv)
int argc;
char *argv[];
{
char addon = 0;
 
argc--, progname = *argv++;
if (argc > 1 && argv[0][0] == '-')
addon++, argc--, argv++;
if (argc < 3)
fprintf(stderr, usagestr, progname), exit(1);
mesgwrite = fopen(argv[0], addon ? "a" : "w");
if (mesgwrite == NULL)
perror(argv[0]), exit(1);
mesgread = fopen(argv[0], "r");
if (mesgread == NULL)
perror(argv[0]), exit(1);
inithash();
argc--, argv++;
strcpy(name, argv[0]);
np = name + strlen(name);
argc--, argv++;
do {
strcpy(np, argv[0]);
if (freopen(name, "w", stdout) == NULL)
perror(name), exit(1);
if (freopen(argv[0], "r", stdin) == NULL)
perror(argv[0]), exit(1);
process();
argc--, argv++;
} while (argc > 0);
exit(0);
}
 
process()
{
register char *cp;
register c;
 
for (;;) {
c = getchar();
if (c == EOF)
return;
if (c != 'e') {
putchar(c);
continue;
}
if (match("error(")) {
printf("error(");
c = getchar();
if (c != '"')
putchar(c);
else
copystr();
}
}
}
 
match(ocp)
char *ocp;
{
register char *cp;
register c;
 
for (cp = ocp + 1; *cp; cp++) {
c = getchar();
if (c != *cp) {
while (ocp < cp)
putchar(*ocp++);
ungetchar(c);
return (0);
}
}
return (1);
}
 
copystr()
{
register c, ch;
char buf[512];
register char *cp = buf;
 
for (;;) {
c = getchar();
if (c == EOF)
break;
switch (c) {
 
case '"':
*cp++ = 0;
goto out;
case '\\':
c = getchar();
switch (c) {
 
case 'b':
c = '\b';
break;
case 't':
c = '\t';
break;
case 'r':
c = '\r';
break;
case 'n':
c = '\n';
break;
case '\n':
continue;
case 'f':
c = '\f';
break;
case '\\':
break;
default:
if (!octdigit(c))
break;
c -= '0';
ch = getchar();
if (!octdigit(ch))
break;
c <<= 3, c += ch - '0';
ch = getchar();
if (!octdigit(ch))
break;
c <<= 3, c += ch - '0';
break;
}
}
*cp++ = c;
}
out:
*cp = 0;
printf("%d", hashit(buf, 1, NULL));
}
 
octdigit(c)
char c;
{
 
return (c >= '0' && c <= '7');
}
 
inithash()
{
char buf[512];
int mesgpt = 0;
 
rewind(mesgread);
while (fgetNUL(buf, sizeof buf, mesgread) != NULL) {
hashit(buf, 0, mesgpt);
mesgpt += strlen(buf) + 2;
}
}
 
#define NBUCKETS 511
 
struct hash {
long hval;
unsigned hpt;
struct hash *hnext;
} *bucket[NBUCKETS];
 
hashit(str, really, fakept)
char *str;
char really;
unsigned fakept;
{
int i;
register struct hash *hp;
char buf[512];
long hashval = 0;
register char *cp;
 
if (really)
fflush(mesgwrite);
for (cp = str; *cp;)
hashval = (hashval << 1) + *cp++;
i = hashval % NBUCKETS;
if (i < 0)
i += NBUCKETS;
if (really != 0)
for (hp = bucket[i]; hp != 0; hp = hp->hnext)
if (hp->hval == hashval) {
fseek(mesgread, (long) hp->hpt, 0);
fgetNUL(buf, sizeof buf, mesgread);
/*
fprintf(stderr, "Got (from %d) %s\n", hp->hpt, buf);
*/
if (strcmp(buf, str) == 0)
break;
}
if (!really || hp == 0) {
hp = (struct hash *) calloc(1, sizeof *hp);
hp->hnext = bucket[i];
hp->hval = hashval;
hp->hpt = really ? ftell(mesgwrite) : fakept;
if (really) {
fwrite(str, sizeof (char), strlen(str) + 1, mesgwrite);
fwrite("\n", sizeof (char), 1, mesgwrite);
}
bucket[i] = hp;
}
/*
fprintf(stderr, "%s hashed to %ld at %d\n", str, hp->hval, hp->hpt);
*/
return (hp->hpt);
}
 
#include <sys/types.h>
#include <sys/stat.h>
 
fgetNUL(obuf, rmdr, file)
char *obuf;
register int rmdr;
FILE *file;
{
register c;
register char *buf = obuf;
 
while (--rmdr > 0 && (c = getc(file)) != 0 && c != EOF)
*buf++ = c;
*buf++ = 0;
getc(file);
return ((feof(file) || ferror(file)) ? NULL : 1);
}
/ports/trunk/editors/rvi/termcap/Makefile
0,0 → 1,59
#
# Copyright (c) 1980 Regents of the University of California.
# All rights reserved. The Berkeley software License Agreement
# specifies the terms and conditions for redistribution.
#
# @(#)Makefile 5.1 (Berkeley) 6/5/85
#
DEFS= -DNOSCCS -I.. -DCM_N -DCM_GT -DCM_B -DCM_D
#CFLAGS= -O -DCM_N -DCM_GT -DCM_B -DCM_D $(DEFS)
CFLAGS= -O $(DEFS)
LD = ld
SRCS= termcap.c tgoto.c tputs.c
OBJS= termcap.o tgoto.o tputs.o
TAGSFILE=tags
 
all: libtermlib.a
 
libtermlib.a: $(OBJS)
ar cr libtermlib.a $(OBJS)
 
clean:
rm -f libtermlib.a $(OBJS) core
 
#.c.o:
# $(CC) $(CFLAGS) -c -p $*.c
# $(LD) $(LDFLAGS) -x -r -o profiled/$*.o $*.o
# $(CC) $(CFLAGS) -c $*.c
# $(LD) $(LDFLAGS) -X -r $*.o
# mv a.out $*.o
#
#termcap.a termcap_p.a: ${OBJS}
# ar cr termcap.a ${OBJS}
# cd profiled; ar cr ../termcap_p.a ${OBJS}
#
#install: termcap.a termcap_p.a
# install -c termcap.a ${DESTDIR}/usr/lib/libtermcap.a
# -rm -f ${DESTDIR}/usr/lib/libtermlib.a
# ln ${DESTDIR}/usr/lib/libtermcap.a ${DESTDIR}/usr/lib/libtermlib.a
# ranlib ${DESTDIR}/usr/lib/libtermcap.a
# install -c termcap_p.a ${DESTDIR}/usr/lib/libtermcap_p.a
# -rm -f ${DESTDIR}/usr/lib/libtermlib_p.a
# ln ${DESTDIR}/usr/lib/libtermcap_p.a ${DESTDIR}/usr/lib/libtermlib_p.a
# ranlib ${DESTDIR}/usr/lib/libtermcap_p.a
#
#tags:
# cwd=`pwd`; \
# for i in ${SRCS}; do \
# ctags -a -f ${TAGSFILE} $$cwd/$$i; \
# done
#
#clean:
# -rm -f *.o profiled/*.o
# -rm -f termcap.a termcap_p.a
#
#VGRIND= csh /usr/ucb/vgrind
#vgrind:
# cp /dev/null index
# ${VGRIND} -h "Termcap library" termcap.c tputs.c tgoto.c
# ${VGRIND} -h "Termcap library" -x index
/ports/trunk/editors/rvi/Makefile
0,0 → 1,89
#
# Copyright (c) 1980 Regents of the University of California.
# All rights reserved. The Berkeley software License Agreement
# specifies the terms and conditions for redistribution.
#
# @(#)Makefile 7.13.1.3 (2.11BSD GTE) 1996/10/23
#
# Changes for newer systems by Gunnar Ritter, May 2000
#
VERSION=3.7
#
# NB: This makefile doesn't indicate any dependencies on header files.
#
# Ex is very large - this version will not fit on PDP-11's without overlay
# software. Things that can be turned off to save
# space include LISPCODE (-l flag, showmatch and lisp options), CHDIR (the
# undocumented chdir command.) VMUNIX makes ex considerably larger, raising
# many limits and improving speed and simplicity of maintenance. It is
# suitable only for a VAX or other large machine, and then probably only in
# a paged system.
#
# Don't define VFORK unless your system has the VFORK system call,
# which is like fork but the two processes have only one data space until the
# child execs. This speeds up ex by saving the memory copy.
#
# If your system expands tabs to 4 spaces you should -DTABS=4 below
#
BINDIR = bin
DESTDIR = /usr/local
DEBUGFLAGS= -DTRACE -g
NONDEBUGFLAGS= -O
DEB= $(NONDEBUGFLAGS) # or $(DEBUGFLAGS) to to debug
# this should be correct for any modern Unix
OPTIONS=-DLISPCODE -DCHDIR -DFASTTAG -DVFORK -DUSG3TTY -DVMUNIX -DBIGMEM -D_BSD_EXTENSION
# define to use ISO-8859-X character sets
#OPTIONS += -DISO
LDFLAGS=
TERMLIB = -L./termcap -ltermlib
 
# That's it.
 
CFLAGS= $(OPTIONS) $(DEB) -DUSG3TTY -DVMUNIX
INCLUDE=/usr/include
OBJS= ex.o ex_addr.o ex_cmds.o ex_cmds2.o ex_cmdsub.o \
ex_data.o ex_extern.o ex_get.o ex_io.o ex_put.o ex_re.o \
ex_set.o ex_subr.o ex_tagio.o ex_temp.o ex_tty.o ex_unix.o \
ex_v.o ex_vadj.o ex_vget.o ex_vmain.o ex_voper.o \
ex_vops.o ex_vops2.o ex_vops3.o ex_vput.o ex_vwind.o \
printf.o
HDRS= ex.h ex_argv.h ex_re.h ex_temp.h ex_tty.h ex_tune.h ex_vars.h ex_vis.h
SRC1= ex.c ex_addr.c ex_cmds.c ex_cmds2.c ex_cmdsub.c
SRC2= ex_data.c ex_get.c ex_io.c ex_put.c ex_re.c
SRC3= ex_set.c ex_subr.c ex_tagio.c ex_temp.c ex_tty.c ex_unix.c
SRC4= ex_v.c ex_vadj.c ex_vget.c ex_vmain.c ex_voper.c
SRC5= ex_vops.c ex_vops2.c ex_vops3.c ex_vput.c ex_vwind.c
SRC6= printf.c expreserve.c exrecover.c
 
all: ex
 
ex: $(OBJS)
cd termcap && make
$(CC) -o ex $(LDFLAGS) $(OBJS) $(TERMLIB)
 
$(OBJS): $(HDRS)
 
ex_vars.h:
csh makeoptions $(CFLAGS)
 
clean:
cd termcap && make clean
# If we dont have ex we cant make it so don't rm ex_vars.h
-rm -f ex *.o x*.[cs] core errs trace
 
# install in standard place
install: ex
rm -f $(DESTDIR)/$(BINDIR)/ex
rm -f $(DESTDIR)/$(BINDIR)/edit
rm -f $(DESTDIR)/$(BINDIR)/vi
rm -f $(DESTDIR)/$(BINDIR)/view
install -s -m 1755 ex $(DESTDIR)/$(BINDIR)/ex
ln -s $(DESTDIR)/$(BINDIR)/ex $(DESTDIR)/$(BINDIR)/edit
ln -s $(DESTDIR)/$(BINDIR)/ex $(DESTDIR)/$(BINDIR)/vi
ln -s $(DESTDIR)/$(BINDIR)/ex $(DESTDIR)/$(BINDIR)/view
rm -f $(DESTDIR)/man/man1/vi.1
rm -f $(DESTDIR)/man/man1/ex.1
rm -f $(DESTDIR)/man/man1/edit.1
install -m 644 vi.1 $(DESTDIR)/man/man1/vi.1
install -m 644 ex.1 $(DESTDIR)/man/man1/ex.1
ln -s $(DESTDIR)/man/man1/ex.1 $(DESTDIR)/man/man1/edit.1
/ports/trunk/editors/rvi/Makefile.old
0,0 → 1,175
#
# Copyright (c) 1980 Regents of the University of California.
# All rights reserved. The Berkeley software License Agreement
# specifies the terms and conditions for redistribution.
#
# @(#)Makefile 7.13.1.3 (2.11BSD GTE) 1996/10/23
#
VERSION=3.7
#
# NB: This makefile doesn't indicate any dependencies on header files.
#
# Ex is very large - this version will not fit on PDP-11's without overlay
# software. Things that can be turned off to save
# space include LISPCODE (-l flag, showmatch and lisp options), CHDIR (the
# undocumented chdir command.) VMUNIX makes ex considerably larger, raising
# many limits and improving speed and simplicity of maintenance. It is
# suitable only for a VAX or other large machine, and then probably only in
# a paged system.
#
# Don't define VFORK unless your system has the VFORK system call,
# which is like fork but the two processes have only one data space until the
# child execs. This speeds up ex by saving the memory copy.
#
# If your system expands tabs to 4 spaces you should -DTABS=4 below
#
BINDIR= /usr/ucb
SBINDIR= /usr/sbin
MISCDIR= /usr/share/misc
FOLD= ${BINDIR}/fold
CTAGS= ${BINDIR}/ctags
XSTR= ${BINDIR}/xstr
DEBUGFLAGS= -DTRACE -g
NONDEBUGFLAGS= -O
DEB= ${NONDEBUGFLAGS} # or ${DEBUGFLAGS} to to debug
#OPTIONS= -DLISPCODE -DCHDIR -DVFORK -DVMUNIX -DFASTTAG
OPTIONS= -DLISPCODE -DCHDIR -DVFORK -DFASTTAG
CFLAGS= -DTABS=8 ${OPTIONS} ${DEB}
LDFLAGS= -X
SEPFLAG= -i
TERMLIB= -ltermcap
MKSTR= ${BINDIR}/mkstr
CXREF= ${BINDIR}/cxref
INCLUDE=/usr/include
PR= pr
OBJS= ex.o ex_addr.o ex_cmds.o ex_cmds2.o ex_cmdsub.o \
ex_data.o ex_extern.o ex_get.o ex_io.o ex_put.o ex_re.o \
ex_set.o ex_subr.o ex_tagio.o ex_temp.o ex_tty.o ex_unix.o \
ex_v.o ex_vadj.o ex_vget.o ex_vmain.o ex_voper.o \
ex_vops.o ex_vops2.o ex_vops3.o ex_vput.o ex_vwind.o \
printf.o strings.o
HDRS= ex.h ex_argv.h ex_re.h ex_temp.h ex_tty.h ex_tune.h ex_vars.h ex_vis.h
SRC1= ex.c ex_addr.c ex_cmds.c ex_cmds2.c ex_cmdsub.c
SRC2= ex_data.c ex_get.c ex_io.c ex_put.c ex_re.c
SRC3= ex_set.c ex_subr.c ex_tagio.c ex_temp.c ex_tty.c ex_unix.c
SRC4= ex_v.c ex_vadj.c ex_vget.c ex_vmain.c ex_voper.c
SRC5= ex_vops.c ex_vops2.c ex_vops3.c ex_vput.c ex_vwind.c
SRC6= printf.c expreserve.c exrecover.c
MISC= makefile READ_ME
VGRIND= csh /usr/ucb/vgrind
VHDR= "Ex Version ${VERSION}"
 
.c.o:
# ifdef VMUNIX
# ${CC} -E ${CFLAGS} $*.c | ${XSTR} -c -
# else
${MKSTR} - exstrings x $*.c
${CC} -E ${CFLAGS} x$*.c | ${XSTR} -c -
rm -f x$*.c
# endif
${CC} ${CFLAGS} -c x.c
mv x.o $*.o
 
all: a.out exrecover expreserve
 
# 11/23, 34, 40 and other non split I/D machines
# each of the 6 overlays must stay less than 16K.
 
a.out: ${OBJS}
-if [ X${SEPFLAG} = X-i ]; then \
ld ${LDFLAGS} /lib/crt0.o ${SEPFLAG} \
-Z ex_voper.o ex_vget.o ex_vops.o ex_vops2.o ex_vops3.o \
-Z ex_set.o ex_re.o ex_io.o ex_tty.o ex_unix.o ex.o ex_v.o \
-Y ex_addr.o ex_cmds.o ex_cmds2.o ex_cmdsub.o ex_tagio.o \
ex_get.o ex_temp.o ex_vadj.o ex_vmain.o ex_vwind.o\
ex_vput.o ex_put.o ex_subr.o printf.o strings.o \
ex_data.o ex_extern.o ${TERMLIB} -lc; \
else \
ld ${LDFLAGS} /lib/crt0.o ${SEPFLAG} \
-Z ex_cmds.o ex_cmds2.o ex_cmdsub.o ex_tagio.o ex_re.o \
-Z ex_vadj.o ex_vmain.o ex_voper.o ex_vwind.o ex_vops3.o\
-Z ex_v.o ex_vget.o ex_vops.o ex_vops2.o ex_vput.o\
-Z ex_get.o ex_io.o ex_temp.o ex_tty.o ex_unix.o \
ex_addr.o ex.o ex_set.o \
-Z ex_put.o ex_subr.o ${TERMLIB} \
-Y ex_data.o ex_extern.o printf.o strings.o -lc; \
fi
 
tags: /tmp
${CTAGS} -w ex.[hc] ex_*.[hc]
 
${OBJS}: ${HDRS}
 
ex_vars.h:
csh makeoptions ${CFLAGS}
 
# xstr: hands off!
strings.o: strings
${XSTR}
${CC} -c -o strings.o xs.c
exrecover: exrecover.o ex_extern.o
${CC} ${SEPFLAG} ${CFLAGS} exrecover.o ex_extern.o -o exrecover
 
exrecover.o: exrecover.c
${CC} ${CFLAGS} -c -O exrecover.c
 
expreserve: expreserve.o
${CC} ${SEPFLAG} expreserve.o -o expreserve
 
expreserve.o:
${CC} ${CFLAGS} -c -O expreserve.c
 
clean:
# If we dont have ex we cant make it so don't rm ex_vars.h
-rm -f a.out exrecover expreserve exstrings strings
-rm -f *.o x*.[cs] core errs trace
 
# install in standard place (/usr/ucb)
install: a.out exrecover expreserve
-rm -f ${DESTDIR}/${BINDIR}/ex
-rm -f ${DESTDIR}/${BINDIR}/vi
-rm -f ${DESTDIR}/${BINDIR}/view
-rm -f ${DESTDIR}/usr/bin/ex
install -s -m 1755 a.out ${DESTDIR}/${BINDIR}/ex
install -c -m 444 exstrings ${DESTDIR}/${MISCDIR}/exstrings
ln ${DESTDIR}/${BINDIR}/ex ${DESTDIR}/${BINDIR}/vi
ln ${DESTDIR}/${BINDIR}/ex ${DESTDIR}/${BINDIR}/view
ln ${DESTDIR}/${BINDIR}/ex ${DESTDIR}/usr/bin/ex
chmod 1755 ${DESTDIR}/${BINDIR}/ex
install -s -o root -m 4755 exrecover ${DESTDIR}/${SBINDIR}/exrecover
install -s -o root -m 4755 expreserve ${DESTDIR}/${SBINDIR}/expreserve
# The following line normally fails. This is OK.
-mkdir ${DESTDIR}/usr/preserve
 
lint:
lint ${CFLAGS} ex.c ex_?*.c
lint ${CFLAGS} -u exrecover.c
lint ${CFLAGS} expreserve.c
 
print:
@${PR} READ* BUGS
@${PR} makefile*
@${PR} /etc/termcap
@(size -l a.out ; size *.o) | ${PR} -h sizes
@${PR} -h errno.h ${INCLUDE}/errno.h
@${PR} -h setjmp.h ${INCLUDE}/setjmp.h
@${PR} -h sgtty.h ${INCLUDE}/sgtty.h
@${PR} -h signal.h ${INCLUDE}/signal.h
@${PR} -h sys/stat.h ${INCLUDE}/sys/stat.h
@${PR} -h sys/types.h ${INCLUDE}/sys/types.h
@ls -ls | ${PR}
@${CXREF} *.c | ${PR} -h XREF
@${PR} *.h *.c
 
vgrind:
tee index < /dev/null
${VGRIND} -h ${VHDR} ${HDRS}
${VGRIND} -h ${VHDR} ${SRC1}
${VGRIND} -h ${VHDR} ${SRC2}
${VGRIND} -h ${VHDR} ${SRC3}
${VGRIND} -h ${VHDR} ${SRC4}
${VGRIND} -h ${VHDR} ${SRC5}
${VGRIND} -h ${VHDR} ${SRC6}
${VGRIND} -n -h ${VHDR} ${MISC}
${VGRIND} -i -h ${VHDR} index
/ports/trunk/editors/rvi/README.NEW
0,0 → 1,34
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!! !!!
!!! THIS IS NOT FREE SOFTWARE !!!
!!! !!!
!!! SEE YOUR CONTRACT WITH SCO FOR DETAILS !!!
!!! !!!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
 
This is basically ex/vi 3.7, 6/7/85, from the 2.11BSD distribution. I
added the manual pages and the 2.11 termcap and made some smaller fixes.
 
A larger addition is the ability to handle ISO character sets. It is
enabled by default, although it may produce subtle changes in vi's
behaviour I did not discover yet. The handling is not perfect since
it does not apply to the character range 0200 to 0239 that contains
seldom-used control sequences and since the regular expression code
has not been updated at all. In case you prefer the 7-bit only,
classic version, remove -DISO from OPTIONS in the Makefile.
 
I compiled this only on my Linux 2.2.15 / glibc 2.1.3 box. Compilation
reports from other systems are highly welcome, whether successful or
not (in the latter case add a detailed description). You will get
some warnings about signal handler types; ignore them. The additional
tools (expreserve, exrecover, mkstr, xstr) will not be built by
default; they probably will not compile.
 
In any case, the included version of the termcap library must be used
since the use of malloc() in other implementations collides with the
sbrk() calls in vi. All you need is an /etc/termcap file; if your
system lacks one, fetch it from <http://www.tuxedo.org/terminfo>.
 
Gunnar Ritter
Freiburg i. Br.
Germany
<g-r@bigfoot.de>
/ports/trunk/editors/rvi/READ_ME
0,0 → 1,19
This is version 3 of the editor. It is too large to fit on a pdp-11
unless you have overlay code. (Such code is available for v7 unix on 2bsd.)
 
Version 2.13 corresponds to version 3 without the enhancements in 3.
There is no reason to use 2.13 unless you have a pdp-11 that does not have
overlay software, since 3 contains all the bug fixes and some new features.
 
Special installation notes for this version.
2) The include file varargs.h should be installed, as the printf here needs it.
3) The include file local/uparm.h should be installed, as ex_tune.h needs it.
The contents of this include file can be modified if you wish to place
the editor in a nonstandard location.
 
Conditional compilation flags:
-DTRACE for debugging (wont then fit on an 11)
-DVFORK for UCB Vax/Unix with the vfork system call.
-DCHDIR compile in undocumented old chdir (cd) command
-DLISP compile in lisp hacks
-DCRYPT -x option to edit encrypted files
/ports/trunk/editors/rvi/termcap/tc3.c
0,0 → 1,82
/*
* Copyright (c) 1980 Regents of the University of California.
* All rights reserved. The Berkeley software License Agreement
* specifies the terms and conditions for redistribution.
*/
 
#if !defined(lint) && !defined(NOSCCS)
static char sccsid[] = "@(#)tc3.c 5.1 (Berkeley) 6/5/85";
#endif not lint
 
/*
* tc3 [term]
* Dummy program to test out termlib.
* Input two numbers and it prints out the tgoto string generated.
*/
#include <stdio.h>
char buf[1024];
char *getenv(), *tgetstr();
char *rdchar();
char *tgoto();
char *CM;
char cmbuff[30];
char *x;
char *UP;
char *tgout;
 
main(argc, argv) char **argv; {
char *p;
int rc;
int row, col;
 
if (argc < 2)
p = getenv("TERM");
else
p = argv[1];
rc = tgetent(buf,p);
x = cmbuff;
UP = tgetstr("up", &x);
printf("UP = %x = ", UP); pr(UP); printf("\n");
if (UP && *UP==0)
UP = 0;
CM = tgetstr("cm", &x);
printf("CM = "); pr(CM); printf("\n");
for (;;) {
if (scanf("%d %d", &row, &col) < 2)
exit(0);
tgout = tgoto(CM, row, col);
pr(tgout);
printf("\n");
}
}
 
pr(p)
register char *p;
{
for (; *p; p++)
printf("%s", rdchar(*p));
}
 
/*
* rdchar: returns a readable representation of an ASCII char, using ^ notation.
*/
#include <ctype.h>
char *rdchar(c)
char c;
{
static char ret[4];
register char *p;
 
/*
* Due to a bug in isprint, this prints spaces as ^`, but this is OK
* because we want something to show up on the screen.
*/
ret[0] = ((c&0377) > 0177) ? '\'' : ' ';
c &= 0177;
ret[1] = isprint(c) ? ' ' : '^';
ret[2] = isprint(c) ? c : c^0100;
ret[3] = 0;
for (p=ret; *p==' '; p++)
;
return (p);
}
/ports/trunk/editors/rvi/termcap/tputs.c
0,0 → 1,96
/*
* Copyright (c) 1980 Regents of the University of California.
* All rights reserved. The Berkeley software License Agreement
* specifies the terms and conditions for redistribution.
*/
 
#if !defined(lint) && !defined(NOSCCS)
static char sccsid[] = "@(#)tputs.c 5.1 (Berkeley) 6/5/85";
#endif not lint
 
#include <sgtty.h>
#include <ctype.h>
 
/*
* The following array gives the number of tens of milliseconds per
* character for each speed as returned by gtty. Thus since 300
* baud returns a 7, there are 33.3 milliseconds per char at 300 baud.
*/
static
short tmspc10[] = {
0, 2000, 1333, 909, 743, 666, 500, 333, 166, 83, 55, 41, 20, 10, 5
};
 
short ospeed;
char PC;
 
/*
* Put the character string cp out, with padding.
* The number of affected lines is affcnt, and the routine
* used to output one character is outc.
*/
tputs(cp, affcnt, outc)
register char *cp;
int affcnt;
int (*outc)();
{
register int i = 0;
register int mspc10;
 
if (cp == 0)
return;
 
/*
* Convert the number representing the delay.
*/
if (isdigit(*cp)) {
do
i = i * 10 + *cp++ - '0';
while (isdigit(*cp));
}
i *= 10;
if (*cp == '.') {
cp++;
if (isdigit(*cp))
i += *cp - '0';
/*
* Only one digit to the right of the decimal point.
*/
while (isdigit(*cp))
cp++;
}
 
/*
* If the delay is followed by a `*', then
* multiply by the affected lines count.
*/
if (*cp == '*')
cp++, i *= affcnt;
 
/*
* The guts of the string.
*/
while (*cp)
(*outc)(*cp++);
 
/*
* If no delay needed, or output speed is
* not comprehensible, then don't try to delay.
*/
if (i == 0)
return;
if (ospeed <= 0 || ospeed >= (sizeof tmspc10 / sizeof tmspc10[0]))
return;
 
/*
* Round up by a half a character frame,
* and then do the delay.
* Too bad there are no user program accessible programmed delays.
* Transmitting pad characters slows many
* terminals down and also loads the system.
*/
mspc10 = tmspc10[ospeed];
i += mspc10 / 2;
for (i /= mspc10; i > 0; i--)
(*outc)(PC);
}
/ports/trunk/editors/rvi/TODO
0,0 → 1,2036
From gbergman@UCBBRAHMS Mon Jul 25 16:21:43 1983
Date: 25 Jul 83 16:16:52 PDT (Mon)
From: gbergman@UCBBRAHMS (George Mark Bergman)
Subject: Re: editor bugs etc.
Message-Id: <8307252316.AA22776@UCBBRAHMS.ARPA>
Received: by UCBBRAHMS.ARPA (3.342/3.7)
id AA22776; 25 Jul 83 16:16:52 PDT (Mon)
Received: from UCBBRAHMS.ARPA by UCBERNIE.ARPA (3.336/3.7)
id AA13678; 25 Jul 83 16:21:17 PDT (Mon)
To: mckusick@UCBERNIE
Status: R
 
The following are (i) a short note from Mark Horton
in reply to a note of mine saying I'd been keeping notes
on editor bugs, had heard he had a new version, was
interested in hearing about it and perhaps sending
him notes on bugs not mentioned as corrected; (ii)
a long letter from me in which I do list bugs, features
I think would be desirable etc., (iii) an addendum I
sent the next day, (iv) brief jottings not yet sent.
 
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
>From mark@cbosgd.UUCP Thu Jul 21 12:31:55 1983
 
The new version of vi isn't very different to the user.
The internals use terminfo instead of termcap, but the
user interface isn't affected by this (except that it
starts up faster). The major new features are
set showmode
will cause an indication on the status line when
you are in input mode
vedit
is a new invocation of vi for novices
more function keys now work
function keys work in both command and input mode
Of course, there are a few bug fixes too.
 
There is a binary in ~mark/bin/vi on ucbarpa. It requires the
/etc/term heirarchy (there is no file called /etc/terminfo) which
was on ucbarpa once but might be gone now. If you want to grab
them from whereever they still exist, please feel free to try them.
Mark
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Sent to Mark Horton 23/7/83, about 11AM
 
Dear Mark,
 
Well, your note didn't say you wanted me to send my comments
on editor bugs and suggestions, but you didn't say I shouldn't,
so I decided to do so. I've tried to organize it into some sort
of sections. I'd be interested to know which of the bugs mentioned
here you have already found and corrected. (I may soon find out for
myself; the person in charge of this machine says he'll try to get a
copy of version 3.9 from UCBARPA if they still have it, and get it
running for me. If you have any helpful information for him, he
is robert@brahms, Robert Gross.)
Vedit sounds like a great idea.
I should mention that throughout this letter, I have
avoided using actual control-characters, but faked them,
e.g. used ^ and H to get the appearance of ^H, since files with
real control-characters can be confusing when looked at in mail,
more, etc. But this means that if you want to try any commands I
refer to that use them, you won't be able to yank and source them,
unless you replace my fakes with real control characters.
The version I am using is 3.7.
 
 
PROBLEMS WITH COUNTS
 
Some vi operations that logically ought to be able to take
counts do not, while others misbehave with counts. In this section,
``N'' will always denote a positive integer placed as a count before
a vi operation.
The most gross case of misbehavior is that of N^B!
The effect is to redraw the screen 23N-44 lines further advanced.
(Probably the numbers depend on the screen-size of the terminal;
this is on a Z19, using termcap h19u.) When N=1, this does indeed
move you a screenful backward, but for higher N it moves the window
forward some amount! Further, whatever controls are supposed to
monitor whether the command would land one at an acceptable line-
number seem to have a different idea of what it is doing: If you
aren't already familiar with these weird effects, try setting the
cursor near the end of a file that is more than 4 screenfuls long,
and hitting 3^B. (You might then try an insert at the place you get
to, and a :f^] .)
N/pattern/ would be useful, but is not allowed.
ND would be a natural synonym for dN$, by analogy with NC for cN$,
but it doesn't work that way; it just ignores the N.
Finally, if N is precisely the number of lines
from the current line to the end of the file, N$ will still correctly
carry one to the last character of the file, but cN$, NC, dN$ and yN$
refuse to do anything! (NY does work, not being a synonym for yN$.)
The failure of NC is particularly annoying; often when I am composing
something, I go back to somewhere in the middle of the next-to-
last line, say, and want to rewrite the rest of the sentence;
2cc would kill not only the part I want to rewrite but also the OK
beginning of the line, and 2C or 2c$ won't work. I realize that I
could get around this by keeping an empty line at the end of the file,
but that should not be necessary.
 
 
PROBLEMS REGARDING SOURCE, MACROS, MAPPINGS
These are enormously useful, but seem to have all kinds of hidden
restrictions.
 
The Appendix to the Ex Reference Manual, "List of Changes from
Version 3.5 to Version 3.6" says ``A bug which prevented the source
command from working...from visual has been fixed''. It is true that
one can now use :so from vi, but it still has a bug: When
the scriptfile invoked contains a global command
and some other command(s) after it, everything after the first global
command is ignored. The same appears to be true of scripts in named
buffers invoked from vi-bottom-line by @x.
 
(It is, perhaps, unexpected that one can invoke scripts with
multiline commands using @x from vi's bottom-line at all, since such
commands will not work if typed on vi's bottom line directly.
A script like
s/$/a\
b
invoked as @x will indeed work. But strangely, if one tries to
invoke from the regular mode of vi the script
:s/$/a\
b
by putting it in buffer x and doing @x, only the first line
will take effect.)
Another serious restriction is that the command ``vi'' appears to
be ignored in sourced ex-scripts, and though the command Q in macros of
various flavors in vi (mapped characters, map!ed characters that contain
``...^V^[...Q...''; @x scripts) does take one into ex, any ex
commands after it are ignored.
I assume you are aware of whatever restrictions lead to the
error-message ``Cannot yank inside global/macro'', since you must
have written it, though ``inside'' seems to here have the peculiar
meaning ``after a text-changing operation of the macro.''
The error-message ``Can't undo in global commands'' is more
mysterious, since I get it when I have a global command after
a text-changing command in an @x script (though not in a sourced file).
Anyway, the fewer such restrictions these operations were subject
to, the more useful they would be!
 
Although nested source commands are allowed (and I find them
useful), they leave the editor in a ``noprompt'' state. This
can be gotten around by including ``se prompt'' as a line in the
outermost scriptfile, but I would hope the problem causing it could
be cured.
 
When one tries to ``:unmap!'' a ``:map!'' command whose
right-hand-side begins with ^H (entered as ^V^H, of course), one
gets the message ``That macro wasn't mapped''. (One can get around
this by using :unmap! ^V[character].)
 
Certain termcaps apparently produce automatic mappings, which
unfortunately may interfere with useful vi commands. In particular,
on a tvi, ^L gets mapped to a movement command, which makes it
unavailable for redrawing the screen, unless unmapped.
 
 
PROBLEMS WITH DIAGNOSTICS
 
"Hit return to continue" -- It took me a long time to realize that
when I got this diagnostic there was an alternative to hitting
return. I suggest it be reworded
"Hit Return or :"
However, the behavior of the editor when this diagnostic is given
seems to be inconsistent. In particular, when the last of a serious
of commands is
:e otherfile
and I get "Hit return to continue", then hitting : usually
has no different effect from hitting return (or any other
key), namely the screen is redrawn; yet I think that sometimes
in this situation it has brought me directly to the bottom line
as desired. Very confusing.
Would it be possible to have other alternatives than : and return
available, such as /pattern ? Or, more simply, when one would presently
be given the diagnostic "Hit return to continue", why not just put the
editor into the state it would have if one then hit :, since one would
then still have the option of hitting return and getting into vi
proper, but it would not require the extra keystroke : to
begin a bottom-line command, nor would one go through the frequent
frustrating experience of absentmindedly starting to write a
bottom-line command, or a pattern-search, and then having to wait
while the screen was redrawn because one had hit a key other than :.
 
"Using open mode"
Again, it took me a long time to learn that when I tried to enter
vi and got this diagnostic, it meant that the system had somehow
lost the termcap for the terminal I was on, and that I would have
to do something to get the correct termcap into the environment.
Till I realized this, I generally ended up either struggling along
frustrated in open mode, or logging out and logging back in. I suggest
that when someone calls for vi and the termcap is not appropriate,
the editor should not be invoked in any form, but instead, a message
be given such as:
``Your environment does not show a termcap entry permitting
the use of the visual editor. If you are working on a terminal not
supporting vi (in particular, a device with no addressable cursor),
you may enter one of the other modes of the editor with the command
"open filename" or "ex filename". If you are working on a terminal
that should support vi, your environment entries are incorrect and
should be corrected. They presently show:
TERM=....
TERMCAP=....
If you know the correct name or abbreviation for your terminal-
type, type it at the end of the next line; if not hit return:
% setenv TERM ''
If the user typed an acceptable terminal-name, the message would
continue, telling how to get the appropriate termcap. If the user
instead typed return, the message would ask him or her to type the
name of the manufacturer shown on the terminal, not
worrying about upper/lower-case distinctions, and a list of possible
terminal names and abbreviations would be given... . This whole
program would not be part of the editor, so there would
be no problem of space within the existing crowded confines of
the editor code.
 
"No such file or directory" -- I think there should be a distinction
between these two cases, because of the important distinction in the
consequences when the user tries to quit the editor:
If the directory exists, the file is created, but
if not, the results are more complicated -- I seem to recall on one
occasion simply losing what I had written on my second try
at quitting; though I just now did an experiment and this time
repeated ZZ's and :x's simply gave repeated error messages.
 
"File already exists..." -- The ``List of changes from 3.5 to 3.6'' says
``If you get I/O errors, the file is considered "not edited"... .''
I presume that this correction is somehow the cause of the fact that
I frequently get the above message when trying to leave the editor
on a machine with version 3.7, and have to use
:w! %|q
to exit. But I've never seen any evidence that there were I/O errors;
it mainly seems to happen when I've written some lines to another
file in the process of editing. So the criteria the editor is using
to decide when there have been ``I/O errors'' should be rechecked.
 
"no such command from open/visual" -- This confused me in my first
few days of using the editor, when I didn't understand that one
couldn't use i and a (in either their vi or ex senses) from the bottom
line of vi. A message "i -- no such command from open/visual"
was perplexing because I knew that "i" was indeed a vi command.
Perhaps it should say "no such command from open/visual bottom line".
 
MISCELLANEOUS PROBLEMS
 
In ex search and replacement patterns, \\ is supposed to represent
a real \-character, but something goes wrong when this occurs
at the end of a global command. E.g., though
:s/^/\\
works OK (in vi or ex), the variant
:.g/^/s//\\
definitely does not. In vi it turns everything off, in ex it seems to
behave as though there were just a single \, and in a scriptfile,
it -- does something still different, which you can discover if you
don't know!
 
The Ex Reference Manual says, ``For sanity with use from within
visual mode, ex ignores a ":" preceding any command.'' But it
ignores it in the wrong place! -- not at the beginning of the
command line, but just before the command letter itself. I.e.,
it accepts 1,3:s/^/ /, but not :1,3s/^/ /.
 
SUGGESTIONS FOR MINOR ADDED CAPABILITIES
 
In a multiline substitute command with the "c" option, when
each line is displayed one has three choices: y, n or break. There
are some further options that would be useful. One would be "p" --
at present, "p" can only be included on the command line, which
means that one has a choice between seeing the result of every
substitution or none. In practice, one would generally like to see
the results of the first few cases to make sure that the command one has
written does what one meant it to, and maybe a few tricky cases that
come up; but not every case! Another might be "u" -- to undo the last
case for which one gave a "y". Still another might be an option that
would mean ``undo the "c" option -- I see that the substitute command
is doing what I wanted, go ahead and finish it without me.''
In a command g/pattern/p, the pattern in question is occasionally
such that it takes a while to figure out where on the line it occurs.
For this purpose, an option that ``pointed out'' the instance of the
pattern, in the same manner that the pattern to be replaced is pointed
out in substitute command with option c, would be desirable.
When g/pattern/p gives more than a screenful of lines, it would
be nice to have it piped through the equivalent of ``more''.
 
ex has the command line option "-", which ``is useful in processing
editor scripts''. But if one wants to use a script in the course of
an otherwise interactive editing session, it would be desirable to have
a corresponding resettable option ``:se -'' (or ``:se nofb'').
 
In strings in pattern-searches, it would be useful to have
^ and $ retain their ``magic'', so that /x[a$]/ could
search for all occurrences of x before an a or a newline.
(Of course, one would then have to decide whether /x[^y]/ should
include the case of x followed by a newline or not.)
 
Just as ex allows the command :vi, so I think that vi should
have some bottom-line command equivalent to the regular-mode
command Q. When one has done some text-changing bottom-line
commands, and realizes one wants to go into ex, it can be time-
consuming to hit return and then Q, and wait for the screen to be
redrawn for vi before one gets the ex prompt.
 
The option of putting several commands on one line, separated
by, "|" is particularly useful in the vi bottom-line mode, because
it avoids having the screen redrawn several times. It would be
useful to be able sometimes do the same thing with non-bottom-line
commands, e.g. in editing a troff file at a low baud rate on a dumb
terminal one might like to be able to do i\fI^]3Ea\fR^] without
watching the line get redrawn twice. Perhaps some key that would
cause any sequence of commands to be ``held'' until some complementary
key was hit would be useful.
It would also be desirable to have a sequence of commands that had
been given in this way available as one unit to be repeated by ``.'',
if desired.
 
The parenthesis-matching facility with % might be extended
to match ` with ' and < with >.
 
I will mention one facility that I discovered by surprize is
possessed by ed but not ex -- sequences such as \1 can be used
within ed search-patterns. E.g. (for the most trivial case)
/\(.\)\1/
will search for doubled letters.
 
 
DEBATABLE SUGGESTIONS
I will mention here some possible changes which have the
difficulty that they would change the meaning of existing commands,
so that it could be argued that the disadvantage of users having
to change their habits might outweigh the advantages.
 
First, one might try to resolve, one way or another, the
contradiction between the count arguments taken by the join commands
in vi and ex: In ex, jN joins N+1 lines; in vi, NJ joins N lines
(except if N=1).
 
Second, the movement commands tx and Tx of vi (x any character)
seem poorly defined. Just as fx will ignore the character on which
the cursor is presently sitting, even if it is an x, and move to the
next occurrence, so I would think that tx should ignore the character
immediately after the cursor, and Tx the character immediately before
the cursor. The point is that when one does Nfx, and finds that one
had failed to count one occurrence of x and fallen short of where one
wanted to go, one can hit ; and get there. Likewise, on doing Ntx
and finding one has fallen short, one should be able to hit ; and get
to the the next occurrence; but at present, hitting ; leaves
the cursor in the same position; one must hit ``2;'' to get any
further. In effect, Ntx is presently defined as Nfxh; I am
suggesting that it be defined as lNfxh.
 
The sequences cw, dw and yw are presently violations of the
principle that c[movement], d[movement] and y[movement] change,
delete, or yank everything from the current cursor position through
the endpoint of the movement command. cw does what one would expect of
ce (in fact, they seem to be synonyms), while there is no way to get
the effect which cw would have if it were treated ``consistently''.
(E.g., if I have a line beginning ``And if'', and I want to change it
to ``If'', I cannot just put the cursor on the A and hit cwI^].) dw
and yw delete up to the character immediately before the point to
which ``w'' would take the cursor. I would have to agree that this
behavior of dw and yw is more useful than that which a literal
interpretation of the movement rule would lead to; but perhaps it
would become still more useful if when applied to the last word on
a line, it deleted or yanked the space immediately before the word
along with the word... . On the other hand, one could argue for
making a distinction between cw and ce.
 
Though I see the motivation for the above definitions,
I see no sensible reason why Y should be equivalent to yy, when
C and D are equivalent to c$ and d$. I would vote for changing
Y to mean y$.
 
RADICAL SUGGESTIONS
 
Is there any reason for maintaining the distinction between
the ``:'' state of vi, and ex itself? At present, there are
relative advantages that lead one to choose to go into one or the
other for a given operation: From the vi-: state, it is easier
to return to the regular vi state; from ex, one has a more powerful
range of commands; and it is easier to give a series of commands
because each carriage-return gives one a new prompt. My suggestion
is that from vi, ``:'' should carry you directly to ex, and when you
are in ex, carriage-return (^M) after a command should give you a new
prompt, while ^] should put you into vi. Conceivably, things might be
simplified even further, and carriage return rather than : could
be the key that would carry one from the regular mode of vi into ex:
 
.-------. .-------.
.-------. a,i... | basic | ^M | |
| vi |<------ | |----->| ex |<---.
| insert| | vi | | | |^M
| mode | ------>| |<-----| mode | ---'
`-------' ^] | mode | ^] | |
`-------' `-------'
 
(Of course, ^M presently has a meaning in vi, but
it has a synonym +.) Clearly, there would also be no need for a
special "Hit return to continue" state.
I have not distinguished vi and open in the above diagram.
My idea here is that ^] would actually return you to either vi
or open, whichever you had last been in, and that to switch
to the other, you could enter ex and type vi^] or o^] respectively.
(Or you could type vi^M, respectively o^M, and further ex commands,
and the mode would be saved for the next time you hit a ^].) Or
alternatively, these could be made settable options: se visual
respectively se novisual.
Having gotten used to the editor as it now exists, I admit that
I feel uneasy about the above idea -- the sense of knowing that
I am ``still in vi'' when I hit :, and not that ``other land'' of ex,
represents a kind of of orientation that it is disconcerting
to abandon. But I can't see any logical disadvantage in making
such a change. Can you? Certainly, there would be things that
would have to be thought out, such as what happens to bottom-line
vi pattern-searches. My thought would be that ``/'' from vi should
give :/ (i.e., put one in ex at the start of a pattern-search),
and ^] after a pattern-search should put one into vi at the appropriate
character on the line, in contrast to ^M after a pattern search,
which would leave one in ex at the appropriate line. In general,
I think such changes would lead to greater simplicity and learnability
of the editor.
I would also hope that excursions between vi and ex and back
could be allowed in scriptfiles. It might also be desirable for
ex to have, in addition to a concept of ``current line'', one of
``current cursor position''... .
 
Well, on to another subject. One of the inconveniences I
found very vexing when first learning to use the editor was that
when in either vi insert mode, or ex/vi-bottom-line, it was very hard
to edit what I was doing. Within insert mode the only ``editing''
I could do, without escaping, was with the three operations ^H,
^W and the kill character. And on a slow line with a dumb terminal,
escaping to make changes could be very time-consuming because large
parts of the screen would insist on being redrawn. Perhaps some
other control-character could serve as
a modified escape, that allowed one to edit what one had entered
in the current insertion without having everything below it redrawn,
and then return to it. Obviously, if carried to its logical
limit this idea could lead to ridiculous nests of
editing operations; but there would be no need to carry it to its
logical limit.
Anyway, the problem of editing ex-style commands
was even worse, because there was no way to ``escape and
revise''. I eventually learned enough to realize that the solution
was to edit complicated commands in another file and source it.
But it is sometimes very useful to have the text on which the
commands are to act in front of you when composing them (e.g., you can
yank and modify various pieces), which led to the variant of writing
command lines within the file I was editing, and then writing
those lines into another file and sourcing that, without ever leaving
the current file. But this is distracting to deal with
when concentrating on the editing task itself, which led me
to divise a scriptfile which would handle the writing-to-another-file-
and-sourcing for me. Or actually, several such files: One for
single-line commands to be used essentially once; one for single-line
commands that I would want to use on the same file during various
editing sessions, and so would want to keep available in that
file, and one for multi-line commands (to be used once). When
I first got the idea, I thought one scriptfile would be enough, and
I would call it ``do'', so that the command to execute a script I
had written in a file I was editing would be ``:so do''. The
file it would write to and source would be ``do.again'', so that
if I wanted to reuse it, I could ``:so do.again''. When I realized
the need for several versions, ``do'' became a directory. Here,
for your amusement, are the three files. (Re the lines ``se prompt'',
cf. my comment on that under PROBLEMS WITH SOURCE etc.):
 
do/1 (for 1-time use of 1-line commands)
.w! ~/do/again
d
so #
se prompt
 
do/1+ (like above, without deleting the command)
.w! ~/do/again
so #
se prompt
 
do/: (to use this, write a multi-line command script, put : at
the beginning of the first line, put the cursor on the last
line of the script, and then source the following:)
?^:?s/:/
,''w! ~/do/again
,''d
so #
se prompt
 
(I also created another version to use in case the script had
to have an internal line beginning with ``:'', so that this couldn't
unambiguously mark the beginning of the script. This used
a line which explicitly specified the address-range of the script.
But I have never had a need for it, so I will not bother you with it.)
Finally, having gotten an account on a machine with a version 3
editor recently, I have divised still another way of doing this. I
have put in my EXINIT the command
 
'map ^A "ayy:@a^M'
 
and now, gratifyingly, the single stroke ^A has essentially the effect
of ``:so do/1+'' -- except for the restrictions to which vi ``map''
commands are subject. But I've only been using this for a
couple of weeks; so I have yet to learn how chafing those restrictions
will or won't be.
Anyway, it might be worth thinking about whether some of these
things that I've done with macros should be incorporated in some form
into the editor itself; or else whether these macros might be written
up in the documentation (or some tutorials) on the editor.
 
Next subject: Complicated pattern-searches in long files
can be time-consuming. I have seen the point mentioned
that if a pattern-description can be begun with "^",
this can speed up the search -- since the pattern-comparisons need
only be begun at beginnings of lines. In some cases, this might
not be possible, but the user might be aware of some other
character or character-sequence in the search-pattern
that will occur relatively rarely in the file. In such cases it would
be desirable if the user could specify one spot from which the pattern
search should start, working forward and backward from there, to
minimize false starts. E.g., if for some reason one wants to
delete every word containing the letter m, the script
%s/[^ ]*m[^ ]*//
would become much less time-consuming if one could mark the point
at which to begin, say writing
%s/[^ ]*\!m[^ ]*//
so as to instruct the editor to search for m's, and each time
one was found, to find the longest possible strings of non-space
characters before and after it, and delete these. (This is a silly
example, but I think the idea is clear.)
 
Something that I've seriously felt the need for is the
capability of searching for lines that satisfy more than one
condition. If one just wants to locate such lines, one can
of course leave the editor and do a pipeline of two or
more greps; but it would be nice to be able to perform global
commands on such lines.
 
Finally, any possibility of introducing the capability of searching
for patterns including embedded newlines, a la sed? Multiple windows,
a la emacs?
 
ADDENDA
I logged in this morning on an adm3a at 300 baud to go over this
letter once more before sending it, and ran into another bug! I had
done 15^D to get a large scroll despite the low speed, and at one point
I saw a line with a typo scrolling up. So I noted its line-number, 402
and without waiting for the screen to stop moving typed something like
402Gfsrd. What happened was that the change was made on line 407 rather
than 402 -- presumably the cursor was sent to where 402 had been when
the command was received... .
Editing this letter this morning reminded me of another feature I
have thought would be desirable for editing on dumb terminals at low
speeds: An option that would cause lines read from a file or typed
in at the bottom of the screen to appear double spaced, with @ signs
@
between them, such as one gets when one deletes a line on such a
@
terminal. (I have faked this effect here, though the fake will not be
@
very successful if you have se nu or se list on.) The point is that
@
editing operations that presently cause painfully slow screen-redrawings
would simply put material in in place of these fillers -- as happens
now when one is lucky enough to be adding material just above a place
where material was previously deleted.
 
- * - * - * - * - * -
 
I hope you've found some things of interest in this hodgepodge.
 
Yours,
George (gbergman@brahms)
 
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
23/7/83, about 8PM
 
Maybe thinking about the editor while writing my previous
``note'' to you has made me more conscious of these things,
but for some reason, I've discovered several more peculiarities of
the the Version 3.7 editor today!
 
First, the abbreviation feature has a strange bug (feature?)
involving the backslash: If some string X is the abbreviation
for a string Y, and if X is typed (following a space etc)
immediately followed by a \ and another character c, the
result is not Y\c by cY\. The case where I discovered this
was a doubly complicated one: I had an abbreviation of
the form
:ab x x\yZ
where x and y were characters and Z was a string of characters.
So when I typed x, it presumably first expanded it as x\yZ,
then performed the transformation I just described on the x\y
turning it into yx\yZ\, and thus giving the result yx\yZ\Z.
This turns out to be one of the cases that can't be unmapped
without doing :una ^Vx. Further, I just tried a similar case
with x replaced by a string of more than one character
(namely, :ab if if\pq) and I find I can't unmap that at all.
I also find that an abbreviated string containing | (which
must be inserted using ^V|, of course) is difficult to unmap.
 
Second, some peculiarities about where the cursor ends
up after a yank. If the yank involved a forward movement,
the cursor stays where it is, which is the beginning
of the yanked segment. If the yank involves a backwards
movement, the place where the cursor originally was is not
the same as the beginning of the yanked segment, and there
seems to be some confusion as to which principle is followed:
y- or yk moves the cursor up, while yb leaves it fixed.
Unfortunately, there is a snake in the grass with yb: If
you hit p after it, the yanked word will not appear after
the position where the cursor has remained, but
after the position to which it would have gone if it had moved
to the beginning of the yanked segment! Likewise if you
you hit an x... .
(You have no idea how much trouble I'm
having with those "if"'s. Of course, I could quit the editor
and come back in, and I would lose that crazy abbreviation
that way.)
 
I also notice that if the cursor is at the end of a word
or between words, 2e behaves the same as e!
 
Finally, I note that d^, when the cursor is before the first
nonwhite character, is another exception to the principle that
d[motion] deletes everything through the endpoint of [motion].
Similarly with c^ and y^.
 
- *- *- *- *- *- *- *- *- *- *- *- *- *- *- *- *- *- *- *- *- *- *- *- *
discovered next day (not yet sent):
 
error with yb does not just concern p and x: any command is
executed as though cursor is at destination of backward in-line
yank.
 
N^B does not work consistently? (Not on a medium-length file in Kim)
 
Can get endless-loop mapping if abbreviation forms word within
abbreviated. E.g. :ab x ( x )
``word'' must be delimited on left by space, tab,
newline, start-of-insert; on right by any punctuation. Why
special ``no tail recursion'' rule?
Things like that ``if'' abbreviation can be undone using
:una i^Vf!
 
Mention desirability of Np
 
copies sent to ralph@ucbarpa, lindahl@topaz 25/7/83
 
From gbergman@UCBCARTAN Mon Aug 1 14:19:27 1983
Date: 1 Aug 83 14:14:06 PDT (Mon)
From: gbergman@UCBCARTAN (George Mark Bergman)
Subject: Re: editor
Message-Id: <8308012114.AA00627@UCBCARTAN.ARPA>
Received: by UCBCARTAN.ARPA (3.342/3.7)
id AA00627; 1 Aug 83 14:14:06 PDT (Mon)
Received: from UCBCARTAN.ARPA by UCBERNIE.ARPA (3.336/3.7)
id AA19324; 1 Aug 83 14:19:12 PDT (Mon)
To: mckusick@UCBERNIE
Status: R
 
Here's Mark Horton's reply to my letter on bugs
 
>From mark@cbosgd.UUCP Thu Jul 28 14:38:55 1983
 
Sorry this has taken so long, but your note was too long to digest
in the cracks between the other stuff I do. Anyway, you've clearly
put a great deal of thought into this, and I appreciate your input.
I'll reply individually to your thoughts, and keep them on file
for use (someday) when vi gets rewritten. Some of them are just
plain bugs that ought to be fixed soon anyway.
 
PROBLEMS WITH COUNTS
 
The most gross case of misbehavior is that of N^B!
The effect is to redraw the screen 23N-44 lines further advanced.
(Probably the numbers depend on the screen-size of the terminal;
this is on a Z19, using termcap h19u.) When N=1, this does indeed
move you a screenful backward, but for higher N it moves the window
forward some amount! Further, whatever controls are supposed to
monitor whether the command would land one at an acceptable line-
number seem to have a different idea of what it is doing: If you
aren't already familiar with these weird effects, try setting the
cursor near the end of a file that is more than 4 screenfuls long,
and hitting 3^B. (You might then try an insert at the place you get
to, and a :f^] .)
This is a known bug, and was fixed in 3.8. The count is supposed to subtract
(LINES-1)*N from the line number, but there's + that should be a -, so it
goes forward instead. The check is correct, so it's possible to go off
the end of the buffer.
N/pattern/ would be useful, but is not allowed.
N/pattern/ resets the window size (silly but true) to N.
ND would be a natural synonym for dN$, by analogy with NC for cN$,
but it doesn't work that way; it just ignores the N.
Finally, if N is precisely the number of lines
from the current line to the end of the file, N$ will still correctly
carry one to the last character of the file, but cN$, NC, dN$ and yN$
refuse to do anything! (NY does work, not being a synonym for yN$.)
The failure of NC is particularly annoying; often when I am composing
something, I go back to somewhere in the middle of the next-to-
last line, say, and want to rewrite the rest of the sentence;
2cc would kill not only the part I want to rewrite but also the OK
beginning of the line, and 2C or 2c$ won't work. I realize that I
could get around this by keeping an empty line at the end of the file,
but that should not be necessary.
While you're making valid observations here, are you aware that you can delete
the current sentence with d} ? I think that's what you really want.
 
 
PROBLEMS REGARDING SOURCE, MACROS, MAPPINGS
These are enormously useful, but seem to have all kinds of hidden
restrictions.
 
The Appendix to the Ex Reference Manual, "List of Changes from
Version 3.5 to Version 3.6" says ``A bug which prevented the source
command from working...from visual has been fixed''. It is true that
one can now use :so from vi, but it still has a bug: When
the scriptfile invoked contains a global command
and some other command(s) after it, everything after the first global
command is ignored. The same appears to be true of scripts in named
buffers invoked from vi-bottom-line by @x.
Sounds like a bug.
 
(It is, perhaps, unexpected that one can invoke scripts with
multiline commands using @x from vi's bottom-line at all, since such
commands will not work if typed on vi's bottom line directly.
A script like
s/$/a\
b
invoked as @x will indeed work. But strangely, if one tries to
invoke from the regular mode of vi the script
:s/$/a\
b
by putting it in buffer x and doing @x, only the first line
will take effect.)
In 3.7 (or 3.8, I'm not sure), you can say
:s/$/a^V^Mb
to get a newline on the RHS. Of course, this doesn't mean there isn't
a bug in scripts.
Another serious restriction is that the command ``vi'' appears to
be ignored in sourced ex-scripts, and though the command Q in macros of
various flavors in vi (mapped characters, map!ed characters that contain
``...^V^[...Q...''; @x scripts) does take one into ex, any ex
commands after it are ignored.
The internals of getting a character from the tty are completely different
in ex and vi. Pushing input for one doesn't affect the other. So there
isn't much hope of changing this situation.
I assume you are aware of whatever restrictions lead to the
error-message ``Cannot yank inside global/macro'', since you must
have written it, though ``inside'' seems to here have the peculiar
meaning ``after a text-changing operation of the macro.''
The error-message ``Can't undo in global commands'' is more
mysterious, since I get it when I have a global command after
a text-changing command in an @x script (though not in a sourced file).
Anyway, the fewer such restrictions these operations were subject
to, the more useful they would be!
It's the way undo is done - the text for undo is saved in the "last deleted"
buffer, and yank puts text there too. Couple this with the fact that globals
and macros can be undone as a unit (they save their state before the first change)
and you'll see that the two notions can't coexist. Of course, you can always
yank into a named buffer, even inside a macro.
 
Although nested source commands are allowed (and I find them
useful), they leave the editor in a ``noprompt'' state. This
can be gotten around by including ``se prompt'' as a line in the
outermost scriptfile, but I would hope the problem causing it could
be cured.
Bug, I guess.
 
When one tries to ``:unmap!'' a ``:map!'' command whose
right-hand-side begins with ^H (entered as ^V^H, of course), one
gets the message ``That macro wasn't mapped''. (One can get around
this by using :unmap! ^V[character].)
Bug, I guess.
 
Certain termcaps apparently produce automatic mappings, which
unfortunately may interfere with useful vi commands. In particular,
on a tvi, ^L gets mapped to a movement command, which makes it
unavailable for redrawing the screen, unless unmapped.
Well, there's no way for vi to tell the difference between ^L that the
user typed as ^L and ^L that the user typed as right arrow. However, there
are a number of terminals that are upward compatible with the adm3a and use
^L for right arrow. Vi has a special case for these built in - if the
terminal has insert/delete line, and ^L is right arrow, then ^R will redraw
the screen.
 
 
PROBLEMS WITH DIAGNOSTICS
 
"Hit return to continue" -- It took me a long time to realize that
when I got this diagnostic there was an alternative to hitting
return. I suggest it be reworded
"Hit Return or :"
However, the behavior of the editor when this diagnostic is given
seems to be inconsistent. In particular, when the last of a serious
of commands is
:e otherfile
and I get "Hit return to continue", then hitting : usually
has no different effect from hitting return (or any other
key), namely the screen is redrawn; yet I think that sometimes
in this situation it has brought me directly to the bottom line
as desired. Very confusing.
Would it be possible to have other alternatives than : and return
available, such as /pattern ? Or, more simply, when one would presently
be given the diagnostic "Hit return to continue", why not just put the
editor into the state it would have if one then hit :, since one would
then still have the option of hitting return and getting into vi
proper, but it would not require the extra keystroke : to
begin a bottom-line command, nor would one go through the frequent
frustrating experience of absentmindedly starting to write a
bottom-line command, or a pattern-search, and then having to wait
while the screen was redrawn because one had hit a key other than :.
There is an internal difference between "ex mode" (where it doesn't keep a
screen image) and "vi mode" (where it does). Any : command that outputs more
than 1 line puts you into "ex mode", requiring "hit return to continue"
before clearing the screen and redrawing it with known stuff. There is no
hope of this changing - the code is too spaghetti-ized already. In the worst
case, the ! command scribbles on the screen and there's nothing vi can do
to know what the command did.
 
What you really want is for vi to check for typeahead and avert the refresh
when it's going to have to redo it anyway. My curses does this, but I simply
don't have time to rewrite vi to use it. This would also solve the other
problem you mention where macros ought to redraw the screen only once.
If you saw the insides of the code, you'd see it needs a rewrite to do this.
Each command knows the screen things to do to fix the screen.
 
What most of us do is hit DEL when the screen is being drawn with something
we don't want to see. This aborts the update and leaves junk on the screen.
Then you move the cursor where you want it, and if the screen is still
garbaged, hit ^L. Ugly but effective.
 
"Using open mode"
Again, it took me a long time to learn that when I tried to enter
vi and got this diagnostic, it meant that the system had somehow
lost the termcap for the terminal I was on, and that I would have
to do something to get the correct termcap into the environment.
Till I realized this, I generally ended up either struggling along
frustrated in open mode, or logging out and logging back in. I suggest
that when someone calls for vi and the termcap is not appropriate,
the editor should not be invoked in any form, but instead, a message
be given such as:
``Your environment does not show a termcap entry permitting
the use of the visual editor. If you are working on a terminal not
supporting vi (in particular, a device with no addressable cursor),
you may enter one of the other modes of the editor with the command
"open filename" or "ex filename". If you are working on a terminal
that should support vi, your environment entries are incorrect and
should be corrected. They presently show:
TERM=....
TERMCAP=....
If you know the correct name or abbreviation for your terminal-
type, type it at the end of the next line; if not hit return:
% setenv TERM ''
If the user typed an acceptable terminal-name, the message would
continue, telling how to get the appropriate termcap. If the user
instead typed return, the message would ask him or her to type the
name of the manufacturer shown on the terminal, not
worrying about upper/lower-case distinctions, and a list of possible
terminal names and abbreviations would be given... . This whole
program would not be part of the editor, so there would
be no problem of space within the existing crowded confines of
the editor code.
This, of course, doesn't belong in vi, but in login or tset. In fact,
tset is much friendlier these days. Vi will print a better diagnostic
if it knows you're on a "generic" terminal type such as "dialup" - in
terminfo there's a capability to indicate this, so it can print
I don't know what kind of terminal you have - all I have is "patchboard"
3.7 can't do this because it can't tell the difference between a generic
terminal and a hardcopy terminal.
 
"No such file or directory" -- I think there should be a distinction
between these two cases, because of the important distinction in the
consequences when the user tries to quit the editor:
The kernel doesn't distinguish, so it's hard for vi to. This is just a perror
string.
If the directory exists, the file is created, but
if not, the results are more complicated -- I seem to recall on one
occasion simply losing what I had written on my second try
at quitting; though I just now did an experiment and this time
repeated ZZ's and :x's simply gave repeated error messages.
Well, if it can't be reproduced, it doesn't stand much chance of getting fixed.
(Not that it would if you could reproduce it, of course.)
 
"File already exists..." -- The ``List of changes from 3.5 to 3.6'' says
``If you get I/O errors, the file is considered "not edited"... .''
I presume that this correction is somehow the cause of the fact that
I frequently get the above message when trying to leave the editor
on a machine with version 3.7, and have to use
:w! %|q
Ick! Most of us just type
:wq!
which is equivalent and much shorter.
to exit. But I've never seen any evidence that there were I/O errors;
it mainly seems to happen when I've written some lines to another
file in the process of editing. So the criteria the editor is using
to decide when there have been ``I/O errors'' should be rechecked.
Actually, if you get ANY error (e.g. :foo<cr>) it resets the "buffer modified"
condition. I have finally been convinced this is not right. For a while, I
considered it a necessary evil in case your /tmp/Ex* file got an I/O error.
 
"no such command from open/visual" -- This confused me in my first
few days of using the editor, when I didn't understand that one
couldn't use i and a (in either their vi or ex senses) from the bottom
line of vi. A message "i -- no such command from open/visual"
was perplexing because I knew that "i" was indeed a vi command.
Perhaps it should say "no such command from open/visual bottom line".
OK.
 
MISCELLANEOUS PROBLEMS
 
In ex search and replacement patterns, \\ is supposed to represent
a real \-character, but something goes wrong when this occurs
at the end of a global command. E.g., though
:s/^/\\
works OK (in vi or ex), the variant
:.g/^/s//\\
definitely does not. In vi it turns everything off, in ex it seems to
behave as though there were just a single \, and in a scriptfile,
it -- does something still different, which you can discover if you
don't know!
Backslash is special at the end of a line in global. You need more \\'s.
 
The Ex Reference Manual says, ``For sanity with use from within
visual mode, ex ignores a ":" preceding any command.'' But it
ignores it in the wrong place! -- not at the beginning of the
command line, but just before the command letter itself. I.e.,
it accepts 1,3:s/^/ /, but not :1,3s/^/ /.
Hmm.
 
SUGGESTIONS FOR MINOR ADDED CAPABILITIES
 
In a multiline substitute command with the "c" option, when
each line is displayed one has three choices: y, n or break. There
are some further options that would be useful. One would be "p" --
at present, "p" can only be included on the command line, which
means that one has a choice between seeing the result of every
substitution or none. In practice, one would generally like to see
the results of the first few cases to make sure that the command one has
written does what one meant it to, and maybe a few tricky cases that
come up; but not every case! Another might be "u" -- to undo the last
case for which one gave a "y". Still another might be an option that
would mean ``undo the "c" option -- I see that the substitute command
is doing what I wanted, go ahead and finish it without me.''
If I were going to do something this involved (I didn't know anybody used the
c option anymore - most people use "n" and "." in vi) I would do query-replace
ala EMACS right.
In a command g/pattern/p, the pattern in question is occasionally
such that it takes a while to figure out where on the line it occurs.
For this purpose, an option that ``pointed out'' the instance of the
pattern, in the same manner that the pattern to be replaced is pointed
out in substitute command with option c, would be desirable.
When g/pattern/p gives more than a screenful of lines, it would
be nice to have it piped through the equivalent of ``more''.
Nice but unlikely. Unless, of course, you have page mode in your tty driver,
like we do, in which case you get it for free.
 
ex has the command line option "-", which ``is useful in processing
editor scripts''. But if one wants to use a script in the course of
an otherwise interactive editing session, it would be desirable to have
a corresponding resettable option ``:se -'' (or ``:se nofb'').
Seems like a good idea.
 
In strings in pattern-searches, it would be useful to have
^ and $ retain their ``magic'', so that /x[a$]/ could
search for all occurrences of x before an a or a newline.
(Of course, one would then have to decide whether /x[^y]/ should
include the case of x followed by a newline or not.)
This sounds pretty hard to do.
 
Just as ex allows the command :vi, so I think that vi should
have some bottom-line command equivalent to the regular-mode
command Q. When one has done some text-changing bottom-line
commands, and realizes one wants to go into ex, it can be time-
consuming to hit return and then Q, and wait for the screen to be
redrawn for vi before one gets the ex prompt.
This would be ugly, since the ex command routine would have to return an
indication to the vi routine to exit back to the top level ex command.
But I suppose it could be done.
 
The option of putting several commands on one line, separated
by, "|" is particularly useful in the vi bottom-line mode, because
it avoids having the screen redrawn several times. It would be
useful to be able sometimes do the same thing with non-bottom-line
commands, e.g. in editing a troff file at a low baud rate on a dumb
terminal one might like to be able to do i\fI^]3Ea\fR^] without
watching the line get redrawn twice. Perhaps some key that would
cause any sequence of commands to be ``held'' until some complementary
key was hit would be useful.
It would also be desirable to have a sequence of commands that had
been given in this way available as one unit to be repeated by ``.'',
if desired.
See above. And get yourself a terminal with insert/delete char!
 
The parenthesis-matching facility with % might be extended
to match ` with ' and < with >.
OK.
 
I will mention one facility that I discovered by surprize is
possessed by ed but not ex -- sequences such as \1 can be used
within ed search-patterns. E.g. (for the most trivial case)
/\(.\)\1/
will search for doubled letters.
This surprises me.
 
 
DEBATABLE SUGGESTIONS
I will mention here some possible changes which have the
difficulty that they would change the meaning of existing commands,
so that it could be argued that the disadvantage of users having
to change their habits might outweigh the advantages.
 
First, one might try to resolve, one way or another, the
contradiction between the count arguments taken by the join commands
in vi and ex: In ex, jN joins N+1 lines; in vi, NJ joins N lines
(except if N=1).
Yeah, ex should be N, not N+1.
 
Second, the movement commands tx and Tx of vi (x any character)
seem poorly defined. Just as fx will ignore the character on which
the cursor is presently sitting, even if it is an x, and move to the
next occurrence, so I would think that tx should ignore the character
immediately after the cursor, and Tx the character immediately before
the cursor. The point is that when one does Nfx, and finds that one
had failed to count one occurrence of x and fallen short of where one
wanted to go, one can hit ; and get there. Likewise, on doing Ntx
and finding one has fallen short, one should be able to hit ; and get
to the the next occurrence; but at present, hitting ; leaves
the cursor in the same position; one must hit ``2;'' to get any
further. In effect, Ntx is presently defined as Nfxh; I am
suggesting that it be defined as lNfxh.
Agreed.
 
The sequences cw, dw and yw are presently violations of the
principle that c[movement], d[movement] and y[movement] change,
delete, or yank everything from the current cursor position through
the endpoint of the movement command. cw does what one would expect of
ce (in fact, they seem to be synonyms), while there is no way to get
the effect which cw would have if it were treated ``consistently''.
(E.g., if I have a line beginning ``And if'', and I want to change it
to ``If'', I cannot just put the cursor on the A and hit cwI^].) dw
and yw delete up to the character immediately before the point to
which ``w'' would take the cursor. I would have to agree that this
behavior of dw and yw is more useful than that which a literal
interpretation of the movement rule would lead to; but perhaps it
would become still more useful if when applied to the last word on
a line, it deleted or yanked the space immediately before the word
along with the word... . On the other hand, one could argue for
making a distinction between cw and ce.
This is to make the user interface friendlier, and is a fact of life.
If I wanted to change "And if" to "If", I'd type "dw~".
 
Though I see the motivation for the above definitions,
I see no sensible reason why Y should be equivalent to yy, when
C and D are equivalent to c$ and d$. I would vote for changing
Y to mean y$.
The users wouldn't stand for such a change. Too many are used to it like it is.
But you could always map it.
 
RADICAL SUGGESTIONS
 
Is there any reason for maintaining the distinction between
the ``:'' state of vi, and ex itself? At present, there are
relative advantages that lead one to choose to go into one or the
other for a given operation: From the vi-: state, it is easier
to return to the regular vi state; from ex, one has a more powerful
range of commands; and it is easier to give a series of commands
because each carriage-return gives one a new prompt. My suggestion
is that from vi, ``:'' should carry you directly to ex, and when you
are in ex, carriage-return (^M) after a command should give you a new
prompt, while ^] should put you into vi. Conceivably, things might be
simplified even further, and carriage return rather than : could
be the key that would carry one from the regular mode of vi into ex:
The basic problem here is that if : put you into ex mode, you'd have to redraw
the screen when you hit return. The motivation for : commands is that you
don't have to go through a conceptually hard mode change and wait for a
screen redraw.
 
.-------. .-------.
.-------. a,i... | basic | ^M | |
| vi |<------ | |----->| ex |<---.
| insert| | vi | | | |^M
| mode | ------>| |<-----| mode | ---'
`-------' ^] | mode | ^] | |
`-------' `-------'
 
(Of course, ^M presently has a meaning in vi, but
it has a synonym +.) Clearly, there would also be no need for a
special "Hit return to continue" state.
I have not distinguished vi and open in the above diagram.
My idea here is that ^] would actually return you to either vi
or open, whichever you had last been in, and that to switch
to the other, you could enter ex and type vi^] or o^] respectively.
(Or you could type vi^M, respectively o^M, and further ex commands,
and the mode would be saved for the next time you hit a ^].) Or
alternatively, these could be made settable options: se visual
respectively se novisual.
Having gotten used to the editor as it now exists, I admit that
I feel uneasy about the above idea -- the sense of knowing that
I am ``still in vi'' when I hit :, and not that ``other land'' of ex,
represents a kind of of orientation that it is disconcerting
to abandon. But I can't see any logical disadvantage in making
such a change. Can you? Certainly, there would be things that
would have to be thought out, such as what happens to bottom-line
vi pattern-searches. My thought would be that ``/'' from vi should
give :/ (i.e., put one in ex at the start of a pattern-search),
and ^] after a pattern-search should put one into vi at the appropriate
character on the line, in contrast to ^M after a pattern search,
which would leave one in ex at the appropriate line. In general,
I think such changes would lead to greater simplicity and learnability
of the editor.
I would also hope that excursions between vi and ex and back
could be allowed in scriptfiles. It might also be desirable for
ex to have, in addition to a concept of ``current line'', one of
``current cursor position''... .
 
Well, on to another subject. One of the inconveniences I
found very vexing when first learning to use the editor was that
when in either vi insert mode, or ex/vi-bottom-line, it was very hard
to edit what I was doing. Within insert mode the only ``editing''
I could do, without escaping, was with the three operations ^H,
^W and the kill character. And on a slow line with a dumb terminal,
escaping to make changes could be very time-consuming because large
parts of the screen would insist on being redrawn. Perhaps some
other control-character could serve as
a modified escape, that allowed one to edit what one had entered
in the current insertion without having everything below it redrawn,
and then return to it. Obviously, if carried to its logical
limit this idea could lead to ridiculous nests of
editing operations; but there would be no need to carry it to its
logical limit.
Why not just get a terminal with insert char? You're paying in performance
for having an obsolete terminal.
Anyway, the problem of editing ex-style commands
was even worse, because there was no way to ``escape and
revise''. I eventually learned enough to realize that the solution
was to edit complicated commands in another file and source it.
This is a standard complaint about a moded editor. It couldn't be fixed
without taking away the property of ESC ending command lines. Besides,
allowing editing on the bottom line would really break a lot of code.
But it is sometimes very useful to have the text on which the
commands are to act in front of you when composing them (e.g., you can
yank and modify various pieces), which led to the variant of writing
command lines within the file I was editing, and then writing
those lines into another file and sourcing that, without ever leaving
the current file. But this is distracting to deal with
when concentrating on the editing task itself, which led me
to divise a scriptfile which would handle the writing-to-another-file-
and-sourcing for me. Or actually, several such files: One for
single-line commands to be used essentially once; one for single-line
commands that I would want to use on the same file during various
editing sessions, and so would want to keep available in that
file, and one for multi-line commands (to be used once). When
I first got the idea, I thought one scriptfile would be enough, and
I would call it ``do'', so that the command to execute a script I
had written in a file I was editing would be ``:so do''. The
file it would write to and source would be ``do.again'', so that
if I wanted to reuse it, I could ``:so do.again''. When I realized
the need for several versions, ``do'' became a directory. Here,
for your amusement, are the three files. (Re the lines ``se prompt'',
cf. my comment on that under PROBLEMS WITH SOURCE etc.):
 
do/1 (for 1-time use of 1-line commands)
.w! ~/do/again
d
so #
se prompt
 
do/1+ (like above, without deleting the command)
.w! ~/do/again
so #
se prompt
 
do/: (to use this, write a multi-line command script, put : at
the beginning of the first line, put the cursor on the last
line of the script, and then source the following:)
?^:?s/:/
,''w! ~/do/again
,''d
so #
se prompt
 
(I also created another version to use in case the script had
to have an internal line beginning with ``:'', so that this couldn't
unambiguously mark the beginning of the script. This used
a line which explicitly specified the address-range of the script.
But I have never had a need for it, so I will not bother you with it.)
Finally, having gotten an account on a machine with a version 3
editor recently, I have divised still another way of doing this. I
have put in my EXINIT the command
 
'map ^A "ayy:@a^M'
 
and now, gratifyingly, the single stroke ^A has essentially the effect
of ``:so do/1+'' -- except for the restrictions to which vi ``map''
commands are subject. But I've only been using this for a
couple of weeks; so I have yet to learn how chafing those restrictions
will or won't be.
Anyway, it might be worth thinking about whether some of these
things that I've done with macros should be incorporated in some form
into the editor itself; or else whether these macros might be written
up in the documentation (or some tutorials) on the editor.
 
Next subject: Complicated pattern-searches in long files
can be time-consuming. I have seen the point mentioned
that if a pattern-description can be begun with "^",
this can speed up the search -- since the pattern-comparisons need
only be begun at beginnings of lines. In some cases, this might
not be possible, but the user might be aware of some other
character or character-sequence in the search-pattern
that will occur relatively rarely in the file. In such cases it would
be desirable if the user could specify one spot from which the pattern
search should start, working forward and backward from there, to
minimize false starts. E.g., if for some reason one wants to
delete every word containing the letter m, the script
%s/[^ ]*m[^ ]*//
would become much less time-consuming if one could mark the point
at which to begin, say writing
%s/[^ ]*\!m[^ ]*//
so as to instruct the editor to search for m's, and each time
one was found, to find the longest possible strings of non-space
characters before and after it, and delete these. (This is a silly
example, but I think the idea is clear.)
Isn't worth doing - this is fast enough for most people in most cases.
 
Something that I've seriously felt the need for is the
capability of searching for lines that satisfy more than one
condition. If one just wants to locate such lines, one can
of course leave the editor and do a pipeline of two or
more greps; but it would be nice to be able to perform global
commands on such lines.
You want the PWB "or" operator. This is hard to put in - their code
is really convoluted.
 
Finally, any possibility of introducing the capability of searching
for patterns including embedded newlines, a la sed?
Newlines aren't stored - the data structure is an array of lines.
So this is nearly impossible.
Multiple windows, a la emacs?
Would be easy after a rewrite, but impossible with current code.
What you really want is a window manager, anyway, most of the time.
 
ADDENDA
I logged in this morning on an adm3a at 300 baud to go over this
letter once more before sending it, and ran into another bug! I had
done 15^D to get a large scroll despite the low speed, and at one point
I saw a line with a typo scrolling up. So I noted its line-number, 402
and without waiting for the screen to stop moving typed something like
402Gfsrd. What happened was that the change was made on line 407 rather
than 402 -- presumably the cursor was sent to where 402 had been when
the command was received... .
Knowing the internals of vi, I'd say this is impossible. It probably just
screwed up your screen from line noise (or your terminal isn't truly full
duplex), or you got the wrong line number.
Editing this letter this morning reminded me of another feature I
have thought would be desirable for editing on dumb terminals at low
speeds: An option that would cause lines read from a file or typed
in at the bottom of the screen to appear double spaced, with @ signs
@
between them, such as one gets when one deletes a line on such a
@
terminal. (I have faked this effect here, though the fake will not be
@
very successful if you have se nu or se list on.) The point is that
@
editing operations that presently cause painfully slow screen-redrawings
would simply put material in in place of these fillers -- as happens
now when one is lucky enough to be adding material just above a place
where material was previously deleted.
Again, it would cost less to buy a real terminal. You can get one for $500
or so now from Falco or Liberty or Zenith.
 
Thanks again for the input.
 
Mark
 
 
From gbergman@UCBCARTAN Fri Jul 29 16:07:10 1983
Date: 29 Jul 83 16:02:06 PDT (Fri)
From: gbergman@UCBCARTAN (George Mark Bergman)
Subject: editor
Message-Id: <8307292302.AA09635@UCBCARTAN.ARPA>
Received: by UCBCARTAN.ARPA (3.342/3.7)
id AA09635; 29 Jul 83 16:02:06 PDT (Fri)
Received: from UCBCARTAN.ARPA by UCBERNIE.ARPA (3.336/3.7)
id AA06658; 29 Jul 83 16:07:04 PDT (Fri)
To: cc-03@ucbcory, danh@kim, lindahl@topaz, mckusick@ernie, ralph@ucbarpa
Status: RO
 
29/7/83
I got a reply from Horton: a copy of my (first) letter,
annotated with comments that this should indeed be fixed, that
would be impossible, etc.. I'll send a copy to anyone who'd
like (or a copy of his comment on some specific bug they're
interested in). Meanwhile, I'll send you my reply to him, since
it discusses still more bugs and possible improvements.
 
Dear Mark,
Got your comments on my first letter! Did you get the second,
shorter one? Glad to see that some things, at least, can be fixed.
Robert has put version 3.9 on this machine and I'm using it.
Using movement arrows within insert mode is amusing; though when there
is documentation, there should be a warning to the user that these
prevent ``u'' from undoing anything before those commands. But
the first thing there needs to be documentation for is vedit! Is
any being written?
In your comment on ``Can't yank inside global/macro'' you said
that one can, of course, always yank to a named buffer. I checked,
in the case of @a scripts, and the answer is yes and no: The yank
works, but one still gets that diagnostic, and anything else in the
sequence of commands is aborted.
And concerning the diagnostic ``Can't undo in global commands'' --
I understand what you say about why one can't, but it still seems an
unenlightening and perhaps inappropriate diagnostic to get when one has
done @a and buffer a contains say
x:g/foo/s//bar
Here the initial ``x'', deleting the character the cursor was on when
the command was given, creates the text-modified condition which,
as I mention, seems to cause globals in @-scripts to give this
diagnostic. I've just tested the above script -- several times,
because I found it hard to believe what was happening -- and it did
indeed give the diagnostic in question, but instead of replacing foo's
with bar's, it replaced the first line containing a foo with a copy of
the last line of the file! I leave it to you to figure that one out!
(This is on version 3.9. Incidentally, I've used a true ^M in that
script, so it is ready for you to try.)
 
Further observations on some of the bugs I mentioned in my
second letter: The business with yb is both more general and more
complicated than I realized. The general fact seems to be that when
one does a yank to a position earlier on the same line, the cursor does
not move, but the editor thinks it has, so that any subsequent command,
e.g. a movement, a delete, etc., has the effect that it would if it had
been done with the cursor starting from the new position. The
complication is that yanks to named buffers don't behave the same as
simple yanks: They also leave the cursor unmoved, but what they actually
yank into the buffer is the text from the cursor location to the end of
the line; and whether they cause the editor to consider the cursor to
be in a different location from where it really is seems to depend on
the movement in question: with "ayNb, no, with "ayN|, yes (where N
again stands for a positive integer). So what I called a snake in the
grass seems to be a can of worms!
In experimenting with this, incidentally, I've found that, while a
put from a named buffer can be undone with u, if one attempts the
command U, ``undo all changes made since coming onto this line'', only
changes made since the last named-buffer-put are undone.
I mentioned various abbreviations that were hard to unabbreviate,
but could be done with the help of ^V -- but that I had found no way
to undo
:ab if if\pq
To be precise, I had found that :una ^Vif, :una ^V^Vif, and
:una ^Vif where each ^V represents two ^V's typed in, didn't work.
I've finally found something that does: :una i^Vf.
 
I find the diagnostic ``No tail recursion'' strange,
since the occurrence of an abbreviation at the end of the item
abbreviated shouldn't cause a recursive loop; while circumstances that
do cause such a loop, namely the occurrence of the abbreviation within
the item abbreviated, preceded by a space and followed by punctuation,
are not censored, e.g. :ab x ( x ), or with more subtle effect,
:ab x x sup 2.
It seems that if one has :ab x word, then the abbreviation works
when x is preceded by a beginning-of-insert, a space, a tab, or a
newline, and followed by most any nonalphabetic character. For
word-processing use, it would be natural to allow it to also be
preceeded by (, ", `, [, perhaps even a user-specified set of
characters.
 
To my list of vi commands which I think should be able to take
counts, add p and P. I often want to put in a large number of copies of
one or more lines, as ``forms'' in which I will enter various kinds of
additional data, and it would be convenient to be able to do compose
such a line and then duplicate it the desired number of times all at
once. What I currently do is produce a copy and then go
yypk2yypk4yyp... till I have enough.
 
Two more commands I think would be useful: (1) One which "puts" to
the screen (in the sense of g/pattern/p, i.e. without affecting the
buffer) the contents, or the first lines in cases that are longer than
one line, of all nonempty buffers, named or numbered, with an
indication, of course, of which each was. (Ideally one would like to
be able to specify some subset of the buffers, whether one wants the
first line, or everything, or some specified number of lines, etc..)
(2) One which would show the script of the command stored to be used as
``.''. (And perhaps ditto with the last search pattern.)
Oh, yes: it would also be nice to have a way to give commands
without having them displace the one specified to be repeated by ``.'',
e.g. if one wants to do a certain change time after time at various
points in the file (using ``n'' and ``.'') but occasionally sees other
changes to be made along the way. An alternative to the above would be
a way to read the command stored to be used as ``.'' into a named
buffer, so that one can give other commands and then return to
that one. This would also allow one to reread the text of the command:
useful if it isn't behaving as one meant it to.
You might as well send any future mail to me here as
gbergman@cartan -- I was using brahms while cartan's terminals were
being moved but I'll probably be using cartan more from now on. But
I'll check mail on both. (However, I'll be on vacation in New York
State from the 3d to the 16th of August.)
Best regards,
George
 
From gbergman@ucbcartan Fri Sep 23 13:57:06 1983
Date: Fri, 23 Sep 83 13:53:36 PDT
From: gbergman@ucbcartan (George Mark Bergman)
Message-Id: <8309232053.AA02960@ucbcartan.ARPA>
Received: by ucbcartan.ARPA (4.6/3.7)
id AA02960; Fri, 23 Sep 83 13:53:36 PDT
Received: from ucbcartan.ARPA by UCBERNIE.ARPA (3.336/3.7)
id AA20684; 23 Sep 83 13:57:01 PDT (Fri)
To: cc-03@ucbcory, danh@kim, lindahl@topaz, mckusick@ucbernie, ralph@ucbarpa
Status: RO
 
Latest letter on ex~vi to Horton:
 
Dear Mark,
 
I hope I can assume that after my long letter to which you
replied, you did get my two shorter notes (one sent the same day,
the other about a week later); and that you've simply been to busy
to think about them or reply.
However, since I have had bad experiences with Mail, I am worried
that either you may never have gotten them or that you may have replied
and your replies not reached me. I hope you can send me a one-liner to
relieve my doubts.
 
Not many bugs to report this time, but I will modify or discuss
a few of the suggestions I've made before.
 
One that I want to modify is my suggestion that % should also
match < with > and ` with '. All right in itself, but terrible if it
would also mean that with :set match, every time one typed a > or a '
the cursor would look for the last occurrence of < or `, since <, >, and
' can occur in ways that have nothing to do with matching. So I think
that making matchable pairs user-specifiable would be much more useful.
Faith Fich (who send her regards -- she and Mike were our neighbors till
a month ago, but have left for their new jobs at U. Seattle) suggested
that user-settable matches would be particularly useful for those
like herself who use distinct opening and closing delimiters with eqn.
Actually, on further thought this isn't quite right, since eqn
delimiters behave differently from parentheses -- if I want to use =
as my delimiter to enter equation mode, and & as my delimiter to leave
it, then eqn will not give any special treatment to &'s in text mode or
='s in equation mode; so perhaps a different kind of matching for
that kind of use might be desirable; one would set :set dmatch =&
for the delimiters suggested above; or :set dmatch $$ if one uses $
for both delimiters as in the eqn tutorials. I know checkeq is
supposed to serve this function; but it would certainly be convenient
to have it in the editor; and anyway, my experience was that checkeq
didn't understand that one could have distinct opening and closing
delimiters. (But this is a disinterested suggestion; I haven't used
eqn for a long time; I put in my equations in raw troff.)
 
The suggestion in my long original letter about an alternative to
the diagnostic "using open mode" was based on a misunderstanding -- but
one that might be worth making a reality. I had been assuming,
since tutorials referred to the "three states" ex, vi, and open, and
since I knew one could enter the editor by typing % ex filename or
% vi filename, that a third way to enter the editor was by typing
% open filename, and that this is what one should do when editing
with a device not having an addressable cursor (which I had never done).
So I supposed that the diagnostic "using open mode" would only be
received if one were trying to use the wrong mode of the editor for the
tty type shown in one's environment. Perhaps one should indeed have
special way to enter the editor when one intended to edit in "open
mode". (If "open" has other uses as a command, the command might be
"vo" or "op".) Then if a user gave the command vi and his
environment did not indicate a device with an addressable cursor, it
would indeed be appropriate to give a diagnostic informing him that his
environment was not compatible with this command. I suggest this
because of the frustrating experiences I had as a beginner, of
being put into open mode (because my tty type had somehow been lost)
and not knowing what I could do about it.
 
I am somewhat confused by your explanation that editor scripts
that would go back and forth between ex and vi modes (using the :vi
and Q commands) are impossible because "The internals of getting a
character from the tty are completely different in ex and vi. Pushing
input for one doesn't affect the other." A vi "script" (i.e. a mapping
or a macro called by @x) can presently include bottom-line commands
called with ":". I would suppose that the way such commands get
characters from the terminal is about the same as the way ex does.
You also write that "Any : command that outputs more than 1 line puts
you into "ex mode"," -- is this a different sense of "ex mode"? If
not, what happens when a vi script does this?
I mentioned that the :so command from vi ignored anything after
any global command in the sourced file. I also find that it will not
accept the ex commands i and a; I realize that a prohibition against
these is part of the way vi's bottom line differs from genuine ex.
I've just done some experimenting, and I find that a script sourced
from the bottom line of vi also interprets the command vi as a command
to edit another file. Do we really want script files to be interpreted
so differently when sourced from ex mode and from the bottom line of vi?
Or if these differences are inevitable, can't the vi version at least
have the positive difference of being able to go to visual mode with
^M^M or something, and continue following the script, interpreting
characters as screen mode commands, just as a mapping or @x macro will
if it has gone down to the bottom line with ":" and come back again?
I am also still reluctant to give up the idea of a version of the
editor in which there is no difference between the line-oriented mode
and the bottom line of the visual mode. Suppose you took the editor as
it exists now, and modified it so that in visual mode, ":" was
interpreted as "Q", and in ex mode, ^[ was interpreted as ^Mvi. The one
obvious disadvantage would be that it would insist on redrawing the
screen after minor editing operations in ex mode. So suppose you made
it remember the previous state of the screen every time it entered ex
mode, and make the necessary adjustments on return to visual mode if the
editing done was fairly trivial -- using the same criteria it now uses
after bottom-line commands. What would be the problem?
Of course, I know I am quite ignorant of what is involved, and what
I am suggesting as possible may be quite impossible, or may have to wait
for that far-off day when you rewrite the editor.
 
You write that some of the restrictions on global commands are due
to the fact that the state of the buffer before the command is stored in
the "last deleted" buffer. But couldn't this situation be modified?
Let's call the "last deleted" buffer buffer 0, since material from it
seems to progress up to 1, 2, etc. with successive changes made.
Suppose things were set up so that during the operation of a global or
macro, deleted material, yanked material, etc. went by default into
buffer 1 instead of 0...? Or alternatively, that the state before the
global were saved in buffer 9, and this was then set outside of the
chain of buffers into which material got pushed with successive
deletions, during the operation of the global? Would this eliminate
the objection to nested globals as well? Presumably, in a global within
a global, the previous state would be stored in buffer 1, and new yanks
put by default in buffer 2 (if the first of the above suggestions were
followed).
You mention in your comments to my letter that the difficulty of
editing command lines "is a standard complaint about a moded editor"
that would be hard to fix. But I point out in that same letter how
appropriate macros or script-files can overcome that difficulty. (My
files "do/1", "do/:" and the "map ^A ..." in my EXINIT.) It should be
possible to introduce commands that would have the
same effects as those macros. (Or at least, to describe such macros
in the documentation. I have realized, by the way, that ^A was a poor
choice of character for me to map, because it occurs in the output of
function keys on a lot of terminals.)
 
It would be nice to extend the kinds of "regular expressions"
allowed in searches, e.g. a la egrep. Sometimes I also want to indicate
something like "a substring of precisely 40 characters". This can be
rendered as "........................................" at present,
but things like that can easily lead to commands that
exceed length limits. (Suppose one wants to look for a string of
40 successive characters other than whitespace. [^ ][^ ][^ ] ... is
too long.) Have you checked out what I mentioned in my first long
letter, that ed allows you to search for (e.g.) successive
identical characters as \(.\)\1?
 
Let me pass on a suggestion made recently in a system message by
someone named nye. He was worried about what would happen
if while one person was editing a file, someone else with write
permission for that file also began editing it. Presumably, whoever
did a "write" last would overwrite the other person's work. He
suggested that there be a diagnostic "file has been changed since last
write" for such a situation. (In particular, he wondered what would
happen if he got his mail by editing /usr/spool/mail/name, rather than
by using Mail -- which is certainly tempting if one is more comfortable
with the editor than with Mail -- and if unbeknownst to him new mail
arrived while he was editing.)
 
I have discovered the hard way that when one has done a
vi -r filename and decided one was satisfied with the version that
was saved, ZZ or :x are n o t the same as :wq -- the first two
discard the saved file, and only the last one writes it over the
previous version. (I actually haven't checked this out on version 3.9.
I tried to, and found that robert hadn't brought
/usr/lib/ex3.9{preserve,recover}, if that's what they're still
called, from arpa along with the rest of 3.9. But he's getting them for
me.) If you don't want to make ZZ and :x actually equivalent to
:wq, you might at least make them give error messages forcing the
user to make the explicit choice of :wq to keep the saved version, or :q
to discard it.
 
I've been having fun using recursive "map" commands from time to
time. For example, a file of mailing addresses of Math Departments is
being prepared, for use with a new Computer Services command "label".
This requires that each address constitute one line of the file, with
the intended lines of each label separated by
semicolons rather than newlines, each being no longer than 34
characters. At present, the lines in the file are of the form
[university-name];[city-state-zip]
and I wanted to find cases where the university name the secretaries
had typed in was over 34 characters long. So I did
:map ^A +36^V|F;^A0
Then, when I hit ^A, the cursor went bopping down the file, passing
through each line at which it could find a ";" before the 36th position,
but stopping when it hit a line where it could not. When I had
corrected such a line, by abbreviating a long word or whatever, I could
hit ^A again and find the next case. The "0" at the end of the map
command is to prevent the "No tail recursion" rule from aborting my
mapping. It has no other effect, because it is never reached.
Of course, the above example of a recursive mapping is guaranteed
to stop eventually, if only by reaching the end of the file. But I find
that mappings that can go on indefinitely are very hard to stop. E.g.
:map q ax^[q0
I've tried some like that, and sometimes hitting <break> or whatever
enough times successfully stops them (often leaving a bit of a mess --
characters that ought to have come after the string of x's embedded
among them), while other times the only thing I can do is to go to
another terminal and kill the editing process. Maybe it is to prevent
things like that that you put in the "No tail recursion" rule --
though clearly it can be gotten around. Might it not be better to
somehow make the editor not quite so deaf to <break>s during such a
process?
 
I will mention one curious experience which I have not been able to
reproduce. I use :set nu. After a delete near the beginning of a file,
I found myself looking at a screen with line-numbers beginning around
-2! The lines with nonpositive numbers were blank, if I recall; lines
1, 2, etc. were as they should be. I mentioned this to someone
who said he'd had the same thing had happened at times, but had
never been able to figure out what conditions caused it.
 
Best regards,
George
 
From gbergman@ucbcartan Thu Nov 3 22:32:01 1983
Received: from ucbcartan.ARPA by ucbernie.ARPA (4.17/4.13)
id AA25163; Thu, 3 Nov 83 22:31:44 pst
Date: Thu, 3 Nov 83 22:28:11 PST
From: gbergman@ucbcartan (George Mark Bergman)
Message-Id: <8311040628.AA06097@ucbcartan.ARPA>
Received: by ucbcartan.ARPA (4.6/3.7)
id AA06097; Thu, 3 Nov 83 22:28:11 PST
To: cc-03@BERKELEY, danh@kim, lindahl@topaz, mckusick@ucbernie, ralph@ucbarpa
Status: R
 
Latest letter to Mark Horton re editor bugs etc.:
 
copies sent to
cc-03@ucbcory
ralph@ucbarpa, lindahl@topaz, mckusick@ucbernie 25/7/83
danh@kim 26/7
Mark Horton's reply: anderson@kim 30/7/83
 
Dear Mark,
I got your reply to my last letter, but you don't say
whether you got the two preceding ones -- in particular, I'm
curious as to what you'd say about the peculiar behavior of
abbreviations terminated by \[character]. (E.g., suppose
someone did :ab ac artistic, and then, supposing the file was for
troffing, typed ac\fP. -- Try it!) And likewise, about what
happens if you hit @x, when register "x contains
x:g/foo/s//bar
(To see this wierd effect, you have to have a file with
an occurrence of ``foo'', and a distinctive l a s t line.)
 
In an earlier letter I commented that the mapping that
the editor sets up to enable the tvi's right-arrow key makes
^L unavailable for refreshing the screen. You replied that,
of course, the editor could not distinguish a ^L generated by
the arrow key from one typed as <ctrl>-L by the user. True,
but some users, such as myself, are quite happy using hjkl
to move around the screen (I use them completely by reflex)
and so have no wish to enable special arrow keys, but do
want to have ^L available to redraw the screen when
a message or something messes it up.
The problem with ^L occurred in using a tvi; now, using
a Z29 (in h19 mode), another version of the problem arises.
It has arrow keys that transmit things like ^]A, ^]B, etc.,
and version 3.9 not only maps these sequences, but also
map!s them. What I found happening is that if in typing
I made a change somewhere in the body of a line and then
wanted to add something at the end of the line, I would often
type a ^] to end the first change and then an A to
begin the second, with less than a second between them, and
this sequence would then be map!ed into ^]ka or
something, landing me in a different place from the one
I wanted. Aside from this major problem, there is the minor
inconvenience that the map! mechanism apparently waits
a second every time I type an ^] from insert mode to see
whether this is going to be one of the map!ed sequences, and
this makes exiting from insert mode sluggish. My temporary
solution has been to write a file of the form
unmap ^]A| unmap ^]B| ...|unmap! ^]A| unmap! ^]B|
which I source every time I go into vi on the Z29. I
guess what I should do is create simplified termcaps that
leave out the arrow keys for my own use when in version 3.7.
Kevin Layer tells me that when he puts 3.9 on all systems here,
there will be documentation on how to create terminfo files;
so I will be able to avoid these inconveniences in 3.9 as well.
What would be better, for these two-or-more character sequences,
though, would be if the "timeout" feature on mappings
could involve a variable interval, which could be set in the
millisecond range for terminal-generated sequences (I suppose
it would have to depend on the baud rate), and longer
for user-mapped sequences. The likelihood of the user
accidentally typing in one of the special sequences so fast
is negligible.
Incidentally, I notice that when I type :map to look
at the automatic mappings, these are labeled "up", "down" etc.,
though for mappings that I create the corresponding position
just shows a repeat of the mapped sequence. Is there
any way the user can put mnemonics in with his own mappings?
 
Two other minor points:
 
In your reply to my first letter, where I suggested that
N/pattern should take one to the Nth occurrence of /pattern,
you said that N/pattern actually resets the window size to N
while carrying one to /pattern. The tutorial says the same,
I believe, but nonetheless, it doesn't work!
When one has pulled a command into a buffer,
say "x, and invoked it with @x, if one then tries to get
a copy of this command by doing "xp, it doesn't seem to work.
The way I've found to make it work is to do any other
yank-and-put (using, say, the last-deleted
buffer). This somehow unfreezes the mechanism, and (after undoing
this last put, unless one wanted it), one can then successfully
do "xp.
Yours,
George
 
(Message inbox:32)
Date: Sun, 10 Jun 84 16:29:50 pdt
From: gbergman@ucbcartan (George Mark Bergman)
To: cc-03@ucbcory, danh@kim, decvax!tarsa, gbergman@ucbcartan,
hplabs!intelca!omsvax!isosvax!root, ihnp4!burl!we13!ltuxa!jab,
leblanc@ucbdali, lindahl@topaz, mckusick@ucbernie, priili@Ardc.ARPA,
ralph@ucbarpa, reiser@ruby, romine@seismo.ARPA, unisoft!eryk,
uw-beaver!ubc-vision!mprvaxa!sonnens
Subject: more editor bugs & ideas
 
Here's another letter of comments on the editor that I'm
sending to Mark Horton (mark@cbosgd).
If any of you to whom I'm sending this aren't interested in
staying on this mailing list, just let me know.
Horton replied to an earlier letter by saying he had no idea
when he'd have any time to work on the editor again, so I don't
expect replies from him to this and further such letters in the near
future.
For those who are not familiar with the subject of item "A."
below, modeline is a feature that he added without publicizing it much,
whereby if any line in the vicinity of the top or bottom of the file
(top and bottom 10 lines? I don't remember) contains the string
vi: or ex: and then another :, everything between these is
interpreted as a command and executed when this file is read by the
editor. There was a big squall in net.news when someone discovered
it by chance (an accidental string of this sort occurred in their
/etc/password; fortunately the "command" was meaningless, and evoked
a diagnostic from the editor). Some serious dangers of this
feature were pointed out by various contributors, one of whom described
for all who were interested how to eliminate it from the source file.
 
Dear Mark,
Here's another few month's collection of comments...
 
A. Modeline
I presume that in following the net.news discussion of the
``MAJOR BUG (modeline)'' you saw my two contributions; but I'll
summarize the relevant points (not in the order I made them):
 
1) One possible feature that would be about as convenient as
the modeline, and would avoid the dangers that people have pointed
out, would be `enhanced tags', in which the 3d entry of a line of the
tags file could be not merely a pattern search, but an arbitrary
command line.
 
2) I described (both in net.news and in an earlier letter to you) a
mapping in my EXINIT which makes one keystroke yank the current line
(or the next N lines if preceded by a count) to a named buffer
and then execute that buffer. If one keeps a set of initializing
commands within a file to which they are to apply, one can then
easily execute them on beginning an editing session, which gives
almost the convenience of the modeline feature, without the dangers,
and has an enormous range of other uses. So I think the modeline
feature could be dropped.
 
Let me add to those points:
 
3) A modeline
vi:r %:
leads to an infinite recursion! Fortunately, ^C cuts it off.
 
4) I agree with others' comments in net.news that if the modeline
feature is not dropped altogether, it should be a settable option
with default ``nomodeline''.
 
5) It should certainly be off when ``novice'' is set!
 
B. Tags
Having mentioned these in point A(1), I will give some other
comments I have: One of your update documents mentions the fixing
of a bug that left ``nomagic'' set if the file named in the tags
file did not exist. Very good, but one still ends up with ``nomagic''
if the file exists but the pattern-search is unsuccessful!
It would also be nice if the tags file could include file addresses
of the form ~user/filename, in particular ~/filename, and if the
command :set tags= recognized the same. (I suppose this makes no
difference to people who get their tags from ctags, but for me, the
tags file is maintained as a collection of items I have been working on
recently, or mean to soon, and entries are put in or removed by
hand regularly.)
 
C. Reversal of n and N
I also mentioned in one of my net.news comments a peculiar behavior
that often seems to occur after I've been using my yank-and-execute
mapping a lot, in which, after a command-line pattern-search :/pattern
(rather than simply /pattern), the screen-mode commands n and N give
searches in the reverse of the expected direction, with ? and /
respectively instead of vice versa. Perhaps you can figure out what
causes this; if not, would it help for me to do something like make
a core dump of the editor when it is happening and send it to you?
(I don't know how to send a nonascii file, though... .)
A few more observations on this behavior: Though I commonly
discover it when I am using my yank-and-execute mapping, it has
happened on at least one occasion when I hadn't use that at all,
so far as I could recall. It may actually happen quite frequently,
but people just don't notice it because they usually use /pattern
instead of :/pattern. (My mapping makes it more convenient for
me to use the latter when the pattern is complicated, or I want to
store it for repeated use.)
 
D. Update on dangerous recursions
Discovering that ^C interrupted the recursive modeline led me
to test it out on a recursive mapping, :ab x ( x ). It interrupts
it OK. Problem solved courtesy of 4.2BSD, I guess.
 
I will collect under the next heading my usual list of:
 
E. Minor bugs and modest suggestions
 
ye and yE yank one character less than they should.
 
If the command Ne (N a count) would land one on a 1-letter
word, one generally lands at the end of the next word instead
(even if it is also a 1-letter word. Exception: if one starts
at or after the end of the preceding word, e behaves as it should.)
 
Note also that in the line
Sentence... . Sentence
if the cursor is on the second S, the command ( causes no motion
at all.
 
I suggest that the motion commands { and } should accept indented
lines as paragraph-starts, or at least that there should be some
way of requesting this in the ":set para=" command. After all, these
motions shouldn't be useful only to people writing troff files!
 
In general, the command NC (where N is a count) changes material
from the cursor position to the end of the Nth line, leaving material
before the cursor on the current line unchanged. But if the line
is indented, and the cursor is on or before the first nonwhite
character, the preceding white text (spaces and tabs) is lost.
 
It should be possible to use
:unmap #1 :unmap! #1 :unab #1
when function-keys have been mapped.
 
Sometimes a noisy phone-line, termcap padding errors, etc.
cause just one or two lines of the screen to be messed up, and one
may only wish to refresh those lines. Could a command be introduced
which would do this? Ironically, on a dumb terminal one can generally
do this by moving the cursor over the line, but not on a smart terminal.
Another way one can do it is ddP, but I would sometimes feel uneasy
about unnecessarily modifying the text. I would
suggest that the form of the command be 1^L (2^L for 2 lines, etc.).
Currently, ^L apparently ignores counts. (Actually, I'm writing at
the moment on a tvi, so I've verified this for ^R rather than ^L.)
 
If one uses the source command, and the file sourced contains
a command
:e newfile
where newfile does not already exist, the diagnostic ``No such file
or directory'' aborts the sourcing process. One ought to be able to
use such commands in a sourced file.
 
In vi screen command mode, ^[ is supposed to ``cancel partially
formed commands'' and generally does so without protesting, but if the
partially formed command is a count (e.g., if one has typed 110 instead
of 10 and wishes to start over) it feeps, which depending on one's
terminal can be a minor or a major annoyance. (Also depending on
whether someone is trying to sleep in the next room.)
 
The diagnostic, ``First address exceeds second'' should not be
needed with one-address commands! The case where a series of addresses
before a command, of which the first may exceed the second, is most
useful is when the last address is a pattern-search preceded
by a ";", e.g.
:$;?^\.PP?-r otherfile
but let me give simpler examples; of the two commands
:3,1,2ka :1,3,2ka
the first correctly marks line 2, but the second is aborted by the
diagnostic quoted.
 
F. A feature that would be very desirable, and might or might not
be easy to implement.
 
In general, when one is inserting text on a smart terminal in vi,
the context below the text being added is pushed downward, line by line,
till none is left on the screen. I would like a settable option that
kept a certain number of lines of following context on the screen
during additions. The point is that one should see the material that
what one is writing will have to mesh with. What would be involved in
implementing this would, of course, depend on terminal capabilities.
If it would be difficult to keep an arbitrary number of lines,
would it at least be possible to have an option that would keep one
line, using the special bottom-line feature of some terminals?
 
G. More radical suggestions (wishlist).
 
1) Editing text with _u_n_d_e_r_l_i_n_i_n_g.
Although one of the valuable features of vi is the explicitness
with which most nonprinting characters are shown, this can be annoying
when one wants to deal with text in which many characters
are ``emphasized'' using the sequence _^H; e.g. nroff output. I
suggest a settable option under which such sequences would be shown as
with ul.
I realize that this would involve working out a great number
of details, e.g. would motion commands treat _^Hx as one
character or as three? How would the nth column be defined? How
would one place the cursor on one of the elements of the string
_^Hx for editing purposes? What would be done with _^H^A or _^H_^H....?
I think the best solution would be to treat _^Hx as a single
character for the purposes of motion commands, definition of nth
column, deletions, etc. when this option was set. In terms of placing
the cursor, two possibilities occur to me. One would be to only allow
the cursor to sit on the underlined character ``as a whole'', and to
have changes in underlining done by special commands: perhaps ^E as a
toggle to turn emphasis on and off in insert mode, _ to change
underlining in screen command mode as ~ changes capitalization
("_" is at present a synonym to "^", except
that it takes counts. ^ could be modified to take
counts, and _ then used as suggested above), and \e in replacement
patterns. The other would be to consider a cursor sitting ``on''
a sequence _^Hx to actually be on the x, and to set things up so that
if the cursor is on any of the other members of this sequence, the
sequence is ``expanded'' on the screen, i.e. shown as it is in the
present vi. Then define a single vi command so as not to skip over
the _ and ^H in such a sequence; namely ^H. (This would mean
making a distinction between h and ^H in screen command mode.)
This one motion would allow one to edit parts of such a sequence.
 
2) Editing several files at once.
When I have to do work that involves more than one file, the
repeated use of :w|e#, yanking text to named buffers to move it, losing
marked lines when I return to a previous file, etc. becomes
annoying. I think it would be desirable if one could make a group
of files behave like one file during an editing session, and move
around within that file as comfortably as one move within one file.
I suggest that visually, each file be separated from the next
by a pattern
:::::::::::::::::::
as when ``more'' is applied to a group of files.
For ``Go to file 3, line 20'' I suggest a screen command syntax
*3*20G. *-1* and *+2* could mean ``the preceding file'' and ``the
file after next'' in such commands. (The initial * could be optional
if there is no preceding + or -, as in the first example.) In a
command such as :w, the default address range would be all of the file
to which the current line belongs (i.e.,
it would be a synonym for :*.*w) To write all files, I suggest :**w.
:q, :x and ZZ would quit the editing session entirely, while :*1*q
would remove the buffer of file 1 from the object being edited.
On the other hand, relative motions such as 10j, H, etc. would work
within the ``visible object'', the union of the files.
%s/pattern/repl/ would apply to the file containing the current line,
and would have the synonym *.*s/pattern/repl/, while
*1,2,4*s/pattern/repl/ would affect the 3 indicated files, and
**s/pattern/repl/ would affect all files.
 
3) Input and output of shell escapes.
The various commands involving shell escapes that you have
set up allow four possible relations between the text being edited
and the input and output of the commands: none (:!command);
specified line-range as input with output not affecting text
(:address-range w !command); no input from text but output inserted at
specified line (:address r !command); and specified input from text
with output replacing input (:address-range !command).
It would be nice to have more flexibility; in particular, to
be able to include input from the file and place the output somewhere
else in the file without destroying the input text, and to input
more than one segment of text, e.g.
!egrep -f[addr.range] [other.addr.range] > [place of insertion]
Obviously, there would be a problem of setting up a syntax that would
avoid confusion with strings that look like address-ranges in the
shell command. Perhaps \[...\] could enclose address-ranges where
I have used [...] above.
 
4) ;
The ; syntax, allowing one to do a pattern-search starting
from a specified line is useful, but in setting up 2-address commands,
one does not necessarily want the point from which one starts the
search for the second address to be the first address. If this business
were being set up now, I would suggest that
address;/pattern/
should simply be a way of specifying the result of doing the indicated
pattern-search relative to the indicated address, so that
:address1,address2;/pattern/d
would delete from address1 to the location found by the pattern-search
relative to address2. Since people are used to the existing
syntax of ;, I suggest that some other symbol be used in the above
way, e.g. ], so that
:address1,address2]/pattern/d
could be interpreted as described.
 
5) Insertions into long lines on smart terminals at low or medium
baud rate (e.g. 1200).
This is annoying because the material coming after the point
of insertion begins to wrap around, and the cursor must jump back and
forth, inserting characters at the beginning of the
continuation line, then going back to the point of insertion, and so
on. (At least, this is my experience on my Z29. I haven't done
editing by phone connection on any other smart terminal.) It's
actually nicer on a dumb terminal, where the editor just overwrites,
and shows you the result after you escape. I suppose that the need
for the cursor to jump back and forth is due to the deficiency of the
terminals -- has anyone suggested to terminal manufacturers that along
with the wraparound feature, they add a feature which ``remembers''
when a line is a continuation of the preceding line, and automatically
pushes material from the preceding line into the continuation line
when characters are added to the former, eliminating the need to send
all these instructions in over a slow line? (Do terminal manufacturers
listen to editor-software specialists?) If not, it might just
be best to not show the pushed-over earlier material till the insertion
is complete.
 
6) Filename convention
This is really a suggestion for UNIX generally, but it could be
implemented on the editor separately. It is that for any file,
filename/.. denote the directory in which the file lies. (This
does not mean that every file should be treated as a directory, or
that the ls command should show filename/..; it would just
be a convenient way to refer to the directory containing a given
nondirectory file, consistent with the existing convention for
directories.) Within the editor, the important cases would be
%/.. and #/.., allowing commands such as:
:1,10w %/../othername
 
All for now! Yours,
George
 
/ports/trunk/editors/rvi/_xstr.c
0,0 → 1,453
/*
* 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
char copyright[] =
"@(#) Copyright (c) 1980 Regents of the University of California.\n\
All rights reserved.\n";
#endif not lint
 
#ifndef lint
static char sccsid[] = "@(#)xstr.c 5.3 (Berkeley) 1/13/86";
#endif not lint
 
#include <stdio.h>
#include <ctype.h>
#include <sys/types.h>
#include <signal.h>
 
/*
* xstr - extract and hash strings in a C program
*
* Bill Joy UCB
* November, 1978
*/
 
#define ignore(a) ((void) a)
 
char *calloc();
off_t tellpt;
off_t hashit();
char *mktemp();
int onintr();
char *savestr();
char *strcat();
char *strcpy();
off_t yankstr();
 
off_t mesgpt;
char *strings = "strings";
 
int cflg;
int vflg;
int readstd;
 
main(argc, argv)
int argc;
char *argv[];
{
 
argc--, argv++;
while (argc > 0 && argv[0][0] == '-') {
register char *cp = &(*argv++)[1];
 
argc--;
if (*cp == 0) {
readstd++;
continue;
}
do switch (*cp++) {
 
case 'c':
cflg++;
continue;
 
case 'v':
vflg++;
continue;
 
default:
fprintf(stderr, "usage: xstr [ -v ] [ -c ] [ - ] [ name ... ]\n");
} while (*cp);
}
if (signal(SIGINT, SIG_IGN) == SIG_DFL)
signal(SIGINT, onintr);
if (cflg || argc == 0 && !readstd)
inithash();
else
strings = mktemp(savestr("/tmp/xstrXXXXXX"));
while (readstd || argc > 0) {
if (freopen("x.c", "w", stdout) == NULL)
perror("x.c"), exit(1);
if (!readstd && freopen(argv[0], "r", stdin) == NULL)
perror(argv[0]), exit(2);
process("x.c");
if (readstd == 0)
argc--, argv++;
else
readstd = 0;
};
flushsh();
if (cflg == 0)
xsdotc();
if (strings[0] == '/')
ignore(unlink(strings));
exit(0);
}
 
char linebuf[BUFSIZ];
 
process(name)
char *name;
{
char *cp;
register int c;
register int incomm = 0;
int ret;
 
printf("extern char\txstr[];\n");
for (;;) {
if (fgets(linebuf, sizeof linebuf, stdin) == NULL) {
if (ferror(stdin)) {
perror(name);
exit(3);
}
break;
}
if (linebuf[0] == '#') {
if (linebuf[1] == ' ' && isdigit(linebuf[2]))
printf("#line%s", &linebuf[1]);
else
printf("%s", linebuf);
continue;
}
for (cp = linebuf; c = *cp++;) switch (c) {
case '"':
if (incomm)
goto def;
if ((ret = (int) yankstr(&cp)) == -1)
goto out;
printf("(&xstr[%d])", ret);
break;
 
case '\'':
if (incomm)
goto def;
putchar(c);
if (*cp)
putchar(*cp++);
break;
 
case '/':
if (incomm || *cp != '*')
goto def;
incomm = 1;
cp++;
printf("/*");
continue;
 
case '*':
if (incomm && *cp == '/') {
incomm = 0;
cp++;
printf("*/");
continue;
}
goto def;
def:
default:
putchar(c);
break;
}
}
out:
if (ferror(stdout))
perror("x.c"), onintr();
}
 
off_t
yankstr(cpp)
register char **cpp;
{
register char *cp = *cpp;
register int c, ch;
char dbuf[BUFSIZ];
register char *dp = dbuf;
register char *tp;
 
while (c = *cp++) {
switch (c) {
 
case '"':
cp++;
goto out;
 
case '\\':
c = *cp++;
if (c == 0)
break;
if (c == '\n') {
if (fgets(linebuf, sizeof linebuf, stdin)
== NULL) {
if (ferror(stdin)) {
perror("x.c");
exit(3);
}
return(-1);
}
cp = linebuf;
continue;
}
for (tp = "b\bt\tr\rn\nf\f\\\\\"\""; ch = *tp++; tp++)
if (c == ch) {
c = *tp;
goto gotc;
}
if (!octdigit(c)) {
*dp++ = '\\';
break;
}
c -= '0';
if (!octdigit(*cp))
break;
c <<= 3, c += *cp++ - '0';
if (!octdigit(*cp))
break;
c <<= 3, c += *cp++ - '0';
break;
}
gotc:
*dp++ = c;
}
out:
*cpp = --cp;
*dp = 0;
return (hashit(dbuf, 1));
}
 
octdigit(c)
char c;
{
 
return (isdigit(c) && c != '8' && c != '9');
}
 
inithash()
{
char buf[BUFSIZ];
register FILE *mesgread = fopen(strings, "r");
 
if (mesgread == NULL)
return;
for (;;) {
mesgpt = tellpt;
if (fgetNUL(buf, sizeof buf, mesgread) == NULL)
break;
ignore(hashit(buf, 0));
}
ignore(fclose(mesgread));
}
 
fgetNUL(obuf, rmdr, file)
char *obuf;
register int rmdr;
FILE *file;
{
register c;
register char *buf = obuf;
 
while (--rmdr > 0 && (c = xgetc(file)) != 0 && c != EOF)
*buf++ = c;
*buf++ = 0;
return ((feof(file) || ferror(file)) ? NULL : 1);
}
 
xgetc(file)
FILE *file;
{
 
tellpt++;
return (getc(file));
}
 
#define BUCKETS 128
 
struct hash {
off_t hpt;
char *hstr;
struct hash *hnext;
short hnew;
} bucket[BUCKETS];
 
off_t
hashit(str, new)
char *str;
int new;
{
int i;
register struct hash *hp, *hp0;
 
hp = hp0 = &bucket[lastchr(str) & 0177];
while (hp->hnext) {
hp = hp->hnext;
i = istail(str, hp->hstr);
if (i >= 0)
return (hp->hpt + i);
}
if ((hp = (struct hash *) calloc(1, sizeof (*hp))) == NULL) {
perror("xstr");
exit(8);
}
hp->hpt = mesgpt;
hp->hstr = savestr(str);
mesgpt += strlen(hp->hstr) + 1;
hp->hnext = hp0->hnext;
hp->hnew = new;
hp0->hnext = hp;
return (hp->hpt);
}
 
flushsh()
{
register int i;
register struct hash *hp;
register FILE *mesgwrit;
register int old = 0, new = 0;
 
for (i = 0; i < BUCKETS; i++)
for (hp = bucket[i].hnext; hp != NULL; hp = hp->hnext)
if (hp->hnew)
new++;
else
old++;
if (new == 0 && old != 0)
return;
mesgwrit = fopen(strings, old ? "r+" : "w");
if (mesgwrit == NULL)
perror(strings), exit(4);
for (i = 0; i < BUCKETS; i++)
for (hp = bucket[i].hnext; hp != NULL; hp = hp->hnext) {
found(hp->hnew, hp->hpt, hp->hstr);
if (hp->hnew) {
fseek(mesgwrit, hp->hpt, 0);
ignore(fwrite(hp->hstr, strlen(hp->hstr) + 1, 1, mesgwrit));
if (ferror(mesgwrit))
perror(strings), exit(4);
}
}
if (fclose(mesgwrit) == EOF)
perror(strings), exit(4);
}
 
found(new, off, str)
int new;
off_t off;
char *str;
{
if (vflg == 0)
return;
if (!new)
fprintf(stderr, "found at %d:", (int) off);
else
fprintf(stderr, "new at %d:", (int) off);
prstr(str);
fprintf(stderr, "\n");
}
 
prstr(cp)
register char *cp;
{
register int c;
 
while (c = (*cp++ & 0377))
if (c < ' ')
fprintf(stderr, "^%c", c + '`');
else if (c == 0177)
fprintf(stderr, "^?");
else if (c > 0200)
fprintf(stderr, "\\%03o", c);
else
fprintf(stderr, "%c", c);
}
 
xsdotc()
{
register FILE *strf = fopen(strings, "r");
register FILE *xdotcf;
 
if (strf == NULL)
perror(strings), exit(5);
xdotcf = fopen("xs.c", "w");
if (xdotcf == NULL)
perror("xs.c"), exit(6);
fprintf(xdotcf, "char\txstr[] = {\n");
for (;;) {
register int i, c;
 
for (i = 0; i < 8; i++) {
c = getc(strf);
if (ferror(strf)) {
perror(strings);
onintr();
}
if (feof(strf)) {
fprintf(xdotcf, "\n");
goto out;
}
fprintf(xdotcf, "0x%02x,", c);
}
fprintf(xdotcf, "\n");
}
out:
fprintf(xdotcf, "};\n");
ignore(fclose(xdotcf));
ignore(fclose(strf));
}
 
char *
savestr(cp)
register char *cp;
{
register char *dp;
 
if ((dp = (char *) calloc(1, strlen(cp) + 1)) == NULL) {
perror("xstr");
exit(8);
}
return (strcpy(dp, cp));
}
 
lastchr(cp)
register char *cp;
{
 
while (cp[0] && cp[1])
cp++;
return (*cp);
}
 
istail(str, of)
register char *str, *of;
{
register int d = strlen(of) - strlen(str);
 
if (d < 0 || strcmp(&of[d], str) != 0)
return (-1);
return (d);
}
 
onintr()
{
 
ignore(signal(SIGINT, SIG_IGN));
if (strings[0] == '/')
ignore(unlink(strings));
ignore(unlink("x.c"));
ignore(unlink("xs.c"));
exit(7);
}
/ports/trunk/editors/rvi/ex.1
0,0 → 1,153
.\" Copyright (c) 1980 Regents of the University of California.
.\" All rights reserved. The Berkeley software License Agreement
.\" specifies the terms and conditions for redistribution.
.\"
.\" @(#)ex.1 6.4.1 (2.11BSD) 1996/10/21
.\"
.\".TH EX 1 "October 21, 1996"
.TH EX 1 "May 22, 2000"
.UC 4
.SH NAME
ex, edit \- text editor
.SH SYNOPSIS
.B ex
[
.B \-
] [
.B \-v
] [
.B \-t
tag
] [
.B \-r
] [
\fB+\fIcommand\fR
] [
.B \-l
]
name ...
.br
.B edit
[
ex options
]
.SH DESCRIPTION
.I Ex
is the root of a family of editors:
.I edit,
.I ex
and
.I vi.
.I Ex
is a superset of
.I ed,
with the most notable extension being a display editing facility.
Display based editing is the focus of
.I vi.
.PP
If you have not used
.I ed,
or are a casual user, you will find that the editor
.I edit
is convenient for you.
It avoids some of the complexities of
.I ex
used mostly by systems programmers and persons very familiar with
.I ed.
.PP
If you have a \s-2CRT\s0 terminal, you may wish to use a display
based editor; in this case
see
.IR vi (1),
which is a command which focuses on the display editing portion of
.I ex.
.SH DOCUMENTATION
The document
.I "Edit: A tutorial"
(USD:14) provides a comprehensive introduction to
.I edit
assuming no previous knowledge of computers or the \s-2UNIX\s0 system.
.PP
The
.I "Ex Reference Manual \- Version 3.7"
(USD:16)
is a comprehensive and complete manual for the command mode features
of
.I ex,
but you cannot learn to use the editor by reading it.
For an introduction to
more advanced forms of editing using the command mode of
.I ex
see the editing documents written by Brian Kernighan for the editor
.I ed;
the material in the introductory and advanced documents works also with
.I ex.
.PP
.I "An Introduction to Display Editing with Vi"
(USD:15)
introduces the display editor
.I vi
and provides reference material on
.I vi.
In addition, the
.I "Vi Quick Reference"
card summarizes the commands
of
.I vi
in a useful, functional way, and is useful with the
.I Introduction.
.SH FILES
.DT
/usr/share/misc/exstrings error messages
.br
/usr/libexec/exrecover recover command
.br
/usr/sbin/expreserve preserve command
.br
/etc/termcap describes capabilities of terminals
.br
~/.exrc editor startup file
.br
/tmp/Ex\fInnnnn\fR editor temporary
.br
/tmp/Rx\fInnnnn\fR named buffer temporary
.br
/usr/preserve preservation directory
.SH SEE ALSO
awk(1), ed(1), grep(1), sed(1), grep(1), vi(1), termcap(5), environ(7)
.SH AUTHOR
Originally written by William Joy
.br
Mark Horton has maintained the editor since version 2.7, adding macros,
support for many unusual terminals,
and other features such as word abbreviation mode.
.br
This version was hacked by Gunnar Ritter
to support ISO-8859 characters.
.SH BUGS
The
.I undo
command causes all marks to be lost on lines changed and then restored
if the marked lines were changed.
.PP
.I Undo
never clears the buffer modified condition.
.PP
The
.I z
command prints a number of logical rather than physical lines.
More than a screen full of output may result if long lines are present.
.PP
File input/output errors don't print a name if the command line \fB`\-'\fR
option is used.
.PP
There is no easy way to do a single scan ignoring case.
.PP
The editor does not warn if text is placed in named buffers and not used
before exiting the editor.
.PP
Null characters are discarded in input files, and cannot appear in resultant
files.
.PP
In regular expressions,
only 7-bit ASCII characters can be used.
/ports/trunk/editors/rvi/ex.c
0,0 → 1,451
/*
* Copyright (c) 1980 Regents of the University of California.
* All rights reserved. The Berkeley software License Agreement
* specifies the terms and conditions for redistribution.
*/
 
#if !defined(lint) && defined(DOSCCS)
char *copyright =
"@(#) Copyright (c) 1980 Regents of the University of California.\n\
All rights reserved.\n";
 
static char *sccsid = "@(#)ex.c 7.5.1.1 (Berkeley) 8/12/86";
#endif not lint
 
#include "ex.h"
#include "ex_argv.h"
#include "ex_temp.h"
#include "ex_tty.h"
 
#ifdef TRACE
char tttrace[] = { '/','d','e','v','/','t','t','y','x','x',0 };
#endif
 
#if defined (__GLIBC__) && (__GLIBC__ >= 2)
#include <malloc.h>
#endif
 
/*
* The code for ex is divided as follows:
*
* ex.c Entry point and routines handling interrupt, hangup
* signals; initialization code.
*
* ex_addr.c Address parsing routines for command mode decoding.
* Routines to set and check address ranges on commands.
*
* ex_cmds.c Command mode command decoding.
*
* ex_cmds2.c Subroutines for command decoding and processing of
* file names in the argument list. Routines to print
* messages and reset state when errors occur.
*
* ex_cmdsub.c Subroutines which implement command mode functions
* such as append, delete, join.
*
* ex_data.c Initialization of options.
*
* ex_get.c Command mode input routines.
*
* ex_io.c General input/output processing: file i/o, unix
* escapes, filtering, source commands, preserving
* and recovering.
*
* ex_put.c Terminal driving and optimizing routines for low-level
* output (cursor-positioning); output line formatting
* routines.
*
* ex_re.c Global commands, substitute, regular expression
* compilation and execution.
*
* ex_set.c The set command.
*
* ex_subr.c Loads of miscellaneous subroutines.
*
* ex_temp.c Editor buffer routines for main buffer and also
* for named buffers (Q registers if you will.)
*
* ex_tty.c Terminal dependent initializations from termcap
* data base, grabbing of tty modes (at beginning
* and after escapes).
*
* ex_unix.c Routines for the ! command and its variations.
*
* ex_v*.c Visual/open mode routines... see ex_v.c for a
* guide to the overall organization.
*/
 
/*
* Main procedure. Process arguments and then
* transfer control to the main command processing loop
* in the routine commands. We are entered as either "ex", "edit", "vi"
* or "view" and the distinction is made here. Actually, we are "vi" if
* there is a 'v' in our name, "view" is there is a 'w', and "edit" if
* there is a 'd' in our name. For edit we just diddle options;
* for vi we actually force an early visual command.
*/
main(ac, av)
register int ac;
register char *av[];
{
#ifndef VMUNIX
char *erpath = EXSTRINGS;
#endif
register char *cp;
register int c;
bool recov = 0;
bool ivis;
bool itag = 0;
bool fast = 0;
extern int onemt();
#ifdef TRACE
register char *tracef;
#endif
 
#if defined (__GLIBC__) && (__GLIBC__ >= 2)
/*
* Disable the use of brk() by malloc,
* it has to use mmap() instead.
*/
mallopt(M_MMAP_THRESHOLD, 0);
#endif
 
/*
* Immediately grab the tty modes so that we wont
* get messed up if an interrupt comes in quickly.
*/
gTTY(1);
#ifndef USG3TTY
normf = tty.sg_flags;
#else
normf = tty;
#endif
ppid = getpid();
/*
* Defend against d's, v's, w's, and a's in directories of
* path leading to our true name.
*/
av[0] = tailpath(av[0]);
 
/*
* Figure out how we were invoked: ex, edit, vi, view.
*/
ivis = any('v', av[0]); /* "vi" */
if (any('w', av[0])) /* "view" */
value(READONLY) = 1;
if (any('d', av[0])) { /* "edit" */
value(OPEN) = 0;
value(REPORT) = 1;
value(MAGIC) = 0;
}
 
#ifndef VMUNIX
/*
* For debugging take files out of . if name is a.out.
*/
if (av[0][0] == 'a')
erpath = tailpath(erpath);
#endif
/*
* Open the error message file.
*/
draino();
#ifndef VMUNIX
erfile = open(erpath+4, 0);
if (erfile < 0) {
erfile = open(erpath, 0);
}
#endif
pstop();
 
/*
* Initialize interrupt handling.
*/
oldhup = signal(SIGHUP, SIG_IGN);
if (oldhup == SIG_DFL)
signal(SIGHUP, onhup);
oldquit = signal(SIGQUIT, SIG_IGN);
ruptible = signal(SIGINT, SIG_IGN) == SIG_DFL;
if (signal(SIGTERM, SIG_IGN) == SIG_DFL)
signal(SIGTERM, onhup);
#ifdef SIGEMT
if (signal(SIGEMT, SIG_IGN) == SIG_DFL)
signal(SIGEMT, onemt);
#endif
 
/*
* Process flag arguments.
*/
ac--, av++;
while (ac && av[0][0] == '-') {
c = av[0][1];
if (c == 0) {
hush = 1;
value(AUTOPRINT) = 0;
fast++;
} else switch (c) {
 
case 'R':
value(READONLY) = 1;
break;
 
#ifdef TRACE
case 'T':
if (av[0][2] == 0)
tracef = "trace";
else {
tracef = tttrace;
tracef[8] = av[0][2];
if (tracef[8])
tracef[9] = av[0][3];
else
tracef[9] = 0;
}
trace = fopen(tracef, "w");
#define tracbuf NULL
if (trace == NULL)
printf("Trace create error\n");
else
setbuf(trace, tracbuf);
break;
 
#endif
 
#ifdef LISPCODE
case 'l':
value(LISP) = 1;
value(SHOWMATCH) = 1;
break;
#endif
 
case 'r':
recov++;
break;
 
case 't':
if (ac > 1 && av[1][0] != '-') {
ac--, av++;
itag = 1;
/* BUG: should check for too long tag. */
CP(lasttag, av[0]);
}
break;
 
case 'v':
ivis = 1;
break;
 
case 'w':
defwind = 0;
if (av[0][2] == 0) defwind = 3;
else for (cp = &av[0][2]; isdigit(*cp); cp++)
defwind = 10*defwind + *cp - '0';
break;
 
 
#if 1 /* GR */
case 'V':
puts(versionstring);
exit(0);
#endif
default:
smerror("Unknown option %s\n", av[0]);
break;
}
ac--, av++;
}
 
/*
* Initialize end of core pointers.
* Normally we avoid breaking back to fendcore after each
* file since this can be expensive (much core-core copying).
* If your system can scatter load processes you could do
* this as ed does, saving a little core, but it will probably
* not often make much difference.
*/
fendcore = (line *) sbrk(0);
endcore = fendcore - 2;
 
#ifdef SIGTSTP
if (!hush && signal(SIGTSTP, SIG_IGN) == SIG_DFL)
signal(SIGTSTP, onsusp), dosusp++;
#endif
 
if (ac && av[0][0] == '+') {
firstpat = &av[0][1];
ac--, av++;
}
 
 
/*
* If we are doing a recover and no filename
* was given, then execute an exrecover command with
* the -r option to type out the list of saved file names.
* Otherwise set the remembered file name to the first argument
* file name so the "recover" initial command will find it.
*/
if (recov) {
if (ac == 0) {
ppid = 0;
setrupt();
execl(EXRECOVER, "exrecover", "-r", 0);
filioerr(EXRECOVER);
exit(1);
}
CP(savedfile, *av++), ac--;
}
 
/*
* Initialize the argument list.
*/
argv0 = av;
argc0 = ac;
args0 = av[0];
erewind();
 
/*
* Initialize a temporary file (buffer) and
* set up terminal environment. Read user startup commands.
*/
if (setexit() == 0) {
setrupt();
intty = isatty(0);
value(PROMPT) = intty;
if (cp = getenv("SHELL"))
CP(shell, cp);
if (fast || !intty)
setterm("dumb");
else {
gettmode();
if ((cp = getenv("TERM")) != 0 && *cp) {
setterm(cp);
}
}
}
if (setexit() == 0 && !fast && intty) {
if ((globp = getenv("EXINIT")) && *globp)
commands(1,1);
else {
globp = 0;
if ((cp = getenv("HOME")) != 0 && *cp) {
(void) strcat(strcpy(genbuf, cp), "/.exrc");
if (iownit(genbuf))
source(genbuf, 1);
}
}
/*
* Allow local .exrc too. This loses if . is $HOME,
* but nobody should notice unless they do stupid things
* like putting a version command in .exrc. Besides,
* they should be using EXINIT, not .exrc, right?
*/
if (iownit(".exrc"))
source(".exrc", 1);
}
init(); /* moved after prev 2 chunks to fix directory option */
 
/*
* Initial processing. Handle tag, recover, and file argument
* implied next commands. If going in as 'vi', then don't do
* anything, just set initev so we will do it later (from within
* visual).
*/
if (setexit() == 0) {
if (recov)
globp = "recover";
else if (itag)
globp = ivis ? "tag" : "tag|p";
else if (argc)
globp = "next";
if (ivis)
initev = globp;
else if (globp) {
inglobal = 1;
commands(1, 1);
inglobal = 0;
}
}
 
/*
* Vi command... go into visual.
* Strange... everything in vi usually happens
* before we ever "start".
*/
if (ivis) {
/*
* Don't have to be upward compatible with stupidity
* of starting editing at line $.
*/
if (dol > zero)
dot = one;
globp = "visual";
if (setexit() == 0)
commands(1, 1);
}
 
/*
* Clear out trash in state accumulated by startup,
* and then do the main command loop for a normal edit.
* If you quit out of a 'vi' command by doing Q or ^\,
* you also fall through to here.
*/
seenprompt = 1;
ungetchar(0);
globp = 0;
initev = 0;
setlastchar('\n');
setexit();
commands(0, 0);
cleanup(1);
exit(0);
}
 
/*
* Initialization, before editing a new file.
* Main thing here is to get a new buffer (in fileinit),
* rest is peripheral state resetting.
*/
init()
{
register int i;
 
fileinit();
dot = zero = truedol = unddol = dol = fendcore;
one = zero+1;
undkind = UNDNONE;
chng = 0;
edited = 0;
for (i = 0; i <= 'z'-'a'+1; i++)
names[i] = 1;
anymarks = 0;
}
 
/*
* Return last component of unix path name p.
*/
char *
tailpath(p)
register char *p;
{
register char *r;
 
for (r=p; *p; p++)
if (*p == '/')
r = p+1;
return(r);
}
 
/*
* Check ownership of file. Return nonzero if it exists and is owned by the
* user or the option sourceany is used
*/
iownit(file)
char *file;
{
struct stat sb;
 
if (stat(file, &sb) == 0 && (value(SOURCEANY) || sb.st_uid == getuid()))
return(1);
else
return(0);
}
/ports/trunk/editors/rvi/ex.h
0,0 → 1,446
/*
* Copyright (c) 1980 Regents of the University of California.
* All rights reserved. The Berkeley software License Agreement
* specifies the terms and conditions for redistribution.
*
* @(#)ex.h 7.7.1.1 (Berkeley) 8/12/86
*/
 
#ifdef V6
#include <retrofit.h>
#endif
 
/*
* Ex version 3 (see exact version in ex_cmds.c, search for /Version/)
*
* Mark Horton, UC Berkeley
* Bill Joy, UC Berkeley
* November 1979
*
* Changes by Gunnar Ritter, Freiburg i. Br., Germany
* May 2000
*
* This file contains most of the declarations common to a large number
* of routines. The file ex_vis.h contains declarations
* which are used only inside the screen editor.
* The file ex_tune.h contains parameters which can be diddled per installation.
*
* The declarations relating to the argument list, regular expressions,
* the temporary file data structure used by the editor
* and the data describing terminals are each fairly substantial and
* are kept in the files ex_{argv,re,temp,tty}.h which
* we #include separately.
*
* If you are going to dig into ex, you should look at the outline of the
* distribution of the code into files at the beginning of ex.c and ex_v.c.
* Code which is similar to that of ed is lightly or undocumented in spots
* (e.g. the regular expression code). Newer code (e.g. open and visual)
* is much more carefully documented, and still rough in spots.
*
* Please forward bug reports to
*
* Mark Horton
* Computer Science Division, EECS
* EVANS HALL
* U.C. Berkeley 94704
* (415) 642-4948
* (415) 642-1024 (dept. office)
*
* or to csvax.mark@berkeley on the ARPA-net. I would particularly like to hear
* of additional terminal descriptions you add to the termcap data base.
*/
 
#include <sys/param.h>
#include <ctype.h>
#include <errno.h>
#include <signal.h>
#include <setjmp.h>
#include <sys/stat.h>
 
#ifndef var
#define var extern
#endif
/*
* The following little dance copes with the new USG tty handling.
* This stuff has the advantage of considerable flexibility, and
* the disadvantage of being incompatible with anything else.
* The presence of the symbol USG3TTY will indicate the new code:
* in this case, we define CBREAK (because we can simulate it exactly),
* but we won't actually use it, so we set it to a value that will
* probably blow the compilation if we goof up.
*/
#ifdef USG3TTY
#include <termios.h>
#define CBREAK xxxxx
#else
#include <sgtty.h>
#endif
 
extern int errno;
 
#ifdef NCURSES
/*
* Some of the symbols collide.
*/
#define LINES LI_NES
#define cleanup clean_up
#define filter fil_ter
#define getch get_ch
#define longname long_name
#define ttytype tty_type
#define winch win_ch
#endif
 
#ifndef VMUNIX
typedef short line;
#else
typedef int line;
#endif
typedef short bool;
 
#include "ex_tune.h"
#include "ex_vars.h"
/*
* Options in the editor are referred to usually by "value(name)" where
* name is all uppercase, i.e. "value(PROMPT)". This is actually a macro
* which expands to a fixed field in a static structure and so generates
* very little code. The offsets for the option names in the structure
* are generated automagically from the structure initializing them in
* ex_data.c... see the shell script "makeoptions".
*/
struct option {
char *oname;
char *oabbrev;
short otype; /* Types -- see below */
short odefault; /* Default value */
short ovalue; /* Current value */
char *osvalue;
};
 
#define ONOFF 0
#define NUMERIC 1
#define STRING 2 /* SHELL or DIRECTORY */
#define OTERM 3
 
#define value(a) options[a].ovalue
#define svalue(a) options[a].osvalue
 
extern struct option options[NOPTS + 1];
 
 
/*
* The editor does not normally use the standard i/o library. Because
* we expect the editor to be a heavily used program and because it
* does a substantial amount of input/output processing it is appropriate
* for it to call low level read/write primitives directly. In fact,
* when debugging the editor we use the standard i/o library. In any
* case the editor needs a printf which prints through "putchar" ala the
* old version 6 printf. Thus we normally steal a copy of the "printf.c"
* and "strout" code from the standard i/o library and mung it for our
* purposes to avoid dragging in the stdio library headers, etc if we
* are not debugging. Such a modified printf exists in "printf.c" here.
*/
#ifdef TRACE
# include <stdio.h>
var FILE *trace;
var bool trubble;
var bool techoin;
var char tracbuf[BUFSIZ];
# undef putchar
# undef getchar
#else
#ifndef BUFSIZ /* GR */
# ifdef VMUNIX
# ifdef BIGMEM
# define BUFSIZ 4096
# else
# define BUFSIZ 1024
# endif
# else
# ifdef u370
# define BUFSIZ 4096
# else
# define BUFSIZ 512
# endif
# endif
#endif
# define NULL 0
# define EOF -1
#endif
 
#ifndef MAXBSIZE /* GR */
#define MAXBSIZE BUFSIZ
#endif
 
/*
* Character constants and bits
*
* The editor uses the QUOTE bit as a flag to pass on with characters
* e.g. to the putchar routine. The editor never uses a simple char variable.
* Only arrays of and pointers to characters are used and parameters and
* registers are never declared character.
*/
#ifndef BIT8
#define QUOTE 0200
#define TRIM 0177
#else
#define QUOTE 0x200
#define TRIM 0x1FF
short *ss_strcpy();
short *sc_strcpy();
char *cs_strcpy();
unsigned int s_strlen();
short *sc_strcat();
#endif
#undef CTRL
#define CTRL(c) (c & 037)
#define NL CTRL('j')
#define CR CTRL('m')
#define DELETE 0177 /* See also ATTN, QUIT in ex_tune.h */
#define ESCAPE 033
 
#ifdef ISO
#define niso(c) ((c) & QUOTE && ((unsigned char)(c)) < (QUOTE + ' '))
#endif
 
var const char *versionstring;
 
/*
* Miscellaneous random variables used in more than one place
*/
var bool aiflag; /* Append/change/insert with autoindent */
var bool anymarks; /* We have used '[a-z] */
var int chng; /* Warn "No write" */
var char *Command;
var short defwind; /* -w# change default window size */
var int dirtcnt; /* When >= MAXDIRT, should sync temporary */
/*#if defined (TIOCLGET) || defined (__linux__)*/
var bool dosusp; /* Do SIGTSTP in visual when ^Z typed */
/*#endif*/
var bool edited; /* Current file is [Edited] */
var line *endcore; /* Last available core location */
extern bool endline; /* Last cmd mode command ended with \n */
#ifndef VMUNIX
var short erfile; /* Error message file unit */
#endif
var line *fendcore; /* First address in line pointer space */
var char file[FNSIZE]; /* Working file name */
var char genbuf[MAXBSIZE]; /* Working buffer when manipulating linebuf */
var bool hush; /* Command line option - was given, hush up! */
#ifndef BIT8
var char *globp; /* (Untyped) input string to command mode */
#else
var short *globp; /* (Untyped) input string to command mode */
#endif
var bool holdcm; /* Don't cursor address */
var bool inappend; /* in ex command append mode */
var bool inglobal; /* Inside g//... or v//... */
#ifndef BIT8
var char *initev; /* Initial : escape for visual */
#else
var short *initev; /* Initial : escape for visual */
#endif
var bool inopen; /* Inside open or visual */
var char *input; /* Current position in cmd line input buffer */
var bool intty; /* Input is a tty */
var short io; /* General i/o unit (auto-closed on error!) */
extern short lastc; /* Last character ret'd from cmd input */
var bool laste; /* Last command was an "e" (or "rec") */
var char lastmac; /* Last macro called for ** */
var char lasttag[TAGSIZE]; /* Last argument to a tag command */
var char *linebp; /* Used in substituting in \n */
var char linebuf[LBSIZE]; /* The primary line buffer */
var bool listf; /* Command should run in list mode */
var char *loc1; /* Where re began to match (in linebuf) */
var char *loc2; /* First char after re match (") */
var line names['z'-'a'+2]; /* Mark registers a-z,' */
var int notecnt; /* Count for notify (to visual from cmd) */
var bool numberf; /* Command should run in number mode */
var char obuf[BUFSIZ]; /* Buffer for tty output */
var short oprompt; /* Saved during source */
extern short o_speed; /* Output speed (from gtty) */
var int otchng; /* Backup tchng to find changes in macros */
var short peekc; /* Peek ahead character (cmd mode input) */
var char *pkill[2]; /* Trim for put with ragged (LISP) delete */
var bool pfast; /* Have stty -nl'ed to go faster */
var int pid; /* Process id of child */
var int ppid; /* Process id of parent (e.g. main ex proc) */
var jmp_buf resetlab; /* For error throws to top level (cmd mode) */
var int rpid; /* Pid returned from wait() */
var bool ruptible; /* Interruptible is normal state */
var bool seenprompt; /* 1 if have gotten user input */
var bool shudclob; /* Have a prompt to clobber (e.g. on ^D) */
var int status; /* Status returned from wait() */
var int tchng; /* If nonzero, then [Modified] */
extern short tfile; /* Temporary file unit */
var bool vcatch; /* Want to catch an error (open/visual) */
var jmp_buf vreslab; /* For error throws to a visual catch */
var bool writing; /* 1 if in middle of a file write */
var int xchng; /* Suppresses multiple "No writes" in !cmd */
var int bsize; /* Block size for disk i/o */
 
/*
* Macros
*/
#define CP(a, b) (ignore(strcpy(a, b)))
/*
* FIXUNDO: do we want to mung undo vars?
* Usually yes unless in a macro or global.
*/
#define FIXUNDO (inopen >= 0 && (inopen || !inglobal))
#define ckaw() {if (chng && value(AUTOWRITE)) wop(0);}
#define copy(a,b,c) Copy((char *) a, (char *) b, c)
#define eq(a, b) ((a) && (b) && strcmp(a, b) == 0)
#define getexit(a) copy(a, resetlab, sizeof (jmp_buf))
#define lastchar() lastc
#define outchar(c) (*Outchar)(c)
#define pastwh() (ignore(skipwh()))
#define pline(no) (*Pline)(no)
#define reset() longjmp(resetlab,1)
#define resexit(a) copy(resetlab, a, sizeof (jmp_buf))
#define setexit() setjmp(resetlab)
#define setlastchar(c) lastc = c
#define ungetchar(c) peekc = c
 
#define CATCH vcatch = 1; if (setjmp(vreslab) == 0) {
#define ONERR } else { vcatch = 0;
#define ENDCATCH } vcatch = 0;
 
/*
* Environment like memory
*/
var char altfile[FNSIZE]; /* Alternate file name */
extern char direct[ONMSZ]; /* Temp file goes here */
extern char shell[ONMSZ]; /* Copied to be settable */
extern char ttytype[ONMSZ]; /* A long and pretty name */
var char uxb[UXBSIZE + 2]; /* Last !command for !! */
 
/*
* The editor data structure for accessing the current file consists
* of an incore array of pointers into the temporary file tfile.
* Each pointer is 15 bits (the low bit is used by global) and is
* padded with zeroes to make an index into the temp file where the
* actual text of the line is stored.
*
* To effect undo, copies of affected lines are saved after the last
* line considered to be in the buffer, between dol and unddol.
* During an open or visual, which uses the command mode undo between
* dol and unddol, a copy of the entire, pre-command buffer state
* is saved between unddol and truedol.
*/
var line *addr1; /* First addressed line in a command */
var line *addr2; /* Second addressed line */
var line *dol; /* Last line in buffer */
var line *dot; /* Current line */
var line *one; /* First line */
var line *truedol; /* End of all lines, including saves */
var line *unddol; /* End of undo saved lines */
var line *zero; /* Points to empty slot before one */
 
/*
* Undo information
*
* For most commands we save lines changed by salting them away between
* dol and unddol before they are changed (i.e. we save the descriptors
* into the temp file tfile which is never garbage collected). The
* lines put here go back after unddel, and to complete the undo
* we delete the lines [undap1,undap2).
*
* Undoing a move is much easier and we treat this as a special case.
* Similarly undoing a "put" is a special case for although there
* are lines saved between dol and unddol we don't stick these back
* into the buffer.
*/
var short undkind;
 
var line *unddel; /* Saved deleted lines go after here */
var line *undap1; /* Beginning of new lines */
var line *undap2; /* New lines end before undap2 */
var line *undadot; /* If we saved all lines, dot reverts here */
 
#define UNDCHANGE 0
#define UNDMOVE 1
#define UNDALL 2
#define UNDNONE 3
#define UNDPUT 4
 
 
/*
* Function type definitions
*/
#define NOSTR (char *) 0
#define NOLINE (line *) 0
 
extern int (*Outchar)();
extern int (*Pline)();
extern int (*Putchar)();
extern void (*oldhup)();
int (*setlist())();
int (*setnorm())();
int (*setnorm())();
int (*setnumb())();
line *address();
char *cgoto();
char *genindent();
char *getblock();
char *getenv();
line *getmark();
char *longname();
char *mesg();
char *place();
char *plural();
line *scanfor();
line *setin();
char *strcat();
char *strcpy();
char *strend();
char *tailpath();
char *tgetstr();
char *tgoto();
char *ttyname();
line *vback();
char *vfindcol();
char *vgetline();
char *vinit();
char *vpastwh();
char *vskipwh();
int put();
int putreg();
int YANKreg();
int delete();
int execl();
int filter();
int getfile();
int getsub();
int gettty();
int join();
int listchar();
off_t lseek();
int normchar();
int normline();
int numbline();
var void (*oldquit)();
void onhup();
int onintr();
void onsusp();
int putch();
int shift();
int termchar();
int vfilter();
#ifdef CBREAK
int vintr();
#endif
int vputch();
int vshftop();
int yank();
 
/*
* C doesn't have a (void) cast, so we have to fake it for lint's sake.
*/
#ifdef lint
# define ignore(a) Ignore((char *) (a))
# define ignorf(a) Ignorf((int (*) ()) (a))
#else
# define ignore(a) a
# define ignorf(a) a
#endif
/ports/trunk/editors/rvi/uparm.h
0,0 → 1,2
#define E_TERMCAP "/etc/termcap"
#define B_CSH "/bin/csh"
/ports/trunk/editors/vim/wrk/vim71/src/diff.c
828,7 → 828,11
/* Build the diff command and execute it. Always use -a, binary
* differences are of no use. Ignore errors, diff returns
* non-zero when differences have been found. */
sprintf((char *)cmd, "diff %s%s%s%s%s %s",
sprintf((char *)cmd,
#if defined(PLAN9)
"/bin/ape/"
#endif
"diff %s%s%s%s%s %s",
diff_a_works == FALSE ? "" : "-a ",
#if defined(MSWIN) || defined(MSDOS)
diff_bin_works == TRUE ? "--binary " : "",
943,7 → 947,11
{
/* Build the patch command and execute it. Ignore errors. Switch to
* cooked mode to allow the user to respond to prompts. */
sprintf((char *)buf, "patch -o %s %s < \"%s\"", tmp_new, tmp_orig,
sprintf((char *)buf,
#if defined(PLAN9)
"/bin/ape/"
#endif
"patch -o %s %s < \"%s\"", tmp_new, tmp_orig,
# ifdef UNIX
fullname != NULL ? fullname :
# endif
/ports/trunk/editors/vim/wrk/vim71/src/charset.c
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
/ports/trunk/editors/rvi/ex.news
0,0 → 1,1284
Version 3.7 October 16, 1981
 
It is now possible to split lines with substitute commands from vi,
by using ^V<return> in the rhs. This takes care of the last
good reason for using ex command mode.
 
Mode lines are now supported. Put a line containing
ex: xxx :
in the first or last 5 lines of the file, where xxx is any ex
command you want executed when that file is read in. To make
other programs happy, it should probably be inside a comment.
You can use vi: as well as ex:. (Note: no space allowed
between the "ex" and ":". The spaces around xxx are considered
part of the command, and so are optional.)
 
If there is a file .exrc in the current directory, it will be
sourced when you enter vi, after your EXINIT or ~/.exrc.
 
The changeable scrolling region of the vt100 is now used in
place of insert line, resulting in much better performance.
 
Vi uses the new AL, DL, LE, RI, DO, and UP (all upper case)
termcap capabilities, which are parameterized versions of their
lower case equivalents. This will results in better performance
on ANSI terminals, and especially on the Tektronix 4025 which
has parameterized local motions but cannot cursor address.
 
Vi uses backtabs again. Now it knows how to handle backtabs on
terminals that have tabs set at intervals other than 8, and it
won't use backtabs if gtty indicates it can't use tabs.
 
A bug causing <escape> <del> to core dump vi has been fixed.
 
A bug causing writes to filters when editing encrypted files
to write encrypted text has been fixed.
 
A bug causing the message "substitution loop", when you enter a
1,$s/xxx/yyy/g with lots of changes, has been fixed.
 
A bug causing the current directory to be chmodded to 0 if you
set nomesg after starting up the editor has been fixed. Note
that the nomesg option is intended for your EXINIT, since it only
takes effect on entry to the editor.
 
The eat newline glitch has been fixed so that vt100's and tab132's
handle long lines correctly.
 
Some internal changes have been made so that vi can run under UNIX
on the Bell Labs 3B machine, the BBN C/70, and the IBM 370.
 
Version 3.6 October 30, 1980
 
A kernel problem on the V7 pdp-11 overlay systems which causes
bad EMT traps to happen randomly, core dumping the editor,
has been programmed around by catching EMT traps.
 
A bug which prevented using a screen larger than 48 lines has
been fixed.
 
A bug which allowed you to set window to a value larger than
your screen size has been fixed.
 
The screen size limit on non-VM/Unix systems has been increased
to 66 lines or 5000 characters, to allow the Ann Arbor Ambassador
terminal to be used.
 
A bug which caused hangups to be ignored on USG systems has
been fixed.
 
A bug which caused maps with multiple changes on multiple lines
to mess up has been fixed.
 
If you get I/O errors, the file is considered "not edited" so
that you don't accidently clobber the good file with a munged
up buffer.
 
An inefficiency in 3.5 which caused the editor to always call
ttyname has been fixed.
 
A bug which prevented the "source" command from working in an
EXINIT or from visual has been fixed.
 
A bug which caused readonly to be cleared when reading from
a writable file with "r" has been fixed.
 
The name "suspend" has been made an alias for "stop".
 
The stop command now once again works correctly from command mode.
 
On a dumb terminal at 1200 baud, "slowopen" is now the default.
 
A bug in the shell script "makeoptions" which searched for a
string that appeared earlier in a comment has been fixed.
 
A bug that caused an infinite loop when you did ":s/\</&/g"
has been fixed.
 
A bug that caused & with no previous substitution to give
"re internal error" has been fixed.
 
A bug in the binary search algorithm for tags which sometimes
prevented the last tag in the file from being found has been fixed.
 
Error messages from expreserve no longer output a linefeed,
messing up the screen.
 
The message from expreserve telling you a buffer was saved when
your phone was hung up has be amended to say the editor was
terminated, since a kill can also produce that message.
 
The "directory" option, which has been broken for over
a year, has been fixed.
 
The "r" command no longer invokes input mode macros.
 
A bug which caused strangeness if you set wrapmargin to 1
and typed a line containing a backslash in column 80 has
been fixed.
 
A bug which caused the "r<cr>" at the wrapmargin column
to mess up has been fixed.
 
On terminals with both scroll reverse and insert line,
the least expensive of the two will be used to scroll up.
This is usually scroll reverse, which is much less annoying
than insert line on terminals such as the mime I and mime 2a.
 
A bug which caused vi to estimate the cost of cursor motion
without taking into account padding has been fixed.
 
The failure of the editor to check counts on ^F and ^B commands
has been fixed.
 
The "remap" option failed completely if it was turned off.
This has been fixed.
 
A check of the wrong limit on a buffer for the right hand side
of substitutions has been fixed. Overflowing this buffer could
produce a core dump.
 
A bug causing the editor to go into insert mode if you typed
return during an R command has been fixed.
 
A bug preventing the + command from working when you edit a
new file has been fixed by making it no longer an error to
edit a new file (when you first enter the editor.) Instead
you are told it is a new file.
 
If an error happens when you are writing out a file, such as
an interrupt, you are warned that the file is incomplete.
 
Version 3.5 -- August 20, 1980
 
The provisions for changing the window size with a numeric
prefix argument to certain visual commands have been deleted.
The correct way to change the window size is to use the z
command, for example z5<cr> to change the window to 5 lines.
 
The code to handle the -x (encryption) option has been made
conditionally compiled, so that ex can run on an an 11/34 (!)
with overlays. Since this code calls getpass, stdio was
being pulled in even without VMUNIX being defined. The
savings from not defining CRYPT are about 4K of text and 4.5K
of bss.
 
Bill Joy put in a buffering scheme under the VMUNIX flag so
that up to 64K of file is edited in-core until you make enough
changes to force a temp file sync. This makes entry into the
editor much faster, but also makes vi much bigger.
 
The source to ex is now sccs'ed.
 
An undocumented "feature" which caused the ^^ command to return
to the previous tag, if in the current file, instead of the
previous file, has been removed.
 
A bug which prevented ex from compiling on systems with the new
tty driver but no process control (such as Cory) was fixed.
 
Version 3.4 -- June 24, 1980
 
The visual page motion commands ^F and ^B now treat any preceding
counts as number of pages to move, instead of changes to the
window size. That is, 2^F moves forward 2 pages.
 
A :vi <file> command from visual mode is now treated the same
as a :edit <file> or :ex <file> command. The meaning of the
vi command from ex command mode is not affected.
 
Provisions to handle the new process stopping features of the
Berkeley TTY driver have been added. A new command, "stop",
takes you out of the editor cleanly and efficiently, returning
you to the shell. Resuming the editor puts you back in command
or visual mode, as appropriate. If autowrite is set and there
are outstanding changes, a write is done first unless you say
"stop!". From visual mode, the command ^Z is the same as :stop.
Note that if you have an arrow key that sends ^Z the stop function
will take priority over the arrow function. If you have your
"susp" character set to something besides ^Z, that key will be
honored as well.
 
A read only mode now lets you guarantee you won't clobber your
file by accident. You can set the on/off option "readonly" (ro)
and writes will fail unless you use an ! after the write.
Commands such as x, ZZ, and autowrite, and in general anything
that writes is affected. This option is turned on if you invoke
ex with the -R flag. A new link called "view" has been created.
View is just like vi but it sets readonly.
 
The encryption code from the v7 editor is now part of ex.
You invoke ex with the -x option and it will ask for a key,
as ed. The ed "x" command (to enter encryption mode from
within the editor) is not available.
 
The editor now adopts the convention that a null string in the
environment is the same as not being set. This applies to
TERM, TERMCAP, and EXINIT.
 
A word abbreviation mode is now available. You can define
abbreviations with the abbreviate command
:abbr foo find outer otter
which maps "foo" to "find outer otter". Abbreviations can be
turned off with the "unabbreviate" command. The syntax of these
commands is identical to the map and unmap commands, except
that the ! forms do not exist. Abbreviations are considered
when in visual input mode only, and only affect whole words
typed in, using the conservative definition. (Thus "foobar"
will not be mapped as it would using map!)
Abbreviate and unabbreviate can be abbreviated to "ab" and
"una", respectively.
 
The editor now supports certain terminals that use strings other
then \r and \n for return and linefeed by implementing the cr
and nl termcap options. (Thanks to UCLA for these enhancements).
 
The termcap attribute ns is now checked for, and ex refuses to
go into visual mode on such a terminal unless it has sf.
 
Terminals that can cursor address but cannot go up a line
now work in visual.
 
If you change your start and stop characters to something other
than the default ^S and ^Q, vi now turns them off. This causes
people who change them to escape not to lose so badly.
The quit character is once again turned off so that datamedias
which send ^\ for the right arrow key work.
 
The ~ command now repeats correctly with ".".
 
If you type in an unmatched ) or } in showmatch mode, the editor
will now beep to warn you about your mistake. The ) or } is
still accepted.
 
The way macros are undone has been improved considerably.
The number of changes inside the macro is counted, and just
before the second change the state before the macro is recreated
internally and saved to allow the macro to be undoable as a unit.
Hence, if the macro makes only one change the particular change is
undone (which will probably not redraw the screen). If no changes
are made (for example, the arrow keys) the previous thing that could
be undone is not clobbered.
 
The undomacro option has been deleted since it is no longer needed.
 
Editor scripts can now contain comments. Begin the comments with "
(double quote). Comments can be on their own line or come at the
end of command lines. The comment continues to the end of the line.
 
The 3rd version of the USG tty driver is now supported, making it
possible on USG systems to interrupt redrawing the screen and to
not flush output when interruptable commands take place.
 
The rewind command has been added to the list of commands that
the autowrite option knows about.
 
The wrapmargin option is now usable. The way it works has been
completely revamped. Now if you go past the margin (even in the
middle of a word) the entire word is erased and rewritten on the
next line. This changes the semantics of the number given to
wrapmargin. 0 still means off. Any other number is still a
distance from the right edge of the screen, but this location
is now the right edge of the area where wraps can take place,
instead of the left edge. Wrapmargin now behaves much like
fill/nojustify mode in nroff.
 
A bug on the USG system where hanging up the phone causes more
than one SIGHUP to be sent has been compensated for.
 
A bug which caused the :sh command not to send the vs and ti
sequences when you returned has been fixed.
 
A bug which caused a file that bombed out in the middle of
an edit command to be considered modified has been fixed.
 
A bug which caused the screen to be wrong after undoing a
:move command has been fixed.
 
A bug which messed up the buffer and the screen after
undoing a :join command has been fixed.
 
The source file ex_io.c has been split into ex_io.c and
ex_unix.c to avoid a problem where many C compilers overflowed
the symbol table.
 
A bug which prevented turning off your prompt in your .exrc
has been fixed.
 
Some of the code internal to the editor has been rearranged
and some comments added.
 
The bug fix to the USG tty driver to output a null character
as padding at 1200 baud has been improved to output a DEL
at 1200 baud or above.
 
Terminals with small screens (less than 20 columns or less
than 5 lines) should now work.
 
A bug which prevented you from entering the character DEL
into the buffer if you changed your interrupt character to
something else besides DEL has been fixed.
 
A bug which caused the current line to be clobbered when
you did a /, ?, or : command which mapped an input macro
successfully has been fixed.
 
If you map o to O and O to o and have remap set, the editor
now catches the infinite loop.
 
A put command after a macro now beeps instead of putting
a copy of the whole buffer. (Note that the arrow keys
on terminals are considered macros.)
 
A bug which caused things like d) and d} to miss the last
character when they should have deleted to the end of the
buffer has been fixed.
 
A bug which caused the last character to be lost when you
read in a file with no newline at the end of the last line
has been fixed.
 
A bug that caused garbage to be in the buffer if the temp file
overflowed has been fixed. (This only affected non-VMUNIX systems
since the temp file cannot overflow on VMUNIX.)
 
When a macro or global is undone, you no longer get picked up and
dropped on line 1.
 
The character | can now be escaped with \| in file names.
 
A bug which prevented the confirm option to a substitution that
was inside a source command has been fixed.
 
A bug which caused the editor to not work if the tab stop size
did not divide the screen width has been fixed.
 
A bug on HP terminals that caused the screen to be messed up if
you scrolled up something that began the same way as the echo line
has been fixed.
 
A macro bug which sometimes caused the next character after an
escape to be ignored on an HP terminal has been fixed.
 
A bug which caused unmap of strings with length 2 to fail has been
fixed.
 
A bug which left vi confused if you invoked a macro containing a
quit to command mode and then did an undo has been fixed.
 
An old ed bug which caused globals to fail when they did a substitute
on the next line has been fixed.
 
The % operator will now find matching square brackets the same way
it does parentheses and braces. It will not display them in
showmatch mode, however, and will not use a ] to match all ('s.
 
Code has been added to handle the Beehive Superbee terminal,
using f1 for escape and f2 for control C.
 
The default value of the option shell is now taken from the
environment variable SHELL, if present.
 
Version 3.3 -- February 2, 1980
 
The default window sizes have been changed. At 300 baud the
window is now 8 lines (was 1/2 the screen size). At 1200 baud
the window is now 16 lines (was 2/3 the screen size, which was
usually also 16 for a typical 24 line CRT). At 9600 baud the
window is still the full screen size. Any baud rate less than
1200 behaves like 300, any over 1200 like 9600.
 
A new command mode command "x" (for "xit") has been added. This
is the same as wq but will not bother to write if there have been
no changes to the file. The command letter was chosen for
convenience and compatibilty with hed.
 
The command "ZZ" from vi is the same as ":x<cr>". This is
the recommended way to leave the editor. Z must be typed twice
since this is two easy to type by accident and has such severe
effects if unintentional.
 
The options w300, w1200, and w9600 can be set. They are synonyms
for "window", but only apply at 300, 1200, or 9600 baud, resp.
Thus you can specify you want a 12 line window at 300 baud and
a 23 line window at 1200 baud with
:set w300=12 w1200=23
 
It is now possible to say
:set window=5
and get the effect the next time the screen is redrawn from scratch.
(^L and Hit return to continue don't start from scratch.)
This is sort of pointless, since both
5:<cr>
and
z5<cr>
do the same thing with better results.
 
The editor no longer uses nondestructive space, except when in
insert mode. It instead prints the character it would be
moving over. This is a real win on terminals that use an
escape sequence to nd space.
 
It is now possible from visual to string several search expressions
together separated by semicolons the same as command mode. For
example, you can say
/foo/;/bar
from visual and it will move to the first "bar" after the next "foo".
This also works within one line.
 
The option "mapinput" is dead. It has been replaced by a much
more powerful mechanism: :map! (e.g. put an ! after the map).
Map and unmap commands with ! apply only to input, others apply
only to command mode.
 
The new option "timeout" (default on) causes macros to time out
after one second. Turn it off and they will wait forever.
 
By using map! and setting notimeout, it is possible to get the
effect of emacs abbreviation mode. Sanity checking is turned off
for map when ! is present. This is a crude facility and does not
take into account things like the abbreviation being part of a longer
word. It also does not echo until it is satisfied.
 
The new option "remap" (default on) causes the editor to attempt
to map the result of a macro mapping again until the mapping fails.
This makes it possible, say, to map q to # and #1 to something else
and get q1 mapped to something else. Turning it off makes it possible
to map ^L to l and map ^R to ^L without having ^R map to l.
 
The new option "undomacro" (default on) makes it possible to undo
macros as a unit. Leaving it off causes macros not to be treated
specially. Macros with zero or one change work better with noum,
with two or more changes better with um. This option may go away
if it becomes unnecessary.
 
The new (string) valued option "tags" allows you to specify a list
of tag files, similar to the "path" variable of csh. The files
are separated by spaces (which are entered preceded by a backslash)
and are searched left to right. The default value is
"tags /usr/lib/tags", which has the same effect as before.
It is recommended that "tags" always be the first entry.
On Ernie, /usr/lib/tags contains entries for the system defined
library procedures from section 3 of the manual.
 
^R is now the same as ^L on terminals where the right arrow key
sends ^L (The tvi and the adm 31).
 
Looking for a tag now uses binary search.
 
The "q" command from visual no longer works at all. You must
use "Q" to get to ex command mode.
 
A minor incompatibility with the v7 ed has been fixed. Previously,
to do a global substitute with an escaped newline in the rhs, you
had to put two \'s in ex and one in ed. Ex now accepts the single
form as well as the double form. For example, instead of
g/foo/s//foo\\
bar/g
(which still works), you can now type, as in ed,
g/foo/s//foo\
bar/g
This means that the following ex command, which used to "work":
g/foo/s//foo bar\
.+1,/mumble/d
won't work anymore unless you put the trailing / on the substitution.
This usage is pretty obscure anyway.
 
Several bugs relating to undoing macros have been fixed.
 
A bug which caused the command "g/pattern" to print an error
message if "pattern" occurred on the last line has been fixed.
 
If you reply ":" to "Hit return to continue", you will again be
asked "Hit return to continue" after the next command finishes.
 
Limits have been raised so that an Ann Arbor terminal can be used,
and long tags can now be accomodated.
 
The maximum length of a string valued option has been raised from
32 to 64, for the benefit of the "tags" option.
 
It is now possible to search for an escape or delete using f, F, t,
T, ;, and ,. These characters must be quoted with ^V.
 
The option "ttytype" is now in correct alphabetical order.
 
A bug that caused HP terminals to mess up in insert mode when inserting
before a tab which follows 7 or fewer characters at the beginning of a
line (such as a tags file) has been fixed.
 
It is now possible to include control D in your EXINIT or .exrc.
 
A bug which caused the screen to mess up when a glob (such as xx*)
doesn't match anything has been fixed.
 
The editor now checks for extra junk after a /r.e./ from visual
(other than the allowed z command) and beeps if any is found.
Previously it was just ignored.
 
A bug that caused j and k (up and down) to behave strangely after
an insertion has been fixed.
 
A bug which causes term to be displayed incorrectly and which
caused a crash when changing terminal type when there happened
to be several |'s and a long string in genbuf has been fixed.
This bug was introduced in ex 2.9.
 
The patch for echo lines longer than 80 characters has been
repaired to do "Hit return to continue" after such lines and
print the entire output.
 
A bug that caused a messed up screen after a :sh command from
open mode has been fixed.
 
A bug which caused a tag request for a nonexistant tag to leave
the editor in nomagic mode has been fixed.
 
A bug which caused strange behavior if there is no default file
name when an autowrite save is attempted has been fixed.
 
A bug which caused the cursor to go to the wrong position when
^^D or 0^D is entered from column 2 in autoindent mode on terminals
that can backspace has been fixed.
 
Version 3.2 -- January 4, 1980
 
A bug that caused nomagic to be set if an error happened within
a tag command has been fixed.
 
A bug that caused put commands to beep after a macro containing
an error has been fixed.
 
The mapinput option has been placed in alphabetical order.
 
A bug that caused undo to undo more than one macro invocation
on the same line has been fixed.
 
On non VM/UNIX systems, the screen size has been increased to
allow a 40 X 80 Ann Arbor to be used.
 
Version 3.2 -- December 28, 1980
Several limits have been increased for VM/UNIX. Longer lines,
more characters of file names, longer regular expressions, etc.
Huge files can now be edited directly. Larger terminals (up to
66 lines) can be used.
 
An internal change has been made for VM/UNIX that causes error
messages to be stored directly instead of in a disk file.
This should cause faster response to errors.
 
Version 3.1.1 -- December 13, 1979
 
A bug that caused nested macros not to be undoable has been fixed.
 
A bug that caused pounding on the escape key on terminals with
arrow keys that send escape sequences to cause undo to screw up
has been fixed.
 
It is now acknowledged that macros cannot contain the put command.
This is due to the implementation of put - previously a put inside
a macro dumped a copy of the buffer instead of the desired text and
left the editor in a very strange state. Now such a put just beeps.
 
Version 3.1 -- November 1, 1979
 
Versions from 3.1 up are too large to fit on pdp-11's.
(Special overlay software is expected to be available soon
for v7 pdp-11 Unix that will make it fit.) Version 2.9
is 3.1 with only the bug fixes and very few of the enhancements.
2.9 will fit on a pdp-11. Version 2.10 will come out and may
correspond to 3.2. (It turned out to correspond to 3.3)
 
For compatibility with ed: 's<newline>' may be used as a
command and means '&'. If you set the option "edcompatible"
(abbr "ed") the presense or abscence of g and c suffices is
remembered and can be toggled by repeating the suffices. The
suffix "r" makes the substitution into "~" instead of "&".
 
A new command line option -w<n> sets the value of window before
starting ex. Hence: 'vi -w5 file' makes a quick change to a file
easier at 300 baud.
 
Arrow keys on terminals that send more than 1 character now
work. Home up keys are supported as are the four directions.
Ex no longer looks at the ma= entry in termcap, but uses the
ku, kd, kl, kr, and kh entries. (Note that the HP 2621 will
turn on function key labels, and even then you have to hold
shift down. To avoid turning on the labels, and to give up the
function keys, use terminal type 2621nl instead of 2621.)
 
A parameterless macro facility is included from visual. Briefly,
there are two flavors of macros:
a) Put the macro body in a buffer register, say x. Then
type @x to invoke it. @ may be followed by another @
to repeat the last macro.
This allows macros up to 512 chars.
b) Use the map command from command mode (typically in the
.exrc file) as follows:
map lhs rhs
where lhs will be mapped to rhs.
There are restrictions: lhs's should be 1-keystroke
(either 1 char or 1 function key) since they must be
entered within 1 second. lhs no longer than 10 chars,
rhs no longer than 100. To get " ", "\t", "|", or "\n"
into lhs or rhs, escape them with ctrl V. (It may be
necessary to escape the ctrl V with ctrl V if the map
command is given from visual mode.)
For 1 shot macros it is best to put the macro in a buffer register
and map a key to '@r', since this will allow the macro to be edited.
 
Macros can be deleted with
unmap lhs
 
The boolean option "mapinput" (mi) will, if on, cause macros to
be mapped in input mode as well as command mode (in visual only).
For example, you can define ctrl T to be four spaces with
:map ^V^T_^V^V____
:set mi
where underlines represent spaces and the ctrl V's are necessary
to get ctrl chars and spaces past various levels, and make ^T be
a software tab that even works in the middle of a line.
 
If the lhs of a macro is "#0" through "#9", this maps the particular
function key instead of the 2 char # sequence, if the terminal has
function keys. This only works if termcap has function key entries
for the particular terminal. For terminals without function keys,
the sequence #x means function key x, as typed. As a special case,
on terminals without function keys, the #x sequence need not be
typed within one second. The character # can be changed by using
a macro in the usual way:
map ^V^I #
to use tab, for example. (This won't affect the map command, which
still uses #, but just the invocation from visual mode.)
The undo command will undo an entire macro call as a unit.
 
New commands in visual: ^Y and ^E. These glitch the screen up
and down 1 line, respectively. They can be given counts, controlling
the number of lines the screen is glitched. They differ from ^U
and ^D in that the cursor stays over the same line in the buffer
it was over before rather than staying in the same place on the
screen. (^Y on a dumb terminal with a full screen will redraw the
screen moving the cursor up a few lines.) If you're looking for
mnemonic value in the names, try this: Y is right next to U and
E is right next to D.
 
More new commands in visual: '&' is a synonym for ':&<cr>'.
'~' changes the case of the letter under the cursor and moves
to the next character.
 
Ex looks in your environment for EXINIT. If it finds it, that
is used instead of looking for your .exrc. This should make
entry into ex faster, along with the termlib feature of looking
for a termcap entry in TERMCAP.
 
Version 2.13 -- September 23, 1980
 
The provisions for changing the window size with a numeric
prefix argument to certain visual commands have been deleted.
The correct way to change the window size is to use the z
command, for example z5<cr> to change the window to 5 lines.
 
An undocumented "feature" which caused the ^^ command to return
to the previous tag, if in the current file, instead of the
previous file, has been removed.
 
Version 2.12 -- July 23, 1980
 
A change was made to the sys_errlist array in ex_subr.c so that
Berkeley V7 quotas will produce the right error message.
 
A couple of minor bug fixes were made to get the editor to
compile on version 6. The option to use 1K BUFSIZ has been
deleted, since it is no longer used on our 1K system.
 
Version 2.11 -- June 24, 1980
 
The visual page motion commands ^F and ^B now treat any preceding
counts as number of pages to move, instead of changes to the
window size. That is, 2^F moves forward 2 pages.
 
A :vi <file> command from visual mode is now treated the same
as a :edit <file> or :ex <file> command. The meaning of the
vi command from ex command mode is not affected.
 
A read only mode now lets you guarantee you won't clobber your
file by accident. You can set the on/off option "readonly" (ro)
and writes will fail unless you use an ! after the write.
Commands such as x, ZZ, and autowrite, and in general anything
that writes is affected. This option is turned on if you invoke
ex with the -R flag. A new link called "view" has been created.
View is just like vi but it sets readonly.
 
The editor now supports certain terminals that use strings other
then \r and \n for return and linefeed by implementing the cr
and nl termcap options. (Thanks to UCLA for these enhancements).
 
The termcap attribute ns is now checked for, and ex refuses to
go into visual mode on such a terminal unless it has sf.
 
If you change your start and stop characters to something other
than the default ^S and ^Q, vi now turns them off. This causes
people who change them to escape not to lose so badly.
The quit character is once again turned off so that datamedias
which send ^\ for the right arrow key work.
 
If you type in an unmatched ) or } in showmatch mode, the editor
will now beep to warn you about your mistake. The ) or } is
still accepted.
 
Editor scripts can now contain comments. Begin the comments with "
(double quote). Comments can be on their own line or come at the
end of command lines. The comment continues to the end of the line.
 
The 3rd version of the USG tty driver is now supported, making it
possible on USG systems to interrupt redrawing the screen and to
not flush output when interruptable commands take place.
 
The rewind command has been added to the list of commands that
the autowrite option knows about.
 
A bug on the USG system where hanging up the phone causes more
than one SIGHUP to be sent has been compensated for.
 
A bug which caused a file that bombed out in the middle of
an edit command to be considered modified has been fixed.
 
The source file ex_io.c has been split into ex_io.c and
ex_unix.c to avoid a problem where many C compilers overflowed
the symbol table.
 
A bug which prevented turning off your prompt in your .exrc
has been fixed.
 
Some of the code internal to the editor has been rearranged
and some comments added.
 
The bug fix to the USG tty driver to output a null character
as padding at 1200 baud has been improved to output a DEL
at 1200 baud or above.
 
Terminals with small screens (less than 20 columns or less
than 5 lines) should now work.
 
A bug which prevented you from entering the character DEL
into the buffer if you changed your interrupt character to
something else besides DEL has been fixed.
 
A bug which caused things like d) and d} to miss the last
character when they should have deleted to the end of the
buffer has been fixed.
 
A bug which caused the last character to be lost when you
read in a file with no newline at the end of the last line
has been fixed.
 
A bug that caused garbage to be in the buffer if the temp file
overflowed has been fixed.
 
The character | can now be escaped with \| in file names.
 
A bug which caused the editor to not work if the tab stop size
did not divide the screen width has been fixed.
 
A bug on HP terminals that caused the screen to be messed up if
you scrolled up something that began the same way as the echo line
has been fixed.
 
An old ed bug which caused globals to fail when they did a substitute
on the next line has been fixed.
 
The % operator will now find matching square brackets the same way
it does parentheses and braces. It will not display them in
showmatch mode, however, and will not use a ] to match all ('s.
 
Ex looks in your environment for EXINIT. If it finds it, that
is used instead of looking for your .exrc. This should make
entry into ex faster, along with the termlib feature of looking
for a termcap entry in TERMCAP.
 
Internally, it is possible to turn off about a dozen different
options when compiling the editor to make it fit in 64K.
See the makefile for a list of options.
 
Version 2.10 -- February 2, 1980 (Corresponds to 3.3)
 
The default window sizes have been changed. At 300 baud the
window is now 8 lines (was 1/2 the screen size). At 1200 baud
the window is now 16 lines (was 2/3 the screen size, which was
usually also 16 for a typical 24 line CRT). At 9600 baud the
window is still the full screen size. Any baud rate less than
1200 behaves like 300, any over 1200 like 9600.
 
A new command mode command "x" (for "xit") has been added. This
is the same as wq but will not bother to write if there have been
no changes to the file. The command letter was chosen for
convenience and compatibilty with hed.
 
The command "ZZ" from vi is the same as ":x<cr>". This is
the recommended way to leave the editor. Z must be typed twice
since this is two easy to type by accident and has such severe
effects if unintentional.
 
The options w300, w1200, and w9600 can be set. They are synonyms
for "window", but only apply at 300, 1200, or 9600 baud, resp.
Thus you can specify you want a 12 line window at 300 baud and
a 23 line window at 1200 baud with
:set w300=12 w1200=23
 
The "q" command from visual no longer works at all. You must
use "Q" to get to ex command mode.
 
The editor no longer uses nondestructive space, except when in
insert mode. It instead prints the character it would be
moving over. This is a real win on terminals that use an
escape sequence to nd space.
 
A minor incompatibility with the v7 ed has been fixed. Previously,
to do a global substitute with an escaped newline in the rhs, you
had to put two \'s in ex and one in ed. Ex now accepts the single
form as well as the double form. For example, instead of
g/foo/s//foo\\
bar/g
(which still works), you can now type, as in ed,
g/foo/s//foo\
bar/g
This means that the following ex command, which used to "work":
g/foo/s//foo bar\
.+1,/mumble/d
won't work anymore unless you put the trailing / on the substitution.
This usage is pretty obscure anyway.
 
A bug which caused the command "g/pattern" to print an error
message if "pattern" occurred on the last line has been fixed.
 
Limits have been raised so that an Ann Arbor terminal can be used,
and long tags can now be accomodated.
 
A bug that caused HP terminals to mess up in insert mode when inserting
before a tab which follows 7 or fewer characters at the beginning of a
line (such as a tags file) has been fixed.
 
A bug which causes term to be displayed incorrectly and which
caused a crash when changing terminal type when there happened
to be several |'s and a long string in genbuf has been fixed.
This bug was introduced in ex 2.9.
 
The patch for echo lines longer than 80 characters has been
repaired to do "Hit return to continue" after such lines and
print the entire output.
 
A bug that caused a messed up screen after a :sh command from
open mode has been fixed.
 
A bug which caused a tag request for a nonexistant tag to leave
the editor in nomagic mode has been fixed.
 
A bug which caused strange behavior if there is no default file
name when an autowrite save is attempted has been fixed.
 
A bug which caused the cursor to go to the wrong position when
^^D or 0^D is entered from column 2 in autoindent mode on terminals
that can backspace has been fixed.
 
In order to get 2.10 to fit on a v7 pdp-11, the following features
have been deleted:
The MASTERTAGS feature (undocumented use of /usr/lib/tags
as an alternate tag file)
Checking that a file being read in is an ascii file.
Turning off ^Q/^S on a v7 system.
 
Version 2.9 -- November 1, 1979
 
The meanings of semicolon and newline, broken in 2.8, have been fixed.
Newline with two arguments still prints the range, unless a semicolon
was present, in which case only the last line is printed. Semicolon
otherwise behaves as in ed (and ex 2.7).
 
For compatibility with ed: '%' is an abbreviation for '1,$'.
The default starting line for 'z' is '.+1' instead of '.'.
If 'z' is followed by a number, this number is remembered
(by setting the scroll option).
 
The + options to the command line invocation and to the edit
command now also work for the next command. In addition, the
text after the + is no longer limited to a line number or
/ or ? search string, but can be any single command. (It cannot
contain spaces except on command line invocation, and then must
be quoted to make the shell happy.) The only special case is
where + is used by itself - this is the same as +$.
 
The way window sizes and scrolling commands are based on the options
window and scroll has been rearranged. All command mode scrolling
commands (z and ctrl D) are based on scroll: ^D moves scroll lines,
z moves scroll*2 lines. Everything in visual (^D, ^U, ^F, ^B, z,
window sizes in general) are based on the window option. The
defaults are arranged so that everything seems as before, but
on hardcopy terminals at 300 baud the default for scroll is
11 instead of 6.
 
Whether ex prompts for commands now depends on the setting of the
prompt variable, so inside script you can say 'set prompt' and
get ex to prompt.
 
Tags are now searched for in nomagic mode instead of the funny
mode where magic characters were impossible to get.
 
Paragraphs and sections with one letter names (such as those
used by PWB/MM) now work - use a space (escaped by a backslash)
for the second letter. Default paragraphs and sections are included
for both MM and MS. (Thanks to adb for this)
 
A bug involving 16 bit arithmetic on a vax for the yank command
has been fixed.
 
The text of the mailed message from expreserve has been improved
slightly.
 
The editor now always turns off the XTABS stty bit when in visual
mode, making terminals that do special things with ^I work.
 
The editor now knows about terminals with destructive tabs, like
the teleray 1061, having the xt option.
 
A bug that caused going past column 80 on terminals with insert line
but not insert char (like the mime, h1500, or i100) to mess up the
screen has been fixed.
 
A bug on 2621's that causes lines longer than 80 chars long with
embedded tabs to mess up when a tab was inserted has been fixed.
 
A bug that caused the wrong line to suddenly appear under very
rare circumstances involving small window sizes and long lines
where a search left the cursor on the top line of the screen
has been fixed.
 
The bug that caused inverse video to sometimes be scrolled up
into the file from an error message has been fixed.
 
The join command has been fixed, so that '3,3j' no longer joins
lines 3 and 4. ('3j' still does.) Thus, '/a/;/b/-j' works right
even if b is found on the line after a.
 
ex -v now finds your .exrc. In related changes, the default in
vi is now magic and nobeautify.
 
If your buffer is empty, ex won't refuse to do an edit, quit,
or tag command because you haven't done a write.
 
A bug causing visual undo not to work after '1,$!cat' has been
fixed.
 
Ex now decides for itself whether to use CBREAK or TIOCSETN
by whether they are defined in <sgtty.h>. This eliminates much
of the #ifdef USG or V6 lines. One USG line remains due to a bug
in the USG tty driver at 1200 baud.
Note that this will mess up if you use libretro because <sgtty.h>
has CBREAK and TIOCSETN defined. Take these out of sgtty.h to fix this.
 
Termcap options TI and TE have been added. These strings are
respectively output at the beginning and end of the editing session.
 
Values for the set command may now include `\ ' for space and
control characters.
 
Changes have been made to /etc/termcap (several new fields have
been added) and to termlib (it now looks for TERMCAP in the environment
and treats it as a termcap entry if the name of the terminal mentioned
is the same as TERM and the entry doesn't start with a slash.
If it starts with a slash it is treated as a filename, as before.
Termcap also checks the 512 byte entry limit and skips lines beginning
with # as comments. It is possible to define one terminal as being
similar to another one with a few differences without making two
copies of the description.) New termcap fields:
ti terminal initialization string. This should be sent
out at the beginning of any program that addresses
the cursor.
te Like te but at end of the program.
(Thanks to adb for these two fields)
us Start underlining.
ue End underlining.
uc Underline one character & move over it.
hc (bool) terminal is hardcopy
ns (bool) terminal doesn't scroll (tektronix)
ff (bool) hardcopy knows ^L means formfeed.
pt (bool) hardware tabs, maybe set by is
xt (bool) destructive tabs (teleray 1061)
ku sequence sent by keypad "up" arrow
kd "down" arrow
kl "left" arrow
kr "right" arrow
kh "home" arrow
ks sequence to make keypad send these codes
ke sequence to make keypad not send these codes.
k0-k9 sequences sent by up to 10 "other" keys
l0-l9 labels on k0-k9. If omitted, default = "f0" - "f9".
ko additional keys on keypad, in terms of their
termcap entry. For example, if "home down" and "clear"
are present and send the same codes as ll and cl, use
:ko=cl,ll:
tc This entry is a list of differences from the named
entry. THIS MUST BE THE LAST FIELD. Example: hp2621
with no ks or ke (e.g. null string):
hn|hp2621nl:ks@:ke@:tc=hp2621:
The @ cancels the string even if it is defined later.
 
Version 2.8 -- July 18, 1979
 
It is now possible to backspace over the first character (:, /, or ?)
on the echo line from visual. The effect is as though delete were hit
except the bell isn't rung.
 
The trailing slash in global commands is now optional.
g/pat means g/pat/p
(This change, as well as the corresponding changes to the substitute
command and r.e. address are also in the latest version of ed.)
 
The j, k, and l keys now move the cursor down, up, and right,
respectively, in visual mode, as they used to do (and still do on
3a's). This is to avoid the creeping of these keys into the map
descriptions of terminals and to compensate for the lack of arrow
keys on terminals like HP's.
 
Two arguments given to a newline command now print the range of
lines instead of just the last line (as though 'p' were appended).
To make forms like /foo/;/bar/ still work, the ; operator sets
the dot as before but then forgets everything to the left of the ;.
 
The + option invoked from the shell or the edit command has
two new forms: +/pat and +?pat
These cause the initial line to be chosen by a search for the
pattern pat. Note that if any special characters are in the
argument (such as ^, $, and even ?) it must be quoted.
 
Two new options are added: autowrite (aw) and hardtabs (ht).
Autowrite is a toggle, off by default. When on, if you have
unsaved changes before a context switching command, a write
is done automatically. The commands that may write are !,
next, and tag. Note that there is an equivalent way to do the
command with autowrite set without the write in each case:
shell, tag!, and edit do not write.
 
Hardtabs is a numeric option,, set to 8 by default.
Changing this to, say, 4, tells ex that either your system
expands tabs to every 4 spaces, or your terminal has hardware
tabs set every 4 spaces.
 
A bug that caused strange behaviour when an echo line contained
more than 79 characters (from a long : command or one or more long
filenames) has been patched by not printing any such characters
past column 79.
 
Handling of systems with nonstandard locations of files (where
the maintainer of ex is not a superuser and cannot create files
with names like /usr/lib/ex2.0strings or /etc/termcap) has been
improved. If the file can't be found as is, it is tried in the
current directory. If that fails, ex tries to run without it.
(Previously it bombed immediately if the error message file wasn't
in /usr/lib.)
 
Shell commands containing ! or % characters are no longer echoed
when in hush mode (as in 'ex -' from a shell file.)
 
Version 2.7 -- June 10, 1979
 
An inefficiency introduced in version 2.3, which increased the
amount of time spent preparing output by approximately 30 percent
has been corrected.
 
A bug which caused ``wrapmargin'' to work as though all hardcopy
terminals were 160 columns wide has been corrected.
 
A bug which caused the display to become confused after the display
of a long line at the bottom of the screen was suppressed (being
replaced temporarily by an @) has been fixed. Previously, under
some circumstances (e.g. after a put created the situation), scrolling
up of the following text would cause the display of this long
line to be skipped, so that the @ line would remain and the line
itself would not be displayed.
 
Version 2.6 -- June 2, 1979
 
A bug which prevented the first field separator in a tags file
from beginning with a blank has been fixed; if the separator
was a blank previously, the tag would not be found in the tags file.
 
A bug which caused the display to be messed up after a ``:''
escape which created long lines has been fixed. Previously
a substitute command which changed the last few lines on the screen
to be very long would leave the screen messed up.
 
A bug in display after 2 successive ``undo'' commands has been fixed.
Previously if you opened new lines on the display, and then did
2 successive undo commands, the display would be messed up after the
second undo if your terminal had insert/delete line.
 
A bug on intelligent terminals which caused unnecessary delete
character commands to be sent has been fixed. This occurred when
you did not have ``autoindent'' set, and opened a new line
below an existing line with tabs.
 
The change operations in open mode on hardcopy terminals has been
fixed. Previously there were several bugs in cursor placement
when the change extended to just before a tab character.
 
Several bugs in the handling of tabs in insert mode on intelligent
terminals have been fixed. Previously, tabs would often expand
incorrectly, leaving the wrong amount of white space, when an
insert occurred just before a tab.
 
A bug has been fixed which caused the editor to skip processing
of the ``.exrc'' file when the terminal type set in the
environment was unknown. The editor now processes ``.exrc'' in
this case.
 
[[A number of formatting changes have been made to the editor code
to eliminate unreasonably long lines. In addition, the code
from the Murray Hill and USG sites has been merged in conditionally,
so that all sites can compile from the same source.]]
 
Version 2.5 -- May 28, 1979
 
A bug which caused the VE sequence not to be sent when exiting
the editor via :q or :wq from visual has been fixed.
 
A bug which caused the command r^Q<ESC> to be weird when it
was repeated has been fixed.
 
The $ command now sets the column for future cursor motions to
effective infinity. Thus a `$' followed by up/down cursor motions
moves at the right margin of each line.
 
[[Internal: a bug in conditional compilation without the LISP
features has been fixed.]]
 
Several bugs relating to insert mode and intelligent terminals
have been fixed:
 
A bug which caused inserts on HP/DATAMEDIA like terminals to act
strangely when the material was inserted immediately before a tab
has been fixed.
 
A bug which caused the insertion of full tabs to not appear to
insert as many spaces as required (under strange circumstances)
has been fixed.
 
A bug which caused inserts on terminals with insert/delete line
but no insert/delete character to act strangely if the insert
caused a line to overflow has been fixed.
 
The ``expreserve'' program has been improved; you now will get
mail if a file is saved for you as a result of your phone being
hung up accidentally.
 
Version 2.4 -- May 19, 1979
 
A bug during inserts on intelligent terminals which occasionally
caused double ``~~'' characters on the last few lines of the display
rather than just single `~' characters has been fixed.
 
The w W b B e and E operations in visual now wrap around line
boundaries. Thus a sequence of enough w commands will get to any
word below the current position in the file, and b's will back
up to any place before. Thus these are more like the sentence
operations ( and ). You still can't back around line boundaries
duing inserts however.
 
Version 2.3 -- May 13, 1979
 
The P command to ex is now a synonym for p, so that 1,$P works,
if you don't let up on the shift key soon enough.
 
The / and ? operations within visual and open now hit later
(or earlier resp) occurrences of the same string on the same
line. This makes scans using / and ? much more useful. You
can move to the right on the current line by typing /pref<ESC>
where `pref' is a prefix of the word you wish to move to, and
delete to a following string `str' by doing d/str<ESC> if it is
on the same or succeeding line. Previously the command
d/pat/
deleted lines through the next line containing `pat'; it now
deletes text up to the next instance of `pat'. To delete to
the next line containing `pat', do
d/pat/0
which is short for
d/pat/+0
In general if you use an offset after the scanning pattern,
whole lines will always be affected.
 
Several bugs relating to the setting of the previous context mark
`` have been fixed, including one which caused operations such as
d`` or c`` to occasionally dump core. In particular, the operations
( ) { } [[ ]] and %
now set the previous context mark correctly, and the mark is set
even if the motion by these operations lands in the same line.
 
More optimization is now done on output cursor motions. This
is particularly much better on HP terminals which have
ridiculously long cursor addressing sequences. A new
capability has been added to the termcap file to aid this:
``bt'' (backtab). Thanks to Chuck Haley for the new code to
implement this.
 
A bug has been fixed on intelligent terminals which caused part
of the screen to be accidentally erased during insertions.
This occurred only on the first line on the screen, when it
became longer than one displayed line and only if a part of
the screen (at the top) was currently not in use.
 
A bug has been fixed which caused the command ``dp'' to be interpreted
as ``delete to register p''. This normally went unnoticed since
the ``autoprint'' option would cause the effect which the ``p'' was
forcing.
 
Version 2.2 -- May 6, 1979
 
"d)" now deletes a line if the current line is a sentence rather
than leaving an empty line.
 
The command
:s/str
now deletes str if it can find it; previously it was an error.
 
The editor now handles multiple ":" escapes correctly; previously
the screen would not be redrawn necessitating a ^L to fix it if you
gave a `:!command' to ``[Hit return to continue]''.
Recursive calls to visual from within open or visual are no longer
permitted. Previously ``:vi'' from within open mode would eventually
leave the editor in a strange state.
 
The %age in the status line is now correctly printed on 11's;
Previously internal 16-bit overflows often caused it to be incorrect.
 
The editor now ignores a ":" in front of commands.
Thus you can say ``:read foo'' within ex.
 
A bug which caused commands involving ]] to not be repeatable has
been fixed. Previously ``d]]'' followed by ``.'' caused an error.
 
"ayw now works correctly. Previously this silently did nothing.
 
Several bugs in "recover" and "ex -r" have been fixed. Thanks
to Andy Koenig for the fixes.
 
In input mode in open and visual ^V (like tenex) is now equivalent
to ^Q (which is reminiscent of ITS) superquoting the next character.
A later version of the UNIX tty driver will implement the standard for
^S ^Q handshaking and make ^Q unusable.
 
There are several typos on page 3 of the ``edit'' manual section:
s/move "a/delete a/
s/"a move ./put a/
/move to copy/s//delete to yank/
 
Version 2.1 -- April 5, 1979
 
Invoking ex via
ex -l
now sets "lisp" and "showmatch". This is suitable for invocations
from within Franz Lisp. If you don't like "showmatch", you can
still use "ex -l", just put the command
set noshowmatch
in your .exrc file.
 
/ports/trunk/editors/rvi/ex_addr.c
0,0 → 1,313
/*
* Copyright (c) 1980 Regents of the University of California.
* All rights reserved. The Berkeley software License Agreement
* specifies the terms and conditions for redistribution.
*/
 
#if !defined(lint) && defined(DOSCCS)
static char *sccsid = "@(#)ex_addr.c 7.3 (Berkeley) 6/7/85";
#endif not lint
 
#include "ex.h"
#include "ex_re.h"
 
/*
* Routines for address parsing and assignment and checking of address bounds
* in command mode. The routine address is called from ex_cmds.c
* to parse each component of a command (terminated by , ; or the beginning
* of the command itself. It is also called by the scanning routine
* in ex_voperate.c from within open/visual.
*
* Other routines here manipulate the externals addr1 and addr2.
* These are the first and last lines for the current command.
*
* The variable bigmove remembers whether a non-local glitch of . was
* involved in an address expression, so we can set the previous context
* mark '' when such a motion occurs.
*/
 
static bool bigmove;
 
/*
* Set up addr1 and addr2 for commands whose default address is dot.
*/
setdot()
{
 
setdot1();
if (bigmove)
markDOT();
}
 
/*
* Call setdot1 to set up default addresses without ever
* setting the previous context mark.
*/
setdot1()
{
 
if (addr2 == 0)
addr1 = addr2 = dot;
if (addr1 > addr2) {
notempty();
error("Addr1 > addr2|First address exceeds second");
}
}
 
/*
* Ex allows you to say
* delete 5
* to delete 5 lines, etc.
* Such nonsense is implemented by setcount.
*/
setcount()
{
register int cnt;
 
pastwh();
if (!isdigit(peekchar())) {
setdot();
return;
}
addr1 = addr2;
setdot();
cnt = getnum();
if (cnt <= 0)
error("Bad count|Nonzero count required");
addr2 += cnt - 1;
if (addr2 > dol)
addr2 = dol;
nonzero();
}
 
/*
* Parse a number out of the command input stream.
*/
getnum()
{
register int cnt;
 
for (cnt = 0; isdigit(peekcd());)
cnt = cnt * 10 + getchar() - '0';
return (cnt);
}
 
/*
* Set the default addresses for commands which use the whole
* buffer as default, notably write.
*/
setall()
{
 
if (addr2 == 0) {
addr1 = one;
addr2 = dol;
if (dol == zero) {
dot = zero;
return;
}
}
/*
* Don't want to set previous context mark so use setdot1().
*/
setdot1();
}
 
/*
* No address allowed on, e.g. the file command.
*/
setnoaddr()
{
 
if (addr2 != 0)
error("No address allowed@on this command");
}
 
/*
* Parse an address.
* Just about any sequence of address characters is legal.
*
* If you are tricky you can use this routine and the = command
* to do simple addition and subtraction of cardinals less
* than the number of lines in the file.
*/
line *
address(in_line)
char *in_line;
{
register line *addr;
register int offset, c;
short lastsign;
 
bigmove = 0;
lastsign = 0;
offset = 0;
addr = 0;
for (;;) {
if (isdigit(peekcd())) {
if (addr == 0) {
addr = zero;
bigmove = 1;
}
loc1 = 0;
addr += offset;
offset = getnum();
if (lastsign >= 0)
addr += offset;
else
addr -= offset;
lastsign = 0;
offset = 0;
}
switch (c = getcd()) {
 
case '?':
case '/':
case '$':
case '\'':
case '\\':
bigmove++;
case '.':
if (addr || offset)
error("Badly formed address");
}
offset += lastsign;
lastsign = 0;
switch (c) {
 
case ' ':
case '\t':
continue;
 
case '+':
lastsign = 1;
if (addr == 0)
addr = dot;
continue;
 
case '^':
case '-':
lastsign = -1;
if (addr == 0)
addr = dot;
continue;
 
case '\\':
case '?':
case '/':
c = compile(c, 1);
notempty();
savere(scanre);
addr = dot;
if (in_line && execute(0, dot)) {
if (c == '/') {
while (loc1 <= in_line) {
if (loc1 == loc2)
loc2++;
if (!execute(1))
goto nope;
}
break;
} else if (loc1 < in_line) {
char *last;
doques:
 
do {
last = loc1