diff --git a/bin/ed/Makefile b/bin/ed/Makefile index ad4bf0eb70f3..210936cb7d68 100644 --- a/bin/ed/Makefile +++ b/bin/ed/Makefile @@ -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 diff --git a/bin/ed/POSIX b/bin/ed/POSIX index dd506fb5a606..47a80b9e72a0 100644 --- a/bin/ed/POSIX +++ b/bin/ed/POSIX @@ -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. diff --git a/bin/ed/buf.c b/bin/ed/buf.c index 57c336121ca1..80af571aae24 100644 --- a/bin/ed/buf.c +++ b/bin/ed/buf.c @@ -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; +} + + diff --git a/bin/ed/ed.1 b/bin/ed/ed.1 index ff6a8499ffc4..e9a318080b39 100644 --- a/bin/ed/ed.1 +++ b/bin/ed/ed.1 @@ -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 diff --git a/bin/ed/ed.c b/bin/ed/ed.c index de226ff45c7e..47911e5b99fd 100644 --- a/bin/ed/ed.c +++ b/bin/ed/ed.c @@ -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) diff --git a/bin/ed/ed.h b/bin/ed/ed.h index 1b277fe9359d..5daec734b724 100644 --- a/bin/ed/ed.h +++ b/bin/ed/ed.h @@ -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)); diff --git a/bin/ed/re.c b/bin/ed/re.c index 462c816626f2..e94475033922 100644 --- a/bin/ed/re.c +++ b/bin/ed/re.c @@ -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; diff --git a/bin/ed/test/g3.d b/bin/ed/test/g3.d new file mode 100644 index 000000000000..92f337e977f2 --- /dev/null +++ b/bin/ed/test/g3.d @@ -0,0 +1,5 @@ +line 1 +line 2 +line 3 +line 4 +line5 diff --git a/bin/ed/test/g3.r b/bin/ed/test/g3.r new file mode 100644 index 000000000000..cc6fbddec23b --- /dev/null +++ b/bin/ed/test/g3.r @@ -0,0 +1,5 @@ +linc 3 +xine 1 +xine 2 +xinc 4 +xinc5 diff --git a/bin/ed/test/g3.t b/bin/ed/test/g3.t new file mode 100644 index 000000000000..2d052a6e840d --- /dev/null +++ b/bin/ed/test/g3.t @@ -0,0 +1,4 @@ +g/./s//x/\ +3m0 +g/./s/e/c/\ +2,3m1 diff --git a/bin/ed/test/g4.d b/bin/ed/test/g4.d new file mode 100644 index 000000000000..92f337e977f2 --- /dev/null +++ b/bin/ed/test/g4.d @@ -0,0 +1,5 @@ +line 1 +line 2 +line 3 +line 4 +line5 diff --git a/bin/ed/test/g4.r b/bin/ed/test/g4.r new file mode 100644 index 000000000000..350882d82375 --- /dev/null +++ b/bin/ed/test/g4.r @@ -0,0 +1,7 @@ +hello +zine 1 +line 2 +line 3 +line 4 +line5 +world diff --git a/bin/ed/test/g4.t b/bin/ed/test/g4.t new file mode 100644 index 000000000000..ec618166cc37 --- /dev/null +++ b/bin/ed/test/g4.t @@ -0,0 +1,13 @@ +g/./s/./x/\ +u\ +s/./y/\ +u\ +s/./z/\ +u +u +0a +hello +. +$a +world +.