From 9e304fa7340b8b74a604564cb44e426cafbe7d34 Mon Sep 17 00:00:00 2001 From: axel Date: Thu, 13 Jul 2006 00:22:42 +1000 Subject: [PATCH] Overhaul of the script autoloader. This should make sure that old scripts are unloaded and new scripts are loaded when the fish_function_path and fish_complete_path variables change darcs-hash:20060712142242-ac50b-3966a0e96a32facc8bc1164d6d0837fc551e4733.gz --- complete.c | 18 ++-- env.c | 3 - function.c | 12 ++- parse_util.c | 175 ++++++++++++++++++++++++++++++++------ parse_util.h | 28 +++++- parser.c | 13 ++- share/functions/grep.fish | 2 +- 7 files changed, 207 insertions(+), 44 deletions(-) diff --git a/complete.c b/complete.c index 79102a9c0..4cda7c8d5 100644 --- a/complete.c +++ b/complete.c @@ -233,7 +233,6 @@ static hash_table_t *condition_cache=0; */ static string_buffer_t *get_desc_buff=0; - /** This command clears the cache of condition tests created by \c condition_test(). */ @@ -354,7 +353,7 @@ void complete_destroy() suffix_hash=0; } - parse_util_load_reset(L"fish_complete_path"); + parse_util_load_reset( L"fish_complete_path", 0 ); } @@ -1551,7 +1550,10 @@ static void complete_load_handler( const wchar_t *cmd ) void complete_load( const wchar_t *name, int reload ) { - parse_util_load( name, L"fish_complete_path", &complete_load_handler, reload ); + parse_util_load( name, + L"fish_complete_path", + &complete_load_handler, + reload ); } /** @@ -1955,20 +1957,18 @@ void complete( const wchar_t *cmd, wchar_t *buff; tokenizer tok; wchar_t *current_token=0, *current_command=0, *prev_token=0; - int on_command=0; int pos; - int done=0; - - int cursor_pos = wcslen(cmd ); + int cursor_pos; CHECK( cmd, ); CHECK( comp, ); // debug( 1, L"Complete '%ls'", cmd ); - - + + cursor_pos = wcslen(cmd ); + /** If we are completing a variable name or a tilde expansion user name, we do that and return. No need for any other competions. diff --git a/env.c b/env.c index a7367772f..47761f97e 100644 --- a/env.c +++ b/env.c @@ -317,9 +317,6 @@ static void handle_locale() if( is_interactive ) { - complete_destroy(); - complete_init(); - debug( 0, _(L"Changing language to English") ); } } diff --git a/function.c b/function.c index 345e4aa9b..5989fd221 100644 --- a/function.c +++ b/function.c @@ -225,7 +225,7 @@ void function_remove( const wchar_t *name ) event_t ev; CHECK( name, ); - + hash_remove( &function, name, &key, @@ -241,6 +241,16 @@ void function_remove( const wchar_t *name ) event_remove( &ev ); clear_function_entry( key, d ); + + /* + Notify the autoloader that the specified function is erased, but + only if this call to fish_remove is not made by the autoloader + itself. + */ + if( !is_autoload ) + { + parse_util_unload( name, L"fish_function_path", 0 ); + } } const wchar_t *function_get_definition( const wchar_t *argv ) diff --git a/parse_util.c b/parse_util.c index 551900735..164269c02 100644 --- a/parse_util.c +++ b/parse_util.c @@ -29,9 +29,36 @@ #include "intern.h" #include "exec.h" #include "env.h" +#include "translate.h" #include "wildcard.h" #include "halloc_util.h" +/** + A structure representing the autoload state for a specific variable, e.g. fish_complete_path +*/ +typedef struct +{ + /** + A table containing the modification times of all loaded + files. Failed loads (non-existing files) have modification time + 0. + */ + hash_table_t load_time; + /** + A string containg the path used to find any files to load. If + this differs from the current environment variable, the + autoloader needs to drop all loaded files and reload them. + */ + wchar_t *old_path; + /** + A table containing all the files that are currently being + loaded. This is here to help prevent recursion. + */ + hash_table_t is_loading; +} + autoload_t; + + /** Set of files which have been autoloaded */ @@ -448,20 +475,32 @@ void parse_util_token_extent( const wchar_t *buff, /** Free hash value, but not hash key */ -static void clear_hash_value( void *key, void *data ) +static void clear_hash_value( void *key, void *data, void *aux ) { + if( aux ) + { + wchar_t *name = (wchar_t *)key; + void (*handler)(const wchar_t *)= (void (*)(const wchar_t *))aux; + handler( name ); + } + free( (void *)data ); } /** Part of the autoloader cleanup */ -static void clear_loaded_entry( void *key, void *data ) +static void clear_loaded_entry( void *key, + void *data, + void *handler ) { - hash_table_t *loaded = (hash_table_t *)data; - hash_foreach( loaded, - &clear_hash_value ); - hash_destroy( loaded ); + autoload_t *loaded = (autoload_t *)data; + hash_foreach2( &loaded->load_time, + &clear_hash_value, + handler ); + hash_destroy( &loaded->load_time ); + + free( loaded->old_path ); free( loaded ); free( (void *)key ); } @@ -475,8 +514,9 @@ static void parse_util_destroy() { if( all_loaded ) { - hash_foreach( all_loaded, - &clear_loaded_entry ); + hash_foreach2( all_loaded, + &clear_loaded_entry, + 0 ); hash_destroy( all_loaded ); free( all_loaded ); @@ -484,18 +524,61 @@ static void parse_util_destroy() } } -void parse_util_load_reset( const wchar_t *path_var ) +void parse_util_load_reset( const wchar_t *path_var_name, + void (*on_load)(const wchar_t *cmd) ) { + wchar_t *path_var; + + CHECK( path_var_name, ); + path_var = env_get( path_var_name ); + + if( !path_var ) + return; + if( all_loaded ) { void *key, *data; - hash_remove( all_loaded, path_var, &key, &data ); + hash_remove( all_loaded, path_var_name, &key, &data ); if( key ) - clear_loaded_entry( key, data ); + clear_loaded_entry( key, data, (void *)on_load ); } } +int parse_util_unload( const wchar_t *cmd, + const wchar_t *path_var_name, + void (*on_load)(const wchar_t *cmd) ) +{ + autoload_t *loaded; + void *val; + + CHECK( path_var_name, 0 ); + CHECK( cmd, 0 ); + + if( !all_loaded ) + { + return 0; + } + + loaded = (autoload_t *)hash_get( all_loaded, path_var_name ); + + if( !loaded ) + { + return 0; + } + + hash_remove( &loaded->load_time, cmd, 0, &val ); + if( val ) + { + if( on_load ) + { + on_load( (wchar_t *)val ); + } + free( val ); + } + + return !!val; +} int parse_util_load( const wchar_t *cmd, const wchar_t *path_var_name, @@ -508,16 +591,22 @@ int parse_util_load( const wchar_t *cmd, int i; time_t *tm; int reloaded = 0; - hash_table_t *loaded; + autoload_t *loaded; - wchar_t *path_var = env_get( path_var_name ); + wchar_t *path_var; + CHECK( path_var_name, 0 ); + CHECK( cmd, 0 ); + + path_var = env_get( path_var_name ); + /* Do we know where to look */ - if( !path_var ) + { return 0; + } if( !all_loaded ) { @@ -530,23 +619,52 @@ int parse_util_load( const wchar_t *cmd, hash_init( all_loaded, &hash_wcs_func, &hash_wcs_cmp ); } - loaded = (hash_table_t *)hash_get( all_loaded, path_var_name ); - - if( !loaded ) + loaded = (autoload_t *)hash_get( all_loaded, path_var_name ); + + if( loaded ) { - loaded = malloc( sizeof( hash_table_t ) ); + if( hash_get( &loaded->is_loading, cmd ) ) + { + debug( 0, _(L"Could not autoload item %ls, it is already being autoloaded. This is a circular dependency in the autoloading scripts, please remove it."), cmd ); + return 1; + } + + /* + Check if the lookup path has changed. If so, drop all loaded + files and start from scratch. + */ + if( wcscmp( path_var, loaded->old_path ) != 0 ) + { + parse_util_load_reset( path_var_name, on_load); + reload = parse_util_load( cmd, path_var_name, on_load, reload ); + return reload; + } + } + else + { + /* + We have never tried to autoload using this name before, set up initial data + */ + loaded = malloc( sizeof( autoload_t ) ); if( !loaded ) { DIE_MEM(); } - hash_init( loaded, &hash_wcs_func, &hash_wcs_cmp ); + hash_init( &loaded->load_time, &hash_wcs_func, &hash_wcs_cmp ); hash_put( all_loaded, wcsdup(path_var_name), loaded ); + + hash_init( &loaded->is_loading, &hash_wcs_func, &hash_wcs_cmp ); + + loaded->old_path = wcsdup( path_var ); } + hash_put( &loaded->is_loading, cmd, cmd ); + + /* Get modification time of file */ - tm = (time_t *)hash_get( loaded, cmd ); + tm = (time_t *)hash_get( &loaded->load_time, cmd ); /* Did we just check this? @@ -555,6 +673,7 @@ int parse_util_load( const wchar_t *cmd, { if(time(0)-tm[1]<=1) { + hash_remove( &loaded->is_loading, cmd, 0, 0 ); return 0; } } @@ -563,7 +682,10 @@ int parse_util_load( const wchar_t *cmd, Return if already loaded and we are skipping reloading */ if( !reload && tm ) + { + hash_remove( &loaded->is_loading, cmd, 0, 0 ); return 0; + } if( !path_list ) path_list = al_halloc( global_context); @@ -601,7 +723,7 @@ int parse_util_load( const wchar_t *cmd, tm[0] = buf.st_mtime; tm[1] = time(0); - hash_put( loaded, + hash_put( &loaded->load_time, intern( cmd ), tm ); @@ -634,12 +756,13 @@ int parse_util_load( const wchar_t *cmd, tm[0] = 0; tm[1] = time(0); - hash_put( loaded, intern( cmd ), tm ); + hash_put( &loaded->load_time, intern( cmd ), tm ); } al_foreach( path_list, &free ); al_truncate( path_list, 0 ); - + + hash_remove( &loaded->is_loading, cmd, 0, 0 ); return reloaded; } @@ -670,7 +793,11 @@ void parse_util_set_argv( wchar_t **argv ) wchar_t *parse_util_unescape_wildcards( const wchar_t *str ) { wchar_t *in, *out; - wchar_t *unescaped = wcsdup(str); + wchar_t *unescaped; + + CHECK( str, 0 ); + + unescaped = wcsdup(str); if( !unescaped ) DIE_MEM(); diff --git a/parse_util.h b/parse_util.h index 23b9b88f6..c91151079 100644 --- a/parse_util.h +++ b/parse_util.h @@ -95,10 +95,11 @@ int parse_util_lineno( const wchar_t *str, int len ); /** Autoload the specified file, if it exists in the specified path. Do - not load it multiple times unless it's timestamp changes. + not load it multiple times unless it's timestamp changes or + parse_util_unload is called. \param cmd the filename to search for. The suffix '.fish' is always added to this name - \param path_var_name the name of an environment variable containing a search path + \param path_var_name the environment variable giving the search path \param on_load a callback function to run if a suitable file is found, which has not already been run \param reload wheter to recheck file timestamps on already loaded files */ @@ -108,9 +109,28 @@ int parse_util_load( const wchar_t *cmd, int reload ); /** - Reset the loader for the specified path variable + Reset the loader for the specified path variable. This will cause + all information on loaded files in the specified directory to be + reset. + + \param path_var_name the environment variable giving the search path + \param on_load the callback function to use when a file is reloaded + \param on_load the callback function to call if the file has been previously loaded */ -void parse_util_load_reset( const wchar_t *path_var ); +void parse_util_load_reset( const wchar_t *path_var_name, + void (*on_load)(const wchar_t *cmd) ); + +/** + Tell the autoloader that the specified file, in the specified path, + is no longer loaded. + + \param cmd the filename to search for. The suffix '.fish' is always added to this name + \param path_var_name the environment variable giving the search path + \return non-zero if the file was removed, zero if the file had not yet been loaded +*/ +int parse_util_unload( const wchar_t *cmd, + const wchar_t *path_var_name, + void (*on_load)(const wchar_t *cmd) ); /** Set the argv environment variable to the specified null-terminated diff --git a/parser.c b/parser.c index 85851c9f0..1ab7972f8 100644 --- a/parser.c +++ b/parser.c @@ -443,11 +443,19 @@ void parser_push_block( int type ) blocks should always be skipped. Rather complicated... :-( */ new->skip=current_block?current_block->skip:0; + + /* + Type TOP and SUBST are never skipped + */ if( type == TOP || type == SUBST ) { new->skip = 0; } - if( type == FAKE ) + + /* + Fake blocks and function definition blocks are never executed + */ + if( type == FAKE || type == FUNCTION_DEF ) { new->skip = 1; } @@ -2065,11 +2073,12 @@ static int parse_job( process_t *p, forbid = (wchar_t *)(al_get_count( forbidden_function)?al_peek( forbidden_function ):0); nxt_forbidden = forbid && (wcscmp( forbid, nxt) == 0 ); - + /* Make feeble attempt to avoid infinite recursion. Will at least catch some accidental infinite recursion calls. */ + if( function_exists( nxt ) && !nxt_forbidden) { /* diff --git a/share/functions/grep.fish b/share/functions/grep.fish index d34b288d6..60e83b204 100644 --- a/share/functions/grep.fish +++ b/share/functions/grep.fish @@ -2,7 +2,7 @@ # Match colors for grep, if supported # -if grep --color=auto --help 1>/dev/null 2>/dev/null +if command grep --color=auto --help 1>/dev/null 2>/dev/null if not set -q GREP_COLOR set -gx GREP_COLOR '97;45' end