diff --git a/src/builtin.cpp b/src/builtin.cpp index e4f14160f..a3e5eedbc 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; - -/** - 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) - { - stderr_buffer.append(s); - stderr_buffer.append(L": "); - } char *err = strerror(errno); - if (err) + if (s != NULL) + { + streams.err.append(s); + streams.err.append(L": "); + } + 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 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 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 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 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"" : 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 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 3a1601957..859c48745 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(); @@ -122,17 +104,6 @@ wcstring_list_t builtin_get_names(); /** Insert all builtin names into list. */ void builtin_get_names(std::vector *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 diff --git a/src/builtin_commandline.cpp b/src/builtin_commandline.cpp index 352baf1c1..971ba2082 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 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 e206f9aba..e8dd2d9dd 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 32079287c..6f4b57a8c 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 916166b0f..b2bcfc7c4 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 c7bde4fba..c3cde7915 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 &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 &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 &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 &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 &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 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 84d5fa9c2..e80d06ec0 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 64b126b79..3b2dadd65 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 50cb1102c..2d8441293 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 88bbf480a..ff7443e58 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 a4ef163ab..ae9d2ef30 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 41c9c3e0b..2083517ed 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 39385b06a..5366e663c 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 block_output_io_buffer; + + // This is the io_streams we pass to internal builtins + std::auto_ptr 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 stdout_io = process_net_io_chain.get_io_for_fd(STDOUT_FILENO); const shared_ptr 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 1b3147994..1b71411cc 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(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(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 aabd5b0e4..d6c46ea5a 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 9f627db4e..36d49a51d 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 86fc79620..8d12f685a 100644 --- a/src/io.h +++ b/src/io.h @@ -216,6 +216,93 @@ shared_ptr 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 c50619edc..471914f69 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 4b2bff930..5813bb12b 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::vectorstack_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(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(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(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 d7ee0570a..13fa6d16e 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