sbase

suckless unix tools
git clone git://git.suckless.org/sbase
Log | Files | Refs | README | LICENSE

commit 1871a3578059a836c3aa1817ba9030f7f50c5e9b
parent 611fb5b365d5a07d59c00db99d692850a7aa3c30
Author: Santtu Lakkala <inz@inz.fi>
Date:   Fri, 30 Jan 2026 14:10:55 +0200

ed: Regex fixes

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.

Diffstat:
Med.c | 49++++++++++++++++++++++++++++++++++++-------------
Atests/0047-ed.sh | 24++++++++++++++++++++++++
2 files changed, 60 insertions(+), 13 deletions(-)

diff --git a/ed.c b/ed.c @@ -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; @@ -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(); @@ -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); } @@ -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; @@ -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 @@ -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 @@ -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