mirror of
https://github.com/fish-shell/fish-shell
synced 2025-01-13 05:28:49 +00:00
Reintroduce IO transmorgrification (yuck) to fix problems with fish_config and complicated IO redirections
This commit is contained in:
parent
04ea680e9a
commit
f5d4e3f94c
2 changed files with 133 additions and 10 deletions
135
exec.cpp
135
exec.cpp
|
@ -319,6 +319,123 @@ static int has_fd( const io_chain_t &d, int fd )
|
|||
return io_chain_get( d, fd ) != NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
Free a transmogrified io chain. Only the chain itself and resources
|
||||
used by a transmogrified IO_FILE redirection are freed, since the
|
||||
original chain may still be needed.
|
||||
*/
|
||||
static void io_cleanup_chains(io_chain_t &chains, const std::vector<int> &opened_fds) {
|
||||
/* Close all the fds */
|
||||
for (size_t idx = 0; idx < opened_fds.size(); idx++) {
|
||||
close(opened_fds.at(idx));
|
||||
}
|
||||
|
||||
/* Then delete all of the redirections we made */
|
||||
chains.destroy();
|
||||
}
|
||||
|
||||
/**
|
||||
Make a copy of the specified io redirection chain, but change file
|
||||
redirection into fd redirection. This makes the redirection chain
|
||||
suitable for use as block-level io, since the file won't be
|
||||
repeatedly reopened for every command in the block, which would
|
||||
reset the cursor position.
|
||||
|
||||
\return the transmogrified chain on sucess, or 0 on failiure
|
||||
*/
|
||||
static bool io_transmogrify(const io_chain_t &in_chain, io_chain_t &out_chain, std::vector<int> &out_opened_fds) {
|
||||
ASSERT_IS_MAIN_THREAD();
|
||||
assert(out_chain.empty());
|
||||
|
||||
/* Just to be clear what we do for an empty chain */
|
||||
if (in_chain.empty()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool success = true;
|
||||
|
||||
/* Make our chain of redirections */
|
||||
io_chain_t result_chain;
|
||||
|
||||
/* In the event we can't finish transmorgrifying, we'll have to close all the files we opened. */
|
||||
std::vector<int> opened_fds;
|
||||
|
||||
for (size_t idx = 0; idx < in_chain.size(); idx++)
|
||||
{
|
||||
io_data_t *in = in_chain.at(idx);
|
||||
io_data_t *out = NULL; //gets allocated via new
|
||||
|
||||
switch( in->io_mode )
|
||||
{
|
||||
default:
|
||||
/* Unknown type, should never happen */
|
||||
fprintf(stderr, "Unknown io_mode %ld\n", (long)in->io_mode);
|
||||
abort();
|
||||
break;
|
||||
|
||||
/*
|
||||
These redirections don't need transmogrification. They can be passed through.
|
||||
*/
|
||||
case IO_PIPE:
|
||||
case IO_FD:
|
||||
case IO_BUFFER:
|
||||
case IO_CLOSE:
|
||||
{
|
||||
out = new io_data_t(*in);
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
Transmogrify file redirections
|
||||
*/
|
||||
case IO_FILE:
|
||||
{
|
||||
out = new io_data_t();
|
||||
out->fd = in->fd;
|
||||
out->io_mode = IO_FD;
|
||||
out->param2.close_old = 1;
|
||||
|
||||
int fd;
|
||||
if ((fd=open(in->filename_cstr, in->param2.flags, OPEN_MASK))==-1)
|
||||
{
|
||||
debug( 1,
|
||||
FILE_ERROR,
|
||||
in->filename_cstr );
|
||||
|
||||
wperror( L"open" );
|
||||
success = false;
|
||||
break;
|
||||
}
|
||||
|
||||
opened_fds.push_back(fd);
|
||||
out->param1.old_fd = fd;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Record this IO redirection even if we failed (so we can free it) */
|
||||
result_chain.push_back(out);
|
||||
|
||||
/* But don't go any further if we failed */
|
||||
if (! success) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Now either return success, or clean up */
|
||||
if (success) {
|
||||
/* Yay */
|
||||
out_chain.swap(result_chain);
|
||||
out_opened_fds.swap(opened_fds);
|
||||
} else {
|
||||
/* No dice - clean up */
|
||||
io_cleanup_chains(result_chain, opened_fds);
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
Morph an io redirection chain into redirections suitable for
|
||||
passing to eval, call eval, and clean up morphed redirections.
|
||||
|
@ -333,15 +450,29 @@ static void internal_exec_helper( parser_t &parser,
|
|||
enum block_type_t block_type,
|
||||
io_chain_t &ios )
|
||||
{
|
||||
io_chain_t morphed_chain;
|
||||
std::vector<int> opened_fds;
|
||||
bool transmorgrified = io_transmogrify(ios, morphed_chain, opened_fds);
|
||||
|
||||
int is_block_old=is_block;
|
||||
is_block=1;
|
||||
|
||||
|
||||
/*
|
||||
Did the transmogrification fail - if so, set error status and return
|
||||
*/
|
||||
if( ! transmorgrified )
|
||||
{
|
||||
proc_set_last_status( STATUS_EXEC_FAIL );
|
||||
return;
|
||||
}
|
||||
|
||||
signal_unblock();
|
||||
|
||||
parser.eval( def, ios, block_type );
|
||||
parser.eval( def, morphed_chain, block_type );
|
||||
|
||||
signal_block();
|
||||
|
||||
io_cleanup_chains(morphed_chain, opened_fds);
|
||||
job_reap( 0 );
|
||||
is_block=is_block_old;
|
||||
}
|
||||
|
|
|
@ -169,10 +169,6 @@ static int handle_child_io( io_chain_t &io_chain )
|
|||
io_data_t *io = io_chain.at(idx);
|
||||
int tmp;
|
||||
|
||||
/* If this is not the last IO redirection for this fd, then skip it. This comes about because of the funky way in which we list IO redirections: every process in a job gets the input and output redirections, even internal ones. For example, in 'cat < foo | cat | cat > bar', the middle cat sees both < foo and > bar. It also gets pipes for its fd 0 and 1, which appear after in the list. */
|
||||
if (io != io_chain.get_io_for_fd(io->fd))
|
||||
continue;
|
||||
|
||||
if( io->io_mode == IO_FD && io->fd == io->param1.old_fd )
|
||||
{
|
||||
continue;
|
||||
|
@ -444,10 +440,6 @@ bool fork_actions_make_spawn_properties(posix_spawnattr_t *attr, posix_spawn_fil
|
|||
{
|
||||
const io_data_t *io = j->io.at(idx);
|
||||
|
||||
/* If this is not the last IO redirection for this fd, then skip it. This comes about because of the funky way in which we list IO redirections: every process in a job gets the input and output redirections, even internal ones. For example, in 'cat < foo | cat | cat > bar', the middle cat sees both < foo and > bar. It also gets pipes for its fd 0 and 1, which appear after in the list. */
|
||||
if (io != j->io.get_io_for_fd(io->fd))
|
||||
continue;
|
||||
|
||||
if( io->io_mode == IO_FD && io->fd == io->param1.old_fd )
|
||||
{
|
||||
continue;
|
||||
|
|
Loading…
Reference in a new issue