2016-05-01 11:31:02 +08:00
|
|
|
// 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.
|
|
|
|
//
|
2016-05-19 06:30:21 +08:00
|
|
|
#include "config.h" // IWYU pragma: keep
|
|
|
|
|
2016-04-21 14:00:54 +08:00
|
|
|
// IWYU pragma: no_include <type_traits>
|
2016-05-01 11:31:02 +08:00
|
|
|
#include <dirent.h>
|
2011-12-27 11:18:46 +08:00
|
|
|
#include <pthread.h>
|
2016-05-01 11:31:02 +08:00
|
|
|
#include <stddef.h>
|
|
|
|
#include <wchar.h>
|
2017-02-14 12:37:27 +08:00
|
|
|
|
2012-01-14 15:44:18 +08:00
|
|
|
#include <map>
|
2016-05-01 11:31:02 +08:00
|
|
|
#include <memory>
|
2012-01-14 15:44:18 +08:00
|
|
|
#include <set>
|
2015-07-25 23:14:25 +08:00
|
|
|
#include <string>
|
|
|
|
#include <utility>
|
2005-09-20 21:26:39 +08:00
|
|
|
|
2015-07-25 23:14:25 +08:00
|
|
|
#include "autoload.h"
|
2005-09-20 21:26:39 +08:00
|
|
|
#include "common.h"
|
2016-05-01 11:31:02 +08:00
|
|
|
#include "env.h"
|
2005-10-06 06:37:08 +08:00
|
|
|
#include "event.h"
|
2016-05-01 11:31:02 +08:00
|
|
|
#include "fallback.h" // IWYU pragma: keep
|
|
|
|
#include "function.h"
|
|
|
|
#include "intern.h"
|
2007-04-22 17:50:26 +08:00
|
|
|
#include "parser_keywords.h"
|
2016-05-01 11:31:02 +08:00
|
|
|
#include "reader.h"
|
|
|
|
#include "wutil.h" // IWYU pragma: keep
|
2005-09-20 21:26:39 +08:00
|
|
|
|
2016-05-01 11:31:02 +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
|
|
|
|
2016-05-01 11:31:02 +08:00
|
|
|
/// Functions that shouldn't be autoloaded (anymore).
|
2015-03-11 21:14:56 +08:00
|
|
|
static std::set<wcstring> function_tombstones;
|
|
|
|
|
2016-05-01 11:31:02 +08:00
|
|
|
/// Lock for functions.
|
2011-12-27 11:18:46 +08:00
|
|
|
static pthread_mutex_t functions_lock;
|
|
|
|
|
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
|
|
|
|
2016-05-01 11:31:02 +08:00
|
|
|
/// Callback when an autoloaded function is removed.
|
2017-01-30 10:56:55 +08:00
|
|
|
void autoloaded_function_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
|
|
|
}
|
|
|
|
|
2017-01-30 10:56:55 +08:00
|
|
|
// Function autoloader
|
|
|
|
static autoload_t function_autoloader(L"fish_function_path", autoloaded_function_removed);
|
|
|
|
|
2016-05-01 11:31:02 +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
|
|
|
|
2016-05-01 11:31:02 +08:00
|
|
|
/// Make sure that if the specified function is a dynamically loaded function, it has been fully
|
|
|
|
/// loaded.
|
|
|
|
static int load(const wcstring &name) {
|
2011-12-27 13:56:23 +08:00
|
|
|
ASSERT_IS_MAIN_THREAD();
|
2016-07-21 13:30:58 +08:00
|
|
|
scoped_lock locker(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;
|
2016-05-01 11:31:02 +08:00
|
|
|
if (no_more_autoload) return 0;
|
2015-03-11 21:14:56 +08:00
|
|
|
|
2012-01-14 15:44:18 +08:00
|
|
|
function_map_t::iterator iter = loaded_functions.find(name);
|
2016-05-01 11:31:02 +08:00
|
|
|
if (iter != loaded_functions.end() && !iter->second.is_autoload) {
|
|
|
|
// 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
|
|
|
}
|
|
|
|
|
2016-05-01 11:31:02 +08:00
|
|
|
/// Insert a list of all dynamically loaded functions into the specified list.
|
|
|
|
static void autoload_names(std::set<wcstring> &names, int get_hidden) {
|
2012-11-19 08:30:30 +08:00
|
|
|
size_t i;
|
2012-11-18 18:23:22 +08:00
|
|
|
|
2017-08-06 12:40:27 +08:00
|
|
|
const env_var_t path_var = env_get(L"fish_function_path");
|
|
|
|
if (path_var.missing_or_empty()) return;
|
2012-11-18 18:23:22 +08:00
|
|
|
|
2012-01-11 04:51:09 +08:00
|
|
|
wcstring_list_t path_list;
|
2017-08-06 12:40:27 +08:00
|
|
|
path_var.to_list(path_list);
|
2006-02-09 01:37:18 +08:00
|
|
|
|
2016-05-01 11:31:02 +08:00
|
|
|
for (i = 0; i < path_list.size(); i++) {
|
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);
|
2016-05-01 11:31:02 +08:00
|
|
|
if (!dir) continue;
|
2012-11-19 08:30:30 +08:00
|
|
|
|
|
|
|
wcstring name;
|
2016-05-01 11:31:02 +08:00
|
|
|
while (wreaddir(dir, name)) {
|
2012-11-19 08:30:30 +08:00
|
|
|
const wchar_t *fn = name.c_str();
|
|
|
|
const wchar_t *suffix;
|
2016-05-01 11:31:02 +08:00
|
|
|
if (!get_hidden && fn[0] == L'_') continue;
|
2012-11-19 08:30:30 +08:00
|
|
|
|
|
|
|
suffix = wcsrchr(fn, L'.');
|
2016-05-01 11:31:02 +08:00
|
|
|
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
|
|
|
}
|
|
|
|
|
2016-05-01 11:31:02 +08:00
|
|
|
void function_init() {
|
|
|
|
// 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;
|
2017-02-15 13:09:15 +08:00
|
|
|
DIE_ON_FAILURE(pthread_mutexattr_init(&a));
|
|
|
|
DIE_ON_FAILURE(pthread_mutexattr_settype(&a, PTHREAD_MUTEX_RECURSIVE));
|
|
|
|
DIE_ON_FAILURE(pthread_mutex_init(&functions_lock, &a));
|
|
|
|
DIE_ON_FAILURE(pthread_mutexattr_destroy(&a));
|
2005-09-20 21:26:39 +08:00
|
|
|
}
|
|
|
|
|
2016-05-01 11:31:02 +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) {
|
2017-08-06 09:22:49 +08:00
|
|
|
result.insert(std::make_pair(*it, env_get(*it)));
|
2014-10-03 06:59:24 +08:00
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2016-05-01 11:31:02 +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),
|
|
|
|
inherit_vars(snapshot_vars(data.inherit_vars)),
|
|
|
|
is_autoload(autoload),
|
2016-05-09 06:57:56 +08:00
|
|
|
shadow_scope(data.shadow_scope) {}
|
2016-05-01 11:31:02 +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),
|
|
|
|
inherit_vars(data.inherit_vars),
|
|
|
|
is_autoload(autoload),
|
2016-05-09 06:57:56 +08:00
|
|
|
shadow_scope(data.shadow_scope) {}
|
2016-05-01 11:31:02 +08:00
|
|
|
|
|
|
|
void function_add(const function_data_t &data, const parser_t &parser, int definition_line_offset) {
|
2016-10-10 05:38:26 +08:00
|
|
|
UNUSED(parser);
|
2012-03-04 07:20:30 +08:00
|
|
|
ASSERT_IS_MAIN_THREAD();
|
2012-11-18 18:23:22 +08:00
|
|
|
|
2016-10-24 04:58:12 +08:00
|
|
|
CHECK(!data.name.empty(), ); //!OCLINT(multiple unary operator)
|
2016-05-01 11:31:02 +08:00
|
|
|
CHECK(data.definition, );
|
2016-07-21 13:30:58 +08:00
|
|
|
scoped_lock locker(functions_lock);
|
2012-11-18 18:23:22 +08:00
|
|
|
|
2016-05-01 11:31:02 +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
|
|
|
|
2016-05-01 11:31:02 +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
|
|
|
|
2016-05-01 11:31:02 +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
|
|
|
|
2016-05-01 11:31:02 +08:00
|
|
|
// Add event handlers.
|
|
|
|
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
|
|
|
}
|
|
|
|
|
2016-05-01 11:31:02 +08:00
|
|
|
int function_exists(const wcstring &cmd) {
|
|
|
|
if (parser_keywords_is_reserved(cmd)) return 0;
|
2016-07-21 13:30:58 +08:00
|
|
|
scoped_lock locker(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
|
|
|
|
2016-05-01 11:31:02 +08:00
|
|
|
void function_load(const wcstring &cmd) {
|
|
|
|
if (!parser_keywords_is_reserved(cmd)) {
|
2016-07-21 13:30:58 +08:00
|
|
|
scoped_lock locker(functions_lock);
|
2015-10-08 09:59:41 +08:00
|
|
|
load(cmd);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-05-01 11:31:02 +08:00
|
|
|
int function_exists_no_autoload(const wcstring &cmd, const env_vars_snapshot_t &vars) {
|
|
|
|
if (parser_keywords_is_reserved(cmd)) return 0;
|
2016-07-21 13:30:58 +08:00
|
|
|
scoped_lock locker(functions_lock);
|
2016-05-01 11:31:02 +08:00
|
|
|
return loaded_functions.find(cmd) != loaded_functions.end() ||
|
|
|
|
function_autoloader.can_load(cmd, vars);
|
2005-09-20 21:26:39 +08:00
|
|
|
}
|
|
|
|
|
2016-05-01 11:31:02 +08:00
|
|
|
static bool function_remove_ignore_autoload(const wcstring &name, bool tombstone) {
|
|
|
|
// Note: the lock may be held at this point, but is recursive.
|
2016-07-21 13:30:58 +08:00
|
|
|
scoped_lock locker(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);
|
|
|
|
|
2016-05-01 11:31:02 +08:00
|
|
|
// Not found. Not erasing.
|
|
|
|
if (iter == loaded_functions.end()) return false;
|
2015-03-11 21:14:56 +08:00
|
|
|
|
2016-05-01 11:31:02 +08:00
|
|
|
// Removing an auto-loaded function. Prevent it from being auto-reloaded.
|
|
|
|
if (iter->second.is_autoload && tombstone) function_tombstones.insert(name);
|
2015-03-11 21:14:56 +08:00
|
|
|
|
|
|
|
loaded_functions.erase(iter);
|
|
|
|
event_t ev(EVENT_ANY);
|
2016-05-01 11:31:02 +08:00
|
|
|
ev.function_name = name;
|
2015-03-11 21:14:56 +08:00
|
|
|
event_remove(ev);
|
|
|
|
return true;
|
2012-01-26 10:40:08 +08:00
|
|
|
}
|
2006-02-05 21:10:35 +08:00
|
|
|
|
2016-05-01 11:31:02 +08:00
|
|
|
void function_remove(const wcstring &name) {
|
|
|
|
if (function_remove_ignore_autoload(name)) function_autoloader.unload(name);
|
2012-01-24 12:32:36 +08:00
|
|
|
}
|
|
|
|
|
2016-05-01 11:31:02 +08:00
|
|
|
static const function_info_t *function_get(const wcstring &name) {
|
|
|
|
// 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);
|
2016-05-01 11:31:02 +08:00
|
|
|
if (iter == loaded_functions.end()) {
|
2012-05-19 05:00:36 +08:00
|
|
|
return NULL;
|
2012-01-24 12:32:36 +08:00
|
|
|
}
|
2016-05-05 06:19:47 +08:00
|
|
|
return &iter->second;
|
2005-09-20 21:26:39 +08:00
|
|
|
}
|
2012-11-18 18:23:22 +08:00
|
|
|
|
2016-05-01 11:31:02 +08:00
|
|
|
bool function_get_definition(const wcstring &name, wcstring *out_definition) {
|
2016-07-21 13:30:58 +08:00
|
|
|
scoped_lock locker(functions_lock);
|
2012-05-19 05:00:36 +08:00
|
|
|
const function_info_t *func = function_get(name);
|
2016-05-01 11:31:02 +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
|
|
|
|
2016-05-01 11:31:02 +08:00
|
|
|
wcstring_list_t function_get_named_arguments(const wcstring &name) {
|
2016-07-21 13:30:58 +08:00
|
|
|
scoped_lock locker(functions_lock);
|
2012-05-19 05:00:36 +08:00
|
|
|
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
|
|
|
}
|
|
|
|
|
2016-05-01 11:31:02 +08:00
|
|
|
std::map<wcstring, env_var_t> function_get_inherit_vars(const wcstring &name) {
|
2016-07-21 13:30:58 +08:00
|
|
|
scoped_lock locker(functions_lock);
|
2014-10-03 06:59:24 +08:00
|
|
|
const function_info_t *func = function_get(name);
|
2016-05-01 11:31:02 +08:00
|
|
|
return func ? func->inherit_vars : std::map<wcstring, env_var_t>();
|
2014-10-03 06:59:24 +08:00
|
|
|
}
|
|
|
|
|
2017-01-10 14:49:33 +08:00
|
|
|
bool function_get_shadow_scope(const wcstring &name) {
|
2016-07-21 13:30:58 +08:00
|
|
|
scoped_lock locker(functions_lock);
|
2016-05-09 06:57:56 +08:00
|
|
|
const function_info_t *func = function_get(name);
|
|
|
|
return func ? func->shadow_scope : false;
|
2007-04-23 06:10:33 +08:00
|
|
|
}
|
|
|
|
|
2016-05-01 11:31:02 +08:00
|
|
|
bool function_get_desc(const wcstring &name, wcstring *out_desc) {
|
|
|
|
// Empty length string goes to NULL.
|
2016-07-21 13:30:58 +08:00
|
|
|
scoped_lock locker(functions_lock);
|
2012-05-19 05:00:36 +08:00
|
|
|
const function_info_t *func = function_get(name);
|
2016-05-01 11:31:02 +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-01-24 12:43:39 +08:00
|
|
|
}
|
2016-10-29 08:42:50 +08:00
|
|
|
|
|
|
|
return false;
|
2005-09-20 21:26:39 +08:00
|
|
|
}
|
|
|
|
|
2016-05-01 11:31:02 +08:00
|
|
|
void function_set_desc(const wcstring &name, const wcstring &desc) {
|
2012-11-19 08:30:30 +08:00
|
|
|
load(name);
|
2016-07-21 13:30:58 +08:00
|
|
|
scoped_lock locker(functions_lock);
|
2012-05-19 05:00:36 +08:00
|
|
|
function_map_t::iterator iter = loaded_functions.find(name);
|
2016-05-01 11:31:02 +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
|
|
|
}
|
|
|
|
|
2016-05-01 11:31:02 +08:00
|
|
|
bool function_copy(const wcstring &name, const wcstring &new_name) {
|
2012-03-04 07:20:30 +08:00
|
|
|
bool result = false;
|
2016-07-21 13:30:58 +08:00
|
|
|
scoped_lock locker(functions_lock);
|
2012-01-14 15:44:18 +08:00
|
|
|
function_map_t::const_iterator iter = loaded_functions.find(name);
|
2016-05-01 11:31:02 +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.
|
|
|
|
const function_map_t::value_type new_pair(new_name,
|
|
|
|
function_info_t(iter->second, NULL, 0, false));
|
2012-05-19 05:00:36 +08:00
|
|
|
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
|
|
|
}
|
|
|
|
|
2016-05-01 11:31:02 +08:00
|
|
|
wcstring_list_t function_get_names(int get_hidden) {
|
2012-01-14 15:44:18 +08:00
|
|
|
std::set<wcstring> names;
|
2016-07-21 13:30:58 +08:00
|
|
|
scoped_lock locker(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;
|
2016-05-01 11:31:02 +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
|
|
|
|
2016-05-01 11:31:02 +08:00
|
|
|
// Maybe skip hidden.
|
2016-10-23 02:21:13 +08:00
|
|
|
if (!get_hidden && (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
|
|
|
|
2016-05-01 11:31:02 +08:00
|
|
|
const wchar_t *function_get_definition_file(const wcstring &name) {
|
2016-07-21 13:30:58 +08:00
|
|
|
scoped_lock locker(functions_lock);
|
2012-05-19 05:00:36 +08:00
|
|
|
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
|
|
|
}
|
|
|
|
|
2017-01-10 14:49:33 +08:00
|
|
|
bool function_is_autoloaded(const wcstring &name) {
|
|
|
|
scoped_lock locker(functions_lock);
|
|
|
|
const function_info_t *func = function_get(name);
|
|
|
|
return func->is_autoload;
|
|
|
|
}
|
|
|
|
|
2016-05-01 11:31:02 +08:00
|
|
|
int function_get_definition_offset(const wcstring &name) {
|
2016-07-21 13:30:58 +08:00
|
|
|
scoped_lock locker(functions_lock);
|
2012-05-19 05:00:36 +08:00
|
|
|
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
|
|
|
|
2017-08-06 06:08:39 +08:00
|
|
|
// Setup the environment for the function. There are three components of the environment:
|
|
|
|
// 1. argv
|
|
|
|
// 2. named arguments
|
|
|
|
// 3. inherited variables
|
2016-05-01 11:31:02 +08:00
|
|
|
void function_prepare_environment(const wcstring &name, const wchar_t *const *argv,
|
|
|
|
const std::map<wcstring, env_var_t> &inherited_vars) {
|
2015-08-16 04:37:17 +08:00
|
|
|
env_set_argv(argv);
|
2016-05-01 11:31:02 +08:00
|
|
|
|
2015-08-16 04:37:17 +08:00
|
|
|
const wcstring_list_t named_arguments = function_get_named_arguments(name);
|
2016-05-01 11:31:02 +08:00
|
|
|
if (!named_arguments.empty()) {
|
2017-08-06 06:08:39 +08:00
|
|
|
const wchar_t *const *arg = argv;
|
|
|
|
for (size_t i = 0; i < named_arguments.size(); i++) {
|
|
|
|
if (*arg) {
|
|
|
|
env_set_one(named_arguments.at(i), ENV_LOCAL | ENV_USER, *arg);
|
|
|
|
arg++;
|
|
|
|
} else {
|
|
|
|
env_set_empty(named_arguments.at(i), ENV_LOCAL | ENV_USER);
|
|
|
|
}
|
2015-08-16 04:37:17 +08:00
|
|
|
}
|
|
|
|
}
|
2016-05-01 11:31:02 +08:00
|
|
|
|
2017-08-06 06:08:39 +08:00
|
|
|
for (auto it = inherited_vars.begin(), end = inherited_vars.end(); it != end; ++it) {
|
|
|
|
// Note: Prior to my rewrite to address issue #4200 this code did the equivalent of this:
|
|
|
|
// if (it->second.missing()) {
|
|
|
|
// env_set_empty(it->first, ENV_LOCAL | ENV_USER);
|
|
|
|
// } else {
|
|
|
|
// It should be impossible for the var to be missing since we're inheriting it from an outer
|
|
|
|
// scope. So we now die horribly if it is missing.
|
|
|
|
assert(!it->second.missing());
|
|
|
|
env_set(it->first, ENV_LOCAL | ENV_USER, it->second.as_const_list());
|
2015-08-16 04:37:17 +08:00
|
|
|
}
|
|
|
|
}
|