fixed undo within a global command (would corrupt the buffer)

This commit is contained in:
alm 1993-07-02 10:02:26 +00:00
parent fe8b331915
commit aef94df1e8
13 changed files with 232 additions and 183 deletions

View File

@ -1,5 +1,5 @@
PROG= ed
CFLAGS+=-DVI_BANG -DDES -DGNU_REGEX -DBACKWARDS
CFLAGS+=-DVI_BANG -DDES -DGNU_REGEX
SRCS= ed.c re.c buf.c cbc.c
LDADD= -lgnuregex -lcrypt
LINKS= ${BINDIR}/ed ${BINDIR}/red

View File

@ -48,3 +48,15 @@ Though ed is not a binary editor, it can be used (if painfully) to edit
binary files. To assist in binary editing, when a file containing at
least one ASCII NUL character is written, a newline is not appended
if it did not already contain one upon reading.
Since the behavior of `u' (undo) within a `g' (global) command list is
not specified by POSIX D11/2, it follows the behavior of the SunOS ed
(this is the best way, I think, in that the alternatives are either too
complicated to implement or too confusing to use): undo forces a global
command list to be executed only once, rather than for each line matching
a global pattern. In addtion, each instance of `u' within a global command
undoes all previous commands (including undo's) in the command list.
The `m' (move) command within a `g' command list also follows the SunOS
ed implementation: any moved lines are removed from the global command's
`active' list.

View File

@ -49,13 +49,13 @@ static char sccsid[] = "@(#)buf.c 5.5 (Berkeley) 3/28/93";
#include "ed.h"
extern char errmsg[];
extern line_t line0;
FILE *sfp; /* scratch file pointer */
char *sfbuf = NULL; /* scratch file input buffer */
int sfbufsz = 0; /* scratch file input buffer size */
off_t sfseek; /* scratch file position */
int seek_write; /* seek before writing */
line_t line0; /* initial node of line queue */
/* gettxt: get a line of text from the scratch file; return pointer
to the text */
@ -165,7 +165,11 @@ getaddr(lp)
while (cp != lp && (cp = cp->next) != &line0)
n++;
return (cp != &line0) ? n : 0;
if (n && cp == &line0) {
sprintf(errmsg, "invalid address");
return ERR;
}
return n;
}
@ -244,3 +248,41 @@ quit(n)
}
exit(n);
}
unsigned char ctab[256]; /* character translation table */
/* init_buf: open scratch buffer; initialize line queue */
void
init_buf()
{
int i = 0;
if (sbopen() < 0)
quit(2);
requeue(&line0, &line0);
for (i = 0; i < 256; i++)
ctab[i] = i;
}
/* translit: translate characters in a string */
char *
translit(s, len, from, to)
char *s;
int len;
int from;
int to;
{
static int i = 0;
unsigned char *us;
ctab[i] = i; /* restore table to initial state */
ctab[i = from] = to;
for (us = (unsigned char *) s; len-- > 0; us++)
*us = ctab[*us];
return s;
}

View File

@ -92,9 +92,9 @@ commands have the structure:
.I [address [,address]]command[parameters]
.RE
.sp
The address(es) indicate the line(s) to be affected by the command.
If fewer addresses are given than the command accepts, then default
addresses are supplied.
The address(es) indicate the line or range of lines to be affected by the
command. If fewer addresses are given than the command accepts, then
default addresses are supplied.
.SS OPTIONS
.TP 8
@ -311,7 +311,7 @@ not listed below, including `{', '}', `(', `)', `<' and `>',
matches itself.
.TP 8
\fR\\\fIc\fR
\fR\e\fIc\fR
Any backslash-escaped character
.IR c ,
except for `{', '}', `(', `)', `<' and `>',
@ -389,28 +389,28 @@ anchors the regular expression to the end of a line.
Otherwise, it matches itself.
.TP 8
\fR\\<\fR
\fR\e<\fR
Anchors the single character regular expression or subexpression
immediately following it to the beginning of a word.
(This may not be available)
.TP 8
\fR\\>\fR
\fR\e>\fR
Anchors the single character regular expression or subexpression
immediately following it to the end of a word.
(This may not be available)
.TP 8
\fR\\(\fIre\fR\\)\fR
\fR\e(\fIre\fR\e)\fR
Defines a subexpression
.IR re .
Subexpressions may be nested.
A subsequent backreference of the form \fI`\\n'\fR, where
A subsequent backreference of the form \fI`\en'\fR, where
.I n
is a number in the range [1,9], expands to the text matched by the
.IR n th
subexpression.
For example, the regular expression `\\(.*\\)\\1' matches any string
For example, the regular expression `\e(.*\e)\e1' matches any string
consisting of identical adjacent substrings.
Subexpressions are ordered relative to
their left delimiter.
@ -426,7 +426,7 @@ the string `abbb' (as opposed to the substring `bbb'), since a null match
is the only left-most match.
.TP 8
\fR\\{\fIn,m\fR\\}\fR or \fR\\{\fIn,\fR\\}\fR or \fR\\{\fIn\fR\\}\fR
\fR\e{\fIn,m\fR\e}\fR or \fR\e{\fIn,\fR\e}\fR or \fR\e{\fIn\fR\e}\fR
Matches the single character regular expression or subexpression
immediately preceding it at least
.I n
@ -735,7 +735,7 @@ An unescaped `&' in
.I replacement
is replaced by the currently matched text.
The character sequence
\fI`\\m'\fR,
\fI`\em'\fR,
where
.I m
is a number in the range [1,9], is replaced by the
@ -912,8 +912,8 @@ that line.
Buffer file
.PD 0
.TP 20
\fR./ed.hup\fR, $HOME/ed.hup
First and second files to which
ed.hup
The file to which
.B ed
attempts to write the buffer if the terminal hangs up.
@ -971,6 +971,10 @@ replaces any occurrences of
.I old
with
.IR new .
If the
.I `u'
(undo) command occurs in a global command list, then
the command list is executed only once.
If diagnostics are not disabled, attempting to quit
.B ed

View File

@ -334,11 +334,6 @@ getone()
}
#define MAXMARK 26 /* max number of marks */
line_t *mark[MAXMARK]; /* line markers */
int markno; /* line marker count */
/* getnum: return a relative line number from the command buffer */
long
getnum(first)
@ -370,7 +365,7 @@ getnum(first)
return first ? curln : 1;
case '\'':
ibufp++;
return (first && islower(*ibufp)) ? getaddr(mark[*ibufp++ - 'a']) : ERR;
return first ? getmark(*ibufp++) : ERR;
case '%':
case ',':
case ';':
@ -583,7 +578,6 @@ doglob(gflag)
long ucurln = -1; /* if >= 0, undo enabled */
long ulastln = -1; /* if >= 0, undo enabled */
int usw = 0; /* if set, undo last undo */
int patlock = 0; /* if set, pattern not released by optpat() */
long rows = 22; /* scroll length: ws_row - 2 */
@ -645,8 +639,8 @@ docmd(glob)
} else if ((fnp = getfn()) == NULL)
return ERR;
VRFYCMD();
memset(mark, 0, sizeof mark);
lndelete(1, lastln);
if (lndelete(1, lastln) < 0)
return ERR;
ureset();
if (sbclose() < 0)
return ERR;
@ -726,13 +720,10 @@ docmd(glob)
if (line2 == 0) {
sprintf(errmsg, "invalid address");
return ERR;
} else if (!islower(c)) {
sprintf(errmsg, "invalid mark character");
return ERR;
}
}
VRFYCMD();
if (!mark[c - 'a']) markno++;
mark[c - 'a'] = getlp(line2);
if (putmark(c, getlp(line2)) < 0)
return ERR;
break;
case 'l':
if (ckrange(curln, curln) < 0)
@ -752,9 +743,7 @@ docmd(glob)
}
VRFYCMD();
if (!glob) ureset();
if (num == line1 - 1 || num == line2)
curln = line2;
else if (move(num) < 0)
if (move(num, glob) < 0)
return ERR;
else
modified = 1;
@ -912,7 +901,7 @@ docmd(glob)
return ERR;
}
VRFYCMD();
if (undo() < 0)
if (undo(glob) < 0)
return ERR;
break;
case 'v':
@ -1300,74 +1289,6 @@ append(n, glob)
}
#ifdef sun
/* subst: change all text matching a pattern in a range of lines according to
a substitution template; return status */
subst(pat, gflag)
pattern_t *pat;
int gflag;
{
undo_t *up = NULL;
char *txt;
char *eot;
line_t *bp, *ep, *np;
long ocl;
long nsubs = 0;
int len;
ep = getlp(curln = line2);
for (bp = getlp(line1); bp != ep->next; bp = bp->next)
if ((len = regsub(pat, bp, gflag)) < 0)
return ERR;
else if (!len) {
/* add copy of bp after current line - this avoids
overloading the undo structure, since only two
undo nodes are needed for the whole substitution;
the cost is high, but the less than if undo is
overloaded on a Sun evidently. XXX */
if ((np = lpdup(bp)) == NULL)
return ERR;
spl1();
lpqueue(np);
if (up)
up->t = getlp(curln);
else if ((up = upush(UADD, curln, curln)) == NULL) {
spl0();
return ERR;
}
spl0();
} else {
txt = rbuf;
eot = rbuf + len;
spl1();
do {
if ((txt = puttxt(txt)) == NULL) {
spl0();
return ERR;
} else if (up)
up->t = getlp(curln);
else if ((up = upush(UADD, curln, curln)) == NULL) {
spl0();
return ERR;
}
} while (txt != eot);
spl0();
nsubs++;
}
ocl = curln;
lndelete(line1, line2);
curln = ocl - (line2 - line1 + 1);
if (nsubs == 0 && !(gflag & GLB)) {
sprintf(errmsg, "no match");
return ERR;
} else if ((gflag & (GPR | GLS | GNP))
&& doprint(curln, curln, gflag) < 0)
return ERR;
return 1;
}
#else /* sun */
/* subst: change all text matching a pattern in a range of lines according to
a substitution template; return status */
subst(pat, gflag)
@ -1389,7 +1310,8 @@ subst(pat, gflag)
return ERR;
else if (len) {
up = NULL;
lndelete(curln, curln);
if (lndelete(curln, curln) < 0)
return ERR;
txt = rbuf;
eot = rbuf + len;
spl1();
@ -1416,7 +1338,6 @@ subst(pat, gflag)
return ERR;
return 1;
}
#endif /* sun */
/* regsub: replace text matched by a pattern according to a substitution
@ -1498,7 +1419,8 @@ join(from, to)
}
CKBUF(buf, n, size + 2, ERR);
memcpy(buf + size, "\n", 2);
lndelete(from, to);
if (lndelete(from, to) < 0)
return ERR;
curln = from - 1;
spl1();
if (puttxt(buf) == NULL
@ -1513,28 +1435,38 @@ join(from, to)
/* move: move a range of lines */
move(num)
move(num, glob)
long num;
int glob;
{
line_t *b1, *a1, *b2, *a2;
line_t *b1, *a1, *b2, *a2, *lp;
long n = nextln(line2, lastln);
long p = prevln(line1, lastln);
int done = (num == line1 - 1 || num == line2);
spl1();
if (upush(UMOV, p, n) == NULL
if (done) {
a2 = getlp(n);
b2 = getlp(p);
curln = line2;
} else if (upush(UMOV, p, n) == NULL
|| upush(UMOV, num, nextln(num, lastln)) == NULL) {
spl0();
return ERR;
spl0();
return ERR;
} else {
a1 = getlp(n);
if (num < line1)
b1 = getlp(p), b2 = getlp(num); /* this getlp last! */
else b2 = getlp(num), b1 = getlp(p); /* this getlp last! */
a2 = b2->next;
requeue(b2, b1->next);
requeue(a1->prev, a2);
requeue(b1, a1);
curln = num + ((num < line1) ? line2 - line1 + 1 : 0);
}
a1 = getlp(n);
if (num < line1)
b1 = getlp(p), b2 = getlp(num); /* this getlp last! */
else b2 = getlp(num), b1 = getlp(p); /* this getlp last! */
a2 = b2->next;
requeue(b2, b1->next);
requeue(a1->prev, a2);
requeue(b1, a1);
curln = num + ((num < line1) ? line2 - line1 + 1 : 0);
if (glob)
for (lp = b2->next; lp != a2; lp = lp->next)
lp->len &= ~ACTV; /* zero ACTV bit */
spl0();
return 0;
}
@ -1857,7 +1789,6 @@ undo_t *ustack = NULL; /* undo stack */
long usize = 0; /* stack size variable */
long u_p = 0; /* undo stack pointer */
/* upush: return pointer to intialized undo node */
undo_t *
upush(type, from, to)
@ -1894,14 +1825,22 @@ upush(type, from, to)
return NULL;
}
/* USWAP: swap undo nodes */
#define USWAP(x,y) { \
undo_t utmp; \
utmp = x, x = y, y = utmp; \
}
/* undo: undo last change to the editor buffer */
undo()
undo(glob)
int glob;
{
long n = usw ? 0 : u_p - 1;
int i = usw ? 1 : -1;
long j = u_p;
long n;
long ocurln = curln;
long olastln = lastln;
line_t *lp, *np;
if (ucurln == -1 || ulastln == -1) {
sprintf(errmsg, "nothing to undo");
@ -1910,8 +1849,8 @@ undo()
modified = 1;
getlp(0); /* this getlp last! */
spl1();
for (; j-- > 0; n += i)
switch(ustack[n].type ^ usw) {
for (n = u_p; n-- > 0;) {
switch(ustack[n].type) {
case UADD:
requeue(ustack[n].h->prev, ustack[n].t->next);
break;
@ -1921,16 +1860,23 @@ undo()
break;
case UMOV:
case VMOV:
requeue(ustack[n + i].h, ustack[n].h->next);
requeue(ustack[n].t->prev, ustack[n + i].t);
requeue(ustack[n - 1].h, ustack[n].h->next);
requeue(ustack[n].t->prev, ustack[n - 1].t);
requeue(ustack[n].h, ustack[n].t);
n += i, j--;
n--;
break;
default:
/*NOTREACHED*/
;
}
usw = 1 - usw;
ustack[n].type ^= 1;
}
/* reverse undo order */
for (n = u_p; n-- > (u_p + 1)/ 2;)
USWAP(ustack[n], ustack[u_p - 1 - n]);
if (glob)
for (lp = np = getlp(0); (lp = lp->next) != np;)
lp->len &= ~ACTV; /* zero ACTV bit */
curln = ucurln, ucurln = ocurln;
lastln = ulastln, ulastln = olastln;
spl0();
@ -1943,28 +1889,70 @@ void
ureset()
{
line_t *lp, *ep, *tl;
int i;
while (u_p--)
if ((ustack[u_p].type ^ usw) == UDEL) {
if (ustack[u_p].type == UDEL) {
ep = ustack[u_p].t->next;
for (lp = ustack[u_p].h; lp != ep; lp = tl) {
if (markno)
for (i = 0; i < MAXMARK; i++)
if (mark[i] == lp) {
mark[i] = NULL;
markno--;
}
clrmark(lp);
tl = lp->next;
free(lp);
}
}
u_p = usw = 0;
u_p = 0;
ucurln = curln;
ulastln = lastln;
}
#define MAXMARK 26 /* max number of marks */
line_t *mark[MAXMARK]; /* line markers */
int markno; /* line marker count */
/* getmark: return address of a marked line */
int
getmark(n)
int n;
{
if (!islower(n)) {
sprintf(errmsg, "invalid mark character");
return ERR;
}
return getaddr(mark[n - 'a']);
}
/* putmark: set a line node mark */
int
putmark(n, lp)
int n;
line_t *lp;
{
if (!islower(n)) {
sprintf(errmsg, "invalid mark character");
return ERR;
} else if (mark[n - 'a'] == NULL)
markno++;
mark[n - 'a'] = lp;
return 0;
}
/* clrmark: clear line node marks */
void
clrmark(lp)
line_t *lp;
{
int i;
if (markno)
for (i = 0; i < MAXMARK; i++)
if (mark[i] == lp) {
mark[i] = NULL;
markno--;
}
}
/* sgetline: read a line of text up a maximum size from a file; return
line length */
@ -2146,7 +2134,6 @@ onintr(signo)
}
void
dohup(signo)
int signo;
@ -2201,44 +2188,6 @@ dowinch(signo)
}
unsigned char ctab[256]; /* character translation table */
/* translit: translate characters in a string */
char *
translit(s, len, from, to)
char *s;
int len;
int from;
int to;
{
static int i = 0;
unsigned char *us;
ctab[i] = i; /* restore table to initial state */
ctab[i = from] = to;
for (us = (unsigned char *) s; len-- > 0; us++)
*us = ctab[*us];
return s;
}
line_t line0; /* initial node of line queue */
/* init_buf: open scratch buffer; initialize line queue */
void
init_buf()
{
int i = 0;
if (sbopen() < 0)
quit(2);
requeue(&line0, &line0);
for (i = 0; i < 256; i++)
ctab[i] = i;
}
/* ckfn: return a legal filename */
char *
ckfn(s)

View File

@ -206,6 +206,7 @@ int desputc __P((int, FILE *));
int docmd __P((int));
void err __P((char *));
char *ccl __P((char *));
void clrmark __P((line_t *));
void cvtkey __P((char *, char *));
long doglob __P((int));
void dohup __P((int));
@ -223,6 +224,7 @@ int getkey __P((void));
char *getlhs __P((int));
int getline __P((void));
int getlist __P((void));
int getmark __P((int));
long getnum __P((int));
long getone __P((void));
line_t *getlp __P((long));
@ -236,12 +238,12 @@ line_t *lpdup __P((line_t *));
void lpqueue __P((line_t *));
void makekey __P((char *));
char *makesub __P((int));
char *translit __P((char *, int, int, int));
int move __P((long));
int move __P((long, int));
int oddesc __P((char *, char *));
void onhup __P((int));
void onintr __P((int));
pattern_t *optpat __P((void));
int putmark __P((int, line_t *));
void putstr __P((char *, int, long, int));
char *puttxt __P((char *));
void quit __P((int));
@ -253,7 +255,8 @@ int catsub __P((char *, regmatch_t *, int));
int subst __P((pattern_t *, int));
int tobinhex __P((int, int));
int transfer __P((long));
int undo __P((void));
char *translit __P((char *, int, int, int));
int undo __P((int));
undo_t *upush __P((int, long, long));
void ureset __P((void));

View File

@ -71,7 +71,7 @@ optpat()
sprintf(errmsg, "invalid pattern delimiter");
return NULL;
} else if (*ibufp == delim) {
sprintf(errmsg, "no previous pattern");
if (!exp) sprintf(errmsg, "no previous pattern");
return exp;
} else if ((exps = getlhs(delim)) == NULL)
return NULL;

5
bin/ed/test/g3.d Normal file
View File

@ -0,0 +1,5 @@
line 1
line 2
line 3
line 4
line5

5
bin/ed/test/g3.r Normal file
View File

@ -0,0 +1,5 @@
linc 3
xine 1
xine 2
xinc 4
xinc5

4
bin/ed/test/g3.t Normal file
View File

@ -0,0 +1,4 @@
g/./s//x/\
3m0
g/./s/e/c/\
2,3m1

5
bin/ed/test/g4.d Normal file
View File

@ -0,0 +1,5 @@
line 1
line 2
line 3
line 4
line5

7
bin/ed/test/g4.r Normal file
View File

@ -0,0 +1,7 @@
hello
zine 1
line 2
line 3
line 4
line5
world

13
bin/ed/test/g4.t Normal file
View File

@ -0,0 +1,13 @@
g/./s/./x/\
u\
s/./y/\
u\
s/./z/\
u
u
0a
hello
.
$a
world
.