aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorGravatar ridiculousfish <corydoras@ridiculousfish.com>2015-09-21 11:24:49 -0700
committerGravatar ridiculousfish <corydoras@ridiculousfish.com>2015-09-22 18:08:00 -0700
commitc1bd3b5824027f23b41bbac77c5e28f856c5dc18 (patch)
treef406ad2f859df99f65c059f8c4019d5a2db1e459
parentfb615843b329b4d3c303b9cebe2af134a52b5bfd (diff)
Eliminate global variables associated with builtin IO
This change eliminates global variables like stdout_buffer. Instead we wrap up the IO information into a new struct io_streams_t, and thread that through every builtin. This makes the intent clearer, gives us a place to hang new IO data, and eliminates the ugly global state management like builtin_push_io.
-rw-r--r--src/builtin.cpp760
-rw-r--r--src/builtin.h33
-rw-r--r--src/builtin_commandline.cpp74
-rw-r--r--src/builtin_complete.cpp44
-rw-r--r--src/builtin_jobs.cpp59
-rw-r--r--src/builtin_printf.cpp20
-rw-r--r--src/builtin_set.cpp113
-rw-r--r--src/builtin_set_color.cpp26
-rw-r--r--src/builtin_string.cpp261
-rw-r--r--src/builtin_test.cpp8
-rw-r--r--src/builtin_ulimit.cpp54
-rw-r--r--src/common.cpp8
-rw-r--r--src/common.h6
-rw-r--r--src/complete.cpp4
-rw-r--r--src/complete.h6
-rw-r--r--src/exec.cpp48
-rw-r--r--src/fish_tests.cpp25
-rw-r--r--src/input.cpp2
-rw-r--r--src/input.h2
-rw-r--r--src/io.h87
-rw-r--r--src/parse_execution.cpp3
-rw-r--r--src/parser.cpp35
-rw-r--r--src/parser.h7
23 files changed, 797 insertions, 888 deletions
diff --git a/src/builtin.cpp b/src/builtin.cpp
index e4f14160..a3e5eedb 100644
--- a/src/builtin.cpp
+++ b/src/builtin.cpp
@@ -92,7 +92,7 @@ struct builtin_data_t
/**
Function pointer tothe builtin implementation
*/
- int (*func)(parser_t &parser, wchar_t **argv);
+ int (*func)(parser_t &parser, io_streams_t &streams, wchar_t **argv);
/**
Description of what the builtin does
*/
@@ -112,54 +112,6 @@ bool builtin_data_t::operator<(const builtin_data_t *other) const
return wcscmp(this->name, other->name) < 0;
}
-int builtin_out_redirect;
-int builtin_err_redirect;
-
-/* Buffers for storing the output of builtin functions */
-wcstring stdout_buffer, stderr_buffer;
-
-const wcstring &get_stdout_buffer()
-{
- ASSERT_IS_MAIN_THREAD();
- return stdout_buffer;
-}
-
-const wcstring &get_stderr_buffer()
-{
- ASSERT_IS_MAIN_THREAD();
- return stderr_buffer;
-}
-
-void builtin_show_error(const wcstring &err)
-{
- ASSERT_IS_MAIN_THREAD();
- stderr_buffer.append(err);
-}
-
-/**
- Stack containing builtin I/O for recursive builtin calls.
-*/
-struct io_stack_elem_t
-{
- int in;
- wcstring out;
- wcstring err;
-};
-static std::stack<io_stack_elem_t, std::vector<io_stack_elem_t> > io_stack;
-
-/**
- The file from which builtin functions should attempt to read, use
- instead of stdin.
-*/
-int builtin_stdin;
-
-/**
- The underlying IO redirections behind the current builtin. This
- should normally not be used - sb_out and friends are already
- configured to handle everything.
-*/
-static const io_chain_t *real_io;
-
/**
Counts the number of non null pointers in the specified array
*/
@@ -175,23 +127,23 @@ int builtin_count_args(const wchar_t * const * argv)
/**
This function works like wperror, but it prints its result into
- the sb_err string instead of to stderr. Used by the builtin
+ the streams.err string instead of to stderr. Used by the builtin
commands.
*/
-static void builtin_wperror(const wchar_t *s)
+static void builtin_wperror(const wchar_t *s, io_streams_t &streams)
{
- if (s != 0)
+ char *err = strerror(errno);
+ if (s != NULL)
{
- stderr_buffer.append(s);
- stderr_buffer.append(L": ");
+ streams.err.append(s);
+ streams.err.append(L": ");
}
- char *err = strerror(errno);
- if (err)
+ if (err != NULL)
{
const wcstring werr = str2wcstring(err);
- stderr_buffer.append(werr);
- stderr_buffer.push_back(L'\n');
+ streams.err.append(werr);
+ streams.err.push_back(L'\n');
}
}
@@ -208,7 +160,7 @@ static int count_char(const wchar_t *str, wchar_t c)
return res;
}
-wcstring builtin_help_get(parser_t &parser, const wchar_t *name)
+wcstring builtin_help_get(parser_t &parser, io_streams_t &streams, const wchar_t *name)
{
/* This won't ever work if no_exec is set */
if (no_exec)
@@ -218,7 +170,7 @@ wcstring builtin_help_get(parser_t &parser, const wchar_t *name)
wcstring out;
const wcstring name_esc = escape_string(name, 1);
wcstring cmd = format_string(L"__fish_print_help %ls", name_esc.c_str());
- if (!builtin_out_redirect && isatty(1))
+ if (!streams.out_is_redirected && isatty(STDOUT_FILENO))
{
// since we're using a subshell, __fish_print_help can't tell we're in
// a terminal. Tell it ourselves.
@@ -247,70 +199,71 @@ wcstring builtin_help_get(parser_t &parser, const wchar_t *name)
*/
-void builtin_print_help(parser_t &parser, const wchar_t *cmd, wcstring &b)
+void builtin_print_help(parser_t &parser, io_streams_t &streams, const wchar_t *cmd, output_stream_t &b)
{
- if (&b == &stderr_buffer)
+ bool is_stderr = (&b == &streams.err);
+ if (is_stderr)
{
- stderr_buffer.append(parser.current_line());
+ b.append(parser.current_line());
}
-
- const wcstring h = builtin_help_get(parser, cmd);
-
+
+ const wcstring h = builtin_help_get(parser, streams, cmd);
+
if (!h.size())
return;
-
+
wchar_t *str = wcsdup(h.c_str());
if (str)
{
bool is_short = false;
- if (&b == &stderr_buffer)
+ if (is_stderr)
{
-
+
/*
- Interactive mode help to screen - only print synopsis if
- the rest won't fit
- */
-
+ Interactive mode help to screen - only print synopsis if
+ the rest won't fit
+ */
+
int screen_height, lines;
-
+
screen_height = common_get_height();
lines = count_char(str, L'\n');
- if (!get_is_interactive() || (lines > 2*screen_height/3))
+ if (! get_is_interactive() || (lines > 2*screen_height/3))
{
wchar_t *pos;
int cut=0;
int i;
-
+
is_short = true;
-
+
/*
- First move down 4 lines
- */
-
+ First move down 4 lines
+ */
+
pos = str;
for (i=0; (i<4) && pos && *pos; i++)
{
pos = wcschr(pos+1, L'\n');
}
-
+
if (pos && *pos)
{
-
+
/*
- Then find the next empty line
- */
+ Then find the next empty line
+ */
for (; *pos; pos++)
{
if (*pos == L'\n')
{
wchar_t *pos2;
int is_empty = 1;
-
+
for (pos2 = pos+1; *pos2; pos2++)
{
if (*pos2 == L'\n')
break;
-
+
if (*pos2 != L'\t' && *pos2 !=L' ')
{
is_empty = 0;
@@ -320,8 +273,8 @@ void builtin_print_help(parser_t &parser, const wchar_t *cmd, wcstring &b)
if (is_empty)
{
/*
- And cut it
- */
+ And cut it
+ */
*(pos2+1)=L'\0';
cut = 1;
break;
@@ -329,46 +282,47 @@ void builtin_print_help(parser_t &parser, const wchar_t *cmd, wcstring &b)
}
}
}
-
+
/*
- We did not find a good place to cut message to
- shorten it - so we make sure we don't print
- anything.
- */
+ We did not find a good place to cut message to
+ shorten it - so we make sure we don't print
+ anything.
+ */
if (!cut)
{
*str = 0;
}
-
+
}
}
-
+
b.append(str);
if (is_short)
{
- append_format(b, _(L"%ls: Type 'help %ls' for related documentation\n\n"), cmd, cmd);
+ b.append_format(_(L"%ls: Type 'help %ls' for related documentation\n\n"), cmd, cmd);
}
-
+
free(str);
}
}
+
/**
Perform error reporting for encounter with unknown option
*/
-static void builtin_unknown_option(parser_t &parser, const wchar_t *cmd, const wchar_t *opt)
+static void builtin_unknown_option(parser_t &parser, io_streams_t &streams, const wchar_t *cmd, const wchar_t *opt)
{
- append_format(stderr_buffer, BUILTIN_ERR_UNKNOWN, cmd, opt);
- builtin_print_help(parser, cmd, stderr_buffer);
+ streams.err.append_format(BUILTIN_ERR_UNKNOWN, cmd, opt);
+ builtin_print_help(parser, streams, cmd, streams.err);
}
/**
Perform error reporting for encounter with missing argument
*/
-static void builtin_missing_argument(parser_t &parser, const wchar_t *cmd, const wchar_t *opt)
+static void builtin_missing_argument(parser_t &parser, io_streams_t &streams, const wchar_t *cmd, const wchar_t *opt)
{
- append_format(stderr_buffer, BUILTIN_ERR_MISSING, cmd, opt);
- builtin_print_help(parser, cmd, stderr_buffer);
+ streams.err.append_format(BUILTIN_ERR_MISSING, cmd, opt);
+ builtin_print_help(parser, streams, cmd, streams.err);
}
/*
@@ -401,16 +355,16 @@ static void builtin_missing_argument(parser_t &parser, const wchar_t *cmd, const
#include "builtin_printf.cpp"
/* builtin_test lives in builtin_test.cpp */
-int builtin_test(parser_t &parser, wchar_t **argv);
+int builtin_test(parser_t &parser, io_streams_t &streams, wchar_t **argv);
/* builtin_string lives in builtin_string.cpp */
-int builtin_string(parser_t &parser, wchar_t **argv);
+int builtin_string(parser_t &parser, io_streams_t &streams, wchar_t **argv);
/**
List a single key binding.
Returns false if no binding with that sequence and mode exists.
*/
-static bool builtin_bind_list_one(const wcstring& seq, const wcstring& bind_mode)
+static bool builtin_bind_list_one(const wcstring &seq, const wcstring &bind_mode, io_streams_t &streams)
{
std::vector<wcstring> ecmds;
wcstring sets_mode;
@@ -420,20 +374,20 @@ static bool builtin_bind_list_one(const wcstring& seq, const wcstring& bind_mode
return false;
}
- stdout_buffer.append(L"bind");
+ streams.out.append(L"bind");
// Append the mode flags if applicable
if (bind_mode != DEFAULT_BIND_MODE)
{
const wcstring emode = escape_string(bind_mode, ESCAPE_ALL);
- stdout_buffer.append(L" -M ");
- stdout_buffer.append(emode);
+ streams.out.append(L" -M ");
+ streams.out.append(emode);
}
if (sets_mode != bind_mode)
{
const wcstring esets_mode = escape_string(sets_mode, ESCAPE_ALL);
- stdout_buffer.append(L" -m ");
- stdout_buffer.append(esets_mode);
+ streams.out.append(L" -m ");
+ streams.out.append(esets_mode);
}
// Append the name
@@ -441,13 +395,13 @@ static bool builtin_bind_list_one(const wcstring& seq, const wcstring& bind_mode
if (input_terminfo_get_name(seq, &tname))
{
// Note that we show -k here because we have an input key name
- append_format(stdout_buffer, L" -k %ls", tname.c_str());
+ streams.out.append_format( L" -k %ls", tname.c_str());
}
else
{
// No key name, so no -k; we show the escape sequence directly
const wcstring eseq = escape_string(seq, ESCAPE_ALL);
- append_format(stdout_buffer, L" %ls", eseq.c_str());
+ streams.out.append_format( L" %ls", eseq.c_str());
}
// Now show the list of commands
@@ -455,10 +409,10 @@ static bool builtin_bind_list_one(const wcstring& seq, const wcstring& bind_mode
{
const wcstring &ecmd = ecmds.at(i);
const wcstring escaped_ecmd = escape_string(ecmd, ESCAPE_ALL);
- stdout_buffer.push_back(' ');
- stdout_buffer.append(escaped_ecmd);
+ streams.out.push_back(' ');
+ streams.out.append(escaped_ecmd);
}
- stdout_buffer.push_back(L'\n');
+ streams.out.push_back(L'\n');
return true;
}
@@ -466,7 +420,7 @@ static bool builtin_bind_list_one(const wcstring& seq, const wcstring& bind_mode
/**
List all current key bindings
*/
-static void builtin_bind_list(const wchar_t *bind_mode)
+static void builtin_bind_list(const wchar_t *bind_mode, io_streams_t &streams)
{
const std::vector<input_mapping_name_t> lst = input_mapping_get_names();
@@ -479,7 +433,7 @@ static void builtin_bind_list(const wchar_t *bind_mode)
continue;
}
- builtin_bind_list_one(it->seq, it->mode);
+ builtin_bind_list_one(it->seq, it->mode, streams);
}
}
@@ -490,14 +444,14 @@ static void builtin_bind_list(const wchar_t *bind_mode)
printed. If not set, only ones that are defined for this terminal
are printed.
*/
-static void builtin_bind_key_names(int all)
+static void builtin_bind_key_names(int all, io_streams_t &streams)
{
const wcstring_list_t names = input_terminfo_get_names(!all);
for (size_t i=0; i<names.size(); i++)
{
const wcstring &name = names.at(i);
- append_format(stdout_buffer, L"%ls\n", name.c_str());
+ streams.out.append_format( L"%ls\n", name.c_str());
}
}
@@ -505,19 +459,19 @@ static void builtin_bind_key_names(int all)
Print all the special key binding functions to string buffer used for standard output.
*/
-static void builtin_bind_function_names()
+static void builtin_bind_function_names(io_streams_t &streams)
{
wcstring_list_t names = input_function_get_names();
for (size_t i=0; i<names.size(); i++)
{
const wchar_t *seq = names.at(i).c_str();
- append_format(stdout_buffer, L"%ls\n", seq);
+ streams.out.append_format( L"%ls\n", seq);
}
}
// Wraps input_terminfo_get_sequence(), appending the correct error messages as needed.
-static int get_terminfo_sequence(const wchar_t *seq, wcstring *out_seq)
+static int get_terminfo_sequence(const wchar_t *seq, wcstring *out_seq, io_streams_t &streams)
{
if (input_terminfo_get_sequence(seq, out_seq))
{
@@ -528,19 +482,19 @@ static int get_terminfo_sequence(const wchar_t *seq, wcstring *out_seq)
{
case ENOENT:
{
- append_format(stderr_buffer, _(L"%ls: No key with name '%ls' found\n"), L"bind", eseq.c_str());
+ streams.err.append_format(_(L"%ls: No key with name '%ls' found\n"), L"bind", eseq.c_str());
break;
}
case EILSEQ:
{
- append_format(stderr_buffer, _(L"%ls: Key with name '%ls' does not have any mapping\n"), L"bind", eseq.c_str());
+ streams.err.append_format(_(L"%ls: Key with name '%ls' does not have any mapping\n"), L"bind", eseq.c_str());
break;
}
default:
{
- append_format(stderr_buffer, _(L"%ls: Unknown error trying to bind to key named '%ls'\n"), L"bind", eseq.c_str());
+ streams.err.append_format(_(L"%ls: Unknown error trying to bind to key named '%ls'\n"), L"bind", eseq.c_str());
break;
}
}
@@ -550,14 +504,15 @@ static int get_terminfo_sequence(const wchar_t *seq, wcstring *out_seq)
/**
Add specified key binding.
*/
-static int builtin_bind_add(const wchar_t *seq, const wchar_t **cmds, size_t cmds_len,
- const wchar_t *mode, const wchar_t *sets_mode, int terminfo)
+static int builtin_bind_add(const wchar_t *seq, const wchar_t * const *cmds, size_t cmds_len,
+ const wchar_t *mode, const wchar_t *sets_mode, int terminfo,
+ io_streams_t &streams)
{
if (terminfo)
{
wcstring seq2;
- if (get_terminfo_sequence(seq, &seq2))
+ if (get_terminfo_sequence(seq, &seq2, streams))
{
input_mapping_add(seq2.c_str(), cmds, cmds_len, mode, sets_mode);
}
@@ -583,7 +538,7 @@ static int builtin_bind_add(const wchar_t *seq, const wchar_t **cmds, size_t cmd
\param all if specified, _all_ key bindings will be erased
\param mode if specified, only bindings from that mode will be erased. If not given and \c all is \c false, \c DEFAULT_BIND_MODE will be used.
*/
-static int builtin_bind_erase(wchar_t **seq, int all, const wchar_t *mode, int use_terminfo)
+static int builtin_bind_erase(wchar_t **seq, int all, const wchar_t *mode, int use_terminfo, io_streams_t &streams)
{
if (all)
{
@@ -611,7 +566,7 @@ static int builtin_bind_erase(wchar_t **seq, int all, const wchar_t *mode, int u
if (use_terminfo)
{
wcstring seq2;
- if (get_terminfo_sequence(*seq++, &seq2))
+ if (get_terminfo_sequence(*seq++, &seq2, streams))
{
input_mapping_erase(seq2, mode);
}
@@ -634,7 +589,7 @@ static int builtin_bind_erase(wchar_t **seq, int all, const wchar_t *mode, int u
/**
The bind builtin, used for setting character sequences
*/
-static int builtin_bind(parser_t &parser, wchar_t **argv)
+static int builtin_bind(parser_t &parser, io_streams_t &streams, wchar_t **argv)
{
wgetopter_t w;
enum
@@ -689,11 +644,10 @@ static int builtin_bind(parser_t &parser, wchar_t **argv)
case 0:
if (long_options[opt_index].flag != 0)
break;
- append_format(stderr_buffer,
- BUILTIN_ERR_UNKNOWN,
+ streams.err.append_format(BUILTIN_ERR_UNKNOWN,
argv[0],
long_options[opt_index].name);
- builtin_print_help(parser, argv[0], stderr_buffer);
+ builtin_print_help(parser, streams, argv[0], streams.err);
return STATUS_BUILTIN_ERROR;
@@ -706,7 +660,7 @@ static int builtin_bind(parser_t &parser, wchar_t **argv)
break;
case 'h':
- builtin_print_help(parser, argv[0], stdout_buffer);
+ builtin_print_help(parser, streams, argv[0], streams.out);
return STATUS_BUILTIN_OK;
case 'k':
@@ -732,7 +686,7 @@ static int builtin_bind(parser_t &parser, wchar_t **argv)
break;
case '?':
- builtin_unknown_option(parser, argv[0], argv[w.woptind-1]);
+ builtin_unknown_option(parser, streams, argv[0], argv[w.woptind-1]);
return STATUS_BUILTIN_ERROR;
@@ -752,7 +706,7 @@ static int builtin_bind(parser_t &parser, wchar_t **argv)
case BIND_ERASE:
{
- if (builtin_bind_erase(&argv[w.woptind], all, bind_mode_given ? bind_mode : NULL, use_terminfo))
+ if (builtin_bind_erase(&argv[w.woptind], all, bind_mode_given ? bind_mode : NULL, use_terminfo, streams))
{
res = STATUS_BUILTIN_ERROR;
}
@@ -765,7 +719,7 @@ static int builtin_bind(parser_t &parser, wchar_t **argv)
{
case 0:
{
- builtin_bind_list(bind_mode_given ? bind_mode : NULL);
+ builtin_bind_list(bind_mode_given ? bind_mode : NULL, streams);
break;
}
@@ -774,7 +728,7 @@ static int builtin_bind(parser_t &parser, wchar_t **argv)
wcstring seq;
if (use_terminfo)
{
- if (!get_terminfo_sequence(argv[w.woptind], &seq))
+ if (!get_terminfo_sequence(argv[w.woptind], &seq, streams))
{
res = STATUS_BUILTIN_ERROR;
// get_terminfo_sequence already printed the error
@@ -785,17 +739,17 @@ static int builtin_bind(parser_t &parser, wchar_t **argv)
{
seq = argv[w.woptind];
}
- if (!builtin_bind_list_one(seq, bind_mode))
+ if (!builtin_bind_list_one(seq, bind_mode, streams))
{
res = STATUS_BUILTIN_ERROR;
wcstring eseq = escape_string(argv[w.woptind], 0);
if (use_terminfo)
{
- append_format(stderr_buffer, _(L"%ls: No binding found for key '%ls'\n"), argv[0], eseq.c_str());
+ streams.err.append_format(_(L"%ls: No binding found for key '%ls'\n"), argv[0], eseq.c_str());
}
else
{
- append_format(stderr_buffer, _(L"%ls: No binding found for sequence '%ls'\n"), argv[0], eseq.c_str());
+ streams.err.append_format(_(L"%ls: No binding found for sequence '%ls'\n"), argv[0], eseq.c_str());
}
}
break;
@@ -803,7 +757,7 @@ static int builtin_bind(parser_t &parser, wchar_t **argv)
default:
{
- if (builtin_bind_add(argv[w.woptind], (const wchar_t **)argv + (w.woptind + 1), argc - (w.woptind + 1), bind_mode, sets_bind_mode, use_terminfo))
+ if (builtin_bind_add(argv[w.woptind], argv + (w.woptind + 1), argc - (w.woptind + 1), bind_mode, sets_bind_mode, use_terminfo, streams))
{
res = STATUS_BUILTIN_ERROR;
}
@@ -816,14 +770,14 @@ static int builtin_bind(parser_t &parser, wchar_t **argv)
case BIND_KEY_NAMES:
{
- builtin_bind_key_names(all);
+ builtin_bind_key_names(all, streams);
break;
}
case BIND_FUNCTION_NAMES:
{
- builtin_bind_function_names();
+ builtin_bind_function_names(streams);
break;
}
@@ -831,7 +785,7 @@ static int builtin_bind(parser_t &parser, wchar_t **argv)
default:
{
res = STATUS_BUILTIN_ERROR;
- append_format(stderr_buffer, _(L"%ls: Invalid state\n"), argv[0]);
+ streams.err.append_format(_(L"%ls: Invalid state\n"), argv[0]);
break;
}
}
@@ -843,7 +797,7 @@ static int builtin_bind(parser_t &parser, wchar_t **argv)
/**
The block builtin, used for temporarily blocking events
*/
-static int builtin_block(parser_t &parser, wchar_t **argv)
+static int builtin_block(parser_t &parser, io_streams_t &streams, wchar_t **argv)
{
wgetopter_t w;
enum
@@ -902,15 +856,14 @@ static int builtin_block(parser_t &parser, wchar_t **argv)
case 0:
if (long_options[opt_index].flag != 0)
break;
- append_format(stderr_buffer,
- BUILTIN_ERR_UNKNOWN,
+ streams.err.append_format(BUILTIN_ERR_UNKNOWN,
argv[0],
long_options[opt_index].name);
- builtin_print_help(parser, argv[0], stderr_buffer);
+ builtin_print_help(parser, streams, argv[0], streams.err);
return STATUS_BUILTIN_ERROR;
case 'h':
- builtin_print_help(parser, argv[0], stdout_buffer);
+ builtin_print_help(parser, streams, argv[0], streams.out);
return STATUS_BUILTIN_OK;
case 'g':
@@ -926,7 +879,7 @@ static int builtin_block(parser_t &parser, wchar_t **argv)
break;
case '?':
- builtin_unknown_option(parser, argv[0], argv[w.woptind-1]);
+ builtin_unknown_option(parser, streams, argv[0], argv[w.woptind-1]);
return STATUS_BUILTIN_ERROR;
}
@@ -937,13 +890,13 @@ static int builtin_block(parser_t &parser, wchar_t **argv)
{
if (scope != UNSET)
{
- append_format(stderr_buffer, _(L"%ls: Can not specify scope when removing block\n"), argv[0]);
+ streams.err.append_format(_(L"%ls: Can not specify scope when removing block\n"), argv[0]);
return STATUS_BUILTIN_ERROR;
}
if (parser.global_event_blocks.empty())
{
- append_format(stderr_buffer, _(L"%ls: No blocks defined\n"), argv[0]);
+ streams.err.append_format(_(L"%ls: No blocks defined\n"), argv[0]);
return STATUS_BUILTIN_ERROR;
}
parser.global_event_blocks.pop_front();
@@ -1000,7 +953,7 @@ static int builtin_block(parser_t &parser, wchar_t **argv)
additional operational modes, such as printing a list of all
builtins, printing help, etc.
*/
-static int builtin_builtin(parser_t &parser, wchar_t **argv)
+static int builtin_builtin(parser_t &parser, io_streams_t &streams, wchar_t **argv)
{
int argc=builtin_count_args(argv);
int list=0;
@@ -1040,16 +993,15 @@ static int builtin_builtin(parser_t &parser, wchar_t **argv)
case 0:
if (long_options[opt_index].flag != 0)
break;
- append_format(stderr_buffer,
- BUILTIN_ERR_UNKNOWN,
+ streams.err.append_format(BUILTIN_ERR_UNKNOWN,
argv[0],
long_options[opt_index].name);
- builtin_print_help(parser, argv[0], stderr_buffer);
+ builtin_print_help(parser, streams, argv[0], streams.err);
return STATUS_BUILTIN_ERROR;
case 'h':
- builtin_print_help(parser, argv[0], stdout_buffer);
+ builtin_print_help(parser, streams, argv[0], streams.out);
return STATUS_BUILTIN_OK;
case 'n':
@@ -1057,7 +1009,7 @@ static int builtin_builtin(parser_t &parser, wchar_t **argv)
break;
case '?':
- builtin_unknown_option(parser, argv[0], argv[w.woptind-1]);
+ builtin_unknown_option(parser, streams, argv[0], argv[w.woptind-1]);
return STATUS_BUILTIN_ERROR;
}
@@ -1073,8 +1025,8 @@ static int builtin_builtin(parser_t &parser, wchar_t **argv)
{
const wchar_t *el = names.at(i).c_str();
- stdout_buffer.append(el);
- stdout_buffer.append(L"\n");
+ streams.out.append(el);
+ streams.out.append(L"\n");
}
}
return STATUS_BUILTIN_OK;
@@ -1083,7 +1035,7 @@ static int builtin_builtin(parser_t &parser, wchar_t **argv)
/**
Implementation of the builtin emit command, used to create events.
*/
-static int builtin_emit(parser_t &parser, wchar_t **argv)
+static int builtin_emit(parser_t &parser, io_streams_t &streams, wchar_t **argv)
{
wgetopter_t w;
int argc=builtin_count_args(argv);
@@ -1118,19 +1070,18 @@ static int builtin_emit(parser_t &parser, wchar_t **argv)
case 0:
if (long_options[opt_index].flag != 0)
break;
- append_format(stderr_buffer,
- BUILTIN_ERR_UNKNOWN,
+ streams.err.append_format(BUILTIN_ERR_UNKNOWN,
argv[0],
long_options[opt_index].name);
- builtin_print_help(parser, argv[0], stderr_buffer);
+ builtin_print_help(parser, streams, argv[0], streams.err);
return STATUS_BUILTIN_ERROR;
case 'h':
- builtin_print_help(parser, argv[0], stdout_buffer);
+ builtin_print_help(parser, streams, argv[0], streams.out);
return STATUS_BUILTIN_OK;
case '?':
- builtin_unknown_option(parser, argv[0], argv[w.woptind-1]);
+ builtin_unknown_option(parser, streams, argv[0], argv[w.woptind-1]);
return STATUS_BUILTIN_ERROR;
}
@@ -1139,7 +1090,7 @@ static int builtin_emit(parser_t &parser, wchar_t **argv)
if (!argv[w.woptind])
{
- append_format(stderr_buffer, L"%ls: expected event name\n", argv[0]);
+ streams.err.append_format(L"%ls: expected event name\n", argv[0]);
return STATUS_BUILTIN_ERROR;
}
const wchar_t *eventname = argv[w.woptind];
@@ -1154,7 +1105,7 @@ static int builtin_emit(parser_t &parser, wchar_t **argv)
Implementation of the builtin 'command'. Actual command running is handled by
the parser, this just processes the flags.
*/
-static int builtin_command(parser_t &parser, wchar_t **argv)
+static int builtin_command(parser_t &parser, io_streams_t &streams, wchar_t **argv)
{
wgetopter_t w;
int argc=builtin_count_args(argv);
@@ -1187,15 +1138,14 @@ static int builtin_command(parser_t &parser, wchar_t **argv)
case 0:
if (long_options[opt_index].flag != 0)
break;
- append_format(stderr_buffer,
- BUILTIN_ERR_UNKNOWN,
+ streams.err.append_format(BUILTIN_ERR_UNKNOWN,
argv[0],
long_options[opt_index].name);
- builtin_print_help(parser, argv[0], stderr_buffer);
+ builtin_print_help(parser, streams, argv[0], streams.err);
return STATUS_BUILTIN_ERROR;
case 'h':
- builtin_print_help(parser, argv[0], stdout_buffer);
+ builtin_print_help(parser, streams, argv[0], streams.out);
return STATUS_BUILTIN_OK;
case 's':
@@ -1204,7 +1154,7 @@ static int builtin_command(parser_t &parser, wchar_t **argv)
break;
case '?':
- builtin_unknown_option(parser, argv[0], argv[w.woptind-1]);
+ builtin_unknown_option(parser, streams, argv[0], argv[w.woptind-1]);
return STATUS_BUILTIN_ERROR;
}
@@ -1213,7 +1163,7 @@ static int builtin_command(parser_t &parser, wchar_t **argv)
if (!print_path)
{
- builtin_print_help(parser, argv[0], stdout_buffer);
+ builtin_print_help(parser, streams, argv[0], streams.out);
return STATUS_BUILTIN_ERROR;
}
@@ -1225,7 +1175,7 @@ static int builtin_command(parser_t &parser, wchar_t **argv)
wcstring path;
if (path_get_path(command_name, &path))
{
- append_format(stdout_buffer, L"%ls\n", path.c_str());
+ streams.out.append_format( L"%ls\n", path.c_str());
++found;
}
}
@@ -1237,7 +1187,7 @@ static int builtin_command(parser_t &parser, wchar_t **argv)
only a placeholder that prints the help message. Useful for
commands that live in the parser.
*/
-static int builtin_generic(parser_t &parser, wchar_t **argv)
+static int builtin_generic(parser_t &parser, io_streams_t &streams, wchar_t **argv)
{
wgetopter_t w;
int argc=builtin_count_args(argv);
@@ -1245,7 +1195,7 @@ static int builtin_generic(parser_t &parser, wchar_t **argv)
/* Hackish - if we have no arguments other than the command, we are a "naked invocation" and we just print help */
if (argc == 1)
{
- builtin_print_help(parser, argv[0], stdout_buffer);
+ builtin_print_help(parser, streams, argv[0], streams.out);
return STATUS_BUILTIN_ERROR;
}
@@ -1273,19 +1223,18 @@ static int builtin_generic(parser_t &parser, wchar_t **argv)
case 0:
if (long_options[opt_index].flag != 0)
break;
- append_format(stderr_buffer,
- BUILTIN_ERR_UNKNOWN,
+ streams.err.append_format(BUILTIN_ERR_UNKNOWN,
argv[0],
long_options[opt_index].name);
- builtin_print_help(parser, argv[0], stderr_buffer);
+ builtin_print_help(parser, streams, argv[0], streams.err);
return STATUS_BUILTIN_ERROR;
case 'h':
- builtin_print_help(parser, argv[0], stdout_buffer);
+ builtin_print_help(parser, streams, argv[0], streams.out);
return STATUS_BUILTIN_OK;
case '?':
- builtin_unknown_option(parser, argv[0], argv[w.woptind-1]);
+ builtin_unknown_option(parser, streams, argv[0], argv[w.woptind-1]);
return STATUS_BUILTIN_ERROR;
}
@@ -1295,12 +1244,12 @@ static int builtin_generic(parser_t &parser, wchar_t **argv)
}
/**
- Output a definition of the specified function to the specified
- string. Used by the functions builtin.
+ Return a definition of the specified function. Used by the functions builtin.
*/
-static void functions_def(const wcstring &name, wcstring &out)
+static wcstring functions_def(const wcstring &name)
{
- CHECK(! name.empty(),);
+ CHECK(! name.empty(), L"");
+ wcstring out;
wcstring desc, def;
function_get_desc(name, &desc);
@@ -1426,13 +1375,14 @@ static void functions_def(const wcstring &name, wcstring &out)
out.push_back(L'\n');
}
out.append(L"end\n");
+ return out;
}
/**
The functions builtin, used for listing and erasing functions.
*/
-static int builtin_functions(parser_t &parser, wchar_t **argv)
+static int builtin_functions(parser_t &parser, io_streams_t &streams, wchar_t **argv)
{
wgetopter_t w;
int i;
@@ -1500,11 +1450,10 @@ static int builtin_functions(parser_t &parser, wchar_t **argv)
case 0:
if (long_options[opt_index].flag != 0)
break;
- append_format(stderr_buffer,
- BUILTIN_ERR_UNKNOWN,
+ streams.err.append_format(BUILTIN_ERR_UNKNOWN,
argv[0],
long_options[opt_index].name);
- builtin_print_help(parser, argv[0], stderr_buffer);
+ builtin_print_help(parser, streams, argv[0], streams.err);
return STATUS_BUILTIN_ERROR;
@@ -1526,7 +1475,7 @@ static int builtin_functions(parser_t &parser, wchar_t **argv)
break;
case 'h':
- builtin_print_help(parser, argv[0], stdout_buffer);
+ builtin_print_help(parser, streams, argv[0], streams.out);
return STATUS_BUILTIN_OK;
case 'q':
@@ -1538,7 +1487,7 @@ static int builtin_functions(parser_t &parser, wchar_t **argv)
break;
case '?':
- builtin_unknown_option(parser, argv[0], argv[w.woptind-1]);
+ builtin_unknown_option(parser, streams, argv[0], argv[w.woptind-1]);
return STATUS_BUILTIN_ERROR;
}
@@ -1550,11 +1499,10 @@ static int builtin_functions(parser_t &parser, wchar_t **argv)
*/
if ((erase + (!!desc) + list + query + copy) > 1)
{
- append_format(stderr_buffer,
- _(L"%ls: Invalid combination of options\n"),
+ streams.err.append_format(_(L"%ls: Invalid combination of options\n"),
argv[0]);
- builtin_print_help(parser, argv[0], stderr_buffer);
+ builtin_print_help(parser, streams, argv[0], streams.err);
return STATUS_BUILTIN_ERROR;
}
@@ -1572,22 +1520,20 @@ static int builtin_functions(parser_t &parser, wchar_t **argv)
if (argc-w.woptind != 1)
{
- append_format(stderr_buffer,
- _(L"%ls: Expected exactly one function name\n"),
+ streams.err.append_format(_(L"%ls: Expected exactly one function name\n"),
argv[0]);
- builtin_print_help(parser, argv[0], stderr_buffer);
+ builtin_print_help(parser, streams, argv[0], streams.err);
return STATUS_BUILTIN_ERROR;
}
func = argv[w.woptind];
if (!function_exists(func))
{
- append_format(stderr_buffer,
- _(L"%ls: Function '%ls' does not exist\n"),
+ streams.err.append_format(_(L"%ls: Function '%ls' does not exist\n"),
argv[0],
func);
- builtin_print_help(parser, argv[0], stderr_buffer);
+ builtin_print_help(parser, streams, argv[0], streams.err);
return STATUS_BUILTIN_ERROR;
}
@@ -1598,7 +1544,7 @@ static int builtin_functions(parser_t &parser, wchar_t **argv)
}
else if (list || (argc==w.woptind))
{
- int is_screen = !builtin_out_redirect && isatty(1);
+ int is_screen = !streams.out_is_redirected && isatty(STDOUT_FILENO);
size_t i;
wcstring_list_t names = function_get_names(show_hidden);
std::sort(names.begin(), names.end());
@@ -1612,14 +1558,14 @@ static int builtin_functions(parser_t &parser, wchar_t **argv)
buff.append(L", ");
}
- write_screen(buff, stdout_buffer);
+ streams.out.append(reformat_for_screen(buff));
}
else
{
for (i=0; i<names.size(); i++)
{
- stdout_buffer.append(names.at(i).c_str());
- stdout_buffer.append(L"\n");
+ streams.out.append(names.at(i).c_str());
+ streams.out.append(L"\n");
}
}
@@ -1632,10 +1578,9 @@ static int builtin_functions(parser_t &parser, wchar_t **argv)
if (argc-w.woptind != 2)
{
- append_format(stderr_buffer,
- _(L"%ls: Expected exactly two names (current function name, and new function name)\n"),
+ streams.err.append_format(_(L"%ls: Expected exactly two names (current function name, and new function name)\n"),
argv[0]);
- builtin_print_help(parser, argv[0], stderr_buffer);
+ builtin_print_help(parser, streams, argv[0], streams.err);
return STATUS_BUILTIN_ERROR;
}
@@ -1644,34 +1589,31 @@ static int builtin_functions(parser_t &parser, wchar_t **argv)
if (!function_exists(current_func))
{
- append_format(stderr_buffer,
- _(L"%ls: Function '%ls' does not exist\n"),
+ streams.err.append_format(_(L"%ls: Function '%ls' does not exist\n"),
argv[0],
current_func.c_str());
- builtin_print_help(parser, argv[0], stderr_buffer);
+ builtin_print_help(parser, streams, argv[0], streams.err);
return STATUS_BUILTIN_ERROR;
}
if ((wcsfuncname(new_func) != 0) || parser_keywords_is_reserved(new_func))
{
- append_format(stderr_buffer,
- _(L"%ls: Illegal function name '%ls'\n"),
+ streams.err.append_format(_(L"%ls: Illegal function name '%ls'\n"),
argv[0],
new_func.c_str());
- builtin_print_help(parser, argv[0], stderr_buffer);
+ builtin_print_help(parser, streams, argv[0], streams.err);
return STATUS_BUILTIN_ERROR;
}
// keep things simple: don't allow existing names to be copy targets.
if (function_exists(new_func))
{
- append_format(stderr_buffer,
- _(L"%ls: Function '%ls' already exists. Cannot create copy '%ls'\n"),
+ streams.err.append_format(_(L"%ls: Function '%ls' already exists. Cannot create copy '%ls'\n"),
argv[0],
new_func.c_str(),
current_func.c_str());
- builtin_print_help(parser, argv[0], stderr_buffer);
+ builtin_print_help(parser, streams, argv[0], streams.err);
return STATUS_BUILTIN_ERROR;
}
@@ -1690,9 +1632,9 @@ static int builtin_functions(parser_t &parser, wchar_t **argv)
if (!query)
{
if (i != w.woptind)
- stdout_buffer.append(L"\n");
+ streams.out.append(L"\n");
- functions_def(argv[i], stdout_buffer);
+ streams.out.append(functions_def(argv[i]));
}
}
}
@@ -1812,7 +1754,7 @@ static bool builtin_echo_parse_numeric_sequence(const wchar_t *str, size_t *cons
We also support a new option -s to mean "no spaces"
*/
-static int builtin_echo(parser_t &parser, wchar_t **argv)
+static int builtin_echo(parser_t &parser, io_streams_t &streams, wchar_t **argv)
{
/* Skip first arg */
if (! *argv++)
@@ -1878,7 +1820,7 @@ static int builtin_echo(parser_t &parser, wchar_t **argv)
{
if (print_spaces && idx > 0)
{
- stdout_buffer.push_back(' ');
+ streams.out.push_back(' ');
}
const wchar_t *str = args_to_echo[idx];
@@ -1887,7 +1829,7 @@ static int builtin_echo(parser_t &parser, wchar_t **argv)
if (! interpret_special_chars || str[j] != L'\\')
{
/* Not an escape */
- stdout_buffer.push_back(str[j]);
+ streams.out.push_back(str[j]);
}
else
{
@@ -1953,20 +1895,20 @@ static int builtin_echo(parser_t &parser, wchar_t **argv)
if (continue_output)
{
- stdout_buffer.push_back(wc);
+ streams.out.push_back(wc);
}
}
}
}
if (print_newline && continue_output)
{
- stdout_buffer.push_back('\n');
+ streams.out.push_back('\n');
}
return STATUS_BUILTIN_OK;
}
/** The pwd builtin. We don't respect -P to resolve symbolic links because we try to always resolve them. */
-static int builtin_pwd(parser_t &parser, wchar_t **argv)
+static int builtin_pwd(parser_t &parser, io_streams_t &streams, wchar_t **argv)
{
wchar_t dir_path[4096];
wchar_t *res = wgetcwd(dir_path, 4096);
@@ -1976,14 +1918,14 @@ static int builtin_pwd(parser_t &parser, wchar_t **argv)
}
else
{
- stdout_buffer.append(dir_path);
- stdout_buffer.push_back(L'\n');
+ streams.out.append(dir_path);
+ streams.out.push_back(L'\n');
return STATUS_BUILTIN_OK;
}
}
/** Adds a function to the function set. It calls into function.cpp to perform any heavy lifting. */
-int define_function(parser_t &parser, const wcstring_list_t &c_args, const wcstring &contents, int definition_line_offset, wcstring *out_err)
+int define_function(parser_t &parser, io_streams_t &streams, const wcstring_list_t &c_args, const wcstring &contents, int definition_line_offset, wcstring *out_err)
{
wgetopter_t w;
assert(out_err != NULL);
@@ -2209,7 +2151,7 @@ int define_function(parser_t &parser, const wcstring_list_t &c_args, const wcstr
}
case 'h':
- builtin_print_help(parser, argv[0], stdout_buffer);
+ builtin_print_help(parser, streams, argv[0], streams.out);
return STATUS_BUILTIN_OK;
case 1:
@@ -2218,7 +2160,7 @@ int define_function(parser_t &parser, const wcstring_list_t &c_args, const wcstr
break;
case '?':
- builtin_unknown_option(parser, argv[0], argv[w.woptind-1]);
+ builtin_unknown_option(parser, streams, argv[0], argv[w.woptind-1]);
res = 1;
break;
@@ -2340,7 +2282,7 @@ int define_function(parser_t &parser, const wcstring_list_t &c_args, const wcstr
/**
The random builtin. For generating random numbers.
*/
-static int builtin_random(parser_t &parser, wchar_t **argv)
+static int builtin_random(parser_t &parser, io_streams_t &streams, wchar_t **argv)
{
static int seeded=0;
static struct drand48_data seed_buffer;
@@ -2379,20 +2321,19 @@ static int builtin_random(parser_t &parser, wchar_t **argv)
case 0:
if (long_options[opt_index].flag != 0)
break;
- append_format(stderr_buffer,
- BUILTIN_ERR_UNKNOWN,
+ streams.err.append_format(BUILTIN_ERR_UNKNOWN,
argv[0],
long_options[opt_index].name);
- builtin_print_help(parser, argv[0], stderr_buffer);
+ builtin_print_help(parser, streams, argv[0], streams.err);
return STATUS_BUILTIN_ERROR;
case 'h':
- builtin_print_help(parser, argv[0], stdout_buffer);
+ builtin_print_help(parser, streams, argv[0], streams.out);
break;
case '?':
- builtin_unknown_option(parser, argv[0], argv[w.woptind-1]);
+ builtin_unknown_option(parser, streams, argv[0], argv[w.woptind-1]);
return STATUS_BUILTIN_ERROR;
}
@@ -2413,7 +2354,7 @@ static int builtin_random(parser_t &parser, wchar_t **argv)
}
lrand48_r(&seed_buffer, &res);
- append_format(stdout_buffer, L"%ld\n", labs(res%32767));
+ streams.out.append_format( L"%ld\n", labs(res%32767));
break;
}
@@ -2426,8 +2367,7 @@ static int builtin_random(parser_t &parser, wchar_t **argv)
foo = wcstol(argv[w.woptind], &end, 10);
if (errno || *end)
{
- append_format(stderr_buffer,
- _(L"%ls: Seed value '%ls' is not a valid number\n"),
+ streams.err.append_format(_(L"%ls: Seed value '%ls' is not a valid number\n"),
argv[0],
argv[w.woptind]);
@@ -2440,11 +2380,10 @@ static int builtin_random(parser_t &parser, wchar_t **argv)
default:
{
- append_format(stderr_buffer,
- _(L"%ls: Expected zero or one argument, got %d\n"),
+ streams.err.append_format(_(L"%ls: Expected zero or one argument, got %d\n"),
argv[0],
argc-w.woptind);
- builtin_print_help(parser, argv[0], stderr_buffer);
+ builtin_print_help(parser, streams, argv[0], streams.err);
return STATUS_BUILTIN_ERROR;
}
}
@@ -2455,7 +2394,7 @@ static int builtin_random(parser_t &parser, wchar_t **argv)
/**
The read builtin. Reads from stdin and stores the values in environment variables.
*/
-static int builtin_read(parser_t &parser, wchar_t **argv)
+static int builtin_read(parser_t &parser, io_streams_t &streams, wchar_t **argv)
{
wgetopter_t w;
wcstring buff;
@@ -2554,11 +2493,10 @@ static int builtin_read(parser_t &parser, wchar_t **argv)
case 0:
if (long_options[opt_index].flag != 0)
break;
- append_format(stderr_buffer,
- BUILTIN_ERR_UNKNOWN,
+ streams.err.append_format(BUILTIN_ERR_UNKNOWN,
argv[0],
long_options[opt_index].name);
- builtin_print_help(parser, argv[0], stderr_buffer);
+ builtin_print_help(parser, streams, argv[0], streams.err);
return STATUS_BUILTIN_ERROR;
@@ -2606,19 +2544,17 @@ static int builtin_read(parser_t &parser, wchar_t **argv)
switch (errno)
{
case ERANGE:
- append_format(stderr_buffer,
- _(L"%ls: Argument '%ls' is out of range\n"),
+ streams.err.append_format( _(L"%ls: Argument '%ls' is out of range\n"),
argv[0],
w.woptarg);
- builtin_print_help(parser, argv[0], stderr_buffer);
+ builtin_print_help(parser, streams, argv[0], streams.err);
return STATUS_BUILTIN_ERROR;
default:
- append_format(stderr_buffer,
- _(L"%ls: Argument '%ls' must be an integer\n"),
+ streams.err.append_format( _(L"%ls: Argument '%ls' must be an integer\n"),
argv[0],
w.woptarg);
- builtin_print_help(parser, argv[0], stderr_buffer);
+ builtin_print_help(parser, streams, argv[0], streams.err);
return STATUS_BUILTIN_ERROR;
}
}
@@ -2637,11 +2573,11 @@ static int builtin_read(parser_t &parser, wchar_t **argv)
break;
case 'h':
- builtin_print_help(parser, argv[0], stdout_buffer);
+ builtin_print_help(parser, streams, argv[0], streams.out);
return STATUS_BUILTIN_OK;
case L'?':
- builtin_unknown_option(parser, argv[0], argv[w.woptind-1]);
+ builtin_unknown_option(parser, streams, argv[0], argv[w.woptind-1]);
return STATUS_BUILTIN_ERROR;
}
@@ -2649,29 +2585,27 @@ static int builtin_read(parser_t &parser, wchar_t **argv)
if ((place & ENV_UNEXPORT) && (place & ENV_EXPORT))
{
- append_format(stderr_buffer,
- BUILTIN_ERR_EXPUNEXP,
+ streams.err.append_format(BUILTIN_ERR_EXPUNEXP,
argv[0]);
- builtin_print_help(parser, argv[0], stderr_buffer);
+ builtin_print_help(parser, streams, argv[0], streams.err);
return STATUS_BUILTIN_ERROR;
}
if ((place&ENV_LOCAL?1:0) + (place & ENV_GLOBAL?1:0) + (place & ENV_UNIVERSAL?1:0) > 1)
{
- append_format(stderr_buffer,
- BUILTIN_ERR_GLOCAL,
+ streams.err.append_format(BUILTIN_ERR_GLOCAL,
argv[0]);
- builtin_print_help(parser, argv[0], stderr_buffer);
+ builtin_print_help(parser, streams, argv[0], streams.err);
return STATUS_BUILTIN_ERROR;
}
if (array && w.woptind+1 != argc)
{
- append_format(stderr_buffer, _(L"%ls: --array option requires a single variable name.\n"), argv[0]);
- builtin_print_help(parser, argv[0], stderr_buffer);
+ streams.err.append_format(_(L"%ls: --array option requires a single variable name.\n"), argv[0]);
+ builtin_print_help(parser, streams, argv[0], streams.err);
return STATUS_BUILTIN_ERROR;
}
@@ -2685,7 +2619,7 @@ static int builtin_read(parser_t &parser, wchar_t **argv)
if (!wcslen(argv[i]))
{
- append_format(stderr_buffer, BUILTIN_ERR_VARNAME_ZERO, argv[0]);
+ streams.err.append_format(BUILTIN_ERR_VARNAME_ZERO, argv[0]);
return STATUS_BUILTIN_ERROR;
}
@@ -2693,8 +2627,8 @@ static int builtin_read(parser_t &parser, wchar_t **argv)
{
if ((!iswalnum(*src)) && (*src != L'_'))
{
- append_format(stderr_buffer, BUILTIN_ERR_VARCHAR, argv[0], *src);
- builtin_print_help(parser, argv[0], stderr_buffer);
+ streams.err.append_format(BUILTIN_ERR_VARCHAR, argv[0], *src);
+ builtin_print_help(parser, streams, argv[0], streams.err);
return STATUS_BUILTIN_ERROR;
}
}
@@ -2709,7 +2643,7 @@ static int builtin_read(parser_t &parser, wchar_t **argv)
/*
Check if we should read interactively using \c reader_readline()
*/
- if (isatty(0) && builtin_stdin == 0 && !split_null)
+ if (isatty(0) && streams.stdin_fd == STDIN_FILENO && !split_null)
{
const wchar_t *line;
@@ -2770,7 +2704,7 @@ static int builtin_read(parser_t &parser, wchar_t **argv)
while (!finished)
{
char b;
- if (read_blocked(builtin_stdin, &b, 1) <= 0)
+ if (read_blocked(streams.stdin_fd, &b, 1) <= 0)
{
eof=1;
break;
@@ -2898,7 +2832,7 @@ static int builtin_read(parser_t &parser, wchar_t **argv)
/**
The status builtin. Gives various status information on fish.
*/
-static int builtin_status(parser_t &parser, wchar_t **argv)
+static int builtin_status(parser_t &parser, io_streams_t &streams, wchar_t **argv)
{
wgetopter_t w;
enum
@@ -2998,11 +2932,10 @@ static int builtin_status(parser_t &parser, wchar_t **argv)
case 0:
if (long_options[opt_index].flag != 0)
break;
- append_format(stderr_buffer,
- BUILTIN_ERR_UNKNOWN,
+ streams.err.append_format(BUILTIN_ERR_UNKNOWN,
argv[0],
long_options[opt_index].name);
- builtin_print_help(parser, argv[0], stderr_buffer);
+ builtin_print_help(parser, streams, argv[0], streams.err);
return STATUS_BUILTIN_ERROR;
case 'c':
@@ -3030,7 +2963,7 @@ static int builtin_status(parser_t &parser, wchar_t **argv)
break;
case 'h':
- builtin_print_help(parser, argv[0], stdout_buffer);
+ builtin_print_help(parser, streams, argv[0], streams.out);
return STATUS_BUILTIN_OK;
case 'j':
@@ -3042,8 +2975,7 @@ static int builtin_status(parser_t &parser, wchar_t **argv)
job_control_mode = JOB_CONTROL_NONE;
else
{
- append_format(stderr_buffer,
- L"%ls: Invalid job control mode '%ls'\n",
+ streams.err.append_format(L"%ls: Invalid job control mode '%ls'\n",
L"status", w.woptarg);
res = 1;
}
@@ -3056,11 +2988,11 @@ static int builtin_status(parser_t &parser, wchar_t **argv)
case ':':
- builtin_missing_argument(parser, argv[0], argv[w.woptind-1]);
+ builtin_missing_argument(parser, streams, argv[0], argv[w.woptind-1]);
return STATUS_BUILTIN_ERROR;
case '?':
- builtin_unknown_option(parser, argv[0], argv[w.woptind-1]);
+ builtin_unknown_option(parser, streams, argv[0], argv[w.woptind-1]);
return STATUS_BUILTIN_ERROR;
}
@@ -3079,14 +3011,14 @@ static int builtin_status(parser_t &parser, wchar_t **argv)
if (!fn)
fn = _(L"Standard input");
- append_format(stdout_buffer, L"%ls\n", fn);
+ streams.out.append_format( L"%ls\n", fn);
break;
}
case CURRENT_LINE_NUMBER:
{
- append_format(stdout_buffer, L"%d\n", parser.get_lineno());
+ streams.out.append_format( L"%d\n", parser.get_lineno());
break;
}
@@ -3113,22 +3045,21 @@ static int builtin_status(parser_t &parser, wchar_t **argv)
case STACK_TRACE:
{
- parser.stack_trace(0, stdout_buffer);
+ streams.out.append(parser.stack_trace());
break;
}
case NORMAL:
{
if (is_login)
- append_format(stdout_buffer, _(L"This is a login shell\n"));
+ streams.out.append_format( _(L"This is a login shell\n"));
else
- append_format(stdout_buffer, _(L"This is not a login shell\n"));
+ streams.out.append_format( _(L"This is not a login shell\n"));
- append_format(stdout_buffer, _(L"Job control: %ls\n"),
+ streams.out.append_format( _(L"Job control: %ls\n"),
job_control_mode==JOB_CONTROL_INTERACTIVE?_(L"Only on interactive jobs"):
(job_control_mode==JOB_CONTROL_NONE ? _(L"Never") : _(L"Always")));
-
- parser.stack_trace(0, stdout_buffer);
+ streams.out.append(parser.stack_trace());
break;
}
}
@@ -3141,7 +3072,7 @@ static int builtin_status(parser_t &parser, wchar_t **argv)
/**
The exit builtin. Calls reader_exit to exit and returns the value specified.
*/
-static int builtin_exit(parser_t &parser, wchar_t **argv)
+static int builtin_exit(parser_t &parser, io_streams_t &streams, wchar_t **argv)
{
int argc = builtin_count_args(argv);
@@ -3161,11 +3092,10 @@ static int builtin_exit(parser_t &parser, wchar_t **argv)
ec = wcstol(argv[1],&end,10);
if (errno || *end != 0)
{
- append_format(stderr_buffer,
- _(L"%ls: Argument '%ls' must be an integer\n"),
+ streams.err.append_format(_(L"%ls: Argument '%ls' must be an integer\n"),
argv[0],
argv[1]);
- builtin_print_help(parser, argv[0], stderr_buffer);
+ builtin_print_help(parser, streams, argv[0], streams.err);
return STATUS_BUILTIN_ERROR;
}
break;
@@ -3173,11 +3103,10 @@ static int builtin_exit(parser_t &parser, wchar_t **argv)
default:
{
- append_format(stderr_buffer,
- BUILTIN_ERR_TOO_MANY_ARGUMENTS,
+ streams.err.append_format(BUILTIN_ERR_TOO_MANY_ARGUMENTS,
argv[0]);
- builtin_print_help(parser, argv[0], stderr_buffer);
+ builtin_print_help(parser, streams, argv[0], streams.err);
return STATUS_BUILTIN_ERROR;
}
@@ -3191,7 +3120,7 @@ static int builtin_exit(parser_t &parser, wchar_t **argv)
or to $HOME if none is specified. The directory can be relative to
any directory in the CDPATH variable.
*/
-static int builtin_cd(parser_t &parser, wchar_t **argv)
+static int builtin_cd(parser_t &parser, io_streams_t &streams, wchar_t **argv)
{
env_var_t dir_in;
wcstring dir;
@@ -3203,8 +3132,7 @@ static int builtin_cd(parser_t &parser, wchar_t **argv)
dir_in = env_get_string(L"HOME");
if (dir_in.missing_or_empty())
{
- append_format(stderr_buffer,
- _(L"%ls: Could not find home directory\n"),
+ streams.err.append_format(_(L"%ls: Could not find home directory\n"),
argv[0]);
}
}
@@ -3223,30 +3151,26 @@ static int builtin_cd(parser_t &parser, wchar_t **argv)
{
if (errno == ENOTDIR)
{
- append_format(stderr_buffer,
- _(L"%ls: '%ls' is not a directory\n"),
+ streams.err.append_format(_(L"%ls: '%ls' is not a directory\n"),
argv[0],
dir_in.c_str());
}
else if (errno == ENOENT)
{
- append_format(stderr_buffer,
- _(L"%ls: The directory '%ls' does not exist\n"),
+ streams.err.append_format(_(L"%ls: The directory '%ls' does not exist\n"),
argv[0],
dir_in.c_str());
}
else if (errno == EROTTEN)
{
- append_format(stderr_buffer,
- _(L"%ls: '%ls' is a rotten symlink\n"),
+ streams.err.append_format(_(L"%ls: '%ls' is a rotten symlink\n"),
argv[0],
dir_in.c_str());
}
else
{
- append_format(stderr_buffer,
- _(L"%ls: Unknown error trying to locate directory '%ls'\n"),
+ streams.err.append_format(_(L"%ls: Unknown error trying to locate directory '%ls'\n"),
argv[0],
dir_in.c_str());
@@ -3255,7 +3179,7 @@ static int builtin_cd(parser_t &parser, wchar_t **argv)
if (!get_is_interactive())
{
- stderr_buffer.append(parser.current_line());
+ streams.err.append(parser.current_line());
}
res = 1;
@@ -3268,8 +3192,7 @@ static int builtin_cd(parser_t &parser, wchar_t **argv)
status = wstat(dir, &buffer);
if (!status && S_ISDIR(buffer.st_mode))
{
- append_format(stderr_buffer,
- _(L"%ls: Permission denied: '%ls'\n"),
+ streams.err.append_format(_(L"%ls: Permission denied: '%ls'\n"),
argv[0],
dir.c_str());
@@ -3277,15 +3200,14 @@ static int builtin_cd(parser_t &parser, wchar_t **argv)
else
{
- append_format(stderr_buffer,
- _(L"%ls: '%ls' is not a directory\n"),
+ streams.err.append_format(_(L"%ls: '%ls' is not a directory\n"),
argv[0],
dir.c_str());
}
if (!get_is_interactive())
{
- stderr_buffer.append(parser.current_line());
+ streams.err.append(parser.current_line());
}
res = 1;
@@ -3293,7 +3215,7 @@ static int builtin_cd(parser_t &parser, wchar_t **argv)
else if (!env_set_pwd())
{
res=1;
- append_format(stderr_buffer, _(L"%ls: Could not set PWD variable\n"), argv[0]);
+ streams.err.append_format(_(L"%ls: Could not set PWD variable\n"), argv[0]);
}
return res;
@@ -3303,11 +3225,11 @@ static int builtin_cd(parser_t &parser, wchar_t **argv)
Implementation of the builtin count command, used to count the
number of arguments sent to it.
*/
-static int builtin_count(parser_t &parser, wchar_t ** argv)
+static int builtin_count(parser_t &parser, io_streams_t &streams, wchar_t **argv)
{
int argc;
argc = builtin_count_args(argv);
- append_format(stdout_buffer, L"%d\n", argc-1);
+ streams.out.append_format( L"%d\n", argc-1);
return !(argc-1);
}
@@ -3315,7 +3237,7 @@ static int builtin_count(parser_t &parser, wchar_t ** argv)
Implementation of the builtin contains command, used to check if a
specified string is part of a list.
*/
-static int builtin_contains(parser_t &parser, wchar_t ** argv)
+static int builtin_contains(parser_t &parser, io_streams_t &streams, wchar_t ** argv)
{
wgetopter_t w;
int argc;
@@ -3348,25 +3270,24 @@ static int builtin_contains(parser_t &parser, wchar_t ** argv)
assert(opt_index >= 0 && (size_t)opt_index < sizeof long_options / sizeof *long_options);
if (long_options[opt_index].flag != 0)
break;
- append_format(stderr_buffer,
- BUILTIN_ERR_UNKNOWN,
+ streams.err.append_format(BUILTIN_ERR_UNKNOWN,
argv[0],
long_options[opt_index].name);
- builtin_print_help(parser, argv[0], stderr_buffer);
+ builtin_print_help(parser, streams, argv[0], streams.err);
return STATUS_BUILTIN_ERROR;
case 'h':
- builtin_print_help(parser, argv[0], stdout_buffer);
+ builtin_print_help(parser, streams, argv[0], streams.out);
return STATUS_BUILTIN_OK;
case ':':
- builtin_missing_argument(parser, argv[0], argv[w.woptind-1]);
+ builtin_missing_argument(parser, streams, argv[0], argv[w.woptind-1]);
return STATUS_BUILTIN_ERROR;
case '?':
- builtin_unknown_option(parser, argv[0], argv[w.woptind-1]);
+ builtin_unknown_option(parser, streams, argv[0], argv[w.woptind-1]);
return STATUS_BUILTIN_ERROR;
case 'i':
@@ -3379,7 +3300,7 @@ static int builtin_contains(parser_t &parser, wchar_t ** argv)
needle = argv[w.woptind];
if (!needle)
{
- append_format(stderr_buffer, _(L"%ls: Key not specified\n"), argv[0]);
+ streams.err.append_format(_(L"%ls: Key not specified\n"), argv[0]);
}
@@ -3388,7 +3309,7 @@ static int builtin_contains(parser_t &parser, wchar_t ** argv)
if (!wcscmp(needle, argv[i]))
{
- if (should_output_index) append_format(stdout_buffer, L"%d\n", i-w.woptind);
+ if (should_output_index) streams.out.append_format( L"%d\n", i-w.woptind);
return 0;
}
}
@@ -3400,7 +3321,7 @@ static int builtin_contains(parser_t &parser, wchar_t ** argv)
/**
The . (dot) builtin, sometimes called source. Evaluates the contents of a file.
*/
-static int builtin_source(parser_t &parser, wchar_t ** argv)
+static int builtin_source(parser_t &parser, io_streams_t &streams, wchar_t **argv)
{
ASSERT_IS_MAIN_THREAD();
int fd;
@@ -3416,30 +3337,30 @@ static int builtin_source(parser_t &parser, wchar_t ** argv)
{
fn = L"-";
fn_intern = fn;
- fd = dup(builtin_stdin);
+ fd = dup(streams.stdin_fd);
}
else
{
if ((fd = wopen_cloexec(argv[1], O_RDONLY)) == -1)
{
- append_format(stderr_buffer, _(L"%ls: Error encountered while sourcing file '%ls':\n"), argv[0], argv[1]);
- builtin_wperror(L"source");
+ streams.err.append_format(_(L"%ls: Error encountered while sourcing file '%ls':\n"), argv[0], argv[1]);
+ builtin_wperror(L"source", streams);
return STATUS_BUILTIN_ERROR;
}
if (fstat(fd, &buf) == -1)
{
close(fd);
- append_format(stderr_buffer, _(L"%ls: Error encountered while sourcing file '%ls':\n"), argv[0], argv[1]);
- builtin_wperror(L"source");
+ streams.err.append_format(_(L"%ls: Error encountered while sourcing file '%ls':\n"), argv[0], argv[1]);
+ builtin_wperror(L"source", streams);
return STATUS_BUILTIN_ERROR;
}
if (!S_ISREG(buf.st_mode))
{
close(fd);
- append_format(stderr_buffer, _(L"%ls: '%ls' is not a file\n"), argv[0], argv[1]);
+ streams.err.append_format(_(L"%ls: '%ls' is not a file\n"), argv[0], argv[1]);
return STATUS_BUILTIN_ERROR;
}
@@ -3451,14 +3372,13 @@ static int builtin_source(parser_t &parser, wchar_t ** argv)
env_set_argv((argc>2)?(argv+2):(argv+1));
- res = reader_read(fd, real_io ? *real_io : io_chain_t());
+ res = reader_read(fd, streams.io_chain ? *streams.io_chain : io_chain_t());
parser.pop_block();
if (res)
{
- append_format(stderr_buffer,
- _(L"%ls: Error while reading file '%ls'\n"),
+ streams.err.append_format(_(L"%ls: Error while reading file '%ls'\n"),
argv[0],
fn_intern == intern_static(L"-") ? L"<stdin>" : fn_intern);
}
@@ -3491,7 +3411,7 @@ static void make_first(job_t *j)
/**
Builtin for putting a job in the foreground
*/
-static int builtin_fg(parser_t &parser, wchar_t **argv)
+static int builtin_fg(parser_t &parser, io_streams_t &streams, wchar_t **argv)
{
job_t *j=NULL;
@@ -3513,8 +3433,7 @@ static int builtin_fg(parser_t &parser, wchar_t **argv)
}
if (!j)
{
- append_format(stderr_buffer,
- _(L"%ls: There are no suitable jobs\n"),
+ streams.err.append_format(_(L"%ls: There are no suitable jobs\n"),
argv[0]);
}
}
@@ -3541,19 +3460,17 @@ static int builtin_fg(parser_t &parser, wchar_t **argv)
if (found_job)
{
- append_format(stderr_buffer,
- _(L"%ls: Ambiguous job\n"),
+ streams.err.append_format(_(L"%ls: Ambiguous job\n"),
argv[0]);
}
else
{
- append_format(stderr_buffer,
- _(L"%ls: '%ls' is not a job\n"),
+ streams.err.append_format(_(L"%ls: '%ls' is not a job\n"),
argv[0],
argv[1]);
}
- builtin_print_help(parser, argv[0], stderr_buffer);
+ builtin_print_help(parser, streams, argv[0], streams.err);
j=0;
@@ -3567,32 +3484,29 @@ static int builtin_fg(parser_t &parser, wchar_t **argv)
if (*end || errno)
{
- append_format(stderr_buffer,
- BUILTIN_ERR_NOT_NUMBER,
+ streams.err.append_format(BUILTIN_ERR_NOT_NUMBER,
argv[0],
argv[1]);
- builtin_print_help(parser, argv[0], stderr_buffer);
+ builtin_print_help(parser, streams, argv[0], streams.err);
}
else
{
j = job_get_from_pid(pid);
if (!j || !job_get_flag(j, JOB_CONSTRUCTED) || job_is_completed(j))
{
- append_format(stderr_buffer,
- _(L"%ls: No suitable job: %d\n"),
+ streams.err.append_format(_(L"%ls: No suitable job: %d\n"),
argv[0],
pid);
- builtin_print_help(parser, argv[0], stderr_buffer);
+ builtin_print_help(parser, streams, argv[0], streams.err);
j=0;
}
else if (!job_get_flag(j, JOB_CONTROL))
{
- append_format(stderr_buffer,
- _(L"%ls: Can't put job %d, '%ls' to foreground because it is not under job control\n"),
+ streams.err.append_format(_(L"%ls: Can't put job %d, '%ls' to foreground because it is not under job control\n"),
argv[0],
pid,
j->command_wcstr());
- builtin_print_help(parser, argv[0], stderr_buffer);
+ builtin_print_help(parser, streams, argv[0], streams.err);
j=0;
}
}
@@ -3600,10 +3514,9 @@ static int builtin_fg(parser_t &parser, wchar_t **argv)
if (j)
{
- if (builtin_err_redirect)
+ if (streams.err_is_redirected)
{
- append_format(stderr_buffer,
- FG_MSG,
+ streams.err.append_format(FG_MSG,
j->job_id,
j->command_wcstr());
}
@@ -3636,31 +3549,28 @@ static int builtin_fg(parser_t &parser, wchar_t **argv)
/**
Helper function for builtin_bg()
*/
-static int send_to_bg(parser_t &parser, job_t *j, const wchar_t *name)
+static int send_to_bg(parser_t &parser, io_streams_t &streams, job_t *j, const wchar_t *name)
{
if (j == 0)
{
- append_format(stderr_buffer,
- _(L"%ls: Unknown job '%ls'\n"),
+ streams.err.append_format(_(L"%ls: Unknown job '%ls'\n"),
L"bg",
name);
- builtin_print_help(parser, L"bg", stderr_buffer);
+ builtin_print_help(parser, streams, L"bg", streams.err);
return STATUS_BUILTIN_ERROR;
}
else if (!job_get_flag(j, JOB_CONTROL))
{
- append_format(stderr_buffer,
- _(L"%ls: Can't put job %d, '%ls' to background because it is not under job control\n"),
+ streams.err.append_format(_(L"%ls: Can't put job %d, '%ls' to background because it is not under job control\n"),
L"bg",
j->job_id,
j->command_wcstr());
- builtin_print_help(parser, L"bg", stderr_buffer);
+ builtin_print_help(parser, streams, L"bg", streams.err);
return STATUS_BUILTIN_ERROR;
}
else
{
- append_format(stderr_buffer,
- _(L"Send job %d '%ls' to background\n"),
+ streams.err.append_format(_(L"Send job %d '%ls' to background\n"),
j->job_id,
j->command_wcstr());
}
@@ -3674,7 +3584,7 @@ static int send_to_bg(parser_t &parser, job_t *j, const wchar_t *name)
/**
Builtin for putting a job in the background
*/
-static int builtin_bg(parser_t &parser, wchar_t **argv)
+static int builtin_bg(parser_t &parser, io_streams_t &streams, wchar_t **argv)
{
int res = STATUS_BUILTIN_OK;
@@ -3692,14 +3602,13 @@ static int builtin_bg(parser_t &parser, wchar_t **argv)
if (!j)
{
- append_format(stderr_buffer,
- _(L"%ls: There are no suitable jobs\n"),
+ streams.err.append_format(_(L"%ls: There are no suitable jobs\n"),
argv[0]);
res = 1;
}
else
{
- res = send_to_bg(parser, j, _(L"(default)"));
+ res = send_to_bg(parser, streams, j, _(L"(default)"));
}
}
else
@@ -3715,8 +3624,7 @@ static int builtin_bg(parser_t &parser, wchar_t **argv)
pid = fish_wcstoi(argv[i], &end, 10);
if (errno || pid < 0 || *end || !job_get_from_pid(pid))
{
- append_format(stderr_buffer,
- _(L"%ls: '%ls' is not a job\n"),
+ streams.err.append_format(_(L"%ls: '%ls' is not a job\n"),
argv[0],
argv[i]);
err = 1;
@@ -3729,7 +3637,7 @@ static int builtin_bg(parser_t &parser, wchar_t **argv)
for (i=1; !res && argv[i]; i++)
{
pid = fish_wcstoi(argv[i], 0, 10);
- res |= send_to_bg(parser, job_get_from_pid(pid), *argv);
+ res |= send_to_bg(parser, streams, job_get_from_pid(pid), *argv);
}
}
}
@@ -3742,7 +3650,7 @@ static int builtin_bg(parser_t &parser, wchar_t **argv)
This function handles both the 'continue' and the 'break' builtins
that are used for loop control.
*/
-static int builtin_break_continue(parser_t &parser, wchar_t **argv)
+static int builtin_break_continue(parser_t &parser, io_streams_t &streams, wchar_t **argv)
{
int is_break = (wcscmp(argv[0],L"break")==0);
int argc = builtin_count_args(argv);
@@ -3750,12 +3658,11 @@ static int builtin_break_continue(parser_t &parser, wchar_t **argv)
if (argc != 1)
{
- append_format(stderr_buffer,
- BUILTIN_ERR_UNKNOWN,
+ streams.err.append_format(BUILTIN_ERR_UNKNOWN,
argv[0],
argv[1]);
- builtin_print_help(parser, argv[0], stderr_buffer);
+ builtin_print_help(parser, streams, argv[0], streams.err);
return STATUS_BUILTIN_ERROR;
}
@@ -3770,10 +3677,9 @@ static int builtin_break_continue(parser_t &parser, wchar_t **argv)
if (loop_idx >= parser.block_count())
{
- append_format(stderr_buffer,
- _(L"%ls: Not inside of loop\n"),
+ streams.err.append_format(_(L"%ls: Not inside of loop\n"),
argv[0]);
- builtin_print_help(parser, argv[0], stderr_buffer);
+ builtin_print_help(parser, streams, argv[0], streams.err);
return STATUS_BUILTIN_ERROR;
}
@@ -3796,11 +3702,11 @@ static int builtin_break_continue(parser_t &parser, wchar_t **argv)
interactive debugger.
*/
-static int builtin_breakpoint(parser_t &parser, wchar_t **argv)
+static int builtin_breakpoint(parser_t &parser, io_streams_t &streams, wchar_t **argv)
{
parser.push_block(new breakpoint_block_t());
- reader_read(STDIN_FILENO, real_io ? *real_io : io_chain_t());
+ reader_read(STDIN_FILENO, streams.io_chain ? *streams.io_chain : io_chain_t());
parser.pop_block();
@@ -3811,7 +3717,7 @@ static int builtin_breakpoint(parser_t &parser, wchar_t **argv)
/**
Function for handling the \c return builtin
*/
-static int builtin_return(parser_t &parser, wchar_t **argv)
+static int builtin_return(parser_t &parser, io_streams_t &streams, wchar_t **argv)
{
int argc = builtin_count_args(argv);
int status = proc_get_last_status();
@@ -3827,20 +3733,18 @@ static int builtin_return(parser_t &parser, wchar_t **argv)
status = fish_wcstoi(argv[1],&end,10);
if (errno || *end != 0)
{
- append_format(stderr_buffer,
- _(L"%ls: Argument '%ls' must be an integer\n"),
+ streams.err.append_format(_(L"%ls: Argument '%ls' must be an integer\n"),
argv[0],
argv[1]);
- builtin_print_help(parser, argv[0], stderr_buffer);
+ builtin_print_help(parser, streams, argv[0], streams.err);
return STATUS_BUILTIN_ERROR;
}
break;
}
default:
- append_format(stderr_buffer,
- _(L"%ls: Too many arguments\n"),
+ streams.err.append_format(_(L"%ls: Too many arguments\n"),
argv[0]);
- builtin_print_help(parser, argv[0], stderr_buffer);
+ builtin_print_help(parser, streams, argv[0], streams.err);
return STATUS_BUILTIN_ERROR;
}
@@ -3855,10 +3759,9 @@ static int builtin_return(parser_t &parser, wchar_t **argv)
if (function_block_idx >= parser.block_count())
{
- append_format(stderr_buffer,
- _(L"%ls: Not inside of function\n"),
+ streams.err.append_format(_(L"%ls: Not inside of function\n"),
argv[0]);
- builtin_print_help(parser, argv[0], stderr_buffer);
+ builtin_print_help(parser, streams, argv[0], streams.err);
return STATUS_BUILTIN_ERROR;
}
@@ -3875,7 +3778,7 @@ static int builtin_return(parser_t &parser, wchar_t **argv)
/**
History of commands executed by user
*/
-static int builtin_history(parser_t &parser, wchar_t **argv)
+static int builtin_history(parser_t &parser, io_streams_t &streams, wchar_t **argv)
{
int argc = builtin_count_args(argv);
@@ -3934,15 +3837,15 @@ static int builtin_history(parser_t &parser, wchar_t **argv)
merge_history = true;
break;
case 'h':
- builtin_print_help(parser, argv[0], stdout_buffer);
+ builtin_print_help(parser, streams, argv[0], streams.out);
return STATUS_BUILTIN_OK;
break;
case '?':
- append_format(stderr_buffer, BUILTIN_ERR_UNKNOWN, argv[0], argv[w.woptind-1]);
+ streams.err.append_format(BUILTIN_ERR_UNKNOWN, argv[0], argv[w.woptind-1]);
return STATUS_BUILTIN_ERROR;
break;
default:
- append_format(stderr_buffer, BUILTIN_ERR_UNKNOWN, argv[0], argv[w.woptind-1]);
+ streams.err.append_format(BUILTIN_ERR_UNKNOWN, argv[0], argv[w.woptind-1]);
return STATUS_BUILTIN_ERROR;
}
}
@@ -3954,8 +3857,8 @@ static int builtin_history(parser_t &parser, wchar_t **argv)
{
wcstring full_history;
history->get_string_representation(&full_history, wcstring(L"\n"));
- stdout_buffer.append(full_history);
- stdout_buffer.push_back('\n');
+ streams.out.append(full_history);
+ streams.out.push_back('\n');
return STATUS_BUILTIN_OK;
}
@@ -3972,15 +3875,15 @@ static int builtin_history(parser_t &parser, wchar_t **argv)
const wcstring &search_string = *iter;
if (search_string.empty())
{
- append_format(stderr_buffer, BUILTIN_ERR_COMBO2, argv[0], L"Use --search with either --contains or --prefix");
+ streams.err.append_format(BUILTIN_ERR_COMBO2, argv[0], L"Use --search with either --contains or --prefix");
return res;
}
history_search_t searcher = history_search_t(*history, search_string, search_prefix?HISTORY_SEARCH_TYPE_PREFIX:HISTORY_SEARCH_TYPE_CONTAINS);
while (searcher.go_backwards())
{
- stdout_buffer.append(searcher.current_string());
- stdout_buffer.append(L"\n");
+ streams.out.append(searcher.current_string());
+ streams.out.append(L"\n");
res = STATUS_BUILTIN_OK;
}
}
@@ -4016,7 +3919,7 @@ static int builtin_history(parser_t &parser, wchar_t **argv)
return STATUS_BUILTIN_ERROR;
}
-int builtin_parse(parser_t &parser, wchar_t **argv)
+int builtin_parse(parser_t &parser, io_streams_t &streams, wchar_t **argv)
{
struct sigaction act;
sigemptyset(& act.sa_mask);
@@ -4028,7 +3931,7 @@ int builtin_parse(parser_t &parser, wchar_t **argv)
for (;;)
{
char buff[256];
- ssize_t amt = read_loop(builtin_stdin, buff, sizeof buff);
+ ssize_t amt = read_loop(streams.stdin_fd, buff, sizeof buff);
if (amt <= 0) break;
txt.insert(txt.end(), buff, buff + amt);
}
@@ -4040,30 +3943,30 @@ int builtin_parse(parser_t &parser, wchar_t **argv)
bool success = parse_tree_from_string(src, parse_flag_include_comments, &parse_tree, &errors);
if (! success)
{
- stdout_buffer.append(L"Parsing failed:\n");
+ streams.out.append(L"Parsing failed:\n");
for (size_t i=0; i < errors.size(); i++)
{
- stdout_buffer.append(errors.at(i).describe(src));
- stdout_buffer.push_back(L'\n');
+ streams.out.append(errors.at(i).describe(src));
+ streams.out.push_back(L'\n');
}
- stdout_buffer.append(L"(Reparsed with continue after error)\n");
+ streams.out.append(L"(Reparsed with continue after error)\n");
parse_tree.clear();
errors.clear();
parse_tree_from_string(src, parse_flag_continue_after_error | parse_flag_include_comments, &parse_tree, &errors);
}
const wcstring dump = parse_dump_tree(parse_tree, src);
- stdout_buffer.append(dump);
+ streams.out.append(dump);
}
return STATUS_BUILTIN_OK;
}
-int builtin_true(parser_t &parser, wchar_t **argv)
+int builtin_true(parser_t &parser, io_streams_t &streams, wchar_t **argv)
{
return STATUS_BUILTIN_OK;
}
-int builtin_false(parser_t &parser, wchar_t **argv)
+int builtin_false(parser_t &parser, io_streams_t &streams, wchar_t **argv)
{
return STATUS_BUILTIN_ERROR;
}
@@ -4179,23 +4082,21 @@ static int internal_help(const wchar_t *cmd)
}
-int builtin_run(parser_t &parser, const wchar_t * const *argv, const io_chain_t &io)
+int builtin_run(parser_t &parser, const wchar_t * const *argv, io_streams_t &streams)
{
- int (*cmd)(parser_t &parser, const wchar_t * const *argv)=0;
+ int (*cmd)(parser_t &parser, io_streams_t &streams, const wchar_t * const *argv)=NULL;
- scoped_push<const io_chain_t*> set_real_io(&real_io, &io);
-
CHECK(argv, STATUS_BUILTIN_ERROR);
CHECK(argv[0], STATUS_BUILTIN_ERROR);
const builtin_data_t *data = builtin_lookup(argv[0]);
- cmd = (int (*)(parser_t &parser, const wchar_t * const*))(data ? data->func : NULL);
+ cmd = (int (*)(parser_t &parser, io_streams_t &streams, const wchar_t * const*))(data ? data->func : NULL);
if (argv[1] != 0 && !internal_help(argv[0]))
{
if (argv[2] == 0 && (parse_util_argument_is_help(argv[1], 0)))
{
- builtin_print_help(parser, argv[0], stdout_buffer);
+ builtin_print_help(parser, streams, argv[0], streams.out);
return STATUS_BUILTIN_OK;
}
}
@@ -4204,7 +4105,7 @@ int builtin_run(parser_t &parser, const wchar_t * const *argv, const io_chain_t
{
int status;
- status = cmd(parser, argv);
+ status = cmd(parser, streams, argv);
return status;
}
@@ -4247,36 +4148,3 @@ wcstring builtin_get_desc(const wcstring &name)
}
return result;
}
-
-void builtin_push_io(parser_t &parser, int in)
-{
- ASSERT_IS_MAIN_THREAD();
- if (builtin_stdin != -1)
- {
- struct io_stack_elem_t elem = {builtin_stdin, stdout_buffer, stderr_buffer};
- io_stack.push(elem);
- }
- builtin_stdin = in;
- stdout_buffer.clear();
- stderr_buffer.clear();
-}
-
-void builtin_pop_io(parser_t &parser)
-{
- ASSERT_IS_MAIN_THREAD();
- builtin_stdin = 0;
- if (! io_stack.empty())
- {
- struct io_stack_elem_t &elem = io_stack.top();
- stderr_buffer = elem.err;
- stdout_buffer = elem.out;
- builtin_stdin = elem.in;
- io_stack.pop();
- }
- else
- {
- stdout_buffer.clear();
- stderr_buffer.clear();
- builtin_stdin = 0;
- }
-}
diff --git a/src/builtin.h b/src/builtin.h
index 3a160195..859c4874 100644
--- a/src/builtin.h
+++ b/src/builtin.h
@@ -69,24 +69,6 @@ enum
#define BUILTIN_ERR_NOT_NUMBER _( L"%ls: Argument '%ls' is not a number\n" )
-/** Get the string used to represent stdout and stderr */
-const wcstring &get_stdout_buffer();
-const wcstring &get_stderr_buffer();
-
-/** Output an error */
-void builtin_show_error(const wcstring &err);
-
-/**
- Kludge. Tells builtins if output is to screen
-*/
-extern int builtin_out_redirect;
-
-/**
- Kludge. Tells builtins if error is to screen
-*/
-extern int builtin_err_redirect;
-
-
/**
Initialize builtin data.
*/
@@ -114,7 +96,7 @@ int builtin_exists(const wcstring &cmd);
\return the exit status of the builtin command
*/
-int builtin_run(parser_t &parser, const wchar_t * const *argv, const io_chain_t &io);
+int builtin_run(parser_t &parser, const wchar_t * const *argv, io_streams_t &streams);
/** Returns a list of all builtin names */
wcstring_list_t builtin_get_names();
@@ -123,17 +105,6 @@ wcstring_list_t builtin_get_names();
void builtin_get_names(std::vector<completion_t> *list);
/**
- Pushes a new set of input/output to the stack. The new stdin is supplied, a new set of output strings is created.
-*/
-void builtin_push_io(parser_t &parser, int stdin_fd);
-
-/**
- Pops a set of input/output from the stack. The output strings are destroued, but the input file is not closed.
-*/
-void builtin_pop_io(parser_t &parser);
-
-
-/**
Return a one-line description of the specified builtin.
*/
wcstring builtin_get_desc(const wcstring &b);
@@ -167,7 +138,7 @@ class builtin_commandline_scoped_transient_t
wcstring builtin_help_get(parser_t &parser, const wchar_t *cmd);
/** Defines a function, like builtin_function. Returns 0 on success. args should NOT contain 'function' as the first argument. */
-int define_function(parser_t &parser, const wcstring_list_t &args, const wcstring &contents, int definition_line_offset, wcstring *out_err);
+int define_function(parser_t &parser, io_streams_t &streams, const wcstring_list_t &c_args, const wcstring &contents, int definition_line_offset, wcstring *out_err);
#endif
diff --git a/src/builtin_commandline.cpp b/src/builtin_commandline.cpp
index 352baf1c..971ba208 100644
--- a/src/builtin_commandline.cpp
+++ b/src/builtin_commandline.cpp
@@ -186,7 +186,8 @@ static void replace_part(const wchar_t *begin,
static void write_part(const wchar_t *begin,
const wchar_t *end,
int cut_at_cursor,
- int tokenize)
+ int tokenize,
+ io_streams_t &streams)
{
size_t pos = get_cursor_pos()-(begin-get_buffer());
@@ -221,7 +222,7 @@ static void write_part(const wchar_t *begin,
}
}
- stdout_buffer.append(out);
+ streams.out.append(out);
free(buff);
}
@@ -235,8 +236,8 @@ static void write_part(const wchar_t *begin,
// debug( 0, L"woot2 %ls -> %ls", buff, esc );
wcstring tmp = wcstring(begin, end - begin);
unescape_string_in_place(&tmp, UNESCAPE_INCOMPLETE);
- stdout_buffer.append(tmp);
- stdout_buffer.append(L"\n");
+ streams.out.append(tmp);
+ streams.out.append(L"\n");
}
}
@@ -246,7 +247,7 @@ static void write_part(const wchar_t *begin,
The commandline builtin. It is used for specifying a new value for
the commandline.
*/
-static int builtin_commandline(parser_t &parser, wchar_t **argv)
+static int builtin_commandline(parser_t &parser, io_streams_t &streams, wchar_t **argv)
{
wgetopter_t w;
int buffer_part=0;
@@ -293,9 +294,9 @@ static int builtin_commandline(parser_t &parser, wchar_t **argv)
return 1;
}
- stderr_buffer.append(argv[0]);
- stderr_buffer.append(L": Can not set commandline in non-interactive mode\n");
- builtin_print_help(parser, argv[0], stderr_buffer);
+ streams.err.append(argv[0]);
+ streams.err.append(L": Can not set commandline in non-interactive mode\n");
+ builtin_print_help(parser, streams, argv[0], streams.err);
return 1;
}
@@ -341,11 +342,10 @@ static int builtin_commandline(parser_t &parser, wchar_t **argv)
case 0:
if (long_options[opt_index].flag != 0)
break;
- append_format(stderr_buffer,
- BUILTIN_ERR_UNKNOWN,
+ streams.err.append_format(BUILTIN_ERR_UNKNOWN,
argv[0],
long_options[opt_index].name);
- builtin_print_help(parser, argv[0], stderr_buffer);
+ builtin_print_help(parser, streams, argv[0], streams.err);
return 1;
@@ -416,11 +416,11 @@ static int builtin_commandline(parser_t &parser, wchar_t **argv)
break;
case 'h':
- builtin_print_help(parser, argv[0], stdout_buffer);
+ builtin_print_help(parser, streams, argv[0], streams.out);
return 0;
case L'?':
- builtin_unknown_option(parser, argv[0], argv[w.woptind-1]);
+ builtin_unknown_option(parser, streams, argv[0], argv[w.woptind-1]);
return 1;
}
}
@@ -434,22 +434,20 @@ static int builtin_commandline(parser_t &parser, wchar_t **argv)
*/
if (buffer_part || cut_at_cursor || append_mode || tokenize || cursor_mode || line_mode || search_mode || paging_mode)
{
- append_format(stderr_buffer,
- BUILTIN_ERR_COMBO,
+ streams.err.append_format(BUILTIN_ERR_COMBO,
argv[0]);
- builtin_print_help(parser, argv[0], stderr_buffer);
+ builtin_print_help(parser, streams, argv[0], streams.err);
return 1;
}
if (argc == w.woptind)
{
- append_format(stderr_buffer,
- BUILTIN_ERR_MISSING,
+ streams.err.append_format(BUILTIN_ERR_MISSING,
argv[0]);
- builtin_print_help(parser, argv[0], stderr_buffer);
+ builtin_print_help(parser, streams, argv[0], streams.err);
return 1;
}
for (i=w.woptind; i<argc; i++)
@@ -466,11 +464,10 @@ static int builtin_commandline(parser_t &parser, wchar_t **argv)
}
else
{
- append_format(stderr_buffer,
- _(L"%ls: Unknown input function '%ls'\n"),
+ streams.err.append_format(_(L"%ls: Unknown input function '%ls'\n"),
argv[0],
argv[i]);
- builtin_print_help(parser, argv[0], stderr_buffer);
+ builtin_print_help(parser, streams, argv[0], streams.err);
return 1;
}
}
@@ -484,7 +481,7 @@ static int builtin_commandline(parser_t &parser, wchar_t **argv)
const wchar_t *buffer = reader_get_buffer();
if (reader_get_selection(&start, &len))
{
- stdout_buffer.append(buffer + start, len);
+ streams.out.append(buffer + start, len);
}
return 0;
}
@@ -495,45 +492,41 @@ static int builtin_commandline(parser_t &parser, wchar_t **argv)
if ((search_mode || line_mode || cursor_mode || paging_mode) && (argc-w.woptind > 1))
{
- append_format(stderr_buffer,
- argv[0],
+ streams.err.append_format(argv[0],
L": Too many arguments\n",
NULL);
- builtin_print_help(parser, argv[0], stderr_buffer);
+ builtin_print_help(parser, streams, argv[0], streams.err);
return 1;
}
if ((buffer_part || tokenize || cut_at_cursor) && (cursor_mode || line_mode || search_mode || paging_mode))
{
- append_format(stderr_buffer,
- BUILTIN_ERR_COMBO,
+ streams.err.append_format(BUILTIN_ERR_COMBO,
argv[0]);
- builtin_print_help(parser, argv[0], stderr_buffer);
+ builtin_print_help(parser, streams, argv[0], streams.err);
return 1;
}
if ((tokenize || cut_at_cursor) && (argc-w.woptind))
{
- append_format(stderr_buffer,
- BUILTIN_ERR_COMBO2,
+ streams.err.append_format(BUILTIN_ERR_COMBO2,
argv[0],
L"--cut-at-cursor and --tokenize can not be used when setting the commandline");
- builtin_print_help(parser, argv[0], stderr_buffer);
+ builtin_print_help(parser, streams, argv[0], streams.err);
return 1;
}
if (append_mode && !(argc-w.woptind))
{
- append_format(stderr_buffer,
- BUILTIN_ERR_COMBO2,
+ streams.err.append_format(BUILTIN_ERR_COMBO2,
argv[0],
L"insertion mode switches can not be used when not in insertion mode");
- builtin_print_help(parser, argv[0], stderr_buffer);
+ builtin_print_help(parser, streams, argv[0], streams.err);
return 1;
}
@@ -561,11 +554,10 @@ static int builtin_commandline(parser_t &parser, wchar_t **argv)
new_pos = wcstol(argv[w.woptind], &endptr, 10);
if (*endptr || errno)
{
- append_format(stderr_buffer,
- BUILTIN_ERR_NOT_NUMBER,
+ streams.err.append_format(BUILTIN_ERR_NOT_NUMBER,
argv[0],
argv[w.woptind]);
- builtin_print_help(parser, argv[0], stderr_buffer);
+ builtin_print_help(parser, streams, argv[0], streams.err);
}
current_buffer = reader_get_buffer();
@@ -575,7 +567,7 @@ static int builtin_commandline(parser_t &parser, wchar_t **argv)
}
else
{
- append_format(stdout_buffer, L"%lu\n", (unsigned long)reader_get_cursor_pos());
+ streams.out.append_format( L"%lu\n", (unsigned long)reader_get_cursor_pos());
return 0;
}
@@ -585,7 +577,7 @@ static int builtin_commandline(parser_t &parser, wchar_t **argv)
{
size_t pos = reader_get_cursor_pos();
const wchar_t *buff = reader_get_buffer();
- append_format(stdout_buffer, L"%lu\n", (unsigned long)parse_util_lineno(buff, pos));
+ streams.out.append_format( L"%lu\n", (unsigned long)parse_util_lineno(buff, pos));
return 0;
}
@@ -644,7 +636,7 @@ static int builtin_commandline(parser_t &parser, wchar_t **argv)
{
case 0:
{
- write_part(begin, end, cut_at_cursor, tokenize);
+ write_part(begin, end, cut_at_cursor, tokenize, streams);
break;
}
diff --git a/src/builtin_complete.cpp b/src/builtin_complete.cpp
index e206f9ab..e8dd2d9d 100644
--- a/src/builtin_complete.cpp
+++ b/src/builtin_complete.cpp
@@ -284,7 +284,7 @@ static void builtin_complete_remove(const wcstring_list_t &cmd,
tab-completions. Calls the functions in complete.c for any heavy
lifting. Defined in builtin_complete.c
*/
-static int builtin_complete(parser_t &parser, wchar_t **argv)
+static int builtin_complete(parser_t &parser, io_streams_t &streams, wchar_t **argv)
{
ASSERT_IS_MAIN_THREAD();
wgetopter_t w;
@@ -351,11 +351,10 @@ static int builtin_complete(parser_t &parser, wchar_t **argv)
case 0:
if (long_options[opt_index].flag != 0)
break;
- append_format(stderr_buffer,
- BUILTIN_ERR_UNKNOWN,
+ streams.err.append_format(BUILTIN_ERR_UNKNOWN,
argv[0],
long_options[opt_index].name);
- builtin_print_help(parser, argv[0], stderr_buffer);
+ builtin_print_help(parser, streams, argv[0], streams.err);
res = true;
@@ -386,7 +385,7 @@ static int builtin_complete(parser_t &parser, wchar_t **argv)
}
else
{
- append_format(stderr_buffer, L"%ls: Invalid token '%ls'\n", argv[0], w.woptarg);
+ streams.err.append_format(L"%ls: Invalid token '%ls'\n", argv[0], w.woptarg);
res = true;
}
break;
@@ -440,7 +439,7 @@ static int builtin_complete(parser_t &parser, wchar_t **argv)
{
// This corresponds to using 'complete -C' in non-interactive mode
// See #2361
- builtin_missing_argument(parser, argv[0], argv[w.woptind-1]);
+ builtin_missing_argument(parser, streams, argv[0], argv[w.woptind-1]);
return STATUS_BUILTIN_ERROR;
}
do_complete_param = arg;
@@ -448,11 +447,11 @@ static int builtin_complete(parser_t &parser, wchar_t **argv)
}
case 'h':
- builtin_print_help(parser, argv[0], stdout_buffer);
+ builtin_print_help(parser, streams, argv[0], streams.out);
return 0;
case '?':
- builtin_unknown_option(parser, argv[0], argv[w.woptind-1]);
+ builtin_unknown_option(parser, streams, argv[0], argv[w.woptind-1]);
res = true;
break;
@@ -468,14 +467,13 @@ static int builtin_complete(parser_t &parser, wchar_t **argv)
parse_error_list_t errors;
if (parse_util_detect_errors(condition_string, &errors, false /* do not accept incomplete */))
{
- append_format(stderr_buffer,
- L"%ls: Condition '%ls' contained a syntax error",
+ streams.err.append_format(L"%ls: Condition '%ls' contained a syntax error",
argv[0],
condition);
for (size_t i=0; i < errors.size(); i++)
{
- append_format(stderr_buffer, L"\n%s: ", argv[0]);
- stderr_buffer.append(errors.at(i).describe(condition_string));
+ streams.err.append_format(L"\n%s: ", argv[0]);
+ streams.err.append(errors.at(i).describe(condition_string));
}
res = true;
}
@@ -496,12 +494,11 @@ static int builtin_complete(parser_t &parser, wchar_t **argv)
wcstring err_text;
if (parser.detect_errors_in_argument_list(comp, &err_text, prefix.c_str()))
{
- append_format(stderr_buffer,
- L"%ls: Completion '%ls' contained a syntax error\n",
+ streams.err.append_format(L"%ls: Completion '%ls' contained a syntax error\n",
argv[0],
comp);
- stderr_buffer.append(err_text);
- stderr_buffer.push_back(L'\n');
+ streams.err.append(err_text);
+ streams.err.push_back(L'\n');
res = true;
}
}
@@ -542,15 +539,15 @@ static int builtin_complete(parser_t &parser, wchar_t **argv)
/* The input data is meant to be something like you would have on the command line, e.g. includes backslashes. The output should be raw, i.e. unescaped. So we need to unescape the command line. See #1127 */
unescape_string_in_place(&faux_cmdline_with_completion, UNESCAPE_DEFAULT);
- stdout_buffer.append(faux_cmdline_with_completion);
+ streams.out.append(faux_cmdline_with_completion);
/* Append any description */
if (! next.description.empty())
{
- stdout_buffer.push_back(L'\t');
- stdout_buffer.append(next.description);
+ streams.out.push_back(L'\t');
+ streams.out.append(next.description);
}
- stdout_buffer.push_back(L'\n');
+ streams.out.push_back(L'\n');
}
recursion_level--;
@@ -558,10 +555,9 @@ static int builtin_complete(parser_t &parser, wchar_t **argv)
}
else if (w.woptind != argc)
{
- append_format(stderr_buffer,
- _(L"%ls: Too many arguments\n"),
+ streams.err.append_format(_(L"%ls: Too many arguments\n"),
argv[0]);
- builtin_print_help(parser, argv[0], stderr_buffer);
+ builtin_print_help(parser, streams, argv[0], streams.err);
res = true;
}
@@ -569,7 +565,7 @@ static int builtin_complete(parser_t &parser, wchar_t **argv)
{
/* No arguments specified, meaning we print the definitions of
* all specified completions to stdout.*/
- complete_print(stdout_buffer);
+ streams.out.append(complete_print());
}
else
{
diff --git a/src/builtin_jobs.cpp b/src/builtin_jobs.cpp
index 32079287..6f4b57a8 100644
--- a/src/builtin_jobs.cpp
+++ b/src/builtin_jobs.cpp
@@ -71,7 +71,7 @@ static int cpu_use(const job_t *j)
/**
Print information about the specified job
*/
-static void builtin_jobs_print(const job_t *j, int mode, int header)
+static void builtin_jobs_print(const job_t *j, int mode, int header, io_streams_t &streams)
{
process_t *p;
switch (mode)
@@ -84,22 +84,22 @@ static void builtin_jobs_print(const job_t *j, int mode, int header)
/*
Print table header before first job
*/
- stdout_buffer.append(_(L"Job\tGroup\t"));
+ streams.out.append(_(L"Job\tGroup\t"));
#ifdef HAVE__PROC_SELF_STAT
- stdout_buffer.append(_(L"CPU\t"));
+ streams.out.append(_(L"CPU\t"));
#endif
- stdout_buffer.append(_(L"State\tCommand\n"));
+ streams.out.append(_(L"State\tCommand\n"));
}
- append_format(stdout_buffer, L"%d\t%d\t", j->job_id, j->pgid);
+ streams.out.append_format( L"%d\t%d\t", j->job_id, j->pgid);
#ifdef HAVE__PROC_SELF_STAT
- append_format(stdout_buffer, L"%d%%\t", cpu_use(j));
+ streams.out.append_format( L"%d%%\t", cpu_use(j));
#endif
- stdout_buffer.append(job_is_stopped(j)?_(L"stopped"):_(L"running"));
- stdout_buffer.append(L"\t");
- stdout_buffer.append(j->command_wcstr());
- stdout_buffer.append(L"\n");
+ streams.out.append(job_is_stopped(j)?_(L"stopped"):_(L"running"));
+ streams.out.append(L"\t");
+ streams.out.append(j->command_wcstr());
+ streams.out.append(L"\n");
break;
}
@@ -110,9 +110,9 @@ static void builtin_jobs_print(const job_t *j, int mode, int header)
/*
Print table header before first job
*/
- stdout_buffer.append(_(L"Group\n"));
+ streams.out.append(_(L"Group\n"));
}
- append_format(stdout_buffer, L"%d\n", j->pgid);
+ streams.out.append_format( L"%d\n", j->pgid);
break;
}
@@ -123,12 +123,12 @@ static void builtin_jobs_print(const job_t *j, int mode, int header)
/*
Print table header before first job
*/
- stdout_buffer.append(_(L"Process\n"));
+ streams.out.append(_(L"Process\n"));
}
for (p=j->first_process; p; p=p->next)
{
- append_format(stdout_buffer, L"%d\n", p->pid);
+ streams.out.append_format( L"%d\n", p->pid);
}
break;
}
@@ -140,12 +140,12 @@ static void builtin_jobs_print(const job_t *j, int mode, int header)
/*
Print table header before first job
*/
- stdout_buffer.append(_(L"Command\n"));
+ streams.out.append(_(L"Command\n"));
}
for (p=j->first_process; p; p=p->next)
{
- append_format(stdout_buffer, L"%ls\n", p->argv0());
+ streams.out.append_format( L"%ls\n", p->argv0());
}
break;
}
@@ -158,7 +158,7 @@ static void builtin_jobs_print(const job_t *j, int mode, int header)
/**
The jobs builtin. Used fopr printing running jobs. Defined in builtin_jobs.c.
*/
-static int builtin_jobs(parser_t &parser, wchar_t **argv)
+static int builtin_jobs(parser_t &parser, io_streams_t &streams, wchar_t **argv)
{
wgetopter_t w;
int argc=0;
@@ -215,12 +215,11 @@ static int builtin_jobs(parser_t &parser, wchar_t **argv)
case 0:
if (long_options[opt_index].flag != 0)
break;
- append_format(stderr_buffer,
- BUILTIN_ERR_UNKNOWN,
+ streams.err.append_format(BUILTIN_ERR_UNKNOWN,
argv[0],
long_options[opt_index].name);
- builtin_print_help(parser, argv[0], stderr_buffer);
+ builtin_print_help(parser, streams, argv[0], streams.err);
return 1;
@@ -245,11 +244,11 @@ static int builtin_jobs(parser_t &parser, wchar_t **argv)
}
case 'h':
- builtin_print_help(parser, argv[0], stdout_buffer);
+ builtin_print_help(parser, streams, argv[0], streams.out);
return 0;
case '?':
- builtin_unknown_option(parser, argv[0], argv[w.woptind-1]);
+ builtin_unknown_option(parser, streams, argv[0], argv[w.woptind-1]);
return 1;
}
@@ -259,7 +258,7 @@ static int builtin_jobs(parser_t &parser, wchar_t **argv)
/*
Do not babble if not interactive
*/
- if (builtin_out_redirect)
+ if (streams.out_is_redirected)
{
found=1;
}
@@ -276,7 +275,7 @@ static int builtin_jobs(parser_t &parser, wchar_t **argv)
if ((j->flags & JOB_CONSTRUCTED) && !job_is_completed(j))
{
- builtin_jobs_print(j, mode, !found);
+ builtin_jobs_print(j, mode, !found, streams);
return 0;
}
}
@@ -298,8 +297,7 @@ static int builtin_jobs(parser_t &parser, wchar_t **argv)
pid=fish_wcstoi(argv[i], &end, 10);
if (errno || *end)
{
- append_format(stderr_buffer,
- _(L"%ls: '%ls' is not a job\n"),
+ streams.err.append_format(_(L"%ls: '%ls' is not a job\n"),
argv[0],
argv[i]);
return 1;
@@ -309,12 +307,11 @@ static int builtin_jobs(parser_t &parser, wchar_t **argv)
if (j && !job_is_completed(j))
{
- builtin_jobs_print(j, mode, !found);
+ builtin_jobs_print(j, mode, !found, streams);
}
else
{
- append_format(stderr_buffer,
- _(L"%ls: No suitable job: %d\n"),
+ streams.err.append_format(_(L"%ls: No suitable job: %d\n"),
argv[0],
pid);
return 1;
@@ -332,7 +329,7 @@ static int builtin_jobs(parser_t &parser, wchar_t **argv)
*/
if ((j->flags & JOB_CONSTRUCTED) && !job_is_completed(j))
{
- builtin_jobs_print(j, mode, !found);
+ builtin_jobs_print(j, mode, !found, streams);
found = 1;
}
}
@@ -341,7 +338,7 @@ static int builtin_jobs(parser_t &parser, wchar_t **argv)
if (!found)
{
- append_format(stdout_buffer,
+ streams.out.append_format(
_(L"%ls: There are no jobs\n"),
argv[0]);
return 1;
diff --git a/src/builtin_printf.cpp b/src/builtin_printf.cpp
index 916166b0..b2bcfc7c 100644
--- a/src/builtin_printf.cpp
+++ b/src/builtin_printf.cpp
@@ -57,13 +57,16 @@
struct builtin_printf_state_t
{
+ /* Out and err streams. Note this is a captured reference! */
+ io_streams_t &streams;
+
/* The status of the operation */
int exit_code;
/* Whether we should stop outputting. This gets set in the case of an error, and also with the \c escape. */
bool early_exit;
- builtin_printf_state_t() : exit_code(0), early_exit(false)
+ builtin_printf_state_t(io_streams_t &s) : streams(s), exit_code(0), early_exit(false)
{
}
@@ -205,9 +208,9 @@ void builtin_printf_state_t::fatal_error(const wchar_t *fmt, ...)
va_start(va, fmt);
wcstring errstr = vformat_string(fmt, va);
va_end(va);
- stderr_buffer.append(errstr);
+ streams.err.append(errstr);
if (! string_suffixes_string(L"\n", errstr))
- stderr_buffer.push_back(L'\n');
+ streams.err.push_back(L'\n');
this->exit_code = STATUS_BUILTIN_ERROR;
this->early_exit = true;
@@ -219,7 +222,7 @@ void builtin_printf_state_t::append_output(wchar_t c)
if (early_exit)
return;
- stdout_buffer.push_back(c);
+ streams.out.push_back(c);
}
void builtin_printf_state_t::append_output(const wchar_t *c)
@@ -228,7 +231,7 @@ void builtin_printf_state_t::append_output(const wchar_t *c)
if (early_exit)
return;
- stdout_buffer.append(c);
+ streams.out.append(c);
}
void builtin_printf_state_t::append_format_output(const wchar_t *fmt, ...)
@@ -239,8 +242,9 @@ void builtin_printf_state_t::append_format_output(const wchar_t *fmt, ...)
va_list va;
va_start(va, fmt);
- append_formatv(stdout_buffer, fmt, va);
+ wcstring tmp = vformat_string(fmt, va);
va_end(va);
+ streams.out.append(tmp);
}
@@ -758,9 +762,9 @@ no_more_flag_characters:
return save_argc - argc;
}
-static int builtin_printf(parser_t &parser, wchar_t **argv)
+static int builtin_printf(parser_t &parser, io_streams_t &streams, wchar_t **argv)
{
- builtin_printf_state_t state;
+ builtin_printf_state_t state(streams);
wchar_t *format;
int args_used;
diff --git a/src/builtin_set.cpp b/src/builtin_set.cpp
index c7bde4fb..c3cde791 100644
--- a/src/builtin_set.cpp
+++ b/src/builtin_set.cpp
@@ -26,9 +26,6 @@ Functions used for implementing the set builtin.
#include "proc.h"
#include "parser.h"
-/* We know about these buffers */
-extern wcstring stdout_buffer, stderr_buffer;
-
/**
Error message for invalid path operations
*/
@@ -56,7 +53,7 @@ static int is_path_variable(const wchar_t *env)
Call env_set. If this is a path variable, e.g. PATH, validate the
elements. On error, print a description of the problem to stderr.
*/
-static int my_env_set(const wchar_t *key, const wcstring_list_t &val, int scope)
+static int my_env_set(const wchar_t *key, const wcstring_list_t &val, int scope, io_streams_t &streams)
{
size_t i;
int retcode = 0;
@@ -104,7 +101,7 @@ static int my_env_set(const wchar_t *key, const wcstring_list_t &val, int scope)
}
else
{
- append_format(stderr_buffer, _(BUILTIN_SET_PATH_ERROR), L"set", dir.c_str(), key);
+ streams.err.append_format(_(BUILTIN_SET_PATH_ERROR), L"set", dir.c_str(), key);
const wchar_t *colon = wcschr(dir.c_str(), L':');
if (colon && *(colon+1))
@@ -116,12 +113,12 @@ static int my_env_set(const wchar_t *key, const wcstring_list_t &val, int scope)
if (show_perror)
{
- builtin_wperror(L"set");
+ builtin_wperror(L"set", streams);
}
if (show_hint)
{
- append_format(stderr_buffer, _(BUILTIN_SET_PATH_HINT), L"set", key, key, wcschr(dir.c_str(), L':')+1);
+ streams.err.append_format(_(BUILTIN_SET_PATH_HINT), L"set", key, key, wcschr(dir.c_str(), L':')+1);
}
}
@@ -152,21 +149,21 @@ static int my_env_set(const wchar_t *key, const wcstring_list_t &val, int scope)
{
case ENV_PERM:
{
- append_format(stderr_buffer, _(L"%ls: Tried to change the read-only variable '%ls'\n"), L"set", key);
+ streams.err.append_format(_(L"%ls: Tried to change the read-only variable '%ls'\n"), L"set", key);
retcode=1;
break;
}
case ENV_SCOPE:
{
- append_format(stderr_buffer, _(L"%ls: Tried to set the special variable '%ls' with the wrong scope\n"), L"set", key);
+ streams.err.append_format(_(L"%ls: Tried to set the special variable '%ls' with the wrong scope\n"), L"set", key);
retcode=1;
break;
}
case ENV_INVALID:
{
- append_format(stderr_buffer, _(L"%ls: Tried to set the special variable '%ls' to an invalid value\n"), L"set", key);
+ streams.err.append_format(_(L"%ls: Tried to set the special variable '%ls' to an invalid value\n"), L"set", key);
retcode=1;
break;
}
@@ -190,7 +187,8 @@ static int my_env_set(const wchar_t *key, const wcstring_list_t &val, int scope)
static int parse_index(std::vector<long> &indexes,
const wchar_t *src,
const wchar_t *name,
- size_t var_count)
+ size_t var_count,
+ io_streams_t &streams)
{
size_t len;
@@ -209,7 +207,7 @@ static int parse_index(std::vector<long> &indexes,
if (*src != L'[')
{
- append_format(stderr_buffer, _(BUILTIN_SET_ARG_COUNT), L"set");
+ streams.err.append_format(_(BUILTIN_SET_ARG_COUNT), L"set");
return 0;
}
@@ -217,8 +215,7 @@ static int parse_index(std::vector<long> &indexes,
if ((wcsncmp(src_orig, name, len)!=0) || (wcslen(name) != (len)))
{
- append_format(stderr_buffer,
- _(L"%ls: Multiple variable names specified in single call (%ls and %.*ls)\n"),
+ streams.err.append_format(_(L"%ls: Multiple variable names specified in single call (%ls and %.*ls)\n"),
L"set",
name,
len,
@@ -245,7 +242,7 @@ static int parse_index(std::vector<long> &indexes,
if (end==src || errno)
{
- append_format(stderr_buffer, _(L"%ls: Invalid index starting at '%ls'\n"), L"set", src);
+ streams.err.append_format(_(L"%ls: Invalid index starting at '%ls'\n"), L"set", src);
return 0;
}
@@ -346,7 +343,7 @@ static void erase_values(wcstring_list_t &list, const std::vector<long> &indexes
Print the names of all environment variables in the scope, with or without shortening,
with or without values, with or without escaping
*/
-static void print_variables(int include_values, int esc, bool shorten_ok, int scope)
+static void print_variables(int include_values, int esc, bool shorten_ok, int scope, io_streams_t &streams)
{
wcstring_list_t names = env_get_names(scope);
sort(names.begin(), names.end());
@@ -356,7 +353,7 @@ static void print_variables(int include_values, int esc, bool shorten_ok, int sc
const wcstring key = names.at(i);
const wcstring e_key = escape_string(key, 0);
- stdout_buffer.append(e_key);
+ streams.out.append(e_key);
if (include_values)
{
@@ -373,18 +370,18 @@ static void print_variables(int include_values, int esc, bool shorten_ok, int sc
wcstring e_value = esc ? expand_escape_variable(value) : value;
- stdout_buffer.append(L" ");
- stdout_buffer.append(e_value);
+ streams.out.append(L" ");
+ streams.out.append(e_value);
if (shorten)
{
- stdout_buffer.push_back(ellipsis_char);
+ streams.out.push_back(ellipsis_char);
}
}
}
- stdout_buffer.append(L"\n");
+ streams.out.append(L"\n");
}
}
@@ -394,7 +391,7 @@ static void print_variables(int include_values, int esc, bool shorten_ok, int sc
The set builtin. Creates, updates and erases environment variables
and environemnt variable arrays.
*/
-static int builtin_set(parser_t &parser, wchar_t **argv)
+static int builtin_set(parser_t &parser, io_streams_t &streams, wchar_t **argv)
{
wgetopter_t w;
/** Variables used for parsing the argument list */
@@ -495,11 +492,11 @@ static int builtin_set(parser_t &parser, wchar_t **argv)
break;
case 'h':
- builtin_print_help(parser, argv[0], stdout_buffer);
+ builtin_print_help(parser, streams, argv[0], streams.out);
return 0;
case '?':
- builtin_unknown_option(parser, argv[0], argv[w.woptind-1]);
+ builtin_unknown_option(parser, streams, argv[0], argv[w.woptind-1]);
return 1;
default:
@@ -518,11 +515,10 @@ static int builtin_set(parser_t &parser, wchar_t **argv)
if (query && (erase || list))
{
- append_format(stderr_buffer,
- BUILTIN_ERR_COMBO,
+ streams.err.append_format(BUILTIN_ERR_COMBO,
argv[0]);
- builtin_print_help(parser, argv[0], stderr_buffer);
+ builtin_print_help(parser, streams, argv[0], streams.err);
return 1;
}
@@ -530,11 +526,10 @@ static int builtin_set(parser_t &parser, wchar_t **argv)
/* We can't both list and erase variables */
if (erase && list)
{
- append_format(stderr_buffer,
- BUILTIN_ERR_COMBO,
+ streams.err.append_format(BUILTIN_ERR_COMBO,
argv[0]);
- builtin_print_help(parser, argv[0], stderr_buffer);
+ builtin_print_help(parser, streams, argv[0], streams.err);
return 1;
}
@@ -543,10 +538,9 @@ static int builtin_set(parser_t &parser, wchar_t **argv)
*/
if (local + global + universal > 1)
{
- append_format(stderr_buffer,
- BUILTIN_ERR_GLOCAL,
+ streams.err.append_format(BUILTIN_ERR_GLOCAL,
argv[0]);
- builtin_print_help(parser, argv[0], stderr_buffer);
+ builtin_print_help(parser, streams, argv[0], streams.err);
return 1;
}
@@ -555,10 +549,9 @@ static int builtin_set(parser_t &parser, wchar_t **argv)
*/
if (exportv && unexport)
{
- append_format(stderr_buffer,
- BUILTIN_ERR_EXPUNEXP,
+ streams.err.append_format(BUILTIN_ERR_EXPUNEXP,
argv[0]);
- builtin_print_help(parser, argv[0], stderr_buffer);
+ builtin_print_help(parser, streams, argv[0], streams.err);
return 1;
}
@@ -600,9 +593,9 @@ static int builtin_set(parser_t &parser, wchar_t **argv)
if (! dest_str.missing())
tokenize_variable_array(dest_str, result);
- if (!parse_index(indexes, arg, dest, result.size()))
+ if (!parse_index(indexes, arg, dest, result.size(), streams))
{
- builtin_print_help(parser, argv[0], stderr_buffer);
+ builtin_print_help(parser, streams, argv[0], streams.err);
retcode = 1;
break;
}
@@ -632,7 +625,7 @@ static int builtin_set(parser_t &parser, wchar_t **argv)
if (list)
{
/* Maybe we should issue an error if there are any other arguments? */
- print_variables(0, 0, shorten_ok, scope);
+ print_variables(0, 0, shorten_ok, scope, streams);
return 0;
}
@@ -644,16 +637,15 @@ static int builtin_set(parser_t &parser, wchar_t **argv)
if (erase)
{
- append_format(stderr_buffer,
- _(L"%ls: Erase needs a variable name\n"),
+ streams.err.append_format(_(L"%ls: Erase needs a variable name\n"),
argv[0]);
- builtin_print_help(parser, argv[0], stderr_buffer);
+ builtin_print_help(parser, streams, argv[0], streams.err);
retcode = 1;
}
else
{
- print_variables(1, 1, shorten_ok, scope);
+ print_variables(1, 1, shorten_ok, scope, streams);
}
return retcode;
@@ -673,15 +665,15 @@ static int builtin_set(parser_t &parser, wchar_t **argv)
if (!wcslen(dest))
{
free(dest);
- append_format(stderr_buffer, BUILTIN_ERR_VARNAME_ZERO, argv[0]);
- builtin_print_help(parser, argv[0], stderr_buffer);
+ streams.err.append_format(BUILTIN_ERR_VARNAME_ZERO, argv[0]);
+ builtin_print_help(parser, streams, argv[0], streams.err);
return 1;
}
if ((bad_char = wcsvarname(dest)))
{
- append_format(stderr_buffer, BUILTIN_ERR_VARCHAR, argv[0], *bad_char);
- builtin_print_help(parser, argv[0], stderr_buffer);
+ streams.err.append_format(BUILTIN_ERR_VARCHAR, argv[0], *bad_char);
+ builtin_print_help(parser, streams, argv[0], streams.err);
free(dest);
return 1;
}
@@ -714,9 +706,9 @@ static int builtin_set(parser_t &parser, wchar_t **argv)
{
for (; w.woptind<argc; w.woptind++)
{
- if (!parse_index(indexes, argv[w.woptind], dest, result.size()))
+ if (!parse_index(indexes, argv[w.woptind], dest, result.size(), streams))
{
- builtin_print_help(parser, argv[0], stderr_buffer);
+ builtin_print_help(parser, streams, argv[0], streams.err);
retcode = 1;
break;
}
@@ -728,8 +720,8 @@ static int builtin_set(parser_t &parser, wchar_t **argv)
{
if (val_count < idx_count)
{
- append_format(stderr_buffer, _(BUILTIN_SET_ARG_COUNT), argv[0]);
- builtin_print_help(parser, argv[0], stderr_buffer);
+ streams.err.append_format(_(BUILTIN_SET_ARG_COUNT), argv[0]);
+ builtin_print_help(parser, streams, argv[0], streams.err);
retcode=1;
break;
}
@@ -751,7 +743,7 @@ static int builtin_set(parser_t &parser, wchar_t **argv)
if (erase)
{
erase_values(result, indexes);
- my_env_set(dest, result, scope);
+ my_env_set(dest, result, scope, streams);
}
else
{
@@ -766,12 +758,12 @@ static int builtin_set(parser_t &parser, wchar_t **argv)
indexes,
value))
{
- append_format(stderr_buffer, L"%ls: ", argv[0]);
- append_format(stderr_buffer, ARRAY_BOUNDS_ERR);
- stderr_buffer.push_back(L'\n');
+ streams.err.append_format(L"%ls: ", argv[0]);
+ streams.err.append_format(ARRAY_BOUNDS_ERR);
+ streams.err.push_back(L'\n');
}
- my_env_set(dest, result, scope);
+ my_env_set(dest, result, scope, streams);
}
@@ -788,10 +780,9 @@ static int builtin_set(parser_t &parser, wchar_t **argv)
{
if (w.woptind != argc)
{
- append_format(stderr_buffer,
- _(L"%ls: Values cannot be specfied with erase\n"),
+ streams.err.append_format(_(L"%ls: Values cannot be specfied with erase\n"),
argv[0]);
- builtin_print_help(parser, argv[0], stderr_buffer);
+ builtin_print_help(parser, streams, argv[0], streams.err);
retcode=1;
}
else
@@ -804,7 +795,7 @@ static int builtin_set(parser_t &parser, wchar_t **argv)
wcstring_list_t val;
for (i=w.woptind; i<argc; i++)
val.push_back(argv[i]);
- retcode = my_env_set(dest, val, scope);
+ retcode = my_env_set(dest, val, scope, streams);
}
}
@@ -815,7 +806,7 @@ static int builtin_set(parser_t &parser, wchar_t **argv)
env_var_t global_dest = env_get_string(dest, ENV_GLOBAL);
if (universal && ! global_dest.missing())
{
- append_format(stderr_buffer, _(L"%ls: Warning: universal scope selected, but a global variable '%ls' exists.\n"), L"set", dest);
+ streams.err.append_format(_(L"%ls: Warning: universal scope selected, but a global variable '%ls' exists.\n"), L"set", dest);
}
free(dest);
diff --git a/src/builtin_set_color.cpp b/src/builtin_set_color.cpp
index 7f400543..b3c7ebaf 100644
--- a/src/builtin_set_color.cpp
+++ b/src/builtin_set_color.cpp
@@ -24,9 +24,6 @@ Functions used for implementing the set_color builtin.
#endif
-/* We know about these buffers */
-extern wcstring stdout_buffer, stderr_buffer;
-
/**
Error message for invalid path operations
*/
@@ -42,14 +39,14 @@ extern wcstring stdout_buffer, stderr_buffer;
*/
#define BUILTIN_SET_ARG_COUNT L"%ls: The number of variable indexes does not match the number of values\n"
-static void print_colors(void)
+static void print_colors(io_streams_t &streams)
{
const wcstring_list_t result = rgb_color_t::named_color_names();
size_t i;
for (i=0; i < result.size(); i++)
{
- stdout_buffer.append(result.at(i));
- stdout_buffer.push_back(L'\n');
+ streams.out.append(result.at(i));
+ streams.out.push_back(L'\n');
}
}
@@ -65,7 +62,7 @@ static int set_color_builtin_outputter(char c)
/**
set_color builtin
*/
-static int builtin_set_color(parser_t &parser, wchar_t **argv)
+static int builtin_set_color(parser_t &parser, io_streams_t &streams, wchar_t **argv)
{
wgetopter_t w;
/** Variables used for parsing the argument list */
@@ -115,7 +112,7 @@ static int builtin_set_color(parser_t &parser, wchar_t **argv)
break;
case 'h':
- builtin_print_help(parser, argv[0], stdout_buffer);
+ builtin_print_help(parser, streams, argv[0], streams.out);
return STATUS_BUILTIN_OK;
case 'o':
@@ -127,7 +124,7 @@ static int builtin_set_color(parser_t &parser, wchar_t **argv)
break;
case 'c':
- print_colors();
+ print_colors(streams);
return STATUS_BUILTIN_OK;
case '?':
@@ -142,7 +139,7 @@ static int builtin_set_color(parser_t &parser, wchar_t **argv)
rgb_color_t fg = rgb_color_t(argv[w.woptind]);
if (fg.is_none() || fg.is_ignore())
{
- append_format(stderr_buffer, _(L"%ls: Unknown color '%ls'\n"), argv[0], argv[w.woptind]);
+ streams.err.append_format(_(L"%ls: Unknown color '%ls'\n"), argv[0], argv[w.woptind]);
return STATUS_BUILTIN_ERROR;
}
fgcolors.push_back(fg);
@@ -150,8 +147,7 @@ static int builtin_set_color(parser_t &parser, wchar_t **argv)
if (fgcolors.empty() && bgcolor == NULL && !bold && !underline)
{
- append_format(stderr_buffer,
- _(L"%ls: Expected an argument\n"),
+ streams.err.append_format(_(L"%ls: Expected an argument\n"),
argv[0]);
return STATUS_BUILTIN_ERROR;
}
@@ -164,14 +160,14 @@ static int builtin_set_color(parser_t &parser, wchar_t **argv)
const rgb_color_t bg = rgb_color_t(bgcolor ? bgcolor : L"");
if (bgcolor && (bg.is_none() || bg.is_ignore()))
{
- append_format(stderr_buffer, _(L"%ls: Unknown color '%ls'\n"), argv[0], bgcolor);
+ streams.err.append_format(_(L"%ls: Unknown color '%ls'\n"), argv[0], bgcolor);
return STATUS_BUILTIN_ERROR;
}
/* Make sure that the term exists */
if (cur_term == NULL && setupterm(0, STDOUT_FILENO, &errret) == ERR)
{
- append_format(stderr_buffer, _(L"%ls: Could not set up terminal\n"), argv[0]);
+ streams.err.append_format(_(L"%ls: Could not set up terminal\n"), argv[0]);
return STATUS_BUILTIN_ERROR;
}
@@ -237,7 +233,7 @@ static int builtin_set_color(parser_t &parser, wchar_t **argv)
output_set_writer(saved_writer_func);
/* Output the collected string */
- stdout_buffer.append(str2wcstring(builtin_set_color_output));
+ streams.out.append(str2wcstring(builtin_set_color_output));
builtin_set_color_output.clear();
return STATUS_BUILTIN_OK;
diff --git a/src/builtin_string.cpp b/src/builtin_string.cpp
index 9457ee4c..47a3f469 100644
--- a/src/builtin_string.cpp
+++ b/src/builtin_string.cpp
@@ -26,9 +26,7 @@
/* externs from builtin.cpp */
extern int builtin_count_args(const wchar_t * const * argv);
-extern wcstring stdout_buffer, stderr_buffer;
-void builtin_print_help(parser_t &parser, const wchar_t *cmd, wcstring &b);
-extern int builtin_stdin;
+void builtin_print_help(parser_t &parser, io_streams_t &streams, const wchar_t *cmd, output_stream_t &b);
enum
@@ -38,33 +36,34 @@ enum
BUILTIN_STRING_ERROR = 2
};
-static void string_error(const wchar_t *fmt, ...)
+static void string_error(io_streams_t &streams, const wchar_t *fmt, ...)
{
+ streams.err.append(L"string ");
va_list va;
va_start(va, fmt);
- stderr_buffer.append(L"string ");
- append_formatv(stderr_buffer, fmt, va);
+ streams.err.append_formatv(fmt, va);
va_end(va);
}
-static void string_unknown_option(parser_t &parser, const wchar_t *subcmd, const wchar_t *opt)
+static void string_unknown_option(parser_t &parser, io_streams_t &streams, const wchar_t *subcmd, const wchar_t *opt)
{
- string_error(BUILTIN_ERR_UNKNOWN, subcmd, opt);
- builtin_print_help(parser, L"string", stderr_buffer);
+ string_error(streams, BUILTIN_ERR_UNKNOWN, subcmd, opt);
+ builtin_print_help(parser, streams, L"string", streams.err);
}
-static bool string_args_from_stdin()
+/* We read from stdin if we are the second or later process in a pipeline. */
+static bool string_args_from_stdin(const io_streams_t &streams)
{
- return builtin_stdin != STDIN_FILENO || !isatty(builtin_stdin);
+ return ! streams.is_first_process_in_pipeline;
}
-static const wchar_t *string_get_arg_stdin(wcstring *storage)
+static const wchar_t *string_get_arg_stdin(wcstring *storage, const io_streams_t &streams)
{
std::string arg;
for (;;)
{
char ch = '\0';
- long rc = read_blocked(builtin_stdin, &ch, 1);
+ long rc = read_blocked(streams.stdin_fd, &ch, 1);
if (rc < 0)
{
@@ -102,11 +101,11 @@ static const wchar_t *string_get_arg_argv(int *argidx, wchar_t **argv)
return (argv && argv[*argidx]) ? argv[(*argidx)++] : 0;
}
-static const wchar_t *string_get_arg(int *argidx, wchar_t **argv, wcstring *storage)
+static const wchar_t *string_get_arg(int *argidx, wchar_t **argv, wcstring *storage, const io_streams_t &streams)
{
- if (string_args_from_stdin())
+ if (string_args_from_stdin(streams))
{
- return string_get_arg_stdin(storage);
+ return string_get_arg_stdin(storage, streams);
}
else
{
@@ -114,7 +113,7 @@ static const wchar_t *string_get_arg(int *argidx, wchar_t **argv, wcstring *stor
}
}
-static int string_escape(parser_t &parser, int argc, wchar_t **argv)
+static int string_escape(parser_t &parser, io_streams_t &streams, int argc, wchar_t **argv)
{
const wchar_t *short_options = L"n";
const struct woption long_options[] =
@@ -143,32 +142,32 @@ static int string_escape(parser_t &parser, int argc, wchar_t **argv)
break;
case '?':
- string_unknown_option(parser, argv[0], argv[w.woptind - 1]);
+ string_unknown_option(parser, streams, argv[0], argv[w.woptind - 1]);
return BUILTIN_STRING_ERROR;
}
}
int i = w.woptind;
- if (string_args_from_stdin() && argc > i)
+ if (string_args_from_stdin(streams) && argc > i)
{
- string_error(BUILTIN_ERR_TOO_MANY_ARGUMENTS, argv[0]);
+ string_error(streams, BUILTIN_ERR_TOO_MANY_ARGUMENTS, argv[0]);
return BUILTIN_STRING_ERROR;
}
int nesc = 0;
wcstring storage;
const wchar_t *arg;
- while ((arg = string_get_arg(&i, argv, &storage)) != 0)
+ while ((arg = string_get_arg(&i, argv, &storage, streams)) != 0)
{
- stdout_buffer += escape(arg, flags);
- stdout_buffer += L'\n';
+ streams.out.append(escape(arg, flags));
+ streams.out.append(L'\n');
nesc++;
}
return (nesc > 0) ? BUILTIN_STRING_OK : BUILTIN_STRING_NONE;
}
-static int string_join(parser_t &parser, int argc, wchar_t **argv)
+static int string_join(parser_t &parser, io_streams_t &streams, int argc, wchar_t **argv)
{
const wchar_t *short_options = L"q";
const struct woption long_options[] =
@@ -197,7 +196,7 @@ static int string_join(parser_t &parser, int argc, wchar_t **argv)
break;
case '?':
- string_unknown_option(parser, argv[0], argv[w.woptind - 1]);
+ string_unknown_option(parser, streams, argv[0], argv[w.woptind - 1]);
return BUILTIN_STRING_ERROR;
}
}
@@ -206,38 +205,40 @@ static int string_join(parser_t &parser, int argc, wchar_t **argv)
const wchar_t *sep;
if ((sep = string_get_arg_argv(&i, argv)) == 0)
{
- string_error(STRING_ERR_MISSING, argv[0]);
+ string_error(streams, STRING_ERR_MISSING, argv[0]);
return BUILTIN_STRING_ERROR;
}
- if (string_args_from_stdin() && argc > i)
+ if (string_args_from_stdin(streams) && argc > i)
{
- string_error(BUILTIN_ERR_TOO_MANY_ARGUMENTS, argv[0]);
+ string_error(streams, BUILTIN_ERR_TOO_MANY_ARGUMENTS, argv[0]);
return BUILTIN_STRING_ERROR;
}
int nargs = 0;
const wchar_t *arg;
wcstring storage;
- while ((arg = string_get_arg(&i, argv, &storage)) != 0)
+ while ((arg = string_get_arg(&i, argv, &storage, streams)) != 0)
{
if (!quiet)
{
- stdout_buffer += arg;
- stdout_buffer += sep;
+ if (nargs > 0)
+ {
+ streams.out.append(sep);
+ }
+ streams.out.append(arg);
}
nargs++;
}
if (nargs > 0 && !quiet)
{
- stdout_buffer.resize(stdout_buffer.length() - wcslen(sep));
- stdout_buffer += L'\n';
+ streams.out.push_back(L'\n');
}
return (nargs > 1) ? BUILTIN_STRING_OK : BUILTIN_STRING_NONE;
}
-static int string_length(parser_t &parser, int argc, wchar_t **argv)
+static int string_length(parser_t &parser, io_streams_t &streams, int argc, wchar_t **argv)
{
const wchar_t *short_options = L"q";
const struct woption long_options[] =
@@ -266,22 +267,22 @@ static int string_length(parser_t &parser, int argc, wchar_t **argv)
break;
case '?':
- string_unknown_option(parser, argv[0], argv[w.woptind - 1]);
+ string_unknown_option(parser, streams, argv[0], argv[w.woptind - 1]);
return BUILTIN_STRING_ERROR;
}
}
int i = w.woptind;
- if (string_args_from_stdin() && argc > i)
+ if (string_args_from_stdin(streams) && argc > i)
{
- string_error(BUILTIN_ERR_TOO_MANY_ARGUMENTS, argv[0]);
+ string_error(streams, BUILTIN_ERR_TOO_MANY_ARGUMENTS, argv[0]);
return BUILTIN_STRING_ERROR;
}
const wchar_t *arg;
int nnonempty = 0;
wcstring storage;
- while ((arg = string_get_arg(&i, argv, &storage)) != 0)
+ while ((arg = string_get_arg(&i, argv, &storage, streams)) != 0)
{
size_t n = wcslen(arg);
if (n > 0)
@@ -290,8 +291,8 @@ static int string_length(parser_t &parser, int argc, wchar_t **argv)
}
if (!quiet)
{
- stdout_buffer += to_string(int(n));
- stdout_buffer += L'\n';
+ streams.out.append(to_string(n));
+ streams.out.append(L'\n');
}
}
@@ -312,11 +313,12 @@ class string_matcher_t
{
protected:
match_options_t opts;
+ io_streams_t &streams;
int total_matched;
public:
- string_matcher_t(const match_options_t &opts_)
- : opts(opts_), total_matched(0)
+ string_matcher_t(const match_options_t &opts_, io_streams_t &streams_)
+ : opts(opts_), streams(streams_), total_matched(0)
{ }
virtual ~string_matcher_t() { }
@@ -329,8 +331,8 @@ class wildcard_matcher_t: public string_matcher_t
wcstring wcpattern;
public:
- wildcard_matcher_t(const wchar_t * /*argv0*/, const wchar_t *pattern, const match_options_t &opts)
- : string_matcher_t(opts)
+ wildcard_matcher_t(const wchar_t * /*argv0*/, const wchar_t *pattern, const match_options_t &opts, io_streams_t &streams)
+ : string_matcher_t(opts, streams)
{
wcpattern = parse_util_unescape_wildcards(pattern);
@@ -373,14 +375,12 @@ public:
{
if (opts.index)
{
- stdout_buffer += L"1 ";
- stdout_buffer += to_string(wcslen(arg));
- stdout_buffer += L'\n';
+ streams.out.append_format(L"1 %lu\n", wcslen(arg));
}
else
{
- stdout_buffer += arg;
- stdout_buffer += L'\n';
+ streams.out.append(arg);
+ streams.out.append(L'\n');
}
}
}
@@ -400,7 +400,7 @@ struct compiled_regex_t
pcre2_code *code;
pcre2_match_data *match;
- compiled_regex_t(const wchar_t *argv0, const wchar_t *pattern, bool ignore_case)
+ compiled_regex_t(const wchar_t *argv0, const wchar_t *pattern, bool ignore_case, io_streams_t &streams)
: code(0), match(0)
{
// Disable some sequences that can lead to security problems
@@ -421,10 +421,10 @@ struct compiled_regex_t
0);
if (code == 0)
{
- string_error(_(L"%ls: Regular expression compile error: %ls\n"),
+ string_error(streams, _(L"%ls: Regular expression compile error: %ls\n"),
argv0, pcre2_strerror(err_code).c_str());
- string_error(L"%ls: %ls\n", argv0, pattern);
- string_error(L"%ls: %*ls\n", argv0, err_offset, L"^");
+ string_error(streams, L"%ls: %ls\n", argv0, pattern);
+ string_error(streams, L"%ls: %*ls\n", argv0, err_offset, L"^");
return;
}
@@ -462,14 +462,14 @@ class pcre2_matcher_t: public string_matcher_t
}
if (pcre2_rc < 0)
{
- string_error(_(L"%ls: Regular expression match error: %ls\n"),
+ string_error(streams, _(L"%ls: Regular expression match error: %ls\n"),
argv0, pcre2_strerror(pcre2_rc).c_str());
return -1;
}
if (pcre2_rc == 0)
{
// The output vector wasn't big enough. Should not happen.
- string_error(_(L"%ls: Regular expression internal error\n"), argv0);
+ string_error(streams, _(L"%ls: Regular expression internal error\n"), argv0);
return -1;
}
PCRE2_SIZE *ovector = pcre2_get_ovector_pointer(regex.match);
@@ -483,15 +483,13 @@ class pcre2_matcher_t: public string_matcher_t
{
if (opts.index)
{
- stdout_buffer += to_string(begin + 1);
- stdout_buffer += ' ';
- stdout_buffer += to_string(end - begin);
+ streams.out.append_format(L"%lu %lu", (unsigned long)(begin + 1), (unsigned long)(end - begin));
}
else if (end > begin) // may have end < begin if \K is used
{
- stdout_buffer += wcstring(&arg[begin], end - begin);
+ streams.out.append(wcstring(&arg[begin], end - begin));
}
- stdout_buffer += L'\n';
+ streams.out.append(L'\n');
}
}
}
@@ -499,10 +497,10 @@ class pcre2_matcher_t: public string_matcher_t
}
public:
- pcre2_matcher_t(const wchar_t *argv0_, const wchar_t *pattern, const match_options_t &opts)
- : string_matcher_t(opts),
+ pcre2_matcher_t(const wchar_t *argv0_, const wchar_t *pattern, const match_options_t &opts, io_streams_t &streams)
+ : string_matcher_t(opts, streams),
argv0(argv0_),
- regex(argv0_, pattern, opts.ignore_case)
+ regex(argv0_, pattern, opts.ignore_case, streams)
{ }
virtual ~pcre2_matcher_t() { }
@@ -573,7 +571,7 @@ public:
}
};
-static int string_match(parser_t &parser, int argc, wchar_t **argv)
+static int string_match(parser_t &parser, io_streams_t &streams, int argc, wchar_t **argv)
{
const wchar_t *short_options = L"ainqr";
const struct woption long_options[] =
@@ -623,7 +621,7 @@ static int string_match(parser_t &parser, int argc, wchar_t **argv)
break;
case '?':
- string_unknown_option(parser, argv[0], argv[w.woptind - 1]);
+ string_unknown_option(parser, streams, argv[0], argv[w.woptind - 1]);
return BUILTIN_STRING_ERROR;
}
}
@@ -632,29 +630,29 @@ static int string_match(parser_t &parser, int argc, wchar_t **argv)
const wchar_t *pattern;
if ((pattern = string_get_arg_argv(&i, argv)) == 0)
{
- string_error(STRING_ERR_MISSING, argv[0]);
+ string_error(streams, STRING_ERR_MISSING, argv[0]);
return BUILTIN_STRING_ERROR;
}
- if (string_args_from_stdin() && argc > i)
+ if (string_args_from_stdin(streams) && argc > i)
{
- string_error(BUILTIN_ERR_TOO_MANY_ARGUMENTS, argv[0]);
+ string_error(streams, BUILTIN_ERR_TOO_MANY_ARGUMENTS, argv[0]);
return BUILTIN_STRING_ERROR;
}
string_matcher_t *matcher;
if (regex)
{
- matcher = new pcre2_matcher_t(argv[0], pattern, opts);
+ matcher = new pcre2_matcher_t(argv[0], pattern, opts, streams);
}
else
{
- matcher = new wildcard_matcher_t(argv[0], pattern, opts);
+ matcher = new wildcard_matcher_t(argv[0], pattern, opts, streams);
}
const wchar_t *arg;
wcstring storage;
- while ((arg = string_get_arg(&i, argv, &storage)) != 0)
+ while ((arg = string_get_arg(&i, argv, &storage, streams)) != 0)
{
if (!matcher->report_matches(arg))
{
@@ -683,10 +681,11 @@ protected:
const wchar_t *argv0;
replace_options_t opts;
int total_replaced;
+ io_streams_t &streams;
public:
- string_replacer_t(const wchar_t *argv0_, const replace_options_t &opts_)
- : argv0(argv0_), opts(opts_), total_replaced(0)
+ string_replacer_t(const wchar_t *argv0_, const replace_options_t &opts_, io_streams_t &streams_)
+ : argv0(argv0_), opts(opts_), total_replaced(0), streams(streams_)
{ }
virtual ~string_replacer_t() {}
@@ -702,8 +701,8 @@ class literal_replacer_t: public string_replacer_t
public:
literal_replacer_t(const wchar_t *argv0, const wchar_t *pattern_, const wchar_t *replacement_,
- const replace_options_t &opts)
- : string_replacer_t(argv0, opts),
+ const replace_options_t &opts, io_streams_t &streams)
+ : string_replacer_t(argv0, opts, streams),
pattern(pattern_), replacement(replacement_), patlen(wcslen(pattern))
{ }
@@ -739,8 +738,8 @@ public:
}
if (!opts.quiet)
{
- stdout_buffer += result;
- stdout_buffer += L'\n';
+ streams.out.append(result);
+ streams.out.append(L'\n');
}
return true;
}
@@ -773,9 +772,9 @@ class regex_replacer_t: public string_replacer_t
public:
regex_replacer_t(const wchar_t *argv0, const wchar_t *pattern, const wchar_t *replacement_,
- const replace_options_t &opts)
- : string_replacer_t(argv0, opts),
- regex(argv0, pattern, opts.ignore_case),
+ const replace_options_t &opts, io_streams_t &streams)
+ : string_replacer_t(argv0, opts, streams),
+ regex(argv0, pattern, opts.ignore_case, streams),
replacement(interpret_escapes(replacement_))
{ }
@@ -827,7 +826,7 @@ public:
}
continue;
}
- string_error(_(L"%ls: Replacement string too large\n"), argv0);
+ string_error(streams, _(L"%ls: Replacement string too large\n"), argv0);
free(output);
return false;
}
@@ -837,7 +836,7 @@ public:
bool rc = true;
if (pcre2_rc < 0)
{
- string_error(_(L"%ls: Regular expression substitute error: %ls\n"),
+ string_error(streams, _(L"%ls: Regular expression substitute error: %ls\n"),
argv0, pcre2_strerror(pcre2_rc).c_str());
rc = false;
}
@@ -845,8 +844,8 @@ public:
{
if (!opts.quiet)
{
- stdout_buffer += output;
- stdout_buffer += L'\n';
+ streams.out.append(output);
+ streams.out.append(L'\n');
}
total_replaced += pcre2_rc;
}
@@ -856,7 +855,7 @@ public:
}
};
-static int string_replace(parser_t &parser, int argc, wchar_t **argv)
+static int string_replace(parser_t &parser, io_streams_t &streams, int argc, wchar_t **argv)
{
const wchar_t *short_options = L"aiqr";
const struct woption long_options[] =
@@ -901,7 +900,7 @@ static int string_replace(parser_t &parser, int argc, wchar_t **argv)
break;
case '?':
- string_unknown_option(parser, argv[0], argv[w.woptind - 1]);
+ string_unknown_option(parser, streams, argv[0], argv[w.woptind - 1]);
return BUILTIN_STRING_ERROR;
}
}
@@ -910,34 +909,34 @@ static int string_replace(parser_t &parser, int argc, wchar_t **argv)
const wchar_t *pattern, *replacement;
if ((pattern = string_get_arg_argv(&i, argv)) == 0)
{
- string_error(STRING_ERR_MISSING, argv[0]);
+ string_error(streams, STRING_ERR_MISSING, argv[0]);
return BUILTIN_STRING_ERROR;
}
if ((replacement = string_get_arg_argv(&i, argv)) == 0)
{
- string_error(STRING_ERR_MISSING, argv[0]);
+ string_error(streams, STRING_ERR_MISSING, argv[0]);
return BUILTIN_STRING_ERROR;
}
- if (string_args_from_stdin() && argc > i)
+ if (string_args_from_stdin(streams) && argc > i)
{
- string_error(BUILTIN_ERR_TOO_MANY_ARGUMENTS, argv[0]);
+ string_error(streams, BUILTIN_ERR_TOO_MANY_ARGUMENTS, argv[0]);
return BUILTIN_STRING_ERROR;
}
string_replacer_t *replacer;
if (regex)
{
- replacer = new regex_replacer_t(argv[0], pattern, replacement, opts);
+ replacer = new regex_replacer_t(argv[0], pattern, replacement, opts, streams);
}
else
{
- replacer = new literal_replacer_t(argv[0], pattern, replacement, opts);
+ replacer = new literal_replacer_t(argv[0], pattern, replacement, opts, streams);
}
const wchar_t *arg;
wcstring storage;
- while ((arg = string_get_arg(&i, argv, &storage)) != 0)
+ while ((arg = string_get_arg(&i, argv, &storage, streams)) != 0)
{
if (!replacer->replace_matches(arg))
{
@@ -990,7 +989,7 @@ void split_about(ITER haystack_start, ITER haystack_end,
output->push_back(wcstring(haystack_cursor, haystack_end));
}
-static int string_split(parser_t &parser, int argc, wchar_t **argv)
+static int string_split(parser_t &parser, io_streams_t &streams, int argc, wchar_t **argv)
{
const wchar_t *short_options = L":m:qr";
const struct woption long_options[] =
@@ -1025,7 +1024,7 @@ static int string_split(parser_t &parser, int argc, wchar_t **argv)
max = wcstol(w.woptarg, &endptr, 10);
if (*endptr != L'\0' || errno != 0)
{
- string_error(BUILTIN_ERR_NOT_NUMBER, argv[0], w.woptarg);
+ string_error(streams, BUILTIN_ERR_NOT_NUMBER, argv[0], w.woptarg);
return BUILTIN_STRING_ERROR;
}
break;
@@ -1040,11 +1039,11 @@ static int string_split(parser_t &parser, int argc, wchar_t **argv)
break;
case ':':
- string_error(STRING_ERR_MISSING, argv[0]);
+ string_error(streams, STRING_ERR_MISSING, argv[0]);
return BUILTIN_STRING_ERROR;
case '?':
- string_unknown_option(parser, argv[0], argv[w.woptind - 1]);
+ string_unknown_option(parser, streams, argv[0], argv[w.woptind - 1]);
return BUILTIN_STRING_ERROR;
}
}
@@ -1053,14 +1052,14 @@ static int string_split(parser_t &parser, int argc, wchar_t **argv)
const wchar_t *sep;
if ((sep = string_get_arg_argv(&i, argv)) == NULL)
{
- string_error(STRING_ERR_MISSING, argv[0]);
+ string_error(streams, STRING_ERR_MISSING, argv[0]);
return BUILTIN_STRING_ERROR;
}
const wchar_t *sep_end = sep + wcslen(sep);
- if (string_args_from_stdin() && argc > i)
+ if (string_args_from_stdin(streams) && argc > i)
{
- string_error(BUILTIN_ERR_TOO_MANY_ARGUMENTS, argv[0]);
+ string_error(streams, BUILTIN_ERR_TOO_MANY_ARGUMENTS, argv[0]);
return BUILTIN_STRING_ERROR;
}
@@ -1068,7 +1067,7 @@ static int string_split(parser_t &parser, int argc, wchar_t **argv)
size_t arg_count = 0;
wcstring storage;
const wchar_t *arg;
- while ((arg = string_get_arg(&i, argv, &storage)) != 0)
+ while ((arg = string_get_arg(&i, argv, &storage, streams)) != 0)
{
const wchar_t *arg_end = arg + wcslen(arg);
if (right)
@@ -1101,8 +1100,8 @@ static int string_split(parser_t &parser, int argc, wchar_t **argv)
{
for (wcstring_list_t::const_iterator si = splits.begin(); si != splits.end(); ++si)
{
- stdout_buffer += *si;
- stdout_buffer += L'\n';
+ streams.out.append(*si);
+ streams.out.append(L'\n');
}
}
@@ -1110,7 +1109,7 @@ static int string_split(parser_t &parser, int argc, wchar_t **argv)
return (splits.size() > arg_count) ? BUILTIN_STRING_OK : BUILTIN_STRING_NONE;
}
-static int string_sub(parser_t &parser, int argc, wchar_t **argv)
+static int string_sub(parser_t &parser, io_streams_t &streams, int argc, wchar_t **argv)
{
const wchar_t *short_options = L":l:qs:";
const struct woption long_options[] =
@@ -1144,12 +1143,12 @@ static int string_sub(parser_t &parser, int argc, wchar_t **argv)
length = wcstol(w.woptarg, &endptr, 10);
if (*endptr != L'\0' || (errno != 0 && errno != ERANGE))
{
- string_error(BUILTIN_ERR_NOT_NUMBER, argv[0], w.woptarg);
+ string_error(streams, BUILTIN_ERR_NOT_NUMBER, argv[0], w.woptarg);
return BUILTIN_STRING_ERROR;
}
if (length < 0 || errno == ERANGE)
{
- string_error(_(L"%ls: Invalid length value '%ls'\n"), argv[0], w.woptarg);
+ string_error(streams, _(L"%ls: Invalid length value '%ls'\n"), argv[0], w.woptarg);
return BUILTIN_STRING_ERROR;
}
break;
@@ -1163,37 +1162,37 @@ static int string_sub(parser_t &parser, int argc, wchar_t **argv)
start = wcstol(w.woptarg, &endptr, 10);
if (*endptr != L'\0' || (errno != 0 && errno != ERANGE))
{
- string_error(BUILTIN_ERR_NOT_NUMBER, argv[0], w.woptarg);
+ string_error(streams, BUILTIN_ERR_NOT_NUMBER, argv[0], w.woptarg);
return BUILTIN_STRING_ERROR;
}
if (start == 0 || start == LONG_MIN || errno == ERANGE)
{
- string_error(_(L"%ls: Invalid start value '%ls'\n"), argv[0], w.woptarg);
+ string_error(streams, _(L"%ls: Invalid start value '%ls'\n"), argv[0], w.woptarg);
return BUILTIN_STRING_ERROR;
}
break;
case ':':
- string_error(STRING_ERR_MISSING, argv[0]);
+ string_error(streams, STRING_ERR_MISSING, argv[0]);
return BUILTIN_STRING_ERROR;
case '?':
- string_unknown_option(parser, argv[0], argv[w.woptind - 1]);
+ string_unknown_option(parser, streams, argv[0], argv[w.woptind - 1]);
return BUILTIN_STRING_ERROR;
}
}
int i = w.woptind;
- if (string_args_from_stdin() && argc > i)
+ if (string_args_from_stdin(streams) && argc > i)
{
- string_error(BUILTIN_ERR_TOO_MANY_ARGUMENTS, argv[0]);
+ string_error(streams, BUILTIN_ERR_TOO_MANY_ARGUMENTS, argv[0]);
return BUILTIN_STRING_ERROR;
}
int nsub = 0;
const wchar_t *arg;
wcstring storage;
- while ((arg = string_get_arg(&i, argv, &storage)) != NULL)
+ while ((arg = string_get_arg(&i, argv, &storage, streams)) != NULL)
{
typedef wcstring::size_type size_type;
size_type pos = 0;
@@ -1222,8 +1221,8 @@ static int string_sub(parser_t &parser, int argc, wchar_t **argv)
// note that std::string permits count to extend past end of string
if (!quiet)
{
- stdout_buffer += s.substr(pos, count);
- stdout_buffer += L'\n';
+ streams.out.append(s.substr(pos, count));
+ streams.out.append(L'\n');
}
nsub++;
}
@@ -1231,7 +1230,7 @@ static int string_sub(parser_t &parser, int argc, wchar_t **argv)
return (nsub > 0) ? BUILTIN_STRING_OK : BUILTIN_STRING_NONE;
}
-static int string_trim(parser_t &parser, int argc, wchar_t **argv)
+static int string_trim(parser_t &parser, io_streams_t &streams, int argc, wchar_t **argv)
{
const wchar_t *short_options = L":c:lqr";
const struct woption long_options[] =
@@ -1277,19 +1276,19 @@ static int string_trim(parser_t &parser, int argc, wchar_t **argv)
break;
case ':':
- string_error(STRING_ERR_MISSING, argv[0]);
+ string_error(streams, STRING_ERR_MISSING, argv[0]);
return BUILTIN_STRING_ERROR;
case '?':
- string_unknown_option(parser, argv[0], argv[w.woptind - 1]);
+ string_unknown_option(parser, streams, argv[0], argv[w.woptind - 1]);
return BUILTIN_STRING_ERROR;
}
}
int i = w.woptind;
- if (string_args_from_stdin() && argc > i)
+ if (string_args_from_stdin(streams) && argc > i)
{
- string_error(BUILTIN_ERR_TOO_MANY_ARGUMENTS, argv[0]);
+ string_error(streams, BUILTIN_ERR_TOO_MANY_ARGUMENTS, argv[0]);
return BUILTIN_STRING_ERROR;
}
@@ -1305,7 +1304,7 @@ static int string_trim(parser_t &parser, int argc, wchar_t **argv)
wcstring argstr;
wcstring storage;
- while ((arg = string_get_arg(&i, argv, &storage)) != 0)
+ while ((arg = string_get_arg(&i, argv, &storage, streams)) != 0)
{
argstr = arg;
// begin and end are respectively the first character to keep on the left,
@@ -1325,8 +1324,8 @@ static int string_trim(parser_t &parser, int argc, wchar_t **argv)
ntrim += argstr.size() - (end - begin);
if (!quiet)
{
- stdout_buffer.append(argstr, begin, end - begin);
- stdout_buffer += L'\n';
+ streams.out.append(wcstring(argstr, begin, end - begin));
+ streams.out.append(L'\n');
}
}
@@ -1336,7 +1335,7 @@ static int string_trim(parser_t &parser, int argc, wchar_t **argv)
static const struct string_subcommand
{
const wchar_t *name;
- int (*handler)(parser_t &, int argc, wchar_t **argv);
+ int (*handler)(parser_t &, io_streams_t &, int argc, wchar_t **argv);
}
string_subcommands[] =
{
@@ -1354,19 +1353,19 @@ string_subcommands[] =
/**
The string builtin, for manipulating strings.
*/
-/*static*/ int builtin_string(parser_t &parser, wchar_t **argv)
+int builtin_string(parser_t &parser, io_streams_t &streams, wchar_t **argv)
{
int argc = builtin_count_args(argv);
if (argc <= 1)
{
- string_error(STRING_ERR_MISSING, argv[0]);
- builtin_print_help(parser, L"string", stderr_buffer);
+ string_error(streams, STRING_ERR_MISSING, argv[0]);
+ builtin_print_help(parser, streams, L"string", streams.err);
return BUILTIN_STRING_ERROR;
}
if (wcscmp(argv[1], L"-h") == 0 || wcscmp(argv[1], L"--help") == 0)
{
- builtin_print_help(parser, L"string", stderr_buffer);
+ builtin_print_help(parser, streams, L"string", streams.err);
return BUILTIN_STRING_OK;
}
@@ -1377,12 +1376,12 @@ string_subcommands[] =
}
if (subcmd->handler == 0)
{
- string_error(_(L"%ls: Unknown subcommand '%ls'\n"), argv[0], argv[1]);
- builtin_print_help(parser, L"string", stderr_buffer);
+ string_error(streams, _(L"%ls: Unknown subcommand '%ls'\n"), argv[0], argv[1]);
+ builtin_print_help(parser, streams, L"string", streams.err);
return BUILTIN_STRING_ERROR;
}
argc--;
argv++;
- return subcmd->handler(parser, argc, argv);
+ return subcmd->handler(parser, streams, argc, argv);
}
diff --git a/src/builtin_test.cpp b/src/builtin_test.cpp
index 84d5fa9c..e80d06ec 100644
--- a/src/builtin_test.cpp
+++ b/src/builtin_test.cpp
@@ -23,7 +23,7 @@ enum
};
-int builtin_test(parser_t &parser, wchar_t **argv);
+int builtin_test(parser_t &parser, io_streams_t &streams, wchar_t **argv);
namespace test_expressions
{
@@ -887,7 +887,7 @@ static bool unary_primary_evaluate(test_expressions::token_t token, const wcstri
* Return status is the final shell status, i.e. 0 for true,
* 1 for false and 2 for error.
*/
-int builtin_test(parser_t &parser, wchar_t **argv)
+int builtin_test(parser_t &parser, io_streams_t &streams, wchar_t **argv)
{
using namespace test_expressions;
@@ -912,7 +912,7 @@ int builtin_test(parser_t &parser, wchar_t **argv)
}
else
{
- builtin_show_error(L"[: the last argument must be ']'\n");
+ streams.err.append(L"[: the last argument must be ']'\n");
return BUILTIN_TEST_FAIL;
}
@@ -948,7 +948,7 @@ int builtin_test(parser_t &parser, wchar_t **argv)
}
printf("and returned parse error: %ls\n", err.c_str());
#endif
- builtin_show_error(err);
+ streams.err.append(err);
return BUILTIN_TEST_FAIL;
}
else
diff --git a/src/builtin_ulimit.cpp b/src/builtin_ulimit.cpp
index 64b126b7..3b2dadd6 100644
--- a/src/builtin_ulimit.cpp
+++ b/src/builtin_ulimit.cpp
@@ -139,21 +139,21 @@ static rlim_t get(int resource, int hard)
/**
Print the value of the specified resource limit
*/
-static void print(int resource, int hard)
+static void print(int resource, int hard, io_streams_t &streams)
{
rlim_t l = get(resource, hard);
if (l == RLIM_INFINITY)
- stdout_buffer.append(L"unlimited\n");
+ streams.out.append(L"unlimited\n");
else
- append_format(stdout_buffer, L"%d\n", l / get_multiplier(resource));
+ streams.out.append_format( L"%d\n", l / get_multiplier(resource));
}
/**
Print values of all resource limits
*/
-static void print_all(int hard)
+static void print_all(int hard, io_streams_t &streams)
{
int i;
int w=0;
@@ -172,7 +172,7 @@ static void print_all(int hard)
const wchar_t *unit = ((resource_arr[i].resource==RLIMIT_CPU)?L"(seconds, ":(get_multiplier(resource_arr[i].resource)==1?L"(":L"(kB, "));
- append_format(stdout_buffer,
+ streams.out.append_format(
L"%-*ls %10ls-%lc) ",
w,
resource_arr[i].desc,
@@ -181,11 +181,11 @@ static void print_all(int hard)
if (l == RLIM_INFINITY)
{
- stdout_buffer.append(L"unlimited\n");
+ streams.out.append(L"unlimited\n");
}
else
{
- append_format(stdout_buffer, L"%d\n", l/get_multiplier(resource_arr[i].resource));
+ streams.out.append_format( L"%d\n", l/get_multiplier(resource_arr[i].resource));
}
}
@@ -213,7 +213,7 @@ static const wchar_t *get_desc(int what)
does _not_ multiply the limit value by the multiplier constant used
by the commandline ulimit.
*/
-static int set(int resource, int hard, int soft, rlim_t value)
+static int set(int resource, int hard, int soft, rlim_t value, io_streams_t &streams)
{
struct rlimit ls;
getrlimit(resource, &ls);
@@ -240,9 +240,9 @@ static int set(int resource, int hard, int soft, rlim_t value)
if (setrlimit(resource, &ls))
{
if (errno == EPERM)
- append_format(stderr_buffer, L"ulimit: Permission denied when changing resource of type '%ls'\n", get_desc(resource));
+ streams.err.append_format(L"ulimit: Permission denied when changing resource of type '%ls'\n", get_desc(resource));
else
- builtin_wperror(L"ulimit");
+ builtin_wperror(L"ulimit", streams);
return 1;
}
return 0;
@@ -252,7 +252,7 @@ static int set(int resource, int hard, int soft, rlim_t value)
The ulimit builtin, used for setting resource limits. Defined in
builtin_ulimit.c.
*/
-static int builtin_ulimit(parser_t &parser, wchar_t ** argv)
+static int builtin_ulimit(parser_t &parser, io_streams_t &streams, wchar_t **argv)
{
wgetopter_t w;
int hard=0;
@@ -348,11 +348,10 @@ static int builtin_ulimit(parser_t &parser, wchar_t ** argv)
case 0:
if (long_options[opt_index].flag != 0)
break;
- append_format(stderr_buffer,
- BUILTIN_ERR_UNKNOWN,
+ streams.err.append_format(BUILTIN_ERR_UNKNOWN,
argv[0],
long_options[opt_index].name);
- builtin_print_help(parser, argv[0], stderr_buffer);
+ builtin_print_help(parser, streams, argv[0], streams.err);
return 1;
@@ -416,11 +415,11 @@ static int builtin_ulimit(parser_t &parser, wchar_t ** argv)
#endif
case L'h':
- builtin_print_help(parser, argv[0], stdout_buffer);
+ builtin_print_help(parser, streams, argv[0], streams.out);
return 0;
case L'?':
- builtin_unknown_option(parser, argv[0], argv[w.woptind-1]);
+ builtin_unknown_option(parser, streams, argv[0], argv[w.woptind-1]);
return 1;
}
}
@@ -429,13 +428,13 @@ static int builtin_ulimit(parser_t &parser, wchar_t ** argv)
{
if (argc - w.woptind == 0)
{
- print_all(hard);
+ print_all(hard, streams);
}
else
{
- stderr_buffer.append(argv[0]);
- stderr_buffer.append(L": Too many arguments\n");
- builtin_print_help(parser, argv[0], stderr_buffer);
+ streams.err.append(argv[0]);
+ streams.err.append(L": Too many arguments\n");
+ builtin_print_help(parser, streams, argv[0], streams.err);
return 1;
}
@@ -449,7 +448,7 @@ static int builtin_ulimit(parser_t &parser, wchar_t ** argv)
/*
Show current limit value
*/
- print(what, hard);
+ print(what, hard, streams);
break;
}
@@ -487,24 +486,23 @@ static int builtin_ulimit(parser_t &parser, wchar_t ** argv)
new_limit = wcstol(argv[w.woptind], &end, 10);
if (errno || *end)
{
- append_format(stderr_buffer,
- L"%ls: Invalid limit '%ls'\n",
+ streams.err.append_format(L"%ls: Invalid limit '%ls'\n",
argv[0],
argv[w.woptind]);
- builtin_print_help(parser, argv[0], stderr_buffer);
+ builtin_print_help(parser, streams, argv[0], streams.err);
return 1;
}
new_limit *= get_multiplier(what);
}
- return set(what, hard, soft, new_limit);
+ return set(what, hard, soft, new_limit, streams);
}
default:
{
- stderr_buffer.append(argv[0]);
- stderr_buffer.append(L": Too many arguments\n");
- builtin_print_help(parser, argv[0], stderr_buffer);
+ streams.err.append(argv[0]);
+ streams.err.append(L": Too many arguments\n");
+ builtin_print_help(parser, streams, argv[0], streams.err);
return 1;
}
diff --git a/src/common.cpp b/src/common.cpp
index 50cb1102..2d844129 100644
--- a/src/common.cpp
+++ b/src/common.cpp
@@ -641,9 +641,7 @@ static bool should_debug(int level)
static void debug_shared(const wcstring &msg)
{
const wcstring sb = wcstring(program_name) + L": " + msg;
- wcstring sb2;
- write_screen(sb, sb2);
- fwprintf(stderr, L"%ls", sb2.c_str());
+ fwprintf(stderr, L"%ls", reformat_for_screen(sb).c_str());
}
void debug(int level, const wchar_t *msg, ...)
@@ -806,8 +804,9 @@ void format_long_safe(wchar_t buff[64], long val)
}
}
-void write_screen(const wcstring &msg, wcstring &buff)
+wcstring reformat_for_screen(const wcstring &msg)
{
+ wcstring buff;
int line_width = 0;
int screen_width = common_get_width();
@@ -892,6 +891,7 @@ void write_screen(const wcstring &msg, wcstring &buff)
buff.append(msg);
}
buff.push_back(L'\n');
+ return buff;
}
/* Escape a string, storing the result in out_str */
diff --git a/src/common.h b/src/common.h
index 88bbf480..ff7443e5 100644
--- a/src/common.h
+++ b/src/common.h
@@ -861,10 +861,10 @@ int common_get_height();
void common_handle_winch(int signal);
/**
- Write paragraph of output to the specified stringbuffer, and redo
- the linebreaks to fit the current screen.
+ Write the given paragraph of output, redoing linebreaks to fit
+ the current screen.
*/
-void write_screen(const wcstring &msg, wcstring &buff);
+wcstring reformat_for_screen(const wcstring &msg);
/**
Tokenize the specified string into the specified wcstring_list_t.
diff --git a/src/complete.cpp b/src/complete.cpp
index a4ef163a..ae9d2ef3 100644
--- a/src/complete.cpp
+++ b/src/complete.cpp
@@ -2100,8 +2100,9 @@ static void append_switch(wcstring &out,
append_format(out, L" --%ls %ls", opt.c_str(), esc.c_str());
}
-void complete_print(wcstring &out)
+wcstring complete_print()
{
+ wcstring out;
scoped_lock locker(completion_lock);
scoped_lock locker2(completion_entry_lock);
@@ -2171,6 +2172,7 @@ void complete_print(wcstring &out)
const wcstring &target = wrap_pairs.at(i++);
append_format(out, L"complete --command %ls --wraps %ls\n", cmd.c_str(), target.c_str());
}
+ return out;
}
diff --git a/src/complete.h b/src/complete.h
index 41c9c3e0..2083517e 100644
--- a/src/complete.h
+++ b/src/complete.h
@@ -213,11 +213,9 @@ void complete(const wcstring &cmd,
completion_request_flags_t flags);
/**
- Print a list of all current completions into the string.
-
- \param out The string to write completions to
+ Return a list of all current completions.
*/
-void complete_print(wcstring &out);
+wcstring complete_print();
/**
Tests if the specified option is defined for the specified command
diff --git a/src/exec.cpp b/src/exec.cpp
index 39385b06..5366e663 100644
--- a/src/exec.cpp
+++ b/src/exec.cpp
@@ -763,6 +763,10 @@ void exec_job(parser_t &parser, job_t *j)
// This is the IO buffer we use for storing the output of a block or function when it is in a pipeline
shared_ptr<io_buffer_t> block_output_io_buffer;
+
+ // This is the io_streams we pass to internal builtins
+ std::auto_ptr<io_streams_t> builtin_io_streams;
+
switch (p->type)
{
case INTERNAL_FUNCTION:
@@ -949,9 +953,14 @@ void exec_job(parser_t &parser, job_t *j)
}
else
{
- int old_out = builtin_out_redirect;
- int old_err = builtin_err_redirect;
+ builtin_io_streams.reset(new io_streams_t());
+ builtin_io_streams->stdin_fd = local_builtin_stdin;
+ builtin_io_streams->out_is_redirected = has_fd(process_net_io_chain, STDOUT_FILENO);
+ builtin_io_streams->err_is_redirected = has_fd(process_net_io_chain, STDERR_FILENO);
+ builtin_io_streams->is_first_process_in_pipeline = (p == j->first_process);
+ builtin_io_streams->io_chain = &process_net_io_chain;
+
/*
Since this may be the foreground job, and since
a builtin may execute another foreground job,
@@ -967,20 +976,12 @@ void exec_job(parser_t &parser, job_t *j)
to make exec handle things.
*/
- builtin_push_io(parser, local_builtin_stdin);
-
- builtin_out_redirect = has_fd(process_net_io_chain, STDOUT_FILENO);
- builtin_err_redirect = has_fd(process_net_io_chain, STDERR_FILENO);
-
const int fg = job_get_flag(j, JOB_FOREGROUND);
job_set_flag(j, JOB_FOREGROUND, 0);
signal_unblock();
- p->status = builtin_run(parser, p->get_argv(), process_net_io_chain);
-
- builtin_out_redirect=old_out;
- builtin_err_redirect=old_err;
+ p->status = builtin_run(parser, p->get_argv(), *builtin_io_streams);
signal_block();
@@ -1116,6 +1117,10 @@ void exec_job(parser_t &parser, job_t *j)
const shared_ptr<io_data_t> stdout_io = process_net_io_chain.get_io_for_fd(STDOUT_FILENO);
const shared_ptr<io_data_t> stderr_io = process_net_io_chain.get_io_for_fd(STDERR_FILENO);
+
+ assert(builtin_io_streams.get() != NULL);
+ const wcstring &stdout_buffer = builtin_io_streams->out.buffer();
+ const wcstring &stderr_buffer = builtin_io_streams->err.buffer();
/* If we are outputting to a file, we have to actually do it, even if we have no output, so that we can truncate the file. Does not apply to /dev/null. */
bool must_fork = redirection_is_to_real_file(stdout_io.get()) || redirection_is_to_real_file(stderr_io.get());
@@ -1124,8 +1129,8 @@ void exec_job(parser_t &parser, job_t *j)
if (p->next == NULL)
{
const bool stdout_is_to_buffer = stdout_io && stdout_io->io_mode == IO_BUFFER;
- const bool no_stdout_output = get_stdout_buffer().empty();
- const bool no_stderr_output = get_stderr_buffer().empty();
+ const bool no_stdout_output = stdout_buffer.empty();
+ const bool no_stderr_output = stderr_buffer.empty();
if (no_stdout_output && no_stderr_output)
{
@@ -1146,7 +1151,7 @@ void exec_job(parser_t &parser, job_t *j)
}
CAST_INIT(io_buffer_t *, io_buffer, stdout_io.get());
- const std::string res = wcs2string(get_stdout_buffer());
+ const std::string res = wcs2string(builtin_io_streams->out.buffer());
io_buffer->out_buffer_append(res.data(), res.size());
fork_was_skipped = true;
}
@@ -1157,9 +1162,8 @@ void exec_job(parser_t &parser, job_t *j)
{
printf("fork #-: Skipping fork due to ordinary output for internal builtin for '%ls'\n", p->argv0());
}
- const wcstring &out = get_stdout_buffer(), &err = get_stderr_buffer();
- const std::string outbuff = wcs2string(out);
- const std::string errbuff = wcs2string(err);
+ const std::string outbuff = wcs2string(stdout_buffer);
+ const std::string errbuff = wcs2string(stderr_buffer);
bool builtin_io_done = do_builtin_io(outbuff.data(), outbuff.size(), errbuff.data(), errbuff.size());
if (! builtin_io_done)
{
@@ -1188,15 +1192,12 @@ void exec_job(parser_t &parser, job_t *j)
/* Ok, unfortunately, we have to do a real fork. Bummer. We work hard to make sure we don't have to wait for all our threads to exit, by arranging things so that we don't have to allocate memory or do anything except system calls in the child. */
- /* Get the strings we'll write before we fork (since they call malloc) */
- const wcstring &out = get_stdout_buffer(), &err = get_stderr_buffer();
-
/* These strings may contain embedded nulls, so don't treat them as C strings */
- const std::string outbuff_str = wcs2string(out);
+ const std::string outbuff_str = wcs2string(stdout_buffer);
const char *outbuff = outbuff_str.data();
size_t outbuff_len = outbuff_str.size();
- const std::string errbuff_str = wcs2string(err);
+ const std::string errbuff_str = wcs2string(stderr_buffer);
const char *errbuff = errbuff_str.data();
size_t errbuff_len = errbuff_str.size();
@@ -1337,9 +1338,6 @@ void exec_job(parser_t &parser, job_t *j)
}
}
- if (p->type == INTERNAL_BUILTIN)
- builtin_pop_io(parser);
-
/*
Close the pipe the current process uses to read from the
previous process_t
diff --git a/src/fish_tests.cpp b/src/fish_tests.cpp
index 1b314799..1b71411c 100644
--- a/src/fish_tests.cpp
+++ b/src/fish_tests.cpp
@@ -1923,7 +1923,7 @@ static void test_is_potential_path()
}
/** Test the 'test' builtin */
-int builtin_test(parser_t &parser, wchar_t **argv);
+int builtin_test(parser_t &parser, io_streams_t &streams, wchar_t **argv);
static bool run_one_test_test(int expected, wcstring_list_t &lst, bool bracket)
{
parser_t parser(PARSER_TYPE_GENERAL, true);
@@ -1940,7 +1940,8 @@ static bool run_one_test_test(int expected, wcstring_list_t &lst, bool bracket)
i++;
}
argv[i+1] = NULL;
- int result = builtin_test(parser, argv);
+ io_streams_t streams;
+ int result = builtin_test(parser, streams, argv);
delete[] argv;
return expected == result;
}
@@ -1965,15 +1966,16 @@ static void test_test_brackets()
{
// Ensure [ knows it needs a ]
parser_t parser(PARSER_TYPE_GENERAL, true);
+ io_streams_t streams;
const wchar_t *argv1[] = {L"[", L"foo", NULL};
- do_test(builtin_test(parser, (wchar_t **)argv1) != 0);
+ do_test(builtin_test(parser, streams, (wchar_t **)argv1) != 0);
const wchar_t *argv2[] = {L"[", L"foo", L"]", NULL};
- do_test(builtin_test(parser, (wchar_t **)argv2) == 0);
+ do_test(builtin_test(parser, streams, (wchar_t **)argv2) == 0);
const wchar_t *argv3[] = {L"[", L"foo", L"]", L"bar", NULL};
- do_test(builtin_test(parser, (wchar_t **)argv3) != 0);
+ do_test(builtin_test(parser, streams, (wchar_t **)argv3) != 0);
}
@@ -4017,14 +4019,13 @@ static void test_wcstring_tok(void)
}
}
-int builtin_string(parser_t &parser, wchar_t **argv);
-extern wcstring stdout_buffer;
+int builtin_string(parser_t &parser, io_streams_t &streams, wchar_t **argv);
static void run_one_string_test(const wchar_t **argv, int expected_rc, const wchar_t *expected_out)
{
parser_t parser(PARSER_TYPE_GENERAL, true);
- wcstring &out = stdout_buffer;
- out.clear();
- int rc = builtin_string(parser, const_cast<wchar_t**>(argv));
+ io_streams_t streams;
+ streams.is_first_process_in_pipeline = true; // read from argv instead of stdin
+ int rc = builtin_string(parser, streams, const_cast<wchar_t**>(argv));
wcstring args;
for (int i = 0; argv[i] != 0; i++)
{
@@ -4036,12 +4037,12 @@ static void run_one_string_test(const wchar_t **argv, int expected_rc, const wch
err(L"Test failed on line %lu: [%ls]: expected return code %d but got %d",
__LINE__, args.c_str(), expected_rc, rc);
}
- else if (out != expected_out)
+ else if (streams.out.buffer() != expected_out)
{
err(L"Test failed on line %lu: [%ls]: expected [%ls] but got [%ls]",
__LINE__, args.c_str(),
escape_string(expected_out, ESCAPE_ALL).c_str(),
- escape_string(out, ESCAPE_ALL).c_str());
+ escape_string(streams.out.buffer(), ESCAPE_ALL).c_str());
}
}
diff --git a/src/input.cpp b/src/input.cpp
index aabd5b0e..d6c46ea5 100644
--- a/src/input.cpp
+++ b/src/input.cpp
@@ -339,7 +339,7 @@ static void input_mapping_insert_sorted(const input_mapping_t &new_mapping)
}
/* Adds an input mapping */
-void input_mapping_add(const wchar_t *sequence, const wchar_t **commands, size_t commands_len,
+void input_mapping_add(const wchar_t *sequence, const wchar_t * const *commands, size_t commands_len,
const wchar_t *mode, const wchar_t *sets_mode)
{
CHECK(sequence,);
diff --git a/src/input.h b/src/input.h
index 9f627db4..36d49a51 100644
--- a/src/input.h
+++ b/src/input.h
@@ -137,7 +137,7 @@ void input_mapping_add(const wchar_t *sequence, const wchar_t *command,
const wchar_t *mode = DEFAULT_BIND_MODE,
const wchar_t *new_mode = DEFAULT_BIND_MODE);
-void input_mapping_add(const wchar_t *sequence, const wchar_t **commands, size_t commands_len,
+void input_mapping_add(const wchar_t *sequence, const wchar_t * const *commands, size_t commands_len,
const wchar_t *mode = DEFAULT_BIND_MODE, const wchar_t *new_mode = DEFAULT_BIND_MODE);
struct input_mapping_name_t {
diff --git a/src/io.h b/src/io.h
index 86fc7962..8d12f685 100644
--- a/src/io.h
+++ b/src/io.h
@@ -216,6 +216,93 @@ shared_ptr<io_data_t> io_chain_get(io_chain_t &src, int fd);
/* Given a pair of fds, if an fd is used by the given io chain, duplicate that fd repeatedly until we find one that does not conflict, or we run out of fds. Returns the new fds by reference, closing the old ones. If we get an error, returns false (in which case both fds are closed and set to -1). */
bool pipe_avoid_conflicts_with_io_chain(int fds[2], const io_chain_t &ios);
+/** Class representing the output that a builtin can generate */
+class output_stream_t
+{
+private:
+ // no copying
+ output_stream_t(const output_stream_t &s);
+ void operator=(const output_stream_t &s);
+
+ wcstring buffer_;
+
+public:
+ output_stream_t()
+ {
+ }
+
+ void append(const wcstring &s)
+ {
+ this->buffer_.append(s);
+ }
+
+ void append(const wchar_t *s)
+ {
+ this->buffer_.append(s);
+ }
+
+ void append(wchar_t s)
+ {
+ this->buffer_.push_back(s);
+ }
+
+ void append(const wchar_t *s, size_t amt)
+ {
+ this->buffer_.append(s, amt);
+ }
+
+ void push_back(wchar_t c)
+ {
+ this->buffer_.push_back(c);
+ }
+
+ void append_format(const wchar_t *format, ...)
+ {
+ va_list va;
+ va_start(va, format);
+ ::append_formatv(this->buffer_, format, va);
+ va_end(va);
+ }
+
+ void append_formatv(const wchar_t *format, va_list va_orig)
+ {
+ ::append_formatv(this->buffer_, format, va_orig);
+ }
+
+ const wcstring &buffer() const
+ {
+ return this->buffer_;
+ }
+
+ bool empty() const
+ {
+ return buffer_.empty();
+ }
+};
+
+struct io_streams_t
+{
+ output_stream_t out;
+ output_stream_t err;
+
+ // fd representing stdin. This is not closed by the destructor.
+ int stdin_fd;
+
+ // Whether this is the first process in a pipeline
+ bool is_first_process_in_pipeline;
+
+ // Indicates whether stdout and stderr are redirected (e.g. to a file or piped)
+ bool out_is_redirected;
+ bool err_is_redirected;
+
+ // Actual IO redirections. This is only used by the source builtin. Unowned.
+ const io_chain_t *io_chain;
+
+ io_streams_t() : stdin_fd(-1), is_first_process_in_pipeline(false), out_is_redirected(false), err_is_redirected(false), io_chain(NULL)
+ {
+ }
+};
+
/** Print debug information about the specified IO redirection chain to stderr. */
void io_print(const io_chain_t &chain);
diff --git a/src/parse_execution.cpp b/src/parse_execution.cpp
index c50619ed..471914f6 100644
--- a/src/parse_execution.cpp
+++ b/src/parse_execution.cpp
@@ -410,7 +410,8 @@ parse_execution_result_t parse_execution_context_t::run_function_statement(const
const wcstring contents_str = wcstring(this->src, contents_start, contents_end - contents_start);
int definition_line_offset = this->line_offset_of_character_at_offset(contents_start);
wcstring error_str;
- int err = define_function(*parser, argument_list, contents_str, definition_line_offset, &error_str);
+ io_streams_t streams;
+ int err = define_function(*parser, streams, argument_list, contents_str, definition_line_offset, &error_str);
proc_set_last_status(err);
if (! error_str.empty())
diff --git a/src/parser.cpp b/src/parser.cpp
index 4b2bff93..5813bb12 100644
--- a/src/parser.cpp
+++ b/src/parser.cpp
@@ -516,7 +516,14 @@ void parser_t::expand_argument_list(const wcstring &arg_list_src, std::vector<co
}
}
-void parser_t::stack_trace(size_t block_idx, wcstring &buff) const
+wcstring parser_t::stack_trace() const
+{
+ wcstring trace;
+ this->stack_trace_internal(0, &trace);
+ return trace;
+}
+
+void parser_t::stack_trace_internal(size_t block_idx, wcstring *buff) const
{
/*
Check if we should end the recursion
@@ -533,8 +540,8 @@ void parser_t::stack_trace(size_t block_idx, wcstring &buff) const
*/
const event_block_t *eb = static_cast<const event_block_t *>(b);
wcstring description = event_get_desc(eb->event);
- append_format(buff, _(L"in event handler: %ls\n"), description.c_str());
- buff.append(L"\n");
+ append_format(*buff, _(L"in event handler: %ls\n"), description.c_str());
+ buff->append(L"\n");
/*
Stop recursing at event handler. No reason to believe that
@@ -561,19 +568,19 @@ void parser_t::stack_trace(size_t block_idx, wcstring &buff) const
{
const source_block_t *sb = static_cast<const source_block_t*>(b);
const wchar_t *source_dest = sb->source_file;
- append_format(buff, _(L"from sourcing file %ls\n"), user_presentable_path(source_dest).c_str());
+ append_format(*buff, _(L"from sourcing file %ls\n"), user_presentable_path(source_dest).c_str());
break;
}
case FUNCTION_CALL:
case FUNCTION_CALL_NO_SHADOW:
{
const function_block_t *fb = static_cast<const function_block_t*>(b);
- append_format(buff, _(L"in function '%ls'\n"), fb->name.c_str());
+ append_format(*buff, _(L"in function '%ls'\n"), fb->name.c_str());
break;
}
case SUBST:
{
- append_format(buff, _(L"in command substitution\n"));
+ append_format(*buff, _(L"in command substitution\n"));
break;
}
@@ -585,18 +592,18 @@ void parser_t::stack_trace(size_t block_idx, wcstring &buff) const
if (file)
{
- append_format(buff,
+ append_format(*buff,
_(L"\tcalled on line %d of file %ls\n"),
b->src_lineno,
user_presentable_path(file).c_str());
}
else if (is_within_fish_initialization)
{
- append_format(buff, _(L"\tcalled during startup\n"));
+ append_format(*buff, _(L"\tcalled during startup\n"));
}
else
{
- append_format(buff, _(L"\tcalled on standard input\n"));
+ append_format(*buff, _(L"\tcalled on standard input\n"));
}
if (b->type() == FUNCTION_CALL)
@@ -613,17 +620,17 @@ void parser_t::stack_trace(size_t block_idx, wcstring &buff) const
tmp.push_back(L' ');
tmp.append(process->argv(i));
}
- append_format(buff, _(L"\twith parameter list '%ls'\n"), tmp.c_str());
+ append_format(*buff, _(L"\twith parameter list '%ls'\n"), tmp.c_str());
}
}
- append_format(buff, L"\n");
+ append_format(*buff, L"\n");
}
/*
Recursively print the next block
*/
- parser_t::stack_trace(block_idx + 1, buff);
+ parser_t::stack_trace_internal(block_idx + 1, buff);
}
/**
@@ -753,7 +760,7 @@ wcstring parser_t::current_line()
line_info.push_back(L'\n');
}
- parser_t::stack_trace(0, line_info);
+ line_info.append(this->stack_trace());
return line_info;
}
@@ -1036,7 +1043,7 @@ void parser_t::get_backtrace(const wcstring &src, const parse_error_list_t &erro
output->append(description);
output->push_back(L'\n');
}
- this->stack_trace(0, *output);
+ output->append(this->stack_trace());
}
}
diff --git a/src/parser.h b/src/parser.h
index d7ee0570..13fa6d16 100644
--- a/src/parser.h
+++ b/src/parser.h
@@ -280,6 +280,9 @@ private:
*/
const wchar_t *is_function() const;
+ /* Helper for stack_trace() */
+ void stack_trace_internal(size_t block_idx, wcstring *out) const;
+
public:
/** Get the "principal" parser, whatever that is */
@@ -425,9 +428,9 @@ public:
const wchar_t *current_filename() const;
/**
- Write a stack trace starting at the specified block to the specified wcstring
+ Return a string representing the current stack trace
*/
- void stack_trace(size_t block_idx, wcstring &buff) const;
+ wcstring stack_trace() const;
};
#endif