From owner-svn-src-all@FreeBSD.ORG Sun Dec 14 16:26:21 2014 Return-Path: Delivered-To: svn-src-all@freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [8.8.178.115]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by hub.freebsd.org (Postfix) with ESMTPS id 85BEF66A; Sun, 14 Dec 2014 16:26:21 +0000 (UTC) Received: from svn.freebsd.org (svn.freebsd.org [IPv6:2001:1900:2254:2068::e6a:0]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (Client did not present a certificate) by mx1.freebsd.org (Postfix) with ESMTPS id 713F4FC9; Sun, 14 Dec 2014 16:26:21 +0000 (UTC) Received: from svn.freebsd.org ([127.0.1.70]) by svn.freebsd.org (8.14.9/8.14.9) with ESMTP id sBEGQLNQ065972; Sun, 14 Dec 2014 16:26:21 GMT (envelope-from jilles@FreeBSD.org) Received: (from jilles@localhost) by svn.freebsd.org (8.14.9/8.14.9/Submit) id sBEGQKHt065965; Sun, 14 Dec 2014 16:26:20 GMT (envelope-from jilles@FreeBSD.org) Message-Id: <201412141626.sBEGQKHt065965@svn.freebsd.org> X-Authentication-Warning: svn.freebsd.org: jilles set sender to jilles@FreeBSD.org using -f From: Jilles Tjoelker Date: Sun, 14 Dec 2014 16:26:20 +0000 (UTC) To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org Subject: svn commit: r275766 - in head/bin/sh: . tests/execution X-SVN-Group: head MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-BeenThere: svn-src-all@freebsd.org X-Mailman-Version: 2.1.18-1 Precedence: list List-Id: "SVN commit messages for the entire src tree \(except for " user" and " projects" \)" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Sun, 14 Dec 2014 16:26:21 -0000 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 #include #include +#include +#include #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