mirror of
https://github.com/fish-shell/fish-shell
synced 2025-01-13 13:39:02 +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;
|
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
|
Morph an io redirection chain into redirections suitable for
|
||||||
passing to eval, call eval, and clean up morphed redirections.
|
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,
|
enum block_type_t block_type,
|
||||||
io_chain_t &ios )
|
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;
|
int is_block_old=is_block;
|
||||||
is_block=1;
|
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();
|
signal_unblock();
|
||||||
|
|
||||||
parser.eval( def, ios, block_type );
|
parser.eval( def, morphed_chain, block_type );
|
||||||
|
|
||||||
signal_block();
|
signal_block();
|
||||||
|
|
||||||
|
io_cleanup_chains(morphed_chain, opened_fds);
|
||||||
job_reap( 0 );
|
job_reap( 0 );
|
||||||
is_block=is_block_old;
|
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);
|
io_data_t *io = io_chain.at(idx);
|
||||||
int tmp;
|
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 )
|
if( io->io_mode == IO_FD && io->fd == io->param1.old_fd )
|
||||||
{
|
{
|
||||||
continue;
|
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);
|
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 )
|
if( io->io_mode == IO_FD && io->fd == io->param1.old_fd )
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
|
|
Loading…
Reference in a new issue