URI: 
       ed: Accept shell escapes in r, e and E commands - sbase - suckless unix tools
  HTML git clone git://git.suckless.org/sbase
   DIR Log
   DIR Files
   DIR Refs
   DIR README
   DIR LICENSE
       ---
   DIR commit 9a0d04fcf9ac486a4056a3641ecb098a227166af
   DIR parent 8adf85e686e8b95d00879e2882b2d925dbdbd681
  HTML Author: Roberto E. Vargas Caballero <k0ga@shike2.net>
       Date:   Sat, 13 Dec 2025 10:29:17 +0100
       
       ed: Accept shell escapes in r, e and E commands
       
       Diffstat:
         M ed.1                                |      12 +++++++++---
         M ed.c                                |      39 ++++++++++++++++++++-----------
         A tests/0011-ed.sh                    |      25 +++++++++++++++++++++++++
         A tests/0012-ed.sh                    |      24 ++++++++++++++++++++++++
         A tests/0013-ed.sh                    |      23 +++++++++++++++++++++++
         A tests/0014-ed.sh                    |      11 +++++++++++
       
       6 files changed, 117 insertions(+), 17 deletions(-)
       ---
   DIR diff --git a/ed.1 b/ed.1
       @@ -1,4 +1,4 @@
       -.Dd December 27, 2016
       +.Dd December 27, 2025
        .Dt ED 1
        .Os sbase
        .Sh NAME
       @@ -107,8 +107,15 @@ uses the currently remembered filename.
        The remembered filename is set to
        .Ar file
        for later use.
       +The current address is set to the last line read.
       +.It e Ar !command
       +Delete the contents of the buffer and load in the output of
       +.Ar command .
       +The remembered filename is not modified.
       +The current address is set to the last line read.
        .It E Ar file
       -As above, but without warning if the current buffer has unsaved changes.
       +As the command e,
       +but without warning if the current buffer has unsaved changes.
        .It f Ar file
        Set the currently remembered filename to
        .Ar file
       @@ -235,4 +242,3 @@ The dot is unchanged.
        POSIX.1-2013.
        Except where noted here:
        g and v operate on single commands rather than lists delimited with '\e'.
       -e, E, r, w, and W commands cannot accept shell escapes.
   DIR diff --git a/ed.c b/ed.c
       @@ -804,18 +804,29 @@ dowrite(const char *fname, int trunc)
        static void
        doread(const char *fname)
        {
       +        int r;
                size_t cnt;
                ssize_t len;
                char *p;
                FILE *aux;
                static size_t n;
       +        static int sh;
                static char *s;
                static FILE *fp;
        
       -        if (fp)
       -                fclose(fp);
       -        if ((fp = fopen(fname, "r")) == NULL)
       +        if (fp) {
       +                sh ? pclose(fp) : fclose(fp);
       +                fp = NULL;
       +        }
       +
       +        if(fname[0] == '!') {
       +                sh = 1;
       +                fname++;
       +                if((fp = popen(fname, "r")) == NULL)
       +                        error("bad exec");
       +        } else if ((fp = fopen(fname, "r")) == NULL) {
                        error("cannot open input file");
       +        }
        
                curln = line2;
                for (cnt = 0; (len = getline(&s, &n, fp)) > 0; cnt += (size_t)len) {
       @@ -836,7 +847,8 @@ doread(const char *fname)
        
                aux = fp;
                fp = NULL;
       -        if (fclose(aux))
       +        r = sh ? pclose(aux) : fclose(aux);
       +        if (r)
                        error("input/output error");
        }
        
       @@ -926,16 +938,16 @@ getfname(int comm)
                        if (savfname[0] == '\0')
                                error("no current filename");
                        return savfname;
       -        } else if (bp == &fname[FILENAME_MAX]) {
       -                error("file name too long");
       -        } else {
       -                *bp = '\0';
       -                if (savfname[0] == '\0' || comm == 'e' || comm == 'f')
       -                        strcpy(savfname, fname);
       -                return fname;
                }
       +        if (bp == &fname[FILENAME_MAX])
       +                error("file name too long");
       +        *bp = '\0';
        
       -        return NULL; /* not reached */
       +        if (fname[0] == '!')
       +                return fname;
       +        if (savfname[0] == '\0' || comm == 'e' || comm == 'f')
       +                strcpy(savfname, fname);
       +        return fname;
        }
        
        static void
       @@ -1443,10 +1455,9 @@ repeat:
                                goto unexpected;
                        if (modflag)
                                goto modified;
       -                getfname(cmd);
                        setscratch();
                        deflines(curln, curln);
       -                doread(savfname);
       +                doread(getfname(cmd));
                        clearundo();
                        break;
                default:
   DIR diff --git a/tests/0011-ed.sh b/tests/0011-ed.sh
       @@ -0,0 +1,25 @@
       +#!/bin/sh
       +
       +tmp=tmp.$$
       +
       +trap 'rm -f $tmp' EXIT
       +trap 'rm -f $tmp' HUP INT TERM
       +
       +cat <<EOF >$tmp
       +y
       +1
       +x
       +y
       +EOF
       +
       +../ed -s /dev/null <<EOF  | diff -u $tmp -
       +a
       +1
       +2
       +3
       +.
       +1r !printf 'x\ny\n'
       +p
       +1,3p
       +w
       +EOF
   DIR diff --git a/tests/0012-ed.sh b/tests/0012-ed.sh
       @@ -0,0 +1,24 @@
       +#!/bin/sh
       +
       +tmp=tmp.$$
       +
       +trap 'rm -f $tmp' EXIT
       +trap 'rm -f $tmp' HUP INT TERM
       +
       +cat <<EOF >$tmp
       +x
       +y
       +/dev/null
       +EOF
       +
       +../ed -s /dev/null <<EOF  | diff -u $tmp -
       +a
       +1
       +2
       +3
       +.
       +w
       +e !printf 'x\ny\n'
       +,p
       +f
       +EOF
   DIR diff --git a/tests/0013-ed.sh b/tests/0013-ed.sh
       @@ -0,0 +1,23 @@
       +#!/bin/sh
       +
       +tmp=tmp.$$
       +
       +trap 'rm -f $tmp' EXIT
       +trap 'rm -f $tmp' HUP INT TERM
       +
       +cat <<EOF >$tmp
       +x
       +y
       +/dev/null
       +EOF
       +
       +../ed -s /dev/null <<EOF  | diff -u $tmp -
       +a
       +1
       +2
       +3
       +.
       +E !printf 'x\ny\n'
       +,p
       +f
       +EOF
   DIR diff --git a/tests/0014-ed.sh b/tests/0014-ed.sh
       @@ -0,0 +1,11 @@
       +#!/bin/sh
       +
       +../ed -s /dev/null <<EOF | (read a && test $a = a)
       +a
       +1
       +2
       +3
       +.
       +1w !sed s/1/a/
       +w
       +EOF