2005-09-20 13:26:39 +00:00
/** \file function.c
2006-11-15 14:16:49 +00:00
Prototypes for functions for storing and retrieving function
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 13:26:39 +00:00
*/
2006-08-11 01:18:35 +00:00
# include "config.h"
2005-09-20 13:26:39 +00:00
# include <stdlib.h>
# include <stdio.h>
2011-12-27 03:18:46 +00:00
# include <string.h>
2005-09-20 13:26:39 +00:00
# include <wchar.h>
# include <unistd.h>
# include <termios.h>
# include <signal.h>
2011-12-27 03:18:46 +00:00
# include <pthread.h>
# include <errno.h>
2012-01-14 07:44:18 +00:00
# include <map>
# include <set>
2005-09-20 13:26:39 +00:00
2006-01-20 14:27:21 +00:00
# include "wutil.h"
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 "function.h"
# include "proc.h"
# include "parser.h"
# include "common.h"
# include "intern.h"
2005-10-05 22:37:08 +00:00
# include "event.h"
2006-01-26 14:48:10 +00:00
# include "reader.h"
2006-02-05 13:10:35 +00:00
# include "parse_util.h"
2007-04-22 09:50:26 +00:00
# include "parser_keywords.h"
2006-02-08 09:20:05 +00:00
# include "env.h"
2006-02-08 15:29:09 +00:00
# include "expand.h"
2012-01-23 19:42:41 +00:00
# include "builtin_scripts.h"
2005-09-20 13:26:39 +00:00
2005-10-05 22:37:08 +00:00
/**
Table containing all functions
*/
2012-05-18 21:00:36 +00:00
typedef std : : map < wcstring , function_info_t > function_map_t ;
2012-01-14 07:44:18 +00:00
static function_map_t loaded_functions ;
2011-12-27 03:18:46 +00:00
/* Lock for functions */
static pthread_mutex_t functions_lock ;
2012-01-26 02:40:08 +00:00
/* Autoloader for functions */
class function_autoload_t : public autoload_t {
public :
function_autoload_t ( ) ;
virtual void command_removed ( const wcstring & cmd ) ;
} ;
static function_autoload_t function_autoloader ;
/** Constructor */
function_autoload_t : : function_autoload_t ( ) : autoload_t ( L " fish_function_path " ,
internal_function_scripts ,
sizeof internal_function_scripts / sizeof * internal_function_scripts )
{
}
/** Removes a function from our internal table, returning true if it was found and false if not */
static bool function_remove_ignore_autoload ( const wcstring & name ) ;
/** Callback when an autoloaded function is removed */
void function_autoload_t : : command_removed ( const wcstring & cmd ) {
2012-01-26 02:51:26 +00:00
function_remove_ignore_autoload ( cmd ) ;
2012-01-26 02:40:08 +00:00
}
2011-12-27 03:18:46 +00:00
/* Helper macro for vomiting */
# define VOMIT_ON_FAILURE(a) do { if (0 != (a)) { int err = errno; fprintf(stderr, "%s failed on line %d in file %s: %d (%s)\n", #a, __LINE__, __FILE__, err, strerror(err)); abort(); }} while (0)
2006-06-17 13:07:08 +00: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-03 23:20:30 +00:00
static bool is_autoload = false ;
2006-02-08 09:20:05 +00:00
2006-02-08 17:37:18 +00:00
/**
Make sure that if the specified function is a dynamically loaded
function , it has been fully loaded .
*/
2012-02-19 17:25:15 +00:00
static int load ( const wcstring & name )
2006-02-08 09:20:05 +00:00
{
2011-12-27 05:56:23 +00:00
ASSERT_IS_MAIN_THREAD ( ) ;
2012-01-24 04:32:36 +00:00
scoped_lock lock ( functions_lock ) ;
2012-03-03 23:20:30 +00:00
bool was_autoload = is_autoload ;
2006-02-08 09:20:05 +00:00
int res ;
2012-01-14 07:44:18 +00:00
function_map_t : : iterator iter = loaded_functions . find ( name ) ;
2012-05-18 21:00:36 +00:00
if ( iter ! = loaded_functions . end ( ) & & ! iter - > second . is_autoload ) {
2012-01-28 22:56:13 +00:00
/* We have a non-autoload version already */
2006-02-08 09:20:05 +00:00
return 0 ;
2011-12-27 03:18:46 +00:00
}
2012-03-03 23:20:30 +00:00
is_autoload = true ;
2012-01-26 02:40:08 +00:00
res = function_autoloader . load ( name , true ) ;
2006-02-08 09:20:05 +00:00
is_autoload = was_autoload ;
return res ;
}
2006-02-08 17:37:18 +00:00
/**
Insert a list of all dynamically loaded functions into the
specified list .
*/
2012-01-14 07:44:18 +00:00
static void autoload_names ( std : : set < wcstring > & names , int get_hidden )
2006-02-08 17:37:18 +00:00
{
2012-01-10 20:51:09 +00:00
size_t i ;
2011-12-27 03:18:46 +00:00
2012-01-14 10:42:17 +00:00
const env_var_t path_var_wstr = env_get_string ( L " fish_function_path " ) ;
if ( path_var_wstr . missing ( ) )
return ;
const wchar_t * path_var = path_var_wstr . c_str ( ) ;
2011-12-27 03:18:46 +00:00
2012-01-10 20:51:09 +00:00
wcstring_list_t path_list ;
2006-02-08 17:37:18 +00:00
2012-02-10 09:37:30 +00:00
tokenize_variable_array ( path_var , path_list ) ;
2012-01-10 20:51:09 +00:00
for ( i = 0 ; i < path_list . size ( ) ; i + + )
2006-02-08 17:37:18 +00:00
{
2012-01-10 20:51:09 +00:00
const wcstring & ndir_str = path_list . at ( i ) ;
const wchar_t * ndir = ( wchar_t * ) ndir_str . c_str ( ) ;
2006-02-08 17:37:18 +00:00
DIR * dir = wopendir ( ndir ) ;
2006-02-13 21:44:16 +00:00
if ( ! dir )
continue ;
2011-12-27 03:18:46 +00:00
wcstring name ;
while ( wreaddir ( dir , name ) )
2006-02-08 17:37:18 +00:00
{
2011-12-27 03:18:46 +00:00
const wchar_t * fn = name . c_str ( ) ;
2012-01-14 07:44:18 +00:00
const wchar_t * suffix ;
2006-02-08 17:37:18 +00:00
if ( ! get_hidden & & fn [ 0 ] = = L ' _ ' )
continue ;
2011-12-27 03:18:46 +00:00
2012-01-14 07:44:18 +00:00
suffix = wcsrchr ( fn , L ' . ' ) ;
2006-02-08 17:37:18 +00:00
if ( suffix & & ( wcscmp ( suffix , L " .fish " ) = = 0 ) )
{
2012-01-14 07:44:18 +00:00
wcstring name ( fn , suffix - fn ) ;
names . insert ( name ) ;
2006-02-08 17:37:18 +00:00
}
2011-12-27 03:18:46 +00:00
}
2006-02-08 17:37:18 +00:00
closedir ( dir ) ;
}
2006-02-08 09:20:05 +00:00
}
2005-09-20 13:26:39 +00:00
void function_init ( )
{
2012-05-18 21:00:36 +00: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 03:18:46 +00: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 13:26:39 +00:00
}
2012-03-03 23:20:30 +00: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 ) ,
is_autoload ( autoload ) ,
shadows ( data . shadows )
{
}
2006-01-26 14:48:10 +00:00
2012-03-03 23:20:30 +00: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 ) ,
is_autoload ( autoload ) ,
shadows ( data . shadows )
2005-09-20 13:26:39 +00:00
{
2012-03-03 23:20:30 +00:00
}
void function_add ( const function_data_t & data , const parser_t & parser )
{
ASSERT_IS_MAIN_THREAD ( ) ;
CHECK ( ! data . name . empty ( ) , ) ;
CHECK ( data . definition , ) ;
2012-01-24 04:32:36 +00:00
scoped_lock lock ( functions_lock ) ;
2012-01-14 07:44:18 +00:00
2012-03-03 23:20:30 +00:00
/* Remove the old function */
function_remove ( data . name ) ;
2012-05-18 21:00:36 +00:00
/* Create and store a new function */
2012-03-03 23:20:30 +00:00
const wchar_t * filename = reader_current_filename ( ) ;
2012-08-05 00:44:14 +00:00
int def_offset = parser . line_number_of_character_at_offset ( parser . current_block - > tok_pos ) - 1 ;
2012-05-18 21:00:36 +00:00
const function_map_t : : value_type new_pair ( data . name , function_info_t ( data , filename , def_offset , is_autoload ) ) ;
loaded_functions . insert ( new_pair ) ;
2011-12-27 03:18:46 +00:00
2012-03-03 23:20:30 +00:00
/* Add event handlers */
for ( std : : vector < event_t > : : const_iterator iter = data . events . begin ( ) ; iter ! = data . events . end ( ) ; + + iter )
2005-10-05 22:37:08 +00:00
{
2012-03-03 23:20:30 +00:00
event_add_handler ( & * iter ) ;
2005-10-05 22:37:08 +00:00
}
2005-09-20 13:26:39 +00:00
}
2012-02-19 17:25:15 +00:00
int function_exists ( const wcstring & cmd )
2010-09-07 17:31:05 +00:00
{
2011-12-27 03:18:46 +00:00
if ( parser_keywords_is_reserved ( cmd ) )
2010-09-07 17:31:05 +00:00
return 0 ;
2012-01-24 04:32:36 +00:00
scoped_lock lock ( functions_lock ) ;
2012-01-28 22:56:13 +00:00
load ( cmd ) ;
return loaded_functions . find ( cmd ) ! = loaded_functions . end ( ) ;
2011-12-27 03:18:46 +00:00
}
2010-09-18 01:51:16 +00:00
2012-07-21 03:39:31 +00:00
int function_exists_no_autoload ( const wcstring & cmd , const env_vars_snapshot_t & vars )
2011-12-27 03:18:46 +00:00
{
2012-01-28 22:56:13 +00:00
if ( parser_keywords_is_reserved ( cmd ) )
return 0 ;
scoped_lock lock ( functions_lock ) ;
return loaded_functions . find ( cmd ) ! = loaded_functions . end ( ) | | function_autoloader . can_load ( cmd , vars ) ;
2005-09-20 13:26:39 +00:00
}
2012-01-26 02:40:08 +00:00
static bool function_remove_ignore_autoload ( const wcstring & name )
2005-09-20 13:26:39 +00:00
{
2012-01-24 04:32:36 +00:00
scoped_lock lock ( functions_lock ) ;
2012-01-14 07:44:18 +00:00
bool erased = ( loaded_functions . erase ( name ) > 0 ) ;
2011-12-27 03:18:46 +00:00
2012-01-26 02:40:08 +00:00
if ( erased ) {
2012-02-09 03:02:25 +00:00
event_t ev ( EVENT_ANY ) ;
2012-02-17 19:36:49 +00:00
ev . function_name = name ;
2012-01-26 02:40:08 +00:00
event_remove ( & ev ) ;
2011-12-27 03:18:46 +00:00
}
2012-01-26 02:40:08 +00:00
return erased ;
2005-09-20 13:26:39 +00:00
2012-01-26 02:40:08 +00:00
}
2006-02-05 13:10:35 +00:00
2012-01-26 02:40:08 +00:00
void function_remove ( const wcstring & name )
{
if ( function_remove_ignore_autoload ( name ) )
function_autoloader . unload ( name ) ;
2012-01-24 04:32:36 +00:00
}
2012-05-18 21:00:36 +00:00
static const function_info_t * function_get ( const wcstring & name )
2012-01-24 04:32:36 +00:00
{
2012-05-18 21:00:36 +00: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 04:32:36 +00:00
function_map_t : : iterator iter = loaded_functions . find ( name ) ;
if ( iter = = loaded_functions . end ( ) ) {
2012-05-18 21:00:36 +00:00
return NULL ;
2012-01-24 04:32:36 +00:00
} else {
2012-05-18 21:00:36 +00:00
return & iter - > second ;
2012-01-24 04:32:36 +00:00
}
2005-09-20 13:26:39 +00:00
}
2011-12-27 03:18:46 +00:00
2012-05-18 21:00:36 +00:00
bool function_get_definition ( const wcstring & name , wcstring * out_definition )
2005-09-20 13:26:39 +00:00
{
2012-05-18 21:00:36 +00:00
scoped_lock lock ( functions_lock ) ;
const function_info_t * func = function_get ( name ) ;
2012-05-18 02:37:46 +00:00
if ( func & & out_definition ) {
out_definition - > assign ( func - > definition ) ;
}
return func ! = NULL ;
2005-09-20 13:26:39 +00:00
}
2007-04-16 20:06:11 +00:00
2012-05-18 21:00:36 +00:00
wcstring_list_t function_get_named_arguments ( const wcstring & name )
2007-04-16 20:06:11 +00:00
{
2012-05-18 21:00:36 +00:00
scoped_lock lock ( functions_lock ) ;
const function_info_t * func = function_get ( name ) ;
2012-01-24 04:43:39 +00:00
return func ? func - > named_arguments : wcstring_list_t ( ) ;
2007-04-16 20:06:11 +00:00
}
2012-05-18 21:00:36 +00:00
int function_get_shadows ( const wcstring & name )
2007-04-22 22:10:33 +00:00
{
2012-05-18 21:00:36 +00:00
scoped_lock lock ( functions_lock ) ;
const function_info_t * func = function_get ( name ) ;
2012-01-24 04:43:39 +00:00
return func ? func - > shadows : false ;
2007-04-22 22:10:33 +00:00
}
2011-12-27 03:18:46 +00:00
2012-05-18 21:00:36 +00:00
bool function_get_desc ( const wcstring & name , wcstring * out_desc )
2005-09-20 13:26:39 +00:00
{
2012-01-14 07:44:18 +00:00
/* Empty length string goes to NULL */
2012-05-18 21:00:36 +00:00
scoped_lock lock ( functions_lock ) ;
const function_info_t * func = function_get ( name ) ;
2012-05-18 02:46:08 +00:00
if ( out_desc & & func & & ! func - > description . empty ( ) ) {
out_desc - > assign ( _ ( func - > description . c_str ( ) ) ) ;
return true ;
2012-01-24 04:43:39 +00:00
} else {
2012-05-18 02:46:08 +00:00
return false ;
2012-01-24 04:43:39 +00:00
}
2005-09-20 13:26:39 +00:00
}
2012-05-18 21:00:36 +00:00
void function_set_desc ( const wcstring & name , const wcstring & desc )
2005-09-20 13:26:39 +00:00
{
2012-05-18 21:00:36 +00:00
load ( name ) ;
scoped_lock lock ( functions_lock ) ;
function_map_t : : iterator iter = loaded_functions . find ( name ) ;
if ( iter ! = loaded_functions . end ( ) ) {
iter - > second . description = desc ;
}
2005-09-20 13:26:39 +00:00
}
2012-05-18 21:00:36 +00:00
bool function_copy ( const wcstring & name , const wcstring & new_name )
2011-12-27 03:18:46 +00:00
{
2012-03-03 23:20:30 +00:00
bool result = false ;
2012-01-24 04:32:36 +00:00
scoped_lock lock ( functions_lock ) ;
2012-01-14 07:44:18 +00:00
function_map_t : : const_iterator iter = loaded_functions . find ( name ) ;
if ( iter ! = loaded_functions . end ( ) ) {
2012-03-03 23:20:30 +00:00
// This new instance of the function shouldn't be tied to the definition file of the original, so pass NULL filename, etc.
2012-05-18 21:00:36 +00: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-03 23:20:30 +00:00
result = true ;
2012-01-14 07:44:18 +00:00
}
return result ;
2005-09-20 13:26:39 +00:00
}
2012-05-18 21:00:36 +00:00
wcstring_list_t function_get_names ( int get_hidden )
2005-09-20 13:26:39 +00:00
{
2012-01-14 07:44:18 +00:00
std : : set < wcstring > names ;
2012-01-24 04:32:36 +00:00
scoped_lock lock ( functions_lock ) ;
2012-05-18 21:00:36 +00:00
autoload_names ( names , get_hidden ) ;
2012-01-14 07:44:18 +00:00
function_map_t : : const_iterator iter ;
2012-03-01 22:56:34 +00:00
for ( iter = loaded_functions . begin ( ) ; iter ! = loaded_functions . end ( ) ; + + iter ) {
2012-01-14 07:44:18 +00:00
const wcstring & name = iter - > first ;
/* Maybe skip hidden */
if ( ! get_hidden ) {
2012-05-18 21:00:36 +00:00
if ( name . empty ( ) | | name . at ( 0 ) = = L ' _ ' ) continue ;
2012-01-14 07:44:18 +00:00
}
names . insert ( name ) ;
}
return wcstring_list_t ( names . begin ( ) , names . end ( ) ) ;
2005-09-20 13:26:39 +00:00
}
2005-10-05 22:37:08 +00:00
2012-05-18 21:00:36 +00:00
const wchar_t * function_get_definition_file ( const wcstring & name )
2006-01-26 14:48:10 +00:00
{
2012-05-18 21:00:36 +00:00
scoped_lock lock ( functions_lock ) ;
const function_info_t * func = function_get ( name ) ;
2012-01-24 04:43:39 +00:00
return func ? func - > definition_file : NULL ;
2006-01-26 14:48:10 +00:00
}
2012-05-18 21:00:36 +00:00
int function_get_definition_offset ( const wcstring & name )
2006-01-26 14:48:10 +00:00
{
2012-05-18 21:00:36 +00:00
scoped_lock lock ( functions_lock ) ;
const function_info_t * func = function_get ( name ) ;
2012-01-24 04:43:39 +00:00
return func ? func - > definition_offset : - 1 ;
2006-01-26 14:48:10 +00:00
}