On 2026-09-06 10:42, Harry Graf wrote:> Probably it is a good idea to combine 
it with
the command flag: --unsafe-names

If the flag is set filenames will be escaped.
Per Artyom Bologov and Harry Graf's suggestion, a new option,
--unsafe-names, -u, has been added to slewsys/ed:
<https://github.com/slewsys/ed.git>

When ed is invoked with option -u, file names are quoted in shell
commands using a variation of Harry Graf's algorithm. A standalone
command-line utility, shell-quote, is provided below in case someone
wants to test it. And per GNU standards, the code is produced by me, a
human, without assistance from an LLM. Enjoy!

Some examples:

    $ ed -p '*' "$(printf 'hello\nworld')"
    hello
    world: No such file or directory
    ?
    *f
    hello
    world
    *!echo %
    echo hello
    world
    hello
    sh: line 2: world: command not found
    !
    *^D

    $ ed -u -p '*' "$(printf 'hello\nworld')"
    hello
    world: No such file or directory
    ?
    *f
    hello
    world
    *!echo %
    echo 'hello
    world'
    hello
    world
    !
    *^D

    $ ed -p '*' "'lo y'all'"
    'lo y'all': No such file or directory
    ?
    *!echo %
    echo 'lo y'all'
    sh: -c: line 1: unexpected EOF while looking for matching `''
    !
    *^D

    $ ed -u -p '*' "'lo y'all'"
    'lo y'all': No such file or directory
    ?
    *!echo %
    echo \''lo y'\''all'\'
    'lo y'all'
    !
    *^D

    $ ed -p '*' ''
    * : No such file or directory
    ?
    *!set % && echo $#
    ... all the environment variables ...
    0
    !
    *

    $ ed -u -p '*' ''
    * : No such file or directory
    ?
    *!set % && echo $#
    set '' && echo $#
    1
    !
    *

Below is the source for a standalone test script.  To compile:

    $ make shell-quote  # If that fails, use: cc -o shell-quote shell-quote.c
    $ ./shell-quote "'''"
    Original: '''
    Quoted: \'\'\'
    $

--- shell-quote.c begins here ---
#include <errno.h>
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>

/* Assert: 256 <= BUFSIZ <= 512KB and that 2 ^ BUFSIZ_LOG2 == BUFSIZ */
#define BUFSIZ_LOG2                                                           \
  ((BUFSIZ & 0x100)   ?  8 :                                                  \
   (BUFSIZ & 0x200)   ?  9 :                                                  \
   (BUFSIZ & 0x400)   ? 10 :                                                  \
   (BUFSIZ & 0x800)   ? 11 :                                                  \
   (BUFSIZ & 0x1000)  ? 12 :                                                  \
   (BUFSIZ & 0x2000)  ? 13 :                                                  \
   (BUFSIZ & 0x4000)  ? 14 :                                                  \
   (BUFSIZ & 0x8000)  ? 15 :                                                  \
   (BUFSIZ & 0x10000) ? 16 :                                                  \
   (BUFSIZ & 0x20000) ? 17 :                                                  \
   (BUFSIZ & 0x40000) ? 18 :                                                  \
   (BUFSIZ & 0x80000) ? 19 : -1)

#define REALLOC_THROW(b, n, i, err)                                           \
  do                                                                          \
    {                                                                         \
      if (((long double)i) > SIZE_MAX)                                        \
        {                                                                     \
          return (err);                                                       \
        }                                                                     \
      else if (!realloc_buffer ((void **)&(b), &(n), (size_t)(i)))            \
        return (err);                                                         \
    }                                                                         \
  while (0)

static char *shell_quote(const char *fn);
static void *realloc_buffer(void **b, size_t *n, size_t i);

int
main(int argc, char *argv[])
{
  char *s = argv[1];
  printf("Original: %s\nQuoted: %s\n", s, shell_quote(s));
  return 0;
}

/* shell_quote: Return pointer shell-quoted fn: 'lo y'all' => \''lo y'\''all'\' 
*/
/* shell_quote: Return pointer shell-quoted fn: 'lo y'all' => \''lo y'\''all'\' 
*/
static char *
shell_quote (const char *fn)
{
  static char *quoted = NULL;
  static size_t quoted_size = 0;

  size_t len = strlen (fn);

  REALLOC_THROW (quoted, quoted_size, len + 3, NULL);

  size_t quoted_len = len + 2; /* 'fn' */
  size_t idx = 0;

  if (!len)
    {
      quoted[idx++] = '\'';
      quoted[idx++] = '\'';
      quoted[idx] = '\0';
      return quoted;
    }

  while (*fn == '\'')
    {
      REALLOC_THROW (quoted, quoted_size, quoted_len + 2, NULL);
      quoted_len += 1;
      quoted[idx++] = '\\';
      quoted[idx++] = '\'';
      ++fn;
    }

  if (*fn)
    quoted[idx++] = '\'';

  for (; *fn; ++fn)
    {
      if (*fn == '\'')
        {
          if (*(fn + 1))
            {
              REALLOC_THROW (quoted, quoted_size, quoted_len + 4, NULL);
              quoted_len += 3;
              quoted[idx++] = '\'';
              quoted[idx++] = '\\';
              quoted[idx++] = '\'';
              quoted[idx++] = '\'';
            }
          else
            {
              REALLOC_THROW (quoted, quoted_size, quoted_len + 3, NULL);
              quoted_len += 2;
              quoted[idx++] = '\'';
              quoted[idx++] = '\\';
              quoted[idx++] = '\'';
            }
        }
      else
        {
          if (*(fn + 1))
            {
              quoted[idx++] = *fn;
            }
          else
            {
              quoted[idx++] = *fn;
              quoted[idx++] = '\'';
            }
        }
    }
  quoted[idx] = '\0';
  return quoted;
}

/* realloc_buffer: Increase size, *n, of buffer, *b, to at least i. */
static void *
realloc_buffer (void **b, size_t *n, size_t i)
{
  char *_ts;
  size_t _ti;

  /*
   * Assert: i >= 0.
   * NB: Allocate memory if i == *n == 0.
   */
  if (i < *n)
    return *b;
  if (i < BUFSIZ)
    _ti = BUFSIZ;
  else if (i >> BUFSIZ_LOG2 <= SIZE_MAX >> (BUFSIZ_LOG2 + 1))
    _ti = (size_t) (i >> BUFSIZ_LOG2 << (BUFSIZ_LOG2 + 1));
  else if (i <= SIZE_MAX)
    _ti = (size_t) i;
  else
    {
      return NULL;
    }
  if (!(_ts = (char *) realloc (*b, _ti)))
    {
      fprintf (stderr, "%s\n", strerror (errno));
      return NULL;
    }
  *n = _ti;
  *b = _ts;
  return *b;
}
--- shell-quote.c ends here ---


Reply via email to