2005-09-20 13:26:39 +00:00
/** \file env.c
2012-11-18 10:23:22 +00:00
Functions for setting and getting environment variables .
2005-09-20 13:26:39 +00:00
*/
2005-10-05 09:58:00 +00:00
# include "config.h"
2005-09-20 13:26:39 +00:00
# include <stdlib.h>
# include <wchar.h>
# include <string.h>
# include <stdio.h>
# include <locale.h>
# include <unistd.h>
# include <signal.h>
2012-01-14 09:06:47 +00:00
# include <assert.h>
2005-09-20 13:26:39 +00:00
# include <sys/types.h>
# include <sys/stat.h>
2011-12-27 03:18:46 +00:00
# include <pthread.h>
2005-09-20 13:26:39 +00:00
# include <pwd.h>
2011-12-27 03:18:46 +00:00
# include <set>
# include <map>
2012-02-17 18:52:30 +00:00
# include <algorithm>
2005-09-20 13:26:39 +00:00
# if HAVE_NCURSES_H
# include <ncurses.h>
# else
# include <curses.h>
# endif
2006-01-19 12:22:07 +00:00
# if HAVE_TERM_H
2005-09-20 13:26:39 +00:00
# include <term.h>
2006-01-19 12:22:07 +00:00
# elif HAVE_NCURSES_TERM_H
# include <ncurses/term.h>
# endif
2006-07-19 23:11:49 +00:00
# if HAVE_LIBINTL_H
# include <libintl.h>
# endif
2005-10-22 10:06:05 +00:00
# include <errno.h>
2005-09-20 13:26:39 +00:00
2006-02-28 13:17:16 +00:00
# include "fallback.h"
2005-09-20 13:26:39 +00:00
# include "util.h"
2006-02-28 13:17:16 +00:00
2005-09-20 13:26:39 +00:00
# include "wutil.h"
# include "proc.h"
# include "common.h"
# include "env.h"
# include "sanity.h"
# include "expand.h"
# include "history.h"
# include "reader.h"
# include "parser.h"
2014-06-16 00:30:50 +00:00
# include "env_universal_common.h"
2012-03-05 22:18:16 +00:00
# include "input.h"
2005-10-05 22:37:08 +00:00
# include "event.h"
2007-05-10 19:11:28 +00:00
# include "path.h"
2006-07-19 22:55:49 +00:00
2006-01-13 01:00:12 +00:00
# include "complete.h"
2014-05-01 07:46:27 +00:00
# include "fish_version.h"
2005-09-20 13:26:39 +00:00
2012-12-28 04:26:16 +00:00
/** Value denoting a null string */
2005-09-26 14:47:03 +00:00
# define ENV_NULL L"\x1d"
2012-07-18 17:50:38 +00:00
/** Some configuration path environment variables */
# define FISH_DATADIR_VAR L"__fish_datadir"
# define FISH_SYSCONFDIR_VAR L"__fish_sysconfdir"
# define FISH_HELPDIR_VAR L"__fish_help_dir"
# define FISH_BIN_DIR L"__fish_bin_dir"
2005-09-20 13:26:39 +00:00
/**
2007-01-27 16:59:11 +00:00
At init , we read all the environment variables from this array .
2005-09-20 13:26:39 +00:00
*/
extern char * * environ ;
2007-01-27 16:59:11 +00:00
/**
This should be the same thing as \ c environ , but it is possible only one of the two work . . .
*/
extern char * * __environ ;
2005-09-20 13:26:39 +00:00
2012-03-07 08:54:01 +00:00
bool g_log_forks = false ;
2012-08-15 07:57:56 +00:00
bool g_use_posix_spawn = false ; //will usually be set to true
2012-03-06 23:51:48 +00:00
2005-09-20 13:26:39 +00:00
/**
Struct representing one level in the function variable stack
*/
2012-02-26 02:54:49 +00:00
struct env_node_t
2005-09-20 13:26:39 +00:00
{
2012-11-19 00:30:30 +00:00
/**
Variable table
*/
var_table_t env ;
/**
Does this node imply a new variable scope ? If yes , all
non - global variables below this one in the stack are
invisible . If new_scope is set for the global variable node ,
the universe will explode .
*/
2013-01-19 21:21:55 +00:00
bool new_scope ;
2012-11-19 00:30:30 +00:00
/**
Does this node contain any variables which are exported to subshells
*/
2013-01-19 21:16:21 +00:00
bool exportv ;
2012-11-19 00:30:30 +00:00
/**
Pointer to next level
*/
struct env_node_t * next ;
2013-02-20 07:16:44 +00:00
env_node_t ( ) : new_scope ( false ) , exportv ( false ) , next ( NULL ) { }
2013-02-20 01:48:51 +00:00
/* Returns a pointer to the given entry if present, or NULL. */
const var_entry_t * find_entry ( const wcstring & key ) ;
/* Returns the next scope to search in order, respecting the new_scope flag, or NULL if we're done. */
env_node_t * next_scope_to_search ( void ) ;
2012-02-26 02:54:49 +00:00
} ;
2005-09-20 13:26:39 +00:00
2012-11-19 00:30:30 +00:00
class variable_entry_t
{
2011-12-27 03:18:46 +00:00
wcstring value ; /**< Value of the variable */
} ;
static pthread_mutex_t env_lock = PTHREAD_MUTEX_INITIALIZER ;
2013-02-20 01:48:51 +00:00
/** Top node on the function stack */
static env_node_t * top = NULL ;
2005-09-20 13:26:39 +00:00
2013-02-20 01:48:51 +00:00
/** Bottom node on the function stack */
static env_node_t * global_env = NULL ;
2005-09-20 13:26:39 +00:00
2014-06-16 00:30:50 +00:00
/** Universal variables global instance. Initialized in env_init. */
static env_universal_t * s_universal_variables = NULL ;
/* Getter for universal variables */
static env_universal_t * uvars ( ) {
return s_universal_variables ;
}
2005-09-20 13:26:39 +00:00
/**
Table for global variables
*/
2012-02-28 23:11:46 +00:00
static var_table_t * global ;
2005-09-20 13:26:39 +00:00
2013-02-20 01:48:51 +00:00
/* Helper class for storing constant strings, without needing to wrap them in a wcstring */
/* Comparer for const string set */
struct const_string_set_comparer
{
bool operator ( ) ( const wchar_t * a , const wchar_t * b )
{
return wcscmp ( a , b ) < 0 ;
}
} ;
typedef std : : set < const wchar_t * , const_string_set_comparer > const_string_set_t ;
/** Table of variables that may not be set using the set command. */
static const_string_set_t env_read_only ;
2011-12-27 03:18:46 +00:00
static bool is_read_only ( const wcstring & key )
{
2013-02-20 01:48:51 +00:00
return env_read_only . find ( key . c_str ( ) ) ! = env_read_only . end ( ) ;
2011-12-27 03:18:46 +00:00
}
2005-09-20 13:26:39 +00:00
2005-10-23 12:14:29 +00:00
/**
Table of variables whose value is dynamically calculated , such as umask , status , etc
*/
2013-02-20 01:48:51 +00:00
static const_string_set_t env_electric ;
2011-12-27 03:18:46 +00:00
static bool is_electric ( const wcstring & key )
{
2013-02-20 01:48:51 +00:00
return env_electric . find ( key . c_str ( ) ) ! = env_electric . end ( ) ;
2011-12-27 03:18:46 +00:00
}
2005-09-20 13:26:39 +00:00
/**
Exported variable array used by execv
*/
2012-02-28 23:11:46 +00:00
static null_terminated_array_t < char > export_array ;
2005-09-20 13:26:39 +00:00
/**
Flag for checking if we need to regenerate the exported variable
array
*/
2012-08-15 07:57:56 +00:00
static bool has_changed_exported = true ;
static void mark_changed_exported ( )
{
has_changed_exported = true ;
}
2005-09-20 13:26:39 +00:00
2006-01-21 20:42:17 +00:00
/**
List of all locale variable names
*/
2012-03-05 22:18:16 +00:00
static const wchar_t * const locale_variable [ ] =
2006-01-21 20:42:17 +00:00
{
2012-11-19 00:30:30 +00:00
L " LANG " ,
L " LC_ALL " ,
L " LC_COLLATE " ,
L " LC_CTYPE " ,
L " LC_MESSAGES " ,
L " LC_MONETARY " ,
L " LC_NUMERIC " ,
L " LC_TIME " ,
NULL
2012-03-05 22:18:16 +00:00
} ;
2006-01-21 20:42:17 +00:00
2013-02-20 01:48:51 +00:00
const var_entry_t * env_node_t : : find_entry ( const wcstring & key )
{
const var_entry_t * result = NULL ;
var_table_t : : const_iterator where = env . find ( key ) ;
if ( where ! = env . end ( ) )
{
result = & where - > second ;
}
return result ;
}
env_node_t * env_node_t : : next_scope_to_search ( void )
{
return this - > new_scope ? global_env : this - > next ;
}
2005-10-23 12:14:29 +00:00
/**
Return the current umask value .
*/
static mode_t get_umask ( )
{
2012-11-19 00:30:30 +00:00
mode_t res ;
res = umask ( 0 ) ;
umask ( res ) ;
return res ;
2005-10-23 12:14:29 +00:00
}
2012-03-05 22:18:16 +00:00
/** Checks if the specified variable is a locale variable */
2012-11-19 00:30:30 +00:00
static bool var_is_locale ( const wcstring & key )
{
for ( size_t i = 0 ; locale_variable [ i ] ; i + + )
{
if ( key = = locale_variable [ i ] )
{
return true ;
}
2012-11-18 10:23:22 +00:00
}
2012-11-19 00:30:30 +00:00
return false ;
2006-01-08 23:00:49 +00:00
}
/**
Properly sets all locale information
*/
static void handle_locale ( )
{
2012-11-19 00:30:30 +00:00
const env_var_t lc_all = env_get_string ( L " LC_ALL " ) ;
const wcstring old_locale = wsetlocale ( LC_MESSAGES , NULL ) ;
2006-01-13 01:00:12 +00:00
2012-11-19 00:30:30 +00:00
/*
Array of locale constants corresponding to the local variable names defined in locale_variable
*/
static const int cat [ ] =
2012-11-18 10:23:22 +00:00
{
2012-11-19 00:30:30 +00:00
0 ,
LC_ALL ,
LC_COLLATE ,
LC_CTYPE ,
LC_MESSAGES ,
LC_MONETARY ,
LC_NUMERIC ,
LC_TIME
2012-11-18 10:23:22 +00:00
}
2012-11-19 00:30:30 +00:00
;
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
if ( ! lc_all . missing ( ) )
2012-11-18 10:23:22 +00:00
{
2012-11-19 00:30:30 +00:00
wsetlocale ( LC_ALL , lc_all . c_str ( ) ) ;
2012-11-18 10:23:22 +00:00
}
2012-11-19 00:30:30 +00:00
else
2012-11-18 10:23:22 +00:00
{
2012-11-19 00:30:30 +00:00
const env_var_t lang = env_get_string ( L " LANG " ) ;
if ( ! lang . missing ( ) )
{
wsetlocale ( LC_ALL , lang . c_str ( ) ) ;
}
2012-11-18 10:23:22 +00:00
2014-01-12 21:42:26 +00:00
for ( int i = 2 ; locale_variable [ i ] ; i + + )
2012-11-19 00:30:30 +00:00
{
const env_var_t val = env_get_string ( locale_variable [ i ] ) ;
if ( ! val . missing ( ) )
{
wsetlocale ( cat [ i ] , val . c_str ( ) ) ;
}
}
2012-11-18 10:23:22 +00:00
}
2012-02-01 05:06:52 +00:00
const wcstring new_locale = wsetlocale ( LC_MESSAGES , NULL ) ;
2012-11-19 00:30:30 +00:00
if ( old_locale ! = new_locale )
{
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
/*
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 .
*/
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
extern int _nl_msg_cat_cntr ;
_nl_msg_cat_cntr + + ;
2012-11-18 10:23:22 +00:00
2013-04-08 19:02:43 +00:00
fish_dcgettext ( " fish " , " Changing language to English " , LC_MESSAGES ) ;
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
if ( get_is_interactive ( ) )
{
2014-10-08 18:46:03 +00:00
debug ( 2 , _ ( L " Changing language to English " ) ) ;
2012-11-19 00:30:30 +00:00
}
2012-11-18 10:23:22 +00:00
}
2006-01-08 23:00:49 +00:00
}
2012-03-05 22:18:16 +00:00
/** React to modifying hte given variable */
2012-11-19 00:30:30 +00:00
static void react_to_variable_change ( const wcstring & key )
{
if ( var_is_locale ( key ) )
{
2012-03-05 22:18:16 +00:00
handle_locale ( ) ;
2012-11-19 00:30:30 +00:00
}
2014-09-20 01:44:18 +00:00
else if ( key = = L " fish_term256 " | | key = = L " fish_term24bit " )
2012-11-19 00:30:30 +00:00
{
2014-09-19 22:37:31 +00:00
update_fish_color_support ( ) ;
2012-03-25 10:00:38 +00:00
reader_react_to_color_change ( ) ;
2012-11-19 00:30:30 +00:00
}
else if ( string_prefixes_string ( L " fish_color_ " , key ) )
{
2012-03-25 10:00:38 +00:00
reader_react_to_color_change ( ) ;
2012-03-05 22:18:16 +00:00
}
}
2005-10-23 12:14:29 +00:00
/**
Universal variable callback function . This function makes sure the
proper events are triggered when an event occurs .
*/
2014-06-16 00:30:50 +00:00
static void universal_callback ( fish_message_type_t type , const wchar_t * name , const wchar_t * val )
2005-10-11 19:23:43 +00:00
{
2013-02-20 01:48:51 +00:00
const wchar_t * str = NULL ;
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
switch ( type )
{
2012-11-19 08:31:03 +00:00
case SET :
case SET_EXPORT :
{
str = L " SET " ;
break ;
}
2014-06-16 19:25:33 +00:00
case ERASE :
{
str = L " ERASE " ;
break ;
}
2012-11-19 08:31:03 +00:00
default :
break ;
2012-11-19 00:30:30 +00:00
}
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
if ( str )
{
mark_changed_exported ( ) ;
2012-11-18 10:23:22 +00:00
2012-02-09 03:02:25 +00:00
event_t ev = event_t : : variable_event ( name ) ;
2012-12-20 09:52:44 +00:00
ev . arguments . push_back ( L " VARIABLE " ) ;
ev . arguments . push_back ( str ) ;
ev . arguments . push_back ( name ) ;
2012-11-19 00:30:30 +00:00
event_fire ( & ev ) ;
}
2012-11-18 10:23:22 +00:00
2012-03-25 10:00:38 +00:00
if ( name )
react_to_variable_change ( name ) ;
2005-10-11 19:23:43 +00:00
}
2006-01-21 20:42:17 +00:00
/**
2013-08-28 01:23:33 +00:00
Make sure the PATH variable contains something
2006-01-21 20:42:17 +00:00
*/
static void setup_path ( )
2012-11-18 10:23:22 +00:00
{
2013-08-28 01:23:33 +00:00
const env_var_t path = env_get_string ( L " PATH " ) ;
if ( path . missing_or_empty ( ) )
2012-01-14 10:42:17 +00:00
{
2013-08-28 01:23:33 +00:00
const wchar_t * value = L " /usr/bin " ARRAY_SEP_STR L " /bin " ;
env_set ( L " PATH " , value , ENV_GLOBAL | ENV_EXPORT ) ;
2012-11-18 10:23:22 +00:00
}
2006-01-21 20:42:17 +00:00
}
2008-01-16 22:07:38 +00:00
int env_set_pwd ( )
{
2012-11-19 00:30:30 +00:00
wchar_t dir_path [ 4096 ] ;
wchar_t * res = wgetcwd ( dir_path , 4096 ) ;
if ( ! res )
{
return 0 ;
}
env_set ( L " PWD " , dir_path , ENV_EXPORT | ENV_GLOBAL ) ;
return 1 ;
2008-01-16 22:07:38 +00:00
}
2013-04-27 07:45:38 +00:00
wcstring env_get_pwd_slash ( void )
{
env_var_t pwd = env_get_string ( L " PWD " ) ;
if ( pwd . missing_or_empty ( ) )
{
return L " " ;
}
if ( ! string_suffixes_string ( L " / " , pwd ) )
{
pwd . push_back ( L ' / ' ) ;
}
return pwd ;
}
2014-10-12 22:01:35 +00:00
/* Here is the whitelist of variables that we colon-delimit, both incoming from the environment and outgoing back to it. This is deliberately very short - we don't want to add language-specific values like CLASSPATH. */
static bool variable_is_colon_delimited_array ( const wcstring & str )
2012-11-19 00:30:30 +00:00
{
2014-10-12 22:01:35 +00:00
return contains ( str , L " PATH " , L " MANPATH " , L " CDPATH " ) ;
2012-03-06 23:51:48 +00:00
}
2012-07-18 17:50:38 +00:00
void env_init ( const struct config_paths_t * paths /* or NULL */ )
2005-09-20 13:26:39 +00:00
{
2012-11-19 00:30:30 +00:00
/*
env_read_only variables can not be altered directly by the user
*/
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
const wchar_t * const ro_keys [ ] =
{
2011-12-27 03:18:46 +00:00
L " status " ,
L " history " ,
L " version " ,
L " _ " ,
L " LINES " ,
L " COLUMNS " ,
L " PWD " ,
2014-08-23 01:05:28 +00:00
//L"SHLVL", // will be inserted a bit lower down
2012-11-02 10:36:06 +00:00
L " FISH_VERSION " ,
2011-12-27 03:18:46 +00:00
} ;
2012-11-19 00:30:30 +00:00
for ( size_t i = 0 ; i < sizeof ro_keys / sizeof * ro_keys ; i + + )
{
2011-12-27 03:18:46 +00:00
env_read_only . insert ( ro_keys [ i ] ) ;
}
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
/*
Names of all dynamically calculated variables
*/
2011-12-27 03:18:46 +00:00
env_electric . insert ( L " history " ) ;
env_electric . insert ( L " status " ) ;
env_electric . insert ( L " umask " ) ;
2014-07-12 20:39:53 +00:00
env_electric . insert ( L " COLUMNS " ) ;
env_electric . insert ( L " LINES " ) ;
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
top = new env_node_t ;
global_env = top ;
global = & top - > env ;
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
/*
Now the environemnt variable handling is set up , the next step
is to insert valid data
*/
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
/*
Import environment variables
*/
2013-02-20 01:48:51 +00:00
for ( char * * p = ( environ ? environ : __environ ) ; p & & * p ; p + + )
2012-11-19 00:30:30 +00:00
{
2012-12-19 21:31:06 +00:00
const wcstring key_and_val = str2wcstring ( * p ) ; //like foo=bar
size_t eql = key_and_val . find ( L ' = ' ) ;
if ( eql = = wcstring : : npos )
2012-11-19 00:30:30 +00:00
{
2012-12-19 21:31:06 +00:00
// no equals found
2014-07-12 07:53:23 +00:00
if ( is_read_only ( key_and_val ) | | is_electric ( key_and_val ) ) continue ;
env_set ( key_and_val , L " " , ENV_EXPORT | ENV_GLOBAL ) ;
2012-11-19 00:30:30 +00:00
}
else
{
2012-12-19 21:31:06 +00:00
wcstring key = key_and_val . substr ( 0 , eql ) ;
2014-07-12 07:53:23 +00:00
if ( is_read_only ( key ) | | is_electric ( key ) ) continue ;
2012-12-19 21:31:06 +00:00
wcstring val = key_and_val . substr ( eql + 1 ) ;
2014-10-12 22:01:35 +00:00
if ( variable_is_colon_delimited_array ( key ) )
2012-11-19 00:30:30 +00:00
{
2012-12-19 21:31:06 +00:00
std : : replace ( val . begin ( ) , val . end ( ) , L ' : ' , ARRAY_SEP ) ;
2012-03-06 23:51:48 +00:00
}
2005-10-25 09:39:45 +00:00
2012-12-19 21:31:06 +00:00
env_set ( key , val . c_str ( ) , ENV_EXPORT | ENV_GLOBAL ) ;
2012-11-19 00:30:30 +00:00
}
2012-11-18 10:23:22 +00:00
}
2012-07-18 17:50:38 +00:00
/* 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 ) ;
}
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
/*
Set up the PATH variable
*/
setup_path ( ) ;
/*
Set up the USER variable
*/
2014-07-25 17:42:22 +00:00
if ( env_get_string ( L " USER " ) . missing_or_empty ( ) )
2012-11-19 00:30:30 +00:00
{
2014-07-25 17:42:22 +00:00
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 ) ;
}
2012-11-19 00:30:30 +00:00
}
/*
Set up the version variables
*/
2014-05-01 07:46:27 +00:00
wcstring version = str2wcstring ( get_fish_version ( ) ) ;
2012-12-19 21:31:06 +00:00
env_set ( L " version " , version . c_str ( ) , ENV_GLOBAL ) ;
env_set ( L " FISH_VERSION " , version . c_str ( ) , ENV_GLOBAL ) ;
2012-11-19 00:30:30 +00:00
/*
Set up SHLVL variable
*/
const env_var_t shlvl_str = env_get_string ( L " SHLVL " ) ;
2012-05-09 10:23:31 +00:00
wcstring nshlvl_str = L " 1 " ;
2012-11-19 00:30:30 +00:00
if ( ! shlvl_str . missing ( ) )
{
2014-09-20 00:31:34 +00:00
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 ' )
2012-05-09 10:23:31 +00:00
{
2013-01-19 21:16:21 +00:00
nshlvl_str = to_string < long > ( shlvl_i + 1 ) ;
2012-05-09 10:23:31 +00:00
}
2012-11-19 00:30:30 +00:00
}
env_set ( L " SHLVL " , nshlvl_str . c_str ( ) , ENV_GLOBAL | ENV_EXPORT ) ;
2014-08-23 01:05:28 +00:00
env_read_only . insert ( L " SHLVL " ) ;
2010-10-08 00:35:22 +00:00
2014-07-25 17:42:22 +00:00
/* 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 ) ;
2014-10-01 19:27:32 +00:00
env_set ( L " HOME " , dir . c_str ( ) , ENV_GLOBAL | ENV_EXPORT ) ;
2014-07-25 17:42:22 +00:00
}
free ( unam_narrow ) ;
}
/* Set PWD */
env_set_pwd ( ) ;
2012-11-18 10:23:22 +00:00
2014-10-01 19:33:30 +00:00
/* 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 ( ) ;
2012-03-09 19:56:33 +00:00
/* 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 ) ;
2012-11-18 10:23:22 +00:00
2012-08-15 07:57:56 +00:00
/* 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 ) ) ;
2014-01-19 09:27:39 +00:00
/* Set fish_bind_mode to "default" */
env_set ( FISH_BIND_MODE_VAR , DEFAULT_BIND_MODE , ENV_GLOBAL ) ;
2014-07-13 20:21:06 +00:00
/*
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 ) ;
2005-09-20 13:26:39 +00:00
}
/**
2007-01-21 15:01:14 +00:00
Search all visible scopes in order for the specified key . Return
the first scope in which it was found .
2005-09-20 13:26:39 +00:00
*/
2012-11-19 00:30:30 +00:00
static env_node_t * env_get_node ( const wcstring & key )
2005-09-20 13:26:39 +00:00
{
2012-11-19 00:30:30 +00:00
env_node_t * env = top ;
while ( env ! = NULL )
2012-11-18 10:23:22 +00:00
{
2013-02-20 01:48:51 +00:00
if ( env - > find_entry ( key ) ! = NULL )
2012-11-19 00:30:30 +00:00
{
break ;
}
2012-11-18 10:23:22 +00:00
2013-02-20 01:48:51 +00:00
env = env - > next_scope_to_search ( ) ;
2012-11-18 10:23:22 +00:00
}
2012-11-19 00:30:30 +00:00
return env ;
2005-09-20 13:26:39 +00:00
}
2006-01-08 23:00:49 +00:00
2014-07-14 00:30:48 +00:00
int env_set ( const wcstring & key , const wchar_t * val , env_mode_flags_t var_mode )
2005-09-20 13:26:39 +00:00
{
2012-03-31 22:33:34 +00:00
ASSERT_IS_MAIN_THREAD ( ) ;
2012-11-19 00:30:30 +00:00
bool has_changed_old = has_changed_exported ;
bool has_changed_new = false ;
int done = 0 ;
2013-01-21 02:34:18 +00:00
2012-11-19 00:30:30 +00:00
int is_universal = 0 ;
2013-01-21 02:34:18 +00:00
2012-11-19 00:30:30 +00:00
if ( val & & contains ( key , L " PWD " , L " HOME " ) )
{
2012-02-08 05:23:12 +00:00
/* Canoncalize our path; if it changes, recurse and try again. */
wcstring val_canonical = val ;
path_make_canonical ( val_canonical ) ;
2012-11-19 00:30:30 +00:00
if ( val ! = val_canonical )
{
return env_set ( key , val_canonical . c_str ( ) , var_mode ) ;
2012-02-08 05:23:12 +00:00
}
2012-11-19 00:30:30 +00:00
}
2013-01-21 02:34:18 +00:00
2014-07-12 21:05:42 +00:00
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 ;
}
2012-11-19 00:30:30 +00:00
if ( ( var_mode & ENV_USER ) & & is_read_only ( key ) )
2012-11-18 10:23:22 +00:00
{
2012-11-19 00:30:30 +00:00
return ENV_PERM ;
2012-11-18 10:23:22 +00:00
}
2013-01-21 02:34:18 +00:00
2012-11-19 00:30:30 +00:00
if ( key = = L " umask " )
{
wchar_t * end ;
2013-01-21 02:34:18 +00:00
2012-11-19 00:30:30 +00:00
/*
2013-01-19 21:16:21 +00:00
Set the new umask
*/
2012-11-19 00:30:30 +00:00
if ( val & & wcslen ( val ) )
{
errno = 0 ;
long mask = wcstol ( val , & end , 8 ) ;
2013-01-21 02:34:18 +00:00
2012-11-19 00:30:30 +00:00
if ( ! errno & & ( ! * end ) & & ( mask < = 0777 ) & & ( mask > = 0 ) )
{
umask ( mask ) ;
2014-07-14 02:08:15 +00:00
/* Do not actually create a umask variable, on env_get, it will be calculated dynamically */
return 0 ;
2012-11-19 00:30:30 +00:00
}
}
2014-07-14 02:08:15 +00:00
return ENV_INVALID ;
2012-11-18 10:23:22 +00:00
}
2013-01-21 02:34:18 +00:00
2012-11-19 00:30:30 +00:00
/*
2013-01-19 21:16:21 +00:00
Zero element arrays are internaly not coded as null but as this
placeholder string
*/
2012-11-19 00:30:30 +00:00
if ( ! val )
2012-11-18 10:23:22 +00:00
{
2012-11-19 00:30:30 +00:00
val = ENV_NULL ;
2012-11-18 10:23:22 +00:00
}
2013-01-21 02:34:18 +00:00
2012-11-19 00:30:30 +00:00
if ( var_mode & ENV_UNIVERSAL )
2012-11-18 10:23:22 +00:00
{
2014-06-16 00:30:50 +00:00
const bool old_export = uvars ( ) & & uvars ( ) - > get_export ( key ) ;
bool new_export ;
2013-01-19 21:16:21 +00:00
if ( var_mode & ENV_EXPORT )
2012-11-19 00:30:30 +00:00
{
2013-01-19 21:16:21 +00:00
// export
2014-06-16 00:30:50 +00:00
new_export = true ;
2013-01-19 21:16:21 +00:00
}
else if ( var_mode & ENV_UNEXPORT )
{
// unexport
2014-06-16 00:30:50 +00:00
new_export = false ;
2012-11-19 00:30:30 +00:00
}
else
{
2013-01-19 21:16:21 +00:00
// not changing the export
2014-06-16 00:30:50 +00:00
new_export = old_export ;
}
if ( uvars ( ) )
{
uvars ( ) - > set ( key , val , new_export ) ;
env_universal_barrier ( ) ;
if ( old_export | | new_export )
{
mark_changed_exported ( ) ;
}
2012-11-19 00:30:30 +00:00
}
is_universal = 1 ;
2013-01-21 02:34:18 +00:00
2012-11-18 10:23:22 +00:00
}
else
{
2013-01-19 21:16:21 +00:00
// Determine the node
2013-01-21 02:34:18 +00:00
2013-01-19 21:16:21 +00:00
env_node_t * preexisting_node = env_get_node ( key ) ;
bool preexisting_entry_exportv = false ;
if ( preexisting_node ! = NULL )
2012-11-18 10:23:22 +00:00
{
2013-01-19 21:16:21 +00:00
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 )
2012-11-19 00:30:30 +00:00
{
2013-01-19 21:16:21 +00:00
preexisting_entry_exportv = true ;
2012-11-19 00:30:30 +00:00
has_changed_new = true ;
}
2012-11-18 10:23:22 +00:00
}
2013-01-21 02:34:18 +00:00
2013-01-19 21:16:21 +00:00
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 )
2012-11-18 10:23:22 +00:00
{
2013-01-19 21:16:21 +00:00
node = preexisting_node ;
2013-01-21 02:34:18 +00:00
2013-01-19 21:16:21 +00:00
if ( ( var_mode & ( ENV_EXPORT | ENV_UNEXPORT ) ) = = 0 )
{
// use existing entry's exportv
var_mode = preexisting_entry_exportv ? ENV_EXPORT : 0 ;
}
2012-11-18 10:23:22 +00:00
}
2012-11-19 00:30:30 +00:00
else
2012-11-18 10:23:22 +00:00
{
2013-01-19 21:16:21 +00:00
if ( ! get_proc_had_barrier ( ) )
2012-11-19 00:30:30 +00:00
{
2013-01-19 21:16:21 +00:00
set_proc_had_barrier ( true ) ;
env_universal_barrier ( ) ;
2012-11-19 00:30:30 +00:00
}
2013-01-21 02:34:18 +00:00
2014-06-16 00:30:50 +00:00
if ( uvars ( ) & & ! uvars ( ) - > get ( key ) . missing ( ) )
2012-11-19 00:30:30 +00:00
{
2013-01-19 21:16:21 +00:00
bool exportv ;
if ( var_mode & ENV_EXPORT )
2012-11-19 00:30:30 +00:00
{
2013-01-19 21:16:21 +00:00
exportv = true ;
2012-11-19 00:30:30 +00:00
}
2013-01-19 21:16:21 +00:00
else if ( var_mode & ENV_UNEXPORT )
2012-11-19 00:30:30 +00:00
{
2013-01-19 21:16:21 +00:00
exportv = false ;
2012-11-19 00:30:30 +00:00
}
else
{
2014-06-16 00:30:50 +00:00
exportv = uvars ( ) - > get_export ( key ) ;
2013-01-19 21:16:21 +00:00
}
2013-01-21 02:34:18 +00:00
2014-06-16 00:30:50 +00:00
uvars ( ) - > set ( key , val , exportv ) ;
env_universal_barrier ( ) ;
2013-01-19 21:16:21 +00:00
is_universal = 1 ;
2013-01-21 02:34:18 +00:00
2013-01-19 21:16:21 +00:00
done = 1 ;
2013-01-21 02:34:18 +00:00
2013-01-19 21:16:21 +00:00
}
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 ;
2012-11-19 00:30:30 +00:00
}
}
2012-11-18 10:23:22 +00:00
}
2013-01-21 02:34:18 +00:00
2012-11-19 00:30:30 +00:00
if ( ! done )
2012-11-18 10:23:22 +00:00
{
2013-01-19 21:16:21 +00:00
// 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 )
2012-11-19 00:30:30 +00:00
{
2013-01-19 21:16:21 +00:00
// this variable already existed, and was exported
has_changed_new = true ;
2012-11-19 00:30:30 +00:00
}
2013-01-19 21:16:21 +00:00
entry . val = val ;
if ( var_mode & ENV_EXPORT )
2012-02-17 20:23:30 +00:00
{
2013-01-19 21:16:21 +00:00
// the new variable is exported
entry . exportv = true ;
node - > exportv = true ;
has_changed_new = true ;
2012-11-18 10:23:22 +00:00
}
2012-11-19 00:30:30 +00:00
else
2012-02-17 20:23:30 +00:00
{
2013-01-19 21:16:21 +00:00
entry . exportv = false ;
2012-02-17 20:23:30 +00:00
}
2013-01-21 02:34:18 +00:00
2012-08-15 07:57:56 +00:00
if ( has_changed_old | | has_changed_new )
mark_changed_exported ( ) ;
2012-02-17 20:23:30 +00:00
}
}
2013-01-21 02:34:18 +00:00
2012-11-19 00:30:30 +00:00
if ( ! is_universal )
2012-02-17 20:23:30 +00:00
{
2012-11-18 10:23:22 +00:00
event_t ev = event_t : : variable_event ( key ) ;
2014-01-07 22:57:58 +00:00
ev . arguments . reserve ( 3 ) ;
2012-12-20 09:52:44 +00:00
ev . arguments . push_back ( L " VARIABLE " ) ;
ev . arguments . push_back ( L " SET " ) ;
ev . arguments . push_back ( key ) ;
2013-01-21 02:34:18 +00:00
2012-11-18 10:23:22 +00:00
// debug( 1, L"env_set: fire events on variable %ls", key );
2012-11-19 00:30:30 +00:00
event_fire ( & ev ) ;
2012-11-18 10:23:22 +00:00
// debug( 1, L"env_set: return from event firing" );
2012-02-17 20:23:30 +00:00
}
2013-01-21 02:34:18 +00:00
2012-03-05 22:18:16 +00:00
react_to_variable_change ( key ) ;
2013-01-21 02:34:18 +00:00
2012-02-17 20:23:30 +00:00
return 0 ;
}
2005-09-20 13:26:39 +00:00
2006-01-08 23:00:49 +00:00
2005-09-20 13:26:39 +00:00
/**
Attempt to remove / free the specified key / value pair from the
2012-02-18 15:34:09 +00:00
specified map .
2006-05-29 13:12:17 +00:00
\ return zero if the variable was not found , non - zero otherwise
2005-09-20 13:26:39 +00:00
*/
2013-01-19 21:16:21 +00:00
static bool try_remove ( env_node_t * n , const wchar_t * key , int var_mode )
2005-09-20 13:26:39 +00:00
{
2013-01-19 21:16:21 +00:00
if ( n = = NULL )
2012-11-19 00:30:30 +00:00
{
2013-01-19 21:16:21 +00:00
return false ;
2012-11-19 00:30:30 +00:00
}
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
var_table_t : : iterator result = n - > env . find ( key ) ;
if ( result ! = n - > env . end ( ) )
2012-11-18 10:23:22 +00:00
{
2013-01-19 21:16:21 +00:00
if ( result - > second . exportv )
2012-11-19 00:30:30 +00:00
{
mark_changed_exported ( ) ;
}
n - > env . erase ( result ) ;
2013-01-19 21:16:21 +00:00
return true ;
2012-11-18 10:23:22 +00:00
}
2012-11-19 00:30:30 +00:00
if ( var_mode & ENV_LOCAL )
{
2013-01-19 21:16:21 +00:00
return false ;
2012-11-19 00:30:30 +00:00
}
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
if ( n - > new_scope )
{
return try_remove ( global_env , key , var_mode ) ;
}
else
{
return try_remove ( n - > next , key , var_mode ) ;
}
2005-09-20 13:26:39 +00:00
}
2012-11-19 00:30:30 +00:00
int env_remove ( const wcstring & key , int var_mode )
2005-09-20 13:26:39 +00:00
{
2012-03-05 22:18:16 +00:00
ASSERT_IS_MAIN_THREAD ( ) ;
2012-11-19 00:30:30 +00:00
env_node_t * first_node ;
int erased = 0 ;
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
if ( ( var_mode & ENV_USER ) & & is_read_only ( key ) )
2012-11-18 10:23:22 +00:00
{
2012-11-19 00:30:30 +00:00
return 2 ;
2012-11-18 10:23:22 +00:00
}
2012-11-19 00:30:30 +00:00
first_node = top ;
if ( ! ( var_mode & ENV_UNIVERSAL ) )
2012-11-18 10:23:22 +00:00
{
2012-11-19 00:30:30 +00:00
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 ) ;
2012-12-20 09:52:44 +00:00
ev . arguments . push_back ( L " VARIABLE " ) ;
ev . arguments . push_back ( L " ERASE " ) ;
ev . arguments . push_back ( key ) ;
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
event_fire ( & ev ) ;
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
erased = 1 ;
}
2012-11-18 10:23:22 +00:00
}
2012-11-19 00:30:30 +00:00
if ( ! erased & &
! ( var_mode & ENV_GLOBAL ) & &
! ( var_mode & ENV_LOCAL ) )
{
2014-06-16 00:30:50 +00:00
erased = uvars ( ) & & uvars ( ) - > remove ( key ) ;
2014-07-07 01:04:30 +00:00
if ( erased )
{
env_universal_barrier ( ) ;
}
2012-11-19 00:30:30 +00:00
}
2006-01-08 23:00:49 +00:00
2012-03-05 22:18:16 +00:00
react_to_variable_change ( key ) ;
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
return ! erased ;
2005-09-20 13:26:39 +00:00
}
2012-11-19 00:30:30 +00:00
const wchar_t * env_var_t : : c_str ( void ) const
{
2012-01-14 09:06:47 +00:00
assert ( ! is_missing ) ;
return wcstring : : c_str ( ) ;
}
2014-07-14 00:30:48 +00:00
env_var_t env_get_string ( const wcstring & key , env_mode_flags_t mode )
2012-11-18 10:23:22 +00:00
{
2014-07-12 21:40:46 +00:00
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 ) )
2012-11-19 00:30:30 +00:00
{
2014-07-12 21:40:46 +00:00
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 ;
2012-11-18 10:23:22 +00:00
2014-07-12 21:40:46 +00:00
history_t * history = reader_get_history ( ) ;
if ( ! history )
{
history = & history_t : : history_with_name ( L " fish " ) ;
}
if ( history )
2014-07-29 21:41:21 +00:00
history - > get_string_representation ( & result , ARRAY_SEP_STR ) ;
2014-07-12 21:40:46 +00:00
return result ;
}
else if ( key = = L " COLUMNS " )
2012-11-19 00:30:30 +00:00
{
2014-07-12 21:40:46 +00:00
return to_string ( common_get_width ( ) ) ;
2012-03-19 18:52:18 +00:00
}
2014-07-12 21:40:46 +00:00
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 " )
2012-03-31 22:33:34 +00:00
{
2014-07-12 21:40:46 +00:00
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 ) ;
2012-11-18 10:23:22 +00:00
2014-07-12 21:40:46 +00:00
env_node_t * env = search_local ? top : global_env ;
2012-11-18 10:23:22 +00:00
2014-07-12 21:40:46 +00:00
while ( env ! = NULL )
{
const var_entry_t * entry = env - > find_entry ( key ) ;
if ( entry ! = NULL & & ( entry - > exportv ? search_exported : search_unexported ) )
2012-03-31 22:33:34 +00:00
{
2014-07-12 21:40:46 +00:00
if ( entry - > val = = ENV_NULL )
{
return env_var_t : : missing_var ( ) ;
}
else
2012-03-31 22:33:34 +00:00
{
2014-07-12 21:40:46 +00:00
return entry - > val ;
2012-04-23 18:08:29 +00:00
}
2014-07-12 21:40:46 +00:00
}
2012-11-18 10:23:22 +00:00
2014-07-12 21:40:46 +00:00
if ( has_scope )
{
if ( ! search_global | | env = = global_env ) break ;
env = global_env ;
}
else
{
2013-02-20 01:48:51 +00:00
env = env - > next_scope_to_search ( ) ;
2012-03-31 22:33:34 +00:00
}
}
2014-07-12 21:40:46 +00:00
}
2012-11-18 10:23:22 +00:00
2014-07-12 21:40:46 +00:00
if ( ! search_universal ) return env_var_t : : missing_var ( ) ;
2012-11-18 10:23:22 +00:00
2014-07-12 21:40:46 +00:00
/* 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 ) )
2011-12-27 03:18:46 +00:00
{
2014-06-16 00:30:50 +00:00
env_var = env_var_t : : missing_var ( ) ;
2011-12-27 03:18:46 +00:00
}
2014-06-16 00:30:50 +00:00
return env_var ;
2011-12-27 03:18:46 +00:00
}
2014-07-12 21:40:46 +00:00
return env_var_t : : missing_var ( ) ;
2011-12-27 03:18:46 +00:00
}
2005-09-20 13:26:39 +00:00
2014-07-14 00:30:48 +00:00
bool env_exist ( const wchar_t * key , env_mode_flags_t mode )
2005-09-26 14:47:03 +00:00
{
2012-11-19 00:30:30 +00:00
env_node_t * env ;
2012-11-18 10:23:22 +00:00
2013-01-19 21:16:21 +00:00
CHECK ( key , false ) ;
2012-11-18 10:23:22 +00:00
2014-07-12 08:15:52 +00:00
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 ) )
2012-11-18 10:23:22 +00:00
{
2014-07-12 08:15:52 +00:00
/*
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 )
2012-11-19 00:30:30 +00:00
{
2013-01-19 21:16:21 +00:00
return true ;
2012-11-19 00:30:30 +00:00
}
2014-07-12 08:15:52 +00:00
return false ;
2012-11-18 10:23:22 +00:00
}
2012-06-18 17:20:40 +00:00
2014-07-12 08:15:52 +00:00
if ( test_local | | test_global )
2012-11-18 10:23:22 +00:00
{
2014-07-12 08:15:52 +00:00
env = test_local ? top : global_env ;
2012-11-18 10:23:22 +00:00
2014-07-12 08:15:52 +00:00
while ( env )
2012-11-19 00:30:30 +00:00
{
var_table_t : : iterator result = env - > env . find ( key ) ;
if ( result ! = env - > env . end ( ) )
{
2013-01-19 21:16:21 +00:00
const var_entry_t & res = result - > second ;
2014-07-12 08:15:52 +00:00
return res . exportv ? test_exported : test_unexported ;
2012-11-19 00:30:30 +00:00
}
2012-11-18 10:23:22 +00:00
2014-07-12 08:15:52 +00:00
if ( has_scope )
{
if ( ! test_global | | env = = global_env ) break ;
env = global_env ;
}
else
{
env = env - > next_scope_to_search ( ) ;
}
2012-11-19 00:30:30 +00:00
}
2012-11-18 10:23:22 +00:00
}
2012-06-18 17:20:40 +00:00
2014-07-12 08:15:52 +00:00
if ( test_universal )
2012-11-18 10:23:22 +00:00
{
2012-11-19 00:30:30 +00:00
if ( ! get_proc_had_barrier ( ) )
{
set_proc_had_barrier ( true ) ;
env_universal_barrier ( ) ;
}
2012-11-18 10:23:22 +00:00
2014-06-16 00:30:50 +00:00
if ( uvars ( ) & & ! uvars ( ) - > get ( key ) . missing ( ) )
2012-06-18 17:20:40 +00:00
{
2014-07-12 08:15:52 +00:00
return uvars ( ) - > get_export ( key ) ? test_exported : test_unexported ;
2012-06-18 17:20:40 +00:00
}
2012-11-19 00:30:30 +00:00
}
2006-06-04 20:14:51 +00:00
2012-11-19 00:30:30 +00:00
return 0 ;
2005-09-20 13:26:39 +00:00
}
2005-10-24 15:26:25 +00:00
/**
Returns true if the specified scope or any non - shadowed non - global subscopes contain an exported variable .
*/
2012-11-19 00:30:30 +00:00
static int local_scope_exports ( env_node_t * n )
2005-09-20 13:26:39 +00:00
{
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
if ( n = = global_env )
return 0 ;
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
if ( n - > exportv )
return 1 ;
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
if ( n - > new_scope )
return 0 ;
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
return local_scope_exports ( n - > next ) ;
2005-09-20 13:26:39 +00:00
}
2013-01-19 21:21:55 +00:00
void env_push ( bool new_scope )
2005-09-20 13:26:39 +00:00
{
2012-11-19 00:30:30 +00:00
env_node_t * node = new env_node_t ;
node - > next = top ;
node - > new_scope = new_scope ;
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
if ( new_scope )
{
2012-08-15 07:57:56 +00:00
if ( local_scope_exports ( top ) )
mark_changed_exported ( ) ;
2012-11-19 00:30:30 +00:00
}
top = node ;
2005-09-20 13:26:39 +00:00
}
void env_pop ( )
{
2012-11-19 00:30:30 +00:00
if ( & top - > env ! = global )
{
int i ;
int locale_changed = 0 ;
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
env_node_t * killme = top ;
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
for ( i = 0 ; locale_variable [ i ] ; i + + )
{
var_table_t : : iterator result = killme - > env . find ( locale_variable [ i ] ) ;
if ( result ! = killme - > env . end ( ) )
{
locale_changed = 1 ;
break ;
}
}
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
if ( killme - > new_scope )
{
if ( killme - > exportv | | local_scope_exports ( killme - > next ) )
2012-08-15 07:57:56 +00:00
mark_changed_exported ( ) ;
2012-11-19 00:30:30 +00:00
}
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
top = top - > next ;
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
var_table_t : : iterator iter ;
for ( iter = killme - > env . begin ( ) ; iter ! = killme - > env . end ( ) ; + + iter )
{
2013-01-19 21:16:21 +00:00
const var_entry_t & entry = iter - > second ;
if ( entry . exportv )
2012-11-19 00:30:30 +00:00
{
mark_changed_exported ( ) ;
2013-01-19 21:16:21 +00:00
break ;
2012-11-19 00:30:30 +00:00
}
}
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
delete killme ;
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
if ( locale_changed )
handle_locale ( ) ;
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
}
else
{
debug ( 0 ,
_ ( L " Tried to pop empty environment stack. " ) ) ;
sanity_lose ( ) ;
}
2005-09-20 13:26:39 +00:00
}
2012-01-10 20:51:09 +00:00
/**
2012-02-18 15:34:09 +00:00
Function used with to insert keys of one table into a set : : set < wcstring >
2012-01-10 20:51:09 +00:00
*/
2013-01-19 21:16:21 +00:00
static void add_key_to_string_set ( const var_table_t & envs , std : : set < wcstring > * str_set , bool show_exported , bool show_unexported )
2012-02-17 18:52:30 +00:00
{
2012-11-19 00:30:30 +00:00
var_table_t : : const_iterator iter ;
for ( iter = envs . begin ( ) ; iter ! = envs . end ( ) ; + + iter )
2012-11-18 10:23:22 +00:00
{
2013-01-19 21:16:21 +00:00
const var_entry_t & e = iter - > second ;
2012-11-18 10:23:22 +00:00
2013-01-19 21:16:21 +00:00
if ( ( e . exportv & & show_exported ) | |
( ! e . exportv & & show_unexported ) )
2012-11-19 00:30:30 +00:00
{
2013-01-19 21:16:21 +00:00
/* Insert this key */
str_set - > insert ( iter - > first ) ;
2012-11-19 00:30:30 +00:00
}
}
2012-02-17 18:52:30 +00:00
}
2012-11-19 00:30:30 +00:00
wcstring_list_t env_get_names ( int flags )
2012-01-10 20:51:09 +00:00
{
2012-02-26 02:54:49 +00:00
scoped_lock lock ( env_lock ) ;
2012-11-18 10:23:22 +00:00
2012-01-10 20:51:09 +00:00
wcstring_list_t result ;
std : : set < wcstring > names ;
int show_local = flags & ENV_LOCAL ;
2012-11-19 00:30:30 +00:00
int show_global = flags & ENV_GLOBAL ;
int show_universal = flags & ENV_UNIVERSAL ;
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
env_node_t * n = top ;
2013-01-19 21:16:21 +00:00
const bool show_exported = ( flags & ENV_EXPORT ) | | ! ( flags & ENV_UNEXPORT ) ;
const bool show_unexported = ( flags & ENV_UNEXPORT ) | | ! ( flags & ENV_EXPORT ) ;
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
if ( ! show_local & & ! show_global & & ! show_universal )
{
show_local = show_universal = show_global = 1 ;
}
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
if ( show_local )
2012-11-18 10:23:22 +00:00
{
2012-11-19 00:30:30 +00:00
while ( n )
{
if ( n = = global_env )
break ;
2012-11-18 10:23:22 +00:00
2013-01-19 21:16:21 +00:00
add_key_to_string_set ( n - > env , & names , show_exported , show_unexported ) ;
2012-11-19 00:30:30 +00:00
if ( n - > new_scope )
break ;
else
n = n - > next ;
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
}
2012-11-18 10:23:22 +00:00
}
2012-11-19 00:30:30 +00:00
if ( show_global )
{
2013-01-19 21:16:21 +00:00
add_key_to_string_set ( global_env - > env , & names , show_exported , show_unexported ) ;
if ( show_unexported )
2012-11-19 00:30:30 +00:00
{
2012-01-10 20:51:09 +00:00
result . insert ( result . end ( ) , env_electric . begin ( ) , env_electric . end ( ) ) ;
}
2012-11-19 00:30:30 +00:00
}
2012-11-18 10:23:22 +00:00
2014-06-16 00:30:50 +00:00
if ( show_universal & & uvars ( ) )
2012-11-19 00:30:30 +00:00
{
2014-06-16 00:30:50 +00:00
const wcstring_list_t uni_list = uvars ( ) - > get_names ( show_exported , show_unexported ) ;
2012-01-10 20:51:09 +00:00
names . insert ( uni_list . begin ( ) , uni_list . end ( ) ) ;
2012-11-19 00:30:30 +00:00
}
2012-11-18 10:23:22 +00:00
2012-01-10 20:51:09 +00:00
result . insert ( result . end ( ) , names . begin ( ) , names . end ( ) ) ;
return result ;
}
2005-10-03 13:24:46 +00:00
/**
2012-11-18 10:23:22 +00:00
Get list of all exported variables
2005-10-03 13:24:46 +00:00
*/
2006-11-26 13:09:43 +00:00
2012-11-19 00:30:30 +00:00
static void get_exported ( const env_node_t * n , std : : map < wcstring , wcstring > & h )
2006-11-26 13:09:43 +00:00
{
2012-11-19 00:30:30 +00:00
if ( ! n )
return ;
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
if ( n - > new_scope )
get_exported ( global_env , h ) ;
else
get_exported ( n - > next , h ) ;
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
var_table_t : : const_iterator iter ;
for ( iter = n - > env . begin ( ) ; iter ! = n - > env . end ( ) ; + + iter )
2012-11-18 10:23:22 +00:00
{
2012-11-19 00:30:30 +00:00
const wcstring & key = iter - > first ;
2013-01-19 21:16:21 +00:00
const var_entry_t & val_entry = iter - > second ;
if ( val_entry . exportv & & ( val_entry . val ! = ENV_NULL ) )
2012-11-19 00:30:30 +00:00
{
2012-06-16 20:05:58 +00:00
// Don't use std::map::insert here, since we need to overwrite existing values from previous scopes
2013-01-19 21:16:21 +00:00
h [ key ] = val_entry . val ;
2012-11-19 00:30:30 +00:00
}
2012-11-18 10:23:22 +00:00
}
2012-02-17 18:52:30 +00:00
}
2006-11-26 13:09:43 +00:00
2014-10-12 22:01:35 +00:00
/* Given a map from key to value, add values to out of the form key=value */
2012-02-28 23:11:46 +00:00
static void export_func ( const std : : map < wcstring , wcstring > & envs , std : : vector < std : : string > & out )
2005-09-22 20:16:52 +00:00
{
2014-10-12 22:01:35 +00:00
out . reserve ( out . size ( ) + envs . size ( ) ) ;
2012-11-19 00:30:30 +00:00
std : : map < wcstring , wcstring > : : const_iterator iter ;
for ( iter = envs . begin ( ) ; iter ! = envs . end ( ) ; + + iter )
2012-11-18 10:23:22 +00:00
{
2014-10-12 22:01:35 +00:00
const wcstring & key = iter - > first ;
const std : : string & ks = wcs2string ( key ) ;
2013-02-12 12:50:43 +00:00
std : : string vs = wcs2string ( iter - > second ) ;
2013-02-14 23:50:24 +00:00
2014-10-12 22:01:35 +00:00
/* Arrays in the value are ASCII record separator (0x1e) delimited. But some variables should have colons. Add those. */
if ( variable_is_colon_delimited_array ( key ) )
2012-11-19 00:30:30 +00:00
{
2014-10-12 22:01:35 +00:00
/* Replace ARRAY_SEP with colon */
std : : replace ( vs . begin ( ) , vs . end ( ) , ( char ) ARRAY_SEP , ' : ' ) ;
2012-11-19 00:30:30 +00:00
}
2012-11-18 10:23:22 +00:00
2012-02-28 23:11:46 +00:00
/* Put a string on the vector */
out . push_back ( std : : string ( ) ) ;
std : : string & str = out . back ( ) ;
2013-02-12 08:14:27 +00:00
str . reserve ( ks . size ( ) + 1 + vs . size ( ) ) ;
2012-11-18 10:23:22 +00:00
2012-02-28 23:11:46 +00:00
/* Append our environment variable data to it */
str . append ( ks ) ;
str . append ( " = " ) ;
str . append ( vs ) ;
2012-11-19 00:30:30 +00:00
}
2005-09-22 20:16:52 +00:00
}
2012-11-19 00:30:30 +00:00
static void update_export_array_if_necessary ( bool recalc )
{
2012-02-28 23:11:46 +00:00
ASSERT_IS_MAIN_THREAD ( ) ;
2012-11-19 00:30:30 +00:00
if ( recalc & & ! get_proc_had_barrier ( ) )
{
set_proc_had_barrier ( true ) ;
env_universal_barrier ( ) ;
}
if ( has_changed_exported )
{
std : : map < wcstring , wcstring > vals ;
size_t i ;
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
debug ( 4 , L " env_export_arr() recalc " ) ;
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
get_exported ( top , vals ) ;
2012-11-18 10:23:22 +00:00
2014-06-16 00:30:50 +00:00
if ( uvars ( ) )
2012-11-19 00:30:30 +00:00
{
2014-06-16 00:30:50 +00:00
const wcstring_list_t uni = uvars ( ) - > get_names ( true , false ) ;
for ( i = 0 ; i < uni . size ( ) ; i + + )
2012-11-19 00:30:30 +00:00
{
2014-06-16 00:30:50 +00:00
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 ) ) ;
}
2012-11-19 00:30:30 +00:00
}
}
2012-11-18 10:23:22 +00:00
2012-02-28 23:11:46 +00:00
std : : vector < std : : string > local_export_buffer ;
2012-11-19 00:30:30 +00:00
export_func ( vals , local_export_buffer ) ;
2012-02-28 23:11:46 +00:00
export_array . set ( local_export_buffer ) ;
2012-11-19 00:30:30 +00:00
has_changed_exported = false ;
}
2005-09-22 20:16:52 +00:00
2012-02-28 23:11:46 +00:00
}
2005-09-22 20:16:52 +00:00
2013-02-23 00:22:56 +00:00
const char * const * env_export_arr ( bool recalc )
2012-02-28 23:11:46 +00:00
{
ASSERT_IS_MAIN_THREAD ( ) ;
2012-08-15 07:57:56 +00:00
update_export_array_if_necessary ( recalc ) ;
2012-02-28 23:11:46 +00:00
return export_array . get ( ) ;
}
2012-07-21 03:39:31 +00:00
env_vars_snapshot_t : : env_vars_snapshot_t ( const wchar_t * const * keys )
2011-12-27 03:18:46 +00:00
{
ASSERT_IS_MAIN_THREAD ( ) ;
2012-07-21 05:11:05 +00:00
wcstring key ;
2012-11-19 00:30:30 +00:00
for ( size_t i = 0 ; keys [ i ] ; i + + )
{
2012-07-21 05:11:05 +00:00
key . assign ( keys [ i ] ) ;
const env_var_t val = env_get_string ( key ) ;
2012-11-19 00:30:30 +00:00
if ( ! val . missing ( ) )
{
2012-07-21 05:11:05 +00:00
vars [ key ] = val ;
2011-12-27 03:18:46 +00:00
}
}
}
2014-06-16 00:30:50 +00:00
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 ( ) , data . val . c_str ( ) ) ;
}
}
}
2012-07-21 03:39:31 +00:00
env_vars_snapshot_t : : env_vars_snapshot_t ( ) { }
2011-12-27 03:18:46 +00:00
2012-07-21 05:11:05 +00:00
/* The "current" variables are not a snapshot at all, but instead trampoline to env_get_string, etc. We identify the current snapshot based on pointer values. */
static const env_vars_snapshot_t sCurrentSnapshot ;
const env_vars_snapshot_t & env_vars_snapshot_t : : current ( )
2011-12-27 03:18:46 +00:00
{
2012-07-21 05:11:05 +00:00
return sCurrentSnapshot ;
2011-12-27 03:18:46 +00:00
}
2012-07-21 05:11:05 +00:00
bool env_vars_snapshot_t : : is_current ( ) const
2012-07-21 03:39:31 +00:00
{
2012-07-21 05:11:05 +00:00
return this = = & sCurrentSnapshot ;
2012-07-21 03:39:31 +00:00
}
2012-07-21 05:11:05 +00:00
env_var_t env_vars_snapshot_t : : get ( const wcstring & key ) const
{
/* If we represent the current state, bounce to env_get_string */
if ( this - > is_current ( ) )
{
return env_get_string ( key ) ;
}
2012-11-19 00:30:30 +00:00
else
{
2012-07-21 05:11:05 +00:00
std : : map < wcstring , wcstring > : : const_iterator iter = vars . find ( key ) ;
return ( iter = = vars . end ( ) ? env_var_t : : missing_var ( ) : env_var_t ( iter - > second ) ) ;
}
}
2012-05-06 21:53:19 +00:00
2012-07-21 05:11:05 +00:00
const wchar_t * const env_vars_snapshot_t : : highlighting_keys [ ] = { L " PATH " , L " CDPATH " , L " fish_function_path " , NULL } ;