hush: make "set -x" output don't redirectable when fd#2 redirected

function                                             old     new   delta
x_mode_print_optionally_squoted                        -     120    +120
x_mode_flush                                           -      68     +68
save_fd_on_redirect                                  208     243     +35
x_mode_prefix                                          -      27     +27
x_mode_addblock                                        -      23     +23
x_mode_addchr                                          -      17     +17
dump_cmd_in_x_mode                                   110      85     -25
run_pipe                                            1919    1890     -29
print_optionally_squoted                             145       -    -145
------------------------------------------------------------------------------
(add/remove: 5/1 grow/shrink: 1/2 up/down: 290/-199)           Total: 91 bytes

Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
This commit is contained in:
Denys Vlasenko 2018-07-28 12:13:58 +02:00
parent 4b70c926bc
commit aa449c927d

View File

@ -954,9 +954,6 @@ struct globals {
unsigned func_nest_level; /* solely to prevent "local v" in non-functions */ unsigned func_nest_level; /* solely to prevent "local v" in non-functions */
# endif # endif
struct function *top_func; struct function *top_func;
#endif
#if ENABLE_HUSH_MODE_X
unsigned x_mode_depth;
#endif #endif
/* Signal and trap handling */ /* Signal and trap handling */
#if ENABLE_HUSH_FAST #if ENABLE_HUSH_FAST
@ -993,6 +990,15 @@ struct globals {
#if ENABLE_HUSH_MEMLEAK #if ENABLE_HUSH_MEMLEAK
unsigned long memleak_value; unsigned long memleak_value;
#endif #endif
#if ENABLE_HUSH_MODE_X
unsigned x_mode_depth;
/* "set -x" output should not be redirectable with subsequent 2>FILE.
* We dup fd#2 to x_mode_fd when "set -x" is executed, and use it
* for all subsequent output.
*/
int x_mode_fd;
o_string x_mode_buf;
#endif
#if HUSH_DEBUG #if HUSH_DEBUG
int debug_indent; int debug_indent;
#endif #endif
@ -1660,6 +1666,12 @@ static int move_HFILEs_on_redirect(int fd, int avoid_fd)
} }
fl = fl->next_hfile; fl = fl->next_hfile;
} }
#if ENABLE_HUSH_MODE_X
if (G.x_mode_fd > 0 && fd == G.x_mode_fd) {
G.x_mode_fd = xdup_CLOEXEC_and_close(fd, avoid_fd);
return 1; /* "found and moved" */
}
#endif
return 0; /* "not in the list" */ return 0; /* "not in the list" */
} }
#if ENABLE_FEATURE_SH_STANDALONE && BB_MMU #if ENABLE_FEATURE_SH_STANDALONE && BB_MMU
@ -2903,6 +2915,11 @@ static void o_addstr(o_string *o, const char *str)
o_addblock(o, str, strlen(str)); o_addblock(o, str, strlen(str));
} }
static void o_addstr_with_NUL(o_string *o, const char *str)
{
o_addblock(o, str, strlen(str) + 1);
}
#if !BB_MMU #if !BB_MMU
static void nommu_addchr(o_string *o, int ch) static void nommu_addchr(o_string *o, int ch)
{ {
@ -2913,10 +2930,36 @@ static void nommu_addchr(o_string *o, int ch)
# define nommu_addchr(o, str) ((void)0) # define nommu_addchr(o, str) ((void)0)
#endif #endif
static void o_addstr_with_NUL(o_string *o, const char *str) #if ENABLE_HUSH_MODE_X
static void x_mode_addchr(int ch)
{ {
o_addblock(o, str, strlen(str) + 1); o_addchr(&G.x_mode_buf, ch);
} }
static void x_mode_addstr(const char *str)
{
o_addstr(&G.x_mode_buf, str);
}
static void x_mode_addblock(const char *str, int len)
{
o_addblock(&G.x_mode_buf, str, len);
}
static void x_mode_prefix(void)
{
int n = G.x_mode_depth;
do x_mode_addchr('+'); while (--n >= 0);
}
static void x_mode_flush(void)
{
int len = G.x_mode_buf.length;
if (len <= 0)
return;
if (G.x_mode_fd > 0) {
G.x_mode_buf.data[len] = '\n';
full_write(G.x_mode_fd, G.x_mode_buf.data, len + 1);
}
G.x_mode_buf.length = 0;
}
#endif
/* /*
* HUSH_BRACE_EXPANSION code needs corresponding quoting on variable expansion side. * HUSH_BRACE_EXPANSION code needs corresponding quoting on variable expansion side.
@ -8030,45 +8073,43 @@ static void execvp_or_die(char **argv)
} }
#if ENABLE_HUSH_MODE_X #if ENABLE_HUSH_MODE_X
static void print_optionally_squoted(FILE *fp, const char *str) static void x_mode_print_optionally_squoted(const char *str)
{ {
unsigned len; unsigned len;
const char *cp; const char *cp;
cp = str; cp = str;
if (str[0] != '{' && str[0] != '(') for (;;) {
if (!*cp) { /* the set of chars which-cause-string-to-be-squoted mimics bash */
/* test a char with: bash -c 'set -x; echo "CH"' */
if (str[strcspn(str, "\\\"'`$(){}[]<>;#&|~*?!^"
" " "\001\002\003\004\005\006\007"
"\010\011\012\013\014\015\016\017"
"\020\021\022\023\024\025\026\027"
"\030\031\032\033\034\035\036\037"
)
] == '\0'
) {
/* string has no special chars */ /* string has no special chars */
fputs(str, fp); x_mode_addstr(str);
return; return;
} }
if (*cp == '\\') break;
if (*cp == '\'') break;
if (*cp == '"') break;
if (*cp == '$') break;
if (*cp == '!') break;
if (*cp == '*') break;
if (*cp == '[') break;
if (*cp == ']') break;
#if ENABLE_HUSH_TICK
if (*cp == '`') break;
#endif
if (isspace(*cp)) break;
cp++;
}
cp = str; cp = str;
for (;;) { for (;;) {
/* print '....' up to EOL or first squote */ /* print '....' up to EOL or first squote */
len = (int)(strchrnul(cp, '\'') - cp); len = (int)(strchrnul(cp, '\'') - cp);
if (len != 0) { if (len != 0) {
fprintf(fp, "'%.*s'", len, cp); x_mode_addchr('\'');
x_mode_addblock(cp, len);
x_mode_addchr('\'');
cp += len; cp += len;
} }
if (*cp == '\0') if (*cp == '\0')
break; break;
/* string contains squote(s), print them as \' */ /* string contains squote(s), print them as \' */
fprintf(fp, "\\'"); x_mode_addchr('\\');
x_mode_addchr('\'');
cp++; cp++;
} }
} }
@ -8078,19 +8119,19 @@ static void dump_cmd_in_x_mode(char **argv)
unsigned n; unsigned n;
/* "+[+++...][ cmd...]\n\0" */ /* "+[+++...][ cmd...]\n\0" */
n = G.x_mode_depth; x_mode_prefix();
do bb_putchar_stderr('+'); while ((int)(--n) >= 0);
n = 0; n = 0;
while (argv[n]) { while (argv[n]) {
if (argv[n][0] == '\0') x_mode_addchr(' ');
fputs(" ''", stderr); if (argv[n][0] == '\0') {
else { x_mode_addchr('\'');
bb_putchar_stderr(' '); x_mode_addchr('\'');
print_optionally_squoted(stderr, argv[n]); } else {
x_mode_print_optionally_squoted(argv[n]);
} }
n++; n++;
} }
bb_putchar_stderr('\n'); x_mode_flush();
} }
} }
#else #else
@ -8885,17 +8926,14 @@ static NOINLINE int run_pipe(struct pipe *pi)
#if ENABLE_HUSH_MODE_X #if ENABLE_HUSH_MODE_X
if (G_x_mode) { if (G_x_mode) {
char *eq; char *eq;
if (i == 0) { if (i == 0)
unsigned n = G.x_mode_depth; x_mode_prefix();
do x_mode_addchr(' ');
bb_putchar_stderr('+');
while ((int)(--n) >= 0);
}
eq = strchrnul(p, '='); eq = strchrnul(p, '=');
fprintf(stderr, " %.*s=", (int)(eq - p), p); if (*eq) eq++;
if (*eq) x_mode_addblock(p, (eq - p));
print_optionally_squoted(stderr, eq + 1); x_mode_print_optionally_squoted(eq);
bb_putchar_stderr('\n'); x_mode_flush();
} }
#endif #endif
debug_printf_env("set shell var:'%s'->'%s'\n", *argv, p); debug_printf_env("set shell var:'%s'->'%s'\n", *argv, p);
@ -9691,6 +9729,7 @@ static int set_mode(int state, char mode, const char *o_opt)
break; break;
case 'x': case 'x':
IF_HUSH_MODE_X(G_x_mode = state;) IF_HUSH_MODE_X(G_x_mode = state;)
IF_HUSH_MODE_X(if (G.x_mode_fd <= 0) G.x_mode_fd = dup_CLOEXEC(2, 10);)
break; break;
case 'o': case 'o':
if (!o_opt) { if (!o_opt) {