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.
This commit is contained in:
ridiculousfish 2015-09-21 11:24:49 -07:00
parent fb615843b3
commit c1bd3b5824
23 changed files with 798 additions and 889 deletions

File diff suppressed because it is too large Load Diff

View File

@ -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();
@ -122,17 +104,6 @@ wcstring_list_t builtin_get_names();
/** Insert all builtin names into list. */
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.
*/
@ -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

View File

@ -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;
}

View File

@ -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
{

View File

@ -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;

View File

@ -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;

View File

@ -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);

View File

@ -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;

View File

@ -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);
}

View File

@ -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

View File

@ -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;
}

View File

@ -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 */

View File

@ -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.

View File

@ -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;
}

View File

@ -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

View File

@ -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

View File

@ -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());
}
}

View File

@ -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,);

View File

@ -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 {

View File

@ -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);

View File

@ -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())

View File

@ -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());
}
}

View File

@ -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