mirror of
https://github.com/fish-shell/fish-shell.git
synced 2024-11-22 12:41:08 +08:00
Make subcommands modify $status, and make builtin_set not modify status unless it fails
https://github.com/fish-shell/fish-shell/issues/547 https://github.com/fish-shell/fish-shell/issues/214
This commit is contained in:
parent
0db1b6ce44
commit
ad8d68dd43
|
@ -358,11 +358,9 @@ bool autoload_t::locate_file_and_maybe_load_it(const wcstring &cmd, bool really_
|
||||||
/* If we have a script, either built-in or a file source, then run it */
|
/* If we have a script, either built-in or a file source, then run it */
|
||||||
if (really_load && has_script_source)
|
if (really_load && has_script_source)
|
||||||
{
|
{
|
||||||
if (exec_subshell(script_source) == -1)
|
if (exec_subshell(script_source, false /* do not apply exit status */) == -1)
|
||||||
{
|
{
|
||||||
/*
|
/* Do nothing on failure */
|
||||||
Do nothing on failiure
|
|
||||||
*/
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -215,7 +215,7 @@ wcstring builtin_help_get(parser_t &parser, const wchar_t *name)
|
||||||
wcstring out;
|
wcstring out;
|
||||||
const wcstring name_esc = escape_string(name, 1);
|
const wcstring name_esc = escape_string(name, 1);
|
||||||
const wcstring cmd = format_string(L"__fish_print_help %ls", name_esc.c_str());
|
const wcstring cmd = format_string(L"__fish_print_help %ls", name_esc.c_str());
|
||||||
if (exec_subshell(cmd, lst) >= 0)
|
if (exec_subshell(cmd, lst, false /* don't apply exit status */) >= 0)
|
||||||
{
|
{
|
||||||
for (size_t i=0; i<lst.size(); i++)
|
for (size_t i=0; i<lst.size(); i++)
|
||||||
{
|
{
|
||||||
|
|
|
@ -379,58 +379,21 @@ static void print_variables(int include_values, int esc, bool shorten_ok, int sc
|
||||||
*/
|
*/
|
||||||
static int builtin_set(parser_t &parser, wchar_t **argv)
|
static int builtin_set(parser_t &parser, wchar_t **argv)
|
||||||
{
|
{
|
||||||
|
/** Variables used for parsing the argument list */
|
||||||
/**
|
const struct woption long_options[] =
|
||||||
Variables used for parsing the argument list
|
|
||||||
*/
|
|
||||||
static const struct woption
|
|
||||||
long_options[] =
|
|
||||||
{
|
{
|
||||||
{
|
{ L"export", no_argument, 0, 'x' },
|
||||||
L"export", no_argument, 0, 'x'
|
{ L"global", no_argument, 0, 'g' },
|
||||||
}
|
{ L"local", no_argument, 0, 'l' },
|
||||||
,
|
{ L"erase", no_argument, 0, 'e' },
|
||||||
{
|
{ L"names", no_argument, 0, 'n' },
|
||||||
L"global", no_argument, 0, 'g'
|
{ L"unexport", no_argument, 0, 'u' },
|
||||||
}
|
{ L"universal", no_argument, 0, 'U' },
|
||||||
,
|
{ L"long", no_argument, 0, 'L' },
|
||||||
{
|
{ L"query", no_argument, 0, 'q' },
|
||||||
L"local", no_argument, 0, 'l'
|
{ L"help", no_argument, 0, 'h' },
|
||||||
}
|
{ 0, 0, 0, 0 }
|
||||||
,
|
} ;
|
||||||
{
|
|
||||||
L"erase", no_argument, 0, 'e'
|
|
||||||
}
|
|
||||||
,
|
|
||||||
{
|
|
||||||
L"names", no_argument, 0, 'n'
|
|
||||||
}
|
|
||||||
,
|
|
||||||
{
|
|
||||||
L"unexport", no_argument, 0, 'u'
|
|
||||||
}
|
|
||||||
,
|
|
||||||
{
|
|
||||||
L"universal", no_argument, 0, 'U'
|
|
||||||
}
|
|
||||||
,
|
|
||||||
{
|
|
||||||
L"long", no_argument, 0, 'L'
|
|
||||||
}
|
|
||||||
,
|
|
||||||
{
|
|
||||||
L"query", no_argument, 0, 'q'
|
|
||||||
}
|
|
||||||
,
|
|
||||||
{
|
|
||||||
L"help", no_argument, 0, 'h'
|
|
||||||
}
|
|
||||||
,
|
|
||||||
{
|
|
||||||
0, 0, 0, 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
;
|
|
||||||
|
|
||||||
const wchar_t *short_options = L"+xglenuULqh";
|
const wchar_t *short_options = L"+xglenuULqh";
|
||||||
|
|
||||||
|
@ -443,6 +406,8 @@ static int builtin_set(parser_t &parser, wchar_t **argv)
|
||||||
int erase = 0, list = 0, unexport=0;
|
int erase = 0, list = 0, unexport=0;
|
||||||
int universal = 0, query=0;
|
int universal = 0, query=0;
|
||||||
bool shorten_ok = true;
|
bool shorten_ok = true;
|
||||||
|
bool preserve_incoming_failure_exit_status = true;
|
||||||
|
const int incoming_exit_status = proc_get_last_status();
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Variables used for performing the actual work
|
Variables used for performing the actual work
|
||||||
|
@ -474,10 +439,12 @@ static int builtin_set(parser_t &parser, wchar_t **argv)
|
||||||
|
|
||||||
case 'e':
|
case 'e':
|
||||||
erase = 1;
|
erase = 1;
|
||||||
|
preserve_incoming_failure_exit_status = false;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'n':
|
case 'n':
|
||||||
list = 1;
|
list = 1;
|
||||||
|
preserve_incoming_failure_exit_status = false;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'x':
|
case 'x':
|
||||||
|
@ -506,6 +473,7 @@ static int builtin_set(parser_t &parser, wchar_t **argv)
|
||||||
|
|
||||||
case 'q':
|
case 'q':
|
||||||
query = 1;
|
query = 1;
|
||||||
|
preserve_incoming_failure_exit_status = false;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'h':
|
case 'h':
|
||||||
|
@ -825,6 +793,8 @@ static int builtin_set(parser_t &parser, wchar_t **argv)
|
||||||
|
|
||||||
free(dest);
|
free(dest);
|
||||||
|
|
||||||
|
if (retcode == STATUS_BUILTIN_OK && preserve_incoming_failure_exit_status)
|
||||||
|
retcode = incoming_exit_status;
|
||||||
return retcode;
|
return retcode;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
2
color.h
2
color.h
|
@ -135,7 +135,7 @@ public:
|
||||||
/** Returns whether the color is bold */
|
/** Returns whether the color is bold */
|
||||||
bool is_bold() const
|
bool is_bold() const
|
||||||
{
|
{
|
||||||
return !! (flags & flag_bold);
|
return !!(flags & flag_bold);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Set whether the color is bold */
|
/** Set whether the color is bold */
|
||||||
|
|
2
common.h
2
common.h
|
@ -150,7 +150,7 @@ extern const wchar_t *program_name;
|
||||||
read( 0, &exit_read_buff, 1 ); \
|
read( 0, &exit_read_buff, 1 ); \
|
||||||
exit_without_destructors( 1 ); \
|
exit_without_destructors( 1 ); \
|
||||||
} \
|
} \
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Exit program at once, leaving an error message about running out of memory.
|
Exit program at once, leaving an error message about running out of memory.
|
||||||
|
|
|
@ -465,7 +465,7 @@ bool completer_t::condition_test(const wcstring &condition)
|
||||||
if (cached_entry == condition_cache.end())
|
if (cached_entry == condition_cache.end())
|
||||||
{
|
{
|
||||||
/* Compute new value and reinsert it */
|
/* Compute new value and reinsert it */
|
||||||
test_res = (0 == exec_subshell(condition));
|
test_res = (0 == exec_subshell(condition, false /* don't apply exit status */));
|
||||||
condition_cache[condition] = test_res;
|
condition_cache[condition] = test_res;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -1007,7 +1007,7 @@ void completer_t::complete_cmd_desc(const wcstring &str)
|
||||||
since apropos is only called once.
|
since apropos is only called once.
|
||||||
*/
|
*/
|
||||||
wcstring_list_t list;
|
wcstring_list_t list;
|
||||||
if (exec_subshell(lookup_cmd, list) != -1)
|
if (exec_subshell(lookup_cmd, list, false /* don't apply exit status */) != -1)
|
||||||
{
|
{
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
@ -11,8 +11,7 @@ By defining the \c fish_prompt function, the user can choose a custom
|
||||||
prompt. The \c fish_prompt function is executed when the prompt is to
|
prompt. The \c fish_prompt function is executed when the prompt is to
|
||||||
be shown, and the output is used as a prompt.
|
be shown, and the output is used as a prompt.
|
||||||
|
|
||||||
Please keep in mind that the function is executed by <a
|
The exit status of commands within \c fish_prompt will not modify the <a href="index.html#variables-status">$status</a> seen outside of fish_prompt.
|
||||||
href='index.html#expand-command-substitution'>command substitution</a>, and so the exit status of commands within fish_prompt will not modify the <a href="index.html#variables-status">$status</a> seen outside of fish_prompt.
|
|
||||||
|
|
||||||
\subsection fish_prompt-example Example
|
\subsection fish_prompt-example Example
|
||||||
|
|
||||||
|
|
|
@ -578,9 +578,8 @@ this list is executed, and substituted by the output. If the output is
|
||||||
more than one line long, each line will be expanded to a new
|
more than one line long, each line will be expanded to a new
|
||||||
parameter.
|
parameter.
|
||||||
|
|
||||||
A command substitution will not change the value of the <a
|
The exit status of the last run command substitution is available in the <a
|
||||||
href='#variables-status'>status</a> variable outside of the command
|
href='#variables-status'>status</a> variable.
|
||||||
substitution.
|
|
||||||
|
|
||||||
Only part of the output can be used, see <a href='#expand-index-range'>index
|
Only part of the output can be used, see <a href='#expand-index-range'>index
|
||||||
range expansion</a> for details.
|
range expansion</a> for details.
|
||||||
|
|
|
@ -68,13 +68,14 @@ non-switch arguments. For example, <code>set flags -l</code> will have
|
||||||
the effect of setting the value of the variable <code>flags</code> to
|
the effect of setting the value of the variable <code>flags</code> to
|
||||||
'-l', not making the variable local.
|
'-l', not making the variable local.
|
||||||
|
|
||||||
In assignment mode, set exits with an exit status of zero it the
|
In assignment mode, set exits with a non-zero exit status if variable
|
||||||
variable assignments where sucessfully performed, with a non-zero exit
|
assignments could not be successfully performed. If the variable assignments
|
||||||
status otherwise. In query mode, the exit status is the number of
|
were performed, the exit status is unchanged. This allows simultaneous capture
|
||||||
variables that where not found. In erase mode, set exits with a zero
|
of the output and exit status of a subcommand, e.g. <code>if set output
|
||||||
exit status in case of success, with a non-zero exit status if the
|
(command)</code>. In query mode, the exit status is the number of variables that
|
||||||
commandline was invalid, if the variable was write-protected or if the
|
were not found. In erase mode, set exits with a zero exit status in case of
|
||||||
variable did not exist.
|
success, with a non-zero exit status if the commandline was invalid, if the
|
||||||
|
variable was write-protected or if the variable did not exist.
|
||||||
|
|
||||||
\subsection set-example Example
|
\subsection set-example Example
|
||||||
|
|
||||||
|
@ -85,3 +86,9 @@ variable did not exist.
|
||||||
<code>set -e smurf</code> removes the variable \c smurf.
|
<code>set -e smurf</code> removes the variable \c smurf.
|
||||||
|
|
||||||
<code>set PATH[4] ~/bin</code> changes the fourth element of the \c PATH array to \c ~/bin
|
<code>set PATH[4] ~/bin</code> changes the fourth element of the \c PATH array to \c ~/bin
|
||||||
|
|
||||||
|
<pre>if set python_path (which python)
|
||||||
|
echo "Python is at $python_path"
|
||||||
|
end</pre>
|
||||||
|
|
||||||
|
The above outputs the path to Python if \c which returns true.
|
||||||
|
|
81
exec.cpp
81
exec.cpp
|
@ -125,7 +125,7 @@ void exec_close(int fd)
|
||||||
int exec_pipe(int fd[2])
|
int exec_pipe(int fd[2])
|
||||||
{
|
{
|
||||||
ASSERT_IS_MAIN_THREAD();
|
ASSERT_IS_MAIN_THREAD();
|
||||||
|
|
||||||
int res;
|
int res;
|
||||||
while ((res=pipe(fd)))
|
while ((res=pipe(fd)))
|
||||||
{
|
{
|
||||||
|
@ -621,11 +621,11 @@ void exec(parser_t &parser, job_t *j)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// This is a pipe that the "current" process in our loop below reads from
|
// This is a pipe that the "current" process in our loop below reads from
|
||||||
// Only pipe_read->pipe_fd[0] is used
|
// Only pipe_read->pipe_fd[0] is used
|
||||||
shared_ptr<io_pipe_t> pipe_read(new io_pipe_t(0, true));
|
shared_ptr<io_pipe_t> pipe_read(new io_pipe_t(0, true));
|
||||||
|
|
||||||
// This is the pipe that the "current" process in our loop below writes to
|
// This is the pipe that the "current" process in our loop below writes to
|
||||||
shared_ptr<io_pipe_t> pipe_write(new io_pipe_t(1, false));
|
shared_ptr<io_pipe_t> pipe_write(new io_pipe_t(1, false));
|
||||||
|
|
||||||
|
@ -687,7 +687,7 @@ void exec(parser_t &parser, job_t *j)
|
||||||
set_child_group(j, &keepalive, 0);
|
set_child_group(j, &keepalive, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
This loop loops over every process_t in the job, starting it as
|
This loop loops over every process_t in the job, starting it as
|
||||||
appropriate. This turns out to be rather complex, since a
|
appropriate. This turns out to be rather complex, since a
|
||||||
|
@ -695,17 +695,17 @@ void exec(parser_t &parser, job_t *j)
|
||||||
|
|
||||||
The loop also has to handle pipelining between the jobs.
|
The loop also has to handle pipelining between the jobs.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/* We can have up to three pipes "in flight" at a time:
|
/* We can have up to three pipes "in flight" at a time:
|
||||||
|
|
||||||
1. The pipe the current process should read from (courtesy of the previous process)
|
1. The pipe the current process should read from (courtesy of the previous process)
|
||||||
2. The pipe that the current process should write to
|
2. The pipe that the current process should write to
|
||||||
3. The pipe that the next process should read from (courtesy of us)
|
3. The pipe that the next process should read from (courtesy of us)
|
||||||
|
|
||||||
We are careful to set these to -1 when closed, so if we exit the loop abruptly, we can still close them.
|
We are careful to set these to -1 when closed, so if we exit the loop abruptly, we can still close them.
|
||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
int pipe_current_read = -1, pipe_current_write = -1, pipe_next_read = -1;
|
int pipe_current_read = -1, pipe_current_write = -1, pipe_next_read = -1;
|
||||||
for (process_t *p=j->first_process; p; p = p->next)
|
for (process_t *p=j->first_process; p; p = p->next)
|
||||||
{
|
{
|
||||||
|
@ -713,10 +713,10 @@ void exec(parser_t &parser, job_t *j)
|
||||||
assert(pipe_current_read == -1);
|
assert(pipe_current_read == -1);
|
||||||
pipe_current_read = pipe_next_read;
|
pipe_current_read = pipe_next_read;
|
||||||
pipe_next_read = -1;
|
pipe_next_read = -1;
|
||||||
|
|
||||||
/* Record the current read in pipe_read */
|
/* Record the current read in pipe_read */
|
||||||
pipe_read->pipe_fd[0] = pipe_current_read;
|
pipe_read->pipe_fd[0] = pipe_current_read;
|
||||||
|
|
||||||
/* See if we need a pipe */
|
/* See if we need a pipe */
|
||||||
const bool pipes_to_next_command = (p->next != NULL);
|
const bool pipes_to_next_command = (p->next != NULL);
|
||||||
|
|
||||||
|
@ -760,15 +760,15 @@ void exec(parser_t &parser, job_t *j)
|
||||||
job_mark_process_as_failed(j, p);
|
job_mark_process_as_failed(j, p);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// This tells the redirection about the fds, but the redirection does not close them
|
// This tells the redirection about the fds, but the redirection does not close them
|
||||||
memcpy(pipe_write->pipe_fd, local_pipe, sizeof(int)*2);
|
memcpy(pipe_write->pipe_fd, local_pipe, sizeof(int)*2);
|
||||||
|
|
||||||
// Record our pipes
|
// Record our pipes
|
||||||
// The fds should be negative to indicate that we aren't overwriting an fd we failed to close
|
// The fds should be negative to indicate that we aren't overwriting an fd we failed to close
|
||||||
assert(pipe_current_write == -1);
|
assert(pipe_current_write == -1);
|
||||||
pipe_current_write = local_pipe[1];
|
pipe_current_write = local_pipe[1];
|
||||||
|
|
||||||
assert(pipe_next_read == -1);
|
assert(pipe_next_read == -1);
|
||||||
pipe_next_read = local_pipe[0];
|
pipe_next_read = local_pipe[0];
|
||||||
}
|
}
|
||||||
|
@ -838,7 +838,7 @@ void exec(parser_t &parser, job_t *j)
|
||||||
j->io.push_back(io_buffer);
|
j->io.push_back(io_buffer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (! exec_error)
|
if (! exec_error)
|
||||||
{
|
{
|
||||||
internal_exec_helper(parser, def.c_str(), TOP, j->io);
|
internal_exec_helper(parser, def.c_str(), TOP, j->io);
|
||||||
|
@ -865,7 +865,7 @@ void exec(parser_t &parser, job_t *j)
|
||||||
j->io.push_back(io_buffer);
|
j->io.push_back(io_buffer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (! exec_error)
|
if (! exec_error)
|
||||||
{
|
{
|
||||||
internal_exec_helper(parser, p->argv0(), TOP, j->io);
|
internal_exec_helper(parser, p->argv0(), TOP, j->io);
|
||||||
|
@ -1389,7 +1389,7 @@ void exec(parser_t &parser, job_t *j)
|
||||||
exec_close(pipe_current_read);
|
exec_close(pipe_current_read);
|
||||||
pipe_current_read = -1;
|
pipe_current_read = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Close the write end too, since the curent child subprocess already has a copy of it. */
|
/* Close the write end too, since the curent child subprocess already has a copy of it. */
|
||||||
if (pipe_current_write >= 0)
|
if (pipe_current_write >= 0)
|
||||||
{
|
{
|
||||||
|
@ -1397,7 +1397,7 @@ void exec(parser_t &parser, job_t *j)
|
||||||
pipe_current_write = -1;
|
pipe_current_write = -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Clean up any file descriptors we left open */
|
/* Clean up any file descriptors we left open */
|
||||||
if (pipe_current_read >= 0)
|
if (pipe_current_read >= 0)
|
||||||
exec_close(pipe_current_read);
|
exec_close(pipe_current_read);
|
||||||
|
@ -1405,7 +1405,7 @@ void exec(parser_t &parser, job_t *j)
|
||||||
exec_close(pipe_current_write);
|
exec_close(pipe_current_write);
|
||||||
if (pipe_next_read >= 0)
|
if (pipe_next_read >= 0)
|
||||||
exec_close(pipe_next_read);
|
exec_close(pipe_next_read);
|
||||||
|
|
||||||
/* The keepalive process is no longer needed, so we terminate it with extreme prejudice */
|
/* The keepalive process is no longer needed, so we terminate it with extreme prejudice */
|
||||||
if (needs_keepalive)
|
if (needs_keepalive)
|
||||||
{
|
{
|
||||||
|
@ -1438,11 +1438,11 @@ void exec(parser_t &parser, job_t *j)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static int exec_subshell_internal(const wcstring &cmd, wcstring_list_t *lst)
|
static int exec_subshell_internal(const wcstring &cmd, wcstring_list_t *lst, bool apply_exit_status)
|
||||||
{
|
{
|
||||||
ASSERT_IS_MAIN_THREAD();
|
ASSERT_IS_MAIN_THREAD();
|
||||||
int prev_subshell = is_subshell;
|
int prev_subshell = is_subshell;
|
||||||
int status, prev_status;
|
const int prev_status = proc_get_last_status();
|
||||||
char sep=0;
|
char sep=0;
|
||||||
|
|
||||||
const env_var_t ifs = env_get_string(L"IFS");
|
const env_var_t ifs = env_get_string(L"IFS");
|
||||||
|
@ -1456,38 +1456,33 @@ static int exec_subshell_internal(const wcstring &cmd, wcstring_list_t *lst)
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
sep = 0;
|
sep = 0;
|
||||||
debug(0, L"Warning - invalid command substitution separator '%lc'. Please change the firsta character of IFS", ifs[0]);
|
debug(0, L"Warning - invalid command substitution separator '%lc'. Please change the first character of IFS", ifs[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
is_subshell=1;
|
is_subshell=1;
|
||||||
|
|
||||||
prev_status = proc_get_last_status();
|
|
||||||
|
|
||||||
const shared_ptr<io_buffer_t> io_buffer(io_buffer_t::create(0));
|
int subcommand_status = -1; //assume the worst
|
||||||
|
|
||||||
// IO buffer creation may fail (e.g. if we have too many open files to make a pipe), so this may be null
|
// IO buffer creation may fail (e.g. if we have too many open files to make a pipe), so this may be null
|
||||||
if (io_buffer.get() == NULL)
|
const shared_ptr<io_buffer_t> io_buffer(io_buffer_t::create(0));
|
||||||
{
|
if (io_buffer.get() != NULL)
|
||||||
status = -1;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
{
|
||||||
parser_t &parser = parser_t::principal_parser();
|
parser_t &parser = parser_t::principal_parser();
|
||||||
if (parser.eval(cmd, io_chain_t(io_buffer), SUBST))
|
if (parser.eval(cmd, io_chain_t(io_buffer), SUBST) == 0)
|
||||||
{
|
{
|
||||||
status = -1;
|
subcommand_status = proc_get_last_status();
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
status = proc_get_last_status();
|
|
||||||
}
|
|
||||||
|
|
||||||
io_buffer->read();
|
io_buffer->read();
|
||||||
}
|
}
|
||||||
|
|
||||||
proc_set_last_status(prev_status);
|
// If the caller asked us to preserve the exit status, restore the old status
|
||||||
|
// Otherwise set the status of the subcommand
|
||||||
|
proc_set_last_status(apply_exit_status ? subcommand_status : prev_status);
|
||||||
|
|
||||||
|
|
||||||
is_subshell = prev_subshell;
|
is_subshell = prev_subshell;
|
||||||
|
|
||||||
|
@ -1515,17 +1510,17 @@ static int exec_subshell_internal(const wcstring &cmd, wcstring_list_t *lst)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return status;
|
return subcommand_status;
|
||||||
}
|
}
|
||||||
|
|
||||||
int exec_subshell(const wcstring &cmd, std::vector<wcstring> &outputs)
|
int exec_subshell(const wcstring &cmd, std::vector<wcstring> &outputs, bool apply_exit_status)
|
||||||
{
|
{
|
||||||
ASSERT_IS_MAIN_THREAD();
|
ASSERT_IS_MAIN_THREAD();
|
||||||
return exec_subshell_internal(cmd, &outputs);
|
return exec_subshell_internal(cmd, &outputs, apply_exit_status);
|
||||||
}
|
}
|
||||||
|
|
||||||
__warn_unused int exec_subshell(const wcstring &cmd)
|
__warn_unused int exec_subshell(const wcstring &cmd, bool apply_exit_status)
|
||||||
{
|
{
|
||||||
ASSERT_IS_MAIN_THREAD();
|
ASSERT_IS_MAIN_THREAD();
|
||||||
return exec_subshell_internal(cmd, NULL);
|
return exec_subshell_internal(cmd, NULL, apply_exit_status);
|
||||||
}
|
}
|
||||||
|
|
4
exec.h
4
exec.h
|
@ -54,8 +54,8 @@ void exec(parser_t &parser, job_t *j);
|
||||||
|
|
||||||
\return the status of the last job to exit, or -1 if en error was encountered.
|
\return the status of the last job to exit, or -1 if en error was encountered.
|
||||||
*/
|
*/
|
||||||
__warn_unused int exec_subshell(const wcstring &cmd, std::vector<wcstring> &outputs);
|
__warn_unused int exec_subshell(const wcstring &cmd, std::vector<wcstring> &outputs, bool preserve_exit_status);
|
||||||
__warn_unused int exec_subshell(const wcstring &cmd);
|
__warn_unused int exec_subshell(const wcstring &cmd, bool preserve_exit_status);
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -1352,7 +1352,7 @@ static int expand_cmdsubst(parser_t &parser, const wcstring &input, std::vector<
|
||||||
|
|
||||||
const wcstring subcmd(paran_begin + 1, paran_end-paran_begin - 1);
|
const wcstring subcmd(paran_begin + 1, paran_end-paran_begin - 1);
|
||||||
|
|
||||||
if (exec_subshell(subcmd, sub_res) == -1)
|
if (exec_subshell(subcmd, sub_res, true /* do apply exit status */) == -1)
|
||||||
{
|
{
|
||||||
parser.error(CMDSUBST_ERROR, -1, L"Unknown error while evaulating command substitution");
|
parser.error(CMDSUBST_ERROR, -1, L"Unknown error while evaulating command substitution");
|
||||||
return 0;
|
return 0;
|
||||||
|
|
4
kill.cpp
4
kill.cpp
|
@ -112,7 +112,7 @@ void kill_add(const wcstring &str)
|
||||||
|
|
||||||
if (! cmd.empty())
|
if (! cmd.empty())
|
||||||
{
|
{
|
||||||
if (exec_subshell(cmd) == -1)
|
if (exec_subshell(cmd, false /* do not apply exit status */) == -1)
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
Do nothing on failiure
|
Do nothing on failiure
|
||||||
|
@ -175,7 +175,7 @@ static void kill_check_x_buffer()
|
||||||
wcstring cmd = L"xsel -t 500 -b";
|
wcstring cmd = L"xsel -t 500 -b";
|
||||||
wcstring new_cut_buffer=L"";
|
wcstring new_cut_buffer=L"";
|
||||||
wcstring_list_t list;
|
wcstring_list_t list;
|
||||||
if (exec_subshell(cmd, list) != -1)
|
if (exec_subshell(cmd, list, false /* do not apply exit status */) != -1)
|
||||||
{
|
{
|
||||||
|
|
||||||
for (i=0; i<list.size(); i++)
|
for (i=0; i<list.size(); i++)
|
||||||
|
|
13
parser.cpp
13
parser.cpp
|
@ -1203,7 +1203,7 @@ bool parser_t::job_remove(job_t *j)
|
||||||
void parser_t::job_promote(job_t *job)
|
void parser_t::job_promote(job_t *job)
|
||||||
{
|
{
|
||||||
signal_block();
|
signal_block();
|
||||||
|
|
||||||
job_list_t::iterator loc = std::find(my_job_list.begin(), my_job_list.end(), job);
|
job_list_t::iterator loc = std::find(my_job_list.begin(), my_job_list.end(), job);
|
||||||
assert(loc != my_job_list.end());
|
assert(loc != my_job_list.end());
|
||||||
|
|
||||||
|
@ -2900,11 +2900,12 @@ int parser_t::test_args(const wchar_t * buff, wcstring *out, const wchar_t *pre
|
||||||
}
|
}
|
||||||
|
|
||||||
// helper type used in parser::test below
|
// helper type used in parser::test below
|
||||||
struct block_info_t {
|
struct block_info_t
|
||||||
|
{
|
||||||
int position; //tokenizer position
|
int position; //tokenizer position
|
||||||
block_type_t type; //type of the block
|
block_type_t type; //type of the block
|
||||||
int indentation; //indentation associated with the block
|
int indentation; //indentation associated with the block
|
||||||
|
|
||||||
bool has_had_case; //if we are a switch, whether we've encountered a case
|
bool has_had_case; //if we are a switch, whether we've encountered a case
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -2922,7 +2923,7 @@ int parser_t::test(const wchar_t *buff, int *block_level, wcstring *out, const w
|
||||||
|
|
||||||
tokenizer_t * const previous_tokenizer=current_tokenizer;
|
tokenizer_t * const previous_tokenizer=current_tokenizer;
|
||||||
const int previous_pos=current_tokenizer_pos;
|
const int previous_pos=current_tokenizer_pos;
|
||||||
|
|
||||||
// These are very nearly stacks, but sometimes we have to inspect non-top elements (e.g. return)
|
// These are very nearly stacks, but sometimes we have to inspect non-top elements (e.g. return)
|
||||||
std::vector<struct block_info_t> block_infos;
|
std::vector<struct block_info_t> block_infos;
|
||||||
int indentation_sum = 0; //sum of indentation in block_infos
|
int indentation_sum = 0; //sum of indentation in block_infos
|
||||||
|
@ -3040,7 +3041,7 @@ int parser_t::test(const wchar_t *buff, int *block_level, wcstring *out, const w
|
||||||
{
|
{
|
||||||
tok_next(&tok);
|
tok_next(&tok);
|
||||||
tok_set_pos(&tok, mark);
|
tok_set_pos(&tok, mark);
|
||||||
|
|
||||||
/* Test that end is not used when not inside any block */
|
/* Test that end is not used when not inside any block */
|
||||||
if (block_infos.empty())
|
if (block_infos.empty())
|
||||||
{
|
{
|
||||||
|
@ -3060,7 +3061,7 @@ int parser_t::test(const wchar_t *buff, int *block_level, wcstring *out, const w
|
||||||
{
|
{
|
||||||
indentation_sum -= block_infos.back().indentation;
|
indentation_sum -= block_infos.back().indentation;
|
||||||
block_infos.pop_back();
|
block_infos.pop_back();
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -10,7 +10,7 @@
|
||||||
#include "exec.h"
|
#include "exec.h"
|
||||||
|
|
||||||
#ifndef JOIN_THREADS_BEFORE_FORK
|
#ifndef JOIN_THREADS_BEFORE_FORK
|
||||||
#define JOIN_THREADS_BEFORE_FORK 0
|
#define JOIN_THREADS_BEFORE_FORK 0
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/** The number of times to try to call fork() before giving up */
|
/** The number of times to try to call fork() before giving up */
|
||||||
|
|
2
proc.cpp
2
proc.cpp
|
@ -328,7 +328,7 @@ void job_set_flag(job_t *j, unsigned int flag, int set)
|
||||||
|
|
||||||
int job_get_flag(const job_t *j, unsigned int flag)
|
int job_get_flag(const job_t *j, unsigned int flag)
|
||||||
{
|
{
|
||||||
return !! (j->flags & flag);
|
return !!(j->flags & flag);
|
||||||
}
|
}
|
||||||
|
|
||||||
int job_signal(job_t *j, int signal)
|
int job_signal(job_t *j, int signal)
|
||||||
|
|
18
reader.cpp
18
reader.cpp
|
@ -692,7 +692,7 @@ void reader_write_title()
|
||||||
wcstring_list_t lst;
|
wcstring_list_t lst;
|
||||||
|
|
||||||
proc_push_interactive(0);
|
proc_push_interactive(0);
|
||||||
if (exec_subshell(title, lst) != -1)
|
if (exec_subshell(title, lst, false /* do not apply exit status */) != -1)
|
||||||
{
|
{
|
||||||
size_t i;
|
size_t i;
|
||||||
if (lst.size() > 0)
|
if (lst.size() > 0)
|
||||||
|
@ -718,6 +718,9 @@ static void exec_prompt()
|
||||||
data->left_prompt_buff.clear();
|
data->left_prompt_buff.clear();
|
||||||
data->right_prompt_buff.clear();
|
data->right_prompt_buff.clear();
|
||||||
|
|
||||||
|
/* Do not allow the exit status of the prompts to leak through */
|
||||||
|
const bool apply_exit_status = false;
|
||||||
|
|
||||||
/* If we have any prompts, they must be run non-interactively */
|
/* If we have any prompts, they must be run non-interactively */
|
||||||
if (data->left_prompt.size() || data->right_prompt.size())
|
if (data->left_prompt.size() || data->right_prompt.size())
|
||||||
{
|
{
|
||||||
|
@ -726,9 +729,9 @@ static void exec_prompt()
|
||||||
if (! data->left_prompt.empty())
|
if (! data->left_prompt.empty())
|
||||||
{
|
{
|
||||||
wcstring_list_t prompt_list;
|
wcstring_list_t prompt_list;
|
||||||
// status is ignored
|
if (exec_subshell(data->left_prompt, prompt_list, apply_exit_status))
|
||||||
if (exec_subshell(data->left_prompt, prompt_list))
|
|
||||||
{
|
{
|
||||||
|
// returned status value is ignored
|
||||||
}
|
}
|
||||||
for (size_t i = 0; i < prompt_list.size(); i++)
|
for (size_t i = 0; i < prompt_list.size(); i++)
|
||||||
{
|
{
|
||||||
|
@ -741,8 +744,9 @@ static void exec_prompt()
|
||||||
{
|
{
|
||||||
wcstring_list_t prompt_list;
|
wcstring_list_t prompt_list;
|
||||||
// status is ignored
|
// status is ignored
|
||||||
if (exec_subshell(data->right_prompt, prompt_list))
|
if (exec_subshell(data->right_prompt, prompt_list, apply_exit_status))
|
||||||
{
|
{
|
||||||
|
// returned status value is ignored
|
||||||
}
|
}
|
||||||
for (size_t i = 0; i < prompt_list.size(); i++)
|
for (size_t i = 0; i < prompt_list.size(); i++)
|
||||||
{
|
{
|
||||||
|
@ -1048,10 +1052,10 @@ static void run_pager(const wcstring &prefix, int is_quoted, const std::vector<c
|
||||||
wcstring msg;
|
wcstring msg;
|
||||||
wcstring prefix_esc;
|
wcstring prefix_esc;
|
||||||
char *foo;
|
char *foo;
|
||||||
|
|
||||||
shared_ptr<io_buffer_t> in(io_buffer_t::create(true));
|
shared_ptr<io_buffer_t> in(io_buffer_t::create(true));
|
||||||
shared_ptr<io_buffer_t> out(io_buffer_t::create(false));
|
shared_ptr<io_buffer_t> out(io_buffer_t::create(false));
|
||||||
|
|
||||||
// The above may fail e.g. if we have too many open fds
|
// The above may fail e.g. if we have too many open fds
|
||||||
if (in.get() == NULL || out.get() == NULL)
|
if (in.get() == NULL || out.get() == NULL)
|
||||||
return;
|
return;
|
||||||
|
@ -1140,7 +1144,7 @@ static void run_pager(const wcstring &prefix, int is_quoted, const std::vector<c
|
||||||
free(foo);
|
free(foo);
|
||||||
|
|
||||||
out->fd = 4;
|
out->fd = 4;
|
||||||
|
|
||||||
term_donate();
|
term_donate();
|
||||||
parser_t &parser = parser_t::principal_parser();
|
parser_t &parser = parser_t::principal_parser();
|
||||||
io_chain_t io_chain;
|
io_chain_t io_chain;
|
||||||
|
|
|
@ -1373,20 +1373,20 @@ void s_reset(screen_t *s, screen_reset_mode_t mode)
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// draw in "bright black" (gray)
|
// draw in "bright black" (gray)
|
||||||
abandon_line_string.append(L"\x1b[0m" //bright
|
abandon_line_string.append(L"\x1b[0m" //bright
|
||||||
L"\x1b[30;1m"); //black
|
L"\x1b[30;1m"); //black
|
||||||
}
|
}
|
||||||
abandon_line_string.push_back(omitted_newline_char);
|
abandon_line_string.push_back(omitted_newline_char);
|
||||||
abandon_line_string.append(L"\x1b[0m"); //normal text ANSI escape sequence
|
abandon_line_string.append(L"\x1b[0m"); //normal text ANSI escape sequence
|
||||||
abandon_line_string.append(screen_width - non_space_width, L' ');
|
abandon_line_string.append(screen_width - non_space_width, L' ');
|
||||||
|
|
||||||
}
|
}
|
||||||
abandon_line_string.push_back(L'\r');
|
abandon_line_string.push_back(L'\r');
|
||||||
// now we are certainly on a new line. But we may have dropped the omitted newline char on it. So append enough spaces to overwrite the omitted newline char, and then
|
// now we are certainly on a new line. But we may have dropped the omitted newline char on it. So append enough spaces to overwrite the omitted newline char, and then
|
||||||
abandon_line_string.append(non_space_width, L' ');
|
abandon_line_string.append(non_space_width, L' ');
|
||||||
abandon_line_string.push_back(L'\r');
|
abandon_line_string.push_back(L'\r');
|
||||||
|
|
||||||
const std::string narrow_abandon_line_string = wcs2string(abandon_line_string);
|
const std::string narrow_abandon_line_string = wcs2string(abandon_line_string);
|
||||||
write_loop(STDOUT_FILENO, narrow_abandon_line_string.c_str(), narrow_abandon_line_string.size());
|
write_loop(STDOUT_FILENO, narrow_abandon_line_string.c_str(), narrow_abandon_line_string.size());
|
||||||
s->actual.cursor.x = 0;
|
s->actual.cursor.x = 0;
|
||||||
|
|
|
@ -160,3 +160,20 @@ else
|
||||||
end;
|
end;
|
||||||
|
|
||||||
set -U -e baz
|
set -U -e baz
|
||||||
|
|
||||||
|
echo "Verify subcommand statuses"
|
||||||
|
echo (false) $status (true) $status (false) $status
|
||||||
|
|
||||||
|
echo "Verify that set passes through exit status, except when passed -n or -q or -e"
|
||||||
|
false ; set foo bar ; echo 1 $status # passthrough
|
||||||
|
true ; set foo bar ; echo 2 $status # passthrough
|
||||||
|
false ; set -q foo ; echo 3 $status # no passthrough
|
||||||
|
true ; set -q foo ; echo 4 $status # no passthrough
|
||||||
|
false ; set -n > /dev/null ; echo 5 $status # no passthrough
|
||||||
|
false ; set -e foo ; echo 6 $status # no passthrough
|
||||||
|
true ; set -e foo ; echo 7 $status # no passthrough
|
||||||
|
false ; set -h > /dev/null ; echo 8 $status # no passthrough
|
||||||
|
true ; set -NOT_AN_OPTION 2> /dev/null ; echo 9 $status # no passthrough
|
||||||
|
false ; set foo (echo A; true) ; echo 10 $status $foo
|
||||||
|
true ; set foo (echo B; false) ; echo 11 $status $foo
|
||||||
|
true
|
||||||
|
|
|
@ -20,3 +20,17 @@ Test 19 pass
|
||||||
Test 20 pass
|
Test 20 pass
|
||||||
Test 21 pass
|
Test 21 pass
|
||||||
Test 22 pass
|
Test 22 pass
|
||||||
|
Verify subcommand statuses
|
||||||
|
1 0 1
|
||||||
|
Verify that set passes through exit status, except when passed -n or -q or -e
|
||||||
|
1 1
|
||||||
|
2 0
|
||||||
|
3 0
|
||||||
|
4 0
|
||||||
|
5 0
|
||||||
|
6 0
|
||||||
|
7 1
|
||||||
|
8 0
|
||||||
|
9 1
|
||||||
|
10 0 A
|
||||||
|
11 1 B
|
||||||
|
|
|
@ -336,7 +336,7 @@ static wcstring complete_get_desc_suffix_internal(const wcstring &suff)
|
||||||
wcstring_list_t lst;
|
wcstring_list_t lst;
|
||||||
wcstring desc;
|
wcstring desc;
|
||||||
|
|
||||||
if (exec_subshell(cmd, lst) != -1)
|
if (exec_subshell(cmd, lst, false /* do not apply exit status */) != -1)
|
||||||
{
|
{
|
||||||
if (lst.size()>0)
|
if (lst.size()>0)
|
||||||
{
|
{
|
||||||
|
|
Loading…
Reference in New Issue
Block a user