Hello, The POSIX specification of ed(1) includes a table in the rationale section which (among others) mandates the following address handling rules [1]:
Address Addr1 Addr2 ,, $ $ ;; $ $ Unfortunately, OpenBSD does not correctly handle these two example addresses. As an example, consider the following `,,p` print command: printf "a\nfoo\nbar\nbaz\n.\n,,p\nQ\n" | ed This should only print the last line (as `,,p` should expand to `$,$p`) but instead prints all lines with OpenBSD ed. This seems to be a regression introduced by Jerome Frgagic in 2017 [2]. Prior to this change, `,,p` as well as `;;p` correctly printed the last line. The aforementioned change added a recursive invocation of next_addr() which does not update the local first variable (causing the second address separator to be mistaken for the first). The patch below fixes this by tracking recursive invocations of next_addr() via an additional function parameter. See also: https://austingroupbugs.net/view.php?id=1582 [1]: https://pubs.opengroup.org/onlinepubs/9699919799/utilities/ed.html#tag_20_38_18 [2]: https://github.com/openbsd/src/commit/b4c905bbf3932a50011ce3436b1e22acc742cfeb diff --git bin/ed/main.c bin/ed/main.c index 231d021ef19..ca1aeb102b3 100644 --- bin/ed/main.c +++ bin/ed/main.c @@ -64,7 +64,7 @@ void signal_hup(int); void signal_int(int); void handle_winch(int); -static int next_addr(void); +static int next_addr(int); static int check_addr_range(int, int); static int get_matching_node_addr(regex_t *, int); static char *get_filename(int); @@ -296,7 +296,7 @@ extract_addr_range(void) addr_cnt = 0; first_addr = second_addr = current_addr; - while ((addr = next_addr()) >= 0) { + while ((addr = next_addr(0)) >= 0) { addr_cnt++; first_addr = second_addr; second_addr = addr; @@ -328,7 +328,7 @@ extract_addr_range(void) /* next_addr: return the next line address in the command buffer */ static int -next_addr(void) +next_addr(int recur) { char *hd; int addr = current_addr; @@ -382,11 +382,15 @@ next_addr(void) case '%': case ',': case ';': - if (first) { + if (first && !recur) { ibufp++; addr_cnt++; second_addr = (c == ';') ? current_addr : 1; - if ((addr = next_addr()) < 0) + + // If the next address is omitted (e.g. "," or ";") or + // if it is also a separator (e.g. ",," or ";;") then + // return the last address (as per the omission rules). + if ((addr = next_addr(1)) < 0) addr = addr_last; break; }