2005-09-20 21:26:39 +08:00
/** \file env.c
Functions for setting and getting environment variables .
*/
2005-10-05 17:58:00 +08:00
# include "config.h"
2005-09-20 21:26:39 +08: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 17:06:47 +08:00
# include <assert.h>
2005-09-20 21:26:39 +08:00
# include <sys/types.h>
# include <sys/stat.h>
2011-12-27 11:18:46 +08:00
# include <pthread.h>
2005-09-20 21:26:39 +08:00
# include <pwd.h>
2011-12-27 11:18:46 +08:00
# include <set>
# include <map>
2012-02-18 02:52:30 +08:00
# include <algorithm>
2005-09-20 21:26:39 +08:00
# if HAVE_NCURSES_H
# include <ncurses.h>
# else
# include <curses.h>
# endif
# if HAVE_TERMIO_H
# include <termio.h>
# endif
2006-01-19 20:22:07 +08:00
# if HAVE_TERM_H
2005-09-20 21:26:39 +08:00
# include <term.h>
2006-01-19 20:22:07 +08:00
# elif HAVE_NCURSES_TERM_H
# include <ncurses/term.h>
# endif
2006-07-20 07:11:49 +08:00
# if HAVE_LIBINTL_H
# include <libintl.h>
# endif
2005-10-22 18:06:05 +08:00
# include <errno.h>
2005-09-20 21:26:39 +08:00
2006-02-28 21:17:16 +08:00
# include "fallback.h"
2005-09-20 21:26:39 +08:00
# include "util.h"
2006-02-28 21:17:16 +08:00
2005-09-20 21:26:39 +08: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-06 06:18:16 +08:00
# include "input.h"
2005-10-06 06:37:08 +08:00
# include "event.h"
2007-05-11 03:11:28 +08:00
# include "path.h"
2006-07-20 06:55:49 +08:00
2006-01-13 09:00:12 +08:00
# include "complete.h"
2005-09-20 21:26:39 +08:00
/**
Command used to start fishd
*/
2012-07-11 11:30:54 +08:00
# define FISHD_CMD L"fishd ^ / tmp / fishd.log.%s"
2005-09-20 21:26:39 +08:00
2005-10-24 23:26:25 +08:00
/**
Value denoting a null string
*/
2005-09-26 22:47:03 +08:00
# define ENV_NULL L"\x1d"
2012-07-19 01:50:38 +08: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 21:26:39 +08:00
/**
2007-01-28 00:59:11 +08:00
At init , we read all the environment variables from this array .
2005-09-20 21:26:39 +08:00
*/
extern char * * environ ;
2007-01-28 00:59:11 +08: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 21:26:39 +08:00
2012-02-18 02:52:30 +08:00
/**
A variable entry . Stores the value of a variable and whether it
should be exported . Obviously , it needs to be allocated large
enough to fit the value string .
*/
2012-02-22 10:12:51 +08:00
struct var_entry_t
2012-02-18 02:52:30 +08:00
{
2012-02-22 10:12:51 +08:00
bool exportv ; /**< Whether the variable should be exported */
2012-02-18 02:52:30 +08:00
wcstring val ; /**< The value of the variable */
2012-02-22 10:12:51 +08:00
var_entry_t ( ) : exportv ( false ) { }
} ;
2012-02-18 02:52:30 +08:00
2012-02-29 07:11:46 +08:00
typedef std : : map < wcstring , var_entry_t * > var_table_t ;
2005-09-20 21:26:39 +08:00
2012-03-07 16:54:01 +08:00
bool g_log_forks = false ;
2012-08-15 15:57:56 +08:00
bool g_use_posix_spawn = false ; //will usually be set to true
2012-03-07 07:51:48 +08:00
2005-09-20 21:26:39 +08:00
/**
Struct representing one level in the function variable stack
*/
2012-02-26 10:54:49 +08:00
struct env_node_t
2005-09-20 21:26:39 +08:00
{
2011-12-27 11:18:46 +08:00
/**
Variable table
2005-09-20 21:26:39 +08:00
*/
2012-02-29 07:11:46 +08:00
var_table_t env ;
2011-12-27 11:18:46 +08:00
/**
2005-09-20 21:26:39 +08:00
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 .
*/
int new_scope ;
/**
Does this node contain any variables which are exported to subshells
*/
2011-12-27 11:18:46 +08:00
int exportv ;
/**
2005-09-20 21:26:39 +08:00
Pointer to next level
*/
2012-02-26 10:54:49 +08:00
struct env_node_t * next ;
2005-09-20 21:26:39 +08:00
2012-02-18 02:52:30 +08:00
2012-02-26 10:54:49 +08:00
env_node_t ( ) : new_scope ( 0 ) , exportv ( 0 ) , next ( NULL ) { }
} ;
2005-09-20 21:26:39 +08:00
2011-12-27 11:18:46 +08:00
class variable_entry_t {
bool exportv ; /**< Whether the variable should be exported */
wcstring value ; /**< Value of the variable */
} ;
static pthread_mutex_t env_lock = PTHREAD_MUTEX_INITIALIZER ;
2005-09-20 21:26:39 +08:00
/**
Top node on the function stack
*/
static env_node_t * top = 0 ;
/**
Bottom node on the function stack
*/
static env_node_t * global_env = 0 ;
/**
Table for global variables
*/
2012-02-29 07:11:46 +08:00
static var_table_t * global ;
2005-09-20 21:26:39 +08:00
/**
Table of variables that may not be set using the set command .
*/
2011-12-27 11:18:46 +08:00
static std : : set < wcstring > env_read_only ;
static bool is_read_only ( const wcstring & key )
{
return env_read_only . find ( key ) ! = env_read_only . end ( ) ;
}
2005-09-20 21:26:39 +08:00
2005-10-23 20:14:29 +08:00
/**
Table of variables whose value is dynamically calculated , such as umask , status , etc
*/
2011-12-27 11:18:46 +08:00
static std : : set < wcstring > env_electric ;
static bool is_electric ( const wcstring & key )
{
return env_electric . find ( key ) ! = env_electric . end ( ) ;
}
2005-10-23 20:14:29 +08:00
2005-09-20 21:26:39 +08:00
/**
Exported variable array used by execv
*/
2012-02-29 07:11:46 +08:00
static null_terminated_array_t < char > export_array ;
2005-09-20 21:26:39 +08:00
2005-09-23 04:16:52 +08:00
2005-09-20 21:26:39 +08:00
/**
Flag for checking if we need to regenerate the exported variable
array
*/
2012-08-15 15:57:56 +08:00
static bool has_changed_exported = true ;
static void mark_changed_exported ( )
{
has_changed_exported = true ;
}
2005-09-20 21:26:39 +08:00
/**
2012-02-06 08:42:24 +08:00
This string is used to store the value of dynamically
2005-09-20 21:26:39 +08:00
generated variables , such as history .
*/
2012-02-06 08:42:24 +08:00
static wcstring dyn_var ;
2005-09-20 21:26:39 +08:00
/**
Variable used by env_get_names to communicate auxiliary information
2012-02-18 23:34:09 +08:00
to add_key_to_string_set
2005-09-20 21:26:39 +08:00
*/
static int get_names_show_exported ;
/**
Variable used by env_get_names to communicate auxiliary information
2012-02-18 23:34:09 +08:00
to add_key_to_string_set
2005-09-20 21:26:39 +08:00
*/
static int get_names_show_unexported ;
2006-01-22 04:42:17 +08:00
/**
List of all locale variable names
*/
2012-03-06 06:18:16 +08:00
static const wchar_t * const locale_variable [ ] =
2006-01-22 04:42:17 +08:00
{
L " LANG " ,
L " LC_ALL " ,
L " LC_COLLATE " ,
L " LC_CTYPE " ,
L " LC_MESSAGES " ,
L " LC_MONETARY " ,
L " LC_NUMERIC " ,
L " LC_TIME " ,
2011-12-27 11:18:46 +08:00
NULL
2012-03-06 06:18:16 +08:00
} ;
2006-01-22 04:42:17 +08:00
2005-09-20 21:26:39 +08:00
/**
When fishd isn ' t started , this function is provided to
2005-10-03 21:09:37 +08: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 21:26:39 +08:00
*/
static void start_fishd ( )
{
2012-01-21 03:24:43 +08:00
struct passwd * pw = getpwuid ( getuid ( ) ) ;
2011-12-27 11:18:46 +08:00
2005-11-29 18:13:03 +08:00
debug ( 3 , L " Spawning new copy of fishd " ) ;
2011-12-27 11:18:46 +08:00
2005-09-20 21:26:39 +08:00
if ( ! pw )
{
2006-01-04 20:51:02 +08:00
debug ( 0 , _ ( L " Could not get user information " ) ) ;
2005-09-20 21:26:39 +08:00
return ;
}
2012-07-19 01:50:38 +08:00
wcstring cmd = format_string ( FISHD_CMD , pw - > pw_name ) ;
/* Prefer the fishd in __fish_bin_dir, if exists */
const env_var_t bin_dir = env_get_string ( L " __fish_bin_dir " ) ;
if ( ! bin_dir . missing_or_empty ( ) )
{
wcstring path = bin_dir + L " /fishd " ;
if ( waccess ( path , X_OK ) = = 0 )
{
/* The path command just looks like 'fishd', so insert the bin path to make it absolute */
cmd . insert ( 0 , bin_dir + L " / " ) ;
}
}
2012-01-23 13:57:30 +08:00
parser_t & parser = parser_t : : principal_parser ( ) ;
2012-08-15 15:57:56 +08:00
parser . eval ( cmd , io_chain_t ( ) , TOP ) ;
2005-09-20 21:26:39 +08:00
}
2005-10-23 20:14:29 +08:00
/**
Return the current umask value .
*/
static mode_t get_umask ( )
{
mode_t res ;
res = umask ( 0 ) ;
umask ( res ) ;
return res ;
}
2012-03-06 06:18:16 +08:00
/** Checks if the specified variable is a locale variable */
static bool var_is_locale ( const wcstring & key ) {
for ( size_t i = 0 ; locale_variable [ i ] ; i + + ) {
if ( key = = locale_variable [ i ] ) {
return true ;
2007-01-21 23:01:14 +08:00
}
}
2012-03-06 06:18:16 +08:00
return false ;
2006-01-09 07:00:49 +08:00
}
/**
Properly sets all locale information
*/
static void handle_locale ( )
{
2012-01-14 17:06:47 +08:00
const env_var_t lc_all = env_get_string ( L " LC_ALL " ) ;
2006-01-09 07:00:49 +08:00
int i ;
2012-02-01 13:06:52 +08:00
const wcstring old_locale = wsetlocale ( LC_MESSAGES , NULL ) ;
2006-01-13 09:00:12 +08:00
/*
Array of locale constants corresponding to the local variable names defined in locale_variable
*/
2011-12-27 11:18:46 +08:00
static const int cat [ ] =
2006-01-09 07:00:49 +08:00
{
2011-12-27 11:18:46 +08:00
0 ,
LC_ALL ,
2006-01-22 04:42:17 +08:00
LC_COLLATE ,
LC_CTYPE ,
LC_MESSAGES ,
LC_MONETARY ,
LC_NUMERIC ,
LC_TIME
2006-01-09 07:00:49 +08:00
}
;
2011-12-27 11:18:46 +08:00
2012-01-14 17:06:47 +08:00
if ( ! lc_all . missing ( ) )
2006-01-09 07:00:49 +08:00
{
2012-01-12 06:27:38 +08:00
wsetlocale ( LC_ALL , lc_all . c_str ( ) ) ;
2006-01-09 07:00:49 +08:00
}
else
{
2012-01-14 18:42:17 +08:00
const env_var_t lang = env_get_string ( L " LANG " ) ;
if ( ! lang . missing ( ) )
2006-01-09 07:00:49 +08:00
{
2012-01-12 06:27:38 +08:00
wsetlocale ( LC_ALL , lang . c_str ( ) ) ;
2006-01-09 07:00:49 +08:00
}
2011-12-27 11:18:46 +08:00
2006-01-13 09:00:12 +08:00
for ( i = 2 ; locale_variable [ i ] ; i + + )
2006-01-09 07:00:49 +08:00
{
2012-01-14 18:42:17 +08:00
const env_var_t val = env_get_string ( locale_variable [ i ] ) ;
2007-01-21 23:01:14 +08:00
2012-01-14 18:42:17 +08:00
if ( ! val . missing ( ) )
2007-01-21 23:01:14 +08:00
{
2012-01-12 06:27:38 +08:00
wsetlocale ( cat [ i ] , val . c_str ( ) ) ;
2007-01-21 23:01:14 +08:00
}
2006-01-09 07:00:49 +08:00
}
}
2011-12-27 11:18:46 +08:00
2012-02-01 13:06:52 +08:00
const wcstring new_locale = wsetlocale ( LC_MESSAGES , NULL ) ;
if ( old_locale ! = new_locale )
2006-01-09 07:00:49 +08:00
{
2006-01-22 00:02:34 +08:00
2011-12-27 11:18:46 +08:00
/*
2006-07-20 07:11:49 +08: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 .
*/
2007-01-21 23:01:14 +08:00
2006-07-20 07:11:49 +08:00
extern int _nl_msg_cat_cntr ;
2007-01-21 23:01:14 +08:00
_nl_msg_cat_cntr + + ;
2006-07-20 07:11:49 +08:00
dcgettext ( " fish " , " Changing language to English " , LC_MESSAGES ) ;
2011-12-27 11:18:46 +08:00
2012-02-26 10:54:49 +08:00
if ( get_is_interactive ( ) )
2006-01-09 07:00:49 +08:00
{
2006-01-21 10:53:07 +08:00
debug ( 0 , _ ( L " Changing language to English " ) ) ;
2006-01-09 07:00:49 +08:00
}
2012-02-01 13:06:52 +08:00
}
2006-01-09 07:00:49 +08:00
}
2012-03-06 06:18:16 +08:00
/** React to modifying hte given variable */
static void react_to_variable_change ( const wcstring & key ) {
if ( var_is_locale ( key ) ) {
handle_locale ( ) ;
} else if ( key = = L " fish_term256 " ) {
update_fish_term256 ( ) ;
2012-03-25 18:00:38 +08:00
reader_react_to_color_change ( ) ;
} else if ( string_prefixes_string ( L " fish_color_ " , key ) ) {
reader_react_to_color_change ( ) ;
2012-03-06 06:18:16 +08:00
}
}
2005-10-23 20:14:29 +08:00
/**
Universal variable callback function . This function makes sure the
proper events are triggered when an event occurs .
*/
2012-10-09 05:47:25 +08:00
static void universal_callback ( fish_message_type_t type ,
2011-12-27 11:18:46 +08:00
const wchar_t * name ,
2005-10-12 03:23:43 +08:00
const wchar_t * val )
{
2011-12-27 11:18:46 +08:00
const wchar_t * str = 0 ;
2005-10-12 03:23:43 +08:00
switch ( type )
{
case SET :
case SET_EXPORT :
2007-01-21 23:01:14 +08:00
{
2005-10-12 03:23:43 +08:00
str = L " SET " ;
break ;
2007-01-21 23:01:14 +08:00
}
2011-12-27 11:18:46 +08:00
2005-10-12 03:23:43 +08:00
case ERASE :
2007-01-21 23:01:14 +08:00
{
2005-10-12 03:23:43 +08:00
str = L " ERASE " ;
break ;
2007-01-21 23:01:14 +08:00
}
2012-10-09 05:47:25 +08:00
default :
break ;
2005-10-12 03:23:43 +08:00
}
2011-12-27 11:18:46 +08:00
2005-10-12 03:23:43 +08:00
if ( str )
{
2012-08-15 15:57:56 +08:00
mark_changed_exported ( ) ;
2011-12-27 11:18:46 +08:00
2012-02-09 11:02:25 +08:00
event_t ev = event_t : : variable_event ( name ) ;
ev . arguments . reset ( new wcstring_list_t ( ) ) ;
2011-12-27 16:06:07 +08:00
ev . arguments - > push_back ( L " VARIABLE " ) ;
ev . arguments - > push_back ( str ) ;
ev . arguments - > push_back ( name ) ;
event_fire ( & ev ) ;
2012-02-09 11:02:25 +08:00
ev . arguments . reset ( NULL ) ;
2005-10-12 03:23:43 +08:00
}
2012-03-25 18:00:38 +08:00
if ( name )
react_to_variable_change ( name ) ;
2005-10-12 03:23:43 +08:00
}
2006-01-22 04:42:17 +08:00
/**
2012-01-14 18:42:17 +08:00
Make sure the PATH variable contains the essential directories
2006-01-22 04:42:17 +08:00
*/
static void setup_path ( )
2012-01-14 18:42:17 +08:00
{
2011-12-28 10:41:38 +08:00
size_t i ;
int j ;
wcstring_list_t lst ;
2012-01-14 18:42:17 +08:00
2011-12-27 11:18:46 +08:00
const wchar_t * path_el [ ] =
2012-01-14 18:42:17 +08:00
{
L " /bin " ,
L " /usr/bin " ,
PREFIX L " /bin " ,
0
}
2006-01-22 04:42:17 +08:00
;
2012-01-14 18:42:17 +08:00
env_var_t path = env_get_string ( L " PATH " ) ;
2011-12-27 11:18:46 +08:00
2012-01-14 18:42:17 +08:00
if ( ! path . missing ( ) )
2007-01-21 23:01:14 +08:00
{
2012-02-10 17:37:30 +08:00
tokenize_variable_array ( path , lst ) ;
2007-01-21 23:01:14 +08:00
}
2011-12-27 11:18:46 +08:00
2006-01-22 04:42:17 +08:00
for ( j = 0 ; path_el [ j ] ; j + + )
{
2012-01-14 18:42:17 +08:00
2006-01-22 04:42:17 +08:00
int has_el = 0 ;
2011-12-27 11:18:46 +08:00
2011-12-28 10:41:38 +08:00
for ( i = 0 ; i < lst . size ( ) ; i + + )
2006-01-22 04:42:17 +08:00
{
2011-12-28 10:41:38 +08:00
wcstring el = lst . at ( i ) ;
size_t len = el . size ( ) ;
2012-01-14 18:42:17 +08:00
2006-01-22 04:42:17 +08:00
while ( ( len > 0 ) & & ( el [ len - 1 ] = = L ' / ' ) )
2007-01-21 23:01:14 +08:00
{
2006-01-22 04:42:17 +08:00
len - - ;
2007-01-21 23:01:14 +08:00
}
2012-01-14 18:42:17 +08:00
2011-12-27 11:18:46 +08:00
if ( ( wcslen ( path_el [ j ] ) = = len ) & &
2012-01-14 18:42:17 +08:00
( wcsncmp ( el . c_str ( ) , path_el [ j ] , len ) = = 0 ) )
2006-01-22 04:42:17 +08:00
{
has_el = 1 ;
}
}
2011-12-27 11:18:46 +08:00
2006-01-22 04:42:17 +08:00
if ( ! has_el )
{
2011-12-28 10:41:38 +08:00
wcstring buffer ;
2012-01-14 18:42:17 +08:00
2006-01-22 04:42:17 +08:00
debug ( 3 , L " directory %ls was missing " , path_el [ j ] ) ;
2012-01-14 18:42:17 +08:00
if ( ! path . missing ( ) )
2006-01-22 04:42:17 +08:00
{
2012-01-14 18:42:17 +08:00
buffer + = path ;
2006-01-22 04:42:17 +08:00
}
2011-12-27 11:18:46 +08:00
2012-01-14 18:42:17 +08:00
buffer + = ARRAY_SEP_STR ;
buffer + = path_el [ j ] ;
2011-12-27 11:18:46 +08:00
2012-01-12 06:27:38 +08:00
env_set ( L " PATH " , buffer . empty ( ) ? NULL : buffer . c_str ( ) , ENV_GLOBAL | ENV_EXPORT ) ;
2012-01-14 18:42:17 +08:00
2012-01-12 06:27:38 +08:00
path = env_get_string ( L " PATH " ) ;
2012-01-14 18:42:17 +08:00
lst . resize ( 0 ) ;
2012-02-10 17:37:30 +08:00
tokenize_variable_array ( path , lst ) ;
2006-01-22 04:42:17 +08:00
}
}
}
2008-01-17 06:07:38 +08:00
int env_set_pwd ( )
{
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-14 00:47:47 +08:00
/**
Set up default values for various variables if not defined .
*/
2006-10-19 19:38:44 +08:00
static void env_set_defaults ( )
{
2007-01-21 23:01:14 +08:00
2012-01-14 18:42:17 +08:00
if ( env_get_string ( L " USER " ) . missing ( ) )
2006-10-19 19:38:44 +08:00
{
struct passwd * pw = getpwuid ( getuid ( ) ) ;
wchar_t * unam = str2wcs ( pw - > pw_name ) ;
env_set ( L " USER " , unam , ENV_GLOBAL ) ;
free ( unam ) ;
}
2007-01-21 23:01:14 +08:00
2012-01-14 18:42:17 +08:00
if ( env_get_string ( L " HOME " ) . missing ( ) )
2006-10-19 19:38:44 +08:00
{
2012-01-14 18:42:17 +08:00
const env_var_t unam = env_get_string ( L " USER " ) ;
2012-01-12 06:27:38 +08:00
char * unam_narrow = wcs2str ( unam . c_str ( ) ) ;
2006-10-19 19:38:44 +08:00
struct passwd * pw = getpwnam ( unam_narrow ) ;
wchar_t * dir = str2wcs ( pw - > pw_dir ) ;
env_set ( L " HOME " , dir , ENV_GLOBAL ) ;
2011-12-27 11:18:46 +08:00
free ( dir ) ;
2006-10-19 19:38:44 +08:00
free ( unam_narrow ) ;
2011-12-27 11:18:46 +08:00
}
2007-01-21 23:01:14 +08:00
2008-01-17 06:07:38 +08:00
env_set_pwd ( ) ;
2011-12-27 11:18:46 +08:00
2006-10-19 19:38:44 +08:00
}
2012-03-07 07:51:48 +08: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.
static bool variable_can_be_array ( const wchar_t * key ) {
if ( ! wcscmp ( key , L " DISPLAY " ) ) {
return false ;
} else {
return true ;
}
}
2012-07-19 01:50:38 +08:00
void env_init ( const struct config_paths_t * paths /* or NULL */ )
2005-09-20 21:26:39 +08:00
{
char * * p ;
2005-10-23 18:22:32 +08:00
struct passwd * pw ;
2006-01-22 04:42:17 +08:00
wchar_t * uname ;
2006-08-28 17:58:40 +08:00
wchar_t * version ;
2011-12-27 11:18:46 +08:00
2005-09-20 21:26:39 +08:00
/*
2011-12-27 11:18:46 +08:00
env_read_only variables can not be altered directly by the user
2005-09-20 21:26:39 +08:00
*/
2011-12-27 11:18:46 +08:00
const wchar_t * const ro_keys [ ] = {
L " status " ,
L " history " ,
L " version " ,
L " _ " ,
L " LINES " ,
L " COLUMNS " ,
L " PWD " ,
L " SHLVL " ,
} ;
for ( size_t i = 0 ; i < sizeof ro_keys / sizeof * ro_keys ; i + + ) {
env_read_only . insert ( ro_keys [ i ] ) ;
}
2005-09-20 21:26:39 +08:00
/*
2005-10-23 18:22:32 +08:00
HOME and USER should be writeable by root , since this can be a
2005-09-20 21:26:39 +08:00
convenient way to install software .
*/
if ( getuid ( ) ! = 0 )
2005-10-23 18:22:32 +08:00
{
2011-12-27 11:18:46 +08:00
env_read_only . insert ( L " HOME " ) ;
env_read_only . insert ( L " USER " ) ;
2005-10-23 18:22:32 +08:00
}
2011-12-27 11:18:46 +08:00
/*
Names of all dynamically calculated variables
*/
env_electric . insert ( L " history " ) ;
env_electric . insert ( L " status " ) ;
env_electric . insert ( L " umask " ) ;
2012-02-18 02:52:30 +08:00
top = new env_node_t ;
2005-09-20 21:26:39 +08:00
global_env = top ;
2011-12-27 11:18:46 +08:00
global = & top - > env ;
2006-01-22 04:42:17 +08:00
/*
Now the environemnt variable handling is set up , the next step
is to insert valid data
*/
2012-07-19 01:50:38 +08:00
2005-09-20 21:26:39 +08:00
/*
Import environment variables
*/
2007-01-28 00:59:11 +08:00
for ( p = environ ? environ : __environ ; p & & * p ; p + + )
2005-09-20 21:26:39 +08:00
{
wchar_t * key , * val ;
2011-12-27 11:18:46 +08:00
2005-09-20 21:26:39 +08:00
key = str2wcs ( * p ) ;
2005-10-25 17:39:45 +08:00
2005-09-20 21:26:39 +08:00
if ( ! key )
2007-01-21 23:01:14 +08:00
{
2005-09-20 21:26:39 +08:00
continue ;
2007-01-21 23:01:14 +08:00
}
2011-12-27 11:18:46 +08:00
2005-09-20 21:26:39 +08:00
val = wcschr ( key , L ' = ' ) ;
2005-10-25 17:39:45 +08:00
2005-09-20 21:26:39 +08:00
if ( val = = 0 )
2007-01-21 23:01:14 +08:00
{
2005-09-20 21:26:39 +08:00
env_set ( key , L " " , ENV_EXPORT ) ;
2007-01-21 23:01:14 +08:00
}
2005-09-20 21:26:39 +08:00
else
2011-12-27 11:18:46 +08:00
{
2005-09-20 21:26:39 +08:00
* val = L ' \0 ' ;
val + + ;
2012-03-07 07:51:48 +08:00
//fwprintf( stderr, L"Set $%ls to %ls\n", key, val );
if ( variable_can_be_array ( val ) ) {
for ( size_t i = 0 ; val [ i ] ! = L ' \0 ' ; i + + ) {
if ( val [ i ] = = L ' : ' ) {
val [ i ] = ARRAY_SEP ;
}
}
}
2005-10-25 17:39:45 +08:00
2005-09-20 21:26:39 +08:00
env_set ( key , val , ENV_EXPORT | ENV_GLOBAL ) ;
2011-12-27 11:18:46 +08:00
}
2005-09-20 21:26:39 +08:00
free ( key ) ;
2005-10-23 18:22:32 +08:00
}
2011-12-27 11:18:46 +08:00
2012-07-19 01:50:38 +08: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 ) ;
}
2006-01-22 04:42:17 +08:00
/*
Set up the PATH variable
*/
setup_path ( ) ;
2005-11-24 02:57:43 +08:00
2006-01-22 04:42:17 +08:00
/*
Set up the USER variable
*/
2005-10-23 18:22:32 +08:00
pw = getpwuid ( getuid ( ) ) ;
2005-10-25 19:03:52 +08:00
if ( pw )
{
uname = str2wcs ( pw - > pw_name ) ;
env_set ( L " USER " , uname , ENV_GLOBAL | ENV_EXPORT ) ;
free ( uname ) ;
}
2006-08-28 17:58:40 +08:00
/*
Set up the version variable
*/
version = str2wcs ( PACKAGE_VERSION ) ;
env_set ( L " version " , version , ENV_GLOBAL ) ;
free ( version ) ;
2012-01-12 06:27:38 +08:00
2012-01-14 18:42:17 +08: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 " ) ;
2012-01-13 00:28:42 +08:00
2012-01-14 18:42:17 +08:00
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 ( ) ) ;
2012-01-12 06:27:38 +08:00
env_universal_init ( fishd_dir , user_dir ,
2005-10-12 03:23:43 +08:00
& start_fishd ,
& universal_callback ) ;
2006-10-19 19:38:44 +08:00
2010-10-08 08:35:22 +08:00
/*
Set up SHLVL variable
*/
2012-01-14 18:42:17 +08:00
const env_var_t shlvl_str = env_get_string ( L " SHLVL " ) ;
2012-05-09 18:23:31 +08:00
wcstring nshlvl_str = L " 1 " ;
if ( ! shlvl_str . missing ( ) )
2010-10-08 08:35:22 +08:00
{
2012-05-09 18:23:31 +08:00
long shlvl_i = wcstol ( shlvl_str . c_str ( ) , NULL , 10 ) ;
if ( shlvl_i > = 0 )
{
nshlvl_str = format_string ( L " %ld " , 1 + shlvl_i ) ;
}
2010-10-08 08:35:22 +08:00
}
2012-05-09 18:23:31 +08:00
env_set ( L " SHLVL " , nshlvl_str . c_str ( ) , ENV_GLOBAL | ENV_EXPORT ) ;
2010-10-08 08:35:22 +08:00
2012-03-10 03:56:33 +08:00
/* Set correct defaults for e.g. USER and HOME variables */
2006-10-19 19:38:44 +08:00
env_set_defaults ( ) ;
2012-03-10 03:56:33 +08: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-08-15 15:57:56 +08: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 ) ) ;
2005-09-20 21:26:39 +08:00
}
void env_destroy ( )
{
env_universal_destroy ( ) ;
2011-12-27 11:18:46 +08:00
2005-09-20 21:26:39 +08:00
while ( & top - > env ! = global )
2007-01-21 23:01:14 +08:00
{
2005-09-20 21:26:39 +08:00
env_pop ( ) ;
2007-01-21 23:01:14 +08:00
}
2011-12-27 11:18:46 +08:00
env_read_only . clear ( ) ;
env_electric . clear ( ) ;
2010-09-18 09:51:16 +08:00
2011-12-27 11:18:46 +08:00
2012-02-29 07:11:46 +08:00
var_table_t : : iterator iter ;
2012-02-18 02:52:30 +08:00
for ( iter = global - > begin ( ) ; iter ! = global - > end ( ) ; + + iter ) {
var_entry_t * entry = iter - > second ;
if ( entry - > exportv )
{
2012-08-15 15:57:56 +08:00
mark_changed_exported ( ) ;
2012-02-18 02:52:30 +08:00
}
2012-02-18 17:12:02 +08:00
2012-02-18 02:52:30 +08:00
delete entry ;
}
delete top ;
2005-09-20 21:26:39 +08:00
}
/**
2007-01-21 23:01:14 +08:00
Search all visible scopes in order for the specified key . Return
the first scope in which it was found .
2005-09-20 21:26:39 +08:00
*/
2012-02-18 02:52:30 +08:00
static env_node_t * env_get_node ( const wcstring & key )
2005-09-20 21:26:39 +08:00
{
env_node_t * env = top ;
2012-03-26 16:21:10 +08:00
while ( env ! = NULL )
2005-09-20 21:26:39 +08:00
{
2012-03-26 14:31:03 +08:00
if ( env - > env . find ( key ) ! = env - > env . end ( ) )
2012-02-18 02:52:30 +08:00
{
2012-03-26 16:21:10 +08:00
break ;
2005-09-20 21:26:39 +08:00
}
if ( env - > new_scope )
2007-01-21 23:01:14 +08:00
{
2005-09-20 21:26:39 +08:00
env = global_env ;
2007-01-21 23:01:14 +08:00
}
2011-12-27 11:18:46 +08:00
else
2007-01-21 23:01:14 +08:00
{
2005-09-20 21:26:39 +08:00
env = env - > next ;
2007-01-21 23:01:14 +08:00
}
2005-09-20 21:26:39 +08:00
}
2012-03-26 16:21:10 +08:00
return env ;
2005-09-20 21:26:39 +08:00
}
2006-01-09 07:00:49 +08:00
2012-05-09 18:06:10 +08:00
int env_set ( const wcstring & key , const wchar_t * val , int var_mode )
2005-09-20 21:26:39 +08:00
{
2012-04-01 06:33:34 +08:00
ASSERT_IS_MAIN_THREAD ( ) ;
2012-02-18 02:52:30 +08:00
env_node_t * node = NULL ;
2012-08-15 15:57:56 +08:00
bool has_changed_old = has_changed_exported ;
2012-02-29 07:11:46 +08:00
bool has_changed_new = false ;
2011-12-27 11:18:46 +08:00
var_entry_t * e = 0 ;
2005-10-06 06:37:08 +08:00
int done = 0 ;
2012-02-18 04:23:30 +08:00
2012-08-15 15:57:56 +08:00
int is_universal = 0 ;
2012-02-18 04:23:30 +08:00
2007-09-29 05:32:27 +08:00
if ( val & & contains ( key , L " PWD " , L " HOME " ) )
2007-05-11 03:11:28 +08:00
{
2012-02-08 13:23:12 +08:00
/* Canoncalize our path; if it changes, recurse and try again. */
wcstring val_canonical = val ;
path_make_canonical ( val_canonical ) ;
if ( val ! = val_canonical ) {
return env_set ( key , val_canonical . c_str ( ) , var_mode ) ;
}
2007-05-11 03:11:28 +08:00
}
2012-02-18 04:23:30 +08:00
2011-12-27 11:18:46 +08:00
if ( ( var_mode & ENV_USER ) & & is_read_only ( key ) )
2005-09-20 21:26:39 +08:00
{
2006-04-10 23:36:26 +08:00
return ENV_PERM ;
2005-09-20 21:26:39 +08:00
}
2011-12-27 11:18:46 +08:00
2012-05-09 18:06:10 +08:00
if ( key = = L " umask " )
2005-10-22 18:06:05 +08:00
{
wchar_t * end ;
2012-07-29 08:49:46 +08:00
2005-10-23 20:14:29 +08:00
/*
2012-02-18 04:23:30 +08:00
Set the new umask
*/
2005-10-22 18:06:05 +08:00
if ( val & & wcslen ( val ) )
2011-12-27 11:18:46 +08:00
{
2005-10-22 18:06:05 +08:00
errno = 0 ;
2012-07-29 08:49:46 +08:00
long mask = wcstol ( val , & end , 8 ) ;
2012-02-18 04:23:30 +08:00
2005-10-23 20:14:29 +08:00
if ( ! errno & & ( ! * end ) & & ( mask < = 0777 ) & & ( mask > = 0 ) )
2005-10-22 18:06:05 +08:00
{
umask ( mask ) ;
}
}
2005-10-23 20:14:29 +08:00
/*
2012-02-18 04:23:30 +08:00
Do not actually create a umask variable , on env_get , it will
be calculated dynamically
*/
2006-04-10 23:36:26 +08:00
return 0 ;
2005-10-22 18:06:05 +08:00
}
2012-02-18 04:23:30 +08:00
2005-09-26 22:47:03 +08:00
/*
2012-02-18 04:23:30 +08:00
Zero element arrays are internaly not coded as null but as this
placeholder string
*/
2006-01-26 23:48:23 +08:00
if ( ! val )
2005-09-26 22:47:03 +08:00
{
val = ENV_NULL ;
}
2011-12-27 11:18:46 +08:00
2005-09-20 21:26:39 +08:00
if ( var_mode & ENV_UNIVERSAL )
{
2011-12-27 11:18:46 +08:00
int exportv = 0 ;
2012-02-18 04:23:30 +08:00
2005-09-23 04:16:52 +08:00
if ( ! ( var_mode & ENV_EXPORT ) & &
2012-02-18 04:23:30 +08:00
! ( var_mode & ENV_UNEXPORT ) )
2005-09-23 04:16:52 +08:00
{
env_universal_get_export ( key ) ;
}
2011-12-27 11:18:46 +08:00
else
2007-01-21 23:01:14 +08:00
{
2011-12-27 11:18:46 +08:00
exportv = ( var_mode & ENV_EXPORT ) ;
2007-01-21 23:01:14 +08:00
}
2011-12-27 11:18:46 +08:00
2012-05-09 18:06:10 +08:00
env_universal_set ( key , val , exportv ) ;
2005-10-12 03:23:43 +08:00
is_universal = 1 ;
2012-02-18 04:23:30 +08:00
2005-09-20 21:26:39 +08:00
}
2005-10-06 06:37:08 +08:00
else
2005-09-20 21:26:39 +08:00
{
2011-12-27 11:18:46 +08:00
2005-10-06 06:37:08 +08:00
node = env_get_node ( key ) ;
2012-02-18 02:52:30 +08:00
if ( node )
2005-10-06 06:37:08 +08:00
{
2012-02-29 07:11:46 +08:00
var_table_t : : iterator result = node - > env . find ( key ) ;
2012-03-26 16:21:10 +08:00
assert ( result ! = node - > env . end ( ) ) ;
e = result - > second ;
2011-12-27 11:18:46 +08:00
if ( e - > exportv )
2007-01-21 23:01:14 +08:00
{
2012-02-29 07:11:46 +08:00
has_changed_new = true ;
2007-01-21 23:01:14 +08:00
}
2005-10-06 06:37:08 +08:00
}
2012-02-18 04:23:30 +08:00
2011-12-27 11:18:46 +08:00
if ( ( var_mode & ENV_LOCAL ) | |
2012-02-18 04:23:30 +08:00
( var_mode & ENV_GLOBAL ) )
2005-09-20 21:26:39 +08:00
{
2005-10-06 06:37:08 +08:00
node = ( var_mode & ENV_GLOBAL ) ? global_env : top ;
2005-09-20 21:26:39 +08:00
}
else
{
2005-10-06 06:37:08 +08:00
if ( node )
2005-09-20 21:26:39 +08:00
{
2005-09-23 04:16:52 +08:00
if ( ! ( var_mode & ENV_EXPORT ) & &
2012-02-18 04:23:30 +08:00
! ( var_mode & ENV_UNEXPORT ) )
2011-12-27 11:18:46 +08:00
{
var_mode = e - > exportv ? ENV_EXPORT : 0 ;
2005-09-23 04:16:52 +08:00
}
2005-09-20 21:26:39 +08:00
}
else
{
2012-04-01 06:33:34 +08:00
if ( ! get_proc_had_barrier ( ) )
2005-11-28 07:22:08 +08:00
{
2012-04-01 06:33:34 +08:00
set_proc_had_barrier ( true ) ;
2005-10-06 06:37:08 +08:00
env_universal_barrier ( ) ;
2005-11-28 07:22:08 +08:00
}
2011-12-27 11:18:46 +08:00
2005-10-06 06:37:08 +08:00
if ( env_universal_get ( key ) )
{
2011-12-27 11:18:46 +08:00
int exportv = 0 ;
2012-02-18 04:23:30 +08:00
2005-10-06 06:37:08 +08:00
if ( ! ( var_mode & ENV_EXPORT ) & &
2012-02-18 04:23:30 +08:00
! ( var_mode & ENV_UNEXPORT ) )
2005-10-06 06:37:08 +08:00
{
env_universal_get_export ( key ) ;
}
2011-12-27 11:18:46 +08:00
else
2007-01-21 23:01:14 +08:00
{
2011-12-27 11:18:46 +08:00
exportv = ( var_mode & ENV_EXPORT ) ;
2007-01-21 23:01:14 +08:00
}
2011-12-27 11:18:46 +08:00
2012-05-09 18:06:10 +08:00
env_universal_set ( key , val , exportv ) ;
2005-10-12 03:23:43 +08:00
is_universal = 1 ;
2011-12-27 11:18:46 +08:00
2005-10-06 06:37:08 +08:00
done = 1 ;
2012-02-18 04:23:30 +08:00
2005-10-06 06:37:08 +08:00
}
else
{
/*
2012-02-18 04:23:30 +08:00
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 .
*/
2005-10-06 06:37:08 +08:00
node = top ;
while ( node - > next & & ! node - > new_scope )
2007-01-21 23:01:14 +08:00
{
2005-10-06 06:37:08 +08:00
node = node - > next ;
2007-01-21 23:01:14 +08:00
}
2005-10-06 06:37:08 +08:00
}
2005-09-20 21:26:39 +08:00
}
}
2012-02-18 04:23:30 +08:00
2005-10-06 06:37:08 +08:00
if ( ! done )
2012-02-18 04:23:30 +08:00
{
2012-02-18 02:52:30 +08:00
var_entry_t * old_entry = NULL ;
2012-02-29 07:11:46 +08:00
var_table_t : : iterator result = node - > env . find ( key ) ;
2012-02-18 02:52:30 +08:00
if ( result ! = node - > env . end ( ) )
{
old_entry = result - > second ;
node - > env . erase ( result ) ;
}
2012-02-18 04:23:30 +08:00
var_entry_t * entry = NULL ;
2012-02-22 10:12:51 +08:00
if ( old_entry )
2012-02-18 04:23:30 +08:00
{
2012-02-18 02:52:30 +08:00
entry = old_entry ;
2011-12-27 11:18:46 +08:00
2012-02-22 10:12:51 +08:00
if ( ( var_mode & ENV_EXPORT ) | | entry - > exportv )
2012-02-18 04:23:30 +08:00
{
entry - > exportv = ! ! ( var_mode & ENV_EXPORT ) ;
2012-02-29 07:11:46 +08:00
has_changed_new = true ;
2012-02-18 04:23:30 +08:00
}
}
2005-10-06 06:37:08 +08:00
else
2012-02-18 04:23:30 +08:00
{
2012-02-22 10:12:51 +08:00
entry = new var_entry_t ;
2012-02-18 04:23:30 +08:00
2012-02-18 02:52:30 +08:00
if ( var_mode & ENV_EXPORT )
2012-02-18 04:23:30 +08:00
{
entry - > exportv = 1 ;
2012-02-29 07:11:46 +08:00
has_changed_new = true ;
2012-02-18 04:23:30 +08:00
}
2012-02-18 02:52:30 +08:00
else
2012-02-18 04:23:30 +08:00
{
entry - > exportv = 0 ;
}
2011-12-27 11:18:46 +08:00
2012-02-18 04:23:30 +08:00
}
2012-02-18 02:52:30 +08:00
entry - > val = val ;
2012-06-17 04:05:58 +08:00
node - > env [ key ] = entry ;
2012-02-18 04:23:30 +08:00
2011-12-27 11:18:46 +08:00
if ( entry - > exportv )
2012-02-18 04:23:30 +08:00
{
2012-02-18 02:52:30 +08:00
node - > exportv = 1 ;
2012-02-18 04:23:30 +08:00
}
2012-08-15 15:57:56 +08:00
if ( has_changed_old | | has_changed_new )
mark_changed_exported ( ) ;
2012-02-18 04:23:30 +08:00
}
}
if ( ! is_universal )
{
event_t ev = event_t : : variable_event ( key ) ;
ev . arguments . reset ( new wcstring_list_t ) ;
ev . arguments - > push_back ( L " VARIABLE " ) ;
ev . arguments - > push_back ( L " SET " ) ;
ev . arguments - > push_back ( key ) ;
2011-12-27 11:18:46 +08:00
2012-02-18 04:23:30 +08:00
// debug( 1, L"env_set: fire events on variable %ls", key );
event_fire ( & ev ) ;
// debug( 1, L"env_set: return from event firing" );
ev . arguments . reset ( NULL ) ;
}
2012-03-06 06:18:16 +08:00
react_to_variable_change ( key ) ;
2012-02-18 04:23:30 +08:00
return 0 ;
}
2005-09-20 21:26:39 +08:00
2006-01-09 07:00:49 +08:00
2005-09-20 21:26:39 +08:00
/**
Attempt to remove / free the specified key / value pair from the
2012-02-18 23:34:09 +08:00
specified map .
2006-05-29 21:12:17 +08:00
\ return zero if the variable was not found , non - zero otherwise
2005-09-20 21:26:39 +08:00
*/
static int try_remove ( env_node_t * n ,
2006-06-05 04:14:51 +08:00
const wchar_t * key ,
int var_mode )
2005-09-20 21:26:39 +08:00
{
if ( n = = 0 )
2007-01-21 23:01:14 +08:00
{
2005-09-20 21:26:39 +08:00
return 0 ;
2007-01-21 23:01:14 +08:00
}
2006-03-26 19:23:39 +08:00
2012-02-29 07:11:46 +08:00
var_table_t : : iterator result = n - > env . find ( key ) ;
2012-02-18 02:52:30 +08:00
if ( result ! = n - > env . end ( ) )
2005-09-20 21:26:39 +08:00
{
2012-02-18 02:52:30 +08:00
var_entry_t * v = result - > second ;
2011-12-27 11:18:46 +08:00
if ( v - > exportv )
2005-09-20 21:26:39 +08:00
{
2012-08-15 15:57:56 +08:00
mark_changed_exported ( ) ;
2005-09-20 21:26:39 +08:00
}
2012-02-18 04:23:30 +08:00
2012-02-18 06:54:58 +08:00
n - > env . erase ( result ) ;
2012-02-18 04:23:30 +08:00
delete v ;
2005-09-20 21:26:39 +08:00
return 1 ;
}
2006-06-05 04:14:51 +08:00
if ( var_mode & ENV_LOCAL )
2007-01-21 23:01:14 +08:00
{
2006-06-05 04:14:51 +08:00
return 0 ;
2007-01-21 23:01:14 +08:00
}
2011-12-27 11:18:46 +08:00
2005-09-20 21:26:39 +08:00
if ( n - > new_scope )
2007-01-21 23:01:14 +08:00
{
2006-06-05 04:14:51 +08:00
return try_remove ( global_env , key , var_mode ) ;
2007-01-21 23:01:14 +08:00
}
2005-09-20 21:26:39 +08:00
else
2007-01-21 23:01:14 +08:00
{
2006-06-05 04:14:51 +08:00
return try_remove ( n - > next , key , var_mode ) ;
2007-01-21 23:01:14 +08:00
}
2005-09-20 21:26:39 +08:00
}
2012-03-06 06:18:16 +08:00
int env_remove ( const wcstring & key , int var_mode )
2005-09-20 21:26:39 +08:00
{
2012-03-06 06:18:16 +08:00
ASSERT_IS_MAIN_THREAD ( ) ;
2006-06-05 04:14:51 +08:00
env_node_t * first_node ;
int erased = 0 ;
2011-12-27 11:18:46 +08:00
if ( ( var_mode & ENV_USER ) & & is_read_only ( key ) )
2005-09-20 21:26:39 +08:00
{
2006-06-05 04:14:51 +08:00
return 2 ;
2005-09-20 21:26:39 +08:00
}
2011-12-27 11:18:46 +08:00
2006-06-05 04:14:51 +08:00
first_node = top ;
2011-12-27 11:18:46 +08:00
2006-06-05 04:14:51 +08:00
if ( ! ( var_mode & ENV_UNIVERSAL ) )
{
2011-12-27 11:18:46 +08:00
2006-06-05 04:14:51 +08:00
if ( var_mode & ENV_GLOBAL )
{
first_node = global_env ;
}
2011-12-27 11:18:46 +08:00
2012-03-06 06:18:16 +08:00
if ( try_remove ( first_node , key . c_str ( ) , var_mode ) )
2011-12-27 11:18:46 +08:00
{
2012-02-09 11:02:25 +08:00
event_t ev = event_t : : variable_event ( key ) ;
ev . arguments . reset ( new wcstring_list_t ) ;
2011-12-27 16:06:07 +08:00
ev . arguments - > push_back ( L " VARIABLE " ) ;
ev . arguments - > push_back ( L " ERASE " ) ;
ev . arguments - > push_back ( key ) ;
2011-12-27 11:18:46 +08:00
event_fire ( & ev ) ;
2012-02-09 11:02:25 +08:00
ev . arguments . reset ( NULL ) ;
2006-06-05 04:14:51 +08:00
erased = 1 ;
}
2006-05-29 21:12:17 +08:00
}
2011-12-27 11:18:46 +08:00
if ( ! erased & &
2006-06-05 04:14:51 +08:00
! ( var_mode & ENV_GLOBAL ) & &
2011-12-27 11:18:46 +08:00
! ( var_mode & ENV_LOCAL ) )
2005-09-20 21:26:39 +08:00
{
2012-03-06 06:18:16 +08:00
erased = ! env_universal_remove ( key . c_str ( ) ) ;
2005-09-20 21:26:39 +08:00
}
2006-01-09 07:00:49 +08:00
2012-03-06 06:18:16 +08:00
react_to_variable_change ( key ) ;
2011-12-27 11:18:46 +08:00
return ! erased ;
2005-09-20 21:26:39 +08:00
}
2012-01-14 17:06:47 +08:00
env_var_t env_var_t : : missing_var ( void ) {
env_var_t result ( L " " ) ;
result . is_missing = true ;
return result ;
}
const wchar_t * env_var_t : : c_str ( void ) const {
assert ( ! is_missing ) ;
return wcstring : : c_str ( ) ;
}
2012-02-24 03:25:46 +08:00
env_var_t env_get_string ( const wcstring & key )
2012-03-20 02:52:18 +08: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-04-01 06:33:34 +08:00
const bool is_main = is_main_thread ( ) ;
if ( key = = L " history " & & is_main )
2011-12-27 11:18:46 +08:00
{
2012-03-20 02:52:18 +08:00
env_var_t result ;
2011-12-27 11:18:46 +08:00
2012-03-20 02:52:18 +08:00
history_t * history = reader_get_history ( ) ;
if ( ! history ) {
history = & history_t : : history_with_name ( L " fish " ) ;
}
if ( history )
history - > get_string_representation ( result , ARRAY_SEP_STR ) ;
2011-12-27 11:18:46 +08:00
return result ;
}
2012-02-24 03:25:46 +08:00
else if ( key = = L " COLUMNS " )
2011-12-27 11:18:46 +08:00
{
2012-08-05 06:11:43 +08:00
return to_string ( common_get_width ( ) ) ;
2011-12-27 11:18:46 +08:00
}
2012-02-24 03:25:46 +08:00
else if ( key = = L " LINES " )
2011-12-27 11:18:46 +08:00
{
2012-08-05 06:11:43 +08:00
return to_string ( common_get_width ( ) ) ;
2011-12-27 11:18:46 +08:00
}
2012-02-24 03:25:46 +08:00
else if ( key = = L " status " )
2011-12-27 11:18:46 +08:00
{
2012-08-05 06:11:43 +08:00
return to_string ( proc_get_last_status ( ) ) ;
2011-12-27 11:18:46 +08:00
}
2012-02-24 03:25:46 +08:00
else if ( key = = L " umask " )
2011-12-27 11:18:46 +08:00
{
return format_string ( L " 0%0.3o " , get_umask ( ) ) ;
}
2012-04-24 02:08:29 +08:00
else
{
2012-04-01 06:33:34 +08:00
{
2012-04-24 02:08:29 +08:00
/* Lock around a local region */
scoped_lock lock ( env_lock ) ;
2011-12-27 11:18:46 +08:00
2012-04-24 02:08:29 +08:00
var_entry_t * res = NULL ;
env_node_t * env = top ;
wcstring result ;
2012-04-01 06:33:34 +08:00
2012-04-24 02:08:29 +08:00
while ( env ! = NULL )
2012-04-01 06:33:34 +08:00
{
2012-04-24 02:08:29 +08:00
var_table_t : : iterator result = env - > env . find ( key ) ;
if ( result ! = env - > env . end ( ) )
res = result - > second ;
if ( res ! = NULL )
2012-04-01 06:33:34 +08:00
{
2012-04-24 02:08:29 +08:00
if ( res - > val = = ENV_NULL )
{
return env_var_t : : missing_var ( ) ;
}
else
{
return res - > val ;
}
}
if ( env - > new_scope )
{
env = global_env ;
2012-04-01 06:33:34 +08:00
}
else
{
2012-04-24 02:08:29 +08:00
env = env - > next ;
2012-04-01 06:33:34 +08:00
}
}
}
2012-04-24 02:08:29 +08: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-04-01 06:33:34 +08:00
if ( is_main & & ! get_proc_had_barrier ( ) )
2011-12-27 11:18:46 +08:00
{
2012-04-01 06:33:34 +08:00
set_proc_had_barrier ( true ) ;
2011-12-27 11:18:46 +08:00
env_universal_barrier ( ) ;
}
2012-04-24 02:08:29 +08:00
wchar_t * item = env_universal_get ( key ) ;
2011-12-27 11:18:46 +08:00
if ( ! item | | ( wcscmp ( item , ENV_NULL ) = = 0 ) )
{
2012-01-14 17:06:47 +08:00
return env_var_t : : missing_var ( ) ;
2011-12-27 11:18:46 +08:00
}
else
{
return item ;
}
}
}
2005-09-20 21:26:39 +08:00
2006-06-05 04:14:51 +08:00
int env_exist ( const wchar_t * key , int mode )
2005-09-26 22:47:03 +08:00
{
var_entry_t * res ;
2006-06-05 04:14:51 +08:00
env_node_t * env ;
wchar_t * item = 0 ;
2006-06-21 08:48:36 +08:00
CHECK ( key , 0 ) ;
2011-12-27 11:18:46 +08:00
2006-06-05 04:14:51 +08:00
/*
Read only variables all exist , and they are all global . A local
2006-06-09 07:52:12 +08:00
version can not exist .
2006-06-05 04:14:51 +08:00
*/
if ( ! ( mode & ENV_LOCAL ) & & ! ( mode & ENV_UNIVERSAL ) )
2005-09-26 22:47:03 +08:00
{
2011-12-27 11:18:46 +08:00
if ( is_read_only ( key ) | | is_electric ( key ) )
2005-09-26 22:47:03 +08:00
{
2012-06-19 01:20:40 +08:00
//Such variables are never exported
if ( mode & ENV_EXPORT )
{
return 0 ;
}
else if ( mode & ENV_UNEXPORT )
{
return 1 ;
}
2005-09-26 22:47:03 +08:00
return 1 ;
}
2006-06-05 04:14:51 +08:00
}
2012-06-19 01:20:40 +08:00
if ( ! ( mode & ENV_UNIVERSAL ) )
2005-11-28 07:22:08 +08:00
{
2006-06-05 04:14:51 +08:00
env = ( mode & ENV_GLOBAL ) ? global_env : top ;
2011-12-27 11:18:46 +08:00
2006-06-05 04:14:51 +08:00
while ( env ! = 0 )
{
2012-02-29 07:11:46 +08:00
var_table_t : : iterator result = env - > env . find ( key ) ;
2012-06-19 01:20:40 +08:00
2012-02-18 02:52:30 +08:00
if ( result ! = env - > env . end ( ) )
{
res = result - > second ;
2012-06-19 01:20:40 +08:00
if ( mode & ENV_EXPORT )
{
return res - > exportv = = 1 ;
}
else if ( mode & ENV_UNEXPORT )
{
return res - > exportv = = 0 ;
}
return 1 ;
2007-01-21 23:01:14 +08:00
}
2012-06-19 01:20:40 +08:00
if ( mode & ENV_LOCAL )
break ;
2006-06-05 04:14:51 +08:00
if ( env - > new_scope )
2007-01-21 23:01:14 +08:00
{
2006-06-05 04:14:51 +08:00
env = global_env ;
2007-01-21 23:01:14 +08:00
}
2006-06-05 04:14:51 +08:00
else
2007-01-21 23:01:14 +08:00
{
2006-06-05 04:14:51 +08:00
env = env - > next ;
2007-01-21 23:01:14 +08:00
}
2011-12-27 11:18:46 +08:00
}
2005-11-28 07:22:08 +08:00
}
2012-06-19 01:20:40 +08:00
if ( ! ( mode & ENV_LOCAL ) & & ! ( mode & ENV_GLOBAL ) )
2006-06-05 04:14:51 +08:00
{
2012-04-01 06:33:34 +08:00
if ( ! get_proc_had_barrier ( ) )
2006-06-05 04:14:51 +08:00
{
2012-04-01 06:33:34 +08:00
set_proc_had_barrier ( true ) ;
2006-06-05 04:14:51 +08:00
env_universal_barrier ( ) ;
}
2011-12-27 11:18:46 +08:00
2006-06-05 04:14:51 +08:00
item = env_universal_get ( key ) ;
2012-06-19 01:20:40 +08:00
if ( item ! = NULL )
{
if ( mode & ENV_EXPORT )
{
return env_universal_get_export ( key ) = = 1 ;
}
else if ( mode & ENV_UNEXPORT )
{
return env_universal_get_export ( key ) = = 0 ;
}
return 1 ;
}
2006-06-05 04:14:51 +08:00
}
2012-06-19 01:20:40 +08:00
return 0 ;
2005-09-20 21:26:39 +08:00
}
2005-10-24 23:26:25 +08:00
/**
Returns true if the specified scope or any non - shadowed non - global subscopes contain an exported variable .
*/
2005-09-20 21:26:39 +08:00
static int local_scope_exports ( env_node_t * n )
{
2011-12-27 11:18:46 +08:00
2005-09-20 21:26:39 +08:00
if ( n = = global_env )
return 0 ;
2011-12-27 11:18:46 +08:00
if ( n - > exportv )
2005-09-20 21:26:39 +08:00
return 1 ;
2011-12-27 11:18:46 +08:00
2005-09-20 21:26:39 +08:00
if ( n - > new_scope )
return 0 ;
2011-12-27 11:18:46 +08:00
2005-09-20 21:26:39 +08:00
return local_scope_exports ( n - > next ) ;
}
void env_push ( int new_scope )
{
2012-02-18 02:52:30 +08:00
env_node_t * node = new env_node_t ;
2005-09-20 21:26:39 +08:00
node - > next = top ;
node - > new_scope = new_scope ;
2012-02-18 02:52:30 +08:00
2005-09-20 21:26:39 +08:00
if ( new_scope )
{
2012-08-15 15:57:56 +08:00
if ( local_scope_exports ( top ) )
mark_changed_exported ( ) ;
2005-09-20 21:26:39 +08:00
}
2011-12-27 11:18:46 +08:00
top = node ;
2005-09-20 21:26:39 +08:00
}
void env_pop ( )
{
if ( & top - > env ! = global )
{
2006-01-13 09:00:12 +08:00
int i ;
int locale_changed = 0 ;
2011-12-27 11:18:46 +08:00
2005-09-20 21:26:39 +08:00
env_node_t * killme = top ;
2006-01-13 09:00:12 +08:00
for ( i = 0 ; locale_variable [ i ] ; i + + )
{
2012-02-29 07:11:46 +08:00
var_table_t : : iterator result = killme - > env . find ( locale_variable [ i ] ) ;
2012-02-18 02:52:30 +08:00
if ( result ! = killme - > env . end ( ) )
2006-01-13 09:00:12 +08:00
{
locale_changed = 1 ;
break ;
}
}
2005-09-20 21:26:39 +08:00
if ( killme - > new_scope )
{
2012-08-15 15:57:56 +08:00
if ( killme - > exportv | | local_scope_exports ( killme - > next ) )
mark_changed_exported ( ) ;
2005-09-20 21:26:39 +08:00
}
2011-12-27 11:18:46 +08:00
2005-09-20 21:26:39 +08:00
top = top - > next ;
2012-02-18 02:52:30 +08:00
2012-02-29 07:11:46 +08:00
var_table_t : : iterator iter ;
2012-02-18 02:52:30 +08:00
for ( iter = killme - > env . begin ( ) ; iter ! = killme - > env . end ( ) ; + + iter )
{
var_entry_t * entry = iter - > second ;
if ( entry - > exportv )
{
2012-08-15 15:57:56 +08:00
mark_changed_exported ( ) ;
2012-02-18 02:52:30 +08:00
}
delete entry ;
}
delete killme ;
2006-01-13 09:00:12 +08:00
if ( locale_changed )
handle_locale ( ) ;
2011-12-27 11:18:46 +08:00
2005-09-20 21:26:39 +08:00
}
else
{
debug ( 0 ,
2006-01-04 20:51:02 +08:00
_ ( L " Tried to pop empty environment stack. " ) ) ;
2005-09-20 21:26:39 +08:00
sanity_lose ( ) ;
2011-12-27 11:18:46 +08:00
}
2005-09-20 21:26:39 +08:00
}
2012-01-11 04:51:09 +08:00
/**
2012-02-18 23:34:09 +08:00
Function used with to insert keys of one table into a set : : set < wcstring >
2012-01-11 04:51:09 +08:00
*/
2012-02-29 07:11:46 +08:00
static void add_key_to_string_set ( const var_table_t & envs , std : : set < wcstring > & strSet )
2012-02-18 02:52:30 +08:00
{
2012-02-29 07:11:46 +08:00
var_table_t : : const_iterator iter ;
2012-02-18 02:52:30 +08:00
for ( iter = envs . begin ( ) ; iter ! = envs . end ( ) ; + + iter )
{
var_entry_t * e = iter - > second ;
if ( ( e - > exportv & & get_names_show_exported ) | |
( ! e - > exportv & & get_names_show_unexported ) )
{
/*Insert Key*/
strSet . insert ( iter - > first ) ;
}
}
}
2012-01-11 04:51:09 +08:00
wcstring_list_t env_get_names ( int flags )
{
2012-02-26 10:54:49 +08:00
scoped_lock lock ( env_lock ) ;
2012-01-11 04:51:09 +08:00
wcstring_list_t result ;
std : : set < wcstring > names ;
int show_local = flags & ENV_LOCAL ;
int show_global = flags & ENV_GLOBAL ;
int show_universal = flags & ENV_UNIVERSAL ;
env_node_t * n = top ;
get_names_show_exported =
2012-02-26 10:54:49 +08:00
( flags & ENV_EXPORT ) | | ! ( flags & ENV_UNEXPORT ) ;
2012-01-11 04:51:09 +08:00
get_names_show_unexported =
2012-02-26 10:54:49 +08:00
( flags & ENV_UNEXPORT ) | | ! ( flags & ENV_EXPORT ) ;
2012-01-11 04:51:09 +08:00
if ( ! show_local & & ! show_global & & ! show_universal )
{
show_local = show_universal = show_global = 1 ;
}
if ( show_local )
{
while ( n )
{
if ( n = = global_env )
break ;
2012-02-18 02:52:30 +08:00
add_key_to_string_set ( n - > env , names ) ;
2012-01-11 04:51:09 +08:00
if ( n - > new_scope )
break ;
else
n = n - > next ;
}
}
if ( show_global )
{
2012-02-18 02:52:30 +08:00
add_key_to_string_set ( global_env - > env , names ) ;
2012-01-11 04:51:09 +08:00
if ( get_names_show_unexported ) {
result . insert ( result . end ( ) , env_electric . begin ( ) , env_electric . end ( ) ) ;
}
if ( get_names_show_exported )
{
result . push_back ( L " COLUMNS " ) ;
result . push_back ( L " LINES " ) ;
}
}
if ( show_universal )
{
wcstring_list_t uni_list ;
env_universal_get_names2 ( uni_list ,
get_names_show_exported ,
get_names_show_unexported ) ;
names . insert ( uni_list . begin ( ) , uni_list . end ( ) ) ;
}
result . insert ( result . end ( ) , names . begin ( ) , names . end ( ) ) ;
return result ;
}
2005-10-03 21:24:46 +08:00
/**
2012-02-18 02:52:30 +08:00
Get list of all exported variables
2005-10-03 21:24:46 +08:00
*/
2006-11-26 21:09:43 +08:00
2012-02-29 07:11:46 +08:00
static void get_exported ( const env_node_t * n , std : : map < wcstring , wcstring > & h )
2006-11-26 21:09:43 +08:00
{
if ( ! n )
return ;
2011-12-27 11:18:46 +08:00
2006-11-26 21:09:43 +08:00
if ( n - > new_scope )
2012-02-29 07:11:46 +08:00
get_exported ( global_env , h ) ;
2006-11-26 21:09:43 +08:00
else
2012-02-29 07:11:46 +08:00
get_exported ( n - > next , h ) ;
2006-11-26 21:09:43 +08:00
2012-02-29 07:11:46 +08:00
var_table_t : : const_iterator iter ;
2012-02-18 02:52:30 +08:00
for ( iter = n - > env . begin ( ) ; iter ! = n - > env . end ( ) ; + + iter )
{
2012-02-18 04:23:30 +08:00
const wcstring & key = iter - > first ;
2012-02-18 02:52:30 +08:00
var_entry_t * val_entry = iter - > second ;
if ( val_entry - > exportv & & ( val_entry - > val ! = ENV_NULL ) )
{
2012-06-17 04:05:58 +08:00
// Don't use std::map::insert here, since we need to overwrite existing values from previous scopes
h [ key ] = val_entry - > val ;
2012-02-18 02:52:30 +08:00
}
}
}
2006-11-26 21:09:43 +08:00
2012-02-29 07:11:46 +08:00
static void export_func ( const std : : map < wcstring , wcstring > & envs , std : : vector < std : : string > & out )
2005-09-23 04:16:52 +08:00
{
2012-02-18 02:52:30 +08:00
std : : map < wcstring , wcstring > : : const_iterator iter ;
for ( iter = envs . begin ( ) ; iter ! = envs . end ( ) ; + + iter )
2005-09-23 04:16:52 +08:00
{
2012-02-18 02:52:30 +08:00
char * ks = wcs2str ( iter - > first . c_str ( ) ) ;
char * vs = wcs2str ( iter - > second . c_str ( ) ) ;
char * pos = vs ;
while ( * pos )
{
if ( * pos = = ARRAY_SEP )
* pos = ' : ' ;
pos + + ;
}
2012-02-29 07:11:46 +08:00
/* Put a string on the vector */
out . push_back ( std : : string ( ) ) ;
std : : string & str = out . back ( ) ;
/* Append our environment variable data to it */
str . append ( ks ) ;
str . append ( " = " ) ;
str . append ( vs ) ;
2012-02-18 02:52:30 +08:00
free ( ks ) ;
free ( vs ) ;
2012-02-29 07:11:46 +08:00
}
2005-09-23 04:16:52 +08:00
}
2012-02-29 07:11:46 +08:00
static void update_export_array_if_necessary ( bool recalc ) {
ASSERT_IS_MAIN_THREAD ( ) ;
2012-04-01 06:33:34 +08:00
if ( recalc & & ! get_proc_had_barrier ( ) )
2005-11-28 07:22:08 +08:00
{
2012-04-01 06:33:34 +08:00
set_proc_had_barrier ( true ) ;
2005-09-23 04:16:52 +08:00
env_universal_barrier ( ) ;
2005-11-28 07:22:08 +08:00
}
2011-12-27 11:18:46 +08:00
2012-08-15 15:57:56 +08:00
if ( has_changed_exported )
2005-09-23 04:16:52 +08:00
{
2012-02-18 02:52:30 +08:00
std : : map < wcstring , wcstring > vals ;
2012-01-14 17:06:47 +08:00
size_t i ;
2005-09-23 04:16:52 +08:00
2005-09-26 22:47:03 +08:00
debug ( 4 , L " env_export_arr() recalc " ) ;
2011-12-27 11:18:46 +08:00
2012-02-29 07:11:46 +08:00
get_exported ( top , vals ) ;
2011-12-27 11:18:46 +08:00
2011-12-29 04:36:47 +08:00
wcstring_list_t uni ;
env_universal_get_names2 ( uni , 1 , 0 ) ;
for ( i = 0 ; i < uni . size ( ) ; i + + )
2005-09-23 04:16:52 +08:00
{
2012-02-18 02:52:30 +08:00
const wcstring & key = uni . at ( i ) ;
const wchar_t * val = env_universal_get ( key . c_str ( ) ) ;
2012-06-17 04:05:58 +08:00
if ( wcscmp ( val , ENV_NULL ) ) {
// Note that std::map::insert does NOT overwrite a value already in the map,
// which we depend on here
2012-02-18 02:52:30 +08:00
vals . insert ( std : : pair < wcstring , wcstring > ( key , val ) ) ;
}
2005-09-23 04:16:52 +08:00
}
2012-02-29 07:11:46 +08:00
std : : vector < std : : string > local_export_buffer ;
export_func ( vals , local_export_buffer ) ;
export_array . set ( local_export_buffer ) ;
2012-08-15 15:57:56 +08:00
has_changed_exported = false ;
2012-02-29 07:11:46 +08:00
}
2005-09-23 04:16:52 +08:00
2012-02-29 07:11:46 +08:00
}
2005-09-23 04:16:52 +08:00
2012-08-15 15:57:56 +08:00
char * * env_export_arr ( bool recalc )
2012-02-29 07:11:46 +08:00
{
ASSERT_IS_MAIN_THREAD ( ) ;
2012-08-15 15:57:56 +08:00
update_export_array_if_necessary ( recalc ) ;
2012-02-29 07:11:46 +08:00
return export_array . get ( ) ;
}
void env_export_arr ( bool recalc , null_terminated_array_t < char > & output )
{
ASSERT_IS_MAIN_THREAD ( ) ;
2012-08-15 15:57:56 +08:00
update_export_array_if_necessary ( recalc ) ;
2012-02-29 07:11:46 +08:00
output = export_array ;
2005-09-20 21:26:39 +08:00
}
2011-12-27 11:18:46 +08:00
2012-07-21 11:39:31 +08:00
env_vars_snapshot_t : : env_vars_snapshot_t ( const wchar_t * const * keys )
2011-12-27 11:18:46 +08:00
{
ASSERT_IS_MAIN_THREAD ( ) ;
2012-07-21 13:11:05 +08:00
wcstring key ;
2011-12-27 11:18:46 +08:00
for ( size_t i = 0 ; keys [ i ] ; i + + ) {
2012-07-21 13:11:05 +08:00
key . assign ( keys [ i ] ) ;
const env_var_t val = env_get_string ( key ) ;
if ( ! val . missing ( ) ) {
vars [ key ] = val ;
2011-12-27 11:18:46 +08:00
}
}
}
2012-07-21 11:39:31 +08:00
env_vars_snapshot_t : : env_vars_snapshot_t ( ) { }
2011-12-27 11:18:46 +08:00
2012-07-21 13:11:05 +08: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 11:18:46 +08:00
{
2012-07-21 13:11:05 +08:00
return sCurrentSnapshot ;
2011-12-27 11:18:46 +08:00
}
2012-07-21 13:11:05 +08:00
bool env_vars_snapshot_t : : is_current ( ) const
2012-07-21 11:39:31 +08:00
{
2012-07-21 13:11:05 +08:00
return this = = & sCurrentSnapshot ;
2012-07-21 11:39:31 +08:00
}
2012-07-21 13:11:05 +08: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 ) ;
}
else {
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-07 05:53:19 +08:00
2012-07-21 13:11:05 +08:00
const wchar_t * const env_vars_snapshot_t : : highlighting_keys [ ] = { L " PATH " , L " CDPATH " , L " fish_function_path " , NULL } ;