* Copyright (c) 1980 Regents of the University of California.
* All rights reserved. The Berkeley software License Agreement
* specifies the terms and conditions for redistribution.
static char *sccsid
= "@(#)ex_re.c 7.6 (Berkeley) %G%";
* Global, substitute and regular expressions.
* Very similar to ed, with some re extensions and
char globuf
[GBSIZE
], *Cwas
;
int oinglobal
= inglobal
;
* 0: ordinary - not in a global command.
* 1: text coming from some buffer, not tty.
* 2: like 1, but the source of the buffer is a global command.
* Hence you're only in a global command if inglobal==2. This
* strange sounding convention is historically derived from
* everybody simulating a global command.
error("Global within global@not allowed");
error("Global needs re|Missing regular expression for global");
while ((c
= ex_getchar()) != '\n') {
if (gp
>= &globuf
[GBSIZE
- 2])
error("Global command too long");
for (a1
= one
; a1
<= dol
; a1
++) {
if (a1
>= addr1
&& a1
<= addr2
&& execute(0, a1
) == k
)
* This code is commented out for now. The problem is that we don't
* fix up the undo area the way we should. Basically, I think what has
* to be done is to copy the undo area down (since we shrunk everything)
* and move the various pointers into it down too. I will do this later
* when I have time. (Mark, 10-20-80)
* Special case: g/.../d (avoid n^2 algorithm)
if (globuf
[0]=='d' && globuf
[1]=='\n' && globuf
[2]=='\0') {
* Now for each marked line, set dot there and do the commands.
* Note the n^2 behavior here for lots of lines matching.
* This is really needed: in some cases you could delete lines,
* causing a marked line to be moved before a1 and missed if
* we didn't restart at zero each time.
for (a1
= one
; a1
<= dol
; a1
++) {
* gdelete: delete inside a global command. Handles the
* special case g/r.e./d. All lines to be deleted have
* already been marked. Squeeze the remaining lines together.
* Note that other cases such as g/r.e./p, g/r.e./s/r.e.2/rhs/,
* and g/r.e./.,/r.e.2/d are not treated specially. There is no
* good reason for this except the question: where to you draw the line?
register line
*a1
, *a2
, *a3
;
/* find first marked line. can skip all before it */
for (a1
=zero
; (*a1
&01)==0; a1
++)
/* copy down unmarked lines, compacting as we go. */
a2
++; /* line is marked, skip it */
dot
= a1
; /* dot left after line deletion */
*a1
++ = *a2
++; /* unmarked, copy it */
int scount
, slines
, stotal
;
save12(), undkind
= UNDCHANGE
;
for (addr
= addr1
; addr
<= addr2
; addr
++) {
if (dosubcon(0, addr
) == 0)
* The loop can happen from s/\</&/g
* but we don't want to break other, reasonable cases.
if (++hopcount
> sizeof linebuf
)
error("substitution loop");
if (dosubcon(1, addr
) == 0)
n
= append(getsub
, addr
);
if (stotal
== 0 && !inglobal
&& !cflag
)
error("Fail|Substitute pattern match failed");
register int seof
, c
, uselastre
;
if (!value(EDCOMPATIBLE
))
if (endcmd(seof
) || any(seof
, "gcr")) {
if (isalpha(seof
) || isdigit(seof
))
error("Substitute needs re|Missing regular expression for substitute");
error("No previous re|No previous regular expression");
if (subre
.Expbuf
[0] == 0)
error("No previous substitute re|No previous substitute to repeat");
* When "magic", \& turns into a plain &,
* and all other chars work fine quoted.
for (orp
= orhsbuf
; *orp
; *rp
++ = *orp
++)
if (rp
>= &rhsbuf
[RHSSIZE
- 1])
if (!(globp
&& globp
[0])) {
if (rp
>= &rhsbuf
[RHSSIZE
- 1]) {
error("Replacement pattern too long@- limit 256 characters");
ex_putchar('\n' | QUOTE
);
ugo(c
- 1 + (inopen
? 1 : 0), ' ');
ugo(column(loc2
- 1) - c
, '^');
if (c
!= '\n' && c
!= EOF
) {
register char *lp
, *sp
, *rp
;
/* ^V <return> from vi to split lines */
sp
= place(sp
, loc1
, loc2
);
if (c
< 0 && (c
&= TRIM
) >= '1' && c
< nbra
+ '1') {
sp
= place(sp
, braslist
[c
- '1'], braelist
[c
- '1']);
*sp
++ = fixcase(c
& TRIM
);
if (sp
>= &genbuf
[LBSIZE
])
error("Line overflow@in substitute");
loc2
= sp
+ (linebuf
- genbuf
);
if (sp
>= &genbuf
[LBSIZE
])
register char *sp
, *l1
, *l2
;
if (sp
>= &genbuf
[LBSIZE
])
register int total
, lines
;
ex_printf(mesg("%d subs|%d substitutions"), total
);
if (lines
!= 1 && lines
!= total
)
ex_printf(" on %d lines", lines
);
char bracket
[NBRA
], *bracketp
, *rhsp
;
if (isalpha(eof
) || isdigit(eof
))
error("Regular expressions cannot be delimited by letters or digits");
if (scanre
.Expbuf
[0] == 0)
error("No previous scan re|No previous scanning regular expression");
if (subre
.Expbuf
[0] == 0)
error("No previous substitute re|No previous substitute regular expression");
error("Badly formed re|Regular expression \\ must be followed by / or ?");
if (c
== eof
|| c
== '\n' || c
== EOF
) {
error("No previous re|No previous regular expression");
if (c
== '\n' && oknl
== 0)
error("Missing closing delimiter@for regular expression");
if (ep
>= &expbuf
[ESIZE
- 2])
cerror("Re too complex|Regular expression too complicated");
if (c
== eof
|| c
== EOF
) {
cerror("Unmatched \\(|More \\('s than \\)'s in regular expression");
if (c
!= '*' || ep
== expbuf
)
if (c
!= '\\' || peekchar() != '*' || ep
== expbuf
)
cerror("Awash in \\('s!|Too many \\('d subexressions in a regular expression");
cerror("Extra \\)|More \\)'s than \\('s in regular expression");
error("Replacement pattern contains &@- cannot use in re");
if (c
>= '1' && c
<= '9')
error("Replacement pattern contains \\d@- cannot use in re");
if (ep
>= &expbuf
[ESIZE
-2])
if (*lastep
== CBRA
|| *lastep
== CKET
)
cerror("Illegal *|Can't * a \\( ... \\) in regular expression");
if (*lastep
== CCHR
&& (lastep
[1] & QUOTE
))
cerror("Illegal *|Can't * a \\n in regular expression");
cerror("Bad character class|Empty character class '[]' or '[^]' cannot match");
if (c
== '\\' && any(peekchar(), "]-^\\"))
c
= ex_getchar() | QUOTE
;
if (c
== '\n' || c
== EOF
)
if (ep
>= &expbuf
[ESIZE
])
cerror("No newlines in re's|Can't escape newlines into regular expressions");
if (c < '1' || c > NBRA + '1') {
cerror("Bad \\n|\\n in regular expression with n greater than the number of \\('s");
cerror("Badly formed re|Missing closing delimiter for regular expression");
if (peekchar() == eof
|| peekchar() == EOF
|| oknl
&& peekchar() == '\n') {
return (a
== b
|| value(IGNORECASE
) &&
((islower(a
) && toupper(a
) == b
) || (islower(b
) && toupper(b
) == a
)));
return (advance(p1
, p2
));
/* fast check for first character */
if (c
!= *p1
&& (!value(IGNORECASE
) ||
!((islower(c
) && toupper(c
) == *p1
) ||
(islower(*p1
) && toupper(*p1
) == c
))))
#define uletter(c) (isalpha(c) || c == '_')
for (;;) switch (*ep
++) {
if (cclass(ep
, *lp
++, 1)) {
if (cclass(ep
, *lp
++, 0)) {
while (cclass(ep
, *lp
++, ep
[-1] == (CCL
|STAR
)))
if ((isdigit(*lp
) || uletter(*lp
)) && !uletter(lp
[-1]) && !isdigit(lp
[-1]))
if (!uletter(*lp
) && !isdigit(*lp
))
error("Re internal error");
if (value(IGNORECASE
) && isupper(c
))
if (n
> 2 && set
[1] == '-') {
if (c
>= (set
[0] & TRIM
) && c
<= (set
[2] & TRIM
))
if ((*set
++ & TRIM
) == c
)