/** Silly function */ static void builtin_complete_remove2(const wchar_t *cmd, int cmd_type, const wchar_t *short_opt, const wcstring_list_t &gnu_opt, const wcstring_list_t &old_opt) { const wchar_t *s = (wchar_t *)short_opt; if (*s) { for (; *s; s++) { if (old_opt.empty() && gnu_opt.empty()) { complete_remove(cmd, cmd_type, *s, 0, 0); } else { builtin_complete_remove3(cmd, cmd_type, *s, gnu_opt, 0); builtin_complete_remove3(cmd, cmd_type, *s, old_opt, 1); } } } else if (gnu_opt.empty() && old_opt.empty()) { complete_remove(cmd, cmd_type, 0, 0, 0); } else { builtin_complete_remove3(cmd, cmd_type, 0, gnu_opt, 0); builtin_complete_remove3(cmd, cmd_type, 0, old_opt, 1); } }
/** Silly function */ static void builtin_complete_remove(const wcstring_list_t &cmd, const wcstring_list_t &path, const wchar_t *short_opt, const wcstring_list_t &gnu_opt, const wcstring_list_t &old_opt) { for (size_t i=0; i<cmd.size(); i++) { builtin_complete_remove2(cmd.at(i).c_str(), COMMAND, short_opt, gnu_opt, old_opt); } for (size_t i=0; i<path.size(); i++) { builtin_complete_remove2(path.at(i).c_str(), PATH, short_opt, gnu_opt, old_opt); } }
static void update_export_array_if_necessary(bool recalc) { ASSERT_IS_MAIN_THREAD(); if (recalc && !get_proc_had_barrier()) { set_proc_had_barrier(true); env_universal_barrier(); } if (has_changed_exported) { std::map<wcstring, wcstring> vals; debug(4, L"env_export_arr() recalc"); get_exported(top, &vals); if (uvars()) { const wcstring_list_t uni = uvars()->get_names(true, false); for (size_t i = 0; i < uni.size(); i++) { const wcstring &key = uni.at(i); const env_var_t val = uvars()->get(key); if (!val.missing() && val != ENV_NULL) { // Note that std::map::insert does NOT overwrite a value already in the map, // which we depend on here. vals.insert(std::pair<wcstring, wcstring>(key, val)); } } } std::vector<std::string> local_export_buffer; export_func(vals, local_export_buffer); export_array.set(local_export_buffer); has_changed_exported = false; } }
/// This handles the common case of setting the entire var to a set of values. static int set_var_array(const wchar_t *cmd, set_cmd_opts_t &opts, const wchar_t *varname, wcstring_list_t &new_values, int argc, wchar_t **argv, parser_t &parser, io_streams_t &streams) { UNUSED(cmd); UNUSED(parser); UNUSED(streams); if (opts.prepend || opts.append) { if (opts.prepend) { for (int i = 0; i < argc; i++) new_values.push_back(argv[i]); } auto var_str = parser.vars().get(varname, ENV_DEFAULT); wcstring_list_t var_array; if (var_str) var_str->to_list(var_array); new_values.insert(new_values.end(), var_array.begin(), var_array.end()); if (opts.append) { for (int i = 0; i < argc; i++) new_values.push_back(argv[i]); } } else { for (int i = 0; i < argc; i++) new_values.push_back(argv[i]); } return STATUS_CMD_OK; }
static void builtin_complete_remove_cmd(const wcstring &cmd, int cmd_type, const wchar_t *short_opt, const wcstring_list_t &gnu_opt, const wcstring_list_t &old_opt) { bool removed = false; size_t i; for (i=0; short_opt[i] != L'\0'; i++) { complete_remove(cmd, cmd_type, wcstring(1, short_opt[i]), option_type_short); removed = true; } for (i=0; i < old_opt.size(); i++) { complete_remove(cmd, cmd_type, old_opt.at(i), option_type_single_long); removed = true; } for (i=0; i < gnu_opt.size(); i++) { complete_remove(cmd, cmd_type, gnu_opt.at(i), option_type_double_long); removed = true; } if (! removed) { // This means that all loops were empty complete_remove_all(cmd, cmd_type); } }
wcstring_list_t env_get_names(int flags) { scoped_lock lock(env_lock); wcstring_list_t result; std::set<wcstring> names; int show_local = flags & ENV_LOCAL; int show_global = flags & ENV_GLOBAL; int show_universal = flags & ENV_UNIVERSAL; env_node_t *n=top; const bool show_exported = (flags & ENV_EXPORT) || !(flags & ENV_UNEXPORT); const bool show_unexported = (flags & ENV_UNEXPORT) || !(flags & ENV_EXPORT); if (!show_local && !show_global && !show_universal) { show_local =show_universal = show_global=1; } if (show_local) { while (n) { if (n == global_env) break; add_key_to_string_set(n->env, &names, show_exported, show_unexported); if (n->new_scope) break; else n = n->next; } } if (show_global) { add_key_to_string_set(global_env->env, &names, show_exported, show_unexported); if (show_unexported) { result.insert(result.end(), env_electric.begin(), env_electric.end()); } if (show_exported) { result.push_back(L"COLUMNS"); result.push_back(L"LINES"); } } if (show_universal && uvars()) { const wcstring_list_t uni_list = uvars()->get_names(show_exported, show_unexported); names.insert(uni_list.begin(), uni_list.end()); } result.insert(result.end(), names.begin(), names.end()); return result; }
void function_prepare_environment(const wcstring &name, const wchar_t *const *argv, const std::map<wcstring, env_var_t> &inherited_vars) { // Three components of the environment: // 1. argv // 2. named arguments // 3. inherited variables env_set_argv(argv); const wcstring_list_t named_arguments = function_get_named_arguments(name); if (!named_arguments.empty()) { const wchar_t *const *arg; size_t i; for (i = 0, arg = argv; i < named_arguments.size(); i++) { env_set(named_arguments.at(i).c_str(), *arg, ENV_LOCAL | ENV_USER); if (*arg) arg++; } } for (std::map<wcstring, env_var_t>::const_iterator it = inherited_vars.begin(), end = inherited_vars.end(); it != end; ++it) { env_set(it->first, it->second.missing() ? NULL : it->second.c_str(), ENV_LOCAL | ENV_USER); } }
static std::map<wcstring, env_var_t> snapshot_vars(const wcstring_list_t &vars) { std::map<wcstring, env_var_t> result; for (wcstring_list_t::const_iterator it = vars.begin(), end = vars.end(); it != end; ++it) { result.insert(std::make_pair(*it, env_get_string(*it))); } return result; }
static int update_values( wcstring_list_t &list, std::vector<long> &indexes, wcstring_list_t &values ) { size_t i; /* Replace values where needed */ for( i = 0; i < indexes.size(); i++ ) { /* The '- 1' below is because the indices in fish are one-based, but the vector uses zero-based indices */ long ind = indexes[i] - 1; const wcstring newv = values[ i ]; if( ind < 0 ) { return 1; } if ( ind >= list.size() ) { list.resize( ind+1 ); } // free((void *) al_get(list, ind)); list[ ind ] = newv; } return 0; }
static void print_colors(io_streams_t &streams) { const wcstring_list_t result = rgb_color_t::named_color_names(); size_t i; for (i = 0; i < result.size(); i++) { streams.out.append(result.at(i)); streams.out.push_back(L'\n'); } }
/// Print terminfo key binding names to string buffer used for standard output. /// /// \param all if set, all terminfo key binding names will be printed. If not set, only ones that /// are defined for this terminal are printed. void builtin_bind_t::key_names(bool all, io_streams_t &streams) { const wcstring_list_t names = input_terminfo_get_names(!all); for (size_t i = 0; i < names.size(); i++) { const wcstring &name = names.at(i); streams.out.append_format(L"%ls\n", name.c_str()); } }
static void print_colors(void) { const wcstring_list_t result = rgb_color_t::named_color_names(); size_t i; for (i=0; i < result.size(); i++) { stdout_buffer.append(result.at(i)); stdout_buffer.push_back(L'\n'); } }
static bool run_test_test(int expected, wcstring_list_t &lst) { parser_t parser(PARSER_TYPE_GENERAL, true); size_t i, count = lst.size(); wchar_t **argv = new wchar_t *[count+2]; argv[0] = (wchar_t *)L"test"; for (i=0; i < count; i++) { argv[i+1] = (wchar_t *)lst.at(i).c_str(); } argv[i+1] = NULL; int result = builtin_test(parser, argv); delete[] argv; return expected == result; }
/** Silly function */ static void builtin_complete_remove3(const wchar_t *cmd, int cmd_type, wchar_t short_opt, const wcstring_list_t &long_opt) { for (size_t i=0; i<long_opt.size(); i++) { complete_remove(cmd, cmd_type, short_opt, long_opt.at(i).c_str()); } }
void history_tests_t::test_history_races_pound_on_history() { /* Called in child process to modify history */ history_t *hist = new history_t(L"race_test"); hist->chaos_mode = true; const wcstring_list_t lines = generate_history_lines(getpid()); for (size_t idx = 0; idx < lines.size(); idx++) { const wcstring &line = lines.at(idx); hist->add(line); hist->save(); } delete hist; }
/// Silly function. static void builtin_complete_add(const wcstring_list_t &cmd, const wcstring_list_t &path, const wchar_t *short_opt, wcstring_list_t &gnu_opt, wcstring_list_t &old_opt, int result_mode, const wchar_t *condition, const wchar_t *comp, const wchar_t *desc, int flags) { for (size_t i = 0; i < cmd.size(); i++) { builtin_complete_add2(cmd.at(i).c_str(), COMMAND, short_opt, gnu_opt, old_opt, result_mode, condition, comp, desc, flags); } for (size_t i = 0; i < path.size(); i++) { builtin_complete_add2(path.at(i).c_str(), PATH, short_opt, gnu_opt, old_opt, result_mode, condition, comp, desc, flags); } }
/** Erase from a list of wcstring values at specified indexes */ static void erase_values(wcstring_list_t &list, const std::vector<long> &indexes) { // Make a set of indexes. // This both sorts them into ascending order and removes duplicates. const std::set<long> indexes_set(indexes.begin(), indexes.end()); // Now walk the set backwards, so we encounter larger indexes first, and remove elements at the given (1-based) indexes. std::set<long>::const_reverse_iterator iter; for (iter = indexes_set.rbegin(); iter != indexes_set.rend(); iter++) { long val = *iter; if (val > 0 && val <= list.size()) { // One-based indexing! list.erase(list.begin() + val - 1); } } }
static bool binary_primary_evaluate(test_expressions::token_t token, const wcstring &left, const wcstring &right, wcstring_list_t &errors) { using namespace test_expressions; long long left_num, right_num; switch (token) { case test_string_equal: return left == right; case test_string_not_equal: return left != right; case test_number_equal: return parse_number(left, &left_num) && parse_number(right, &right_num) && left_num == right_num; case test_number_not_equal: return parse_number(left, &left_num) && parse_number(right, &right_num) && left_num != right_num; case test_number_greater: return parse_number(left, &left_num) && parse_number(right, &right_num) && left_num > right_num; case test_number_greater_equal: return parse_number(left, &left_num) && parse_number(right, &right_num) && left_num >= right_num; case test_number_lesser: return parse_number(left, &left_num) && parse_number(right, &right_num) && left_num < right_num; case test_number_lesser_equal: return parse_number(left, &left_num) && parse_number(right, &right_num) && left_num <= right_num; default: errors.push_back(format_string(L"Unknown token type in %s", __func__)); return false; } }
// IEEE 1003.1 says nothing about what it means for two strings to be "algebraically equal". For // example, should we interpret 0x10 as 0, 10, or 16? Here we use only base 10 and use wcstoll, // which allows for leading + and -, and whitespace. This is consistent, albeit a bit more lenient // since we allow trailing whitespace, with other implementations such as bash. static bool parse_number(const wcstring &arg, long long *out, wcstring_list_t &errors) { *out = fish_wcstoll(arg.c_str()); if (errno) { errors.push_back(format_string(_(L"invalid integer '%ls'"), arg.c_str())); } return !errno; }
/** Replace completion strings with a comp_t structure */ static std::vector<comp_t *> mangle_completions( wcstring_list_t &lst, const wchar_t *prefix ) { std::vector<comp_t *> result; for( size_t i=0; i<lst.size(); i++ ) { wcstring &next = lst.at(i); size_t start, end; comp_t zerod = {}; comp_t *comp = new comp_t(zerod); for( start=end=0; 1; end++ ) { wchar_t c = next.c_str()[end]; if( (c == COMPLETE_ITEM_SEP) || (c==COMPLETE_SEP) || !c) { wcstring start2 = wcstring(next, start, end - start); wcstring str = escape_string(start2, ESCAPE_ALL | ESCAPE_NO_QUOTED); comp->comp_width += my_wcswidth( str.c_str() ); comp->comp.push_back(str); start = end+1; } if( c == COMPLETE_SEP ) { comp->desc = next.c_str() + start; break; } if( !c ) break; } comp->comp_width += (int)(my_wcswidth(prefix)*comp->comp.size() + 2*(comp->comp.size()-1)); comp->desc_width = comp->desc.empty()?0:my_wcswidth( comp->desc.c_str() ); comp->pref_width = comp->comp_width + comp->desc_width + (comp->desc_width?4:0); result.push_back(comp); } recalc_width( result, prefix ); return result; }
bool unary_operator::evaluate(wcstring_list_t &errors) { if (token == test_bang) { assert(subject.get()); return !subject->evaluate(errors); } errors.push_back(format_string(L"Unknown token type in %s", __func__)); return false; }
// Validate the given path `list`. If there are any entries referring to invalid directories which // contain a colon, then complain. Return true if any path element was valid, false if not. static bool validate_path_warning_on_colons(const wchar_t *cmd, const wchar_t *key, //!OCLINT(npath complexity) const wcstring_list_t &list, io_streams_t &streams, const environment_t &vars) { // Always allow setting an empty value. if (list.empty()) return true; bool any_success = false; // Don't bother validating (or complaining about) values that are already present. When // determining already-present values, use ENV_DEFAULT instead of the passed-in scope because // in: // // set -l PATH stuff $PATH // // where we are temporarily shadowing a variable, we want to compare against the shadowed value, // not the (missing) local value. Also don't bother to complain about relative paths, which // don't start with /. wcstring_list_t existing_values; const auto existing_variable = vars.get(key, ENV_DEFAULT); if (!existing_variable.missing_or_empty()) existing_variable->to_list(existing_values); for (const wcstring &dir : list) { if (!string_prefixes_string(L"/", dir) || contains(existing_values, dir)) { any_success = true; continue; } const wchar_t *colon = std::wcschr(dir.c_str(), L':'); bool looks_like_colon_sep = colon && colon[1]; if (!looks_like_colon_sep && any_success) { // Once we have one valid entry, skip the remaining ones unless we might warn. continue; } struct stat buff; bool valid = true; if (wstat(dir, &buff) == -1) { valid = false; } else if (!S_ISDIR(buff.st_mode)) { errno = ENOTDIR; valid = false; } else if (waccess(dir, X_OK) == -1) { valid = false; } if (valid) { any_success = true; } else if (looks_like_colon_sep) { streams.err.append_format(BUILTIN_SET_PATH_ERROR, cmd, key, dir.c_str(), std::strerror(errno)); streams.err.append_format(BUILTIN_SET_PATH_HINT, cmd, key, key, std::wcschr(dir.c_str(), L':') + 1); } } return any_success; }
static int update_values(wcstring_list_t &list, std::vector<long> &indexes, wcstring_list_t &values) { // Replace values where needed. for (size_t i = 0; i < indexes.size(); i++) { // The '- 1' below is because the indices in fish are one-based, but the vector uses // zero-based indices. long ind = indexes[i] - 1; const wcstring newv = values[i]; if (ind < 0) { return 1; } if ((size_t)ind >= list.size()) { list.resize(ind + 1); } list[ind] = newv; } return 0; }
/// Silly function. static void builtin_complete_add2(const wchar_t *cmd, int cmd_type, const wchar_t *short_opt, const wcstring_list_t &gnu_opt, const wcstring_list_t &old_opt, int result_mode, const wchar_t *condition, const wchar_t *comp, const wchar_t *desc, int flags) { size_t i; const wchar_t *s; for (s = short_opt; *s; s++) { complete_add(cmd, cmd_type, wcstring(1, *s), option_type_short, result_mode, condition, comp, desc, flags); } for (i = 0; i < gnu_opt.size(); i++) { complete_add(cmd, cmd_type, gnu_opt.at(i), option_type_double_long, result_mode, condition, comp, desc, flags); } for (i = 0; i < old_opt.size(); i++) { complete_add(cmd, cmd_type, old_opt.at(i), option_type_single_long, result_mode, condition, comp, desc, flags); } if (old_opt.empty() && gnu_opt.empty() && wcslen(short_opt) == 0) { complete_add(cmd, cmd_type, wcstring(), option_type_args_only, result_mode, condition, comp, desc, flags); } }
void parse_util_set_argv( const wchar_t * const *argv, const wcstring_list_t &named_arguments ) { if( *argv ) { const wchar_t * const *arg; wcstring sb; for( arg=argv; *arg; arg++ ) { if( arg != argv ) { sb.append(ARRAY_SEP_STR); } sb.append(*arg); } env_set( L"argv", sb.c_str(), ENV_LOCAL ); } else { env_set( L"argv", 0, ENV_LOCAL ); } if( named_arguments.size() ) { const wchar_t * const *arg; size_t i; for( i=0, arg=argv; i < named_arguments.size(); i++ ) { env_set( named_arguments.at(i).c_str(), *arg, ENV_LOCAL ); if( *arg ) arg++; } } }
bool combining_expression::evaluate(wcstring_list_t &errors) { switch (token) { case test_combine_and: case test_combine_or: { /* One-element case */ if (subjects.size() == 1) return subjects.at(0)->evaluate(errors); /* Evaluate our lists, remembering that AND has higher precedence than OR. We can visualize this as a sequence of OR expressions of AND expressions. */ assert(combiners.size() + 1 == subjects.size()); assert(! subjects.empty()); size_t idx = 0, max = subjects.size(); bool or_result = false; while (idx < max) { if (or_result) { /* Short circuit */ break; } /* Evaluate a stream of AND starting at given subject index. It may only have one element. */ bool and_result = true; for (; idx < max; idx++) { /* Evaluate it, short-circuiting */ and_result = and_result && subjects.at(idx)->evaluate(errors); /* If the combiner at this index (which corresponding to how we combine with the next subject) is not AND, then exit the loop */ if (idx + 1 < max && combiners.at(idx) != test_combine_and) { idx++; break; } } /* OR it in */ or_result = or_result || and_result; } return or_result; } default: errors.push_back(format_string(L"Unknown token type in %s", __func__)); return BUILTIN_TEST_FAIL; } }
bool unary_operator::evaluate(wcstring_list_t &errors) { switch (token) { case test_bang: assert(subject.get()); return ! subject->evaluate(errors); default: errors.push_back(format_string(L"Unknown token type in %s", __func__)); return false; } }
/** Merge multiple completions with the same description to the same line */ static void join_completions( wcstring_list_t lst ) { std::map<wcstring, long> desc_table; for( size_t i=0; i<lst.size(); i++ ) { const wchar_t *item = lst.at(i).c_str(); const wchar_t *desc = wcschr( item, COMPLETE_SEP ); long prev_idx; if( !desc ) continue; desc++; prev_idx = desc_table[desc] - 1; if( prev_idx == -1 ) { desc_table[desc] = (long)(i+1); } else { const wchar_t *old = lst.at(i).c_str(); const wchar_t *old_end = wcschr( old, COMPLETE_SEP ); if( old_end ) { wcstring foo; foo.append(old, old_end - old); foo.push_back(COMPLETE_ITEM_SEP); foo.append(item); lst.at(prev_idx) = foo; lst.at(i).clear(); } } } /* Remove empty strings */ lst.erase(remove(lst.begin(), lst.end(), wcstring()), lst.end()); }
/** Substitute any series of whitespace with a single space character inside completion descriptions. Remove all whitespace from beginning/end of completion descriptions. */ static void mangle_descriptions( wcstring_list_t &lst ) { int skip; for( size_t i=0; i<lst.size(); i++ ) { wcstring &next = lst.at(i); size_t in, out; skip=1; size_t next_idx = 0; while( next_idx < next.size() && next[next_idx] != COMPLETE_SEP ) next_idx++; if( next_idx == next.size() ) continue; in=out=next_idx + 1; while( in < next.size() ) { if( next[in] == L' ' || next[in]==L'\t' || next[in]<32 ) { if( !skip ) next[out++]=L' '; skip=1; } else { next[out++] = next[in]; skip=0; } in++; } next.resize(out); } }
/** Put exported or unexported variables in a string list */ void env_universal_common_get_names(wcstring_list_t &lst, int show_exported, int show_unexported) { env_var_table_t::const_iterator iter; for (iter = env_universal_var.begin(); iter != env_universal_var.end(); ++iter) { const wcstring& key = iter->first; const var_uni_entry_t *e = iter->second; if ((e->exportv && show_exported) || (!e->exportv && show_unexported)) { lst.push_back(key); } } }