[hackers] [sbase][PATCH] ed: Regex fixes

From: Santtu Lakkala <inz_AT_inz.fi>
Date: Fri, 30 Jan 2026 14:10:55 +0200

Fix somewhat broken beginning and end of line handling using REG_NOEOL
flag of regexec().

Also force progress with zero-width matches by re-running regexec at
next position if a zero-width match is found at the current position.
---
 ed.c             | 49 +++++++++++++++++++++++++++++++++++-------------
 tests/0047-ed.sh | 24 ++++++++++++++++++++++++
 2 files changed, 60 insertions(+), 13 deletions(-)
 create mode 100755 tests/0047-ed.sh
diff --git a/ed.c b/ed.c
index 59101d3..081acaf 100644
--- a/ed.c
+++ b/ed.c
_AT_@ -69,7 +69,6 @@ static char *rhs;
 static char *lastmatch;
 static struct undo udata;
 static int newcmd;
-static int eol, bol;
 
 static sig_atomic_t intr, hup;
 
_AT_@ -403,15 +402,11 @@ compile(int delim)
 	if (!isgraph(delim))
 		error("invalid pattern delimiter");
 
-	eol = bol = bracket = lastre.siz = 0;
+	bracket = lastre.siz = 0;
 	for (n = 0;; ++n) {
 		c = input();
 		if (c == delim && !bracket || c == '\0') {
 			break;
-		} else if (c == '^') {
-			bol = 1;
-		} else if (c == '$') {
-			eol = 1;
 		} else if (c == '\\') {
 			addchar(c, &lastre);
 			c = input();
_AT_@ -433,7 +428,7 @@ compile(int delim)
 		regfree(pattern);
 	if (!pattern && (!(pattern = malloc(sizeof(*pattern)))))
 		error("out of memory");
-	if ((ret = regcomp(pattern, lastre.str, REG_NEWLINE))) {
+	if ((ret = regcomp(pattern, lastre.str, 0))) {
 		regerror(ret, pattern, buf, sizeof(buf));
 		error(buf);
 	}
_AT_@ -446,7 +441,7 @@ match(int num)
 
 	lastmatch = gettxt(num);
 	text.str[text.siz - 2] = '\0';
-	r =!regexec(pattern, lastmatch, 10, matchs, 0);
+	r = !regexec(pattern, lastmatch, 10, matchs, 0);
 	text.str[text.siz - 2] = '\n';
 
 	return r;
_AT_@ -456,13 +451,43 @@ static int
 rematch(int num)
 {
 	regoff_t off = matchs[0].rm_eo;
+	regmatch_t *m;
+	int r;
+
+	text.str[text.siz - 2] = '\0';
+	r = !regexec(pattern, lastmatch + off, 10, matchs, REG_NOTBOL);
+	text.str[text.siz - 2] = '\n';
+
+	if (!r)
+		return 0;
 
-	if (!regexec(pattern, lastmatch + off, 10, matchs, 0)) {
+	if (matchs[0].rm_eo > 0) {
 		lastmatch += off;
 		return 1;
 	}
 
-	return 0;
+	/* Zero width match was found at the end of the input, done */
+	if (lastmatch[off] == '\n') {
+		lastmatch += off;
+		return 0;
+	}
+
+	/* Zero width match at the current posiion, find the next one */
+	text.str[text.siz - 2] = '\0';
+	r = !regexec(pattern, lastmatch + off + 1, 10, matchs, REG_NOTBOL);
+	text.str[text.siz - 2] = '\n';
+
+	if (!r)
+		return 0;
+
+	/* Re-adjust matches to account for +1 in regexec */
+	for (m = matchs; m < &matchs[10]; m++) {
+		m->rm_so += 1;
+		m->rm_eo += 1;
+	}
+	lastmatch += off;
+
+	return 1;
 }
 
 static int
_AT_@ -1238,12 +1263,10 @@ subline(int num, int nth)
 
 	string(&s);
 	i = changed = 0;
-	for (m = match(num); m; m = rematch(num)) {
+	for (m = match(num); m; m = (nth < 0 || i < nth) && rematch(num)) {
 		chksignals();
 		addpre(&s);
 		changed |= addsub(&s, nth, ++i);
-		if (eol || bol)
-			break;
 	}
 	if (!changed)
 		return;
diff --git a/tests/0047-ed.sh b/tests/0047-ed.sh
new file mode 100755
index 0000000..47f77ff
--- /dev/null
+++ b/tests/0047-ed.sh
_AT_@ -0,0 +1,24 @@
+#!/bin/sh
+
+tmp=tmp.$$
+
+trap 'rm -f $tmp' EXIT
+trap 'exit $?' HUP INT TERM
+
+cat <<EOF > $tmp
+LLLx
+yLyLyLyxy
+zzzzxy
+EOF
+
+$EXEC ../ed -s /dev/null <<EOF | diff -u $tmp -
+i
+LLL
+.
+s! *!x!4
+p
+s# *#y#g
+p
+s/\(^\|L\)y/z/g
+p
+EOF
-- 
2.42.0
Received on Fri Jan 30 2026 - 13:10:55 CET

This archive was generated by hypermail 2.3.0 : Fri Jan 30 2026 - 13:12:33 CET