/** Set up default values for various variables if not defined. */ static void env_set_defaults() { if (env_get_string(L"USER").missing()) { struct passwd *pw = getpwuid(getuid()); if (pw->pw_name != NULL) { const wcstring wide_name = str2wcstring(pw->pw_name); env_set(L"USER", wide_name.c_str(), ENV_GLOBAL); } } if (env_get_string(L"HOME").missing()) { 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); } free(unam_narrow); } env_set_pwd(); }
/** Get environment variable value. */ static env_var_t fishd_env_get(const char *key) { const char *env = getenv(key); if (env != NULL) { return env_var_t(str2wcstring(env)); } else { const wcstring wkey = str2wcstring(key); return env_universal_common_get(wkey); } }
/** Get environment variable value. */ static env_var_t fishd_env_get(const char *key) { const char *env = getenv(key); if (env != NULL) { return env_var_t(str2wcstring(env)); } else { const wcstring wkey = str2wcstring(key); const wchar_t *tmp = env_universal_common_get(wkey); return tmp ? env_var_t(tmp) : env_var_t::missing_var(); } }
void history_t::populate_from_bash(FILE *stream) { /* Bash's format is very simple: just lines with #s for comments. Ignore a few commands that are bash-specific. This list ought to be expanded. */ std::string line; for (;;) { line.clear(); bool success = false, has_newline = false; /* Loop until we've read a line */ do { char buff[128]; success = !! fgets(buff, sizeof buff, stream); if (success) { /* Skip the newline */ char *newline = strchr(buff, '\n'); if (newline) *newline = '\0'; has_newline = (newline != NULL); /* Append what we've got */ line.append(buff); } } while (success && ! has_newline); /* Maybe add this line */ if (should_import_bash_history_line(line)) { this->add(str2wcstring(line)); } if (line.empty()) break; } }
bool wreaddir_for_dirs(DIR *dir, wcstring *out_name) { struct dirent *result = NULL; while (result == NULL) { struct dirent *d = readdir(dir); if (!d) break; #if HAVE_STRUCT_DIRENT_D_TYPE switch (d->d_type) { // These may be directories case DT_DIR: case DT_LNK: case DT_UNKNOWN: result = d; break; // Nothing else can default: break; } #else /* We can't determine if it's a directory or not, so just return it */ result = d; #endif } if (result && out_name) { *out_name = str2wcstring(result->d_name); } return result != NULL; }
static const wchar_t *string_get_arg_stdin(wcstring *storage, const io_streams_t &streams) { std::string arg; for (;;) { char ch = '\0'; long rc = read_blocked(streams.stdin_fd, &ch, 1); if (rc < 0) { // failure return 0; } if (rc == 0) { // EOF if (arg.empty()) { return 0; } break; } if (ch == '\n') { break; } arg += ch; } *storage = str2wcstring(arg); return storage->c_str(); }
bool wreaddir(DIR *dir, std::wstring &out_name) { struct dirent *d = readdir(dir); if (!d) return false; out_name = str2wcstring(d->d_name); return true; }
wchar_t *wrealpath(const wcstring &pathname, wchar_t *resolved_path) { cstring narrow_path = wcs2string(pathname); char *narrow_res = realpath(narrow_path.c_str(), NULL); if (!narrow_res) return NULL; wchar_t *res; wcstring wide_res = str2wcstring(narrow_res); if (resolved_path) { wcslcpy(resolved_path, wide_res.c_str(), PATH_MAX); res = resolved_path; } else { res = wcsdup(wide_res.c_str()); } #if __APPLE__ && __DARWIN_C_LEVEL < 200809L // OS X Snow Leopard is broken with respect to the dynamically allocated buffer returned by // realpath(). It's not dynamically allocated so attempting to free that buffer triggers a // malloc/free error. Thus we don't attempt the free in this case. #else free(narrow_res); #endif return res; }
bool process_iterator_t::next_process(wcstring *out_str, pid_t *out_pid) { wcstring cmd; pid_t pid = 0; while (cmd.empty()) { wcstring name; if (! dir || ! wreaddir(dir, name)) break; if (!iswnumeric(name.c_str())) continue; wcstring path = wcstring(L"/proc/") + name; struct stat buf; if (wstat(path, &buf)) continue; if (buf.st_uid != getuid()) continue; /* remember the pid */ pid = fish_wcstoi(name.c_str(), NULL, 10); /* the 'cmdline' file exists, it should contain the commandline */ FILE *cmdfile; if ((cmdfile=wfopen(path + L"/cmdline", "r"))) { wcstring full_command_line; signal_block(); fgetws2(&full_command_line, cmdfile); signal_unblock(); /* The command line needs to be escaped */ cmd = tok_first(full_command_line.c_str()); } #ifdef SunOS else if ((cmdfile=wfopen(path + L"/psinfo", "r"))) { psinfo_t info; if (fread(&info, sizeof(info), 1, cmdfile)) { /* The filename is unescaped */ cmd = str2wcstring(info.pr_fname); } } #endif if (cmdfile) fclose(cmdfile); } bool result = ! cmd.empty(); if (result) { *out_str = cmd; *out_pid = pid; } return result; }
static struct config_paths_t determine_config_directory_paths(const char *argv0) { struct config_paths_t paths; bool done = false; std::string exec_path = get_executable_path(argv0); if (get_realpath(exec_path)) { debug(2, L"exec_path: '%s'", exec_path.c_str()); if (!done) { // The next check is that we are in a reloctable directory tree const char *installed_suffix = "/bin/fish"; const char *just_a_fish = "/fish"; const char *suffix = NULL; if (has_suffix(exec_path, installed_suffix, false)) { suffix = installed_suffix; } else if (has_suffix(exec_path, just_a_fish, false)) { debug(2, L"'fish' not in a 'bin/', trying paths relative to source tree"); suffix = just_a_fish; } if (suffix) { bool seems_installed = (suffix == installed_suffix); wcstring base_path = str2wcstring(exec_path); base_path.resize(base_path.size() - strlen(suffix)); paths.data = base_path + (seems_installed ? L"/share/fish" : L"/share"); paths.sysconf = base_path + (seems_installed ? L"/etc/fish" : L"/etc"); paths.doc = base_path + (seems_installed ? L"/share/doc/fish" : L"/user_doc/html"); paths.bin = base_path + (seems_installed ? L"/bin" : L""); // Check only that the data and sysconf directories exist. Handle the doc // directories separately. struct stat buf; if (0 == wstat(paths.data, &buf) && 0 == wstat(paths.sysconf, &buf)) { // The docs dir may not exist; in that case fall back to the compiled in path. if (0 != wstat(paths.doc, &buf)) { paths.doc = L"" DOCDIR; } done = true; } } } } if (!done) { // Fall back to what got compiled in. debug(2, L"Using compiled in paths:"); paths.data = L"" DATADIR "/fish"; paths.sysconf = L"" SYSCONFDIR "/fish"; paths.doc = L"" DOCDIR; paths.bin = L"" BINDIR; } debug(2, L"determine_config_directory_paths() results:\npaths.data: %ls\npaths.sysconf: " L"%ls\npaths.doc: %ls\npaths.bin: %ls", paths.data.c_str(), paths.sysconf.c_str(), paths.doc.c_str(), paths.bin.c_str()); return paths; }
bool env_universal_t::open_temporary_file(const wcstring &directory, wcstring *out_path, int *out_fd) { /* Create and open a temporary file for writing within the given directory */ /* Try to create a temporary file, up to 10 times. We don't use mkstemps because we want to open it CLO_EXEC. This should almost always succeed on the first try. */ assert(! string_suffixes_string(L"/", directory)); bool success = false; const wcstring tmp_name_template = directory + L"/fishd.tmp.XXXXXX"; wcstring tmp_name; for (size_t attempt = 0; attempt < 10 && ! success; attempt++) { int result_fd = -1; char *narrow_str = wcs2str(tmp_name_template.c_str()); #if HAVE_MKOSTEMP result_fd = mkostemp(narrow_str, O_CLOEXEC); if (result_fd >= 0) { tmp_name = str2wcstring(narrow_str); } #else if (mktemp(narrow_str)) { /* It was successfully templated; try opening it atomically */ tmp_name = str2wcstring(narrow_str); result_fd = wopen_cloexec(tmp_name, O_WRONLY | O_CREAT | O_EXCL | O_TRUNC, 0644); } #endif if (result_fd >= 0) { /* Success */ *out_fd = result_fd; *out_path = str2wcstring(narrow_str); success = true; } free(narrow_str); } if (! success) { int err = errno; report_error(err, L"Unable to open file '%ls'", tmp_name.c_str()); } return success; }
wchar_t *wrealpath(const wcstring &pathname, wchar_t *resolved_path) { if (pathname.size() == 0) return NULL; cstring real_path(""); cstring narrow_path = wcs2string(pathname); // Strip trailing slashes. This is needed to be bug-for-bug compatible with GNU realpath which // treats "/a//" as equivalent to "/a" whether or not /a exists. while (narrow_path.size() > 1 && narrow_path.at(narrow_path.size() - 1) == '/') { narrow_path.erase(narrow_path.size() - 1, 1); } char *narrow_res = realpath(narrow_path.c_str(), NULL); if (narrow_res) { real_path.append(narrow_res); } else { size_t pathsep_idx = narrow_path.rfind('/'); if (pathsep_idx == 0) { // If the only pathsep is the first character then it's an absolute path with a // single path component and thus doesn't need conversion. real_path = narrow_path; } else { if (pathsep_idx == cstring::npos) { // No pathsep means a single path component relative to pwd. narrow_res = realpath(".", NULL); if (!narrow_res) DIE("unexpected realpath(\".\") failure"); pathsep_idx = 0; } else { // Only call realpath() on the portion up to the last component. narrow_res = realpath(narrow_path.substr(0, pathsep_idx).c_str(), NULL); if (!narrow_res) return NULL; pathsep_idx++; } real_path.append(narrow_res); // This test is to deal with pathological cases such as /../../x => //x. if (real_path.size() > 1) real_path.append("/"); real_path.append(narrow_path.substr(pathsep_idx, cstring::npos)); } } #if __APPLE__ && __DARWIN_C_LEVEL < 200809L // OS X Snow Leopard is broken with respect to the dynamically allocated buffer returned by // realpath(). It's not dynamically allocated so attempting to free that buffer triggers a // malloc/free error. Thus we don't attempt the free in this case. #else free(narrow_res); #endif wcstring wreal_path = str2wcstring(real_path); if (resolved_path) { wcslcpy(resolved_path, wreal_path.c_str(), PATH_MAX); return resolved_path; } return wcsdup(wreal_path.c_str()); }
int run_command_list(std::vector<std::string> *cmds, const io_chain_t &io) { int res = 1; parser_t &parser = parser_t::principal_parser(); for (size_t i = 0; i < cmds->size(); i++) { const wcstring cmd_wcs = str2wcstring(cmds->at(i)); res = parser.eval(cmd_wcs, io, TOP); } return res; }
static wcstring default_vars_path() { wcstring wdir = fishd_get_config(); const std::string dir = wcs2string(wdir); if (dir.empty()) return L""; const std::string machine_id = get_machine_identifier(); const std::string machine_id_path = get_variables_file_path(dir, machine_id); return str2wcstring(machine_id_path); }
/// This function works like wperror, but it prints its result into the streams.err string instead /// to stderr. Used by the builtin commands. void builtin_wperror(const wchar_t *s, io_streams_t &streams) { char *err = std::strerror(errno); if (s != NULL) { streams.err.append(s); streams.err.append(L": "); } if (err != NULL) { const wcstring werr = str2wcstring(err); streams.err.append(werr); streams.err.push_back(L'\n'); } }
bool wreaddir_resolving(DIR *dir, const std::wstring &dir_path, std::wstring &out_name, bool *out_is_dir) { struct dirent *d = readdir(dir); if (!d) return false; out_name = str2wcstring(d->d_name); if (out_is_dir) { /* The caller cares if this is a directory, so check */ bool is_dir = false; /* We may be able to skip stat, if the readdir can tell us the file type directly */ bool check_with_stat = true; #ifdef HAVE_STRUCT_DIRENT_D_TYPE if (d->d_type == DT_DIR) { /* Known directory */ is_dir = true; check_with_stat = false; } else if (d->d_type == DT_LNK || d->d_type == DT_UNKNOWN) { /* We want to treat symlinks to directories as directories. Use stat to resolve it. */ check_with_stat = true; } else { /* Regular file */ is_dir = false; check_with_stat = false; } #endif // HAVE_STRUCT_DIRENT_D_TYPE if (check_with_stat) { /* We couldn't determine the file type from the dirent; check by stat'ing it */ cstring fullpath = wcs2string(dir_path); fullpath.push_back('/'); fullpath.append(d->d_name); struct stat buf; if (stat(fullpath.c_str(), &buf) != 0) { is_dir = false; } else { is_dir = !!(S_ISDIR(buf.st_mode)); } } *out_is_dir = is_dir; } return true; }
const wcstring wgetcwd() { wcstring retval; char *res = getcwd(NULL, 0); if (res) { retval = str2wcstring(res); free(res); } else { debug(0, _(L"getcwd() failed with errno %d/%s"), errno, strerror(errno)); retval = wcstring(); } return retval; }
/** 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); } } }
wchar_t *wrealpath(const wcstring &pathname, wchar_t *resolved_path) { cstring tmp = wcs2string(pathname); char narrow_buff[PATH_MAX]; char *narrow_res = realpath(tmp.c_str(), narrow_buff); wchar_t *res; if (!narrow_res) return 0; const wcstring wide_res = str2wcstring(narrow_res); if (resolved_path) { wcslcpy(resolved_path, wide_res.c_str(), PATH_MAX); res = resolved_path; } else { res = wcsdup(wide_res.c_str()); } return res; }
/** Get environment variable value. The resulting string needs to be free'd. */ static wchar_t *fishd_env_get(const wchar_t *key) { char *nres, *nkey; wchar_t *res; nkey = wcs2str(key); nres = getenv(nkey); free(nkey); if (nres) { wcstring tmp = str2wcstring(nres); return wcsdup(tmp.c_str()); } else { res = env_universal_common_get(key); if (res) res = wcsdup(res); return res; } }
wcstring process_iterator_t::name_for_pid(pid_t pid) { wcstring result; int mib[4], maxarg = 0, numArgs = 0; size_t size = 0; char *args = NULL, *stringPtr = NULL; mib[0] = CTL_KERN; mib[1] = KERN_ARGMAX; size = sizeof(maxarg); if (sysctl(mib, 2, &maxarg, &size, NULL, 0) == -1) { return result; } args = (char *)malloc(maxarg); if (args == NULL) { return result; } mib[0] = CTL_KERN; mib[1] = KERN_PROCARGS2; mib[2] = pid; size = (size_t)maxarg; if (sysctl(mib, 3, args, &size, NULL, 0) == -1) { free(args); return result;; } memcpy(&numArgs, args, sizeof(numArgs)); stringPtr = args + sizeof(numArgs); result = str2wcstring(stringPtr); free(args); return result; }
bool wreaddir_resolving(DIR *dir, const std::wstring &dir_path, std::wstring &out_name, bool *out_is_dir) { struct dirent *d = readdir(dir); if (!d) return false; out_name = str2wcstring(d->d_name); if (out_is_dir) { /* The caller cares if this is a directory, so check */ bool is_dir; if (d->d_type == DT_DIR) { is_dir = true; } else if (d->d_type == DT_LNK || d->d_type == DT_UNKNOWN) { /* We want to treat symlinks to directories as directories. Use stat to resolve it. */ cstring fullpath = wcs2string(dir_path); fullpath.push_back('/'); fullpath.append(d->d_name); struct stat buf; if (stat(fullpath.c_str(), &buf) != 0) { is_dir = false; } else { is_dir = !!(S_ISDIR(buf.st_mode)); } } else { is_dir = false; } *out_is_dir = is_dir; } return true; }
/// Return the key name if the recent sequence of characters matches a known terminfo sequence. char *const key_name(unsigned char c) { static char recent_chars[8] = {0}; recent_chars[0] = recent_chars[1]; recent_chars[1] = recent_chars[2]; recent_chars[2] = recent_chars[3]; recent_chars[3] = recent_chars[4]; recent_chars[4] = recent_chars[5]; recent_chars[5] = recent_chars[6]; recent_chars[6] = recent_chars[7]; recent_chars[7] = c; for (int idx = 7; idx >= 0; idx--) { wcstring out_name; wcstring seq = str2wcstring(recent_chars + idx, 8 - idx); bool found = input_terminfo_get_name(seq, &out_name); if (found) { return strdup(wcs2string(out_name).c_str()); } } return NULL; }
bool env_universal_t::load() { scoped_lock locker(lock); callback_data_list_t callbacks; const wcstring vars_path = explicit_vars_path.empty() ? default_vars_path() : explicit_vars_path; bool success = load_from_path(vars_path, &callbacks); if (! success && ! tried_renaming && errno == ENOENT) { /* We failed to load, because the file was not found. Older fish used the hostname only. Try *moving* the filename based on the hostname into place; if that succeeds try again. Silently "upgraded." */ tried_renaming = true; std::string hostname_id; if (get_hostname_identifier(&hostname_id)) { const wcstring hostname_path = wdirname(vars_path) + L'/' + str2wcstring(hostname_id); if (0 == wrename(hostname_path, vars_path)) { /* We renamed - try again */ success = this->load(); } } } return success; }
/** Read lines of input from the specified file, unescape them and insert them into the specified list. */ static void read_array(FILE* file, wcstring_list_t &comp) { std::vector<char> buffer; int c; while (!feof(file)) { buffer.clear(); while (1) { c = getc(file); if (c == EOF) { break; } if (c == '\n') { break; } buffer.push_back(static_cast<char>(c)); } if (! buffer.empty()) { buffer.push_back(0); wcstring wcs = str2wcstring(&buffer.at(0)); if (unescape_string(wcs, false)) { comp.push_back(wcs); } } } }
wchar_t *wrealpath(const wcstring &pathname, wchar_t *resolved_path) { cstring narrow_path = wcs2string(pathname); char *narrow_res = realpath(narrow_path.c_str(), NULL); if (!narrow_res) return NULL; wchar_t *res; wcstring wide_res = str2wcstring(narrow_res); if (resolved_path) { wcslcpy(resolved_path, wide_res.c_str(), PATH_MAX); res = resolved_path; } else { res = wcsdup(wide_res.c_str()); } free(narrow_res); return res; }
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); }
int main( int argc, char **argv ) { struct stat tmp; int res=1; const char *cmd=0; int my_optind=0; set_main_thread(); setup_fork_guards(); wsetlocale( LC_ALL, L"" ); is_interactive_session=1; program_name=L"fish"; stat("----------FISH_HIT_MAIN----------", &tmp); my_optind = fish_parse_opt( argc, argv, &cmd ); /* No-exec is prohibited when in interactive mode */ if( is_interactive_session && no_exec) { debug( 1, _(L"Can not use the no-execute mode when running an interactive session") ); no_exec = 0; } const struct config_paths_t paths = determine_config_directory_paths(argv[0]); proc_init(); event_init(); wutil_init(); //parser_init(); builtin_init(); function_init(); env_init(&paths); reader_init(); history_init(); parser_t &parser = parser_t::principal_parser(); if (g_log_forks) printf("%d: g_fork_count: %d\n", __LINE__, g_fork_count); if( read_init(paths) ) { if( cmd != 0 ) { wchar_t *cmd_wcs = str2wcs( cmd ); res = parser.eval( cmd_wcs, 0, TOP ); free(cmd_wcs); reader_exit(0, 0); } else { if( my_optind == argc ) { res = reader_read( STDIN_FILENO, 0 ); } else { char **ptr; char *file = *(argv+(my_optind++)); int i; int fd; wchar_t *rel_filename, *abs_filename; if( ( fd = open(file, O_RDONLY) ) == -1 ) { wperror( L"open" ); return 1; } // OK to not do this atomically since we cannot have gone multithreaded yet set_cloexec(fd); if( *(argv+my_optind)) { wcstring sb; for( i=1,ptr = argv+my_optind; *ptr; i++, ptr++ ) { if( i != 1 ) sb.append( ARRAY_SEP_STR ); sb.append( str2wcstring( *ptr )); } env_set( L"argv", sb.c_str(), 0 ); } rel_filename = str2wcs( file ); abs_filename = wrealpath( rel_filename, 0 ); if( !abs_filename ) { abs_filename = wcsdup(rel_filename); } reader_push_current_filename( intern( abs_filename ) ); free( rel_filename ); free( abs_filename ); res = reader_read( fd, 0 ); if( res ) { debug( 1, _(L"Error while reading file %ls\n"), reader_current_filename()?reader_current_filename(): _(L"Standard input") ); } reader_pop_current_filename(); } } } proc_fire_event( L"PROCESS_EXIT", EVENT_EXIT, getpid(), res ); history_destroy(); proc_destroy(); builtin_destroy(); reader_destroy(); parser.destroy(); wutil_destroy(); event_destroy(); env_destroy(); if (g_log_forks) printf("%d: g_fork_count: %d\n", __LINE__, g_fork_count); return res?STATUS_UNKNOWN_COMMAND:proc_get_last_status(); }
static struct config_paths_t determine_config_directory_paths(const char *argv0) { struct config_paths_t paths; bool done = false; std::string exec_path = get_executable_path(argv0); if (get_realpath(exec_path)) { #if __APPLE__ /* On OS X, maybe we're an app bundle, and should use the bundle's files. Since we don't link CF, use this lame approach to test it: see if the resolved path ends with /Contents/MacOS/fish, case insensitive since HFS+ usually is. */ if (! done) { const char *suffix = "/Contents/MacOS/fish"; const size_t suffixlen = strlen(suffix); if (has_suffix(exec_path, suffix, true)) { /* Looks like we're a bundle. Cut the string at the / prefixing /Contents... and then the rest */ wcstring wide_resolved_path = str2wcstring(exec_path); wide_resolved_path.resize(exec_path.size() - suffixlen); wide_resolved_path.append(L"/Contents/Resources/"); /* Append share, etc, doc */ paths.data = wide_resolved_path + L"share/fish"; paths.sysconf = wide_resolved_path + L"etc/fish"; paths.doc = wide_resolved_path + L"doc/fish"; /* But the bin_dir is the resolved_path, minus fish (aka the MacOS directory) */ paths.bin = str2wcstring(exec_path); paths.bin.resize(paths.bin.size() - strlen("/fish")); done = true; } } #endif if (! done) { /* The next check is that we are in a reloctable directory tree like this: bin/fish etc/fish share/fish Check it! */ const char *suffix = "/bin/fish"; if (has_suffix(exec_path, suffix, false)) { wcstring base_path = str2wcstring(exec_path); base_path.resize(base_path.size() - strlen(suffix)); paths.data = base_path + L"/share/fish"; paths.sysconf = base_path + L"/etc/fish"; paths.doc = base_path + L"/share/doc/fish"; paths.bin = base_path + L"/bin"; struct stat buf; if (0 == wstat(paths.data, &buf) && 0 == wstat(paths.sysconf, &buf)) { done = true; } } } } if (! done) { /* Fall back to what got compiled in. */ paths.data = L"" DATADIR "/fish"; paths.sysconf = L"" SYSCONFDIR "/fish"; paths.doc = L"" DATADIR "/doc/fish"; paths.bin = L"" PREFIX "/bin"; done = true; } return paths; }
/** This internal helper function does all the real work. By using two functions, the internal function can return on various places in the code, and the caller can take care of various cleanup work. cmd: the command name ('grep') really_load: whether to actually parse it as a function, or just check it it exists reload: whether to reload it if it's already loaded path_list: the set of paths to check Result: if really_load is true, returns whether the function was loaded. Otherwise returns whether the function existed. */ bool autoload_t::locate_file_and_maybe_load_it(const wcstring &cmd, bool really_load, bool reload, const wcstring_list_t &path_list) { /* Note that we are NOT locked in this function! */ bool reloaded = 0; /* Try using a cached function. If we really want the function to be loaded, require that it be really loaded. If we're not reloading, allow stale functions. */ { bool allow_stale_functions = ! reload; /* Take a lock */ scoped_lock locker(lock); /* Get the function */ autoload_function_t * func = this->get_node(cmd); /* Determine if we can use this cached function */ bool use_cached; if (! func) { /* Can't use a function that doesn't exist */ use_cached = false; } else if (really_load && ! func->is_placeholder && ! func->is_loaded) { /* Can't use an unloaded function */ use_cached = false; } else if (! allow_stale_functions && is_stale(func)) { /* Can't use a stale function */ use_cached = false; } else { /* I guess we can use it */ use_cached = true; } /* If we can use this function, return whether we were able to access it */ if (use_cached) { return func->is_internalized || func->access.accessible; } } /* The source of the script will end up here */ wcstring script_source; bool has_script_source = false; /* Whether we found an accessible file */ bool found_file = false; /* Look for built-in scripts via a binary search */ const builtin_script_t *matching_builtin_script = NULL; if (builtin_script_count > 0) { const builtin_script_t test_script = {cmd.c_str(), NULL}; const builtin_script_t *array_end = builtin_scripts + builtin_script_count; const builtin_script_t *found = std::lower_bound(builtin_scripts, array_end, test_script, script_name_precedes_script_name); if (found != array_end && ! wcscmp(found->name, test_script.name)) { /* We found it */ matching_builtin_script = found; } } if (matching_builtin_script) { has_script_source = true; script_source = str2wcstring(matching_builtin_script->def); /* Make a node representing this function */ scoped_lock locker(lock); autoload_function_t *func = this->get_autoloaded_function_with_creation(cmd, really_load); /* This function is internalized */ func->is_internalized = true; /* It's a fiction to say the script is loaded at this point, but we're definitely going to load it down below. */ if (really_load) func->is_loaded = true; } if (! has_script_source) { /* Iterate over path searching for suitable completion files */ for (size_t i=0; i<path_list.size(); i++) { wcstring next = path_list.at(i); wcstring path = next + L"/" + cmd + L".fish"; const file_access_attempt_t access = access_file(path, R_OK); if (access.accessible) { /* Found it! */ found_file = true; /* Now we're actually going to take the lock. */ scoped_lock locker(lock); autoload_function_t *func = this->get_node(cmd); /* Generate the source if we need to load it */ bool need_to_load_function = really_load && (func == NULL || func->access.mod_time != access.mod_time || ! func->is_loaded); if (need_to_load_function) { /* Generate the script source */ wcstring esc = escape_string(path, 1); script_source = L". " + esc; has_script_source = true; /* Remove any loaded command because we are going to reload it. Note that this will deadlock if command_removed calls back into us. */ if (func && func->is_loaded) { command_removed(cmd); func->is_placeholder = false; } /* Mark that we're reloading it */ reloaded = true; } /* Create the function if we haven't yet. This does not load it. Do not trigger eviction unless we are actually loading, because we don't want to evict off of the main thread. */ if (! func) { func = get_autoloaded_function_with_creation(cmd, really_load); } /* It's a fiction to say the script is loaded at this point, but we're definitely going to load it down below. */ if (need_to_load_function) func->is_loaded = true; /* Unconditionally record our access time */ func->access = access; break; } } /* If no file or builtin script was found we insert a placeholder function. Later we only research if the current time is at least five seconds later. This way, the files won't be searched over and over again. */ if (! found_file && ! has_script_source) { scoped_lock locker(lock); /* Generate a placeholder */ autoload_function_t *func = this->get_node(cmd); if (! func) { func = new autoload_function_t(cmd); func->is_placeholder = true; if (really_load) { this->add_node(func); } else { this->add_node_without_eviction(func); } } func->access.last_checked = time(NULL); } } /* If we have a script, either built-in or a file source, then run it */ if (really_load && has_script_source) { if (exec_subshell(script_source, false /* do not apply exit status */) == -1) { /* Do nothing on failure */ } } if (really_load) { return reloaded; } else { return found_file || has_script_source; } }