URI:
       ed: Regex fixes - sbase - suckless unix tools
  HTML git clone git://git.suckless.org/sbase
   DIR Log
   DIR Files
   DIR Refs
   DIR README
   DIR LICENSE
       ---
   DIR commit 1871a3578059a836c3aa1817ba9030f7f50c5e9b
   DIR parent 611fb5b365d5a07d59c00db99d692850a7aa3c30
  HTML 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:
         M ed.c                                |      49 +++++++++++++++++++++++--------
         A tests/0047-ed.sh                    |      24 ++++++++++++++++++++++++
       
       2 files changed, 60 insertions(+), 13 deletions(-)
       ---
   DIR 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;
   DIR 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