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 ---