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"
# include "env_universal.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"
2005-09-20 13:26:39 +00:00
2012-12-28 04:26:16 +00:00
/** Command used to start fishd */
2012-07-11 03:30:54 +00:00
# define FISHD_CMD L"fishd ^ / tmp / fishd.log.%s"
2005-09-20 13:26:39 +00:00
2012-12-28 04:26:16 +00:00
// Version for easier debugging
//#define FISHD_CMD L"fishd"
/** 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
/**
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-09-20 13:26:39 +00:00
/**
When fishd isn ' t started , this function is provided to
2005-10-03 13:09:37 +00:00
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 .
2005-09-20 13:26:39 +00:00
*/
static void start_fishd ( )
{
2012-11-19 00:30:30 +00:00
struct passwd * pw = getpwuid ( getuid ( ) ) ;
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
debug ( 3 , L " Spawning new copy of fishd " ) ;
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
if ( ! pw )
{
debug ( 0 , _ ( L " Could not get user information " ) ) ;
return ;
}
2012-11-18 10:23:22 +00:00
2012-07-18 17:50:38 +00:00
wcstring cmd = format_string ( FISHD_CMD , pw - > pw_name ) ;
2012-11-18 10:23:22 +00:00
2012-07-18 17:50:38 +00:00
/* 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 " / " ) ;
}
}
2012-01-23 05:57:30 +00:00
parser_t & parser = parser_t : : principal_parser ( ) ;
2012-11-19 00:30:30 +00:00
parser . eval ( cmd , io_chain_t ( ) , TOP ) ;
2005-09-20 13:26:39 +00:00
}
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 ( ) )
{
debug ( 0 , _ ( L " Changing language to English " ) ) ;
}
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
}
else if ( key = = L " fish_term256 " )
{
2012-03-05 22:18:16 +00:00
update_fish_term256 ( ) ;
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 .
*/
2012-11-19 00:30:30 +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 ;
}
2012-11-18 10:23:22 +00:00
2012-11-19 08:31:03 +00:00
case ERASE :
{
str = L " ERASE " ;
break ;
}
2012-11-18 10:23:22 +00:00
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 ;
}
2008-01-13 16:47:47 +00:00
/**
Set up default values for various variables if not defined .
*/
2006-10-19 11:38:44 +00:00
static void env_set_defaults ( )
{
2007-01-21 15:01:14 +00:00
2012-11-19 00:30:30 +00:00
if ( env_get_string ( L " USER " ) . missing ( ) )
{
struct passwd * pw = getpwuid ( getuid ( ) ) ;
2012-12-19 21:31:06 +00:00
if ( pw - > pw_name ! = NULL )
{
const wcstring wide_name = str2wcstring ( pw - > pw_name ) ;
2014-01-12 21:42:26 +00:00
env_set ( L " USER " , wide_name . c_str ( ) , ENV_GLOBAL ) ;
2012-12-19 21:31:06 +00:00
}
2012-11-19 00:30:30 +00:00
}
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 ) ;
2012-12-19 21:31:06 +00:00
if ( pw - > pw_dir ! = NULL )
{
const wcstring dir = str2wcstring ( pw - > pw_dir ) ;
env_set ( L " HOME " , dir . c_str ( ) , ENV_GLOBAL ) ;
}
2012-11-19 00:30:30 +00:00
free ( unam_narrow ) ;
}
env_set_pwd ( ) ;
2012-11-18 10:23:22 +00:00
2006-10-19 11:38:44 +00:00
}
2012-03-06 23:51:48 +00:00
// Some variables should not be arrays. This used to be handled by a startup script, but we'd like to get down to 0 forks for startup, so handle it here.
2012-12-19 21:31:06 +00:00
static bool variable_can_be_array ( const wcstring & key )
2012-11-19 00:30:30 +00:00
{
2012-12-19 21:31:06 +00:00
if ( key = = L " DISPLAY " )
2012-11-19 00:30:30 +00:00
{
2012-03-06 23:51:48 +00:00
return false ;
2012-11-19 00:30:30 +00:00
}
else
{
2012-03-06 23:51:48 +00:00
return true ;
}
}
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 " ,
L " SHLVL " ,
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
/*
HOME and USER should be writeable by root , since this can be a
convenient way to install software .
*/
if ( getuid ( ) ! = 0 )
{
2011-12-27 03:18:46 +00:00
env_read_only . insert ( L " HOME " ) ;
env_read_only . insert ( L " USER " ) ;
2012-11-19 00:30:30 +00:00
}
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 " ) ;
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
env_set ( key_and_val , L " " , ENV_EXPORT ) ;
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 ) ;
wcstring val = key_and_val . substr ( eql + 1 ) ;
2012-11-19 00:30:30 +00:00
if ( variable_can_be_array ( val ) )
{
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
*/
2012-12-19 21:31:06 +00:00
const struct passwd * pw = getpwuid ( getuid ( ) ) ;
if ( pw & & pw - > pw_name )
2012-11-19 00:30:30 +00:00
{
2012-12-19 21:31:06 +00:00
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
*/
2013-06-24 10:12:09 +00:00
wcstring version = str2wcstring ( FISH_BUILD_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
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 " ) ;
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 ( ) )
{
2012-05-09 10:23:31 +00:00
long shlvl_i = wcstol ( shlvl_str . c_str ( ) , NULL , 10 ) ;
if ( shlvl_i > = 0 )
{
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 ) ;
2010-10-08 00:35:22 +00:00
2012-11-19 00:30:30 +00:00
/* Set correct defaults for e.g. USER and HOME variables */
env_set_defaults ( ) ;
2012-11-18 10:23:22 +00:00
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 ) ;
2005-09-20 13:26:39 +00:00
}
void env_destroy ( )
{
2012-11-19 00:30:30 +00:00
env_universal_destroy ( ) ;
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
while ( & top - > env ! = global )
{
env_pop ( ) ;
}
2012-11-18 10:23:22 +00:00
2011-12-27 03:18:46 +00:00
env_read_only . clear ( ) ;
env_electric . clear ( ) ;
2010-09-18 01:51:16 +00:00
2012-11-19 00:30:30 +00:00
var_table_t : : iterator iter ;
for ( iter = global - > begin ( ) ; iter ! = global - > end ( ) ; + + iter )
2012-11-18 10:23:22 +00:00
{
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 top ;
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
2012-05-09 10:06:10 +00:00
int env_set ( const wcstring & key , const wchar_t * val , int 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
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 ) ;
}
}
2013-01-19 21:16:21 +00:00
/* Do not actually create a umask variable, on env_get, it will be calculated dynamically */
2012-11-19 00:30:30 +00:00
return 0 ;
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
{
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
// export
exportv = true ;
}
else if ( var_mode & ENV_UNEXPORT )
{
// unexport
exportv = false ;
2012-11-19 00:30:30 +00:00
}
else
{
2013-01-19 21:16:21 +00:00
// not changing the export
exportv = env_universal_get_export ( key ) ;
2012-11-19 00:30:30 +00:00
}
env_universal_set ( key , val , exportv ) ;
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-04-25 23:09:26 +00:00
if ( ! env_universal_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
{
2013-01-19 21:16:21 +00:00
exportv = env_universal_get_export ( key ) ;
}
2013-01-21 02:34:18 +00:00
2013-01-19 21:16:21 +00:00
env_universal_set ( key , val , exportv ) ;
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-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 ) )
{
erased = ! env_universal_remove ( key . c_str ( ) ) ;
}
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 ( ) ;
}
2012-11-19 00:30:30 +00:00
env_var_t env_get_string ( const wcstring & key )
2012-11-18 10:23:22 +00:00
{
2012-03-19 18:52:18 +00:00
/* 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) */
2012-03-31 22:33:34 +00:00
const bool is_main = is_main_thread ( ) ;
2012-11-19 00:30:30 +00:00
if ( key = = L " history " & & is_main )
{
2012-03-19 18:52:18 +00:00
env_var_t result ;
2012-11-18 10:23:22 +00:00
2012-03-19 18:52:18 +00:00
history_t * history = reader_get_history ( ) ;
2012-11-19 00:30:30 +00:00
if ( ! history )
{
2012-03-19 18:52:18 +00:00
history = & history_t : : history_with_name ( L " fish " ) ;
}
if ( history )
2012-11-18 10:23:22 +00:00
history - > get_string_representation ( result , ARRAY_SEP_STR ) ;
2012-11-19 00:30:30 +00:00
return result ;
}
else if ( key = = L " COLUMNS " )
{
2012-08-04 22:11:43 +00:00
return to_string ( common_get_width ( ) ) ;
2012-11-19 00:30:30 +00:00
}
else if ( key = = L " LINES " )
{
2013-05-20 17:59:33 +00:00
return to_string ( common_get_height ( ) ) ;
2012-11-19 00:30:30 +00:00
}
else if ( key = = L " status " )
{
2012-08-04 22:11:43 +00:00
return to_string ( proc_get_last_status ( ) ) ;
2012-11-19 00:30:30 +00:00
}
else if ( key = = L " umask " )
{
return format_string ( L " 0%0.3o " , get_umask ( ) ) ;
}
else
2012-04-23 18:08:29 +00:00
{
2012-03-31 22:33:34 +00:00
{
2012-04-23 18:08:29 +00:00
/* Lock around a local region */
scoped_lock lock ( env_lock ) ;
2012-11-18 10:23:22 +00:00
2012-04-23 18:08:29 +00:00
env_node_t * env = top ;
wcstring result ;
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
while ( env ! = NULL )
2012-03-31 22:33:34 +00:00
{
2013-02-20 01:48:51 +00:00
const var_entry_t * entry = env - > find_entry ( key ) ;
if ( entry ! = NULL )
2012-03-31 22:33:34 +00:00
{
2013-02-20 01:48:51 +00:00
if ( entry - > val = = ENV_NULL )
2012-04-23 18:08:29 +00:00
{
return env_var_t : : missing_var ( ) ;
}
else
{
2013-02-20 01:48:51 +00:00
return entry - > val ;
2012-04-23 18:08:29 +00:00
}
}
2012-11-18 10:23:22 +00:00
2013-02-20 01:48:51 +00:00
env = env - > next_scope_to_search ( ) ;
2012-03-31 22:33:34 +00:00
}
}
2012-11-18 10:23:22 +00:00
2012-04-23 18:08:29 +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 */
2012-11-19 00:30:30 +00:00
if ( is_main & & ! get_proc_had_barrier ( ) )
2011-12-27 03:18:46 +00:00
{
2012-03-31 22:33:34 +00:00
set_proc_had_barrier ( true ) ;
2011-12-27 03:18:46 +00:00
env_universal_barrier ( ) ;
}
2012-11-18 10:23:22 +00:00
2014-04-25 23:09:26 +00:00
env_var_t item = env_universal_get ( key ) ;
2012-11-18 10:23:22 +00:00
2014-04-25 23:09:26 +00:00
if ( item . missing ( ) | | ( wcscmp ( item . c_str ( ) , ENV_NULL ) = = 0 ) )
2011-12-27 03:18:46 +00:00
{
2012-01-14 09:06:47 +00:00
return env_var_t : : missing_var ( ) ;
2011-12-27 03:18:46 +00:00
}
else
{
return item ;
}
}
}
2005-09-20 13:26:39 +00:00
2013-01-19 21:16:21 +00:00
bool env_exist ( const wchar_t * key , int 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
2012-11-19 00:30:30 +00:00
/*
Read only variables all exist , and they are all global . A local
version can not exist .
*/
if ( ! ( mode & ENV_LOCAL ) & & ! ( mode & ENV_UNIVERSAL ) )
2012-11-18 10:23:22 +00:00
{
2012-11-19 00:30:30 +00:00
if ( is_read_only ( key ) | | is_electric ( key ) )
{
2012-06-18 17:20:40 +00:00
//Such variables are never exported
if ( mode & ENV_EXPORT )
{
2013-01-19 21:16:21 +00:00
return false ;
2012-06-18 17:20:40 +00:00
}
else if ( mode & ENV_UNEXPORT )
{
2013-01-19 21:16:21 +00:00
return true ;
2012-06-18 17:20:40 +00:00
}
2013-01-19 21:16:21 +00:00
return true ;
2012-11-19 00:30:30 +00:00
}
2012-11-18 10:23:22 +00:00
}
2012-06-18 17:20:40 +00:00
2012-11-19 00:30:30 +00:00
if ( ! ( mode & ENV_UNIVERSAL ) )
2012-11-18 10:23:22 +00:00
{
2012-11-19 00:30:30 +00:00
env = ( mode & ENV_GLOBAL ) ? global_env : top ;
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
while ( env ! = 0 )
{
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 ;
2012-02-17 18:52:30 +00:00
2012-06-18 17:20:40 +00:00
if ( mode & ENV_EXPORT )
{
2013-01-19 21:16:21 +00:00
return res . exportv ;
2012-06-18 17:20:40 +00:00
}
else if ( mode & ENV_UNEXPORT )
{
2013-01-19 21:16:21 +00:00
return ! res . exportv ;
2012-06-18 17:20:40 +00:00
}
2012-11-18 10:23:22 +00:00
2013-01-19 21:16:21 +00:00
return true ;
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 ( mode & ENV_LOCAL )
2012-11-18 10:23:22 +00:00
break ;
2013-02-20 01:48:51 +00:00
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
2012-11-19 00:30:30 +00:00
if ( ! ( mode & ENV_LOCAL ) & & ! ( mode & ENV_GLOBAL ) )
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-04-25 23:09:26 +00:00
if ( ! env_universal_get ( key ) . missing ( ) )
2012-06-18 17:20:40 +00:00
{
if ( mode & ENV_EXPORT )
{
2012-11-18 10:23:22 +00:00
return env_universal_get_export ( key ) = = 1 ;
2012-06-18 17:20:40 +00:00
}
else if ( mode & ENV_UNEXPORT )
{
2012-11-18 10:23:22 +00:00
return env_universal_get_export ( key ) = = 0 ;
2012-06-18 17:20:40 +00:00
}
return 1 ;
}
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-18 10:23:22 +00:00
2013-01-19 21:16:21 +00:00
if ( show_exported )
2012-11-19 00:30:30 +00:00
{
2012-01-10 20:51:09 +00:00
result . push_back ( L " COLUMNS " ) ;
result . push_back ( L " LINES " ) ;
2012-11-19 00:30:30 +00:00
}
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_universal )
{
2012-11-18 10:23:22 +00:00
2012-01-10 20:51:09 +00:00
wcstring_list_t uni_list ;
2013-02-12 07:16:50 +00:00
env_universal_get_names ( uni_list ,
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
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
{
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
{
2013-02-12 12:50:43 +00:00
const std : : string ks = wcs2string ( iter - > first ) ;
std : : string vs = wcs2string ( iter - > second ) ;
2013-02-14 23:50:24 +00:00
2013-02-12 12:50:43 +00:00
for ( size_t i = 0 ; i < vs . size ( ) ; i + + )
2012-11-19 00:30:30 +00:00
{
2013-02-12 12:50:43 +00:00
char & vc = vs . at ( i ) ;
if ( vc = = ARRAY_SEP )
vc = ' : ' ;
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
2011-12-28 20:36:47 +00:00
wcstring_list_t uni ;
2013-02-12 07:16:50 +00:00
env_universal_get_names ( uni , 1 , 0 ) ;
2012-11-19 00:30:30 +00:00
for ( i = 0 ; i < uni . size ( ) ; i + + )
{
const wcstring & key = uni . at ( i ) ;
2014-04-25 23:09:26 +00:00
const env_var_t val = env_universal_get ( key ) ;
2012-11-18 10:23:22 +00:00
2014-04-25 23:09:26 +00:00
if ( ! val . missing ( ) & & wcscmp ( val . c_str ( ) , ENV_NULL ) )
2012-11-19 00:30:30 +00:00
{
// 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-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
}
}
}
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 } ;