From patchwork Thu Mar 29 08:28:51 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Martijn Dekker X-Patchwork-Id: 10314735 X-Patchwork-Delegate: herbert@gondor.apana.org.au Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork.web.codeaurora.org (Postfix) with ESMTP id CA5446037E for ; Thu, 29 Mar 2018 08:28:54 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id B404629154 for ; Thu, 29 Mar 2018 08:28:54 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id A87AB29448; Thu, 29 Mar 2018 08:28:54 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-6.9 required=2.0 tests=BAYES_00, RCVD_IN_DNSWL_HI, T_TVD_MIME_EPI autolearn=ham version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 93D3829154 for ; Thu, 29 Mar 2018 08:28:53 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1750716AbeC2I2x (ORCPT ); Thu, 29 Mar 2018 04:28:53 -0400 Received: from kahlil.inlv.org ([37.59.109.123]:41218 "EHLO kahlil.inlv.org" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1750708AbeC2I2w (ORCPT ); Thu, 29 Mar 2018 04:28:52 -0400 Received: from [192.168.1.81] (host81-159-166-89.range81-159.btcentralplus.com [81.159.166.89]) (authenticated bits=0) by kahlil.inlv.org (8.14.9/8.14.4) with ESMTP id w2T8SoJt011246 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES128-SHA bits=128 verify=NO) for ; Thu, 29 Mar 2018 10:28:50 +0200 From: Martijn Dekker Subject: [PATCH] [v5] quote arguments in xtrace output To: dash@vger.kernel.org References: <6e8434e8-87e3-db2a-9d80-147f064c4793@inlv.org> <9092df1b-e6b6-a879-1268-596932892a58@inlv.org> <0cc5830e-4078-8d4b-55e7-8278f8811e13@inlv.org> Openpgp: preference=signencrypt Autocrypt: addr=martijn@inlv.org; keydata= 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 48csIvBNgfdRumZbb7sieWyff/8hEQk= Message-ID: Date: Thu, 29 Mar 2018 09:28:51 +0100 User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.11; rv:52.0) Gecko/20100101 Thunderbird/52.7.0 MIME-Version: 1.0 In-Reply-To: <0cc5830e-4078-8d4b-55e7-8278f8811e13@inlv.org> Content-Language: en-GB Sender: dash-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: dash@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP The xtrace (set -x) output in dash is a bit of a pain, because arguments containing whitespace aren't quoted. This can it impossible to tell which bit belongs to which argument: $ dash -c 'set -x; printf "%s\n" one "two three" four' + printf %s\n one two three four one two three four Another disadvantage is you cannot simply copy and paste the commands from this xtrace output to repeat them for debugging purposes. The attached patch shell-quotes xtrace arguments that contain non-shell-safe characters or are identical to reserved words. A parameter is added to single_quote() indicating whether quoting should be unconditional (0) or conditional upon the string containing characters that are not shell-safe (1). This leaves the unconditional quoting in the output of commands like 'trap' and 'set' unchanged while avoiding excessive quoting in xtrace output. Shell-safe characters are defined as this set: 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz%+,./:@_^- Quoting of assignments needs to be handled specially; in order for the output to be suitable for re-entry into the shell, only the part after the "=" must be quoted. For this purpose, eprintlist() was split in into two functions, eprintvarlist() to print the variable assignments and eprintarglist() to print the rest. I've had this patch in use for a year and found it to work reliably, so I hope it can be considered. The only differences with v4, sent on 18 March 2017, is that I fixed a line with trailing whitespace, and that it should now be good for Patchwork. Thanks, - M. diff --git a/src/alias.c b/src/alias.c index daeacbb..7a88b44 100644 --- a/src/alias.c +++ b/src/alias.c @@ -197,7 +197,7 @@ freealias(struct alias *ap) { void printalias(const struct alias *ap) { - out1fmt("%s=%s\n", ap->name, single_quote(ap->val)); + out1fmt("%s=%s\n", ap->name, single_quote(ap->val, 0)); } STATIC struct alias ** diff --git a/src/eval.c b/src/eval.c index 7498f9d..aba305e 100644 --- a/src/eval.c +++ b/src/eval.c @@ -95,7 +95,8 @@ STATIC int evalcommand(union node *, int); STATIC int evalbltin(const struct builtincmd *, int, char **, int); STATIC int evalfun(struct funcnode *, int, char **, int); STATIC void prehash(union node *); -STATIC int eprintlist(struct output *, struct strlist *, int); +STATIC int eprintvarlist(struct output *, struct strlist *, int); +STATIC void eprintarglist(struct output *, struct strlist *, int); STATIC int bltincmd(int, char **); @@ -786,8 +787,8 @@ evalcommand(union node *cmd, int flags) out = &preverrout; outstr(expandstr(ps4val()), out); sep = 0; - sep = eprintlist(out, varlist.list, sep); - eprintlist(out, arglist.list, sep); + sep = eprintvarlist(out, varlist.list, sep); + eprintarglist(out, arglist.list, sep); outcslow('\n', out); #ifdef FLUSHERR flushout(out); @@ -1107,16 +1108,35 @@ execcmd(int argc, char **argv) STATIC int -eprintlist(struct output *out, struct strlist *sp, int sep) +eprintvarlist(struct output *out, struct strlist *sp, int sep) { while (sp) { const char *p; + int i; - p = " %s" + (1 - sep); + if (sep) + outfmt(out, " "); sep |= 1; - outfmt(out, p, sp->text); + i = 0; + while (sp->text[i] != '=' && sp->text[i] != '\0') + outfmt(out, "%c", sp->text[i++]); + if (sp->text[i] == '=') + outfmt(out, "=%s", single_quote(sp->text+i+1, 1)); sp = sp->next; } return sep; } + +STATIC void +eprintarglist(struct output *out, struct strlist *sp, int sep) +{ + while (sp) { + const char *p; + + p = " %s" + (1 - sep); + sep |= 1; + outfmt(out, p, single_quote(sp->text, 1)); + sp = sp->next; + } +} diff --git a/src/mystring.c b/src/mystring.c index de624b8..e13b1a6 100644 --- a/src/mystring.c +++ b/src/mystring.c @@ -55,6 +55,7 @@ #include "memalloc.h" #include "parser.h" #include "system.h" +#include "token_vars.h" char nullstr[1]; /* zero length string */ @@ -184,13 +185,20 @@ is_number(const char *p) /* * Produce a possibly single quoted string suitable as input to the shell. - * The return string is allocated on the stack. + * If 'conditional' is nonzero, quoting is only done if the string contains + * non-shellsafe characters, or is identical to a shell keyword (reserved + * word); if it is zero, quoting is always done. + * If quoting was done, the return string is allocated on the stack, + * otherwise a pointer to the original string is returned. */ char * -single_quote(const char *s) { +single_quote(const char *s, int conditional) { char *p; + if (conditional && *s != '\0' && s[strspn(s, SHELLSAFECHARS)] == '\0' && ! findkwd(s)) + return (char *)s; + STARTSTACKSTR(p); do { diff --git a/src/mystring.h b/src/mystring.h index 083ea98..d89dde8 100644 --- a/src/mystring.h +++ b/src/mystring.h @@ -54,10 +54,13 @@ intmax_t atomax(const char *, int); intmax_t atomax10(const char *); int number(const char *); int is_number(const char *); -char *single_quote(const char *); +char *single_quote(const char *, int); char *sstrdup(const char *); int pstrcmp(const void *, const void *); const char *const *findstring(const char *, const char *const *, size_t); #define equal(s1, s2) (strcmp(s1, s2) == 0) #define scopy(s1, s2) ((void)strcpy(s2, s1)) + +/* Characters that don't need quoting before re-entry into the shell */ +#define SHELLSAFECHARS "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz%+,./:@_^-" diff --git a/src/trap.c b/src/trap.c index 69eb8ab..a0e4612 100644 --- a/src/trap.c +++ b/src/trap.c @@ -107,7 +107,7 @@ trapcmd(int argc, char **argv) if (trap[signo] != NULL) { out1fmt( "trap -- %s %s\n", - single_quote(trap[signo]), + single_quote(trap[signo], 0), signal_names[signo] ); } diff --git a/src/var.c b/src/var.c index cc6f7f2..414235f 100644 --- a/src/var.c +++ b/src/var.c @@ -417,7 +417,7 @@ showvars(const char *prefix, int on, int off) p = strchrnul(*ep, '='); q = nullstr; if (*p) - q = single_quote(++p); + q = single_quote(++p, 0); out1fmt("%s%s%.*s%s\n", prefix, sep, (int)(p - *ep), *ep, q); }