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; } }
wcstring_list_t env_get_names(int flags) { scoped_lock lock(env_lock); wcstring_list_t result; std::set<wcstring> names; int show_local = flags & ENV_LOCAL; int show_global = flags & ENV_GLOBAL; int show_universal = flags & ENV_UNIVERSAL; env_node_t *n=top; const bool show_exported = (flags & ENV_EXPORT) || !(flags & ENV_UNEXPORT); const bool show_unexported = (flags & ENV_UNEXPORT) || !(flags & ENV_EXPORT); if (!show_local && !show_global && !show_universal) { show_local =show_universal = show_global=1; } if (show_local) { while (n) { if (n == global_env) break; add_key_to_string_set(n->env, &names, show_exported, show_unexported); if (n->new_scope) break; else n = n->next; } } if (show_global) { add_key_to_string_set(global_env->env, &names, show_exported, show_unexported); if (show_unexported) { result.insert(result.end(), env_electric.begin(), env_electric.end()); } if (show_exported) { result.push_back(L"COLUMNS"); result.push_back(L"LINES"); } } if (show_universal && uvars()) { const wcstring_list_t uni_list = uvars()->get_names(show_exported, show_unexported); names.insert(uni_list.begin(), uni_list.end()); } result.insert(result.end(), names.begin(), names.end()); return result; }
int env_remove(const wcstring &key, int var_mode) { ASSERT_IS_MAIN_THREAD(); env_node_t *first_node; int erased = 0; if ((var_mode & ENV_USER) && is_read_only(key)) { return 2; } first_node = top; if (!(var_mode & ENV_UNIVERSAL)) { if (var_mode & ENV_GLOBAL) { first_node = global_env; } if (try_remove(first_node, key.c_str(), var_mode)) { event_t ev = event_t::variable_event(key); ev.arguments.push_back(L"VARIABLE"); ev.arguments.push_back(L"ERASE"); ev.arguments.push_back(key); event_fire(&ev); erased = 1; } } if (!erased && !(var_mode & ENV_GLOBAL) && !(var_mode & ENV_LOCAL)) { bool is_exported = uvars()->get_export(key); erased = uvars() && uvars()->remove(key); if (erased) { env_universal_barrier(); event_t ev = event_t::variable_event(key); ev.arguments.push_back(L"VARIABLE"); ev.arguments.push_back(L"ERASE"); ev.arguments.push_back(key); event_fire(&ev); } if (is_exported) mark_changed_exported(); } react_to_variable_change(key); return !erased; }
bool env_exist(const wchar_t *key, env_mode_flags_t mode) { CHECK(key, false); const bool has_scope = mode & (ENV_LOCAL | ENV_GLOBAL | ENV_UNIVERSAL); const bool test_local = !has_scope || (mode & ENV_LOCAL); const bool test_global = !has_scope || (mode & ENV_GLOBAL); const bool test_universal = !has_scope || (mode & ENV_UNIVERSAL); const bool test_exported = (mode & ENV_EXPORT) || !(mode & ENV_UNEXPORT); const bool test_unexported = (mode & ENV_UNEXPORT) || !(mode & ENV_EXPORT); if (is_electric(key)) { // Electric variables all exist, and they are all global. A local or universal version can // not exist. They are also never exported. if (test_global && test_unexported) { return true; } return false; } if (test_local || test_global) { const env_node_t *env = test_local ? top : global_env; while (env != NULL) { if (env == global_env && !test_global) { break; } var_table_t::const_iterator result = env->env.find(key); if (result != env->env.end()) { const var_entry_t &res = result->second; return res.exportv ? test_exported : test_unexported; } env = env->next_scope_to_search(); } } if (test_universal) { if (!get_proc_had_barrier()) { set_proc_had_barrier(true); env_universal_barrier(); } if (uvars() && !uvars()->get(key).missing()) { return uvars()->get_export(key) ? test_exported : test_unexported; } } return 0; }
void env_universal_barrier() { ASSERT_IS_MAIN_THREAD(); if (uvars()) { callback_data_list_t changes; bool changed = uvars()->sync(&changes); if (changed) { universal_notifier_t::default_notifier().post_notification(); } // Post callbacks. for (size_t i = 0; i < changes.size(); i++) { const callback_data_t &data = changes.at(i); universal_callback(data.type, data.key.c_str()); } } }
env_var_t env_get_string(const wcstring &key, env_mode_flags_t mode) { const bool has_scope = mode & (ENV_LOCAL | ENV_GLOBAL | ENV_UNIVERSAL); const bool search_local = !has_scope || (mode & ENV_LOCAL); const bool search_global = !has_scope || (mode & ENV_GLOBAL); const bool search_universal = !has_scope || (mode & ENV_UNIVERSAL); const bool search_exported = (mode & ENV_EXPORT) || !(mode & ENV_UNEXPORT); const bool search_unexported = (mode & ENV_UNEXPORT) || !(mode & ENV_EXPORT); /* Make the assumption that electric keys can't be shadowed elsewhere, since we currently block that in env_set() */ if (is_electric(key)) { if (!search_global) return env_var_t::missing_var(); /* Big hack...we only allow getting the history on the main thread. Note that history_t may ask for an environment variable, so don't take the lock here (we don't need it) */ if (key == L"history" && is_main_thread()) { env_var_t result; history_t *history = reader_get_history(); if (! history) { history = &history_t::history_with_name(L"fish"); } if (history) history->get_string_representation(&result, ARRAY_SEP_STR); return result; } else if (key == L"COLUMNS") { return to_string(common_get_width()); } else if (key == L"LINES") { return to_string(common_get_height()); } else if (key == L"status") { return to_string(proc_get_last_status()); } else if (key == L"umask") { return format_string(L"0%0.3o", get_umask()); } // we should never get here unless the electric var list is out of sync } if (search_local || search_global) { /* Lock around a local region */ scoped_lock lock(env_lock); env_node_t *env = search_local ? top : global_env; while (env != NULL) { const var_entry_t *entry = env->find_entry(key); if (entry != NULL && (entry->exportv ? search_exported : search_unexported)) { if (entry->val == ENV_NULL) { return env_var_t::missing_var(); } else { return entry->val; } } if (has_scope) { if (!search_global || env == global_env) break; env = global_env; } else { env = env->next_scope_to_search(); } } } if (!search_universal) return env_var_t::missing_var(); /* Another big hack - only do a universal barrier on the main thread (since it can change variable values) Make sure we do this outside the env_lock because it may itself call env_get_string */ if (is_main_thread() && ! get_proc_had_barrier()) { set_proc_had_barrier(true); env_universal_barrier(); } if (uvars()) { env_var_t env_var = uvars()->get(key); if (env_var == ENV_NULL || !(uvars()->get_export(key) ? search_exported : search_unexported)) { env_var = env_var_t::missing_var(); } return env_var; } return env_var_t::missing_var(); }
int env_set(const wcstring &key, const wchar_t *val, env_mode_flags_t var_mode) { ASSERT_IS_MAIN_THREAD(); bool has_changed_old = has_changed_exported; bool has_changed_new = false; int done=0; if (val && contains(key, L"PWD", L"HOME")) { /* Canoncalize our path; if it changes, recurse and try again. */ wcstring val_canonical = val; path_make_canonical(val_canonical); if (val != val_canonical) { return env_set(key, val_canonical.c_str(), var_mode); } } if ((var_mode & (ENV_LOCAL | ENV_UNIVERSAL)) && (is_read_only(key) || is_electric(key))) { return ENV_SCOPE; } if ((var_mode & ENV_EXPORT) && is_electric(key)) { return ENV_SCOPE; } if ((var_mode & ENV_USER) && is_read_only(key)) { return ENV_PERM; } if (key == L"umask") { wchar_t *end; /* Set the new umask */ if (val && wcslen(val)) { errno=0; long mask = wcstol(val, &end, 8); if (!errno && (!*end) && (mask <= 0777) && (mask >= 0)) { umask(mask); /* Do not actually create a umask variable, on env_get, it will be calculated dynamically */ return 0; } } return ENV_INVALID; } /* Zero element arrays are internaly not coded as null but as this placeholder string */ if (!val) { val = ENV_NULL; } if (var_mode & ENV_UNIVERSAL) { const bool old_export = uvars() && uvars()->get_export(key); bool new_export; if (var_mode & ENV_EXPORT) { // export new_export = true; } else if (var_mode & ENV_UNEXPORT) { // unexport new_export = false; } else { // not changing the export new_export = old_export; } if (uvars()) { uvars()->set(key, val, new_export); env_universal_barrier(); if (old_export || new_export) { mark_changed_exported(); } } } else { // Determine the node env_node_t *preexisting_node = env_get_node(key); bool preexisting_entry_exportv = false; if (preexisting_node != NULL) { var_table_t::const_iterator result = preexisting_node->env.find(key); assert(result != preexisting_node->env.end()); const var_entry_t &entry = result->second; if (entry.exportv) { preexisting_entry_exportv = true; has_changed_new = true; } } env_node_t *node = NULL; if (var_mode & ENV_GLOBAL) { node = global_env; } else if (var_mode & ENV_LOCAL) { node = top; } else if (preexisting_node != NULL) { node = preexisting_node; if ((var_mode & (ENV_EXPORT | ENV_UNEXPORT)) == 0) { // use existing entry's exportv var_mode = preexisting_entry_exportv ? ENV_EXPORT : 0; } } else { if (! get_proc_had_barrier()) { set_proc_had_barrier(true); env_universal_barrier(); } if (uvars() && ! uvars()->get(key).missing()) { bool exportv; if (var_mode & ENV_EXPORT) { exportv = true; } else if (var_mode & ENV_UNEXPORT) { exportv = false; } else { exportv = uvars()->get_export(key); } uvars()->set(key, val, exportv); env_universal_barrier(); done = 1; } else { /* New variable with unspecified scope. The default scope is the innermost scope that is shadowing, which will be either the current function or the global scope. */ node = top; while (node->next && !node->new_scope) { node = node->next; } } } if (!done) { // Set the entry in the node // Note that operator[] accesses the existing entry, or creates a new one var_entry_t &entry = node->env[key]; if (entry.exportv) { // this variable already existed, and was exported has_changed_new = true; } entry.val = val; if (var_mode & ENV_EXPORT) { // the new variable is exported entry.exportv = true; node->exportv = true; has_changed_new = true; } else { entry.exportv = false; } if (has_changed_old || has_changed_new) mark_changed_exported(); } } event_t ev = event_t::variable_event(key); ev.arguments.reserve(3); ev.arguments.push_back(L"VARIABLE"); ev.arguments.push_back(L"SET"); ev.arguments.push_back(key); // debug( 1, L"env_set: fire events on variable %ls", key ); event_fire(&ev); // debug( 1, L"env_set: return from event firing" ); react_to_variable_change(key); return 0; }
env_var_t env_get_string(const wcstring &key) { /* Big hack...we only allow getting the history on the main thread. Note that history_t may ask for an environment variable, so don't take the lock here (we don't need it) */ const bool is_main = is_main_thread(); if (key == L"history" && is_main) { env_var_t result; history_t *history = reader_get_history(); if (! history) { history = &history_t::history_with_name(L"fish"); } if (history) history->get_string_representation(result, ARRAY_SEP_STR); return result; } else if (key == L"COLUMNS") { return to_string(common_get_width()); } else if (key == L"LINES") { return to_string(common_get_height()); } else if (key == L"status") { return to_string(proc_get_last_status()); } else if (key == L"umask") { return format_string(L"0%0.3o", get_umask()); } else { { /* Lock around a local region */ scoped_lock lock(env_lock); env_node_t *env = top; wcstring result; while (env != NULL) { const var_entry_t *entry = env->find_entry(key); if (entry != NULL) { if (entry->val == ENV_NULL) { return env_var_t::missing_var(); } else { return entry->val; } } env = env->next_scope_to_search(); } } /* Another big hack - only do a universal barrier on the main thread (since it can change variable values) Make sure we do this outside the env_lock because it may itself call env_get_string */ if (is_main && ! get_proc_had_barrier()) { set_proc_had_barrier(true); env_universal_barrier(); } env_var_t env_var = uvars() ? uvars()->get(key) : env_var_t::missing_var(); if (env_var == ENV_NULL) { env_var = env_var_t::missing_var(); } return env_var; } }
bool env_exist(const wchar_t *key, int mode) { env_node_t *env; CHECK(key, false); /* Read only variables all exist, and they are all global. A local version can not exist. */ if (!(mode & ENV_LOCAL) && !(mode & ENV_UNIVERSAL)) { if (is_read_only(key) || is_electric(key)) { //Such variables are never exported if (mode & ENV_EXPORT) { return false; } else if (mode & ENV_UNEXPORT) { return true; } return true; } } if (!(mode & ENV_UNIVERSAL)) { env = (mode & ENV_GLOBAL)?global_env:top; while (env != 0) { var_table_t::iterator result = env->env.find(key); if (result != env->env.end()) { const var_entry_t &res = result->second; if (mode & ENV_EXPORT) { return res.exportv; } else if (mode & ENV_UNEXPORT) { return ! res.exportv; } return true; } if (mode & ENV_LOCAL) break; env = env->next_scope_to_search(); } } if (!(mode & ENV_LOCAL) && !(mode & ENV_GLOBAL)) { if (! get_proc_had_barrier()) { set_proc_had_barrier(true); env_universal_barrier(); } if (uvars() && ! uvars()->get(key).missing()) { if (mode & ENV_EXPORT) { return uvars()->get_export(key); } else if (mode & ENV_UNEXPORT) { return ! uvars()->get_export(key); } return 1; } } return 0; }