/** Test path functions */ static void test_path() { say( L"Testing path functions" ); void *context = halloc( 0, 0 ); wchar_t *can = path_make_canonical( context, L"//foo//////bar/" ); if( wcscmp( can, L"/foo/bar" ) ) { err( L"Bug in canonical PATH code" ); } halloc_free( context ); }
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; }
int env_set(const wcstring &key, const wchar_t *val, int var_mode) { ASSERT_IS_MAIN_THREAD(); env_node_t *node = NULL; bool has_changed_old = has_changed_exported; bool has_changed_new = false; var_entry_t *e=0; int done=0; int is_universal = 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_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; } /* Zero element arrays are internaly not coded as null but as this placeholder string */ if( !val ) { val = ENV_NULL; } if( var_mode & ENV_UNIVERSAL ) { int exportv = 0; if( !(var_mode & ENV_EXPORT ) && !(var_mode & ENV_UNEXPORT ) ) { env_universal_get_export( key ); } else { exportv = (var_mode & ENV_EXPORT ); } env_universal_set(key, val, exportv); is_universal = 1; } else { node = env_get_node( key ); if( node ) { var_table_t::iterator result = node->env.find(key); assert(result != node->env.end()); e = result->second; if( e->exportv ) { has_changed_new = true; } } if( (var_mode & ENV_LOCAL) || (var_mode & ENV_GLOBAL) ) { node = ( var_mode & ENV_GLOBAL )?global_env:top; } else { if( node ) { if( !(var_mode & ENV_EXPORT ) && !(var_mode & ENV_UNEXPORT ) ) { var_mode = e->exportv?ENV_EXPORT:0; } } else { if( ! get_proc_had_barrier()) { set_proc_had_barrier(true); env_universal_barrier(); } if( env_universal_get( key ) ) { int exportv = 0; if( !(var_mode & ENV_EXPORT ) && !(var_mode & ENV_UNEXPORT ) ) { env_universal_get_export( key ); } else { exportv = (var_mode & ENV_EXPORT ); } env_universal_set(key, val, exportv); is_universal = 1; 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 ) { var_entry_t *old_entry = NULL; var_table_t::iterator result = node->env.find(key); if ( result != node->env.end() ) { old_entry = result->second; node->env.erase(result); } var_entry_t *entry = NULL; if( old_entry ) { entry = old_entry; if( (var_mode & ENV_EXPORT) || entry->exportv ) { entry->exportv = !!(var_mode & ENV_EXPORT); has_changed_new = true; } } else { entry = new var_entry_t; if( var_mode & ENV_EXPORT) { entry->exportv = 1; has_changed_new = true; } else { entry->exportv = 0; } } entry->val = val; node->env[key] = entry; if( entry->exportv ) { node->exportv=1; } if (has_changed_old || has_changed_new) mark_changed_exported(); } } if( !is_universal ) { event_t ev = event_t::variable_event(key); ev.arguments.reset(new wcstring_list_t); 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" ); ev.arguments.reset(NULL); } react_to_variable_change(key); return 0; }