void tokenize_variable_array( const wcstring &val, std::vector<wcstring> &out) { size_t pos = 0, end = val.size(); while (pos < end) { size_t next_pos = val.find(ARRAY_SEP, pos); if (next_pos == wcstring::npos) break; out.push_back(val.substr(pos, next_pos - pos)); pos = next_pos + 1; //skip the separator } out.push_back(val.substr(pos, end - pos)); }
// Dump a parse tree node in a form helpful to someone debugging the behavior of this program. static void dump_node(indent_t node_indent, const parse_node_t &node, const wcstring &source) { wchar_t nextc = L' '; wchar_t prevc = L' '; wcstring source_txt = L""; if (node.source_start != SOURCE_OFFSET_INVALID && node.source_length != SOURCE_OFFSET_INVALID) { int nextc_idx = node.source_start + node.source_length; if ((size_t)nextc_idx < source.size()) { nextc = source[node.source_start + node.source_length]; } if (node.source_start > 0) prevc = source[node.source_start - 1]; source_txt = source.substr(node.source_start, node.source_length); } wchar_t prevc_str[4] = {prevc, 0, 0, 0}; wchar_t nextc_str[4] = {nextc, 0, 0, 0}; if (prevc < L' ') { prevc_str[0] = L'\\'; prevc_str[1] = L'c'; prevc_str[2] = prevc + '@'; } if (nextc < L' ') { nextc_str[0] = L'\\'; nextc_str[1] = L'c'; nextc_str[2] = nextc + '@'; } fwprintf(stderr, L"{off %4u, len %4u, indent %2u, kw %ls, %ls} [%ls|%ls|%ls]\n", node.source_start, node.source_length, node_indent, keyword_description(node.keyword), token_type_description(node.type), prevc_str, source_txt.c_str(), nextc_str); }
/** Attempts tilde expansion of the string specified, modifying it in place. */ static void expand_home_directory(wcstring &input) { const wchar_t * const in = input.c_str(); if (in[0] == HOME_DIRECTORY) { int tilde_error = 0; size_t tail_idx; wcstring home; if (in[1] == '/' || in[1] == '\0') { /* Current users home directory */ home = env_get_string(L"HOME"); tail_idx = 1; } else { /* Some other users home directory */ const wchar_t *name_end = wcschr(in, L'/'); if (name_end) { tail_idx = name_end - in; } else { tail_idx = wcslen(in); } wcstring name_str = input.substr(1, tail_idx - 1); std::string name_cstr = wcs2string(name_str); struct passwd *userinfo = getpwnam(name_cstr.c_str()); if (userinfo == NULL) { tilde_error = 1; input[0] = L'~'; } else { home = str2wcstring(userinfo->pw_dir); } } if (! tilde_error) { input.replace(input.begin(), input.begin() + tail_idx, home); } } }
int wildcard_expand_string(const wcstring &wc, const wcstring &working_directory, expand_flags_t flags, std::vector<completion_t> *output) { assert(output != NULL); // Fuzzy matching only if we're doing completions. assert(flags.get(expand_flag::for_completions) || !flags.get(expand_flag::fuzzy_match)); // expand_flag::special_for_cd requires expand_flag::directories_only and // expand_flag::for_completions and expand_flag::no_descriptions. assert(!(flags.get(expand_flag::special_for_cd)) || ((flags.get(expand_flag::directories_only)) && (flags.get(expand_flag::for_completions)) && (flags.get(expand_flag::no_descriptions)))); // Hackish fix for issue #1631. We are about to call c_str(), which will produce a string // truncated at any embedded nulls. We could fix this by passing around the size, etc. However // embedded nulls are never allowed in a filename, so we just check for them and return 0 (no // matches) if there is an embedded null. if (wc.find(L'\0') != wcstring::npos) { return 0; } // Compute the prefix and base dir. The prefix is what we prepend for filesystem operations // (i.e. the working directory), the base_dir is the part of the wildcard consumed thus far, // which we also have to append. The difference is that the base_dir is returned as part of the // expansion, and the prefix is not. // // Check for a leading slash. If we find one, we have an absolute path: the prefix is empty, the // base dir is /, and the wildcard is the remainder. If we don't find one, the prefix is the // working directory, the base dir is empty. wcstring prefix = L"", base_dir = L"", effective_wc; if (string_prefixes_string(L"/", wc)) { base_dir = L"/"; effective_wc = wc.substr(1); } else { prefix = working_directory; effective_wc = wc; } wildcard_expander_t expander(prefix, flags, output); expander.expand(base_dir, effective_wc.c_str(), base_dir); return expander.status_code(); }
// Dump a parse tree node in a form helpful to someone debugging the behavior of this program. static void dump_node(indent_t node_indent, const parse_node_t &node, const wcstring &source) { int nextc_idx = node.source_start + node.source_length; wchar_t prevc = node.source_start > 0 ? source[node.source_start - 1] : L' '; wchar_t nextc = nextc_idx < source.size() ? source[nextc_idx] : L' '; wchar_t prevc_str[4] = {prevc, 0, 0, 0}; wchar_t nextc_str[4] = {nextc, 0, 0, 0}; if (prevc < L' ') { prevc_str[0] = L'\\'; prevc_str[1] = L'c'; prevc_str[2] = prevc + '@'; } if (nextc < L' ') { nextc_str[0] = L'\\'; nextc_str[1] = L'c'; nextc_str[2] = nextc + '@'; } fwprintf(stderr, L"{off %4d, len %4d, indent %2u, kw %ls, %ls} [%ls|%ls|%ls]\n", node.source_start, node.source_length, node_indent, keyword_description(node.keyword), token_type_description(node.type), prevc_str, source.substr(node.source_start, node.source_length).c_str(), nextc_str); }
void env_init(const struct config_paths_t *paths /* or NULL */) { /* env_read_only variables can not be altered directly by the user */ const wchar_t * const ro_keys[] = { L"status", L"history", L"version", L"_", L"LINES", L"COLUMNS", L"PWD", //L"SHLVL", // will be inserted a bit lower down L"FISH_VERSION", }; for (size_t i=0; i < sizeof ro_keys / sizeof *ro_keys; i++) { env_read_only.insert(ro_keys[i]); } /* Names of all dynamically calculated variables */ env_electric.insert(L"history"); env_electric.insert(L"status"); env_electric.insert(L"umask"); env_electric.insert(L"COLUMNS"); env_electric.insert(L"LINES"); top = new env_node_t; global_env = top; global = &top->env; /* Now the environemnt variable handling is set up, the next step is to insert valid data */ /* Import environment variables */ for (char **p = (environ ? environ : __environ); p && *p; p++) { const wcstring key_and_val = str2wcstring(*p); //like foo=bar size_t eql = key_and_val.find(L'='); if (eql == wcstring::npos) { // no equals found if (is_read_only(key_and_val) || is_electric(key_and_val)) continue; env_set(key_and_val, L"", ENV_EXPORT | ENV_GLOBAL); } else { wcstring key = key_and_val.substr(0, eql); if (is_read_only(key) || is_electric(key)) continue; wcstring val = key_and_val.substr(eql + 1); if (variable_is_colon_delimited_array(key)) { std::replace(val.begin(), val.end(), L':', ARRAY_SEP); } env_set(key, val.c_str(), ENV_EXPORT | ENV_GLOBAL); } } /* Set the given paths in the environment, if we have any */ if (paths != NULL) { env_set(FISH_DATADIR_VAR, paths->data.c_str(), ENV_GLOBAL); env_set(FISH_SYSCONFDIR_VAR, paths->sysconf.c_str(), ENV_GLOBAL); env_set(FISH_HELPDIR_VAR, paths->doc.c_str(), ENV_GLOBAL); env_set(FISH_BIN_DIR, paths->bin.c_str(), ENV_GLOBAL); } /* Set up the PATH variable */ setup_path(); /* Set up the USER variable */ if (env_get_string(L"USER").missing_or_empty()) { const struct passwd *pw = getpwuid(getuid()); if (pw && pw->pw_name) { const wcstring uname = str2wcstring(pw->pw_name); env_set(L"USER", uname.c_str(), ENV_GLOBAL | ENV_EXPORT); } } /* Set up the version variables */ wcstring version = str2wcstring(get_fish_version()); env_set(L"version", version.c_str(), ENV_GLOBAL); env_set(L"FISH_VERSION", version.c_str(), ENV_GLOBAL); /* Set up SHLVL variable */ const env_var_t shlvl_str = env_get_string(L"SHLVL"); wcstring nshlvl_str = L"1"; if (! shlvl_str.missing()) { wchar_t *end; long shlvl_i = wcstol(shlvl_str.c_str(), &end, 10); while (iswspace(*end)) ++end; /* skip trailing whitespace */ if (shlvl_i >= 0 && *end == '\0') { nshlvl_str = to_string<long>(shlvl_i + 1); } } env_set(L"SHLVL", nshlvl_str.c_str(), ENV_GLOBAL | ENV_EXPORT); env_read_only.insert(L"SHLVL"); /* Set up the HOME variable */ if (env_get_string(L"HOME").missing_or_empty()) { const env_var_t unam = env_get_string(L"USER"); char *unam_narrow = wcs2str(unam.c_str()); struct passwd *pw = getpwnam(unam_narrow); if (pw->pw_dir != NULL) { const wcstring dir = str2wcstring(pw->pw_dir); env_set(L"HOME", dir.c_str(), ENV_GLOBAL | ENV_EXPORT); } free(unam_narrow); } /* Set PWD */ env_set_pwd(); /* Set up universal variables. The empty string means to use the deafult path. */ assert(s_universal_variables == NULL); s_universal_variables = new env_universal_t(L""); s_universal_variables->load(); /* Set g_log_forks */ env_var_t log_forks = env_get_string(L"fish_log_forks"); g_log_forks = ! log_forks.missing_or_empty() && from_string<bool>(log_forks); /* Set g_use_posix_spawn. Default to true. */ env_var_t use_posix_spawn = env_get_string(L"fish_use_posix_spawn"); g_use_posix_spawn = (use_posix_spawn.missing_or_empty() ? true : from_string<bool>(use_posix_spawn)); /* Set fish_bind_mode to "default" */ env_set(FISH_BIND_MODE_VAR, DEFAULT_BIND_MODE, ENV_GLOBAL); /* Now that the global scope is fully initialized, add a toplevel local scope. This same local scope will persist throughout the lifetime of the fish process, and it will ensure that `set -l` commands run at the command-line don't affect the global scope. */ env_push(false); }
void env_init(const struct config_paths_t *paths /* or NULL */) { /* env_read_only variables can not be altered directly by the user */ const wchar_t * const ro_keys[] = { L"status", L"history", L"version", L"_", L"LINES", L"COLUMNS", L"PWD", L"SHLVL", L"FISH_VERSION", }; for (size_t i=0; i < sizeof ro_keys / sizeof *ro_keys; i++) { env_read_only.insert(ro_keys[i]); } /* HOME and USER should be writeable by root, since this can be a convenient way to install software. */ if (getuid() != 0) { env_read_only.insert(L"HOME"); env_read_only.insert(L"USER"); } /* Names of all dynamically calculated variables */ env_electric.insert(L"history"); env_electric.insert(L"status"); env_electric.insert(L"umask"); top = new env_node_t; global_env = top; global = &top->env; /* Now the environemnt variable handling is set up, the next step is to insert valid data */ /* Import environment variables */ for (char **p = (environ ? environ : __environ); p && *p; p++) { const wcstring key_and_val = str2wcstring(*p); //like foo=bar size_t eql = key_and_val.find(L'='); if (eql == wcstring::npos) { // no equals found env_set(key_and_val, L"", ENV_EXPORT); } else { wcstring key = key_and_val.substr(0, eql); wcstring val = key_and_val.substr(eql + 1); if (variable_can_be_array(val)) { std::replace(val.begin(), val.end(), L':', ARRAY_SEP); } env_set(key, val.c_str(), ENV_EXPORT | ENV_GLOBAL); } } /* Set the given paths in the environment, if we have any */ if (paths != NULL) { env_set(FISH_DATADIR_VAR, paths->data.c_str(), ENV_GLOBAL | ENV_EXPORT); env_set(FISH_SYSCONFDIR_VAR, paths->sysconf.c_str(), ENV_GLOBAL | ENV_EXPORT); env_set(FISH_HELPDIR_VAR, paths->doc.c_str(), ENV_GLOBAL | ENV_EXPORT); env_set(FISH_BIN_DIR, paths->bin.c_str(), ENV_GLOBAL | ENV_EXPORT); } /* Set up the PATH variable */ setup_path(); /* Set up the USER variable */ const struct passwd *pw = getpwuid(getuid()); if (pw && pw->pw_name) { const wcstring uname = str2wcstring(pw->pw_name); env_set(L"USER", uname.c_str(), ENV_GLOBAL | ENV_EXPORT); } /* Set up the version variables */ wcstring version = str2wcstring(FISH_BUILD_VERSION); env_set(L"version", version.c_str(), ENV_GLOBAL); env_set(L"FISH_VERSION", version.c_str(), ENV_GLOBAL); const env_var_t fishd_dir_wstr = env_get_string(L"FISHD_SOCKET_DIR"); const env_var_t user_dir_wstr = env_get_string(L"USER"); wchar_t * fishd_dir = fishd_dir_wstr.missing()?NULL:const_cast<wchar_t*>(fishd_dir_wstr.c_str()); wchar_t * user_dir = user_dir_wstr.missing()?NULL:const_cast<wchar_t*>(user_dir_wstr.c_str()); env_universal_init(fishd_dir , user_dir , &start_fishd, &universal_callback); /* Set up SHLVL variable */ const env_var_t shlvl_str = env_get_string(L"SHLVL"); wcstring nshlvl_str = L"1"; if (! shlvl_str.missing()) { long shlvl_i = wcstol(shlvl_str.c_str(), NULL, 10); if (shlvl_i >= 0) { nshlvl_str = to_string<long>(shlvl_i + 1); } } env_set(L"SHLVL", nshlvl_str.c_str(), ENV_GLOBAL | ENV_EXPORT); /* Set correct defaults for e.g. USER and HOME variables */ env_set_defaults(); /* Set g_log_forks */ env_var_t log_forks = env_get_string(L"fish_log_forks"); g_log_forks = ! log_forks.missing_or_empty() && from_string<bool>(log_forks); /* Set g_use_posix_spawn. Default to true. */ env_var_t use_posix_spawn = env_get_string(L"fish_use_posix_spawn"); g_use_posix_spawn = (use_posix_spawn.missing_or_empty() ? true : from_string<bool>(use_posix_spawn)); }
void s_write(screen_t *s, const wcstring &left_prompt, const wcstring &right_prompt, const wcstring &commandline, size_t explicit_len, const std::vector<highlight_spec_t> &colors, const std::vector<int> &indent, size_t cursor_pos, const page_rendering_t &pager, bool cursor_is_within_pager) { screen_data_t::cursor_t cursor_arr; // Turn the command line into the explicit portion and the autosuggestion. const wcstring explicit_command_line = commandline.substr(0, explicit_len); const wcstring autosuggestion = commandline.substr(explicit_len); // If we are using a dumb terminal, don't try any fancy stuff, just print out the text. // right_prompt not supported. if (is_dumb()) { const std::string prompt_narrow = wcs2string(left_prompt); const std::string command_line_narrow = wcs2string(explicit_command_line); write_loop(STDOUT_FILENO, "\r", 1); write_loop(STDOUT_FILENO, prompt_narrow.c_str(), prompt_narrow.size()); write_loop(STDOUT_FILENO, command_line_narrow.c_str(), command_line_narrow.size()); return; } s_check_status(s); const size_t screen_width = common_get_width(); // Completely ignore impossibly small screens. if (screen_width < 4) { return; } // Compute a layout. const screen_layout_t layout = compute_layout(s, screen_width, left_prompt, right_prompt, explicit_command_line, autosuggestion, indent); // Determine whether, if we have an autosuggestion, it was truncated. s->autosuggestion_is_truncated = !autosuggestion.empty() && autosuggestion != layout.autosuggestion; // Clear the desired screen. s->desired.resize(0); s->desired.cursor.x = s->desired.cursor.y = 0; // Append spaces for the left prompt. for (size_t i = 0; i < layout.left_prompt_space; i++) { s_desired_append_char(s, L' ', highlight_spec_t{}, 0, layout.left_prompt_space); } // If overflowing, give the prompt its own line to improve the situation. size_t first_line_prompt_space = layout.left_prompt_space; if (layout.prompts_get_own_line) { s_desired_append_char(s, L'\n', highlight_spec_t{}, 0, 0); first_line_prompt_space = 0; } // Reconstruct the command line. wcstring effective_commandline = explicit_command_line + layout.autosuggestion; // Output the command line. size_t i; for (i = 0; i < effective_commandline.size(); i++) { // Grab the current cursor's x,y position if this character matches the cursor's offset. if (!cursor_is_within_pager && i == cursor_pos) { cursor_arr = s->desired.cursor; } s_desired_append_char(s, effective_commandline.at(i), colors[i], indent[i], first_line_prompt_space); } // Cursor may have been at the end too. if (!cursor_is_within_pager && i == cursor_pos) { cursor_arr = s->desired.cursor; } // Now that we've output everything, set the cursor to the position that we saved in the loop // above. s->desired.cursor = cursor_arr; if (cursor_is_within_pager) { s->desired.cursor.x = (int)cursor_pos; s->desired.cursor.y = (int)s->desired.line_count(); } // Append pager_data (none if empty). s->desired.append_lines(pager.screen_data); s_update(s, layout.left_prompt, layout.right_prompt); s_save_status(s); }
void s_write(screen_t *s, const wcstring &left_prompt, const wcstring &right_prompt, const wcstring &commandline, size_t explicit_len, const int *colors, const int *indent, size_t cursor_pos) { screen_data_t::cursor_t cursor_arr; CHECK(s,); CHECK(indent,); /* Turn the command line into the explicit portion and the autosuggestion */ const wcstring explicit_command_line = commandline.substr(0, explicit_len); const wcstring autosuggestion = commandline.substr(explicit_len); /* If we are using a dumb terminal, don't try any fancy stuff, just print out the text. right_prompt not supported. */ if (is_dumb()) { const std::string prompt_narrow = wcs2string(left_prompt); const std::string command_line_narrow = wcs2string(explicit_command_line); write_loop(STDOUT_FILENO, "\r", 1); write_loop(STDOUT_FILENO, prompt_narrow.c_str(), prompt_narrow.size()); write_loop(STDOUT_FILENO, command_line_narrow.c_str(), command_line_narrow.size()); return; } s_check_status(s); const size_t screen_width = common_get_width(); /* Completely ignore impossibly small screens */ if (screen_width < 4) { return; } /* Compute a layout */ const screen_layout_t layout = compute_layout(s, screen_width, left_prompt, right_prompt, explicit_command_line, autosuggestion, indent); /* Determine whether, if we have an autosuggestion, it was truncated */ s->autosuggestion_is_truncated = ! autosuggestion.empty() && autosuggestion != layout.autosuggestion; /* Clear the desired screen */ s->desired.resize(0); s->desired.cursor.x = s->desired.cursor.y = 0; /* Append spaces for the left prompt */ for (size_t i=0; i < layout.left_prompt_space; i++) { s_desired_append_char(s, L' ', 0, 0, layout.left_prompt_space); } /* If overflowing, give the prompt its own line to improve the situation. */ size_t first_line_prompt_space = layout.left_prompt_space; if (layout.prompts_get_own_line) { s_desired_append_char(s, L'\n', 0, 0, 0); first_line_prompt_space = 0; } /* Reconstruct the command line */ wcstring effective_commandline = explicit_command_line + layout.autosuggestion; /* Output the command line */ size_t i; for (i=0; i < effective_commandline.size(); i++) { int color = colors[i]; if (i == cursor_pos) { color = 0; } if (i == cursor_pos) { cursor_arr = s->desired.cursor; } s_desired_append_char(s, effective_commandline.at(i), color, indent[i], first_line_prompt_space); } if (i == cursor_pos) { cursor_arr = s->desired.cursor; } s->desired.cursor = cursor_arr; s_update(s, layout.left_prompt.c_str(), layout.right_prompt.c_str()); s_save_status(s); }
// Helper function to abstract the repeat until logic from string_repeat // returns the to_repeat string, repeated until max char has been reached. static wcstring wcsrepeat_until(const wcstring &to_repeat, size_t max) { size_t count = max / to_repeat.length(); size_t mod = max % to_repeat.length(); return wcsrepeat(to_repeat, count) + to_repeat.substr(0, mod); }