[Bug 284434] ed(1) special replacement with "%" doesn't quite match with man-page

From: <bugzilla-noreply_at_freebsd.org>
Date: Wed, 29 Jan 2025 14:40:01 UTC
https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=284434

            Bug ID: 284434
           Summary: ed(1) special replacement with "%" doesn't quite match
                    with man-page
           Product: Base System
           Version: Unspecified
          Hardware: Any
                OS: Any
            Status: New
          Severity: Affects Only Me
          Priority: ---
         Component: misc
          Assignee: bugs@FreeBSD.org
          Reporter: freebsd@tim.thechases.com

From `man ed`

> If replacement consists of a single `%', then replacement from the last substitution is used

However, there seems to be a bug in this if the closing delimiter isn't
provided.

Normally the closing replacement delimiter is optional:

  s/foo/bar/
  s/foo/bar

(both replace "foo" with "bar") However using "%" per the man-page seems to
require the closing delimiter.  To demonstrate

  $ cat test.txt
  a
  b
  c

  $ ed test.txt
  6
  1s/a/hello
  hello
  2/s/b/%
  %
  ,n
  1       hello
  2       %
  3       c
  Q

  $ ed test.txt
  6
  1s/a/hello
  hello
  2/s/b/%/
  ,n
  1       hello
  2       hello
  3       c
  Q

So the biggest issue is that, without the trailing slash, it replaces with "%"
rather than the last replacement text.  As a minor side-note, when using the
most recent replacement (with the slash), it doesn't print the resulting line
the same way it does with a normal substitution (i.e., note that the first
example prints the "%", while the second emits nothing)

It looks like src/bin/ed/sub.c:77 has the test

  if (*ibufp == '%' && *(ibufp + 1) == delimiter) {

which should likely be something like (untested)

  if (*ibufp == '%' && (*(ibufp + 1) == delimiter || *(ibufp + 1) == '\0')) {

-- 
You are receiving this mail because:
You are the assignee for the bug.