2014-02-28 18:15:24 +08:00
/** \file function.c
2006-11-15 22:16:49 +08:00
Prototypes for functions for storing and retrieving function
2012-11-18 18:23:22 +08:00
information . These functions also take care of autoloading
functions in the $ fish_function_path . Actual function evaluation
is taken care of by the parser and to some degree the builtin
handling library .
2005-09-20 21:26:39 +08:00
*/
2006-08-11 09:18:35 +08:00
2015-07-25 23:14:25 +08:00
# include "config.h" // IWYU pragma: keep
2006-08-11 09:18:35 +08:00
2005-09-20 21:26:39 +08:00
# include <wchar.h>
2011-12-27 11:18:46 +08:00
# include <pthread.h>
2012-01-14 15:44:18 +08:00
# include <map>
# include <set>
2015-07-25 23:14:25 +08:00
# include <dirent.h>
# include <stddef.h>
# include <string>
# include <utility>
2005-09-20 21:26:39 +08:00
2006-01-20 22:27:21 +08:00
# include "wutil.h"
2015-07-25 23:14:25 +08:00
# include "fallback.h" // IWYU pragma: keep
2006-02-28 21:17:16 +08:00
2015-07-25 23:14:25 +08:00
# include "autoload.h"
2005-09-20 21:26:39 +08:00
# include "function.h"
# include "common.h"
# include "intern.h"
2005-10-06 06:37:08 +08:00
# include "event.h"
2006-01-26 22:48:10 +08:00
# include "reader.h"
2007-04-22 17:50:26 +08:00
# include "parser_keywords.h"
2006-02-08 17:20:05 +08:00
# include "env.h"
2005-09-20 21:26:39 +08:00
2005-10-06 06:37:08 +08:00
/**
Table containing all functions
*/
2012-05-19 05:00:36 +08:00
typedef std : : map < wcstring , function_info_t > function_map_t ;
2012-01-14 15:44:18 +08:00
static function_map_t loaded_functions ;
2011-12-27 11:18:46 +08:00
2015-03-11 21:14:56 +08:00
/**
Functions that shouldn ' t be autoloaded ( anymore ) .
*/
static std : : set < wcstring > function_tombstones ;
2011-12-27 11:18:46 +08:00
/* Lock for functions */
static pthread_mutex_t functions_lock ;
2012-01-26 10:40:08 +08:00
/* Autoloader for functions */
2012-11-19 08:30:30 +08:00
class function_autoload_t : public autoload_t
{
2012-01-26 10:40:08 +08:00
public :
function_autoload_t ( ) ;
virtual void command_removed ( const wcstring & cmd ) ;
} ;
static function_autoload_t function_autoloader ;
/** Constructor */
2012-12-09 13:41:38 +08:00
function_autoload_t : : function_autoload_t ( ) : autoload_t ( L " fish_function_path " , NULL , 0 )
2012-01-26 10:40:08 +08:00
{
}
2015-04-08 17:58:29 +08:00
static bool function_remove_ignore_autoload ( const wcstring & name , bool tombstone = true ) ;
2015-03-11 21:14:56 +08:00
2012-01-26 10:40:08 +08:00
/** Callback when an autoloaded function is removed */
2012-11-19 08:30:30 +08:00
void function_autoload_t : : command_removed ( const wcstring & cmd )
{
2015-04-08 17:58:29 +08:00
function_remove_ignore_autoload ( cmd , false ) ;
2012-01-26 10:40:08 +08:00
}
2006-06-17 21:07:08 +08:00
/**
Kludgy flag set by the load function in order to tell function_add
that the function being defined is autoloaded . There should be a
better way to do this . . .
*/
2012-03-04 07:20:30 +08:00
static bool is_autoload = false ;
2006-02-08 17:20:05 +08:00
2006-02-09 01:37:18 +08:00
/**
Make sure that if the specified function is a dynamically loaded
function , it has been fully loaded .
*/
2012-11-19 08:30:30 +08:00
static int load ( const wcstring & name )
2006-02-08 17:20:05 +08:00
{
2011-12-27 13:56:23 +08:00
ASSERT_IS_MAIN_THREAD ( ) ;
2012-01-24 12:32:36 +08:00
scoped_lock lock ( functions_lock ) ;
2012-11-19 08:30:30 +08:00
bool was_autoload = is_autoload ;
int res ;
2015-03-11 21:14:56 +08:00
2015-04-06 23:38:18 +08:00
bool no_more_autoload = function_tombstones . count ( name ) > 0 ;
2015-03-11 21:14:56 +08:00
if ( no_more_autoload )
return 0 ;
2012-01-14 15:44:18 +08:00
function_map_t : : iterator iter = loaded_functions . find ( name ) ;
2012-11-19 08:30:30 +08:00
if ( iter ! = loaded_functions . end ( ) & & ! iter - > second . is_autoload )
{
2012-01-29 06:56:13 +08:00
/* We have a non-autoload version already */
2012-11-19 08:30:30 +08:00
return 0 ;
2011-12-27 11:18:46 +08:00
}
2012-11-18 18:23:22 +08:00
2012-11-19 08:30:30 +08:00
is_autoload = true ;
res = function_autoloader . load ( name , true ) ;
is_autoload = was_autoload ;
return res ;
2006-02-08 17:20:05 +08:00
}
2006-02-09 01:37:18 +08:00
/**
Insert a list of all dynamically loaded functions into the
specified list .
*/
2012-11-19 08:30:30 +08:00
static void autoload_names ( std : : set < wcstring > & names , int get_hidden )
2006-02-09 01:37:18 +08:00
{
2012-11-19 08:30:30 +08:00
size_t i ;
2012-11-18 18:23:22 +08:00
2012-11-19 08:30:30 +08:00
const env_var_t path_var_wstr = env_get_string ( L " fish_function_path " ) ;
2012-01-14 18:42:17 +08:00
if ( path_var_wstr . missing ( ) )
return ;
2012-11-19 08:30:30 +08:00
const wchar_t * path_var = path_var_wstr . c_str ( ) ;
2012-11-18 18:23:22 +08:00
2012-01-11 04:51:09 +08:00
wcstring_list_t path_list ;
2006-02-09 01:37:18 +08:00
2012-11-19 08:30:30 +08:00
tokenize_variable_array ( path_var , path_list ) ;
for ( i = 0 ; i < path_list . size ( ) ; i + + )
2012-11-18 18:23:22 +08:00
{
2012-11-19 08:30:30 +08:00
const wcstring & ndir_str = path_list . at ( i ) ;
const wchar_t * ndir = ( wchar_t * ) ndir_str . c_str ( ) ;
DIR * dir = wopendir ( ndir ) ;
if ( ! dir )
continue ;
wcstring name ;
while ( wreaddir ( dir , name ) )
{
const wchar_t * fn = name . c_str ( ) ;
const wchar_t * suffix ;
if ( ! get_hidden & & fn [ 0 ] = = L ' _ ' )
continue ;
suffix = wcsrchr ( fn , L ' . ' ) ;
if ( suffix & & ( wcscmp ( suffix , L " .fish " ) = = 0 ) )
{
2012-01-14 15:44:18 +08:00
wcstring name ( fn , suffix - fn ) ;
names . insert ( name ) ;
2012-11-19 08:30:30 +08:00
}
}
closedir ( dir ) ;
2012-11-18 18:23:22 +08:00
}
2006-02-08 17:20:05 +08:00
}
2005-09-20 21:26:39 +08:00
void function_init ( )
{
2012-05-19 05:00:36 +08:00
/* PCA: This recursive lock was introduced early in my work. I would like to make this a non-recursive lock but I haven't fully investigated all the call paths (for autoloading functions, etc.) */
2011-12-27 11:18:46 +08:00
pthread_mutexattr_t a ;
VOMIT_ON_FAILURE ( pthread_mutexattr_init ( & a ) ) ;
VOMIT_ON_FAILURE ( pthread_mutexattr_settype ( & a , PTHREAD_MUTEX_RECURSIVE ) ) ;
VOMIT_ON_FAILURE ( pthread_mutex_init ( & functions_lock , & a ) ) ;
VOMIT_ON_FAILURE ( pthread_mutexattr_destroy ( & a ) ) ;
2005-09-20 21:26:39 +08:00
}
2014-10-03 06:59:24 +08:00
static std : : map < wcstring , env_var_t > snapshot_vars ( const wcstring_list_t & vars )
{
std : : map < wcstring , env_var_t > result ;
for ( wcstring_list_t : : const_iterator it = vars . begin ( ) , end = vars . end ( ) ; it ! = end ; + + it )
{
result . insert ( std : : make_pair ( * it , env_get_string ( * it ) ) ) ;
}
return result ;
}
2012-03-04 07:20:30 +08:00
function_info_t : : function_info_t ( const function_data_t & data , const wchar_t * filename , int def_offset , bool autoload ) :
definition ( data . definition ) ,
description ( data . description ) ,
definition_file ( intern ( filename ) ) ,
definition_offset ( def_offset ) ,
named_arguments ( data . named_arguments ) ,
2014-10-03 06:59:24 +08:00
inherit_vars ( snapshot_vars ( data . inherit_vars ) ) ,
2012-03-04 07:20:30 +08:00
is_autoload ( autoload ) ,
shadows ( data . shadows )
{
}
2006-01-26 22:48:10 +08:00
2012-03-04 07:20:30 +08:00
function_info_t : : function_info_t ( const function_info_t & data , const wchar_t * filename , int def_offset , bool autoload ) :
definition ( data . definition ) ,
description ( data . description ) ,
definition_file ( intern ( filename ) ) ,
definition_offset ( def_offset ) ,
named_arguments ( data . named_arguments ) ,
2014-10-03 06:59:24 +08:00
inherit_vars ( data . inherit_vars ) ,
2012-03-04 07:20:30 +08:00
is_autoload ( autoload ) ,
shadows ( data . shadows )
2005-09-20 21:26:39 +08:00
{
2012-03-04 07:20:30 +08:00
}
2014-03-17 07:45:00 +08:00
void function_add ( const function_data_t & data , const parser_t & parser , int definition_line_offset )
2012-03-04 07:20:30 +08:00
{
ASSERT_IS_MAIN_THREAD ( ) ;
2012-11-18 18:23:22 +08:00
2012-11-19 08:30:30 +08:00
CHECK ( ! data . name . empty ( ) , ) ;
CHECK ( data . definition , ) ;
scoped_lock lock ( functions_lock ) ;
2012-11-18 18:23:22 +08:00
2012-03-04 07:20:30 +08:00
/* Remove the old function */
2012-11-19 08:30:30 +08:00
function_remove ( data . name ) ;
2012-11-18 18:23:22 +08:00
2012-05-19 05:00:36 +08:00
/* Create and store a new function */
2012-03-04 07:20:30 +08:00
const wchar_t * filename = reader_current_filename ( ) ;
2013-12-21 09:44:37 +08:00
2014-03-17 07:45:00 +08:00
const function_map_t : : value_type new_pair ( data . name , function_info_t ( data , filename , definition_line_offset , is_autoload ) ) ;
2012-05-19 05:00:36 +08:00
loaded_functions . insert ( new_pair ) ;
2012-11-18 18:23:22 +08:00
2012-03-04 07:20:30 +08:00
/* Add event handlers */
2012-11-19 08:30:30 +08:00
for ( std : : vector < event_t > : : const_iterator iter = data . events . begin ( ) ; iter ! = data . events . end ( ) ; + + iter )
{
2012-12-20 18:48:36 +08:00
event_add_handler ( * iter ) ;
2012-11-19 08:30:30 +08:00
}
2005-09-20 21:26:39 +08:00
}
2012-11-19 08:30:30 +08:00
int function_exists ( const wcstring & cmd )
2010-09-08 01:31:05 +08:00
{
2012-11-19 08:30:30 +08:00
if ( parser_keywords_is_reserved ( cmd ) )
return 0 ;
2012-01-24 12:32:36 +08:00
scoped_lock lock ( functions_lock ) ;
2012-01-29 06:56:13 +08:00
load ( cmd ) ;
2012-11-18 18:23:22 +08:00
return loaded_functions . find ( cmd ) ! = loaded_functions . end ( ) ;
2011-12-27 11:18:46 +08:00
}
2010-09-18 09:51:16 +08:00
2012-11-19 08:30:30 +08:00
int function_exists_no_autoload ( const wcstring & cmd , const env_vars_snapshot_t & vars )
2011-12-27 11:18:46 +08:00
{
2012-11-19 08:30:30 +08:00
if ( parser_keywords_is_reserved ( cmd ) )
return 0 ;
2012-01-29 06:56:13 +08:00
scoped_lock lock ( functions_lock ) ;
2012-11-18 18:23:22 +08:00
return loaded_functions . find ( cmd ) ! = loaded_functions . end ( ) | | function_autoloader . can_load ( cmd , vars ) ;
2005-09-20 21:26:39 +08:00
}
2015-04-08 17:58:29 +08:00
static bool function_remove_ignore_autoload ( const wcstring & name , bool tombstone )
2005-09-20 21:26:39 +08:00
{
2015-04-06 23:38:18 +08:00
// Note: the lock may be held at this point, but is recursive
2012-01-24 12:32:36 +08:00
scoped_lock lock ( functions_lock ) ;
2012-11-18 18:23:22 +08:00
2015-03-11 21:14:56 +08:00
function_map_t : : iterator iter = loaded_functions . find ( name ) ;
// not found. not erasing.
if ( iter = = loaded_functions . end ( ) )
return false ;
// removing an auto-loaded function. prevent it from being
// auto-reloaded.
2015-04-08 17:58:29 +08:00
if ( iter - > second . is_autoload & & tombstone )
2015-03-11 21:14:56 +08:00
function_tombstones . insert ( name ) ;
loaded_functions . erase ( iter ) ;
event_t ev ( EVENT_ANY ) ;
ev . function_name = name ;
event_remove ( ev ) ;
2005-09-20 21:26:39 +08:00
2015-03-11 21:14:56 +08:00
return true ;
2012-01-26 10:40:08 +08:00
}
2006-02-05 21:10:35 +08:00
2012-11-19 08:30:30 +08:00
void function_remove ( const wcstring & name )
2012-01-26 10:40:08 +08:00
{
if ( function_remove_ignore_autoload ( name ) )
2012-11-19 08:30:30 +08:00
function_autoloader . unload ( name ) ;
2012-01-24 12:32:36 +08:00
}
2012-05-19 05:00:36 +08:00
static const function_info_t * function_get ( const wcstring & name )
2012-01-24 12:32:36 +08:00
{
2012-05-19 05:00:36 +08:00
// The caller must lock the functions_lock before calling this; however our mutex is currently recursive, so trylock will never fail
// We need a way to correctly check if a lock is locked (or better yet, make our lock non-recursive)
//ASSERT_IS_LOCKED(functions_lock);
2012-01-24 12:32:36 +08:00
function_map_t : : iterator iter = loaded_functions . find ( name ) ;
2012-11-19 08:30:30 +08:00
if ( iter = = loaded_functions . end ( ) )
{
2012-05-19 05:00:36 +08:00
return NULL ;
2012-11-19 08:30:30 +08:00
}
else
{
2012-05-19 05:00:36 +08:00
return & iter - > second ;
2012-01-24 12:32:36 +08:00
}
2005-09-20 21:26:39 +08:00
}
2012-11-18 18:23:22 +08:00
2012-05-19 05:00:36 +08:00
bool function_get_definition ( const wcstring & name , wcstring * out_definition )
2005-09-20 21:26:39 +08:00
{
2012-05-19 05:00:36 +08:00
scoped_lock lock ( functions_lock ) ;
const function_info_t * func = function_get ( name ) ;
2012-11-19 08:30:30 +08:00
if ( func & & out_definition )
{
2012-05-18 10:37:46 +08:00
out_definition - > assign ( func - > definition ) ;
}
return func ! = NULL ;
2005-09-20 21:26:39 +08:00
}
2007-04-17 04:06:11 +08:00
2012-05-19 05:00:36 +08:00
wcstring_list_t function_get_named_arguments ( const wcstring & name )
2007-04-17 04:06:11 +08:00
{
2012-05-19 05:00:36 +08:00
scoped_lock lock ( functions_lock ) ;
const function_info_t * func = function_get ( name ) ;
2012-01-24 12:43:39 +08:00
return func ? func - > named_arguments : wcstring_list_t ( ) ;
2007-04-17 04:06:11 +08:00
}
2014-10-03 06:59:24 +08:00
std : : map < wcstring , env_var_t > function_get_inherit_vars ( const wcstring & name )
{
scoped_lock lock ( functions_lock ) ;
const function_info_t * func = function_get ( name ) ;
return func ? func - > inherit_vars : std : : map < wcstring , env_var_t > ( ) ;
}
2012-05-19 05:00:36 +08:00
int function_get_shadows ( const wcstring & name )
2007-04-23 06:10:33 +08:00
{
2012-05-19 05:00:36 +08:00
scoped_lock lock ( functions_lock ) ;
const function_info_t * func = function_get ( name ) ;
2012-01-24 12:43:39 +08:00
return func ? func - > shadows : false ;
2007-04-23 06:10:33 +08:00
}
2012-11-18 18:23:22 +08:00
2012-05-19 05:00:36 +08:00
bool function_get_desc ( const wcstring & name , wcstring * out_desc )
2005-09-20 21:26:39 +08:00
{
2012-01-14 15:44:18 +08:00
/* Empty length string goes to NULL */
2012-05-19 05:00:36 +08:00
scoped_lock lock ( functions_lock ) ;
const function_info_t * func = function_get ( name ) ;
2012-11-19 08:30:30 +08:00
if ( out_desc & & func & & ! func - > description . empty ( ) )
{
2012-05-18 10:46:08 +08:00
out_desc - > assign ( _ ( func - > description . c_str ( ) ) ) ;
return true ;
2012-11-19 08:30:30 +08:00
}
else
{
2012-05-18 10:46:08 +08:00
return false ;
2012-01-24 12:43:39 +08:00
}
2005-09-20 21:26:39 +08:00
}
2012-05-19 05:00:36 +08:00
void function_set_desc ( const wcstring & name , const wcstring & desc )
2005-09-20 21:26:39 +08:00
{
2012-11-19 08:30:30 +08:00
load ( name ) ;
2012-05-19 05:00:36 +08:00
scoped_lock lock ( functions_lock ) ;
function_map_t : : iterator iter = loaded_functions . find ( name ) ;
2012-11-19 08:30:30 +08:00
if ( iter ! = loaded_functions . end ( ) )
{
2012-05-19 05:00:36 +08:00
iter - > second . description = desc ;
}
2005-09-20 21:26:39 +08:00
}
2012-05-19 05:00:36 +08:00
bool function_copy ( const wcstring & name , const wcstring & new_name )
2011-12-27 11:18:46 +08:00
{
2012-03-04 07:20:30 +08:00
bool result = false ;
2012-01-24 12:32:36 +08:00
scoped_lock lock ( functions_lock ) ;
2012-01-14 15:44:18 +08:00
function_map_t : : const_iterator iter = loaded_functions . find ( name ) ;
2012-11-19 08:30:30 +08:00
if ( iter ! = loaded_functions . end ( ) )
{
// This new instance of the function shouldn't be tied to the definition file of the original, so pass NULL filename, etc.
2012-05-19 05:00:36 +08:00
const function_map_t : : value_type new_pair ( new_name , function_info_t ( iter - > second , NULL , 0 , false ) ) ;
loaded_functions . insert ( new_pair ) ;
2012-03-04 07:20:30 +08:00
result = true ;
2012-01-14 15:44:18 +08:00
}
2012-11-19 08:30:30 +08:00
return result ;
2005-09-20 21:26:39 +08:00
}
2012-05-19 05:00:36 +08:00
wcstring_list_t function_get_names ( int get_hidden )
2005-09-20 21:26:39 +08:00
{
2012-01-14 15:44:18 +08:00
std : : set < wcstring > names ;
2012-01-24 12:32:36 +08:00
scoped_lock lock ( functions_lock ) ;
2012-11-19 08:30:30 +08:00
autoload_names ( names , get_hidden ) ;
2012-11-18 18:23:22 +08:00
2012-01-14 15:44:18 +08:00
function_map_t : : const_iterator iter ;
2012-11-19 08:30:30 +08:00
for ( iter = loaded_functions . begin ( ) ; iter ! = loaded_functions . end ( ) ; + + iter )
{
2012-01-14 15:44:18 +08:00
const wcstring & name = iter - > first ;
2012-11-18 18:23:22 +08:00
2012-01-14 15:44:18 +08:00
/* Maybe skip hidden */
2012-11-19 08:30:30 +08:00
if ( ! get_hidden )
{
2012-05-19 05:00:36 +08:00
if ( name . empty ( ) | | name . at ( 0 ) = = L ' _ ' ) continue ;
2012-01-14 15:44:18 +08:00
}
names . insert ( name ) ;
}
return wcstring_list_t ( names . begin ( ) , names . end ( ) ) ;
2005-09-20 21:26:39 +08:00
}
2005-10-06 06:37:08 +08:00
2012-05-19 05:00:36 +08:00
const wchar_t * function_get_definition_file ( const wcstring & name )
2006-01-26 22:48:10 +08:00
{
2012-05-19 05:00:36 +08:00
scoped_lock lock ( functions_lock ) ;
const function_info_t * func = function_get ( name ) ;
2012-01-24 12:43:39 +08:00
return func ? func - > definition_file : NULL ;
2006-01-26 22:48:10 +08:00
}
2012-05-19 05:00:36 +08:00
int function_get_definition_offset ( const wcstring & name )
2006-01-26 22:48:10 +08:00
{
2012-05-19 05:00:36 +08:00
scoped_lock lock ( functions_lock ) ;
const function_info_t * func = function_get ( name ) ;
2012-01-24 12:43:39 +08:00
return func ? func - > definition_offset : - 1 ;
2006-01-26 22:48:10 +08:00
}
2015-08-16 04:37:17 +08:00
void function_prepare_environment ( const wcstring & name , const wchar_t * const * argv , const std : : map < wcstring , env_var_t > & inherited_vars )
{
/* Three components of the environment:
1. argv
2. named arguments
3. inherited variables
*/
env_set_argv ( argv ) ;
const wcstring_list_t named_arguments = function_get_named_arguments ( name ) ;
if ( ! named_arguments . empty ( ) )
{
const wchar_t * const * arg ;
size_t i ;
for ( i = 0 , arg = argv ; i < named_arguments . size ( ) ; i + + )
{
env_set ( named_arguments . at ( i ) . c_str ( ) , * arg , ENV_LOCAL | ENV_USER ) ;
if ( * arg )
arg + + ;
}
}
for ( std : : map < wcstring , env_var_t > : : const_iterator it = inherited_vars . begin ( ) , end = inherited_vars . end ( ) ; it ! = end ; + + it )
{
env_set ( it - > first , it - > second . missing ( ) ? NULL : it - > second . c_str ( ) , ENV_LOCAL | ENV_USER ) ;
}
}