URI:
       ed: Implement command expansion for io 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 611fb5b365d5a07d59c00db99d692850a7aa3c30
   DIR parent e9577cde96b603adc73731a24d0fd331b02019f1
  HTML Author: Santtu Lakkala <inz@inz.fi>
       Date:   Fri, 30 Jan 2026 13:21:00 +0200
       
       ed: Implement command expansion for io commands
       
       Split command expansion functionality from execsh() for reuse in the
       read and write commands. Adjust getfname() to leave the command in the
       input buffer for extraction by the new expandcmd().
       
       Diffstat:
         M TODO                                |       2 --
         M ed.c                                |      95 +++++++++++++++++--------------
       
       2 files changed, 52 insertions(+), 45 deletions(-)
       ---
   DIR diff --git a/TODO b/TODO
       @@ -30,8 +30,6 @@ ed
        --
        
        * Editing huge files doesn't work well.
       -* Using % in shell escapes of r, e, E, w, W, x and X commands.
       -* Using !! in shell escapes of r, e, E, w, W, x and X commands.
        
        
        printf
   DIR diff --git a/ed.c b/ed.c
       @@ -751,6 +751,51 @@ chksignals(void)
                }
        }
        
       +static const char *
       +expandcmd(void)
       +{
       +        static String cmd;
       +        char *p;
       +        int c, repl = 0;
       +
       +        skipblank();
       +        if ((c = input()) != '!') {
       +                back(c);
       +                string(&cmd);
       +        } else if (cmd.siz) {
       +                --cmd.siz;
       +                repl = 1;
       +        } else {
       +                error("no previous command");
       +        }
       +
       +        while ((c = input()) != '\0') {
       +                switch (c) {
       +                case '%':
       +                        if (savfname[0] == '\0')
       +                                error("no current filename");
       +                        repl = 1;
       +                        for (p = savfname; *p; ++p)
       +                                addchar(*p, &cmd);
       +                        break;
       +                case '\\':
       +                        c = input();
       +                        if (c != '%') {
       +                                back(c);
       +                                c = '\\';
       +                        }
       +                default:
       +                        addchar(c, &cmd);
       +                }
       +        }
       +        addchar('\0', &cmd);
       +
       +        if (repl)
       +                puts(cmd.str);
       +
       +        return cmd.str;
       +}
       +
        static void
        dowrite(const char *fname, int trunc)
        {
       @@ -768,8 +813,7 @@ dowrite(const char *fname, int trunc)
        
                if(fname[0] == '!') {
                        sh = 1;
       -                fname++;
       -                if((fp = popen(fname, "w")) == NULL)
       +                if((fp = popen(expandcmd(), "w")) == NULL)
                                error("bad exec");
                } else {
                        sh = 0;
       @@ -822,8 +866,7 @@ doread(const char *fname)
        
                if(fname[0] == '!') {
                        sh = 1;
       -                fname++;
       -                if((fp = popen(fname, "r")) == NULL)
       +                if((fp = popen(expandcmd(), "r")) == NULL)
                                error("bad exec");
                } else if ((fp = fopen(fname, "r")) == NULL) {
                        error("cannot open input file");
       @@ -931,6 +974,10 @@ getfname(int comm)
                static char fname[FILENAME_MAX];
        
                skipblank();
       +        if ((c = input()) == '!') {
       +                return strcpy(fname, "!");
       +        }
       +        back(c);
                for (bp = fname; bp < &fname[FILENAME_MAX]; *bp++ = c) {
                        if ((c = input()) == '\0')
                                break;
       @@ -1076,45 +1123,7 @@ copy(int where)
        static void
        execsh(void)
        {
       -        static String cmd;
       -        char *p;
       -        int c, repl = 0;
       -
       -        skipblank();
       -        if ((c = input()) != '!') {
       -                back(c);
       -                string(&cmd);
       -        } else if (cmd.siz) {
       -                --cmd.siz;
       -                repl = 1;
       -        } else {
       -                error("no previous command");
       -        }
       -
       -        while ((c = input()) != '\0') {
       -                switch (c) {
       -                case '%':
       -                        if (savfname[0] == '\0')
       -                                error("no current filename");
       -                        repl = 1;
       -                        for (p = savfname; *p; ++p)
       -                                addchar(*p, &cmd);
       -                        break;
       -                case '\\':
       -                        c = input();
       -                        if (c != '%') {
       -                                back(c);
       -                                c = '\\';
       -                        }
       -                default:
       -                        addchar(c, &cmd);
       -                }
       -        }
       -        addchar('\0', &cmd);
       -
       -        if (repl)
       -                puts(cmd.str);
       -        system(cmd.str);
       +        system(expandcmd());
                if (optdiag)
                        puts("!");
        }