Author: jilles
Date: Sun Dec 14 16:26:19 2014
New Revision: 275766
URL: https://svnweb.freebsd.org/changeset/base/275766

Log:
  sh: Make sure output suitable as shell input is also printable.
  
  Commands like 'export -p', 'set' and 'trap', and tracing enabled via 'set
  -x' generate output suitable as shell input by adding quotes as necessary.
  
  If there are control characters other than newline or invalid UTF-8
  sequences, use $'...' and \OOO to display them safely.
  
  The resulting output is not parsable by a strict POSIX.1-2008 shell but sh
  from FreeBSD 9.0 and newer and many other shells can parse it.

Added:
  head/bin/sh/tests/execution/set-x4.0   (contents, props changed)
Modified:
  head/bin/sh/output.c
  head/bin/sh/tests/execution/Makefile

Modified: head/bin/sh/output.c
==============================================================================
--- head/bin/sh/output.c        Sun Dec 14 16:17:48 2014        (r275765)
+++ head/bin/sh/output.c        Sun Dec 14 16:26:19 2014        (r275766)
@@ -54,6 +54,8 @@ __FBSDID("$FreeBSD$");
 #include <errno.h>
 #include <unistd.h>
 #include <stdlib.h>
+#include <wchar.h>
+#include <wctype.h>
 
 #include "shell.h"
 #include "syntax.h"
@@ -111,42 +113,86 @@ outstr(const char *p, struct output *fil
        outbin(p, strlen(p), file);
 }
 
+static void
+byteseq(int ch, struct output *file)
+{
+       char seq[4];
+
+       seq[0] = '\\';
+       seq[1] = (ch >> 6 & 0x3) + '0';
+       seq[2] = (ch >> 3 & 0x7) + '0';
+       seq[3] = (ch & 0x7) + '0';
+       outbin(seq, 4, file);
+}
+
+static void
+outdqstr(const char *p, struct output *file)
+{
+       const char *end;
+       mbstate_t mbs;
+       size_t clen;
+       wchar_t wc;
+
+       memset(&mbs, '\0', sizeof(mbs));
+       end = p + strlen(p);
+       outstr("$'", file);
+       while ((clen = mbrtowc(&wc, p, end - p + 1, &mbs)) != 0) {
+               if (clen == (size_t)-2) {
+                       while (p < end)
+                               byteseq(*p++, file);
+                       break;
+               }
+               if (clen == (size_t)-1) {
+                       memset(&mbs, '\0', sizeof(mbs));
+                       byteseq(*p++, file);
+                       continue;
+               }
+               if (wc == L'\n')
+                       outcslow('\n', file), p++;
+               else if (wc == L'\r')
+                       outstr("\\r", file), p++;
+               else if (wc == L'\t')
+                       outstr("\\t", file), p++;
+               else if (!iswprint(wc)) {
+                       for (; clen > 0; clen--)
+                               byteseq(*p++, file);
+               } else {
+                       if (wc == L'\'' || wc == L'\\')
+                               outcslow('\\', file);
+                       outbin(p, clen, file);
+                       p += clen;
+               }
+       }
+       outcslow('\'', file);
+}
+
 /* Like outstr(), but quote for re-input into the shell. */
 void
 outqstr(const char *p, struct output *file)
 {
-       char ch;
-       int inquotes;
+       int i;
 
        if (p[0] == '\0') {
                outstr("''", file);
                return;
        }
-       if (p[strcspn(p, "|&;<>()$`\\\"' \t\n*?[~#=")] == '\0' ||
+       for (i = 0; p[i] != '\0'; i++) {
+               if ((p[i] > '\0' && p[i] < ' ' && p[i] != '\n') ||
+                   (p[i] & 0x80) != 0 || p[i] == '\'') {
+                       outdqstr(p, file);
+                       return;
+               }
+       }
+
+       if (p[strcspn(p, "|&;<>()$`\\\" \n*?[~#=")] == '\0' ||
                        strcmp(p, "[") == 0) {
                outstr(p, file);
                return;
        }
 
-       inquotes = 0;
-       while ((ch = *p++) != '\0') {
-               switch (ch) {
-               case '\'':
-                       /* Can't quote single quotes inside single quotes. */
-                       if (inquotes)
-                               outcslow('\'', file);
-                       inquotes = 0;
-                       outstr("\\'", file);
-                       break;
-               default:
-                       if (!inquotes)
-                               outcslow('\'', file);
-                       inquotes = 1;
-                       outc(ch, file);
-               }
-       }
-       if (inquotes)
-               outcslow('\'', file);
+       outcslow('\'', file);
+       outstr(p, file);
+       outcslow('\'', file);
 }
 
 void

Modified: head/bin/sh/tests/execution/Makefile
==============================================================================
--- head/bin/sh/tests/execution/Makefile        Sun Dec 14 16:17:48 2014        
(r275765)
+++ head/bin/sh/tests/execution/Makefile        Sun Dec 14 16:26:19 2014        
(r275766)
@@ -44,6 +44,7 @@ FILES+=               set-n4.0
 FILES+=                set-x1.0
 FILES+=                set-x2.0
 FILES+=                set-x3.0
+FILES+=                set-x4.0
 FILES+=                shellproc1.0
 FILES+=                subshell1.0 subshell1.0.stdout
 FILES+=                subshell2.0

Added: head/bin/sh/tests/execution/set-x4.0
==============================================================================
--- /dev/null   00:00:00 1970   (empty, because file is newly added)
+++ head/bin/sh/tests/execution/set-x4.0        Sun Dec 14 16:26:19 2014        
(r275766)
@@ -0,0 +1,7 @@
+# $FreeBSD$
+
+key=`printf '\r\t\001\200\300'`
+r=`{ set -x; : "$key"; } 2>&1 >/dev/null`
+case $r in
+*[![:print:]]*) echo fail; exit 3
+esac
_______________________________________________
[email protected] mailing list
http://lists.freebsd.org/mailman/listinfo/svn-src-head
To unsubscribe, send any mail to "[email protected]"

Reply via email to