Example #1
0
static wcstring path_create_data() {
    bool done = false;
    wcstring res;

    const env_var_t xdg_dir = env_get_string(L"XDG_DATA_HOME");
    if (!xdg_dir.missing()) {
        res = xdg_dir + L"/fish";
        if (!create_directory(res)) {
            done = true;
        }
    } else {
        const env_var_t home = env_get_string(L"HOME");
        if (!home.missing()) {
            res = home + L"/.local/share/fish";
            if (!create_directory(res)) {
                done = true;
            }
        }
    }

    if (!done) {
        res.clear();

        debug(0, _(L"Unable to create a data directory for fish. Your history will not be saved. "
                   L"Please set the $XDG_DATA_HOME variable to a directory where the current user "
                   L"has write access."));
    }
    return res;
}
Example #2
0
/// Make sure the PATH variable contains something.
static void setup_path() {
    const env_var_t path = env_get_string(L"PATH");
    if (path.missing_or_empty()) {
        const wchar_t *value = L"/usr/bin" ARRAY_SEP_STR L"/bin";
        env_set(L"PATH", value, ENV_GLOBAL | ENV_EXPORT);
    }
}
Example #3
0
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;
    }
}
Example #4
0
static wcstring path_create_config() {
    bool done = false;
    wcstring res;

    const env_var_t xdg_dir = env_get_string(L"XDG_CONFIG_HOME");
    if (!xdg_dir.missing()) {
        res = xdg_dir + L"/fish";
        if (!create_directory(res)) {
            done = true;
        }
    } else {
        const env_var_t home = env_get_string(L"HOME");
        if (!home.missing()) {
            res = home + L"/.config/fish";
            if (!create_directory(res)) {
                done = true;
            }
        }
    }

    if (!done) {
        res.clear();

        debug(0, _(L"Unable to create a configuration directory for fish. Your personal settings "
                   L"will not be saved. Please set the $XDG_CONFIG_HOME variable to a directory "
                   L"where the current user has write access."));
    }
    return res;
}
Example #5
0
/// Properly sets all locale information.
static void handle_locale(const wchar_t *env_var_name) {
    debug(2, L"handle_locale() called in response to '%ls' changing", env_var_name);
    const char *old_msg_locale = setlocale(LC_MESSAGES, NULL);
    const env_var_t val = env_get_string(env_var_name, ENV_EXPORT);
    const std::string &value = wcs2string(val);
    const std::string &name = wcs2string(env_var_name);
    debug(2, L"locale var %s='%s'", name.c_str(), value.c_str());
    if (val.empty()) {
        unsetenv(name.c_str());
    } else {
        setenv(name.c_str(), value.c_str(), 1);
    }

    char *locale = setlocale(LC_ALL, "");
    fish_setlocale();
    debug(2, L"handle_locale() setlocale(): '%s'", locale);

    const char *new_msg_locale = setlocale(LC_MESSAGES, NULL);
    debug(3, L"old LC_MESSAGES locale: '%s'", old_msg_locale);
    debug(3, L"new LC_MESSAGES locale: '%s'", new_msg_locale);
#ifdef HAVE__NL_MSG_CAT_CNTR
    if (strcmp(old_msg_locale, new_msg_locale)) {
        // Make change known to GNU gettext.
        extern int _nl_msg_cat_cntr;
        _nl_msg_cat_cntr++;
    }
#endif
}
Example #6
0
/**
   When fishd isn't started, this function is provided to
   env_universal as a callback, it tries to start up fishd. It's
   implementation is a bit of a hack, since it evaluates a bit of
   shellscript, and it might be used at times when that might not be
   the best idea.
*/
static void start_fishd()
{
    struct passwd *pw = getpwuid(getuid());

    debug(3, L"Spawning new copy of fishd");

    if (!pw)
    {
        debug(0, _(L"Could not get user information"));
        return;
    }

    wcstring cmd = format_string(FISHD_CMD, pw->pw_name);

    /* Prefer the fishd in __fish_bin_dir, if exists */
    const env_var_t bin_dir = env_get_string(L"__fish_bin_dir");
    if (! bin_dir.missing_or_empty())
    {
        wcstring path = bin_dir + L"/fishd";
        if (waccess(path, X_OK) == 0)
        {
            /* The path command just looks like 'fishd', so insert the bin path to make it absolute */
            cmd.insert(0, bin_dir + L"/");
        }
    }
    parser_t &parser = parser_t::principal_parser();
    parser.eval(cmd, io_chain_t(), TOP);
}
Example #7
0
/**
   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());
		wchar_t *unam = str2wcs( pw->pw_name );
		env_set( L"USER", unam, ENV_GLOBAL );
		free( unam );
	}

	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 );
		wchar_t *dir = str2wcs( pw->pw_dir );
		env_set( L"HOME", dir, ENV_GLOBAL );
		free( dir );		
		free( unam_narrow );
	}	

	env_set_pwd();
	
}
Example #8
0
/**
   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();

}
Example #9
0
/// Insert a list of all dynamically loaded functions into the specified list.
static void autoload_names(std::set<wcstring> &names, int get_hidden) {
    size_t i;

    const env_var_t path_var_wstr = env_get_string(L"fish_function_path");
    if (path_var_wstr.missing()) return;
    const wchar_t *path_var = path_var_wstr.c_str();

    wcstring_list_t path_list;

    tokenize_variable_array(path_var, path_list);
    for (i = 0; i < path_list.size(); i++) {
        const wcstring &ndir_str = path_list.at(i);
        const wchar_t *ndir = (wchar_t *)ndir_str.c_str();
        DIR *dir = wopendir(ndir);
        if (!dir) continue;

        wcstring name;
        while (wreaddir(dir, name)) {
            const wchar_t *fn = name.c_str();
            const wchar_t *suffix;
            if (!get_hidden && fn[0] == L'_') continue;

            suffix = wcsrchr(fn, L'.');
            if (suffix && (wcscmp(suffix, L".fish") == 0)) {
                wcstring name(fn, suffix - fn);
                names.insert(name);
            }
        }
        closedir(dir);
    }
}
Example #10
0
bool autoload_t::can_load(const wcstring &cmd, const env_vars_snapshot_t &vars) {
    const env_var_t path_var = vars.get(env_var_name);
    if (path_var.missing_or_empty()) return false;

    std::vector<wcstring> path_list;
    tokenize_variable_array(path_var, path_list);
    return this->locate_file_and_maybe_load_it(cmd, false, false, path_list);
}
Example #11
0
void kill_add(const wcstring &str)
{
    ASSERT_IS_MAIN_THREAD();
    if (str.empty())
        return;

    wcstring cmd;
    wcstring escaped_str;
    kill_list.push_front(str);

    /*
       Check to see if user has set the FISH_CLIPBOARD_CMD variable,
       and, if so, use it instead of checking the display, etc.

       I couldn't think of a safe way to allow overide of the echo
       command too, so, the command used must accept the input via stdin.
    */

    const env_var_t clipboard_wstr = env_get_string(L"FISH_CLIPBOARD_CMD");
    if (!clipboard_wstr.missing())
    {
        escaped_str = escape(str.c_str(), ESCAPE_ALL);
        cmd.assign(L"echo -n ");
        cmd.append(escaped_str);
        cmd.append(clipboard_wstr);
    }
    else
    {
        /* This is for sending the kill to the X copy-and-paste buffer */
        if (!has_xsel())
        {
            return;
        }

        const env_var_t disp_wstr = env_get_string(L"DISPLAY");
        if (!disp_wstr.missing())
        {
            escaped_str = escape(str.c_str(), ESCAPE_ALL);
            cmd.assign(L"echo -n ");
            cmd.append(escaped_str);
            cmd.append(L" | xsel -i -b");
        }
    }

    if (! cmd.empty())
    {
        if (exec_subshell(cmd, false /* do not apply exit status */) == -1)
        {
            /*
               Do nothing on failiure
            */
        }

        cut_buffer = escaped_str;
    }
}
Example #12
0
env_vars_snapshot_t::env_vars_snapshot_t(const wchar_t *const *keys) {
    ASSERT_IS_MAIN_THREAD();
    wcstring key;
    for (size_t i = 0; keys[i]; i++) {
        key.assign(keys[i]);
        const env_var_t val = env_get_string(key);
        if (!val.missing()) {
            vars[key] = val;
        }
    }
}
Example #13
0
/// Properly sets all timezone information.
static void handle_timezone(const wchar_t *env_var_name) {
    debug(2, L"handle_timezone() called in response to '%ls' changing", env_var_name);
    const env_var_t val = env_get_string(env_var_name, ENV_EXPORT);
    const std::string &value = wcs2string(val);
    const std::string &name = wcs2string(env_var_name);
    debug(2, L"timezone var %s='%s'", name.c_str(), value.c_str());
    if (val.empty()) {
        unsetenv(name.c_str());
    } else {
        setenv(name.c_str(), value.c_str(), 1);
    }
    tzset();
}
Example #14
0
/// Push all curses/terminfo env vars into the global environment where they can be found by those
/// libraries.
static void handle_curses(const wchar_t *env_var_name) {
    debug(2, L"handle_curses() called in response to '%ls' changing", env_var_name);
    const env_var_t val = env_get_string(env_var_name, ENV_EXPORT);
    const std::string &name = wcs2string(env_var_name);
    const std::string &value = wcs2string(val);
    debug(2, L"curses var %s='%s'", name.c_str(), value.c_str());
    if (val.empty()) {
        unsetenv(name.c_str());
    } else {
        setenv(name.c_str(), value.c_str(), 1);
    }
    // TODO: Modify input_init() to allow calling it when the terminfo env vars are dynamically
    // changed. At the present time it can be called just once. Also, we should really only do this
    // if the TERM var is set.
    // input_init();
}
Example #15
0
wcstring expand_escape_variable(const env_var_t &var) {
    wcstring buff;
    wcstring_list_t lst;

    var.to_list(lst);
    if (lst.size() == 0) {
        ;  // empty list expands to nothing
    } else if (lst.size() == 1) {
        const wcstring &el = lst.at(0);

        if (el.find(L' ') != wcstring::npos && is_quotable(el)) {
            buff.append(L"'");
            buff.append(el);
            buff.append(L"'");
        } else {
            buff.append(escape_string(el, 1));
        }
    } else {
        for (size_t j = 0; j < lst.size(); j++) {
            const wcstring &el = lst.at(j);
            if (j) buff.append(L"  ");

            if (is_quotable(el)) {
                buff.append(L"'");
                buff.append(el);
                buff.append(L"'");
            } else {
                buff.append(escape_string(el, 1));
            }
        }
    }

    return buff;
}
Example #16
0
/// Does this look like the escape sequence for setting a screen name.
static bool is_screen_name_escape_seq(const wchar_t *code, size_t *resulting_length) {
    bool found = false;
    if (code[1] == L'k') {
        const env_var_t term_name = env_get_string(L"TERM");
        if (!term_name.missing() && string_prefixes_string(L"screen", term_name)) {
            const wchar_t *const screen_name_end_sentinel = L"\x1b\\";
            const wchar_t *screen_name_end = wcsstr(&code[2], screen_name_end_sentinel);
            if (screen_name_end == NULL) {
                // Consider just <esc>k to be the code.
                *resulting_length = 2;
            } else {
                const wchar_t *escape_sequence_end =
                    screen_name_end + wcslen(screen_name_end_sentinel);
                *resulting_length = escape_sequence_end - code;
            }
            found = true;
        }
    }
    return found;
}
Example #17
0
/**
   Check the X clipboard. If it has been changed, add the new
   clipboard contents to the fish killring.
*/
static void kill_check_x_buffer()
{
    if (!has_xsel())
        return;

    const env_var_t disp = env_get_string(L"DISPLAY");
    if (! disp.missing())
    {
        size_t i;
        wcstring cmd = L"xsel -t 500 -b";
        wcstring new_cut_buffer=L"";
        wcstring_list_t list;
        if (exec_subshell(cmd, list, false /* do not apply exit status */) != -1)
        {

            for (i=0; i<list.size(); i++)
            {
                wcstring next_line = escape_string(list.at(i), 0);
                if (i > 0) new_cut_buffer += L"\\n";
                new_cut_buffer += next_line;
            }

            if (new_cut_buffer.size() > 0)
            {
                /*
                  The buffer is inserted with backslash escapes,
                  since we don't really like tabs, newlines,
                  etc. anyway.
                */

                if (cut_buffer != new_cut_buffer)
                {
                    cut_buffer = new_cut_buffer;
                    kill_list.push_front(new_cut_buffer);
                }
            }
        }
    }
}
Example #18
0
static void path_create(wcstring &path, const wcstring &xdg_var, const wcstring &which_dir,
                        const wcstring &custom_error_msg) {
    bool path_done = false;
    bool using_xdg = false;
    int saved_errno = 0;

    // The vars we fetch must be exported. Allowing them to be universal doesn't make sense and
    // allowing that creates a lock inversion that deadlocks the shell since we're called before
    // uvars are available.
    const env_var_t xdg_dir = env_get_string(xdg_var, ENV_GLOBAL | ENV_EXPORT);
    if (!xdg_dir.missing_or_empty()) {
        using_xdg = true;
        path = xdg_dir + L"/fish";
        if (create_directory(path) != -1) {
            path_done = true;
        } else {
            saved_errno = errno;
        }
    } else {
        const env_var_t home = env_get_string(L"HOME", ENV_GLOBAL | ENV_EXPORT);
        if (!home.missing_or_empty()) {
            path = home + (which_dir == L"config" ? L"/.config/fish" : L"/.local/share/fish");
            if (create_directory(path) != -1) {
                path_done = true;
            } else {
                saved_errno = errno;
            }
        }
    }

    if (!path_done) {
        maybe_issue_path_warning(which_dir, custom_error_msg, using_xdg, xdg_var, path,
                                 saved_errno);
        path.clear();
    }

    return;
}
Example #19
0
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);
}
Example #20
0
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));
}
Example #21
0
/**
  Properly sets all locale information
*/
static void handle_locale()
{
    const env_var_t lc_all = env_get_string(L"LC_ALL");
    const wcstring old_locale = wsetlocale(LC_MESSAGES, NULL);

    /*
      Array of locale constants corresponding to the local variable names defined in locale_variable
    */
    static const int cat[] =
    {
        0,
        LC_ALL,
        LC_COLLATE,
        LC_CTYPE,
        LC_MESSAGES,
        LC_MONETARY,
        LC_NUMERIC,
        LC_TIME
    }
    ;

    if (!lc_all.missing())
    {
        wsetlocale(LC_ALL, lc_all.c_str());
    }
    else
    {
        const env_var_t lang = env_get_string(L"LANG");
        if (!lang.missing())
        {
            wsetlocale(LC_ALL, lang.c_str());
        }

        for (int i=2; locale_variable[i]; i++)
        {
            const env_var_t val = env_get_string(locale_variable[i]);

            if (!val.missing())
            {
                wsetlocale(cat[i], val.c_str());
            }
        }
    }

    const wcstring new_locale = wsetlocale(LC_MESSAGES, NULL);
    if (old_locale != new_locale)
    {

        /*
           Try to make change known to gettext. Both changing
           _nl_msg_cat_cntr and calling dcgettext might potentially
           tell some gettext implementation that the translation
           strings should be reloaded. We do both and hope for the
           best.
        */

        extern int _nl_msg_cat_cntr;
        _nl_msg_cat_cntr++;

        fish_dcgettext("fish", "Changing language to English", LC_MESSAGES);

        if (get_is_interactive())
        {
            debug(2, _(L"Changing language to English"));
        }
    }
}
Example #22
0
/**
   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, io_streams_t &streams)
{
    size_t i;
    int retcode = 0;
    const wchar_t *val_str=NULL;

    if (is_path_variable(key))
    {
        /* Fix for https://github.com/fish-shell/fish-shell/issues/199 . Return success if any path setting succeeds. */
        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 env_var_t existing_variable = env_get_string(key, ENV_DEFAULT);
        if (! existing_variable.missing_or_empty())
            tokenize_variable_array(existing_variable, existing_values);

        for (i=0; i< val.size() ; i++)
        {
            const wcstring &dir = val.at(i);
            if (!string_prefixes_string(L"/", dir) || list_contains_string(existing_values, dir))
            {
                any_success = true;
                continue;
            }

            bool show_perror = false;
            int show_hint = 0;
            bool error = false;

            struct stat buff;
            if (wstat(dir, &buff))
            {
                error = true;
                show_perror = true;
            }

            if (!(S_ISDIR(buff.st_mode)))
            {
                error = true;
            }

            if (!error)
            {
                any_success = true;
            }
            else
            {
                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))
                {
                    show_hint = 1;
                }

            }

            if (show_perror)
            {
                builtin_wperror(L"set", streams);
            }

            if (show_hint)
            {
                streams.err.append_format(_(BUILTIN_SET_PATH_HINT), L"set", key, key, wcschr(dir.c_str(), L':')+1);
            }

        }

        /* Fail at setting the path if we tried to set it to something non-empty, but it wound up empty. */
        if (! val.empty() && ! any_success)
        {
            return 1;
        }

    }

    wcstring sb;
    if (! val.empty())
    {
        for (i=0; i< val.size() ; i++)
        {
            sb.append(val[i]);
            if (i<val.size() - 1)
            {
                sb.append(ARRAY_SEP_STR);
            }
        }
        val_str = sb.c_str();
    }

    switch (env_set(key, val_str, scope | ENV_USER))
    {
        case ENV_PERM:
        {
            streams.err.append_format(_(L"%ls: Tried to change the read-only variable '%ls'\n"), L"set", key);
            retcode=1;
            break;
        }

        case ENV_SCOPE:
        {
            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:
        {
            streams.err.append_format(_(L"%ls: Tried to set the special variable '%ls' to an invalid value\n"), L"set", key);
            retcode=1;
            break;
        }
    }

    return retcode;
}
Example #23
0
/// The set builtin creates, updates, and erases (removes, deletes) variables.
int builtin_set(parser_t &parser, io_streams_t &streams, wchar_t **argv) {
    wchar_t *cmd = argv[0];
    int argc = builtin_count_args(argv);

    // Flags to set the work mode.
    int local = 0, global = 0, exportv = 0;
    int erase = 0, list = 0, unexport = 0;
    int universal = 0, query = 0;
    bool shorten_ok = true;
    bool preserve_failure_exit_status = true;
    const int incoming_exit_status = proc_get_last_status();

    // Variables used for performing the actual work.
    wchar_t *dest = NULL;
    int retcode = STATUS_CMD_OK;
    int scope;
    int slice = 0;

    // Variables used for parsing the argument list. This command is atypical in using the "+"
    // (REQUIRE_ORDER) option for flag parsing. This is not typical of most fish commands. It means
    // we stop scanning for flags when the first non-flag argument is seen.
    static const wchar_t *short_options = L"+LUeghlnqux";
    static const struct woption long_options[] = {{L"export", no_argument, NULL, 'x'},
                                                  {L"global", no_argument, NULL, 'g'},
                                                  {L"local", no_argument, NULL, 'l'},
                                                  {L"erase", no_argument, NULL, 'e'},
                                                  {L"names", no_argument, NULL, 'n'},
                                                  {L"unexport", no_argument, NULL, 'u'},
                                                  {L"universal", no_argument, NULL, 'U'},
                                                  {L"long", no_argument, NULL, 'L'},
                                                  {L"query", no_argument, NULL, 'q'},
                                                  {L"help", no_argument, NULL, 'h'},
                                                  {NULL, 0, NULL, 0}};

    // Parse options to obtain the requested operation and the modifiers.
    int opt;
    wgetopter_t w;
    while ((opt = w.wgetopt_long(argc, argv, short_options, long_options, NULL)) != -1) {
        switch (opt) {
            case 'e': {
                erase = 1;
                preserve_failure_exit_status = false;
                break;
            }
            case 'n': {
                list = 1;
                preserve_failure_exit_status = false;
                break;
            }
            case 'x': {
                exportv = 1;
                break;
            }
            case 'l': {
                local = 1;
                break;
            }
            case 'g': {
                global = 1;
                break;
            }
            case 'u': {
                unexport = 1;
                break;
            }
            case 'U': {
                universal = 1;
                break;
            }
            case 'L': {
                shorten_ok = false;
                break;
            }
            case 'q': {
                query = 1;
                preserve_failure_exit_status = false;
                break;
            }
            case 'h': {
                builtin_print_help(parser, streams, cmd, streams.out);
                return STATUS_CMD_OK;
            }
            case '?': {
                builtin_unknown_option(parser, streams, cmd, argv[w.woptind - 1]);
                return STATUS_INVALID_ARGS;
            }
            default: {
                DIE("unexpected retval from wgetopt_long");
                break;
            }
        }
    }

    // Ok, all arguments have been parsed, let's validate them. If we are checking the existance of
    // a variable (-q) we can not also specify scope.
    if (query && (erase || list)) {
        streams.err.append_format(BUILTIN_ERR_COMBO, cmd);
        builtin_print_help(parser, streams, cmd, streams.err);
        return STATUS_INVALID_ARGS;
    }

    // We can't both list and erase variables.
    if (erase && list) {
        streams.err.append_format(BUILTIN_ERR_COMBO, cmd);
        builtin_print_help(parser, streams, cmd, streams.err);
        return STATUS_INVALID_ARGS;
    }

    // Variables can only have one scope.
    if (local + global + universal > 1) {
        streams.err.append_format(BUILTIN_ERR_GLOCAL, cmd);
        builtin_print_help(parser, streams, cmd, streams.err);
        return STATUS_INVALID_ARGS;
    }

    // Variables can only have one export status.
    if (exportv && unexport) {
        streams.err.append_format(BUILTIN_ERR_EXPUNEXP, argv[0]);
        builtin_print_help(parser, streams, cmd, streams.err);
        return STATUS_INVALID_ARGS;
    }

    // Calculate the scope value for variable assignement.
    scope = (local ? ENV_LOCAL : 0) | (global ? ENV_GLOBAL : 0) | (exportv ? ENV_EXPORT : 0) |
            (unexport ? ENV_UNEXPORT : 0) | (universal ? ENV_UNIVERSAL : 0) | ENV_USER;

    if (query) {
        // Query mode. Return the number of variables that do not exist out of the specified
        // variables.
        int i;
        for (i = w.woptind; i < argc; i++) {
            wchar_t *arg = argv[i];
            int slice = 0;

            dest = wcsdup(arg);
            assert(dest);

            if (wcschr(dest, L'[')) {
                slice = 1;
                *wcschr(dest, L'[') = 0;
            }

            if (slice) {
                std::vector<long> indexes;
                wcstring_list_t result;
                size_t j;

                env_var_t dest_str = env_get_string(dest, scope);
                if (!dest_str.missing()) tokenize_variable_array(dest_str, result);

                if (!parse_index(indexes, arg, dest, result.size(), streams)) {
                    builtin_print_help(parser, streams, cmd, streams.err);
                    retcode = 1;
                    break;
                }
                for (j = 0; j < indexes.size(); j++) {
                    long idx = indexes[j];
                    if (idx < 1 || (size_t)idx > result.size()) {
                        retcode++;
                    }
                }
            } else {
                if (!env_exist(arg, scope)) {
                    retcode++;
                }
            }

            free(dest);
        }
        return retcode;
    }

    if (list) {
        // Maybe we should issue an error if there are any other arguments?
        print_variables(0, 0, shorten_ok, scope, streams);
        return STATUS_CMD_OK;
    }

    if (w.woptind == argc) {
        // Print values of variables.
        if (erase) {
            streams.err.append_format(_(L"%ls: Erase needs a variable name\n"), cmd);
            builtin_print_help(parser, streams, cmd, streams.err);
            retcode = STATUS_INVALID_ARGS;
        } else {
            print_variables(1, 1, shorten_ok, scope, streams);
        }

        return retcode;
    }

    dest = wcsdup(argv[w.woptind]);
    assert(dest);

    if (wcschr(dest, L'[')) {
        slice = 1;
        *wcschr(dest, L'[') = 0;
    }

    if (!valid_var_name(dest)) {
        streams.err.append_format(BUILTIN_ERR_VARNAME, cmd, dest);
        builtin_print_help(parser, streams, argv[0], streams.err);
        return STATUS_INVALID_ARGS;
    }

    // Set assignment can work in two modes, either using slices or using the whole array. We detect
    // which mode is used here.
    if (slice) {
        // Slice mode.
        std::vector<long> indexes;
        wcstring_list_t result;

        const env_var_t dest_str = env_get_string(dest, scope);
        if (!dest_str.missing()) {
            tokenize_variable_array(dest_str, result);
        } else if (erase) {
            retcode = 1;
        }

        if (!retcode) {
            for (; w.woptind < argc; w.woptind++) {
                if (!parse_index(indexes, argv[w.woptind], dest, result.size(), streams)) {
                    builtin_print_help(parser, streams, argv[0], streams.err);
                    retcode = 1;
                    break;
                }

                size_t idx_count = indexes.size();
                size_t val_count = argc - w.woptind - 1;

                if (!erase) {
                    if (val_count < idx_count) {
                        streams.err.append_format(_(BUILTIN_SET_ARG_COUNT), argv[0]);
                        builtin_print_help(parser, streams, argv[0], streams.err);
                        retcode = 1;
                        break;
                    }
                    if (val_count == idx_count) {
                        w.woptind++;
                        break;
                    }
                }
            }
        }

        if (!retcode) {
            // Slice indexes have been calculated, do the actual work.
            if (erase) {
                erase_values(result, indexes);
                my_env_set(dest, result, scope, streams);
            } else {
                wcstring_list_t value;

                while (w.woptind < argc) {
                    value.push_back(argv[w.woptind++]);
                }

                if (update_values(result, indexes, value)) {
                    streams.err.append_format(L"%ls: ", argv[0]);
                    streams.err.append_format(ARRAY_BOUNDS_ERR);
                    streams.err.push_back(L'\n');
                }

                my_env_set(dest, result, scope, streams);
            }
        }
    } else {
        w.woptind++;
        // No slicing.
        if (erase) {
            if (w.woptind != argc) {
                streams.err.append_format(_(L"%ls: Values cannot be specfied with erase\n"),
                                          argv[0]);
                builtin_print_help(parser, streams, argv[0], streams.err);
                retcode = 1;
            } else {
                retcode = env_remove(dest, scope);
            }
        } else {
            wcstring_list_t val;
            for (int i = w.woptind; i < argc; i++) val.push_back(argv[i]);
            retcode = my_env_set(dest, val, scope, streams);
        }
    }

    // Check if we are setting variables above the effective scope. See
    // https://github.com/fish-shell/fish-shell/issues/806
    env_var_t global_dest = env_get_string(dest, ENV_GLOBAL);
    if (universal && !global_dest.missing()) {
        streams.err.append_format(
            _(L"%ls: Warning: universal scope selected, but a global variable '%ls' exists.\n"),
            L"set", dest);
    }

    free(dest);

    if (retcode == STATUS_CMD_OK && preserve_failure_exit_status) retcode = incoming_exit_status;
    return retcode;
}
Example #24
0
/* Returns the number of characters in the escape code starting at 'code' (which should initially contain \x1b) */
size_t escape_code_length(const wchar_t *code)
{
    assert(code != NULL);

    /* The only escape codes we recognize start with \x1b */
    if (code[0] != L'\x1b')
        return 0;

    size_t resulting_length = 0;
    bool found = false;

    if (cur_term != NULL)
    {
        /*
         Detect these terminfo color escapes with parameter
         value 0..7, all of which don't move the cursor
         */
        char * const esc[] =
        {
            set_a_foreground,
            set_a_background,
            set_foreground,
            set_background,
        };

        for (size_t p=0; p < sizeof esc / sizeof *esc && !found; p++)
        {
            if (!esc[p])
                continue;

            for (size_t k=0; k<8; k++)
            {
                size_t len = try_sequence(tparm(esc[p],k), code);
                if (len)
                {
                    resulting_length = len;
                    found = true;
                    break;
                }
            }
        }
    }

    if (cur_term != NULL)
    {
        /*
         Detect these semi-common terminfo escapes without any
         parameter values, all of which don't move the cursor
         */
        char * const esc2[] =
        {
            enter_bold_mode,
            exit_attribute_mode,
            enter_underline_mode,
            exit_underline_mode,
            enter_standout_mode,
            exit_standout_mode,
            flash_screen,
            enter_subscript_mode,
            exit_subscript_mode,
            enter_superscript_mode,
            exit_superscript_mode,
            enter_blink_mode,
            enter_italics_mode,
            exit_italics_mode,
            enter_reverse_mode,
            enter_shadow_mode,
            exit_shadow_mode,
            enter_standout_mode,
            exit_standout_mode,
            enter_secure_mode
        };



        for (size_t p=0; p < sizeof esc2 / sizeof *esc2 && !found; p++)
        {
            if (!esc2[p])
                continue;
            /*
             Test both padded and unpadded version, just to
             be safe. Most versions of tparm don't actually
             seem to do anything these days.
             */
            size_t len = maxi(try_sequence(tparm(esc2[p]), code), try_sequence(esc2[p], code));
            if (len)
            {
                resulting_length = len;
                found = true;
            }
        }
    }

    if (!found)
    {
        if (code[1] == L'k')
        {
            /* This looks like the escape sequence for setting a screen name */
            const env_var_t term_name = env_get_string(L"TERM");
            if (!term_name.missing() && string_prefixes_string(L"screen", term_name))
            {
                const wchar_t * const screen_name_end_sentinel = L"\x1b\\";
                const wchar_t *screen_name_end = wcsstr(&code[2], screen_name_end_sentinel);
                if (screen_name_end != NULL)
                {
                    const wchar_t *escape_sequence_end = screen_name_end + wcslen(screen_name_end_sentinel);
                    resulting_length = escape_sequence_end - code;
                }
                else
                {
                    /* Consider just <esc>k to be the code */
                    resulting_length = 2;
                }
                found = true;
            }
        }
    }

    if (! found)
    {
        /* Generic VT100 one byte sequence: CSI followed by something in the range @ through _ */
        if (code[1] == L'[' && (code[2] >= L'@' && code[2] <= L'_'))
        {
            resulting_length = 3;
            found = true;
        }
    }

    if (! found)
    {
        /* Generic VT100 CSI-style sequence. <esc>, followed by zero or more ASCII characters NOT in the range [@,_], followed by one character in that range */
        if (code[1] == L'[')
        {
            // Start at 2 to skip over <esc>[
            size_t cursor = 2;
            for (; code[cursor] != L'\0'; cursor++)
            {
                /* Consume a sequence of ASCII characters not in the range [@, ~] */
                wchar_t c = code[cursor];

                /* If we're not in ASCII, just stop */
                if (c > 127)
                    break;

                /* If we're the end character, then consume it and then stop */
                if (c >= L'@' && c <= L'~')
                {
                    cursor++;
                    break;
                }
            }
            /* curs now indexes just beyond the end of the sequence (or at the terminating zero) */
            found = true;
            resulting_length = cursor;
        }
    }

    if (! found)
    {
        /* Generic VT100 two byte sequence: <esc> followed by something in the range @ through _ */
        if (code[1] >= L'@' && code[1] <= L'_')
        {
            resulting_length = 2;
            found = true;
        }
    }

    return resulting_length;
}
Example #25
0
/**
   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)
{
    size_t i;
    int retcode = 0;
    const wchar_t *val_str=NULL;

    if (is_path_variable(key))
    {
        /* Fix for https://github.com/fish-shell/fish-shell/issues/199 . Return success if any path setting succeeds. */
        bool any_success = false;

        /* Don't bother validating (or complaining about) values that are already present */
        wcstring_list_t existing_values;
        const env_var_t existing_variable = env_get_string(key, scope);
        if (! existing_variable.missing_or_empty())
            tokenize_variable_array(existing_variable, existing_values);

        for (i=0; i< val.size() ; i++)
        {
            const wcstring &dir = val.at(i);
            if (list_contains_string(existing_values, dir))
            {
                any_success = true;
                continue;
            }

            bool show_perror = false;
            int show_hint = 0;
            bool error = false;

            struct stat buff;
            if (wstat(dir, &buff))
            {
                error = true;
                show_perror = true;
            }

            if (!(S_ISDIR(buff.st_mode)))
            {
                error = true;
            }

            if (!error)
            {
                any_success = true;
            }
            else
            {
                append_format(stderr_buffer, _(BUILTIN_SET_PATH_ERROR), L"set", dir.c_str(), key);
                const wchar_t *colon = wcschr(dir.c_str(), L':');

                if (colon && *(colon+1))
                {
                    show_hint = 1;
                }

            }

            if (show_perror)
            {
                builtin_wperror(L"set");
            }

            if (show_hint)
            {
                append_format(stderr_buffer, _(BUILTIN_SET_PATH_HINT), L"set", key, key, wcschr(dir.c_str(), L':')+1);
            }

        }

        /* Fail at setting the path if we tried to set it to something non-empty, but it wound up empty. */
        if (! val.empty() && ! any_success)
        {
            return 1;
        }

    }

    wcstring sb;
    if (! val.empty())
    {
        for (i=0; i< val.size() ; i++)
        {
            sb.append(val[i]);
            if (i<val.size() - 1)
            {
                sb.append(ARRAY_SEP_STR);
            }
        }
        val_str = sb.c_str();
    }

    switch (env_set(key, val_str, scope | ENV_USER))
    {
        case ENV_PERM:
        {
            append_format(stderr_buffer, _(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);
            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);
            retcode=1;
            break;
        }
    }

    return retcode;
}
Example #26
0
static bool path_get_path_core(const wcstring &cmd, wcstring *out_path,
                               const env_var_t &bin_path_var) {
    int err = ENOENT;
    debug(3, L"path_get_path( '%ls' )", cmd.c_str());

    // If the command has a slash, it must be a full path.
    if (cmd.find(L'/') != wcstring::npos) {
        if (waccess(cmd, X_OK) != 0) {
            return false;
        }

        struct stat buff;
        if (wstat(cmd, &buff)) {
            return false;
        }
        if (S_ISREG(buff.st_mode)) {
            if (out_path) out_path->assign(cmd);
            return true;
        }
        errno = EACCES;
        return false;
    }

    wcstring bin_path;
    if (!bin_path_var.missing()) {
        bin_path = bin_path_var;
    } else {
        if (contains(PREFIX L"/bin", L"/bin", L"/usr/bin")) {
            bin_path = L"/bin" ARRAY_SEP_STR L"/usr/bin";
        } else {
            bin_path = L"/bin" ARRAY_SEP_STR L"/usr/bin" ARRAY_SEP_STR PREFIX L"/bin";
        }
    }

    wcstring nxt_path;
    wcstokenizer tokenizer(bin_path, ARRAY_SEP_STR);
    while (tokenizer.next(nxt_path)) {
        if (nxt_path.empty()) continue;
        append_path_component(nxt_path, cmd);
        if (waccess(nxt_path, X_OK) == 0) {
            struct stat buff;
            if (wstat(nxt_path, &buff) == -1) {
                if (errno != EACCES) {
                    wperror(L"stat");
                }
                continue;
            }
            if (S_ISREG(buff.st_mode)) {
                if (out_path) out_path->swap(nxt_path);
                return true;
            }
            err = EACCES;
        } else {
            switch (errno) {
                case ENOENT:
                case ENAMETOOLONG:
                case EACCES:
                case ENOTDIR: {
                    break;
                }
                default: {
                    debug(1, MISSING_COMMAND_ERR_MSG, nxt_path.c_str());
                    wperror(L"access");
                }
            }
        }
    }

    errno = err;
    return false;
}
Example #27
0
void env_init(const struct config_paths_t *paths /* or NULL */)
{
	char **p;
	struct passwd *pw;
	wchar_t *uname;
	wchar_t *version;
	
	/*
	  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",
    };
    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( p=environ?environ:__environ; p && *p; p++ )
	{
		wchar_t *key, *val;
		
		key = str2wcs(*p);

		if( !key )
		{
			continue;
		}
		
		val = wcschr( key, L'=' );

		if( val == 0 )
		{
			env_set( key, L"", ENV_EXPORT );
		}
		else
		{ 
			*val = L'\0';
			val++;
            
            //fwprintf( stderr, L"Set $%ls to %ls\n", key, val );
            if (variable_can_be_array(val)) {
                for (size_t i=0; val[i] != L'\0'; i++) {
                    if( val[i] == L':' ) {
                        val[i] = ARRAY_SEP;
                    }
                }
            }

			env_set( key, val, ENV_EXPORT | ENV_GLOBAL );
		}		
		free(key);
	}
	
    /* 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
	*/
	pw = getpwuid( getuid() );
	if( pw )
	{
		uname = str2wcs( pw->pw_name );
		env_set( L"USER", uname, ENV_GLOBAL | ENV_EXPORT );
		free( uname );
	}

	/*
	  Set up the version variable
	*/
	version = str2wcs( PACKAGE_VERSION );
	env_set( L"version", version, ENV_GLOBAL );
	free( version );

	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 = format_string(L"%ld", 1 + shlvl_i);
        }
	}
    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));
}
Example #28
0
/**
   Calculate the width of the specified prompt. Does some clever magic
   to detect common escape sequences that may be embeded in a prompt,
   such as color codes.
*/
static size_t calc_prompt_width( const wchar_t *prompt )
{
	size_t res = 0;
	size_t j, k;

	for( j=0; prompt[j]; j++ )
	{
		if( prompt[j] == L'\x1b' )
		{
			/*
			  This is the start of an escape code. Try to guess it's width.
			*/
			size_t p;
			int len=0;
			bool found = false;
			
			/*
			  Detect these terminfo color escapes with parameter
			  value 0..7, all of which don't move the cursor
			*/
			char * const esc[] =
				{
					set_a_foreground,
					set_a_background,
					set_foreground,
					set_background,
				}
			;

			/*
			  Detect these semi-common terminfo escapes without any
			  parameter values, all of which don't move the cursor
			*/
			char * const esc2[] =
				{
					enter_bold_mode,
					exit_attribute_mode,
					enter_underline_mode,
					exit_underline_mode,
					enter_standout_mode,
					exit_standout_mode,
					flash_screen,
					enter_subscript_mode,
					exit_subscript_mode,
					enter_superscript_mode,
					exit_superscript_mode,
					enter_blink_mode,
					enter_italics_mode,
					exit_italics_mode,
					enter_reverse_mode,
					enter_shadow_mode,
					exit_shadow_mode,
					enter_standout_mode,
					exit_standout_mode,
					enter_secure_mode
				}
			;

			for( p=0; p < sizeof esc / sizeof *esc && !found; p++ )
			{
				if( !esc[p] )
					continue;

				for( k=0; k<8; k++ )
				{
					len = try_sequence( tparm(esc[p],k), &prompt[j] );
					if( len )
					{
						j += (len-1);
						found = true;
						break;
					}
				}
			}
            
            // PCA for term256 support, let's just detect the escape codes directly
            if (! found) {
                len = is_term256_escape(&prompt[j]);
                if (len) {
                    j += (len - 1);
                    found = true;
                }
            }


			for( p=0; p < (sizeof(esc2)/sizeof(char *)) && !found; p++ )
			{
				if( !esc2[p] )
					continue;
				/*
				  Test both padded and unpadded version, just to
				  be safe. Most versions of tparm don't actually
				  seem to do anything these days.
				*/
				len = maxi( try_sequence( tparm(esc2[p]), &prompt[j] ),
					    try_sequence( esc2[p], &prompt[j] ));
					
				if( len )
				{
					j += (len-1);
					found = true;
				}
			}
				
			if( !found )
			{
				if( prompt[j+1] == L'k' )
				{
					const env_var_t term_name = env_get_string( L"TERM" );
					if( !term_name.missing() && wcsstr( term_name.c_str(), L"screen" ) == term_name )
					{
						const wchar_t *end;
						j+=2;
						found = true;
						end = wcsstr( &prompt[j], L"\x1b\\" );
						if( end )
						{
							/*
							  You'd thing this should be
							  '(end-prompt)+2', in order to move j
							  past the end of the string, but there is
							  a 'j++' at the end of each lap, so j
							  should always point to the last menged
							  character, e.g. +1.
							*/
							j = (end-prompt)+1;
						}
						else
						{
							break;
						}
					}						
				}					
			}
				
		}
		else if( prompt[j] == L'\t' )
		{
			res = next_tab_stop( res );
		}
		else if( prompt[j] == L'\n' )
		{
			res = 0;
		}
		else
		{
			/*
			  Ordinary decent character. Just add width.
			*/
			res += fish_wcwidth( prompt[j] );
		}
	}
	return res;
}
Example #29
0
/**
   The set builtin. Creates, updates and erases environment variables
   and environemnt variable arrays.
*/
static int builtin_set( parser_t &parser, wchar_t **argv ) 
{
	
	/**
	   Variables used for parsing the argument list
	*/
	static const struct woption
		long_options[] = 
		{
			{ 
				L"export", no_argument, 0, 'x' 
			}
			,
			{ 
				L"global", no_argument, 0, 'g' 
			}
			,
			{ 
				L"local", no_argument, 0, 'l'  
			}
			,
			{ 
				L"erase", no_argument, 0, 'e'  
			}
			,
			{ 
				L"names", no_argument, 0, 'n' 
			} 
			,
			{ 
				L"unexport", no_argument, 0, 'u' 
			} 
			,
			{ 
				L"universal", no_argument, 0, 'U'
			}
            ,
			{ 
				L"long", no_argument, 0, 'L'
			} 
			,
			{ 
				L"query", no_argument, 0, 'q' 
			} 
			,
			{ 
				L"help", no_argument, 0, 'h' 
			} 
			,
			{ 
				0, 0, 0, 0 
			}
		}
	;
	
	const wchar_t *short_options = L"+xglenuULqh";

	int argc = builtin_count_args(argv);

	/*
	  Flags to set the work mode
	*/
	int local = 0, global = 0, exportv = 0;
	int erase = 0, list = 0, unexport=0;
	int universal = 0, query=0;
	bool shorten_ok = true;

	/*
	  Variables used for performing the actual work
	*/
	wchar_t *dest = 0;
	int retcode=0;
	int scope;
	int slice=0;
	int i;
	
	wchar_t *bad_char;
	
	
	/* Parse options to obtain the requested operation and the modifiers */
	woptind = 0;
	while (1) 
	{
		int c = wgetopt_long(argc, argv, short_options, long_options, 0);

		if (c == -1) 
		{
			break;
		}
    
		switch(c) 
		{
			case 0:
				break;

			case 'e':
				erase = 1;
				break;

			case 'n':
				list = 1;
				break;

			case 'x':
				exportv = 1;
				break;

			case 'l':
				local = 1;
				break;

			case 'g':
				global = 1;
				break;

			case 'u':
				unexport = 1;
				break;

			case 'U':
				universal = 1;
				break;
            
            case 'L':
                shorten_ok = false;
                break;

			case 'q':
				query = 1;
				break;

			case 'h':
				builtin_print_help( parser, argv[0], stdout_buffer );
				return 0;

			case '?':
				builtin_unknown_option( parser, argv[0], argv[woptind-1] );
				return 1;

			default:
				break;
		}
	}

	/*
	  Ok, all arguments have been parsed, let's validate them
	*/

	/*
	  If we are checking the existance of a variable (-q) we can not
	  also specify scope
	*/

	if( query && (erase || list) )
	{
		append_format(stderr_buffer,
				  BUILTIN_ERR_COMBO,
				  argv[0] );
		
		builtin_print_help( parser, argv[0], stderr_buffer );
		return 1;
	}
	

	/* We can't both list and erase varaibles */
	if( erase && list ) 
	{
		append_format(stderr_buffer,
				  BUILTIN_ERR_COMBO,
				  argv[0] );		

		builtin_print_help( parser, argv[0], stderr_buffer );
		return 1;
	}

	/*
	  Variables can only have one scope
	*/
	if( local + global + universal > 1 ) 
	{
		append_format(stderr_buffer,
				   BUILTIN_ERR_GLOCAL,
				   argv[0] );
		builtin_print_help( parser, argv[0], stderr_buffer );
		return 1;
	}

	/*
	  Variables can only have one export status
	*/
	if( exportv && unexport ) 
	{
		append_format(stderr_buffer,
				   BUILTIN_ERR_EXPUNEXP,
				   argv[0] );
		builtin_print_help( parser, argv[0], stderr_buffer );
		return 1;
	}

	/*
	  Calculate the scope value for variable assignement
	*/
	scope = (local ? ENV_LOCAL : 0) | (global ? ENV_GLOBAL : 0) | (exportv ? ENV_EXPORT : 0) | (unexport ? ENV_UNEXPORT : 0) | (universal ? ENV_UNIVERSAL:0) | ENV_USER; 

	if( query )
	{
		/*
		  Query mode. Return the number of variables that do not exist
		  out of the specified variables.
		*/
		int i;
		for( i=woptind; i<argc; i++ )
		{
			wchar_t *arg = argv[i];
			int slice=0;

			if( !(dest = wcsdup(arg)))
			{
				DIE_MEM();		
			}

			if( wcschr( dest, L'[' ) )
			{
				slice = 1;
				*wcschr( dest, L'[' )=0;
			}
			
			if( slice )
			{
				std::vector<long> indexes;
				wcstring_list_t result;
				size_t j;
				
                env_var_t dest_str = env_get_string(dest);
                if (! dest_str.missing())
                    tokenize_variable_array( dest_str, result );
								
				if( !parse_index( indexes, arg, dest, result.size() ) )
				{
					builtin_print_help( parser, argv[0], stderr_buffer );
					retcode = 1;
					break;
				}
				for( j=0; j < indexes.size() ; j++ )
				{
					long idx = indexes[j];
					if( idx < 1 || (size_t)idx > result.size() )
					{
						retcode++;
					}
				}
			}
			else
			{
				if( !env_exist( arg, scope ) )
				{
					retcode++;
				}
			}
			
			free( dest );
			
		}
		return retcode;
	}
	
	if( list ) 
	{
		/* Maybe we should issue an error if there are any other arguments? */
		print_variables(0, 0, shorten_ok, scope);
		return 0;
	} 
	
	if( woptind == argc )
	{
		/*
		  Print values of variables
		*/

		if( erase ) 
		{
			append_format(stderr_buffer,
					   _(L"%ls: Erase needs a variable name\n"), 
					   argv[0] );
			
			builtin_print_help( parser, argv[0], stderr_buffer );
			retcode = 1;
		}
		else
		{
			print_variables( 1, 1, shorten_ok, scope );
		}
		
		return retcode;
	}

	if( !(dest = wcsdup(argv[woptind])))
	{
		DIE_MEM();		
	}

	if( wcschr( dest, L'[' ) )
	{
		slice = 1;
		*wcschr( dest, L'[' )=0;
	}
	
	if( !wcslen( dest ) )
	{
		free( dest );
		append_format(stderr_buffer, BUILTIN_ERR_VARNAME_ZERO, argv[0] );
		builtin_print_help( parser, argv[0], stderr_buffer );
		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 );
		free( dest );
		return 1;
	}
	
	if( slice && erase && (scope != ENV_USER) )
	{
		free( dest );
		append_format(stderr_buffer, _(L"%ls: Can not specify scope when erasing array slice\n"), argv[0] );
		builtin_print_help( parser, argv[0], stderr_buffer );
		return 1;
	}
	
	/*
	  set assignment can work in two modes, either using slices or
	  using the whole array. We detect which mode is used here.
	*/
	
	if( slice )
	{

		/*
		  Slice mode
		*/
		int idx_count, val_count;
		wcstring_list_t values;
		std::vector<long> indexes;
		wcstring_list_t result;
		
        const env_var_t dest_str = env_get_string(dest);
        if (! dest_str.missing())
            tokenize_variable_array( dest_str, result );
		
		for( ; woptind<argc; woptind++ )
		{			
			if( !parse_index( indexes, argv[woptind], dest, result.size() ) )
			{
				builtin_print_help( parser, argv[0], stderr_buffer );
				retcode = 1;
				break;
			}
			
			val_count = argc-woptind-1;
			idx_count = indexes.size();

			if( !erase )
			{
				if( val_count < idx_count )
				{
					append_format(stderr_buffer, _(BUILTIN_SET_ARG_COUNT), argv[0] );
					builtin_print_help( parser, argv[0], stderr_buffer );
					retcode=1;
					break;
				}
				if( val_count == idx_count )
				{
					woptind++;
					break;
				}
			}
		}		

		if( !retcode )
		{
			/*
			  Slice indexes have been calculated, do the actual work
			*/

			if( erase )
			{
				erase_values(result, indexes);
				my_env_set( dest, result, scope);
			}
			else
			{
				wcstring_list_t value;
//				al_init(&value);

				while( woptind < argc ) 
				{
					value.push_back( argv[woptind++] );
				}

				if( update_values( result, 
								   indexes,
								   value ) )
				{
					append_format(stderr_buffer, L"%ls: ", argv[0] );
					append_format(stderr_buffer, ARRAY_BOUNDS_ERR );
					stderr_buffer.push_back(L'\n');
				}
				
				my_env_set(dest, result, scope);
								
//				al_destroy( &value );
								
			}			
		}

//		al_foreach( &result, &free );
//		al_destroy( &result );

//		al_destroy(&indexes);
//		al_destroy(&values);
		
	}
	else
	{
		woptind++;
		
		/*
		  No slicing
		*/
		if( erase )
		{
			if( woptind != argc )
			{
				append_format(stderr_buffer, 
						   _(L"%ls: Values cannot be specfied with erase\n"),
						   argv[0] );
				builtin_print_help( parser, argv[0], stderr_buffer );
				retcode=1;
			}
			else
			{
				retcode = env_remove( dest, scope );
			}
		}
		else
		{
            wcstring_list_t val;
			for( i=woptind; i<argc; i++ )
                val.push_back(argv[i]);
			retcode = my_env_set( dest, val, scope );
		}		
	}
	
	free( dest );
	
	return retcode;

}
Example #30
0
bool path_get_cdpath_string(const wcstring &dir_str, wcstring &result, const env_var_t &cdpath)
{
    wchar_t *res = 0;
    int err = ENOENT;
    bool success = false;

    const wchar_t *const dir = dir_str.c_str();
    if (dir[0] == L'/'|| (wcsncmp(dir, L"./", 2)==0))
    {
        struct stat buf;
        if (wstat(dir, &buf) == 0)
        {
            if (S_ISDIR(buf.st_mode))
            {
                result = dir_str;
                success = true;
            }
            else
            {
                err = ENOTDIR;
            }

        }
    }
    else
    {

        wcstring path = L".";

        // Respect CDPATH
        env_var_t cdpath = env_get_string(L"CDPATH");
        if (! cdpath.missing_or_empty())
        {
            path = cdpath.c_str();
        }

        wcstokenizer tokenizer(path, ARRAY_SEP_STR);
        wcstring next_path;
        while (tokenizer.next(next_path))
        {
            expand_tilde(next_path);
            if (next_path.size() == 0) continue;

            wcstring whole_path = next_path;
            append_path_component(whole_path, dir);

            struct stat buf;
            if (wstat(whole_path, &buf) == 0)
            {
                if (S_ISDIR(buf.st_mode))
                {
                    result = whole_path;
                    success = true;
                    break;
                }
                else
                {
                    err = ENOTDIR;
                }
            }
            else
            {
                if (lwstat(whole_path, &buf) == 0)
                {
                    err = EROTTEN;
                }
            }
        }
    }


    if (!success)
    {
        errno = err;
    }

    return res;
}