On Wed, 18 Feb 2026 at 18:22:38 -0700, Theo de Raadt wrote:
> Can someone write the -e and -E diff for echo like I suggest please?

this makes cups-filters ippfind happy, running an mkr with it now


diff --git bin/echo/echo.1 bin/echo/echo.1
index 7286602d752..cfd0391eb44 100644
--- bin/echo/echo.1
+++ bin/echo/echo.1
@@ -41,7 +41,7 @@
 .Nd write arguments to the standard output
 .Sh SYNOPSIS
 .Nm echo
-.Op Fl n
+.Op Fl Een
 .Op Ar string ...
 .Sh DESCRIPTION
 The
@@ -63,6 +63,41 @@ is treated as part of
 .Pp
 The options are as follows:
 .Bl -tag -width Ds
+.It Fl E
+Disable interpretation of backslash escape sequences (default).
+.It Fl e
+Enable interpretation of the following backslash escape sequences:
+.Pp
+.Bl -tag -width Ds -offset indent -compact
+.It Cm \e\e
+A literal backslash.
+.It Cm \ea
+Alert (BEL).
+.It Cm \eb
+Backspace.
+.It Cm \ec
+Suppress further output, including the trailing newline character.
+.It Cm \ee
+Escape character.
+.It Cm \ef
+Form feed.
+.It Cm \en
+Newline.
+.It Cm \er
+Carriage return.
+.It Cm \et
+Horizontal tab.
+.It Cm \ev
+Vertical tab.
+.It Cm \e0 Ns Ar nnn
+The character whose octal value is
+.Ar nnn
+(zero to three octal digits).
+.It Cm \ex Ns Ar hh
+The character whose hexadecimal value is
+.Ar hh
+(one or two hexadecimal digits).
+.El
 .It Fl n
 Do not print the trailing newline character.
 .El
@@ -79,17 +114,17 @@ utility is compliant with the
 .St -p1003.1-2008
 specification.
 .Pp
-The flag
+The flags
+.Op Fl E ,
+.Op Fl e ,
+and
 .Op Fl n
-conflicts with the behaviour mandated by the
+conflict with the behaviour mandated by the
 X/Open System Interfaces option of the
 .St -p1003.1-2008
 specification,
-which says it should be treated as part of
+which says they should be treated as part of
 .Ar string .
-Additionally,
-.Nm
-does not support any of the backslash character sequences mandated by XSI.
 .Pp
 .Nm
 also exists as a built-in to
diff --git bin/echo/echo.c bin/echo/echo.c
index 52da05c050f..37e037e5d8c 100644
--- bin/echo/echo.c
+++ bin/echo/echo.c
@@ -30,29 +30,52 @@
  * SUCH DAMAGE.
  */
 
+#include <ctype.h>
 #include <stdio.h>
 #include <string.h>
 #include <unistd.h>
 #include <err.h>
 
+int escape(const char *);
+
 int
 main(int argc, char *argv[])
 {
-       int nflag;
+       int nflag = 0, eflag = 0;
+       const char *p;
 
        if (pledge("stdio", NULL) == -1)
                err(1, "pledge");
 
        /* This utility may NOT do getopt(3) option parsing. */
-       if (*++argv && !strcmp(*argv, "-n")) {
-               ++argv;
-               nflag = 1;
+       if (*++argv && **argv == '-' && (*argv)[1] != '\0') {
+               for (p = *argv + 1; *p != '\0'; p++)
+                       if (*p != 'E' && *p != 'e' && *p != 'n')
+                               goto echoargs;
+
+               for (p = *argv + 1; *p != '\0'; p++) {
+                       switch (*p) {
+                       case 'E':
+                               eflag = 0;
+                               break;
+                       case 'e':
+                               eflag = 1;
+                               break;
+                       case 'n':
+                               nflag = 1;
+                               break;
+                       }
+               }
        }
-       else
-               nflag = 0;
 
+echoargs:
        while (*argv) {
-               (void)fputs(*argv, stdout);
+               if (eflag) {
+                       if (escape(*argv) != 0)
+                               /* \c encountered */
+                               return 0;
+               } else
+                       (void)fputs(*argv, stdout);
                if (*++argv)
                        putchar(' ');
        }
@@ -61,3 +84,86 @@ main(int argc, char *argv[])
 
        return 0;
 }
+
+/* return -1 on \c to suppress further output */
+int
+escape(const char *s)
+{
+       int ch, n;
+
+       while ((ch = *s++) != '\0') {
+               if (ch != '\\') {
+                       putchar(ch);
+                       continue;
+               }
+
+               switch ((ch = *s++)) {
+               case '\0':
+                       putchar('\\');
+                       return 0;
+               case '\\':
+                       putchar('\\');
+                       break;
+               case 'a':
+                       putchar('\a');
+                       break;
+               case 'b':
+                       putchar('\b');
+                       break;
+               case 'c':
+                       return -1;
+               case 'e':
+                       putchar('\033');
+                       break;
+               case 'f':
+                       putchar('\f');
+                       break;
+               case 'n':
+                       putchar('\n');
+                       break;
+               case 'r':
+                       putchar('\r');
+                       break;
+               case 't':
+                       putchar('\t');
+                       break;
+               case 'v':
+                       putchar('\v');
+                       break;
+               case '0':
+                       /* octal: \0nnn */
+                       ch = 0;
+                       for (n = 0; n < 3 && *s >= '0' && *s <= '7'; n++)
+                               ch = ch * 8 + (*s++ - '0');
+                       putchar(ch);
+                       break;
+               case 'x':
+                       /* hexadecimal: \xhh */
+                       if (isxdigit((unsigned char)*s)) {
+                               ch = 0;
+                               for (n = 0;
+                                   n < 2 && isxdigit(*s); n++) {
+                                       ch *= 16;
+                                       if (*s >= '0' && *s <= '9')
+                                               ch += *s - '0';
+                                       else if (*s >= 'a' && *s <= 'f')
+                                               ch += *s - 'a' + 10;
+                                       else
+                                               ch += *s - 'A' + 10;
+                                       s++;
+                               }
+                               putchar(ch);
+                       } else {
+                               putchar('\\');
+                               putchar('x');
+                       }
+                       break;
+               default:
+                       putchar('\\');
+                       putchar(ch);
+                       break;
+               }
+       }
+
+       return 0;
+}

Reply via email to