aboutsummaryrefslogtreecommitdiffhomepage
path: root/autoload.cpp
diff options
context:
space:
mode:
authorGravatar ridiculousfish <corydoras@ridiculousfish.com>2012-01-25 18:59:35 -0800
committerGravatar ridiculousfish <corydoras@ridiculousfish.com>2012-01-25 18:59:35 -0800
commit843ba4ac2cd616a4fbc1d67b2405220e7465e4b0 (patch)
tree32050f564a92ac1fbeea601288527fb63deff899 /autoload.cpp
parentd6545588a3005074be0597396c077bc1a2435783 (diff)
Move autoload class into autoload.h and cpp
Diffstat (limited to 'autoload.cpp')
-rw-r--r--autoload.cpp219
1 files changed, 219 insertions, 0 deletions
diff --git a/autoload.cpp b/autoload.cpp
index 0bae1a26..4eaab7cb 100644
--- a/autoload.cpp
+++ b/autoload.cpp
@@ -6,6 +6,11 @@ The classes responsible for autoloading functions and completions.
#include "config.h"
#include "autoload.h"
#include "wutil.h"
+#include "common.h"
+#include "signal.h"
+#include "env.h"
+#include "builtin_scripts.h"
+#include "exec.h"
#include <assert.h>
static const size_t kLRULimit = 16;
@@ -131,3 +136,217 @@ void lru_cache_impl_t::evict_all_nodes() {
evict_last_node();
}
}
+
+
+
+autoload_t::autoload_t(const wcstring &env_var_name_var, const builtin_script_t * const scripts, size_t script_count) :
+ env_var_name(env_var_name_var),
+ builtin_scripts(scripts),
+ builtin_script_count(script_count)
+{
+}
+
+void autoload_t::node_was_evicted(autoload_function_t *node) {
+ // Tell ourselves that the command was removed, unless it was a placeholder
+ if (! node->is_placeholder)
+ this->command_removed(node->key);
+ delete node;
+}
+
+void autoload_t::reset( )
+{
+ this->evict_all_nodes();
+}
+
+int autoload_t::unload( const wcstring &cmd )
+{
+ return this->evict_node(cmd);
+}
+
+int autoload_t::load( const wcstring &cmd, bool reload )
+{
+ int res;
+ int c, c2;
+
+ CHECK_BLOCK( 0 );
+
+ const env_var_t path_var = env_get_string( env_var_name.c_str() );
+
+ /*
+ Do we know where to look?
+ */
+ if( path_var.empty() )
+ {
+ return 0;
+ }
+
+ /*
+ Check if the lookup path has changed. If so, drop all loaded
+ files.
+ */
+ if( path_var != this->path )
+ {
+ this->path = path_var;
+ this->reset();
+ }
+
+ /**
+ Warn and fail on infinite recursion
+ */
+ if (this->is_loading(cmd))
+ {
+ debug( 0,
+ _( L"Could not autoload item '%ls', it is already being autoloaded. "
+ L"This is a circular dependency in the autoloading scripts, please remove it."),
+ cmd.c_str() );
+ return 1;
+ }
+
+
+
+ std::vector<wcstring> path_list;
+ tokenize_variable_array2( path_var, path_list );
+
+ c = path_list.size();
+
+ is_loading_set.insert(cmd);
+
+ /*
+ Do the actual work in the internal helper function
+ */
+ res = this->load_internal( cmd, reload, path_list );
+
+ int erased = is_loading_set.erase(cmd);
+ assert(erased);
+
+ c2 = path_list.size();
+
+ /**
+ Make sure we didn't 'drop' something
+ */
+
+ assert( c == c2 );
+
+ return res;
+}
+
+static bool script_name_precedes_script_name(const builtin_script_t &script1, const builtin_script_t &script2)
+{
+ return wcscmp(script1.name, script2.name) < 0;
+}
+
+/**
+ This internal helper function does all the real work. By using two
+ functions, the internal function can return on various places in
+ the code, and the caller can take care of various cleanup work.
+*/
+
+int autoload_t::load_internal( const wcstring &cmd,
+ int reload,
+ const wcstring_list_t &path_list )
+{
+
+ size_t i;
+ int reloaded = 0;
+
+ /* Get the function */
+ autoload_function_t * func = this->get_function_with_name(cmd);
+
+ /* Return if already loaded and we are skipping reloading */
+ if( !reload && func )
+ return 0;
+
+ /* Nothing to do if we just checked it */
+ if (func && time(NULL) - func->access.last_checked <= 1)
+ return 0;
+
+ /* The source of the script will end up here */
+ wcstring script_source;
+ bool has_script_source = false;
+
+ /*
+ Look for built-in scripts via a binary search
+ */
+ const builtin_script_t *matching_builtin_script = NULL;
+ if (builtin_script_count > 0)
+ {
+ const builtin_script_t test_script = {cmd.c_str(), NULL};
+ const builtin_script_t *array_end = builtin_scripts + builtin_script_count;
+ const builtin_script_t *found = std::lower_bound(builtin_scripts, array_end, test_script, script_name_precedes_script_name);
+ if (found != array_end && ! wcscmp(found->name, test_script.name))
+ {
+ /* We found it */
+ matching_builtin_script = found;
+ }
+ }
+ if (matching_builtin_script) {
+ has_script_source = true;
+ script_source = str2wcstring(matching_builtin_script->def);
+ }
+
+ if (! has_script_source)
+ {
+ /*
+ Iterate over path searching for suitable completion files
+ */
+ for( i=0; i<path_list.size(); i++ )
+ {
+ wcstring next = path_list.at(i);
+ wcstring path = next + L"/" + cmd + L".fish";
+
+ const file_access_attempt_t access = access_file(path, R_OK);
+ if (access.accessible) {
+ if (! func || access.mod_time != func->access.mod_time) {
+ wcstring esc = escape_string(path, 1);
+ script_source = L". " + esc;
+ has_script_source = true;
+
+ if( !func )
+ func = new autoload_function_t(cmd);
+ func->access = access;
+
+ // Remove this command because we are going to reload it
+ command_removed(cmd);
+
+ reloaded = 1;
+ }
+ else if( func )
+ {
+ /*
+ If we are rechecking an autoload file, and it hasn't
+ changed, update the 'last check' timestamp.
+ */
+ func->access = access;
+ }
+
+ break;
+ }
+ }
+
+ /*
+ If no file was found we insert a placeholder function. Later we only
+ research if the current time is at least five seconds later.
+ This way, the files won't be searched over and over again.
+ */
+ if( !func )
+ {
+ func = new autoload_function_t(cmd);
+ func->access.last_checked = time(NULL);
+ func->is_placeholder = true;
+ }
+ }
+
+ /* If we have a script, either built-in or a file source, then run it */
+ if (has_script_source)
+ {
+ if( exec_subshell( script_source.c_str(), 0 ) == -1 )
+ {
+ /*
+ Do nothing on failiure
+ */
+ }
+
+ }
+
+ return reloaded;
+}