Remove trailing whitespaces and change tabs to spaces

This commit is contained in:
Łukasz Niemier 2012-11-18 11:23:22 +01:00
parent b79854ad1a
commit 47df1ae40a
140 changed files with 29549 additions and 29549 deletions

View file

@ -1,3 +1,3 @@
24-01-2012 Jan Kanis 24-01-2012 Jan Kanis
* Added a changelog file * Added a changelog file
* removed unescaping if the 'commandline' builtin is called without the -o (tokenise) flag * removed unescaping if the 'commandline' builtin is called without the -o (tokenise) flag

View file

@ -1,5 +1,5 @@
/** \file autoload.cpp /** \file autoload.cpp
The classes responsible for autoloading functions and completions. The classes responsible for autoloading functions and completions.
*/ */
@ -31,7 +31,7 @@ file_access_attempt_t access_file(const wcstring &path, int mode) {
result.accessible = true; result.accessible = true;
} }
} }
// Note that we record the last checked time after the call, on the assumption that in a slow filesystem, the lag comes before the kernel check, not after. // Note that we record the last checked time after the call, on the assumption that in a slow filesystem, the lag comes before the kernel check, not after.
result.stale = false; result.stale = false;
result.last_checked = time(NULL); result.last_checked = time(NULL);
@ -56,7 +56,7 @@ autoload_t::~autoload_t() {
void autoload_t::node_was_evicted(autoload_function_t *node) { void autoload_t::node_was_evicted(autoload_function_t *node) {
// This should only ever happen on the main thread // This should only ever happen on the main thread
ASSERT_IS_MAIN_THREAD(); ASSERT_IS_MAIN_THREAD();
// Tell ourselves that the command was removed if it was loaded // Tell ourselves that the command was removed if it was loaded
if (! node->is_loaded) if (! node->is_loaded)
this->command_removed(node->key); this->command_removed(node->key);
@ -70,18 +70,18 @@ int autoload_t::unload( const wcstring &cmd )
int autoload_t::load( const wcstring &cmd, bool reload ) int autoload_t::load( const wcstring &cmd, bool reload )
{ {
int res; int res;
CHECK_BLOCK( 0 ); CHECK_BLOCK( 0 );
ASSERT_IS_MAIN_THREAD(); ASSERT_IS_MAIN_THREAD();
env_var_t path_var = env_get_string( env_var_name ); env_var_t path_var = env_get_string( env_var_name );
/* /*
Do we know where to look? Do we know where to look?
*/ */
if( path_var.empty() ) if( path_var.empty() )
return 0; return 0;
/* Check if the lookup path has changed. If so, drop all loaded files. path_var may only be inspected on the main thread. */ /* Check if the lookup path has changed. If so, drop all loaded files. path_var may only be inspected on the main thread. */
if( path_var != this->last_path ) if( path_var != this->last_path )
{ {
@ -89,32 +89,32 @@ int autoload_t::load( const wcstring &cmd, bool reload )
scoped_lock locker(lock); scoped_lock locker(lock);
this->evict_all_nodes(); this->evict_all_nodes();
} }
/** Warn and fail on infinite recursion. It's OK to do this because this function is only called on the main thread. */ /** Warn and fail on infinite recursion. It's OK to do this because this function is only called on the main thread. */
if (this->is_loading(cmd)) if (this->is_loading(cmd))
{ {
debug( 0, debug( 0,
_( L"Could not autoload item '%ls', it is already being autoloaded. " _( L"Could not autoload item '%ls', it is already being autoloaded. "
L"This is a circular dependency in the autoloading scripts, please remove it."), L"This is a circular dependency in the autoloading scripts, please remove it."),
cmd.c_str() ); cmd.c_str() );
return 1; return 1;
} }
/* Mark that we're loading this */ /* Mark that we're loading this */
is_loading_set.insert(cmd); is_loading_set.insert(cmd);
/* Get the list of paths from which we will try to load */ /* Get the list of paths from which we will try to load */
std::vector<wcstring> path_list; std::vector<wcstring> path_list;
tokenize_variable_array( path_var, path_list ); tokenize_variable_array( path_var, path_list );
/* Try loading it */
res = this->locate_file_and_maybe_load_it( cmd, true, reload, path_list );
/* Try loading it */
res = this->locate_file_and_maybe_load_it( cmd, true, reload, path_list );
/* Clean up */ /* Clean up */
bool erased = !! is_loading_set.erase(cmd); bool erased = !! is_loading_set.erase(cmd);
assert(erased); assert(erased);
return res; return res;
} }
bool autoload_t::can_load( const wcstring &cmd, const env_vars_snapshot_t &vars ) bool autoload_t::can_load( const wcstring &cmd, const env_vars_snapshot_t &vars )
@ -124,7 +124,7 @@ bool autoload_t::can_load( const wcstring &cmd, const env_vars_snapshot_t &vars
return false; return false;
std::vector<wcstring> path_list; std::vector<wcstring> path_list;
tokenize_variable_array( path_var, path_list ); tokenize_variable_array( path_var, path_list );
return this->locate_file_and_maybe_load_it( cmd, false, false, path_list ); return this->locate_file_and_maybe_load_it( cmd, false, false, path_list );
} }
@ -141,7 +141,7 @@ void autoload_t::unload_all(void) {
/** Check whether the given command is loaded. */ /** Check whether the given command is loaded. */
bool autoload_t::has_tried_loading( const wcstring &cmd ) bool autoload_t::has_tried_loading( const wcstring &cmd )
{ {
scoped_lock locker(lock); scoped_lock locker(lock);
autoload_function_t * func = this->get_node(cmd); autoload_function_t * func = this->get_node(cmd);
return func != NULL; return func != NULL;
} }
@ -170,30 +170,30 @@ autoload_function_t *autoload_t::get_autoloaded_function_with_creation(const wcs
This internal helper function does all the real work. By using two This internal helper function does all the real work. By using two
functions, the internal function can return on various places in functions, the internal function can return on various places in
the code, and the caller can take care of various cleanup work. the code, and the caller can take care of various cleanup work.
cmd: the command name ('grep') cmd: the command name ('grep')
really_load: whether to actually parse it as a function, or just check it it exists really_load: whether to actually parse it as a function, or just check it it exists
reload: whether to reload it if it's already loaded reload: whether to reload it if it's already loaded
path_list: the set of paths to check path_list: the set of paths to check
Result: if really_load is true, returns whether the function was loaded. Otherwise returns whether the function existed. Result: if really_load is true, returns whether the function was loaded. Otherwise returns whether the function existed.
*/ */
bool autoload_t::locate_file_and_maybe_load_it( const wcstring &cmd, bool really_load, bool reload, const wcstring_list_t &path_list ) bool autoload_t::locate_file_and_maybe_load_it( const wcstring &cmd, bool really_load, bool reload, const wcstring_list_t &path_list )
{ {
/* Note that we are NOT locked in this function! */ /* Note that we are NOT locked in this function! */
size_t i; size_t i;
bool reloaded = 0; bool reloaded = 0;
/* Try using a cached function. If we really want the function to be loaded, require that it be really loaded. If we're not reloading, allow stale functions. */ /* Try using a cached function. If we really want the function to be loaded, require that it be really loaded. If we're not reloading, allow stale functions. */
{ {
bool allow_stale_functions = ! reload; bool allow_stale_functions = ! reload;
/* Take a lock */ /* Take a lock */
scoped_lock locker(lock); scoped_lock locker(lock);
/* Get the function */ /* Get the function */
autoload_function_t * func = this->get_node(cmd); autoload_function_t * func = this->get_node(cmd);
/* Determine if we can use this cached function */ /* Determine if we can use this cached function */
bool use_cached; bool use_cached;
if (! func) { if (! func) {
@ -209,19 +209,19 @@ bool autoload_t::locate_file_and_maybe_load_it( const wcstring &cmd, bool really
/* I guess we can use it */ /* I guess we can use it */
use_cached = true; use_cached = true;
} }
/* If we can use this function, return whether we were able to access it */ /* If we can use this function, return whether we were able to access it */
if (use_cached) { if (use_cached) {
return func->is_internalized || func->access.accessible; return func->is_internalized || func->access.accessible;
} }
} }
/* The source of the script will end up here */ /* The source of the script will end up here */
wcstring script_source; wcstring script_source;
bool has_script_source = false; bool has_script_source = false;
/* Whether we found an accessible file */ /* Whether we found an accessible file */
bool found_file = false; bool found_file = false;
/* Look for built-in scripts via a binary search */ /* Look for built-in scripts via a binary search */
const builtin_script_t *matching_builtin_script = NULL; const builtin_script_t *matching_builtin_script = NULL;
if (builtin_script_count > 0) if (builtin_script_count > 0)
@ -238,18 +238,18 @@ bool autoload_t::locate_file_and_maybe_load_it( const wcstring &cmd, bool really
if (matching_builtin_script) { if (matching_builtin_script) {
has_script_source = true; has_script_source = true;
script_source = str2wcstring(matching_builtin_script->def); script_source = str2wcstring(matching_builtin_script->def);
/* Make a node representing this function */ /* Make a node representing this function */
scoped_lock locker(lock); scoped_lock locker(lock);
autoload_function_t *func = this->get_autoloaded_function_with_creation(cmd, really_load); autoload_function_t *func = this->get_autoloaded_function_with_creation(cmd, really_load);
/* This function is internalized */ /* This function is internalized */
func->is_internalized = true; func->is_internalized = true;
/* It's a fiction to say the script is loaded at this point, but we're definitely going to load it down below. */ /* It's a fiction to say the script is loaded at this point, but we're definitely going to load it down below. */
if (really_load) func->is_loaded = true; if (really_load) func->is_loaded = true;
} }
if (! has_script_source) if (! has_script_source)
{ {
/* Iterate over path searching for suitable completion files */ /* Iterate over path searching for suitable completion files */
@ -262,38 +262,38 @@ bool autoload_t::locate_file_and_maybe_load_it( const wcstring &cmd, bool really
if (access.accessible) { if (access.accessible) {
/* Found it! */ /* Found it! */
found_file = true; found_file = true;
/* Now we're actually going to take the lock. */ /* Now we're actually going to take the lock. */
scoped_lock locker(lock); scoped_lock locker(lock);
autoload_function_t *func = this->get_node(cmd); autoload_function_t *func = this->get_node(cmd);
/* Generate the source if we need to load it */ /* Generate the source if we need to load it */
bool need_to_load_function = really_load && (func == NULL || func->access.mod_time != access.mod_time || ! func->is_loaded); bool need_to_load_function = really_load && (func == NULL || func->access.mod_time != access.mod_time || ! func->is_loaded);
if (need_to_load_function) { if (need_to_load_function) {
/* Generate the script source */ /* Generate the script source */
wcstring esc = escape_string(path, 1); wcstring esc = escape_string(path, 1);
script_source = L". " + esc; script_source = L". " + esc;
has_script_source = true; has_script_source = true;
/* Remove any loaded command because we are going to reload it. Note that this will deadlock if command_removed calls back into us. */ /* Remove any loaded command because we are going to reload it. Note that this will deadlock if command_removed calls back into us. */
if (func && func->is_loaded) { if (func && func->is_loaded) {
command_removed(cmd); command_removed(cmd);
func->is_placeholder = false; func->is_placeholder = false;
} }
/* Mark that we're reloading it */ /* Mark that we're reloading it */
reloaded = true; reloaded = true;
} }
/* Create the function if we haven't yet. This does not load it. Do not trigger eviction unless we are actually loading, because we don't want to evict off of the main thread. */ /* Create the function if we haven't yet. This does not load it. Do not trigger eviction unless we are actually loading, because we don't want to evict off of the main thread. */
if (! func) { if (! func) {
func = get_autoloaded_function_with_creation(cmd, really_load); func = get_autoloaded_function_with_creation(cmd, really_load);
} }
/* It's a fiction to say the script is loaded at this point, but we're definitely going to load it down below. */ /* It's a fiction to say the script is loaded at this point, but we're definitely going to load it down below. */
if (need_to_load_function) func->is_loaded = true; if (need_to_load_function) func->is_loaded = true;
/* Unconditionally record our access time */ /* Unconditionally record our access time */
func->access = access; func->access = access;
@ -323,7 +323,7 @@ bool autoload_t::locate_file_and_maybe_load_it( const wcstring &cmd, bool really
func->access.last_checked = time(NULL); func->access.last_checked = time(NULL);
} }
} }
/* If we have a script, either built-in or a file source, then run it */ /* If we have a script, either built-in or a file source, then run it */
if (really_load && has_script_source) if (really_load && has_script_source)
{ {

View file

@ -25,7 +25,7 @@ struct file_access_attempt_t {
file_access_attempt_t access_file(const wcstring &path, int mode); file_access_attempt_t access_file(const wcstring &path, int mode);
struct autoload_function_t : public lru_node_t struct autoload_function_t : public lru_node_t
{ {
autoload_function_t(const wcstring &key) : lru_node_t(key), access(), is_loaded(false), is_placeholder(false), is_internalized(false) { } autoload_function_t(const wcstring &key) : lru_node_t(key), access(), is_loaded(false), is_placeholder(false), is_internalized(false) { }
file_access_attempt_t access; /** The last access attempt */ file_access_attempt_t access; /** The last access attempt */
bool is_loaded; /** Whether we have actually loaded this function */ bool is_loaded; /** Whether we have actually loaded this function */
@ -48,61 +48,61 @@ private:
/** The environment variable name */ /** The environment variable name */
const wcstring env_var_name; const wcstring env_var_name;
/** Builtin script array */ /** Builtin script array */
const struct builtin_script_t *const builtin_scripts; const struct builtin_script_t *const builtin_scripts;
/** Builtin script count */ /** Builtin script count */
const size_t builtin_script_count; const size_t builtin_script_count;
/** The path from which we most recently autoloaded */ /** The path from which we most recently autoloaded */
wcstring last_path; wcstring last_path;
/** /**
A table containing all the files that are currently being A table containing all the files that are currently being
loaded. This is here to help prevent recursion. loaded. This is here to help prevent recursion.
*/ */
std::set<wcstring> is_loading_set; std::set<wcstring> is_loading_set;
bool is_loading(const wcstring &name) const { bool is_loading(const wcstring &name) const {
return is_loading_set.find(name) != is_loading_set.end(); return is_loading_set.find(name) != is_loading_set.end();
} }
void remove_all_functions(void) { void remove_all_functions(void) {
this->evict_all_nodes(); this->evict_all_nodes();
} }
bool locate_file_and_maybe_load_it( const wcstring &cmd, bool really_load, bool reload, const wcstring_list_t &path_list ); bool locate_file_and_maybe_load_it( const wcstring &cmd, bool really_load, bool reload, const wcstring_list_t &path_list );
virtual void node_was_evicted(autoload_function_t *node); virtual void node_was_evicted(autoload_function_t *node);
autoload_function_t *get_autoloaded_function_with_creation(const wcstring &cmd, bool allow_eviction); autoload_function_t *get_autoloaded_function_with_creation(const wcstring &cmd, bool allow_eviction);
protected: protected:
/** Overridable callback for when a command is removed */ /** Overridable callback for when a command is removed */
virtual void command_removed(const wcstring &cmd) { } virtual void command_removed(const wcstring &cmd) { }
public: public:
/** Create an autoload_t for the given environment variable name */ /** Create an autoload_t for the given environment variable name */
autoload_t(const wcstring &env_var_name_var, const builtin_script_t *scripts, size_t script_count ); autoload_t(const wcstring &env_var_name_var, const builtin_script_t *scripts, size_t script_count );
/** Destructor */ /** Destructor */
virtual ~autoload_t(); virtual ~autoload_t();
/** /**
Autoload the specified file, if it exists in the specified path. Do Autoload the specified file, if it exists in the specified path. Do
not load it multiple times unless it's timestamp changes or not load it multiple times unless it's timestamp changes or
parse_util_unload is called. parse_util_unload is called.
Autoloading one file may unload another. Autoloading one file may unload another.
\param cmd the filename to search for. The suffix '.fish' is always added to this name \param cmd the filename to search for. The suffix '.fish' is always added to this name
\param on_unload a callback function to run if a suitable file is found, which has not already been run. unload will also be called for old files which are unloaded. \param on_unload a callback function to run if a suitable file is found, which has not already been run. unload will also be called for old files which are unloaded.
\param reload wheter to recheck file timestamps on already loaded files \param reload wheter to recheck file timestamps on already loaded files
*/ */
int load( const wcstring &cmd, bool reload ); int load( const wcstring &cmd, bool reload );
/** Check whether we have tried loading the given command. Does not do any I/O. */ /** Check whether we have tried loading the given command. Does not do any I/O. */
bool has_tried_loading( const wcstring &cmd ); bool has_tried_loading( const wcstring &cmd );
@ -115,12 +115,12 @@ private:
\return non-zero if the file was removed, zero if the file had not yet been loaded \return non-zero if the file was removed, zero if the file had not yet been loaded
*/ */
int unload( const wcstring &cmd ); int unload( const wcstring &cmd );
/** /**
Unloads all files. Unloads all files.
*/ */
void unload_all( ); void unload_all( );
/** Check whether the given command could be loaded, but do not load it. */ /** Check whether the given command could be loaded, but do not load it. */
bool can_load( const wcstring &cmd, const env_vars_snapshot_t &vars ); bool can_load( const wcstring &cmd, const env_vars_snapshot_t &vars );

View file

@ -13,25 +13,25 @@ This release is beta r2.\
{\colortbl;\red255\green255\blue255;} {\colortbl;\red255\green255\blue255;}
\pard\tx720\tx1440\tx2160\tx2880\tx3600\tx4320\tx5040\tx5760\tx6480\tx7200\tx7920\tx8640\pardirnatural \pard\tx720\tx1440\tx2160\tx2880\tx3600\tx4320\tx5040\tx5760\tx6480\tx7200\tx7920\tx8640\pardirnatural
\f0\fs26 \cf0 Run \f0\fs26 \cf0 Run
\f1 fish \f1 fish
\f0 at the command line to start it up! Some useful commands:\ \f0 at the command line to start it up! Some useful commands:\
\ \
Interactively set your colors from a web page:\ Interactively set your colors from a web page:\
\ \
\f1 fish_config \f1 fish_config
\f0 \ \f0 \
\ \
Update man-page completions:\ Update man-page completions:\
\ \
\f1 fish_update_completions \f1 fish_update_completions
\f0 \ \f0 \
\ \
Make fish your default shell:\ Make fish your default shell:\
\ \
\f1 chsh -s /usr/local/bin/fish\ \f1 chsh -s /usr/local/bin/fish\
\f0 \ \f0 \

File diff suppressed because it is too large Load diff

View file

@ -1,5 +1,5 @@
/** \file builtin.h /** \file builtin.h
Prototypes for functions for executing builtin functions. Prototypes for functions for executing builtin functions.
*/ */
#ifndef FISH_BUILTIN_H #ifndef FISH_BUILTIN_H
@ -15,9 +15,9 @@ class parser_t;
enum enum
{ {
COMMAND_NOT_BUILTIN, COMMAND_NOT_BUILTIN,
BUILTIN_REGULAR, BUILTIN_REGULAR,
BUILTIN_FUNCTION BUILTIN_FUNCTION
} }
; ;
@ -49,7 +49,7 @@ enum
/** /**
Error message for unknown switch Error message for unknown switch
*/ */
#define BUILTIN_ERR_UNKNOWN _( L"%ls: Unknown option '%ls'\n" ) #define BUILTIN_ERR_UNKNOWN _( L"%ls: Unknown option '%ls'\n" )
/** /**
Error message for invalid character in variable name Error message for invalid character in variable name
@ -67,7 +67,7 @@ enum
#define BUILTIN_FOR_ERR_IN _( L"%ls: Second argument must be 'in'\n" ) #define BUILTIN_FOR_ERR_IN _( L"%ls: Second argument must be 'in'\n" )
/** /**
Error message for insufficient number of arguments Error message for insufficient number of arguments
*/ */
#define BUILTIN_FOR_ERR_COUNT _( L"%ls: Expected at least two arguments, got %d\n") #define BUILTIN_FOR_ERR_COUNT _( L"%ls: Expected at least two arguments, got %d\n")
@ -113,7 +113,7 @@ extern int builtin_err_redirect;
/** /**
Initialize builtin data. Initialize builtin data.
*/ */
void builtin_init(); void builtin_init();
@ -128,12 +128,12 @@ void builtin_destroy();
int builtin_exists( const wcstring &cmd ); int builtin_exists( const wcstring &cmd );
/** /**
Execute a builtin command Execute a builtin command
\param parser The parser being used \param parser The parser being used
\param argv Array containing the command and parameters \param argv Array containing the command and parameters
of the builtin. The list is terminated by a of the builtin. The list is terminated by a
null pointer. This syntax resembles the syntax null pointer. This syntax resembles the syntax
for exec. for exec.
\param io the io redirections to perform on this builtin. \param io the io redirections to perform on this builtin.

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -1,5 +1,5 @@
/** \file builtin_jobs.c /** \file builtin_jobs.c
Functions for executing the jobs builtin. Functions for executing the jobs builtin.
*/ */
#include "config.h" #include "config.h"
@ -30,12 +30,12 @@
*/ */
enum enum
{ {
JOBS_DEFAULT, /**< Print lots of general info */ JOBS_DEFAULT, /**< Print lots of general info */
JOBS_PRINT_PID, /**< Print pid of each process in job */ JOBS_PRINT_PID, /**< Print pid of each process in job */
JOBS_PRINT_COMMAND, /**< Print command name of each process in job */ JOBS_PRINT_COMMAND, /**< Print command name of each process in job */
JOBS_PRINT_GROUP, /**< Print group id of job */ JOBS_PRINT_GROUP, /**< Print group id of job */
} }
; ;
@ -45,26 +45,26 @@ enum
*/ */
static int cpu_use( const job_t *j ) static int cpu_use( const job_t *j )
{ {
double u=0; double u=0;
process_t *p; process_t *p;
for( p=j->first_process; p; p=p->next ) for( p=j->first_process; p; p=p->next )
{ {
struct timeval t; struct timeval t;
int jiffies; int jiffies;
gettimeofday( &t, 0 ); gettimeofday( &t, 0 );
jiffies = proc_get_jiffies( p ); jiffies = proc_get_jiffies( p );
double t1 = 1000000.0*p->last_time.tv_sec+p->last_time.tv_usec; double t1 = 1000000.0*p->last_time.tv_sec+p->last_time.tv_usec;
double t2 = 1000000.0*t.tv_sec+t.tv_usec; double t2 = 1000000.0*t.tv_sec+t.tv_usec;
/* fwprintf( stderr, L"t1 %f t2 %f p1 %d p2 %d\n", /* fwprintf( stderr, L"t1 %f t2 %f p1 %d p2 %d\n",
t1, t2, jiffies, p->last_jiffies ); t1, t2, jiffies, p->last_jiffies );
*/ */
u += ((double)(jiffies-p->last_jiffies))/(t2-t1); u += ((double)(jiffies-p->last_jiffies))/(t2-t1);
} }
return u*1000000; return u*1000000;
} }
#endif #endif
@ -73,83 +73,83 @@ static int cpu_use( const job_t *j )
*/ */
static void builtin_jobs_print( const job_t *j, int mode, int header ) static void builtin_jobs_print( const job_t *j, int mode, int header )
{ {
process_t *p; process_t *p;
switch( mode ) switch( mode )
{ {
case JOBS_DEFAULT: case JOBS_DEFAULT:
{ {
if( header ) if( header )
{ {
/* /*
Print table header before first job Print table header before first job
*/ */
stdout_buffer.append( _( L"Job\tGroup\t" )); stdout_buffer.append( _( L"Job\tGroup\t" ));
#ifdef HAVE__PROC_SELF_STAT #ifdef HAVE__PROC_SELF_STAT
stdout_buffer.append( _( L"CPU\t" ) ); stdout_buffer.append( _( L"CPU\t" ) );
#endif #endif
stdout_buffer.append( _( L"State\tCommand\n" ) ); stdout_buffer.append( _( L"State\tCommand\n" ) );
} }
append_format(stdout_buffer, L"%d\t%d\t", j->job_id, j->pgid ); append_format(stdout_buffer, L"%d\t%d\t", j->job_id, j->pgid );
#ifdef HAVE__PROC_SELF_STAT #ifdef HAVE__PROC_SELF_STAT
append_format(stdout_buffer, L"%d%%\t", cpu_use(j) ); append_format(stdout_buffer, L"%d%%\t", cpu_use(j) );
#endif #endif
stdout_buffer.append(job_is_stopped(j)?_(L"stopped"):_(L"running")); stdout_buffer.append(job_is_stopped(j)?_(L"stopped"):_(L"running"));
stdout_buffer.append(L"\t"); stdout_buffer.append(L"\t");
stdout_buffer.append(j->command_wcstr()); stdout_buffer.append(j->command_wcstr());
stdout_buffer.append(L"\n"); stdout_buffer.append(L"\n");
break; break;
} }
case JOBS_PRINT_GROUP: case JOBS_PRINT_GROUP:
{ {
if( header ) if( header )
{ {
/* /*
Print table header before first job Print table header before first job
*/ */
stdout_buffer.append( _( L"Group\n" )); stdout_buffer.append( _( L"Group\n" ));
} }
append_format(stdout_buffer, L"%d\n", j->pgid ); append_format(stdout_buffer, L"%d\n", j->pgid );
break; break;
} }
case JOBS_PRINT_PID: case JOBS_PRINT_PID:
{ {
if( header ) if( header )
{ {
/* /*
Print table header before first job Print table header before first job
*/ */
stdout_buffer.append( _( L"Procces\n" )); stdout_buffer.append( _( L"Procces\n" ));
} }
for( p=j->first_process; p; p=p->next ) for( p=j->first_process; p; p=p->next )
{ {
append_format(stdout_buffer, L"%d\n", p->pid ); append_format(stdout_buffer, L"%d\n", p->pid );
} }
break; break;
} }
case JOBS_PRINT_COMMAND: case JOBS_PRINT_COMMAND:
{ {
if( header ) if( header )
{ {
/* /*
Print table header before first job Print table header before first job
*/ */
stdout_buffer.append( _( L"Command\n" )); stdout_buffer.append( _( L"Command\n" ));
} }
for( p=j->first_process; p; p=p->next ) for( p=j->first_process; p; p=p->next )
{ {
append_format(stdout_buffer, L"%ls\n", p->argv0() ); append_format(stdout_buffer, L"%ls\n", p->argv0() );
} }
break; break;
} }
} }
} }
@ -160,192 +160,192 @@ static void builtin_jobs_print( const job_t *j, int mode, int header )
*/ */
static int builtin_jobs( parser_t &parser, wchar_t **argv ) static int builtin_jobs( parser_t &parser, wchar_t **argv )
{ {
int argc=0; int argc=0;
int found=0; int found=0;
int mode=JOBS_DEFAULT; int mode=JOBS_DEFAULT;
int print_last = 0; int print_last = 0;
const job_t *j; const job_t *j;
argc = builtin_count_args( argv ); argc = builtin_count_args( argv );
woptind=0; woptind=0;
while( 1 ) while( 1 )
{ {
static const struct woption static const struct woption
long_options[] = long_options[] =
{ {
{ {
L"pid", no_argument, 0, 'p' L"pid", no_argument, 0, 'p'
} }
, ,
{ {
L"command", no_argument, 0, 'c' L"command", no_argument, 0, 'c'
} }
, ,
{ {
L"group", no_argument, 0, 'g' L"group", no_argument, 0, 'g'
} }
, ,
{ {
L"last", no_argument, 0, 'l' L"last", no_argument, 0, 'l'
} }
, ,
{ {
L"help", no_argument, 0, 'h' L"help", no_argument, 0, 'h'
} }
, ,
{ {
0, 0, 0, 0 0, 0, 0, 0
} }
} }
; ;
int opt_index = 0; int opt_index = 0;
int opt = wgetopt_long( argc, int opt = wgetopt_long( argc,
argv, argv,
L"pclgh", L"pclgh",
long_options, long_options,
&opt_index ); &opt_index );
if( opt == -1 ) if( opt == -1 )
break; break;
switch( opt ) switch( opt )
{ {
case 0: case 0:
if(long_options[opt_index].flag != 0) if(long_options[opt_index].flag != 0)
break; break;
append_format(stderr_buffer, append_format(stderr_buffer,
BUILTIN_ERR_UNKNOWN, BUILTIN_ERR_UNKNOWN,
argv[0], argv[0],
long_options[opt_index].name ); long_options[opt_index].name );
builtin_print_help( parser, argv[0], stderr_buffer ); builtin_print_help( parser, argv[0], stderr_buffer );
return 1; return 1;
case 'p': case 'p':
mode=JOBS_PRINT_PID; mode=JOBS_PRINT_PID;
break; break;
case 'c': case 'c':
mode=JOBS_PRINT_COMMAND; mode=JOBS_PRINT_COMMAND;
break; break;
case 'g': case 'g':
mode=JOBS_PRINT_GROUP; mode=JOBS_PRINT_GROUP;
break; break;
case 'l': case 'l':
{ {
print_last = 1; print_last = 1;
break; break;
} }
case 'h': case 'h':
builtin_print_help( parser, argv[0], stdout_buffer ); builtin_print_help( parser, argv[0], stdout_buffer );
return 0; return 0;
case '?': case '?':
builtin_unknown_option( parser, argv[0], argv[woptind-1] ); builtin_unknown_option( parser, argv[0], argv[woptind-1] );
return 1; return 1;
} }
} }
/* /*
Do not babble if not interactive Do not babble if not interactive
*/ */
if( builtin_out_redirect ) if( builtin_out_redirect )
{ {
found=1; found=1;
} }
if( print_last ) if( print_last )
{ {
/* /*
Ignore unconstructed jobs, i.e. ourself. Ignore unconstructed jobs, i.e. ourself.
*/ */
job_iterator_t jobs; job_iterator_t jobs;
const job_t *j; const job_t *j;
while ((j = jobs.next())) while ((j = jobs.next()))
{ {
if( (j->flags & JOB_CONSTRUCTED) && !job_is_completed(j) )
{
builtin_jobs_print( j, mode, !found );
return 0;
}
}
} if( (j->flags & JOB_CONSTRUCTED) && !job_is_completed(j) )
else {
{ builtin_jobs_print( j, mode, !found );
if( woptind < argc ) return 0;
{ }
int i; }
found = 1; }
else
{
if( woptind < argc )
{
int i;
for( i=woptind; i<argc; i++ ) found = 1;
{
int pid;
wchar_t *end;
errno=0;
pid=fish_wcstoi( argv[i], &end, 10 );
if( errno || *end )
{
append_format(stderr_buffer,
_( L"%ls: '%ls' is not a job\n" ),
argv[0],
argv[i] );
return 1;
}
j = job_get_from_pid( pid ); for( i=woptind; i<argc; i++ )
{
int pid;
wchar_t *end;
errno=0;
pid=fish_wcstoi( argv[i], &end, 10 );
if( errno || *end )
{
append_format(stderr_buffer,
_( L"%ls: '%ls' is not a job\n" ),
argv[0],
argv[i] );
return 1;
}
if( j && !job_is_completed( j ) ) j = job_get_from_pid( pid );
{
builtin_jobs_print( j, mode, !found ); if( j && !job_is_completed( j ) )
} {
else builtin_jobs_print( j, mode, !found );
{ }
append_format(stderr_buffer, else
_( L"%ls: No suitable job: %d\n" ), {
argv[0], append_format(stderr_buffer,
pid ); _( L"%ls: No suitable job: %d\n" ),
return 1; argv[0],
} pid );
} return 1;
} }
else }
{ }
else
{
job_iterator_t jobs; job_iterator_t jobs;
const job_t *j; const job_t *j;
while ((j = jobs.next())) while ((j = jobs.next()))
{ {
/* /*
Ignore unconstructed jobs, i.e. ourself. Ignore unconstructed jobs, i.e. ourself.
*/ */
if( (j->flags & JOB_CONSTRUCTED) && !job_is_completed(j) ) if( (j->flags & JOB_CONSTRUCTED) && !job_is_completed(j) )
{ {
builtin_jobs_print( j, mode, !found ); builtin_jobs_print( j, mode, !found );
found = 1; found = 1;
} }
} }
} }
} }
if( !found ) if( !found )
{ {
append_format(stdout_buffer, append_format(stdout_buffer,
_( L"%ls: There are no jobs\n" ), _( L"%ls: There are no jobs\n" ),
argv[0] ); argv[0] );
} }
return 0; return 0;
} }

File diff suppressed because it is too large Load diff

View file

@ -1,6 +1,6 @@
/** \file builtin_test.cpp Functions defining the test builtin /** \file builtin_test.cpp Functions defining the test builtin
Functions used for implementing the test builtin. Functions used for implementing the test builtin.
Implemented from scratch (yes, really) by way of IEEE 1003.1 as reference. Implemented from scratch (yes, really) by way of IEEE 1003.1 as reference.
*/ */
@ -28,12 +28,12 @@ static const wchar_t * const condstr[] = {
}; };
namespace test_expressions { namespace test_expressions {
enum token_t { enum token_t {
test_unknown, // arbitrary string test_unknown, // arbitrary string
test_bang, // "!", inverts sense test_bang, // "!", inverts sense
test_filetype_b, // "-b", for block special files test_filetype_b, // "-b", for block special files
test_filetype_c, // "-c" for character special files test_filetype_c, // "-c" for character special files
test_filetype_d, // "-d" for directories test_filetype_d, // "-d" for directories
@ -44,44 +44,44 @@ namespace test_expressions {
test_filetype_L, // "-L", same as -h test_filetype_L, // "-L", same as -h
test_filetype_p, // "-p", for FIFO test_filetype_p, // "-p", for FIFO
test_filetype_S, // "-S", socket test_filetype_S, // "-S", socket
test_filesize_s, // "-s", size greater than zero test_filesize_s, // "-s", size greater than zero
test_filedesc_t, // "-t", whether the fd is associated with a terminal test_filedesc_t, // "-t", whether the fd is associated with a terminal
test_fileperm_r, // "-r", read permission test_fileperm_r, // "-r", read permission
test_fileperm_u, // "-u", whether file is setuid test_fileperm_u, // "-u", whether file is setuid
test_fileperm_w, // "-w", whether file write permission is allowed test_fileperm_w, // "-w", whether file write permission is allowed
test_fileperm_x, // "-x", whether file execute/search is allowed test_fileperm_x, // "-x", whether file execute/search is allowed
test_string_n, // "-n", non-empty string test_string_n, // "-n", non-empty string
test_string_z, // "-z", true if length of string is 0 test_string_z, // "-z", true if length of string is 0
test_string_equal, // "=", true if strings are identical test_string_equal, // "=", true if strings are identical
test_string_not_equal, // "!=", true if strings are not identical test_string_not_equal, // "!=", true if strings are not identical
test_number_equal, // "-eq", true if numbers are equal test_number_equal, // "-eq", true if numbers are equal
test_number_not_equal, // "-ne", true if numbers are not equal test_number_not_equal, // "-ne", true if numbers are not equal
test_number_greater, // "-gt", true if first number is larger than second test_number_greater, // "-gt", true if first number is larger than second
test_number_greater_equal, // "-ge", true if first number is at least second test_number_greater_equal, // "-ge", true if first number is at least second
test_number_lesser, // "-lt", true if first number is smaller than second test_number_lesser, // "-lt", true if first number is smaller than second
test_number_lesser_equal, // "-le", true if first number is at most second test_number_lesser_equal, // "-le", true if first number is at most second
test_combine_and, // "-a", true if left and right are both true test_combine_and, // "-a", true if left and right are both true
test_combine_or, // "-o", true if either left or right is true test_combine_or, // "-o", true if either left or right is true
test_paren_open, // "(", open paren test_paren_open, // "(", open paren
test_paren_close, // ")", close paren test_paren_close, // ")", close paren
}; };
static bool binary_primary_evaluate(test_expressions::token_t token, const wcstring &left, const wcstring &right, wcstring_list_t &errors); static bool binary_primary_evaluate(test_expressions::token_t token, const wcstring &left, const wcstring &right, wcstring_list_t &errors);
static bool unary_primary_evaluate(test_expressions::token_t token, const wcstring &arg, wcstring_list_t &errors); static bool unary_primary_evaluate(test_expressions::token_t token, const wcstring &arg, wcstring_list_t &errors);
enum { enum {
UNARY_PRIMARY = 1 << 0, UNARY_PRIMARY = 1 << 0,
BINARY_PRIMARY = 1 << 1 BINARY_PRIMARY = 1 << 1
}; };
static const struct token_info_t { token_t tok; const wchar_t *string; unsigned int flags; } token_infos[] = static const struct token_info_t { token_t tok; const wchar_t *string; unsigned int flags; } token_infos[] =
{ {
{test_unknown, L"", 0}, {test_unknown, L"", 0},
@ -117,7 +117,7 @@ namespace test_expressions {
{test_paren_open, L"(", 0}, {test_paren_open, L"(", 0},
{test_paren_close, L")", 0} {test_paren_close, L")", 0}
}; };
const token_info_t *token_for_string(const wcstring &str) { const token_info_t *token_for_string(const wcstring &str) {
for (size_t i=0; i < sizeof token_infos / sizeof *token_infos; i++) { for (size_t i=0; i < sizeof token_infos / sizeof *token_infos; i++) {
if (str == token_infos[i].string) { if (str == token_infos[i].string) {
@ -127,55 +127,55 @@ namespace test_expressions {
return &token_infos[0]; //unknown return &token_infos[0]; //unknown
} }
/* Grammar. /* Grammar.
<expr> = <combining_expr> <expr> = <combining_expr>
<combining_expr> = <unary_expr> and/or <combining_expr> | <combining_expr> = <unary_expr> and/or <combining_expr> |
<unary_expr> <unary_expr>
<unary_expr> = bang <unary_expr> | <unary_expr> = bang <unary_expr> |
<primary> <primary>
<primary> = <unary_primary> arg | <primary> = <unary_primary> arg |
arg <binary_primary> arg | arg <binary_primary> arg |
'(' <expr> ')' '(' <expr> ')'
*/ */
class expression; class expression;
class test_parser { class test_parser {
private: private:
wcstring_list_t strings; wcstring_list_t strings;
wcstring_list_t errors; wcstring_list_t errors;
expression *error(const wchar_t *fmt, ...); expression *error(const wchar_t *fmt, ...);
void add_error(const wchar_t *fmt, ...); void add_error(const wchar_t *fmt, ...);
const wcstring &arg(unsigned int idx) { return strings.at(idx); } const wcstring &arg(unsigned int idx) { return strings.at(idx); }
public: public:
test_parser(const wcstring_list_t &val) : strings(val) test_parser(const wcstring_list_t &val) : strings(val)
{ } { }
expression *parse_expression(unsigned int start, unsigned int end); expression *parse_expression(unsigned int start, unsigned int end);
expression *parse_combining_expression(unsigned int start, unsigned int end); expression *parse_combining_expression(unsigned int start, unsigned int end);
expression *parse_unary_expression(unsigned int start, unsigned int end); expression *parse_unary_expression(unsigned int start, unsigned int end);
expression *parse_primary(unsigned int start, unsigned int end); expression *parse_primary(unsigned int start, unsigned int end);
expression *parse_parenthentical(unsigned int start, unsigned int end); expression *parse_parenthentical(unsigned int start, unsigned int end);
expression *parse_unary_primary(unsigned int start, unsigned int end); expression *parse_unary_primary(unsigned int start, unsigned int end);
expression *parse_binary_primary(unsigned int start, unsigned int end); expression *parse_binary_primary(unsigned int start, unsigned int end);
expression *parse_just_a_string(unsigned int start, unsigned int end); expression *parse_just_a_string(unsigned int start, unsigned int end);
static expression *parse_args(const wcstring_list_t &args, wcstring &err); static expression *parse_args(const wcstring_list_t &args, wcstring &err);
}; };
struct range_t { struct range_t {
unsigned int start; unsigned int start;
unsigned int end; unsigned int end;
range_t(unsigned s, unsigned e) : start(s), end(e) { } range_t(unsigned s, unsigned e) : start(s), end(e) { }
}; };
@ -184,13 +184,13 @@ namespace test_expressions {
class expression { class expression {
protected: protected:
expression(token_t what, range_t where) : token(what), range(where) { } expression(token_t what, range_t where) : token(what), range(where) { }
public: public:
const token_t token; const token_t token;
range_t range; range_t range;
virtual ~expression() { } virtual ~expression() { }
// evaluate returns true if the expression is true (i.e. BUILTIN_TEST_SUCCESS) // evaluate returns true if the expression is true (i.e. BUILTIN_TEST_SUCCESS)
virtual bool evaluate(wcstring_list_t &errors) = 0; virtual bool evaluate(wcstring_list_t &errors) = 0;
}; };
@ -210,59 +210,59 @@ namespace test_expressions {
public: public:
wcstring arg_left; wcstring arg_left;
wcstring arg_right; wcstring arg_right;
binary_primary(token_t tok, range_t where, const wcstring &left, const wcstring &right) : expression(tok, where), arg_left(left), arg_right(right) binary_primary(token_t tok, range_t where, const wcstring &left, const wcstring &right) : expression(tok, where), arg_left(left), arg_right(right)
{ } { }
bool evaluate(wcstring_list_t &errors); bool evaluate(wcstring_list_t &errors);
}; };
/* Unary operator like bang */ /* Unary operator like bang */
class unary_operator : public expression { class unary_operator : public expression {
public: public:
expr_ref_t subject; expr_ref_t subject;
unary_operator(token_t tok, range_t where, expr_ref_t &exp) : expression(tok, where), subject(exp) { } unary_operator(token_t tok, range_t where, expr_ref_t &exp) : expression(tok, where), subject(exp) { }
bool evaluate(wcstring_list_t &errors); bool evaluate(wcstring_list_t &errors);
}; };
/* Combining expression. Contains a list of AND or OR expressions. It takes more than two so that we don't have to worry about precedence in the parser. */ /* Combining expression. Contains a list of AND or OR expressions. It takes more than two so that we don't have to worry about precedence in the parser. */
class combining_expression : public expression { class combining_expression : public expression {
public: public:
const std::vector<expression *> subjects; const std::vector<expression *> subjects;
const std::vector<token_t> combiners; const std::vector<token_t> combiners;
combining_expression(token_t tok, range_t where, const std::vector<expression *> &exprs, const std::vector<token_t> &combs) : expression(tok, where), subjects(exprs), combiners(combs) combining_expression(token_t tok, range_t where, const std::vector<expression *> &exprs, const std::vector<token_t> &combs) : expression(tok, where), subjects(exprs), combiners(combs)
{ {
/* We should have one more subject than combiner */ /* We should have one more subject than combiner */
assert(subjects.size() == combiners.size() + 1); assert(subjects.size() == combiners.size() + 1);
} }
/* We are responsible for destroying our expressions */ /* We are responsible for destroying our expressions */
virtual ~combining_expression() { virtual ~combining_expression() {
for (size_t i=0; i < subjects.size(); i++) { for (size_t i=0; i < subjects.size(); i++) {
delete subjects[i]; delete subjects[i];
} }
} }
bool evaluate(wcstring_list_t &errors); bool evaluate(wcstring_list_t &errors);
}; };
/* Parenthetical expression */ /* Parenthetical expression */
class parenthetical_expression : public expression { class parenthetical_expression : public expression {
public: public:
expr_ref_t contents; expr_ref_t contents;
parenthetical_expression(token_t tok, range_t where, expr_ref_t &expr) : expression(tok, where), contents(expr) { } parenthetical_expression(token_t tok, range_t where, expr_ref_t &expr) : expression(tok, where), contents(expr) { }
virtual bool evaluate(wcstring_list_t &errors); virtual bool evaluate(wcstring_list_t &errors);
}; };
void test_parser::add_error(const wchar_t *fmt, ...) { void test_parser::add_error(const wchar_t *fmt, ...) {
assert(fmt != NULL); assert(fmt != NULL);
va_list va; va_list va;
va_start(va, fmt); va_start(va, fmt);
this->errors.push_back(vformat_string(fmt, va)); this->errors.push_back(vformat_string(fmt, va));
va_end(va); va_end(va);
} }
expression *test_parser::error(const wchar_t *fmt, ...) { expression *test_parser::error(const wchar_t *fmt, ...) {
assert(fmt != NULL); assert(fmt != NULL);
va_list va; va_list va;
@ -271,7 +271,7 @@ namespace test_expressions {
va_end(va); va_end(va);
return NULL; return NULL;
} }
expression *test_parser::parse_unary_expression(unsigned int start, unsigned int end) { expression *test_parser::parse_unary_expression(unsigned int start, unsigned int end) {
if (start >= end) { if (start >= end) {
return error(L"Missing argument at index %u", start); return error(L"Missing argument at index %u", start);
@ -288,18 +288,18 @@ namespace test_expressions {
return parse_primary(start, end); return parse_primary(start, end);
} }
} }
/* Parse a combining expression (AND, OR) */ /* Parse a combining expression (AND, OR) */
expression *test_parser::parse_combining_expression(unsigned int start, unsigned int end) { expression *test_parser::parse_combining_expression(unsigned int start, unsigned int end) {
if (start >= end) if (start >= end)
return NULL; return NULL;
std::vector<expression *> subjects; std::vector<expression *> subjects;
std::vector<token_t> combiners; std::vector<token_t> combiners;
unsigned int idx = start; unsigned int idx = start;
while (idx < end) { while (idx < end) {
if (! subjects.empty()) { if (! subjects.empty()) {
/* This is not the first expression, so we expect a combiner. */ /* This is not the first expression, so we expect a combiner. */
token_t combiner = token_for_string(arg(idx))->tok; token_t combiner = token_for_string(arg(idx))->tok;
@ -310,19 +310,19 @@ namespace test_expressions {
combiners.push_back(combiner); combiners.push_back(combiner);
idx++; idx++;
} }
/* Parse another expression */ /* Parse another expression */
expression *expr = parse_unary_expression(idx, end); expression *expr = parse_unary_expression(idx, end);
if (! expr) { if (! expr) {
add_error(L"Missing argument at index %u", idx); add_error(L"Missing argument at index %u", idx);
break; break;
} }
/* Go to the end of this expression */ /* Go to the end of this expression */
idx = expr->range.end; idx = expr->range.end;
subjects.push_back(expr); subjects.push_back(expr);
} }
if (! subjects.empty()) { if (! subjects.empty()) {
/* Our new expression takes ownership of all expressions we created. The token we pass is irrelevant. */ /* Our new expression takes ownership of all expressions we created. The token we pass is irrelevant. */
return new combining_expression(test_combine_and, range_t(start, idx), subjects, combiners); return new combining_expression(test_combine_and, range_t(start, idx), subjects, combiners);
@ -331,7 +331,7 @@ namespace test_expressions {
return NULL; return NULL;
} }
} }
expression *test_parser::parse_unary_primary(unsigned int start, unsigned int end) { expression *test_parser::parse_unary_primary(unsigned int start, unsigned int end) {
/* We need two arguments */ /* We need two arguments */
if (start >= end) { if (start >= end) {
@ -340,54 +340,54 @@ namespace test_expressions {
if (start + 1 >= end) { if (start + 1 >= end) {
return error(L"Missing argument at index %u", start + 1); return error(L"Missing argument at index %u", start + 1);
} }
/* All our unary primaries are prefix, so the operator is at start. */ /* All our unary primaries are prefix, so the operator is at start. */
const token_info_t *info = token_for_string(arg(start)); const token_info_t *info = token_for_string(arg(start));
if (! (info->flags & UNARY_PRIMARY)) if (! (info->flags & UNARY_PRIMARY))
return NULL; return NULL;
return new unary_primary(info->tok, range_t(start, start + 2), arg(start + 1)); return new unary_primary(info->tok, range_t(start, start + 2), arg(start + 1));
} }
expression *test_parser::parse_just_a_string(unsigned int start, unsigned int end) { expression *test_parser::parse_just_a_string(unsigned int start, unsigned int end) {
/* Handle a string as a unary primary that is not a token of any other type. /* Handle a string as a unary primary that is not a token of any other type.
e.g. 'test foo -a bar' should evaluate to true e.g. 'test foo -a bar' should evaluate to true
We handle this with a unary primary of test_string_n We handle this with a unary primary of test_string_n
*/ */
/* We need one arguments */ /* We need one arguments */
if (start >= end) { if (start >= end) {
return error(L"Missing argument at index %u", start); return error(L"Missing argument at index %u", start);
} }
const token_info_t *info = token_for_string(arg(start)); const token_info_t *info = token_for_string(arg(start));
if (info->tok != test_unknown) { if (info->tok != test_unknown) {
return error(L"Unexpected argument type at index %u", start); return error(L"Unexpected argument type at index %u", start);
} }
/* This is hackish; a nicer way to implement this would be with a "just a string" expression type */ /* This is hackish; a nicer way to implement this would be with a "just a string" expression type */
return new unary_primary(test_string_n, range_t(start, start + 1), arg(start)); return new unary_primary(test_string_n, range_t(start, start + 1), arg(start));
} }
#if 0 #if 0
expression *test_parser::parse_unary_primary(unsigned int start, unsigned int end) { expression *test_parser::parse_unary_primary(unsigned int start, unsigned int end) {
/* We need either one or two arguments */ /* We need either one or two arguments */
if (start >= end) { if (start >= end) {
return error(L"Missing argument at index %u", start); return error(L"Missing argument at index %u", start);
} }
/* The index of the argument to the unary primary */ /* The index of the argument to the unary primary */
unsigned int arg_idx; unsigned int arg_idx;
/* All our unary primaries are prefix, so any operator is at start. But it also may just be a string, with no operator. */ /* All our unary primaries are prefix, so any operator is at start. But it also may just be a string, with no operator. */
const token_info_t *info = token_for_string(arg(start)); const token_info_t *info = token_for_string(arg(start));
if (info->flags & UNARY_PRIMARY) { if (info->flags & UNARY_PRIMARY) {
/* We have an operator. Skip the operator argument */ /* We have an operator. Skip the operator argument */
arg_idx = start + 1; arg_idx = start + 1;
/* We have some freedom here...do we allow other tokens for the argument to operate on? /* We have some freedom here...do we allow other tokens for the argument to operate on?
For example, should 'test -n =' work? I say yes. So no typechecking on the next token. */ For example, should 'test -n =' work? I say yes. So no typechecking on the next token. */
} else if (info->tok == test_unknown) { } else if (info->tok == test_unknown) {
/* "Just a string. */ /* "Just a string. */
arg_idx = start; arg_idx = start;
@ -395,16 +395,16 @@ namespace test_expressions {
/* Here we don't allow arbitrary tokens as "just a string." I.e. 'test = -a =' should have a parse error. We could relax this at some point. */ /* Here we don't allow arbitrary tokens as "just a string." I.e. 'test = -a =' should have a parse error. We could relax this at some point. */
return error(L"Parse error at argument index %u", start); return error(L"Parse error at argument index %u", start);
} }
/* Verify we have the argument we want, i.e. test -n should fail to parse */ /* Verify we have the argument we want, i.e. test -n should fail to parse */
if (arg_idx >= end) { if (arg_idx >= end) {
return error(L"Missing argument at index %u", arg_idx); return error(L"Missing argument at index %u", arg_idx);
} }
return new unary_primary(info->tok, range_t(start, arg_idx + 1), arg(arg_idx)); return new unary_primary(info->tok, range_t(start, arg_idx + 1), arg(arg_idx));
} }
#endif #endif
expression *test_parser::parse_binary_primary(unsigned int start, unsigned int end) { expression *test_parser::parse_binary_primary(unsigned int start, unsigned int end) {
/* We need three arguments */ /* We need three arguments */
for (unsigned int idx = start; idx < start + 3; idx++) { for (unsigned int idx = start; idx < start + 3; idx++) {
@ -412,31 +412,31 @@ namespace test_expressions {
return error(L"Missing argument at index %u", idx); return error(L"Missing argument at index %u", idx);
} }
} }
/* All our binary primaries are infix, so the operator is at start + 1. */ /* All our binary primaries are infix, so the operator is at start + 1. */
const token_info_t *info = token_for_string(arg(start + 1)); const token_info_t *info = token_for_string(arg(start + 1));
if (! (info->flags & BINARY_PRIMARY)) if (! (info->flags & BINARY_PRIMARY))
return NULL; return NULL;
return new binary_primary(info->tok, range_t(start, start + 3), arg(start), arg(start + 2)); return new binary_primary(info->tok, range_t(start, start + 3), arg(start), arg(start + 2));
} }
expression *test_parser::parse_parenthentical(unsigned int start, unsigned int end) { expression *test_parser::parse_parenthentical(unsigned int start, unsigned int end) {
/* We need at least three arguments: open paren, argument, close paren */ /* We need at least three arguments: open paren, argument, close paren */
if (start + 3 >= end) if (start + 3 >= end)
return NULL; return NULL;
/* Must start with an open expression */ /* Must start with an open expression */
const token_info_t *open_paren = token_for_string(arg(start)); const token_info_t *open_paren = token_for_string(arg(start));
if (open_paren->tok != test_paren_open) if (open_paren->tok != test_paren_open)
return NULL; return NULL;
/* Parse a subexpression */ /* Parse a subexpression */
expression *subexr_ptr = parse_expression(start + 1, end); expression *subexr_ptr = parse_expression(start + 1, end);
if (! subexr_ptr) if (! subexr_ptr)
return NULL; return NULL;
expr_ref_t subexpr(subexr_ptr); expr_ref_t subexpr(subexr_ptr);
/* Parse a close paren */ /* Parse a close paren */
unsigned close_index = subexpr->range.end; unsigned close_index = subexpr->range.end;
assert(close_index <= end); assert(close_index <= end);
@ -447,7 +447,7 @@ namespace test_expressions {
if (close_paren->tok != test_paren_close) { if (close_paren->tok != test_paren_close) {
return error(L"Expected close paren at index %u", close_index); return error(L"Expected close paren at index %u", close_index);
} }
/* Success */ /* Success */
return new parenthetical_expression(test_paren_open, range_t(start, close_index+1), subexpr); return new parenthetical_expression(test_paren_open, range_t(start, close_index+1), subexpr);
} }
@ -456,7 +456,7 @@ namespace test_expressions {
if (start >= end) { if (start >= end) {
return error(L"Missing argument at index %u", start); return error(L"Missing argument at index %u", start);
} }
expression *expr = NULL; expression *expr = NULL;
if (! expr) expr = parse_parenthentical(start, end); if (! expr) expr = parse_parenthentical(start, end);
if (! expr) expr = parse_unary_primary(start, end); if (! expr) expr = parse_unary_primary(start, end);
@ -469,17 +469,17 @@ namespace test_expressions {
if (start >= end) { if (start >= end) {
return error(L"Missing argument at index %u", start); return error(L"Missing argument at index %u", start);
} }
return parse_combining_expression(start, end); return parse_combining_expression(start, end);
} }
expression *test_parser::parse_args(const wcstring_list_t &args, wcstring &err) { expression *test_parser::parse_args(const wcstring_list_t &args, wcstring &err) {
/* Empty list and one-arg list should be handled by caller */ /* Empty list and one-arg list should be handled by caller */
assert(args.size() > 1); assert(args.size() > 1);
test_parser parser(args); test_parser parser(args);
expression *result = parser.parse_expression(0, (unsigned int)args.size()); expression *result = parser.parse_expression(0, (unsigned int)args.size());
/* Handle errors */ /* Handle errors */
bool errored = false; bool errored = false;
for (size_t i = 0; i < parser.errors.size(); i++) { for (size_t i = 0; i < parser.errors.size(); i++) {
@ -490,7 +490,7 @@ namespace test_expressions {
// For now we only show the first error // For now we only show the first error
break; break;
} }
if (! errored && result) { if (! errored && result) {
/* It's also an error if there are any unused arguments. This is not detected by parse_expression() */ /* It's also an error if there are any unused arguments. This is not detected by parse_expression() */
assert(result->range.end <= args.size()); assert(result->range.end <= args.size());
@ -501,19 +501,19 @@ namespace test_expressions {
errored = true; errored = true;
} }
} }
return result; return result;
} }
bool unary_primary::evaluate(wcstring_list_t &errors) { bool unary_primary::evaluate(wcstring_list_t &errors) {
return unary_primary_evaluate(token, arg, errors); return unary_primary_evaluate(token, arg, errors);
} }
bool binary_primary::evaluate(wcstring_list_t &errors) { bool binary_primary::evaluate(wcstring_list_t &errors) {
return binary_primary_evaluate(token, arg_left, arg_right, errors); return binary_primary_evaluate(token, arg_left, arg_right, errors);
} }
bool unary_operator::evaluate(wcstring_list_t &errors) { bool unary_operator::evaluate(wcstring_list_t &errors) {
switch (token) { switch (token) {
case test_bang: case test_bang:
@ -525,7 +525,7 @@ namespace test_expressions {
} }
} }
bool combining_expression::evaluate(wcstring_list_t &errors) { bool combining_expression::evaluate(wcstring_list_t &errors) {
switch (token) { switch (token) {
case test_combine_and: case test_combine_and:
@ -534,11 +534,11 @@ namespace test_expressions {
/* One-element case */ /* One-element case */
if (subjects.size() == 1) if (subjects.size() == 1)
return subjects.at(0)->evaluate(errors); return subjects.at(0)->evaluate(errors);
/* Evaluate our lists, remembering that AND has higher precedence than OR. We can visualize this as a sequence of OR expressions of AND expressions. */ /* Evaluate our lists, remembering that AND has higher precedence than OR. We can visualize this as a sequence of OR expressions of AND expressions. */
assert(combiners.size() + 1 == subjects.size()); assert(combiners.size() + 1 == subjects.size());
assert(! subjects.empty()); assert(! subjects.empty());
size_t idx = 0, max = subjects.size(); size_t idx = 0, max = subjects.size();
bool or_result = false; bool or_result = false;
while (idx < max) { while (idx < max) {
@ -546,33 +546,33 @@ namespace test_expressions {
/* Short circuit */ /* Short circuit */
break; break;
} }
/* Evaluate a stream of AND starting at given subject index. It may only have one element. */ /* Evaluate a stream of AND starting at given subject index. It may only have one element. */
bool and_result = true; bool and_result = true;
for (; idx < max; idx++) { for (; idx < max; idx++) {
/* Evaluate it, short-circuiting */ /* Evaluate it, short-circuiting */
and_result = and_result && subjects.at(idx)->evaluate(errors); and_result = and_result && subjects.at(idx)->evaluate(errors);
/* If the combiner at this index (which corresponding to how we combine with the next subject) is not AND, then exit the loop */ /* If the combiner at this index (which corresponding to how we combine with the next subject) is not AND, then exit the loop */
if (idx + 1 < max && combiners.at(idx) != test_combine_and) { if (idx + 1 < max && combiners.at(idx) != test_combine_and) {
idx++; idx++;
break; break;
} }
} }
/* OR it in */ /* OR it in */
or_result = or_result || and_result; or_result = or_result || and_result;
} }
return or_result; return or_result;
} }
default: default:
errors.push_back(format_string(L"Unknown token type in %s", __func__)); errors.push_back(format_string(L"Unknown token type in %s", __func__));
return BUILTIN_TEST_FAIL; return BUILTIN_TEST_FAIL;
} }
} }
bool parenthetical_expression::evaluate(wcstring_list_t &errors) { bool parenthetical_expression::evaluate(wcstring_list_t &errors) {
return contents->evaluate(errors); return contents->evaluate(errors);
} }
@ -591,28 +591,28 @@ namespace test_expressions {
switch (token) { switch (token) {
case test_string_equal: case test_string_equal:
return left == right; return left == right;
case test_string_not_equal: case test_string_not_equal:
return left != right; return left != right;
case test_number_equal: case test_number_equal:
return parse_number(left, &left_num) && parse_number(right, &right_num) && left_num == right_num; return parse_number(left, &left_num) && parse_number(right, &right_num) && left_num == right_num;
case test_number_not_equal: case test_number_not_equal:
return parse_number(left, &left_num) && parse_number(right, &right_num) && left_num != right_num; return parse_number(left, &left_num) && parse_number(right, &right_num) && left_num != right_num;
case test_number_greater: case test_number_greater:
return parse_number(left, &left_num) && parse_number(right, &right_num) && left_num > right_num; return parse_number(left, &left_num) && parse_number(right, &right_num) && left_num > right_num;
case test_number_greater_equal: case test_number_greater_equal:
return parse_number(left, &left_num) && parse_number(right, &right_num) && left_num >= right_num; return parse_number(left, &left_num) && parse_number(right, &right_num) && left_num >= right_num;
case test_number_lesser: case test_number_lesser:
return parse_number(left, &left_num) && parse_number(right, &right_num) && left_num < right_num; return parse_number(left, &left_num) && parse_number(right, &right_num) && left_num < right_num;
case test_number_lesser_equal: case test_number_lesser_equal:
return parse_number(left, &left_num) && parse_number(right, &right_num) && left_num <= right_num; return parse_number(left, &left_num) && parse_number(right, &right_num) && left_num <= right_num;
default: default:
errors.push_back(format_string(L"Unknown token type in %s", __func__)); errors.push_back(format_string(L"Unknown token type in %s", __func__));
return false; return false;
@ -627,60 +627,60 @@ namespace test_expressions {
switch (token) { switch (token) {
case test_filetype_b: // "-b", for block special files case test_filetype_b: // "-b", for block special files
return !wstat(arg, &buf) && S_ISBLK(buf.st_mode); return !wstat(arg, &buf) && S_ISBLK(buf.st_mode);
case test_filetype_c: // "-c" for character special files case test_filetype_c: // "-c" for character special files
return !wstat(arg, &buf) && S_ISCHR(buf.st_mode); return !wstat(arg, &buf) && S_ISCHR(buf.st_mode);
case test_filetype_d: // "-d" for directories case test_filetype_d: // "-d" for directories
return !wstat(arg, &buf) && S_ISDIR(buf.st_mode); return !wstat(arg, &buf) && S_ISDIR(buf.st_mode);
case test_filetype_e: // "-e" for files that exist case test_filetype_e: // "-e" for files that exist
return !wstat(arg, &buf); return !wstat(arg, &buf);
case test_filetype_f: // "-f" for for regular files case test_filetype_f: // "-f" for for regular files
return !wstat(arg, &buf) && S_ISREG(buf.st_mode); return !wstat(arg, &buf) && S_ISREG(buf.st_mode);
case test_filetype_g: // "-g" for set-group-id case test_filetype_g: // "-g" for set-group-id
return !wstat(arg, &buf) && (S_ISGID & buf.st_mode); return !wstat(arg, &buf) && (S_ISGID & buf.st_mode);
case test_filetype_h: // "-h" for symbolic links case test_filetype_h: // "-h" for symbolic links
case test_filetype_L: // "-L", same as -h case test_filetype_L: // "-L", same as -h
return !lwstat(arg, &buf) && S_ISLNK(buf.st_mode); return !lwstat(arg, &buf) && S_ISLNK(buf.st_mode);
case test_filetype_p: // "-p", for FIFO case test_filetype_p: // "-p", for FIFO
return !wstat(arg, &buf) && S_ISFIFO(buf.st_mode); return !wstat(arg, &buf) && S_ISFIFO(buf.st_mode);
case test_filetype_S: // "-S", socket case test_filetype_S: // "-S", socket
return !wstat(arg, &buf) && S_ISSOCK(buf.st_mode); return !wstat(arg, &buf) && S_ISSOCK(buf.st_mode);
case test_filesize_s: // "-s", size greater than zero case test_filesize_s: // "-s", size greater than zero
return !wstat(arg, &buf) && buf.st_size > 0; return !wstat(arg, &buf) && buf.st_size > 0;
case test_filedesc_t: // "-t", whether the fd is associated with a terminal case test_filedesc_t: // "-t", whether the fd is associated with a terminal
return parse_number(arg, &num) && num == (int)num && isatty((int)num); return parse_number(arg, &num) && num == (int)num && isatty((int)num);
case test_fileperm_r: // "-r", read permission case test_fileperm_r: // "-r", read permission
return !waccess(arg, R_OK); return !waccess(arg, R_OK);
case test_fileperm_u: // "-u", whether file is setuid case test_fileperm_u: // "-u", whether file is setuid
return !wstat(arg, &buf) && (S_ISUID & buf.st_mode); return !wstat(arg, &buf) && (S_ISUID & buf.st_mode);
case test_fileperm_w: // "-w", whether file write permission is allowed case test_fileperm_w: // "-w", whether file write permission is allowed
return !waccess(arg, W_OK); return !waccess(arg, W_OK);
case test_fileperm_x: // "-x", whether file execute/search is allowed case test_fileperm_x: // "-x", whether file execute/search is allowed
return !waccess(arg, X_OK); return !waccess(arg, X_OK);
case test_string_n: // "-n", non-empty string case test_string_n: // "-n", non-empty string
return ! arg.empty(); return ! arg.empty();
case test_string_z: // "-z", true if length of string is 0 case test_string_z: // "-z", true if length of string is 0
return arg.empty(); return arg.empty();
default: default:
errors.push_back(format_string(L"Unknown token type in %s", __func__)); errors.push_back(format_string(L"Unknown token type in %s", __func__));
return false; return false;
} }
} }
}; };
@ -698,11 +698,11 @@ namespace test_expressions {
int builtin_test( parser_t &parser, wchar_t **argv ) int builtin_test( parser_t &parser, wchar_t **argv )
{ {
using namespace test_expressions; using namespace test_expressions;
/* The first argument should be the name of the command ('test') */ /* The first argument should be the name of the command ('test') */
if (! argv[0]) if (! argv[0])
return BUILTIN_TEST_FAIL; return BUILTIN_TEST_FAIL;
size_t argc = 0; size_t argc = 0;
while (argv[argc + 1]) while (argv[argc + 1])
argc++; argc++;

View file

@ -1,6 +1,6 @@
/** \file builtin_ulimit.c Functions defining the ulimit builtin /** \file builtin_ulimit.c Functions defining the ulimit builtin
Functions used for implementing the ulimit builtin. Functions used for implementing the ulimit builtin.
*/ */
#include "config.h" #include "config.h"
@ -27,99 +27,99 @@ Functions used for implementing the ulimit builtin.
*/ */
struct resource_t struct resource_t
{ {
/** /**
Resource id Resource id
*/ */
int resource; int resource;
/** /**
Description of resource Description of resource
*/ */
const wchar_t *desc; const wchar_t *desc;
/** /**
Switch used on commandline to specify resource Switch used on commandline to specify resource
*/ */
wchar_t switch_char; wchar_t switch_char;
/** /**
The implicit multiplier used when setting getting values The implicit multiplier used when setting getting values
*/ */
int multiplier; int multiplier;
} }
; ;
/** /**
Array of resource_t structs, describing all known resource types. Array of resource_t structs, describing all known resource types.
*/ */
static const struct resource_t resource_arr[] = static const struct resource_t resource_arr[] =
{ {
{ {
RLIMIT_CORE, L"Maximum size of core files created", L'c', 1024 RLIMIT_CORE, L"Maximum size of core files created", L'c', 1024
} }
, ,
{ {
RLIMIT_DATA, L"Maximum size of a processs data segment", L'd', 1024 RLIMIT_DATA, L"Maximum size of a processs data segment", L'd', 1024
} }
, ,
{ {
RLIMIT_FSIZE, L"Maximum size of files created by the shell", L'f', 1024 RLIMIT_FSIZE, L"Maximum size of files created by the shell", L'f', 1024
} }
, ,
#ifdef RLIMIT_MEMLOCK #ifdef RLIMIT_MEMLOCK
{ {
RLIMIT_MEMLOCK, L"Maximum size that may be locked into memory", L'l', 1024 RLIMIT_MEMLOCK, L"Maximum size that may be locked into memory", L'l', 1024
} }
, ,
#endif #endif
#ifdef RLIMIT_RSS #ifdef RLIMIT_RSS
{ {
RLIMIT_RSS, L"Maximum resident set size", L'm', 1024 RLIMIT_RSS, L"Maximum resident set size", L'm', 1024
} }
, ,
#endif #endif
{ {
RLIMIT_NOFILE, L"Maximum number of open file descriptors", L'n', 1 RLIMIT_NOFILE, L"Maximum number of open file descriptors", L'n', 1
} }
, ,
{ {
RLIMIT_STACK, L"Maximum stack size", L's', 1024 RLIMIT_STACK, L"Maximum stack size", L's', 1024
} }
, ,
{ {
RLIMIT_CPU, L"Maximum amount of cpu time in seconds", L't', 1 RLIMIT_CPU, L"Maximum amount of cpu time in seconds", L't', 1
} }
, ,
#ifdef RLIMIT_NPROC #ifdef RLIMIT_NPROC
{ {
RLIMIT_NPROC, L"Maximum number of processes available to a single user", L'u', 1 RLIMIT_NPROC, L"Maximum number of processes available to a single user", L'u', 1
} }
, ,
#endif #endif
#ifdef RLIMIT_AS #ifdef RLIMIT_AS
{ {
RLIMIT_AS, L"Maximum amount of virtual memory available to the shell", L'v', 1024 RLIMIT_AS, L"Maximum amount of virtual memory available to the shell", L'v', 1024
} }
, ,
#endif #endif
{ {
0, 0, 0, 0 0, 0, 0, 0
} }
} }
; ;
/** /**
Get the implicit multiplication factor for the specified resource limit Get the implicit multiplication factor for the specified resource limit
*/ */
static int get_multiplier( int what ) static int get_multiplier( int what )
{ {
int i; int i;
for( i=0; resource_arr[i].desc; i++ ) for( i=0; resource_arr[i].desc; i++ )
{ {
if( resource_arr[i].resource == what ) if( resource_arr[i].resource == what )
{ {
return resource_arr[i].multiplier; return resource_arr[i].multiplier;
} }
} }
return -1; return -1;
} }
/** /**
@ -129,11 +129,11 @@ static int get_multiplier( int what )
*/ */
static rlim_t get( int resource, int hard ) static rlim_t get( int resource, int hard )
{ {
struct rlimit ls; struct rlimit ls;
getrlimit( resource, &ls ); getrlimit( resource, &ls );
return hard ? ls.rlim_max:ls.rlim_cur; return hard ? ls.rlim_max:ls.rlim_cur;
} }
/** /**
@ -141,13 +141,13 @@ static rlim_t get( int resource, int hard )
*/ */
static void print( int resource, int hard ) static void print( int resource, int hard )
{ {
rlim_t l = get( resource, hard ); rlim_t l = get( resource, hard );
if( l == RLIM_INFINITY )
stdout_buffer.append( L"unlimited\n" );
else
append_format(stdout_buffer, L"%d\n", l / get_multiplier( resource ) );
if( l == RLIM_INFINITY )
stdout_buffer.append( L"unlimited\n" );
else
append_format(stdout_buffer, L"%d\n", l / get_multiplier( resource ) );
} }
/** /**
@ -155,40 +155,40 @@ static void print( int resource, int hard )
*/ */
static void print_all( int hard ) static void print_all( int hard )
{ {
int i; int i;
int w=0; int w=0;
for( i=0; resource_arr[i].desc; i++ ) for( i=0; resource_arr[i].desc; i++ )
{ {
w=maxi( w, my_wcswidth(resource_arr[i].desc)); w=maxi( w, my_wcswidth(resource_arr[i].desc));
} }
for( i=0; resource_arr[i].desc; i++ ) for( i=0; resource_arr[i].desc; i++ )
{ {
struct rlimit ls; struct rlimit ls;
rlim_t l; rlim_t l;
getrlimit( resource_arr[i].resource, &ls ); getrlimit( resource_arr[i].resource, &ls );
l = hard ? ls.rlim_max:ls.rlim_cur; l = hard ? ls.rlim_max:ls.rlim_cur;
const wchar_t *unit = ((resource_arr[i].resource==RLIMIT_CPU)?L"(seconds, ":(get_multiplier(resource_arr[i].resource)==1?L"(":L"(kB, "));
append_format(stdout_buffer,
L"%-*ls %10ls-%lc) ",
w,
resource_arr[i].desc,
unit,
resource_arr[i].switch_char);
if( l == RLIM_INFINITY )
{
stdout_buffer.append( L"unlimited\n" );
}
else
{
append_format(stdout_buffer, L"%d\n", l/get_multiplier(resource_arr[i].resource) );
}
}
const wchar_t *unit = ((resource_arr[i].resource==RLIMIT_CPU)?L"(seconds, ":(get_multiplier(resource_arr[i].resource)==1?L"(":L"(kB, "));
append_format(stdout_buffer,
L"%-*ls %10ls-%lc) ",
w,
resource_arr[i].desc,
unit,
resource_arr[i].switch_char);
if( l == RLIM_INFINITY )
{
stdout_buffer.append( L"unlimited\n" );
}
else
{
append_format(stdout_buffer, L"%d\n", l/get_multiplier(resource_arr[i].resource) );
}
}
} }
/** /**
@ -196,16 +196,16 @@ static void print_all( int hard )
*/ */
static const wchar_t *get_desc( int what ) static const wchar_t *get_desc( int what )
{ {
int i; int i;
for( i=0; resource_arr[i].desc; i++ ) for( i=0; resource_arr[i].desc; i++ )
{ {
if( resource_arr[i].resource == what ) if( resource_arr[i].resource == what )
{ {
return resource_arr[i].desc; return resource_arr[i].desc;
} }
} }
return L"Not a resource"; return L"Not a resource";
} }
/** /**
@ -215,37 +215,37 @@ static const wchar_t *get_desc( int what )
*/ */
static int set( int resource, int hard, int soft, rlim_t value ) static int set( int resource, int hard, int soft, rlim_t value )
{ {
struct rlimit ls; struct rlimit ls;
getrlimit( resource, &ls ); getrlimit( resource, &ls );
if( hard ) if( hard )
{ {
ls.rlim_max = value; ls.rlim_max = value;
} }
if( soft ) if( soft )
{ {
ls.rlim_cur = value; ls.rlim_cur = value;
/* /*
Do not attempt to set the soft limit higher than the hard limit Do not attempt to set the soft limit higher than the hard limit
*/ */
if( ( value == RLIM_INFINITY && ls.rlim_max != RLIM_INFINITY ) || if( ( value == RLIM_INFINITY && ls.rlim_max != RLIM_INFINITY ) ||
( value != RLIM_INFINITY && ls.rlim_max != RLIM_INFINITY && value > ls.rlim_max)) ( value != RLIM_INFINITY && ls.rlim_max != RLIM_INFINITY && value > ls.rlim_max))
{ {
ls.rlim_cur = ls.rlim_max; ls.rlim_cur = ls.rlim_max;
} }
} }
if( setrlimit( resource, &ls ) ) if( setrlimit( resource, &ls ) )
{ {
if( errno == EPERM ) if( errno == EPERM )
append_format(stderr_buffer, L"ulimit: Permission denied when changing resource of type '%ls'\n", get_desc( resource ) ); append_format(stderr_buffer, L"ulimit: Permission denied when changing resource of type '%ls'\n", get_desc( resource ) );
else else
builtin_wperror( L"ulimit" ); builtin_wperror( L"ulimit" );
return 1; return 1;
} }
return 0; return 0;
} }
/** /**
@ -254,259 +254,259 @@ static int set( int resource, int hard, int soft, rlim_t value )
*/ */
static int builtin_ulimit( parser_t &parser, wchar_t ** argv ) static int builtin_ulimit( parser_t &parser, wchar_t ** argv )
{ {
int hard=0; int hard=0;
int soft=0; int soft=0;
int what = RLIMIT_FSIZE;
int report_all = 0;
int argc = builtin_count_args( argv ); int what = RLIMIT_FSIZE;
int report_all = 0;
woptind=0;
while( 1 )
{
static const struct woption
long_options[] =
{
{
L"all", no_argument, 0, 'a'
}
,
{
L"hard", no_argument, 0, 'H'
}
,
{
L"soft", no_argument, 0, 'S'
}
,
{
L"core-size", no_argument, 0, 'c'
}
,
{
L"data-size", no_argument, 0, 'd'
}
,
{
L"file-size", no_argument, 0, 'f'
}
,
{
L"lock-size", no_argument, 0, 'l'
}
,
{
L"resident-set-size", no_argument, 0, 'm'
}
,
{
L"file-descriptor-count", no_argument, 0, 'n'
}
,
{
L"stack-size", no_argument, 0, 's'
}
,
{
L"cpu-time", no_argument, 0, 't'
}
,
{
L"process-count", no_argument, 0, 'u'
}
,
{
L"virtual-memory-size", no_argument, 0, 'v'
}
,
{
L"help", no_argument, 0, 'h'
}
,
{
0, 0, 0, 0
}
}
;
int opt_index = 0; int argc = builtin_count_args( argv );
int opt = wgetopt_long( argc, woptind=0;
argv,
L"aHScdflmnstuvh", while( 1 )
long_options, {
&opt_index ); static const struct woption
if( opt == -1 ) long_options[] =
break; {
{
switch( opt ) L"all", no_argument, 0, 'a'
{ }
case 0: ,
if(long_options[opt_index].flag != 0) {
break; L"hard", no_argument, 0, 'H'
}
,
{
L"soft", no_argument, 0, 'S'
}
,
{
L"core-size", no_argument, 0, 'c'
}
,
{
L"data-size", no_argument, 0, 'd'
}
,
{
L"file-size", no_argument, 0, 'f'
}
,
{
L"lock-size", no_argument, 0, 'l'
}
,
{
L"resident-set-size", no_argument, 0, 'm'
}
,
{
L"file-descriptor-count", no_argument, 0, 'n'
}
,
{
L"stack-size", no_argument, 0, 's'
}
,
{
L"cpu-time", no_argument, 0, 't'
}
,
{
L"process-count", no_argument, 0, 'u'
}
,
{
L"virtual-memory-size", no_argument, 0, 'v'
}
,
{
L"help", no_argument, 0, 'h'
}
,
{
0, 0, 0, 0
}
}
;
int opt_index = 0;
int opt = wgetopt_long( argc,
argv,
L"aHScdflmnstuvh",
long_options,
&opt_index );
if( opt == -1 )
break;
switch( opt )
{
case 0:
if(long_options[opt_index].flag != 0)
break;
append_format(stderr_buffer, append_format(stderr_buffer,
BUILTIN_ERR_UNKNOWN, BUILTIN_ERR_UNKNOWN,
argv[0], argv[0],
long_options[opt_index].name ); long_options[opt_index].name );
builtin_print_help( parser, argv[0], stderr_buffer ); builtin_print_help( parser, argv[0], stderr_buffer );
return 1; return 1;
case L'a':
report_all=1;
break;
case L'H': case L'a':
hard=1; report_all=1;
break; break;
case L'S': case L'H':
soft=1; hard=1;
break; break;
case L'c': case L'S':
what=RLIMIT_CORE; soft=1;
break; break;
case L'd': case L'c':
what=RLIMIT_DATA; what=RLIMIT_CORE;
break; break;
case L'f': case L'd':
what=RLIMIT_FSIZE; what=RLIMIT_DATA;
break; break;
case L'f':
what=RLIMIT_FSIZE;
break;
#ifdef RLIMIT_MEMLOCK #ifdef RLIMIT_MEMLOCK
case L'l': case L'l':
what=RLIMIT_MEMLOCK; what=RLIMIT_MEMLOCK;
break; break;
#endif #endif
#ifdef RLIMIT_RSS #ifdef RLIMIT_RSS
case L'm': case L'm':
what=RLIMIT_RSS; what=RLIMIT_RSS;
break; break;
#endif #endif
case L'n':
what=RLIMIT_NOFILE;
break;
case L's':
what=RLIMIT_STACK;
break;
case L't':
what=RLIMIT_CPU;
break;
#ifdef RLIMIT_NPROC
case L'u':
what=RLIMIT_NPROC;
break;
#endif
#ifdef RLIMIT_AS
case L'v':
what=RLIMIT_AS;
break;
#endif
case L'h':
builtin_print_help( parser, argv[0], stdout_buffer );
return 0;
case L'?': case L'n':
builtin_unknown_option( parser, argv[0], argv[woptind-1] ); what=RLIMIT_NOFILE;
return 1; break;
}
}
if( report_all ) case L's':
{ what=RLIMIT_STACK;
if( argc - woptind == 0 ) break;
{
print_all( hard ); case L't':
} what=RLIMIT_CPU;
else break;
{
#ifdef RLIMIT_NPROC
case L'u':
what=RLIMIT_NPROC;
break;
#endif
#ifdef RLIMIT_AS
case L'v':
what=RLIMIT_AS;
break;
#endif
case L'h':
builtin_print_help( parser, argv[0], stdout_buffer );
return 0;
case L'?':
builtin_unknown_option( parser, argv[0], argv[woptind-1] );
return 1;
}
}
if( report_all )
{
if( argc - woptind == 0 )
{
print_all( hard );
}
else
{
stderr_buffer.append(argv[0]); stderr_buffer.append(argv[0]);
stderr_buffer.append(L": Too many arguments\n"); stderr_buffer.append(L": Too many arguments\n");
builtin_print_help( parser, argv[0], stderr_buffer ); builtin_print_help( parser, argv[0], stderr_buffer );
return 1; return 1;
} }
return 0; return 0;
} }
switch( argc - woptind )
{
case 0:
{
/*
Show current limit value
*/
print( what, hard );
break;
}
case 1:
{
/*
Change current limit value
*/
rlim_t new_limit;
wchar_t *end;
/* switch( argc - woptind )
Set both hard and soft limits if nothing else was specified {
*/ case 0:
if( !(hard+soft) ) {
{ /*
hard=soft=1; Show current limit value
} */
print( what, hard );
if( wcscasecmp( argv[woptind], L"unlimited" )==0) break;
{ }
new_limit = RLIM_INFINITY;
} case 1:
else if( wcscasecmp( argv[woptind], L"hard" )==0) {
{ /*
new_limit = get( what, 1 ); Change current limit value
} */
else if( wcscasecmp( argv[woptind], L"soft" )==0) rlim_t new_limit;
{ wchar_t *end;
new_limit = get( what, soft );
} /*
else Set both hard and soft limits if nothing else was specified
{ */
errno=0; if( !(hard+soft) )
new_limit = wcstol( argv[woptind], &end, 10 ); {
if( errno || *end ) hard=soft=1;
{ }
append_format(stderr_buffer,
L"%ls: Invalid limit '%ls'\n", if( wcscasecmp( argv[woptind], L"unlimited" )==0)
argv[0], {
argv[woptind] ); new_limit = RLIM_INFINITY;
builtin_print_help( parser, argv[0], stderr_buffer ); }
return 1; else if( wcscasecmp( argv[woptind], L"hard" )==0)
} {
new_limit *= get_multiplier( what ); new_limit = get( what, 1 );
} }
else if( wcscasecmp( argv[woptind], L"soft" )==0)
return set( what, hard, soft, new_limit ); {
} new_limit = get( what, soft );
}
default: else
{ {
errno=0;
new_limit = wcstol( argv[woptind], &end, 10 );
if( errno || *end )
{
append_format(stderr_buffer,
L"%ls: Invalid limit '%ls'\n",
argv[0],
argv[woptind] );
builtin_print_help( parser, argv[0], stderr_buffer );
return 1;
}
new_limit *= get_multiplier( what );
}
return set( what, hard, soft, new_limit );
}
default:
{
stderr_buffer.append(argv[0]); stderr_buffer.append(argv[0]);
stderr_buffer.append(L": Too many arguments\n"); stderr_buffer.append(L": Too many arguments\n");
builtin_print_help( parser, argv[0], stderr_buffer ); builtin_print_help( parser, argv[0], stderr_buffer );
return 1; return 1;
} }
} }
return 0; return 0;
} }

View file

@ -72,13 +72,13 @@ bool rgb_color_t::try_parse_rgb(const wcstring &name) {
FA3 FA3
F3A035 F3A035
*/ */
size_t digit_idx = 0, len = name.size(); size_t digit_idx = 0, len = name.size();
/* Skip any leading # */ /* Skip any leading # */
if (len > 0 && name.at(0) == L'#') if (len > 0 && name.at(0) == L'#')
digit_idx++; digit_idx++;
bool success = false; bool success = false;
size_t i; size_t i;
if (len - digit_idx == 3) { if (len - digit_idx == 3) {
@ -98,7 +98,7 @@ bool rgb_color_t::try_parse_rgb(const wcstring &name) {
data.rgb[i] = hi*16+lo; data.rgb[i] = hi*16+lo;
} }
success = (i == 3); success = (i == 3);
} }
if (success) { if (success) {
this->type = type_rgb; this->type = type_rgb;
} }
@ -152,7 +152,7 @@ rgb_color_t::rgb_color_t(unsigned char t, unsigned char i) : type(t), flags(), d
data.name_idx = i; data.name_idx = i;
} }
rgb_color_t rgb_color_t::normal() { return rgb_color_t(type_normal); } rgb_color_t rgb_color_t::normal() { return rgb_color_t(type_normal); }
rgb_color_t rgb_color_t::reset() { return rgb_color_t(type_reset); } rgb_color_t rgb_color_t::reset() { return rgb_color_t(type_reset); }
rgb_color_t rgb_color_t::ignore() { return rgb_color_t(type_ignore); } rgb_color_t rgb_color_t::ignore() { return rgb_color_t(type_ignore); }
rgb_color_t rgb_color_t::none() { return rgb_color_t(type_none); } rgb_color_t rgb_color_t::none() { return rgb_color_t(type_none); }
@ -184,26 +184,26 @@ static unsigned char term256_color_for_rgb(const unsigned char rgb[3]) {
0x5f8700, 0x5f875f, 0x5f8787, 0x5f87af, 0x5f87d7, 0x5f87ff, 0x5faf00, 0x5faf5f, 0x5f8700, 0x5f875f, 0x5f8787, 0x5f87af, 0x5f87d7, 0x5f87ff, 0x5faf00, 0x5faf5f,
0x5faf87, 0x5fafaf, 0x5fafd7, 0x5fafff, 0x5fd700, 0x5fd75f, 0x5fd787, 0x5fd7af, 0x5faf87, 0x5fafaf, 0x5fafd7, 0x5fafff, 0x5fd700, 0x5fd75f, 0x5fd787, 0x5fd7af,
0x5fd7d7, 0x5fd7ff, 0x5fff00, 0x5fff5f, 0x5fff87, 0x5fffaf, 0x5fffd7, 0x5fffff, 0x5fd7d7, 0x5fd7ff, 0x5fff00, 0x5fff5f, 0x5fff87, 0x5fffaf, 0x5fffd7, 0x5fffff,
0x870000, 0x87005f, 0x870087, 0x8700af, 0x8700d7, 0x8700ff, 0x875f00, 0x875f5f, 0x870000, 0x87005f, 0x870087, 0x8700af, 0x8700d7, 0x8700ff, 0x875f00, 0x875f5f,
0x875f87, 0x875faf, 0x875fd7, 0x875fff, 0x878700, 0x87875f, 0x878787, 0x8787af, 0x875f87, 0x875faf, 0x875fd7, 0x875fff, 0x878700, 0x87875f, 0x878787, 0x8787af,
0x8787d7, 0x8787ff, 0x87af00, 0x87af5f, 0x87af87, 0x87afaf, 0x87afd7, 0x87afff, 0x8787d7, 0x8787ff, 0x87af00, 0x87af5f, 0x87af87, 0x87afaf, 0x87afd7, 0x87afff,
0x87d700, 0x87d75f, 0x87d787, 0x87d7af, 0x87d7d7, 0x87d7ff, 0x87ff00, 0x87ff5f, 0x87d700, 0x87d75f, 0x87d787, 0x87d7af, 0x87d7d7, 0x87d7ff, 0x87ff00, 0x87ff5f,
0x87ff87, 0x87ffaf, 0x87ffd7, 0x87ffff, 0xaf0000, 0xaf005f, 0xaf0087, 0xaf00af, 0x87ff87, 0x87ffaf, 0x87ffd7, 0x87ffff, 0xaf0000, 0xaf005f, 0xaf0087, 0xaf00af,
0xaf00d7, 0xaf00ff, 0xaf5f00, 0xaf5f5f, 0xaf5f87, 0xaf5faf, 0xaf5fd7, 0xaf5fff, 0xaf00d7, 0xaf00ff, 0xaf5f00, 0xaf5f5f, 0xaf5f87, 0xaf5faf, 0xaf5fd7, 0xaf5fff,
0xaf8700, 0xaf875f, 0xaf8787, 0xaf87af, 0xaf87d7, 0xaf87ff, 0xafaf00, 0xafaf5f, 0xaf8700, 0xaf875f, 0xaf8787, 0xaf87af, 0xaf87d7, 0xaf87ff, 0xafaf00, 0xafaf5f,
0xafaf87, 0xafafaf, 0xafafd7, 0xafafff, 0xafd700, 0xafd75f, 0xafd787, 0xafd7af, 0xafaf87, 0xafafaf, 0xafafd7, 0xafafff, 0xafd700, 0xafd75f, 0xafd787, 0xafd7af,
0xafd7d7, 0xafd7ff, 0xafff00, 0xafff5f, 0xafff87, 0xafffaf, 0xafffd7, 0xafffff, 0xafd7d7, 0xafd7ff, 0xafff00, 0xafff5f, 0xafff87, 0xafffaf, 0xafffd7, 0xafffff,
0xd70000, 0xd7005f, 0xd70087, 0xd700af, 0xd700d7, 0xd700ff, 0xd75f00, 0xd75f5f, 0xd70000, 0xd7005f, 0xd70087, 0xd700af, 0xd700d7, 0xd700ff, 0xd75f00, 0xd75f5f,
0xd75f87, 0xd75faf, 0xd75fd7, 0xd75fff, 0xd78700, 0xd7875f, 0xd78787, 0xd787af, 0xd75f87, 0xd75faf, 0xd75fd7, 0xd75fff, 0xd78700, 0xd7875f, 0xd78787, 0xd787af,
0xd787d7, 0xd787ff, 0xd7af00, 0xd7af5f, 0xd7af87, 0xd7afaf, 0xd7afd7, 0xd7afff, 0xd787d7, 0xd787ff, 0xd7af00, 0xd7af5f, 0xd7af87, 0xd7afaf, 0xd7afd7, 0xd7afff,
0xd7d700, 0xd7d75f, 0xd7d787, 0xd7d7af, 0xd7d7d7, 0xd7d7ff, 0xd7ff00, 0xd7ff5f, 0xd7d700, 0xd7d75f, 0xd7d787, 0xd7d7af, 0xd7d7d7, 0xd7d7ff, 0xd7ff00, 0xd7ff5f,
0xd7ff87, 0xd7ffaf, 0xd7ffd7, 0xd7ffff, 0xff0000, 0xff005f, 0xff0087, 0xff00af, 0xd7ff87, 0xd7ffaf, 0xd7ffd7, 0xd7ffff, 0xff0000, 0xff005f, 0xff0087, 0xff00af,
0xff00d7, 0xff00ff, 0xff5f00, 0xff5f5f, 0xff5f87, 0xff5faf, 0xff5fd7, 0xff5fff, 0xff00d7, 0xff00ff, 0xff5f00, 0xff5f5f, 0xff5f87, 0xff5faf, 0xff5fd7, 0xff5fff,
0xff8700, 0xff875f, 0xff8787, 0xff87af, 0xff87d7, 0xff87ff, 0xffaf00, 0xffaf5f, 0xff8700, 0xff875f, 0xff8787, 0xff87af, 0xff87d7, 0xff87ff, 0xffaf00, 0xffaf5f,
0xffaf87, 0xffafaf, 0xffafd7, 0xffafff, 0xffd700, 0xffd75f, 0xffd787, 0xffd7af, 0xffaf87, 0xffafaf, 0xffafd7, 0xffafff, 0xffd700, 0xffd75f, 0xffd787, 0xffd7af,
0xffd7d7, 0xffd7ff, 0xffff00, 0xffff5f, 0xffff87, 0xffffaf, 0xffffd7, 0xffffff, 0xffd7d7, 0xffd7ff, 0xffff00, 0xffff5f, 0xffff87, 0xffffaf, 0xffffd7, 0xffffff,
0x080808, 0x121212, 0x1c1c1c, 0x262626, 0x303030, 0x3a3a3a, 0x444444, 0x4e4e4e, 0x080808, 0x121212, 0x1c1c1c, 0x262626, 0x303030, 0x3a3a3a, 0x444444, 0x4e4e4e,
0x585858, 0x626262, 0x6c6c6c, 0x767676, 0x808080, 0x8a8a8a, 0x949494, 0x9e9e9e, 0x585858, 0x626262, 0x6c6c6c, 0x767676, 0x808080, 0x8a8a8a, 0x949494, 0x9e9e9e,
0xa8a8a8, 0xb2b2b2, 0xbcbcbc, 0xc6c6c6, 0xd0d0d0, 0xdadada, 0xe4e4e4, 0xeeeeee 0xa8a8a8, 0xb2b2b2, 0xbcbcbc, 0xc6c6c6, 0xd0d0d0, 0xdadada, 0xe4e4e4, 0xeeeeee
}; };
return 16 + convert_color(rgb, kColors, sizeof kColors / sizeof *kColors); return 16 + convert_color(rgb, kColors, sizeof kColors / sizeof *kColors);

52
color.h
View file

@ -22,22 +22,22 @@ class rgb_color_t {
type_ignore type_ignore
}; };
unsigned char type:4; unsigned char type:4;
/* Flags */ /* Flags */
enum { enum {
flag_bold = 1 << 0, flag_bold = 1 << 0,
flag_underline = 1 << 1 flag_underline = 1 << 1
}; };
unsigned char flags:4; unsigned char flags:4;
union { union {
unsigned char name_idx; //0-10 unsigned char name_idx; //0-10
unsigned char rgb[3]; unsigned char rgb[3];
} data; } data;
/** Try parsing a special color name like "normal" */ /** Try parsing a special color name like "normal" */
bool try_parse_special(const wcstring &str); bool try_parse_special(const wcstring &str);
/** Try parsing an rgb color like "#F0A030" */ /** Try parsing an rgb color like "#F0A030" */
bool try_parse_rgb(const wcstring &str); bool try_parse_rgb(const wcstring &str);
@ -49,52 +49,52 @@ class rgb_color_t {
/** Private constructor */ /** Private constructor */
explicit rgb_color_t(unsigned char t, unsigned char i=0); explicit rgb_color_t(unsigned char t, unsigned char i=0);
public: public:
/** Default constructor of type none */ /** Default constructor of type none */
explicit rgb_color_t() : type(type_none), flags(), data() {} explicit rgb_color_t() : type(type_none), flags(), data() {}
/** Parse a color from a string */ /** Parse a color from a string */
explicit rgb_color_t(const wcstring &str); explicit rgb_color_t(const wcstring &str);
explicit rgb_color_t(const std::string &str); explicit rgb_color_t(const std::string &str);
/** Returns white */ /** Returns white */
static rgb_color_t white(); static rgb_color_t white();
/** Returns black */ /** Returns black */
static rgb_color_t black(); static rgb_color_t black();
/** Returns the reset special color */ /** Returns the reset special color */
static rgb_color_t reset(); static rgb_color_t reset();
/** Returns the normal special color */ /** Returns the normal special color */
static rgb_color_t normal(); static rgb_color_t normal();
/** Returns the ignore special color */ /** Returns the ignore special color */
static rgb_color_t ignore(); static rgb_color_t ignore();
/** Returns the none special color */ /** Returns the none special color */
static rgb_color_t none(); static rgb_color_t none();
/** Returns whether the color is the ignore special color */ /** Returns whether the color is the ignore special color */
bool is_ignore(void) const { return type == type_ignore; } bool is_ignore(void) const { return type == type_ignore; }
/** Returns whether the color is the normal special color */ /** Returns whether the color is the normal special color */
bool is_normal(void) const { return type == type_normal; } bool is_normal(void) const { return type == type_normal; }
/** Returns whether the color is the reset special color */ /** Returns whether the color is the reset special color */
bool is_reset(void) const { return type == type_reset; } bool is_reset(void) const { return type == type_reset; }
/** Returns whether the color is the none special color */ /** Returns whether the color is the none special color */
bool is_none(void) const { return type == type_none; } bool is_none(void) const { return type == type_none; }
/** Returns whether the color is a named color (like "magenta") */ /** Returns whether the color is a named color (like "magenta") */
bool is_named(void) const { return type == type_named; } bool is_named(void) const { return type == type_named; }
/** Returns whether the color is specified via RGB components */ /** Returns whether the color is specified via RGB components */
bool is_rgb(void) const { return type == type_rgb; } bool is_rgb(void) const { return type == type_rgb; }
/** Returns whether the color is special, that is, not rgb or named */ /** Returns whether the color is special, that is, not rgb or named */
bool is_special(void) const { return type != type_named && type != type_rgb; } bool is_special(void) const { return type != type_named && type != type_rgb; }
@ -103,27 +103,27 @@ class rgb_color_t {
/** Returns the name index for the given color. Requires that the color be named or RGB. */ /** Returns the name index for the given color. Requires that the color be named or RGB. */
unsigned char to_name_index() const; unsigned char to_name_index() const;
/** Returns the term256 index for the given color. Requires that the color be named or RGB. */ /** Returns the term256 index for the given color. Requires that the color be named or RGB. */
unsigned char to_term256_index() const; unsigned char to_term256_index() const;
/** Returns whether the color is bold */ /** Returns whether the color is bold */
bool is_bold() const { return flags & flag_bold; } bool is_bold() const { return flags & flag_bold; }
/** Set whether the color is bold */ /** Set whether the color is bold */
void set_bold(bool x) { if (x) flags |= flag_bold; else flags &= ~flag_bold; } void set_bold(bool x) { if (x) flags |= flag_bold; else flags &= ~flag_bold; }
/** Returns whether the color is underlined */ /** Returns whether the color is underlined */
bool is_underline() const { return !! (flags & flag_underline); } bool is_underline() const { return !! (flags & flag_underline); }
/** Set whether the color is underlined */ /** Set whether the color is underlined */
void set_underline(bool x) { if (x) flags |= flag_underline; else flags &= ~flag_underline; } void set_underline(bool x) { if (x) flags |= flag_underline; else flags &= ~flag_underline; }
/** Compare two colors for equality */ /** Compare two colors for equality */
bool operator==(const rgb_color_t &other) const { bool operator==(const rgb_color_t &other) const {
return type == other.type && ! memcmp(&data, &other.data, sizeof data); return type == other.type && ! memcmp(&data, &other.data, sizeof data);
} }
/** Compare two colors for inequality */ /** Compare two colors for inequality */
bool operator!=(const rgb_color_t &other) const { bool operator!=(const rgb_color_t &other) const {
return !(*this == other); return !(*this == other);

2396
common.cpp

File diff suppressed because it is too large Load diff

164
common.h
View file

@ -1,5 +1,5 @@
/** \file common.h /** \file common.h
Prototypes for various functions, mostly string utilities, that are used by most parts of fish. Prototypes for various functions, mostly string utilities, that are used by most parts of fish.
*/ */
#ifndef FISH_COMMON_H #ifndef FISH_COMMON_H
@ -67,10 +67,10 @@ typedef std::vector<wcstring> wcstring_list_t;
enum { enum {
/** Escape all characters, including magic characters like the semicolon */ /** Escape all characters, including magic characters like the semicolon */
ESCAPE_ALL = 1 << 0, ESCAPE_ALL = 1 << 0,
/** Do not try to use 'simplified' quoted escapes, and do not use empty quotes as the empty string */ /** Do not try to use 'simplified' quoted escapes, and do not use empty quotes as the empty string */
ESCAPE_NO_QUOTED = 1 << 1, ESCAPE_NO_QUOTED = 1 << 1,
/** Do not escape tildes */ /** Do not escape tildes */
ESCAPE_NO_TILDE = 1 << 2 ESCAPE_NO_TILDE = 1 << 2
}; };
@ -84,10 +84,10 @@ typedef unsigned int escape_flags_t;
/** Exits without invoking destructors (via _exit), useful for code after fork. */ /** Exits without invoking destructors (via _exit), useful for code after fork. */
void exit_without_destructors(int code) __attribute__ ((noreturn)); void exit_without_destructors(int code) __attribute__ ((noreturn));
/** /**
Save the shell mode on startup so we can restore them on exit Save the shell mode on startup so we can restore them on exit
*/ */
extern struct termios shell_modes; extern struct termios shell_modes;
/** /**
The character to use where the text has been truncated. Is an The character to use where the text has been truncated. Is an
@ -118,57 +118,57 @@ extern const wchar_t *program_name;
failiure, the current function is ended at once. The second failiure, the current function is ended at once. The second
parameter is the return value of the current function on failiure. parameter is the return value of the current function on failiure.
*/ */
#define CHECK( arg, retval ) \ #define CHECK( arg, retval ) \
if( !(arg) ) \ if( !(arg) ) \
{ \ { \
debug( 0, \ debug( 0, \
"function %s called with null value for argument %s. ", \ "function %s called with null value for argument %s. ", \
__func__, \ __func__, \
#arg ); \ #arg ); \
bugreport(); \ bugreport(); \
show_stackframe(); \ show_stackframe(); \
return retval; \ return retval; \
} }
/** /**
Pause for input, then exit the program. If supported, print a backtrace first. Pause for input, then exit the program. If supported, print a backtrace first.
*/ */
#define FATAL_EXIT() \ #define FATAL_EXIT() \
{ \ { \
char exit_read_buff; \ char exit_read_buff; \
show_stackframe(); \ show_stackframe(); \
read( 0, &exit_read_buff, 1 ); \ read( 0, &exit_read_buff, 1 ); \
exit_without_destructors( 1 ); \ exit_without_destructors( 1 ); \
} \ } \
/** /**
Exit program at once, leaving an error message about running out of memory. Exit program at once, leaving an error message about running out of memory.
*/ */
#define DIE_MEM() \ #define DIE_MEM() \
{ \ { \
fwprintf( stderr, \ fwprintf( stderr, \
L"fish: Out of memory on line %ld of file %s, shutting down fish\n", \ L"fish: Out of memory on line %ld of file %s, shutting down fish\n", \
(long)__LINE__, \ (long)__LINE__, \
__FILE__ ); \ __FILE__ ); \
FATAL_EXIT(); \ FATAL_EXIT(); \
} }
/** /**
Check if signals are blocked. If so, print an error message and Check if signals are blocked. If so, print an error message and
return from the function performing this check. return from the function performing this check.
*/ */
#define CHECK_BLOCK( retval ) \ #define CHECK_BLOCK( retval ) \
if( signal_is_blocked() ) \ if( signal_is_blocked() ) \
{ \ { \
debug( 0, \ debug( 0, \
"function %s called while blocking signals. ", \ "function %s called while blocking signals. ", \
__func__); \ __func__); \
bugreport(); \ bugreport(); \
show_stackframe(); \ show_stackframe(); \
return retval; \ return retval; \
} }
/** /**
Shorthand for wgettext call Shorthand for wgettext call
*/ */
@ -176,7 +176,7 @@ extern const wchar_t *program_name;
/** /**
Noop, used to tell xgettext that a string should be translated, Noop, used to tell xgettext that a string should be translated,
even though it is not directly sent to wgettext. even though it is not directly sent to wgettext.
*/ */
#define N_(wstr) wstr #define N_(wstr) wstr
@ -192,7 +192,7 @@ void show_stackframe();
/** /**
Read a line from the stream f into the string. Returns Read a line from the stream f into the string. Returns
the number of bytes read or -1 on failiure. the number of bytes read or -1 on failiure.
If the carriage return character is encountered, it is If the carriage return character is encountered, it is
@ -214,7 +214,7 @@ wchar_t *str2wcs( const char *in );
/** /**
Returns a newly allocated wide character string equivalent of the Returns a newly allocated wide character string equivalent of the
specified multibyte character string specified multibyte character string
This function encodes illegal character sequences in a reversible This function encodes illegal character sequences in a reversible
way using the private use area. way using the private use area.
*/ */
@ -344,7 +344,7 @@ inline wcstring to_string(const int &x) {
template <typename CharType_t> template <typename CharType_t>
class null_terminated_array_t { class null_terminated_array_t {
CharType_t **array; CharType_t **array;
typedef std::basic_string<CharType_t> string_t; typedef std::basic_string<CharType_t> string_t;
typedef std::vector<string_t> string_list_t; typedef std::vector<string_t> string_list_t;
@ -356,7 +356,7 @@ class null_terminated_array_t {
size_t len; size_t len;
for (len=0; arr[len] != T(0); len++) for (len=0; arr[len] != T(0); len++)
; ;
return len; return len;
} }
size_t size() const { size_t size() const {
@ -372,24 +372,24 @@ class null_terminated_array_t {
array = NULL; array = NULL;
} }
} }
public: public:
null_terminated_array_t() : array(NULL) { } null_terminated_array_t() : array(NULL) { }
null_terminated_array_t(const string_list_t &argv) : array(NULL) { this->set(argv); } null_terminated_array_t(const string_list_t &argv) : array(NULL) { this->set(argv); }
~null_terminated_array_t() { this->free(); } ~null_terminated_array_t() { this->free(); }
/** operator=. Notice the pass-by-value parameter. */ /** operator=. Notice the pass-by-value parameter. */
null_terminated_array_t& operator=(null_terminated_array_t rhs) { null_terminated_array_t& operator=(null_terminated_array_t rhs) {
if (this != &rhs) if (this != &rhs)
this->swap(rhs); this->swap(rhs);
return *this; return *this;
} }
/* Copy constructor. */ /* Copy constructor. */
null_terminated_array_t(const null_terminated_array_t &him) : array(NULL) { null_terminated_array_t(const null_terminated_array_t &him) : array(NULL) {
this->set(him.array); this->set(him.array);
} }
void set(const string_list_t &argv) { void set(const string_list_t &argv) {
/* Get rid of the old argv */ /* Get rid of the old argv */
this->free(); this->free();
@ -405,14 +405,14 @@ class null_terminated_array_t {
} }
this->array[count] = NULL; this->array[count] = NULL;
} }
void set(const CharType_t * const *new_array) { void set(const CharType_t * const *new_array) {
if (new_array == array) if (new_array == array)
return; return;
/* Get rid of the old argv */ /* Get rid of the old argv */
this->free(); this->free();
/* Copy the new one */ /* Copy the new one */
if (new_array) { if (new_array) {
size_t i, count = count_not_null(new_array); size_t i, count = count_not_null(new_array);
@ -426,10 +426,10 @@ class null_terminated_array_t {
this->array[count] = NULL; this->array[count] = NULL;
} }
} }
CharType_t **get() { return array; } CharType_t **get() { return array; }
const CharType_t * const *get() const { return array; } const CharType_t * const *get() const { return array; }
string_list_t to_list() const { string_list_t to_list() const {
string_list_t lst; string_list_t lst;
if (array != NULL) { if (array != NULL) {
@ -448,23 +448,23 @@ null_terminated_array_t<char> convert_wide_array_to_narrow(const null_terminated
class narrow_string_rep_t { class narrow_string_rep_t {
private: private:
const char *str; const char *str;
/* No copying */ /* No copying */
narrow_string_rep_t &operator=(const narrow_string_rep_t &); narrow_string_rep_t &operator=(const narrow_string_rep_t &);
narrow_string_rep_t(const narrow_string_rep_t &x); narrow_string_rep_t(const narrow_string_rep_t &x);
public: public:
~narrow_string_rep_t() { ~narrow_string_rep_t() {
free((void *)str); free((void *)str);
} }
narrow_string_rep_t() : str(NULL) {} narrow_string_rep_t() : str(NULL) {}
void set(const wcstring &s) { void set(const wcstring &s) {
free((void *)str); free((void *)str);
str = wcs2str(s.c_str()); str = wcs2str(s.c_str());
} }
const char *get() const { const char *get() const {
return str; return str;
} }
@ -476,7 +476,7 @@ bool is_forked_child();
class scoped_lock { class scoped_lock {
pthread_mutex_t *lock_obj; pthread_mutex_t *lock_obj;
bool locked; bool locked;
/* No copying */ /* No copying */
scoped_lock &operator=(const scoped_lock &); scoped_lock &operator=(const scoped_lock &);
scoped_lock(const scoped_lock &); scoped_lock(const scoped_lock &);
@ -492,18 +492,18 @@ public:
class wcstokenizer { class wcstokenizer {
wchar_t *buffer, *str, *state; wchar_t *buffer, *str, *state;
const wcstring sep; const wcstring sep;
/* No copying */ /* No copying */
wcstokenizer &operator=(const wcstokenizer &); wcstokenizer &operator=(const wcstokenizer &);
wcstokenizer(const wcstokenizer &); wcstokenizer(const wcstokenizer &);
public: public:
wcstokenizer(const wcstring &s, const wcstring &separator); wcstokenizer(const wcstring &s, const wcstring &separator);
bool next(wcstring &result); bool next(wcstring &result);
~wcstokenizer(); ~wcstokenizer();
}; };
/** /**
Appends a path component, with a / if necessary Appends a path component, with a / if necessary
*/ */
void append_path_component(wcstring &path, const wcstring &component); void append_path_component(wcstring &path, const wcstring &component);
@ -519,7 +519,7 @@ void append_format(wcstring &str, const wchar_t *format, ...);
char **wcsv2strv( const wchar_t * const *in ); char **wcsv2strv( const wchar_t * const *in );
/** /**
Test if the given string is a valid variable name. Test if the given string is a valid variable name.
\return null if this is a valid name, and a pointer to the first invalid character otherwise \return null if this is a valid name, and a pointer to the first invalid character otherwise
*/ */
@ -528,7 +528,7 @@ wchar_t *wcsvarname( const wchar_t *str );
/** /**
Test if the given string is a valid function name. Test if the given string is a valid function name.
\return null if this is a valid name, and a pointer to the first invalid character otherwise \return null if this is a valid name, and a pointer to the first invalid character otherwise
*/ */
@ -536,7 +536,7 @@ wchar_t *wcsvarname( const wchar_t *str );
const wchar_t *wcsfuncname( const wchar_t *str ); const wchar_t *wcsfuncname( const wchar_t *str );
/** /**
Test if the given string is valid in a variable name Test if the given string is valid in a variable name
\return 1 if this is a valid name, 0 otherwise \return 1 if this is a valid name, 0 otherwise
*/ */
@ -572,14 +572,14 @@ void error_reset();
This function behaves exactly like a wide character equivalent of This function behaves exactly like a wide character equivalent of
the C function setlocale, except that it will also try to detect if the C function setlocale, except that it will also try to detect if
the user is using a Unicode character set, and if so, use the the user is using a Unicode character set, and if so, use the
unicode ellipsis character as ellipsis, instead of '$'. unicode ellipsis character as ellipsis, instead of '$'.
*/ */
wcstring wsetlocale( int category, const wchar_t *locale ); wcstring wsetlocale( int category, const wchar_t *locale );
/** /**
Checks if \c needle is included in the list of strings specified. A warning is printed if needle is zero. Checks if \c needle is included in the list of strings specified. A warning is printed if needle is zero.
\param needle the string to search for in the list \param needle the string to search for in the list
\return zero if needle is not found, of if needle is null, non-zero otherwise \return zero if needle is not found, of if needle is null, non-zero otherwise
*/ */
@ -614,9 +614,9 @@ ssize_t read_loop(int fd, void *buff, size_t count);
Because debug is often called to tell the user about an error, Because debug is often called to tell the user about an error,
before using wperror to give a specific error message, debug will before using wperror to give a specific error message, debug will
never ever modify the value of errno. never ever modify the value of errno.
\param level the priority of the message. Lower number means higher priority. Messages with a priority_number higher than \c debug_level will be ignored.. \param level the priority of the message. Lower number means higher priority. Messages with a priority_number higher than \c debug_level will be ignored..
\param msg the message format string. \param msg the message format string.
Example: Example:
@ -629,7 +629,7 @@ void debug( int level, const wchar_t *msg, ... );
/** /**
Replace special characters with backslash escape sequences. Newline is Replace special characters with backslash escape sequences. Newline is
replaced with \n, etc. replaced with \n, etc.
\param in The string to be escaped \param in The string to be escaped
\param escape_all Whether all characters wich hold special meaning in fish (Pipe, semicolon, etc,) should be escaped, or only unprintable characters \param escape_all Whether all characters wich hold special meaning in fish (Pipe, semicolon, etc,) should be escaped, or only unprintable characters
@ -650,14 +650,14 @@ wcstring escape_string( const wcstring &in, escape_flags_t flags );
an invalid sequence is specified, 0 is returned. an invalid sequence is specified, 0 is returned.
*/ */
wchar_t *unescape( const wchar_t * in, wchar_t *unescape( const wchar_t * in,
int escape_special ); int escape_special );
bool unescape_string( wcstring &str, bool unescape_string( wcstring &str,
int escape_special ); int escape_special );
/** /**
Returns the width of the terminal window, so that not all Returns the width of the terminal window, so that not all
functions that use these values continually have to keep track of functions that use these values continually have to keep track of
it separately. it separately.
@ -688,9 +688,9 @@ void common_handle_winch( int signal );
void write_screen( const wcstring &msg, wcstring &buff ); void write_screen( const wcstring &msg, wcstring &buff );
/** /**
Tokenize the specified string into the specified wcstring_list_t. Tokenize the specified string into the specified wcstring_list_t.
\param val the input string. The contents of this string is not changed. \param val the input string. The contents of this string is not changed.
\param out the list in which to place the elements. \param out the list in which to place the elements.
*/ */
void tokenize_variable_array( const wcstring &val, wcstring_list_t &out); void tokenize_variable_array( const wcstring &val, wcstring_list_t &out);
@ -717,7 +717,7 @@ void bugreport();
double timef(); double timef();
/** /**
Call the following function early in main to set the main thread. Call the following function early in main to set the main thread.
This is our replacement for pthread_main_np(). This is our replacement for pthread_main_np().
*/ */
void set_main_thread(); void set_main_thread();

File diff suppressed because it is too large Load diff

View file

@ -1,8 +1,8 @@
/** \file complete.h /** \file complete.h
Prototypes for functions related to tab-completion. Prototypes for functions related to tab-completion.
These functions are used for storing and retrieving tab-completion These functions are used for storing and retrieving tab-completion
data, as well as for performing tab-completion. data, as well as for performing tab-completion.
*/ */
#ifndef FISH_COMPLETE_H #ifndef FISH_COMPLETE_H
@ -17,40 +17,40 @@
#include "util.h" #include "util.h"
#include "common.h" #include "common.h"
/** /**
Use all completions Use all completions
*/ */
#define SHARED 0 #define SHARED 0
/** /**
Do not use file completion Do not use file completion
*/ */
#define NO_FILES 1 #define NO_FILES 1
/** /**
Require a parameter after completion Require a parameter after completion
*/ */
#define NO_COMMON 2 #define NO_COMMON 2
/** /**
Only use the argument list specifies with completion after Only use the argument list specifies with completion after
option. This is the same as (NO_FILES & NO_COMMON) option. This is the same as (NO_FILES & NO_COMMON)
*/ */
#define EXCLUSIVE 3 #define EXCLUSIVE 3
/** /**
Command is a path Command is a path
*/ */
#define PATH 1 #define PATH 1
/** /**
Command is not a path Command is not a path
*/ */
#define COMMAND 0 #define COMMAND 0
/** /**
Separator between completion and description Separator between completion and description
*/ */
#define COMPLETE_SEP L'\004' #define COMPLETE_SEP L'\004'
/** /**
Separator between completion and description Separator between completion and description
*/ */
#define COMPLETE_SEP_STR L"\004" #define COMPLETE_SEP_STR L"\004"
@ -75,7 +75,7 @@ enum {
COMPLETE_NO_SPACE = 1 << 0, COMPLETE_NO_SPACE = 1 << 0,
/** /**
This compeltion is case insensitive. This compeltion is case insensitive.
Warning: The contents of the completion_t structure is actually Warning: The contents of the completion_t structure is actually
different if this flag is set! Specifically, the completion string different if this flag is set! Specifically, the completion string
@ -109,42 +109,42 @@ class completion_t
private: private:
/* No public default constructor */ /* No public default constructor */
completion_t(); completion_t();
public: public:
/** /**
The completion string The completion string
*/ */
wcstring completion; wcstring completion;
/** /**
The description for this completion The description for this completion
*/ */
wcstring description; wcstring description;
/** /**
Flags determining the completion behaviour. Flags determining the completion behaviour.
Determines whether a space should be inserted after this Determines whether a space should be inserted after this
compeltion if it is the only possible completion using the compeltion if it is the only possible completion using the
COMPLETE_NO_SPACE flag. COMPLETE_NO_SPACE flag.
The COMPLETE_NO_CASE can be used to signal that this completion
is case insensitive.
*/
int flags;
The COMPLETE_NO_CASE can be used to signal that this completion
is case insensitive.
*/
int flags;
bool is_case_insensitive() const { return !! (flags & COMPLETE_NO_CASE); } bool is_case_insensitive() const { return !! (flags & COMPLETE_NO_CASE); }
/* Construction. Note: defining these so that they are not inlined reduces the executable size. */ /* Construction. Note: defining these so that they are not inlined reduces the executable size. */
completion_t(const wcstring &comp, const wcstring &desc = L"", int flags_val = 0); completion_t(const wcstring &comp, const wcstring &desc = L"", int flags_val = 0);
completion_t(const completion_t &); completion_t(const completion_t &);
completion_t &operator=(const completion_t &); completion_t &operator=(const completion_t &);
/* The following are needed for sorting and uniquing completions */ /* The following are needed for sorting and uniquing completions */
bool operator < (const completion_t& rhs) const; bool operator < (const completion_t& rhs) const;
bool operator == (const completion_t& rhs) const; bool operator == (const completion_t& rhs) const;
bool operator != (const completion_t& rhs) const; bool operator != (const completion_t& rhs) const;
}; };
enum complete_type_t { enum complete_type_t {
@ -160,17 +160,17 @@ void sort_completions( std::vector<completion_t> &completions);
/** /**
Add a completion. Add a completion.
All supplied values are copied, they should be freed by or otherwise All supplied values are copied, they should be freed by or otherwise
disposed by the caller. disposed by the caller.
Examples: Examples:
The command 'gcc -o' requires that a file follows it, so the The command 'gcc -o' requires that a file follows it, so the
NO_COMMON option is suitable. This can be done using the following NO_COMMON option is suitable. This can be done using the following
line: line:
complete -c gcc -s o -r complete -c gcc -s o -r
The command 'grep -d' required that one of the strings 'read', The command 'grep -d' required that one of the strings 'read',
@ -187,7 +187,7 @@ void sort_completions( std::vector<completion_t> &completions);
will be interpreted as the command name. will be interpreted as the command name.
\param short_opt The single character name of an option. (-a is a short option, --all and -funroll are long options) \param short_opt The single character name of an option. (-a is a short option, --all and -funroll are long options)
\param long_opt The multi character name of an option. (-a is a short option, --all and -funroll are long options) \param long_opt The multi character name of an option. (-a is a short option, --all and -funroll are long options)
\param long_mode Whether to use old style, single dash long options. \param long_mode Whether to use old style, single dash long options.
\param result_mode Whether to search further completions when this \param result_mode Whether to search further completions when this
completion has been succesfully matched. If result_mode is SHARED, completion has been succesfully matched. If result_mode is SHARED,
any other completions may also be used. If result_mode is NO_FILES, any other completions may also be used. If result_mode is NO_FILES,
@ -200,7 +200,7 @@ void sort_completions( std::vector<completion_t> &completions);
\param condition a command to be run to check it this completion should be used. If \c condition is empty, the completion is always used. \param condition a command to be run to check it this completion should be used. If \c condition is empty, the completion is always used.
\param flags A set of completion flags \param flags A set of completion flags
*/ */
void complete_add( const wchar_t *cmd, void complete_add( const wchar_t *cmd,
bool cmd_is_path, bool cmd_is_path,
wchar_t short_opt, wchar_t short_opt,
const wchar_t *long_opt, const wchar_t *long_opt,
@ -209,7 +209,7 @@ void complete_add( const wchar_t *cmd,
const wchar_t *condition, const wchar_t *condition,
const wchar_t *comp, const wchar_t *comp,
const wchar_t *desc, const wchar_t *desc,
int flags ); int flags );
/** /**
Sets whether the completion list for this command is complete. If Sets whether the completion list for this command is complete. If
true, any options not matching one of the provided options will be true, any options not matching one of the provided options will be
@ -220,8 +220,8 @@ void complete_set_authoritative( const wchar_t *cmd, bool cmd_type, bool authori
/** /**
Remove a previously defined completion Remove a previously defined completion
*/ */
void complete_remove( const wchar_t *cmd, void complete_remove( const wchar_t *cmd,
bool cmd_is_path, bool cmd_is_path,
wchar_t short_opt, wchar_t short_opt,
const wchar_t *long_opt ); const wchar_t *long_opt );
@ -230,7 +230,7 @@ void complete_remove( const wchar_t *cmd,
void complete( const wcstring &cmd, std::vector<completion_t> &comp, complete_type_t type, wcstring_list_t *to_load = NULL ); void complete( const wcstring &cmd, std::vector<completion_t> &comp, complete_type_t type, wcstring_list_t *to_load = NULL );
/** /**
Print a list of all current completions into the string. Print a list of all current completions into the string.
\param out The string to write completions to \param out The string to write completions to
*/ */
@ -240,17 +240,17 @@ void complete_print( wcstring &out );
Tests if the specified option is defined for the specified command Tests if the specified option is defined for the specified command
*/ */
int complete_is_valid_option( const wcstring &str, int complete_is_valid_option( const wcstring &str,
const wcstring &opt, const wcstring &opt,
wcstring_list_t *inErrorsOrNull, wcstring_list_t *inErrorsOrNull,
bool allow_autoload ); bool allow_autoload );
/** /**
Tests if the specified argument is valid for the specified option Tests if the specified argument is valid for the specified option
and command and command
*/ */
bool complete_is_valid_argument( const wcstring &str, bool complete_is_valid_argument( const wcstring &str,
const wcstring &opt, const wcstring &opt,
const wcstring &arg ); const wcstring &arg );
/** /**

File diff suppressed because it is too large Load diff

1706
env.cpp

File diff suppressed because it is too large Load diff

18
env.h
View file

@ -1,5 +1,5 @@
/** \file env.h /** \file env.h
Prototypes for functions for setting and getting environment variables. Prototypes for functions for setting and getting environment variables.
*/ */
#ifndef FISH_ENV_H #ifndef FISH_ENV_H
@ -47,8 +47,8 @@
Error code for trying to alter read-only variable Error code for trying to alter read-only variable
*/ */
enum{ enum{
ENV_PERM = 1, ENV_PERM = 1,
ENV_INVALID ENV_INVALID
} }
; ;
@ -74,7 +74,7 @@ void env_destroy();
/** /**
Set the value of the environment variable whose name matches key to val. Set the value of the environment variable whose name matches key to val.
Memory policy: All keys and values are copied, the parameters can and should be freed by the caller afterwards Memory policy: All keys and values are copied, the parameters can and should be freed by the caller afterwards
@ -119,7 +119,7 @@ public:
wcstring::operator=(s); wcstring::operator=(s);
return *this; return *this;
} }
bool operator==(const env_var_t &s) const { bool operator==(const env_var_t &s) const {
if (is_missing && s.is_missing) if (is_missing && s.is_missing)
return true; return true;
@ -146,7 +146,7 @@ int env_exist( const wchar_t *key, int mode );
/** /**
Remove environemnt variable Remove environemnt variable
\param key The name of the variable to remove \param key The name of the variable to remove
\param mode should be ENV_USER if this is a remove request from the user, 0 otherwise. If this is a user request, read-only variables can not be removed. The mode may also specify the scope of the variable that should be erased. \param mode should be ENV_USER if this is a remove request from the user, 0 otherwise. If this is a user request, read-only variables can not be removed. The mode may also specify the scope of the variable that should be erased.
@ -188,12 +188,12 @@ class env_vars_snapshot_t {
public: public:
env_vars_snapshot_t(const wchar_t * const * keys); env_vars_snapshot_t(const wchar_t * const * keys);
env_vars_snapshot_t(void); env_vars_snapshot_t(void);
env_var_t get(const wcstring &key) const; env_var_t get(const wcstring &key) const;
// Returns the fake snapshot representing the live variables array // Returns the fake snapshot representing the live variables array
static const env_vars_snapshot_t &current(); static const env_vars_snapshot_t &current();
// vars necessary for highlighting // vars necessary for highlighting
static const wchar_t * const highlighting_keys[]; static const wchar_t * const highlighting_keys[];
}; };

View file

@ -72,7 +72,7 @@ void env_universal_barrier();
static int is_dead() static int is_dead()
{ {
return env_universal_server.fd < 0; return env_universal_server.fd < 0;
} }
@ -81,101 +81,101 @@ static int is_dead()
*/ */
static int get_socket( int fork_ok ) static int get_socket( int fork_ok )
{ {
int s, len; int s, len;
struct sockaddr_un local; struct sockaddr_un local;
char *name;
wchar_t *wdir;
wchar_t *wuname;
char *dir =0, *uname=0;
get_socket_count++; char *name;
wdir = path; wchar_t *wdir;
wuname = user; wchar_t *wuname;
char *dir =0, *uname=0;
if ((s = socket(AF_UNIX, SOCK_STREAM, 0)) == -1)
{
wperror(L"socket");
return -1;
}
if( wdir )
dir = wcs2str(wdir );
else
dir = strdup("/tmp");
if( wuname )
uname = wcs2str(wuname );
else
{
struct passwd *pw;
pw = getpwuid( getuid() );
uname = strdup( pw->pw_name );
}
name = (char *)malloc( strlen(dir) +
strlen(uname) +
strlen(SOCK_FILENAME) +
2 );
strcpy( name, dir );
strcat( name, "/" );
strcat( name, SOCK_FILENAME );
strcat( name, uname );
free( dir );
free( uname );
debug( 3, L"Connect to socket %s at fd %2", name, s );
local.sun_family = AF_UNIX;
strcpy(local.sun_path, name );
free( name );
len = sizeof(local);
if( connect( s, (struct sockaddr *)&local, len) == -1 )
{
close( s );
if( fork_ok && start_fishd )
{
debug( 2, L"Could not connect to socket %d, starting fishd", s );
start_fishd();
return get_socket( 0 );
}
debug( 1, L"Could not connect to universal variable server, already tried manual restart (or no command supplied). You will not be able to share variable values between fish sessions. Is fish properly installed?" );
return -1;
}
if( (fcntl( s, F_SETFL, O_NONBLOCK ) != 0) || (fcntl( s, F_SETFD, FD_CLOEXEC ) != 0) )
{
wperror( L"fcntl" );
close( s );
return -1;
}
debug( 3, L"Connected to fd %d", s ); get_socket_count++;
wdir = path;
return s; wuname = user;
if ((s = socket(AF_UNIX, SOCK_STREAM, 0)) == -1)
{
wperror(L"socket");
return -1;
}
if( wdir )
dir = wcs2str(wdir );
else
dir = strdup("/tmp");
if( wuname )
uname = wcs2str(wuname );
else
{
struct passwd *pw;
pw = getpwuid( getuid() );
uname = strdup( pw->pw_name );
}
name = (char *)malloc( strlen(dir) +
strlen(uname) +
strlen(SOCK_FILENAME) +
2 );
strcpy( name, dir );
strcat( name, "/" );
strcat( name, SOCK_FILENAME );
strcat( name, uname );
free( dir );
free( uname );
debug( 3, L"Connect to socket %s at fd %2", name, s );
local.sun_family = AF_UNIX;
strcpy(local.sun_path, name );
free( name );
len = sizeof(local);
if( connect( s, (struct sockaddr *)&local, len) == -1 )
{
close( s );
if( fork_ok && start_fishd )
{
debug( 2, L"Could not connect to socket %d, starting fishd", s );
start_fishd();
return get_socket( 0 );
}
debug( 1, L"Could not connect to universal variable server, already tried manual restart (or no command supplied). You will not be able to share variable values between fish sessions. Is fish properly installed?" );
return -1;
}
if( (fcntl( s, F_SETFL, O_NONBLOCK ) != 0) || (fcntl( s, F_SETFD, FD_CLOEXEC ) != 0) )
{
wperror( L"fcntl" );
close( s );
return -1;
}
debug( 3, L"Connected to fd %d", s );
return s;
} }
/** /**
Callback function used whenever a new fishd message is recieved Callback function used whenever a new fishd message is recieved
*/ */
static void callback( fish_message_type_t type, const wchar_t *name, const wchar_t *val ) static void callback( fish_message_type_t type, const wchar_t *name, const wchar_t *val )
{ {
if( type == BARRIER_REPLY ) if( type == BARRIER_REPLY )
{ {
barrier_reply = 1; barrier_reply = 1;
} }
else else
{ {
if( external_callback ) if( external_callback )
external_callback( type, name, val ); external_callback( type, name, val );
} }
} }
/** /**
@ -184,23 +184,23 @@ static void callback( fish_message_type_t type, const wchar_t *name, const wchar
*/ */
static void check_connection() static void check_connection()
{ {
if( !init ) if( !init )
return; return;
if( env_universal_server.killme ) if( env_universal_server.killme )
{ {
debug( 3, L"Lost connection to universal variable server." ); debug( 3, L"Lost connection to universal variable server." );
if( close( env_universal_server.fd ) ) if( close( env_universal_server.fd ) )
{ {
wperror( L"close" ); wperror( L"close" );
} }
env_universal_server.fd = -1; env_universal_server.fd = -1;
env_universal_server.killme=0; env_universal_server.killme=0;
env_universal_server.input.clear(); env_universal_server.input.clear();
env_universal_read_all(); env_universal_read_all();
} }
} }
/** /**
@ -208,18 +208,18 @@ static void check_connection()
*/ */
static void env_universal_remove_all() static void env_universal_remove_all()
{ {
size_t i; size_t i;
wcstring_list_t lst; wcstring_list_t lst;
env_universal_common_get_names( lst, env_universal_common_get_names( lst,
1, 1,
1 ); 1 );
for( i=0; i<lst.size(); i++ ) for( i=0; i<lst.size(); i++ )
{ {
const wcstring &key = lst.at(i); const wcstring &key = lst.at(i);
env_universal_common_remove( key ); env_universal_common_remove( key );
} }
} }
@ -230,63 +230,63 @@ static void env_universal_remove_all()
*/ */
static void reconnect() static void reconnect()
{ {
if( get_socket_count >= RECONNECT_COUNT ) if( get_socket_count >= RECONNECT_COUNT )
return; return;
debug( 3, L"Get new fishd connection" ); debug( 3, L"Get new fishd connection" );
init = 0; init = 0;
env_universal_server.buffer_consumed = env_universal_server.buffer_used = 0; env_universal_server.buffer_consumed = env_universal_server.buffer_used = 0;
env_universal_server.fd = get_socket(1); env_universal_server.fd = get_socket(1);
init = 1; init = 1;
if( env_universal_server.fd >= 0 ) if( env_universal_server.fd >= 0 )
{ {
env_universal_remove_all(); env_universal_remove_all();
env_universal_barrier(); env_universal_barrier();
} }
} }
void env_universal_init( wchar_t * p, void env_universal_init( wchar_t * p,
wchar_t *u, wchar_t *u,
void (*sf)(), void (*sf)(),
void (*cb)( fish_message_type_t type, const wchar_t *name, const wchar_t *val )) void (*cb)( fish_message_type_t type, const wchar_t *name, const wchar_t *val ))
{ {
path=p; path=p;
user=u; user=u;
start_fishd=sf; start_fishd=sf;
external_callback = cb; external_callback = cb;
connection_init( &env_universal_server, -1 ); connection_init( &env_universal_server, -1 );
env_universal_server.fd = get_socket(1); env_universal_server.fd = get_socket(1);
env_universal_common_init( &callback ); env_universal_common_init( &callback );
env_universal_read_all(); env_universal_read_all();
init = 1; init = 1;
if( env_universal_server.fd >= 0 ) if( env_universal_server.fd >= 0 )
{ {
env_universal_barrier(); env_universal_barrier();
} }
} }
void env_universal_destroy() void env_universal_destroy()
{ {
/* /*
Go into blocking mode and send all data before exiting Go into blocking mode and send all data before exiting
*/ */
if( env_universal_server.fd >= 0 ) if( env_universal_server.fd >= 0 )
{ {
if( fcntl( env_universal_server.fd, F_SETFL, 0 ) != 0 ) if( fcntl( env_universal_server.fd, F_SETFL, 0 ) != 0 )
{ {
wperror( L"fcntl" ); wperror( L"fcntl" );
} }
try_send_all( &env_universal_server ); try_send_all( &env_universal_server );
} }
connection_destroy( &env_universal_server ); connection_destroy( &env_universal_server );
env_universal_server.fd =-1; env_universal_server.fd =-1;
env_universal_common_destroy(); env_universal_common_destroy();
init = 0; init = 0;
} }
@ -295,177 +295,177 @@ void env_universal_destroy()
*/ */
int env_universal_read_all() int env_universal_read_all()
{ {
if( !init) if( !init)
return 0; return 0;
if( env_universal_server.fd == -1 ) if( env_universal_server.fd == -1 )
{ {
reconnect(); reconnect();
if( env_universal_server.fd == -1 ) if( env_universal_server.fd == -1 )
return 0; return 0;
} }
if( env_universal_server.fd != -1 ) if( env_universal_server.fd != -1 )
{ {
read_message( &env_universal_server ); read_message( &env_universal_server );
check_connection(); check_connection();
return 1; return 1;
} }
else else
{ {
debug( 2, L"No connection to universal variable server" ); debug( 2, L"No connection to universal variable server" );
return 0; return 0;
} }
} }
wchar_t *env_universal_get( const wcstring &name ) wchar_t *env_universal_get( const wcstring &name )
{ {
if( !init) if( !init)
return 0; return 0;
return env_universal_common_get( name ); return env_universal_common_get( name );
} }
int env_universal_get_export( const wcstring &name ) int env_universal_get_export( const wcstring &name )
{ {
if( !init) if( !init)
return 0; return 0;
return env_universal_common_get_export( name ); return env_universal_common_get_export( name );
} }
void env_universal_barrier() void env_universal_barrier()
{ {
ASSERT_IS_MAIN_THREAD(); ASSERT_IS_MAIN_THREAD();
message_t *msg; message_t *msg;
fd_set fds; fd_set fds;
if( !init || is_dead() ) if( !init || is_dead() )
return; return;
barrier_reply = 0; barrier_reply = 0;
/* /*
Create barrier request Create barrier request
*/ */
msg= create_message( BARRIER, 0, 0); msg= create_message( BARRIER, 0, 0);
msg->count=1; msg->count=1;
env_universal_server.unsent->push(msg); env_universal_server.unsent->push(msg);
/* /*
Wait until barrier request has been sent Wait until barrier request has been sent
*/ */
debug( 3, L"Create barrier" ); debug( 3, L"Create barrier" );
while( 1 ) while( 1 )
{ {
try_send_all( &env_universal_server ); try_send_all( &env_universal_server );
check_connection(); check_connection();
if( env_universal_server.unsent->empty() )
break;
if( env_universal_server.fd == -1 )
{
reconnect();
debug( 2, L"barrier interrupted, exiting" );
return;
}
FD_ZERO( &fds );
FD_SET( env_universal_server.fd, &fds );
select( env_universal_server.fd+1, 0, &fds, 0, 0 );
}
/* if( env_universal_server.unsent->empty() )
Wait for barrier reply break;
*/
debug( 3, L"Sent barrier request" ); if( env_universal_server.fd == -1 )
while( !barrier_reply ) {
{ reconnect();
if( env_universal_server.fd == -1 ) debug( 2, L"barrier interrupted, exiting" );
{ return;
reconnect(); }
debug( 2, L"barrier interrupted, exiting (2)" );
return; FD_ZERO( &fds );
} FD_SET( env_universal_server.fd, &fds );
FD_ZERO( &fds ); select( env_universal_server.fd+1, 0, &fds, 0, 0 );
FD_SET( env_universal_server.fd, &fds ); }
/*
Wait for barrier reply
*/
debug( 3, L"Sent barrier request" );
while( !barrier_reply )
{
if( env_universal_server.fd == -1 )
{
reconnect();
debug( 2, L"barrier interrupted, exiting (2)" );
return;
}
FD_ZERO( &fds );
FD_SET( env_universal_server.fd, &fds );
select( env_universal_server.fd+1, &fds, 0, 0, 0 ); select( env_universal_server.fd+1, &fds, 0, 0, 0 );
env_universal_read_all(); env_universal_read_all();
} }
debug( 3, L"End barrier" ); debug( 3, L"End barrier" );
} }
void env_universal_set( const wcstring &name, const wcstring &value, int exportv ) void env_universal_set( const wcstring &name, const wcstring &value, int exportv )
{ {
message_t *msg; message_t *msg;
if( !init )
return;
debug( 3, L"env_universal_set( \"%ls\", \"%ls\" )", name.c_str(), value.c_str() ); if( !init )
return;
if( is_dead() ) debug( 3, L"env_universal_set( \"%ls\", \"%ls\" )", name.c_str(), value.c_str() );
{
env_universal_common_set( name.c_str(), value.c_str(), exportv );
}
else
{
msg = create_message( exportv?SET_EXPORT:SET,
name.c_str(),
value.c_str());
if( !msg ) if( is_dead() )
{ {
debug( 1, L"Could not create universal variable message" ); env_universal_common_set( name.c_str(), value.c_str(), exportv );
return; }
} else
{
msg->count=1; msg = create_message( exportv?SET_EXPORT:SET,
name.c_str(),
value.c_str());
if( !msg )
{
debug( 1, L"Could not create universal variable message" );
return;
}
msg->count=1;
env_universal_server.unsent->push(msg); env_universal_server.unsent->push(msg);
env_universal_barrier(); env_universal_barrier();
} }
} }
int env_universal_remove( const wchar_t *name ) int env_universal_remove( const wchar_t *name )
{ {
int res; int res;
message_t *msg;
if( !init )
return 1;
CHECK( name, 1 );
res = !env_universal_common_get( name ); message_t *msg;
debug( 3, if( !init )
L"env_universal_remove( \"%ls\" )", return 1;
name );
CHECK( name, 1 );
if( is_dead() )
{ res = !env_universal_common_get( name );
env_universal_common_remove( wcstring(name) ); debug( 3,
} L"env_universal_remove( \"%ls\" )",
else name );
{
msg= create_message( ERASE, name, 0); if( is_dead() )
msg->count=1; {
env_universal_common_remove( wcstring(name) );
}
else
{
msg= create_message( ERASE, name, 0);
msg->count=1;
env_universal_server.unsent->push(msg); env_universal_server.unsent->push(msg);
env_universal_barrier(); env_universal_barrier();
} }
return res; return res;
} }
void env_universal_get_names2( wcstring_list_t &lst, void env_universal_get_names2( wcstring_list_t &lst,
int show_exported, int show_exported,
int show_unexported ) int show_unexported )
{ {
if( !init ) if( !init )
return; return;
env_universal_common_get_names( lst, env_universal_common_get_names( lst,
show_exported, show_exported,
show_unexported ); show_unexported );
} }

View file

@ -1,5 +1,5 @@
/** \file env_universal.h /** \file env_universal.h
Universal variable client library. Universal variable client library.
*/ */
#ifndef ENV_UNIVERSAL_H #ifndef ENV_UNIVERSAL_H
@ -17,8 +17,8 @@ extern connection_t env_universal_server;
/** /**
Initialize the envuni library Initialize the envuni library
*/ */
void env_universal_init( wchar_t * p, void env_universal_init( wchar_t * p,
wchar_t *u, wchar_t *u,
void (*sf)(), void (*sf)(),
void (*cb)( fish_message_type_t type, const wchar_t *name, const wchar_t *val )); void (*cb)( fish_message_type_t type, const wchar_t *name, const wchar_t *val ));
/** /**
@ -43,7 +43,7 @@ int env_universal_get_export( const wcstring &name );
void env_universal_set( const wcstring &name, const wcstring &val, int exportv ); void env_universal_set( const wcstring &name, const wcstring &val, int exportv );
/** /**
Erase a universal variable Erase a universal variable
\return zero if the variable existed, and non-zero if the variable did not exist \return zero if the variable existed, and non-zero if the variable did not exist
*/ */
int env_universal_remove( const wchar_t *name ); int env_universal_remove( const wchar_t *name );
@ -55,14 +55,14 @@ int env_universal_read_all();
/** /**
Get the names of all universal variables Get the names of all universal variables
\param l the list to insert the names into \param l the list to insert the names into
\param show_exported whether exported variables should be shown \param show_exported whether exported variables should be shown
\param show_unexported whether unexported variables should be shown \param show_unexported whether unexported variables should be shown
*/ */
void env_universal_get_names2( wcstring_list_t &list, void env_universal_get_names2( wcstring_list_t &list,
int show_exported, int show_exported,
int show_unexported ); int show_unexported );
/** /**
Synchronize with fishd Synchronize with fishd

File diff suppressed because it is too large Load diff

View file

@ -42,11 +42,11 @@
*/ */
typedef enum typedef enum
{ {
SET, SET,
SET_EXPORT, SET_EXPORT,
ERASE, ERASE,
BARRIER, BARRIER,
BARRIER_REPLY, BARRIER_REPLY,
} fish_message_type_t; } fish_message_type_t;
/** /**
@ -57,18 +57,18 @@ typedef enum
/** /**
A struct representing a message to be sent between client and server A struct representing a message to be sent between client and server
*/ */
typedef struct typedef struct
{ {
/** /**
Number of queues that contain this message. Once this reaches zero, the message should be deleted Number of queues that contain this message. Once this reaches zero, the message should be deleted
*/ */
int count; int count;
/**
Message body. The message must be allocated using enough memory to actually contain the message.
*/
std::string body;
/**
Message body. The message must be allocated using enough memory to actually contain the message.
*/
std::string body;
} message_t; } message_t;
typedef std::queue<message_t *> message_queue_t; typedef std::queue<message_t *> message_queue_t;
@ -78,46 +78,46 @@ typedef std::queue<message_t *> message_queue_t;
*/ */
typedef struct connection typedef struct connection
{ {
/** /**
The file descriptor this socket lives on The file descriptor this socket lives on
*/ */
int fd; int fd;
/** /**
Queue of onsent messages Queue of onsent messages
*/ */
message_queue_t *unsent; message_queue_t *unsent;
/** /**
Set to one when this connection should be killed Set to one when this connection should be killed
*/ */
int killme; int killme;
/** /**
The input string. Input from the socket goes here. When a The input string. Input from the socket goes here. When a
newline is encountered, the buffer is parsed and cleared. newline is encountered, the buffer is parsed and cleared.
*/ */
std::vector<char> input; std::vector<char> input;
/**
The read buffer.
*/
char buffer[ENV_UNIVERSAL_BUFFER_SIZE];
/** /**
Number of bytes that have already been consumed. The read buffer.
*/ */
size_t buffer_consumed; char buffer[ENV_UNIVERSAL_BUFFER_SIZE];
/**
Number of bytes that have been read into the buffer.
*/
size_t buffer_used;
/** /**
Link to the next connection Number of bytes that have already been consumed.
*/ */
struct connection *next; size_t buffer_consumed;
/**
Number of bytes that have been read into the buffer.
*/
size_t buffer_used;
/**
Link to the next connection
*/
struct connection *next;
} }
connection_t; connection_t;
/** /**
Read all available messages on this connection Read all available messages on this connection
@ -151,8 +151,8 @@ void env_universal_common_destroy();
variables, it does not communicate with any other process. variables, it does not communicate with any other process.
*/ */
void env_universal_common_get_names( wcstring_list_t &lst, void env_universal_common_get_names( wcstring_list_t &lst,
int show_exported, int show_exported,
int show_unexported ); int show_unexported );
/** /**
Perform the specified variable assignment. Perform the specified variable assignment.
@ -166,7 +166,7 @@ void env_universal_common_get_names( wcstring_list_t &lst,
void env_universal_common_set( const wchar_t *key, const wchar_t *val, int exportv ); void env_universal_common_set( const wchar_t *key, const wchar_t *val, int exportv );
/** /**
Remove the specified variable. Remove the specified variable.
This function operate agains the local copy of all universal This function operate agains the local copy of all universal
variables, it does not communicate with any other process. variables, it does not communicate with any other process.

694
event.cpp
View file

@ -1,6 +1,6 @@
/** \file event.c /** \file event.c
Functions for handling event triggers Functions for handling event triggers
*/ */
#include "config.h" #include "config.h"
@ -36,20 +36,20 @@
*/ */
typedef struct typedef struct
{ {
/** /**
Number of delivered signals Number of delivered signals
*/ */
int count; int count;
/** /**
Whether signals have been skipped Whether signals have been skipped
*/ */
int overflow; int overflow;
/** /**
Array of signal events Array of signal events
*/ */
int signal[SIG_UNHANDLED_MAX]; int signal[SIG_UNHANDLED_MAX];
} }
signal_list_t; signal_list_t;
/** /**
The signal event list. Actually two separate lists. One which is The signal event list. Actually two separate lists. One which is
@ -63,7 +63,7 @@ static signal_list_t sig_list[]={{0,0},{0,0}};
*/ */
static int active_list=0; static int active_list=0;
typedef std::vector<event_t *> event_list_t; typedef std::vector<event_t *> event_list_t;
/** /**
List of event handlers. List of event handlers.
@ -90,48 +90,48 @@ static int event_match( const event_t *classv, const event_t *instance )
{ {
/* If the function names are both non-empty and different, then it's not a match */ /* If the function names are both non-empty and different, then it's not a match */
if( ! classv->function_name.empty() && if( ! classv->function_name.empty() &&
! instance->function_name.empty() && ! instance->function_name.empty() &&
classv->function_name != instance->function_name) classv->function_name != instance->function_name)
{ {
return 0; return 0;
} }
if( classv->type == EVENT_ANY ) if( classv->type == EVENT_ANY )
return 1; return 1;
if( classv->type != instance->type )
return 0;
switch( classv->type ) if( classv->type != instance->type )
{ return 0;
case EVENT_SIGNAL:
if( classv->param1.signal == EVENT_ANY_SIGNAL )
return 1;
return classv->param1.signal == instance->param1.signal;
case EVENT_VARIABLE:
return instance->str_param1 == classv->str_param1;
case EVENT_EXIT:
if( classv->param1.pid == EVENT_ANY_PID )
return 1;
return classv->param1.pid == instance->param1.pid;
case EVENT_JOB_ID:
return classv->param1.job_id == instance->param1.job_id;
case EVENT_GENERIC: switch( classv->type )
return instance->str_param1 == classv->str_param1; {
} case EVENT_SIGNAL:
if( classv->param1.signal == EVENT_ANY_SIGNAL )
/** return 1;
This should never be reached return classv->param1.signal == instance->param1.signal;
*/
return 0; case EVENT_VARIABLE:
return instance->str_param1 == classv->str_param1;
case EVENT_EXIT:
if( classv->param1.pid == EVENT_ANY_PID )
return 1;
return classv->param1.pid == instance->param1.pid;
case EVENT_JOB_ID:
return classv->param1.job_id == instance->param1.job_id;
case EVENT_GENERIC:
return instance->str_param1 == classv->str_param1;
}
/**
This should never be reached
*/
return 0;
} }
@ -142,14 +142,14 @@ static int event_match( const event_t *classv, const event_t *instance )
static event_t *event_copy( const event_t *event, int copy_arguments ) static event_t *event_copy( const event_t *event, int copy_arguments )
{ {
event_t *e = new event_t(*event); event_t *e = new event_t(*event);
e->arguments.reset(new wcstring_list_t); e->arguments.reset(new wcstring_list_t);
if( copy_arguments && event->arguments.get() != NULL ) if( copy_arguments && event->arguments.get() != NULL )
{ {
*(e->arguments) = *(event->arguments); *(e->arguments) = *(event->arguments);
} }
return e; return e;
} }
/** /**
@ -157,71 +157,71 @@ static event_t *event_copy( const event_t *event, int copy_arguments )
*/ */
static int event_is_blocked( event_t *e ) static int event_is_blocked( event_t *e )
{ {
block_t *block; block_t *block;
parser_t &parser = parser_t::principal_parser(); parser_t &parser = parser_t::principal_parser();
for( block = parser.current_block; block; block = block->outer ) for( block = parser.current_block; block; block = block->outer )
{ {
if (event_block_list_blocks_type(block->event_blocks, e->type)) if (event_block_list_blocks_type(block->event_blocks, e->type))
return true; return true;
} }
return event_block_list_blocks_type(parser.global_event_blocks, e->type); return event_block_list_blocks_type(parser.global_event_blocks, e->type);
} }
wcstring event_get_desc( const event_t *e ) wcstring event_get_desc( const event_t *e )
{ {
CHECK( e, 0 ); CHECK( e, 0 );
wcstring result; wcstring result;
switch( e->type ) switch( e->type )
{ {
case EVENT_SIGNAL: case EVENT_SIGNAL:
result = format_string(_(L"signal handler for %ls (%ls)"), sig2wcs(e->param1.signal ), signal_get_desc( e->param1.signal )); result = format_string(_(L"signal handler for %ls (%ls)"), sig2wcs(e->param1.signal ), signal_get_desc( e->param1.signal ));
break; break;
case EVENT_VARIABLE:
result = format_string(_(L"handler for variable '%ls'"), e->str_param1.c_str() );
break;
case EVENT_EXIT:
if( e->param1.pid > 0 )
{
result = format_string(_(L"exit handler for process %d"), e->param1.pid );
}
else
{
job_t *j = job_get_from_pid( -e->param1.pid );
if( j )
result = format_string(_(L"exit handler for job %d, '%ls'"), j->job_id, j->command_wcstr() );
else
result = format_string(_(L"exit handler for job with process group %d"), -e->param1.pid );
}
break;
case EVENT_JOB_ID:
{
job_t *j = job_get( e->param1.job_id );
if( j )
result = format_string(_(L"exit handler for job %d, '%ls'"), j->job_id, j->command_wcstr() );
else
result = format_string(_(L"exit handler for job with job id %d"), e->param1.job_id );
break; case EVENT_VARIABLE:
} result = format_string(_(L"handler for variable '%ls'"), e->str_param1.c_str() );
break;
case EVENT_GENERIC:
result = format_string(_(L"handler for generic event '%ls'"), e->str_param1.c_str() ); case EVENT_EXIT:
break; if( e->param1.pid > 0 )
{
default: result = format_string(_(L"exit handler for process %d"), e->param1.pid );
result = format_string(_(L"Unknown event type") ); }
break; else
{
} job_t *j = job_get_from_pid( -e->param1.pid );
if( j )
return result; result = format_string(_(L"exit handler for job %d, '%ls'"), j->job_id, j->command_wcstr() );
else
result = format_string(_(L"exit handler for job with process group %d"), -e->param1.pid );
}
break;
case EVENT_JOB_ID:
{
job_t *j = job_get( e->param1.job_id );
if( j )
result = format_string(_(L"exit handler for job %d, '%ls'"), j->job_id, j->command_wcstr() );
else
result = format_string(_(L"exit handler for job with job id %d"), e->param1.job_id );
break;
}
case EVENT_GENERIC:
result = format_string(_(L"handler for generic event '%ls'"), e->str_param1.c_str() );
break;
default:
result = format_string(_(L"Unknown event type") );
break;
}
return result;
} }
#if 0 #if 0
@ -237,17 +237,17 @@ static void show_all_handlers(void) {
void event_add_handler( const event_t *event ) void event_add_handler( const event_t *event )
{ {
event_t *e; event_t *e;
CHECK( event, ); CHECK( event, );
e = event_copy( event, 0 ); e = event_copy( event, 0 );
if( e->type == EVENT_SIGNAL )
{
signal_handle( e->param1.signal, 1 );
}
if( e->type == EVENT_SIGNAL )
{
signal_handle( e->param1.signal, 1 );
}
// Block around updating the events vector // Block around updating the events vector
signal_block(); signal_block();
events.push_back(e); events.push_back(e);
@ -257,75 +257,75 @@ void event_add_handler( const event_t *event )
void event_remove( event_t *criterion ) void event_remove( event_t *criterion )
{ {
size_t i; size_t i;
event_list_t new_list; event_list_t new_list;
CHECK( criterion, );
/* CHECK( criterion, );
Because of concurrency issues (env_remove could remove an event
that is currently being executed), env_remove does not actually
free any events - instead it simply moves all events that should
be removed from the event list to the killme list, and the ones
that shouldn't be killed to new_list, and then drops the empty
events-list.
*/
if( events.empty() )
return;
for( i=0; i<events.size(); i++ ) /*
{ Because of concurrency issues (env_remove could remove an event
event_t *n = events.at(i); that is currently being executed), env_remove does not actually
if( event_match( criterion, n ) ) free any events - instead it simply moves all events that should
{ be removed from the event list to the killme list, and the ones
that shouldn't be killed to new_list, and then drops the empty
events-list.
*/
if( events.empty() )
return;
for( i=0; i<events.size(); i++ )
{
event_t *n = events.at(i);
if( event_match( criterion, n ) )
{
killme.push_back(n); killme.push_back(n);
/* /*
If this event was a signal handler and no other handler handles If this event was a signal handler and no other handler handles
the specified signal type, do not handle that type of signal any the specified signal type, do not handle that type of signal any
more. more.
*/ */
if( n->type == EVENT_SIGNAL ) if( n->type == EVENT_SIGNAL )
{ {
event_t e = event_t::signal_event(n->param1.signal); event_t e = event_t::signal_event(n->param1.signal);
if( event_get( &e, 0 ) == 1 ) if( event_get( &e, 0 ) == 1 )
{ {
signal_handle( e.param1.signal, 0 ); signal_handle( e.param1.signal, 0 );
} }
} }
} }
else else
{ {
new_list.push_back(n); new_list.push_back(n);
} }
} }
signal_block(); signal_block();
events.swap(new_list); events.swap(new_list);
signal_unblock(); signal_unblock();
} }
int event_get( event_t *criterion, std::vector<event_t *> *out ) int event_get( event_t *criterion, std::vector<event_t *> *out )
{ {
size_t i; size_t i;
int found = 0; int found = 0;
if( events.empty() )
return 0;
CHECK( criterion, 0 ); if( events.empty() )
return 0;
for( i=0; i<events.size(); i++ )
{ CHECK( criterion, 0 );
event_t *n = events.at(i);
if( event_match(criterion, n ) ) for( i=0; i<events.size(); i++ )
{ {
found++; event_t *n = events.at(i);
if( out ) if( event_match(criterion, n ) )
{
found++;
if( out )
out->push_back(n); out->push_back(n);
} }
} }
return found; return found;
} }
bool event_is_signal_observed(int sig) bool event_is_signal_observed(int sig)
@ -365,7 +365,7 @@ static void event_free_kills()
static int event_is_killed( event_t *e ) static int event_is_killed( event_t *e )
{ {
return std::find(killme.begin(), killme.end(), e) != killme.end(); return std::find(killme.begin(), killme.end(), e) != killme.end();
} }
/** /**
Perform the specified event. Since almost all event firings will Perform the specified event. Since almost all event firings will
@ -376,96 +376,96 @@ static int event_is_killed( event_t *e )
static void event_fire_internal( const event_t *event ) static void event_fire_internal( const event_t *event )
{ {
size_t i, j; size_t i, j;
event_list_t fire; event_list_t fire;
/*
First we free all events that have been removed
*/
event_free_kills();
if( events.empty() ) /*
return; First we free all events that have been removed
*/
event_free_kills();
/* if( events.empty() )
Then we iterate over all events, adding events that should be return;
fired to a second list. We need to do this in a separate step
since an event handler might call event_remove or /*
event_add_handler, which will change the contents of the \c Then we iterate over all events, adding events that should be
events list. fired to a second list. We need to do this in a separate step
*/ since an event handler might call event_remove or
for( i=0; i<events.size(); i++ ) event_add_handler, which will change the contents of the \c
{ events list.
event_t *criterion = events.at(i); */
for( i=0; i<events.size(); i++ )
/* {
Check if this event is a match event_t *criterion = events.at(i);
*/
if(event_match( criterion, event ) ) /*
{ Check if this event is a match
*/
if(event_match( criterion, event ) )
{
fire.push_back(criterion); fire.push_back(criterion);
} }
} }
/*
No matches. Time to return.
*/
if( fire.empty() )
return;
/*
Iterate over our list of matching events
*/
for( i=0; i<fire.size(); i++ )
{
event_t *criterion = fire.at(i);
int prev_status;
/* /*
Check if this event has been removed, if so, dont fire it No matches. Time to return.
*/ */
if( event_is_killed( criterion ) ) if( fire.empty() )
continue; return;
/*
Iterate over our list of matching events
*/
for( i=0; i<fire.size(); i++ )
{
event_t *criterion = fire.at(i);
int prev_status;
/*
Check if this event has been removed, if so, dont fire it
*/
if( event_is_killed( criterion ) )
continue;
/*
Fire event
*/
wcstring buffer = criterion->function_name;
/*
Fire event
*/
wcstring buffer = criterion->function_name;
if (event->arguments.get()) if (event->arguments.get())
{ {
for( j=0; j< event->arguments->size(); j++ ) for( j=0; j< event->arguments->size(); j++ )
{ {
wcstring arg_esc = escape_string( event->arguments->at(j), 1 ); wcstring arg_esc = escape_string( event->arguments->at(j), 1 );
buffer += L" "; buffer += L" ";
buffer += arg_esc; buffer += arg_esc;
} }
} }
// debug( 1, L"Event handler fires command '%ls'", buffer.c_str() ); // debug( 1, L"Event handler fires command '%ls'", buffer.c_str() );
/* /*
Event handlers are not part of the main flow of code, so Event handlers are not part of the main flow of code, so
they are marked as non-interactive they are marked as non-interactive
*/ */
proc_push_interactive(0); proc_push_interactive(0);
prev_status = proc_get_last_status(); prev_status = proc_get_last_status();
parser_t &parser = parser_t::principal_parser(); parser_t &parser = parser_t::principal_parser();
block_t *block = new event_block_t(event); block_t *block = new event_block_t(event);
parser.push_block(block); parser.push_block(block);
parser.eval( buffer, io_chain_t(), TOP ); parser.eval( buffer, io_chain_t(), TOP );
parser.pop_block(); parser.pop_block();
proc_pop_interactive(); proc_pop_interactive();
proc_set_last_status( prev_status ); proc_set_last_status( prev_status );
} }
/*
Free killed events
*/
event_free_kills();
/*
Free killed events
*/
event_free_kills();
} }
/** /**
@ -474,78 +474,78 @@ static void event_fire_internal( const event_t *event )
static void event_fire_delayed() static void event_fire_delayed()
{ {
size_t i; size_t i;
/* /*
If is_event is one, we are running the event-handler non-recursively. If is_event is one, we are running the event-handler non-recursively.
When the event handler has called a piece of code that triggers When the event handler has called a piece of code that triggers
another event, we do not want to fire delayed events because of another event, we do not want to fire delayed events because of
concurrency problems. concurrency problems.
*/ */
if( ! blocked.empty() && is_event==1) if( ! blocked.empty() && is_event==1)
{ {
event_list_t new_blocked; event_list_t new_blocked;
for( i=0; i<blocked.size(); i++ ) for( i=0; i<blocked.size(); i++ )
{ {
event_t *e = blocked.at(i); event_t *e = blocked.at(i);
if( event_is_blocked( e ) ) if( event_is_blocked( e ) )
{ {
new_blocked.push_back(e); new_blocked.push_back(e);
} }
else else
{ {
event_fire_internal( e ); event_fire_internal( e );
event_free( e ); event_free( e );
} }
} }
blocked.swap(new_blocked); blocked.swap(new_blocked);
} }
while( sig_list[active_list].count > 0 )
{
signal_list_t *lst;
/* while( sig_list[active_list].count > 0 )
Switch signal lists {
*/ signal_list_t *lst;
sig_list[1-active_list].count=0;
sig_list[1-active_list].overflow=0;
active_list=1-active_list;
/* /*
Set up Switch signal lists
*/ */
sig_list[1-active_list].count=0;
sig_list[1-active_list].overflow=0;
active_list=1-active_list;
/*
Set up
*/
event_t e = event_t::signal_event(0); event_t e = event_t::signal_event(0);
e.arguments.reset(new wcstring_list_t(1)); //one element e.arguments.reset(new wcstring_list_t(1)); //one element
lst = &sig_list[1-active_list]; lst = &sig_list[1-active_list];
if( lst->overflow ) if( lst->overflow )
{ {
debug( 0, _( L"Signal list overflow. Signals have been ignored." ) ); debug( 0, _( L"Signal list overflow. Signals have been ignored." ) );
} }
/* /*
Send all signals in our private list Send all signals in our private list
*/ */
for( int i=0; i < lst->count; i++ ) for( int i=0; i < lst->count; i++ )
{ {
e.param1.signal = lst->signal[i]; e.param1.signal = lst->signal[i];
e.arguments->at(0) = sig2wcs( e.param1.signal ); e.arguments->at(0) = sig2wcs( e.param1.signal );
if( event_is_blocked( &e ) ) if( event_is_blocked( &e ) )
{ {
blocked.push_back(event_copy(&e, 1)); blocked.push_back(event_copy(&e, 1));
} }
else else
{ {
event_fire_internal( &e ); event_fire_internal( &e );
} }
} }
e.arguments.reset(NULL); e.arguments.reset(NULL);
} }
} }
void event_fire_signal(int signal) void event_fire_signal(int signal)
@ -565,33 +565,33 @@ void event_fire_signal(int signal)
void event_fire( event_t *event ) void event_fire( event_t *event )
{ {
if( event && (event->type == EVENT_SIGNAL) ) if( event && (event->type == EVENT_SIGNAL) )
{ {
event_fire_signal(event->param1.signal); event_fire_signal(event->param1.signal);
} }
else else
{ {
is_event++; is_event++;
/* /*
Fire events triggered by signals Fire events triggered by signals
*/ */
event_fire_delayed(); event_fire_delayed();
if( event ) if( event )
{ {
if( event_is_blocked( event ) ) if( event_is_blocked( event ) )
{ {
blocked.push_back(event_copy(event, 1)); blocked.push_back(event_copy(event, 1));
} }
else else
{ {
event_fire_internal( event ); event_fire_internal( event );
} }
} }
is_event--; is_event--;
} }
} }
@ -604,36 +604,36 @@ void event_destroy()
for_each(events.begin(), events.end(), event_free); for_each(events.begin(), events.end(), event_free);
events.clear(); events.clear();
for_each(killme.begin(), killme.end(), event_free); for_each(killme.begin(), killme.end(), event_free);
killme.clear(); killme.clear();
} }
void event_free( event_t *e ) void event_free( event_t *e )
{ {
CHECK( e, ); CHECK( e, );
delete e; delete e;
} }
void event_fire_generic_internal(const wchar_t *name, ...) void event_fire_generic_internal(const wchar_t *name, ...)
{ {
va_list va; va_list va;
wchar_t *arg; wchar_t *arg;
CHECK( name, ); CHECK( name, );
event_t ev(EVENT_GENERIC); event_t ev(EVENT_GENERIC);
ev.str_param1 = name; ev.str_param1 = name;
ev.arguments.reset(new wcstring_list_t); ev.arguments.reset(new wcstring_list_t);
va_start( va, name ); va_start( va, name );
while( (arg=va_arg(va, wchar_t *) )!= 0 ) while( (arg=va_arg(va, wchar_t *) )!= 0 )
{ {
ev.arguments->push_back(arg); ev.arguments->push_back(arg);
} }
va_end( va ); va_end( va );
event_fire( &ev ); event_fire( &ev );
ev.arguments.reset(NULL); ev.arguments.reset(NULL);
} }

78
event.h
View file

@ -1,13 +1,13 @@
/** \file event.h /** \file event.h
Functions for handling event triggers Functions for handling event triggers
Because most of these functions can be called by signal
handler, it is important to make it well defined when these
functions produce output or perform memory allocations, since
such functions may not be safely called by signal handlers.
Because most of these functions can be called by signal
handler, it is important to make it well defined when these
functions produce output or perform memory allocations, since
such functions may not be safely called by signal handlers.
*/ */
#ifndef FISH_EVENT_H #ifndef FISH_EVENT_H
#define FISH_EVENT_H #define FISH_EVENT_H
@ -31,14 +31,14 @@
*/ */
enum enum
{ {
EVENT_ANY, /**< Matches any event type (Not always any event, as the function name may limit the choice as well */ EVENT_ANY, /**< Matches any event type (Not always any event, as the function name may limit the choice as well */
EVENT_SIGNAL, /**< An event triggered by a signal */ EVENT_SIGNAL, /**< An event triggered by a signal */
EVENT_VARIABLE, /**< An event triggered by a variable update */ EVENT_VARIABLE, /**< An event triggered by a variable update */
EVENT_EXIT, /**< An event triggered by a job or process exit */ EVENT_EXIT, /**< An event triggered by a job or process exit */
EVENT_JOB_ID, /**< An event triggered by a job exit */ EVENT_JOB_ID, /**< An event triggered by a job exit */
EVENT_GENERIC, /**< A generic event */ EVENT_GENERIC, /**< A generic event */
} }
; ;
/** /**
The structure which represents an event. The event_t struct has The structure which represents an event. The event_t struct has
@ -49,75 +49,75 @@ enum
*/ */
struct event_t struct event_t
{ {
/** /**
Type of event Type of event
*/ */
int type; int type;
/** The type-specific parameter. The int types are one of the following: /** The type-specific parameter. The int types are one of the following:
signal: Signal number for signal-type events.Use EVENT_ANY_SIGNAL to match any signal signal: Signal number for signal-type events.Use EVENT_ANY_SIGNAL to match any signal
pid: Process id for process-type events. Use EVENT_ANY_PID to match any pid. pid: Process id for process-type events. Use EVENT_ANY_PID to match any pid.
job_id: Job id for EVENT_JOB_ID type events job_id: Job id for EVENT_JOB_ID type events
*/ */
union { union {
int signal; int signal;
int job_id; int job_id;
pid_t pid; pid_t pid;
} param1; } param1;
/** The string types are one of the following: /** The string types are one of the following:
variable: Variable name for variable-type events. variable: Variable name for variable-type events.
param: The parameter describing this generic event. param: The parameter describing this generic event.
*/ */
wcstring str_param1; wcstring str_param1;
/** /**
The name of the event handler function The name of the event handler function
*/ */
wcstring function_name; wcstring function_name;
/** /**
The argument list. Only used when sending a new event using The argument list. Only used when sending a new event using
event_fire. In all other situations, the value of this variable event_fire. In all other situations, the value of this variable
is ignored. is ignored.
*/ */
std::auto_ptr<wcstring_list_t> arguments; std::auto_ptr<wcstring_list_t> arguments;
event_t(int t) : type(t), param1(), str_param1(), function_name(), arguments() { } event_t(int t) : type(t), param1(), str_param1(), function_name(), arguments() { }
/** Copy constructor */ /** Copy constructor */
event_t(const event_t &x); event_t(const event_t &x);
static event_t signal_event(int sig); static event_t signal_event(int sig);
static event_t variable_event(const wcstring &str); static event_t variable_event(const wcstring &str);
static event_t generic_event(const wcstring &str); static event_t generic_event(const wcstring &str);
}; };
/** /**
Add an event handler Add an event handler
May not be called by a signal handler, since it may allocate new memory. May not be called by a signal handler, since it may allocate new memory.
*/ */
void event_add_handler( const event_t *event ); void event_add_handler( const event_t *event );
/** /**
Remove all events matching the specified criterion. Remove all events matching the specified criterion.
May not be called by a signal handler, since it may free allocated memory. May not be called by a signal handler, since it may free allocated memory.
*/ */
void event_remove( event_t *event ); void event_remove( event_t *event );
/** /**
Return all events which match the specified event class Return all events which match the specified event class
This function is safe to call from a signal handler _ONLY_ if the This function is safe to call from a signal handler _ONLY_ if the
out parameter is null. out parameter is null.
\param criterion Is the class of events to return. If the criterion has a non-null function_name, only events which trigger the specified function will return. \param criterion Is the class of events to return. If the criterion has a non-null function_name, only events which trigger the specified function will return.
\param out the list to add events to. May be 0, in which case no events will be added, but the result count will still be valid \param out the list to add events to. May be 0, in which case no events will be added, but the result count will still be valid
\return the number of found matches \return the number of found matches
*/ */
int event_get( event_t *criterion, std::vector<event_t *> *out ); int event_get( event_t *criterion, std::vector<event_t *> *out );

1814
exec.cpp

File diff suppressed because it is too large Load diff

4
exec.h
View file

@ -1,5 +1,5 @@
/** \file exec.h /** \file exec.h
Prototypes for functions for executing a program Prototypes for functions for executing a program
*/ */
#ifndef FISH_EXEC_H #ifndef FISH_EXEC_H
@ -21,7 +21,7 @@
#define PIPE_ERROR _(L"An error occurred while setting up pipe") #define PIPE_ERROR _(L"An error occurred while setting up pipe")
/** /**
Execute the processes specified by j. Execute the processes specified by j.
I've put a fair bit of work into making builtins behave like other I've put a fair bit of work into making builtins behave like other
programs as far as pipes are concerned. Unlike i.e. bash, builtins programs as far as pipes are concerned. Unlike i.e. bash, builtins

1536
expand.cpp

File diff suppressed because it is too large Load diff

View file

@ -6,7 +6,7 @@
benefit from using a more clever memory allocation scheme, perhaps benefit from using a more clever memory allocation scheme, perhaps
an evil combination of talloc, string buffers and reference an evil combination of talloc, string buffers and reference
counting. counting.
*/ */
#ifndef FISH_EXPAND_H #ifndef FISH_EXPAND_H
@ -24,10 +24,10 @@
enum { enum {
/** Flag specifying that cmdsubst expansion should be skipped */ /** Flag specifying that cmdsubst expansion should be skipped */
EXPAND_SKIP_CMDSUBST = 1 << 0, EXPAND_SKIP_CMDSUBST = 1 << 0,
/** Flag specifying that variable expansion should be skipped */ /** Flag specifying that variable expansion should be skipped */
EXPAND_SKIP_VARIABLES = 1 << 1, EXPAND_SKIP_VARIABLES = 1 << 1,
/** Flag specifying that wildcard expansion should be skipped */ /** Flag specifying that wildcard expansion should be skipped */
EXPAND_SKIP_WILDCARDS = 1 << 2, EXPAND_SKIP_WILDCARDS = 1 << 2,
@ -36,21 +36,21 @@ enum {
completion). An incomplete match is a wildcard that matches a completion). An incomplete match is a wildcard that matches a
prefix of the filename. If accept_incomplete is true, only the prefix of the filename. If accept_incomplete is true, only the
remainder of the string is returned. remainder of the string is returned.
*/ */
ACCEPT_INCOMPLETE = 1 << 3, ACCEPT_INCOMPLETE = 1 << 3,
/** Only match files that are executable by the current user. Only applicable together with ACCEPT_INCOMPLETE. */ /** Only match files that are executable by the current user. Only applicable together with ACCEPT_INCOMPLETE. */
EXECUTABLES_ONLY = 1 << 4, EXECUTABLES_ONLY = 1 << 4,
/** Only match directories. Only applicable together with ACCEPT_INCOMPLETE. */ /** Only match directories. Only applicable together with ACCEPT_INCOMPLETE. */
DIRECTORIES_ONLY = 1 << 5, DIRECTORIES_ONLY = 1 << 5,
/** Don't generate descriptions */ /** Don't generate descriptions */
EXPAND_NO_DESCRIPTIONS = 1 << 6, EXPAND_NO_DESCRIPTIONS = 1 << 6,
/** Don't do process expansion */ /** Don't do process expansion */
EXPAND_SKIP_PROCESS = 1 << 7, EXPAND_SKIP_PROCESS = 1 << 7,
/** Don't expand jobs (but you can still expand processes). This is because job expansion is not thread safe. */ /** Don't expand jobs (but you can still expand processes). This is because job expansion is not thread safe. */
EXPAND_SKIP_JOBS = 1 << 8 EXPAND_SKIP_JOBS = 1 << 8
}; };
@ -69,33 +69,33 @@ class completion_t;
enum enum
{ {
/** Character represeting a home directory */ /** Character represeting a home directory */
HOME_DIRECTORY = EXPAND_RESERVED, HOME_DIRECTORY = EXPAND_RESERVED,
/** Character represeting process expansion */ /** Character represeting process expansion */
PROCESS_EXPAND, PROCESS_EXPAND,
/** Character representing variable expansion */
VARIABLE_EXPAND,
/** Character rpresenting variable expansion into a single element*/ /** Character representing variable expansion */
VARIABLE_EXPAND_SINGLE, VARIABLE_EXPAND,
/** Character representing the start of a bracket expansion */ /** Character rpresenting variable expansion into a single element*/
BRACKET_BEGIN, VARIABLE_EXPAND_SINGLE,
/** Character representing the end of a bracket expansion */ /** Character representing the start of a bracket expansion */
BRACKET_END, BRACKET_BEGIN,
/** Character representing separation between two bracket elements */ /** Character representing the end of a bracket expansion */
BRACKET_SEP, BRACKET_END,
/**
Separate subtokens in a token with this character. /** Character representing separation between two bracket elements */
*/ BRACKET_SEP,
INTERNAL_SEPARATOR, /**
Separate subtokens in a token with this character.
*/
INTERNAL_SEPARATOR,
} }
; ;
/** /**
@ -103,14 +103,14 @@ enum
*/ */
enum enum
{ {
/** Error */ /** Error */
EXPAND_ERROR, EXPAND_ERROR,
/** Ok */ /** Ok */
EXPAND_OK, EXPAND_OK,
/** Ok, a wildcard in the string matched no files */ /** Ok, a wildcard in the string matched no files */
EXPAND_WILDCARD_NO_MATCH, EXPAND_WILDCARD_NO_MATCH,
/* Ok, a wildcard in the string matched a file */ /* Ok, a wildcard in the string matched a file */
EXPAND_WILDCARD_MATCH EXPAND_WILDCARD_MATCH
}; };
/** Character for separating two array elements. We use 30, i.e. the ascii record separator since that seems logical. */ /** Character for separating two array elements. We use 30, i.e. the ascii record separator since that seems logical. */
@ -132,7 +132,7 @@ class parser_t;
(\$VAR_NAME becomes the value of the environment variable VAR_NAME), (\$VAR_NAME becomes the value of the environment variable VAR_NAME),
cmdsubst expansion and wildcard expansion. The results are inserted cmdsubst expansion and wildcard expansion. The results are inserted
into the list out. into the list out.
If the parameter does not need expansion, it is copied into the list If the parameter does not need expansion, it is copied into the list
out. out.
@ -148,7 +148,7 @@ __warn_unused int expand_string( const wcstring &input, std::vector<completion_t
expand_one is identical to expand_string, except it will fail if in expand_one is identical to expand_string, except it will fail if in
expands to more than one string. This is used for expanding command expands to more than one string. This is used for expanding command
names. names.
\param inout_str The parameter to expand in-place \param inout_str The parameter to expand in-place
\param flag Specifies if any expansion pass should be skipped. Legal values are any combination of EXPAND_SKIP_CMDSUBST EXPAND_SKIP_VARIABLES and EXPAND_SKIP_WILDCARDS \param flag Specifies if any expansion pass should be skipped. Legal values are any combination of EXPAND_SKIP_CMDSUBST EXPAND_SKIP_VARIABLES and EXPAND_SKIP_WILDCARDS
\return Whether expansion succeded \return Whether expansion succeded

File diff suppressed because it is too large Load diff

View file

@ -61,16 +61,16 @@ typedef char tputs_arg_t;
/** /**
Structure used to get the size of a terminal window Structure used to get the size of a terminal window
*/ */
struct winsize struct winsize
{ {
/** /**
Number of rows Number of rows
*/ */
unsigned short ws_row; unsigned short ws_row;
/** /**
Number of columns Number of columns
*/ */
unsigned short ws_col; unsigned short ws_col;
} }
; ;
@ -197,7 +197,7 @@ wchar_t *wcstok(wchar_t *wcs, const wchar_t *delim, wchar_t **ptr);
/** /**
Return the number of columns used by a character. This is a libc Return the number of columns used by a character. This is a libc
function, but the prototype for this function is missing in some libc function, but the prototype for this function is missing in some libc
implementations. implementations.
Fish has a fallback implementation in case the implementation is Fish has a fallback implementation in case the implementation is
missing altogether. In locales without a native wcwidth, Unicode missing altogether. In locales without a native wcwidth, Unicode
@ -219,7 +219,7 @@ int wcwidth( wchar_t c );
int wcscasecmp_use_weak(const wchar_t *, const wchar_t *); int wcscasecmp_use_weak(const wchar_t *, const wchar_t *);
#define wcsdup(a) wcsdup_use_weak((a)) #define wcsdup(a) wcsdup_use_weak((a))
#define wcscasecmp(a, b) wcscasecmp_use_weak((a), (b)) #define wcscasecmp(a, b) wcscasecmp_use_weak((a), (b))
#else #else
#ifndef HAVE_WCSDUP #ifndef HAVE_WCSDUP
@ -312,8 +312,8 @@ long convert_digit( wchar_t d, int base );
supported. supported.
*/ */
long wcstol(const wchar_t *nptr, long wcstol(const wchar_t *nptr,
wchar_t **endptr, wchar_t **endptr,
int base); int base);
#endif #endif
#ifndef HAVE_WCSLCAT #ifndef HAVE_WCSLCAT
@ -340,7 +340,7 @@ size_t wcslcat( wchar_t *dst, const wchar_t *src, size_t siz );
wcslen(src); if retval >= siz, truncation occurred. wcslen(src); if retval >= siz, truncation occurred.
This is the OpenBSD strlcpy function, modified for wide characters, This is the OpenBSD strlcpy function, modified for wide characters,
and renamed to reflect this change. and renamed to reflect this change.
*/ */
size_t wcslcpy( wchar_t *dst, const wchar_t *src, size_t siz ); size_t wcslcpy( wchar_t *dst, const wchar_t *src, size_t siz );
@ -361,10 +361,10 @@ size_t wcslcpy( wchar_t *dst, const wchar_t *src, size_t siz );
*/ */
struct drand48_data struct drand48_data
{ {
/** /**
Seed value Seed value
*/ */
unsigned int seed; unsigned int seed;
} }
; ;
@ -410,9 +410,9 @@ char * textdomain( const char * domainname );
/** /**
Fallback implementation of dcgettext. Just returns the original string. Fallback implementation of dcgettext. Just returns the original string.
*/ */
char * dcgettext ( const char * domainname, char * dcgettext ( const char * domainname,
const char * msgid, const char * msgid,
int category ); int category );
#endif #endif
@ -441,44 +441,44 @@ int killpg( int pgr, int sig );
/** /**
Struct describing a long getopt option Struct describing a long getopt option
*/ */
struct option struct option
{ {
/** /**
Name of option Name of option
*/ */
const char *name; const char *name;
/** /**
Flag Flag
*/ */
int has_arg; int has_arg;
/** /**
Flag Flag
*/ */
int *flag; int *flag;
/** /**
Return value Return value
*/ */
int val; int val;
} }
; ;
#ifndef no_argument #ifndef no_argument
#define no_argument 0 #define no_argument 0
#endif #endif
#ifndef required_argument #ifndef required_argument
#define required_argument 1 #define required_argument 1
#endif #endif
#ifndef optional_argument #ifndef optional_argument
#define optional_argument 2 #define optional_argument 2
#endif #endif
int getopt_long(int argc, int getopt_long(int argc,
char * const argv[], char * const argv[],
const char *optstring, const char *optstring,
const struct option *longopts, const struct option *longopts,
int *longindex); int *longindex);
#endif #endif

620
fish.cpp
View file

@ -17,7 +17,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
/** \file fish.c /** \file fish.c
The main loop of <tt>fish</tt>. The main loop of <tt>fish</tt>.
*/ */
#include "config.h" #include "config.h"
@ -104,12 +104,12 @@ static std::string get_executable_path(const char *argv0)
uint32_t buffSize = sizeof buff; uint32_t buffSize = sizeof buff;
if (0 == _NSGetExecutablePath(buff, &buffSize)) if (0 == _NSGetExecutablePath(buff, &buffSize))
return std::string(buff); return std::string(buff);
/* Loop until we're big enough */ /* Loop until we're big enough */
char *mbuff = (char *)malloc(buffSize); char *mbuff = (char *)malloc(buffSize);
while (0 > _NSGetExecutablePath(mbuff, &buffSize)) while (0 > _NSGetExecutablePath(mbuff, &buffSize))
mbuff = (char *)realloc(mbuff, buffSize); mbuff = (char *)realloc(mbuff, buffSize);
/* Return the string */ /* Return the string */
std::string result = mbuff; std::string result = mbuff;
free(mbuff); free(mbuff);
@ -125,7 +125,7 @@ static std::string get_executable_path(const char *argv0)
return std::string(buff); return std::string(buff);
} }
} }
/* Just return argv0, which probably won't work (i.e. it's not an absolute path or a path relative to the working directory, but instead something the caller found via $PATH). We'll eventually fall back to the compile time paths. */ /* Just return argv0, which probably won't work (i.e. it's not an absolute path or a path relative to the working directory, but instead something the caller found via $PATH). We'll eventually fall back to the compile time paths. */
return std::string(argv0 ? argv0 : ""); return std::string(argv0 ? argv0 : "");
} }
@ -137,9 +137,9 @@ static struct config_paths_t determine_config_directory_paths(const char *argv0)
bool done = false; bool done = false;
std::string exec_path = get_executable_path(argv0); std::string exec_path = get_executable_path(argv0);
if (get_realpath(exec_path)) if (get_realpath(exec_path))
{ {
#if __APPLE__ #if __APPLE__
/* On OS X, maybe we're an app bundle, and should use the bundle's files. Since we don't link CF, use this lame approach to test it: see if the resolved path ends with /Contents/MacOS/fish, case insensitive since HFS+ usually is. /* On OS X, maybe we're an app bundle, and should use the bundle's files. Since we don't link CF, use this lame approach to test it: see if the resolved path ends with /Contents/MacOS/fish, case insensitive since HFS+ usually is.
*/ */
if (! done) if (! done)
@ -152,28 +152,28 @@ static struct config_paths_t determine_config_directory_paths(const char *argv0)
wcstring wide_resolved_path = str2wcstring(exec_path); wcstring wide_resolved_path = str2wcstring(exec_path);
wide_resolved_path.resize(exec_path.size() - suffixlen); wide_resolved_path.resize(exec_path.size() - suffixlen);
wide_resolved_path.append(L"/Contents/Resources/"); wide_resolved_path.append(L"/Contents/Resources/");
/* Append share, etc, doc */ /* Append share, etc, doc */
paths.data = wide_resolved_path + L"share/fish"; paths.data = wide_resolved_path + L"share/fish";
paths.sysconf = wide_resolved_path + L"etc/fish"; paths.sysconf = wide_resolved_path + L"etc/fish";
paths.doc = wide_resolved_path + L"doc/fish"; paths.doc = wide_resolved_path + L"doc/fish";
/* But the bin_dir is the resolved_path, minus fish (aka the MacOS directory) */ /* But the bin_dir is the resolved_path, minus fish (aka the MacOS directory) */
paths.bin = str2wcstring(exec_path); paths.bin = str2wcstring(exec_path);
paths.bin.resize(paths.bin.size() - strlen("/fish")); paths.bin.resize(paths.bin.size() - strlen("/fish"));
done = true; done = true;
} }
} }
#endif #endif
if (! done) if (! done)
{ {
/* The next check is that we are in a reloctable directory tree like this: /* The next check is that we are in a reloctable directory tree like this:
bin/fish bin/fish
etc/fish etc/fish
share/fish share/fish
Check it! Check it!
*/ */
const char *suffix = "/bin/fish"; const char *suffix = "/bin/fish";
@ -181,12 +181,12 @@ static struct config_paths_t determine_config_directory_paths(const char *argv0)
{ {
wcstring base_path = str2wcstring(exec_path); wcstring base_path = str2wcstring(exec_path);
base_path.resize(base_path.size() - strlen(suffix)); base_path.resize(base_path.size() - strlen(suffix));
paths.data = base_path + L"/share/fish"; paths.data = base_path + L"/share/fish";
paths.sysconf = base_path + L"/etc/fish"; paths.sysconf = base_path + L"/etc/fish";
paths.doc = base_path + L"/share/doc/fish"; paths.doc = base_path + L"/share/doc/fish";
paths.bin = base_path + L"/bin"; paths.bin = base_path + L"/bin";
struct stat buf; struct stat buf;
if (0 == wstat(paths.data, &buf) && 0 == wstat(paths.sysconf, &buf)) if (0 == wstat(paths.data, &buf) && 0 == wstat(paths.sysconf, &buf))
{ {
@ -195,7 +195,7 @@ static struct config_paths_t determine_config_directory_paths(const char *argv0)
} }
} }
} }
if (! done) if (! done)
{ {
/* Fall back to what got compiled in. */ /* Fall back to what got compiled in. */
@ -203,10 +203,10 @@ static struct config_paths_t determine_config_directory_paths(const char *argv0)
paths.sysconf = L"" SYSCONFDIR "/fish"; paths.sysconf = L"" SYSCONFDIR "/fish";
paths.doc = L"" DATADIR "/doc/fish"; paths.doc = L"" DATADIR "/doc/fish";
paths.bin = L"" PREFIX "/bin"; paths.bin = L"" PREFIX "/bin";
done = true; done = true;
} }
return paths; return paths;
} }
@ -217,27 +217,27 @@ static int read_init(const struct config_paths_t &paths)
{ {
parser_t &parser = parser_t::principal_parser(); parser_t &parser = parser_t::principal_parser();
const io_chain_t empty_ios; const io_chain_t empty_ios;
parser.eval( L"builtin . " + paths.data + L"/config.fish 2>/dev/null", empty_ios, TOP ); parser.eval( L"builtin . " + paths.data + L"/config.fish 2>/dev/null", empty_ios, TOP );
parser.eval( L"builtin . " + paths.sysconf + L"/config.fish 2>/dev/null", empty_ios, TOP ); parser.eval( L"builtin . " + paths.sysconf + L"/config.fish 2>/dev/null", empty_ios, TOP );
/*
We need to get the configuration directory before we can source the user configuration file
*/
wcstring config_dir;
/* /*
If path_get_config returns false then we have no configuration directory We need to get the configuration directory before we can source the user configuration file
and no custom config to load. */
*/ wcstring config_dir;
/*
If path_get_config returns false then we have no configuration directory
and no custom config to load.
*/
if (path_get_config(config_dir)) if (path_get_config(config_dir))
{ {
wcstring config_dir_escaped = escape_string( config_dir, 1 ); wcstring config_dir_escaped = escape_string( config_dir, 1 );
wcstring eval_buff = format_string(L"builtin . %ls/config.fish 2>/dev/null", config_dir_escaped.c_str()); wcstring eval_buff = format_string(L"builtin . %ls/config.fish 2>/dev/null", config_dir_escaped.c_str());
parser.eval( eval_buff, empty_ios, TOP ); parser.eval( eval_buff, empty_ios, TOP );
} }
return 1; return 1;
} }
@ -247,162 +247,162 @@ static int read_init(const struct config_paths_t &paths)
*/ */
static int fish_parse_opt( int argc, char **argv, const char **cmd_ptr ) static int fish_parse_opt( int argc, char **argv, const char **cmd_ptr )
{ {
int my_optind; int my_optind;
int force_interactive=0; int force_interactive=0;
while( 1 )
{
static struct option
long_options[] =
{
{
"command", required_argument, 0, 'c'
}
,
{
"debug-level", required_argument, 0, 'd'
}
,
{
"interactive", no_argument, 0, 'i'
}
,
{
"login", no_argument, 0, 'l'
}
,
{
"no-execute", no_argument, 0, 'n'
}
,
{
"profile", required_argument, 0, 'p'
}
,
{
"help", no_argument, 0, 'h'
}
,
{
"version", no_argument, 0, 'v'
}
,
{
0, 0, 0, 0
}
}
;
int opt_index = 0;
int opt = getopt_long( argc,
argv,
GETOPT_STRING,
long_options,
&opt_index );
if( opt == -1 )
break;
switch( opt )
{
case 0:
{
break;
}
case 'c':
{
*cmd_ptr = optarg;
is_interactive_session = 0;
break;
}
case 'd':
{
char *end;
long tmp;
errno = 0; while( 1 )
tmp = strtol(optarg, &end, 10); {
static struct option
if( tmp >= 0 && tmp <=10 && !*end && !errno ) long_options[] =
{ {
debug_level = (int)tmp; {
} "command", required_argument, 0, 'c'
else }
{ ,
debug( 0, _(L"Invalid value '%s' for debug level switch"), optarg ); {
exit_without_destructors(1); "debug-level", required_argument, 0, 'd'
} }
break; ,
} {
"interactive", no_argument, 0, 'i'
case 'h': }
{ ,
*cmd_ptr = "__fish_print_help fish"; {
break; "login", no_argument, 0, 'l'
} }
,
case 'i': {
{ "no-execute", no_argument, 0, 'n'
force_interactive = 1; }
break; ,
} {
"profile", required_argument, 0, 'p'
case 'l': }
{ ,
is_login=1; {
break; "help", no_argument, 0, 'h'
} }
,
case 'n': {
{ "version", no_argument, 0, 'v'
no_exec=1; }
break; ,
} {
0, 0, 0, 0
case 'p': }
{ }
profile = optarg; ;
break;
}
case 'v':
{
fwprintf( stderr,
_(L"%s, version %s\n"),
PACKAGE_NAME,
PACKAGE_VERSION );
exit_without_destructors( 0 );
}
case '?':
{
exit_without_destructors( 1 );
}
}
}
my_optind = optind; int opt_index = 0;
is_login |= (strcmp( argv[0], "-fish") == 0);
/*
We are an interactive session if we have not been given an
explicit command to execute, _and_ stdin is a tty.
*/
is_interactive_session &= (*cmd_ptr == 0);
is_interactive_session &= (my_optind == argc);
is_interactive_session &= isatty(STDIN_FILENO);
/* int opt = getopt_long( argc,
We are also an interactive session if we have are forced- argv,
*/ GETOPT_STRING,
is_interactive_session |= force_interactive; long_options,
&opt_index );
return my_optind; if( opt == -1 )
break;
switch( opt )
{
case 0:
{
break;
}
case 'c':
{
*cmd_ptr = optarg;
is_interactive_session = 0;
break;
}
case 'd':
{
char *end;
long tmp;
errno = 0;
tmp = strtol(optarg, &end, 10);
if( tmp >= 0 && tmp <=10 && !*end && !errno )
{
debug_level = (int)tmp;
}
else
{
debug( 0, _(L"Invalid value '%s' for debug level switch"), optarg );
exit_without_destructors(1);
}
break;
}
case 'h':
{
*cmd_ptr = "__fish_print_help fish";
break;
}
case 'i':
{
force_interactive = 1;
break;
}
case 'l':
{
is_login=1;
break;
}
case 'n':
{
no_exec=1;
break;
}
case 'p':
{
profile = optarg;
break;
}
case 'v':
{
fwprintf( stderr,
_(L"%s, version %s\n"),
PACKAGE_NAME,
PACKAGE_VERSION );
exit_without_destructors( 0 );
}
case '?':
{
exit_without_destructors( 1 );
}
}
}
my_optind = optind;
is_login |= (strcmp( argv[0], "-fish") == 0);
/*
We are an interactive session if we have not been given an
explicit command to execute, _and_ stdin is a tty.
*/
is_interactive_session &= (*cmd_ptr == 0);
is_interactive_session &= (my_optind == argc);
is_interactive_session &= isatty(STDIN_FILENO);
/*
We are also an interactive session if we have are forced-
*/
is_interactive_session |= force_interactive;
return my_optind;
} }
/** /**
@ -412,68 +412,68 @@ static int fish_parse_opt( int argc, char **argv, const char **cmd_ptr )
static wcstring full_escape( const wchar_t *in ) static wcstring full_escape( const wchar_t *in )
{ {
wcstring out; wcstring out;
for( ; *in; in++ ) for( ; *in; in++ )
{ {
if( *in < 32 ) if( *in < 32 )
{ {
append_format( out, L"\\x%.2x", *in ); append_format( out, L"\\x%.2x", *in );
} }
else if( *in < 128 ) else if( *in < 128 )
{ {
out.push_back(*in); out.push_back(*in);
} }
else if( *in < 65536 ) else if( *in < 65536 )
{ {
append_format( out, L"\\u%.4x", *in ); append_format( out, L"\\u%.4x", *in );
} }
else else
{ {
append_format( out, L"\\U%.8x", *in ); append_format( out, L"\\U%.8x", *in );
} }
} }
return out; return out;
} }
extern int g_fork_count; extern int g_fork_count;
int main( int argc, char **argv ) int main( int argc, char **argv )
{ {
int res=1; int res=1;
const char *cmd=0; const char *cmd=0;
int my_optind=0; int my_optind=0;
set_main_thread(); set_main_thread();
setup_fork_guards(); setup_fork_guards();
wsetlocale( LC_ALL, L"" ); wsetlocale( LC_ALL, L"" );
is_interactive_session=1; is_interactive_session=1;
program_name=L"fish"; program_name=L"fish";
//struct stat tmp; //struct stat tmp;
//stat("----------FISH_HIT_MAIN----------", &tmp); //stat("----------FISH_HIT_MAIN----------", &tmp);
my_optind = fish_parse_opt( argc, argv, &cmd ); my_optind = fish_parse_opt( argc, argv, &cmd );
/* /*
No-exec is prohibited when in interactive mode No-exec is prohibited when in interactive mode
*/ */
if( is_interactive_session && no_exec) if( is_interactive_session && no_exec)
{ {
debug( 1, _(L"Can not use the no-execute mode when running an interactive session") ); debug( 1, _(L"Can not use the no-execute mode when running an interactive session") );
no_exec = 0; no_exec = 0;
} }
const struct config_paths_t paths = determine_config_directory_paths(argv[0]); const struct config_paths_t paths = determine_config_directory_paths(argv[0]);
proc_init(); proc_init();
event_init(); event_init();
wutil_init(); wutil_init();
//parser_init(); //parser_init();
builtin_init(); builtin_init();
function_init(); function_init();
env_init(&paths); env_init(&paths);
reader_init(); reader_init();
history_init(); history_init();
parser_t &parser = parser_t::principal_parser(); parser_t &parser = parser_t::principal_parser();
@ -481,91 +481,91 @@ int main( int argc, char **argv )
printf("%d: g_fork_count: %d\n", __LINE__, g_fork_count); printf("%d: g_fork_count: %d\n", __LINE__, g_fork_count);
const io_chain_t empty_ios; const io_chain_t empty_ios;
if( read_init(paths) ) if( read_init(paths) )
{ {
if( cmd != 0 ) if( cmd != 0 )
{ {
wchar_t *cmd_wcs = str2wcs( cmd ); wchar_t *cmd_wcs = str2wcs( cmd );
res = parser.eval( cmd_wcs, empty_ios, TOP ); res = parser.eval( cmd_wcs, empty_ios, TOP );
free(cmd_wcs); free(cmd_wcs);
reader_exit(0, 0); reader_exit(0, 0);
} }
else else
{ {
if( my_optind == argc ) if( my_optind == argc )
{ {
res = reader_read( STDIN_FILENO, empty_ios ); res = reader_read( STDIN_FILENO, empty_ios );
} }
else else
{ {
char **ptr; char **ptr;
char *file = *(argv+(my_optind++)); char *file = *(argv+(my_optind++));
int i; int i;
int fd; int fd;
wchar_t *rel_filename, *abs_filename; wchar_t *rel_filename, *abs_filename;
if( ( fd = open(file, O_RDONLY) ) == -1 )
{
wperror( L"open" );
return 1;
}
if( ( fd = open(file, O_RDONLY) ) == -1 )
{
wperror( L"open" );
return 1;
}
// OK to not do this atomically since we cannot have gone multithreaded yet // OK to not do this atomically since we cannot have gone multithreaded yet
set_cloexec(fd); set_cloexec(fd);
if( *(argv+my_optind)) if( *(argv+my_optind))
{ {
wcstring sb; wcstring sb;
for( i=1,ptr = argv+my_optind; *ptr; i++, ptr++ ) for( i=1,ptr = argv+my_optind; *ptr; i++, ptr++ )
{ {
if( i != 1 ) if( i != 1 )
sb.append( ARRAY_SEP_STR ); sb.append( ARRAY_SEP_STR );
sb.append( str2wcstring( *ptr )); sb.append( str2wcstring( *ptr ));
} }
env_set( L"argv", sb.c_str(), 0 );
}
rel_filename = str2wcs( file ); env_set( L"argv", sb.c_str(), 0 );
abs_filename = wrealpath( rel_filename, 0 ); }
if( !abs_filename ) rel_filename = str2wcs( file );
{ abs_filename = wrealpath( rel_filename, 0 );
abs_filename = wcsdup(rel_filename);
}
reader_push_current_filename( intern( abs_filename ) ); if( !abs_filename )
free( rel_filename ); {
free( abs_filename ); abs_filename = wcsdup(rel_filename);
}
res = reader_read( fd, empty_ios ); reader_push_current_filename( intern( abs_filename ) );
free( rel_filename );
free( abs_filename );
res = reader_read( fd, empty_ios );
if( res )
{
debug( 1,
_(L"Error while reading file %ls\n"),
reader_current_filename()?reader_current_filename(): _(L"Standard input") );
}
reader_pop_current_filename();
}
}
}
proc_fire_event( L"PROCESS_EXIT", EVENT_EXIT, getpid(), res );
history_destroy();
proc_destroy();
builtin_destroy();
reader_destroy();
parser.destroy();
wutil_destroy();
event_destroy();
env_destroy();
if( res )
{
debug( 1,
_(L"Error while reading file %ls\n"),
reader_current_filename()?reader_current_filename(): _(L"Standard input") );
}
reader_pop_current_filename();
}
}
}
proc_fire_event( L"PROCESS_EXIT", EVENT_EXIT, getpid(), res );
history_destroy();
proc_destroy();
builtin_destroy();
reader_destroy();
parser.destroy();
wutil_destroy();
event_destroy();
env_destroy();
if (g_log_forks) if (g_log_forks)
printf("%d: g_fork_count: %d\n", __LINE__, g_fork_count); printf("%d: g_fork_count: %d\n", __LINE__, g_fork_count);
return res?STATUS_UNKNOWN_COMMAND:proc_get_last_status(); return res?STATUS_UNKNOWN_COMMAND:proc_get_last_status();
} }

View file

@ -17,7 +17,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
/** \file fish_indent.cpp /** \file fish_indent.cpp
The fish_indent proegram. The fish_indent proegram.
*/ */
#include "config.h" #include "config.h"
@ -51,22 +51,22 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/ */
static void read_file( FILE *f, wcstring &b ) static void read_file( FILE *f, wcstring &b )
{ {
while( 1 ) while( 1 )
{ {
errno=0; errno=0;
wint_t c = fgetwc( f ); wint_t c = fgetwc( f );
if( c == WEOF ) if( c == WEOF )
{ {
if( errno ) if( errno )
{ {
wperror(L"fgetwc"); wperror(L"fgetwc");
exit(1); exit(1);
} }
break; break;
} }
b.push_back((wchar_t)c); b.push_back((wchar_t)c);
} }
} }
/** /**
@ -76,7 +76,7 @@ static void insert_tabs( wcstring &out, int indent )
{ {
if (indent > 0) if (indent > 0)
out.append((size_t)indent, L'\t'); out.append((size_t)indent, L'\t');
} }
/** /**
@ -84,175 +84,175 @@ static void insert_tabs( wcstring &out, int indent )
*/ */
static int indent( wcstring &out, const wcstring &in, int flags ) static int indent( wcstring &out, const wcstring &in, int flags )
{ {
tokenizer tok; tokenizer tok;
int res=0; int res=0;
int is_command = 1; int is_command = 1;
int indent = 0; int indent = 0;
int do_indent = 1; int do_indent = 1;
int prev_type = 0; int prev_type = 0;
int prev_prev_type = 0; int prev_prev_type = 0;
tok_init( &tok, in.c_str(), TOK_SHOW_COMMENTS );
for( ; tok_has_next( &tok ); tok_next( &tok ) )
{
int type = tok_last_type( &tok );
wchar_t *last = tok_last( &tok );
switch( type )
{
case TOK_STRING:
{
if( is_command )
{
int next_indent = indent;
is_command = 0;
tok_init( &tok, in.c_str(), TOK_SHOW_COMMENTS );
for( ; tok_has_next( &tok ); tok_next( &tok ) )
{
int type = tok_last_type( &tok );
wchar_t *last = tok_last( &tok );
switch( type )
{
case TOK_STRING:
{
if( is_command )
{
int next_indent = indent;
is_command = 0;
wcstring unesc = last; wcstring unesc = last;
unescape_string(unesc, UNESCAPE_SPECIAL); unescape_string(unesc, UNESCAPE_SPECIAL);
if( parser_keywords_is_block(unesc)) if( parser_keywords_is_block(unesc))
{ {
next_indent++; next_indent++;
} }
else if (unesc == L"else") else if (unesc == L"else")
{ {
indent--; indent--;
} }
/* case should have the same indent level as switch*/ /* case should have the same indent level as switch*/
else if (unesc == L"case") else if (unesc == L"case")
{ {
indent--; indent--;
} }
else if (unesc == L"end") else if (unesc == L"end")
{ {
indent--; indent--;
next_indent--; next_indent--;
} }
if( do_indent && flags && prev_type != TOK_PIPE ) if( do_indent && flags && prev_type != TOK_PIPE )
{ {
insert_tabs( out, indent ); insert_tabs( out, indent );
} }
append_format(out, L"%ls", last ); append_format(out, L"%ls", last );
indent = next_indent;
}
else
{
if ( prev_type != TOK_REDIRECT_FD )
out.append( L" " );
out.append( last );
}
break;
}
case TOK_END:
{
if( prev_type != TOK_END || prev_prev_type != TOK_END )
out.append( L"\n" );
do_indent = 1;
is_command = 1;
break;
}
case TOK_PIPE: indent = next_indent;
{
out.append( L" " ); }
if ( last[0] == '2' && !last[1] ) { else
out.append( L"^" ); {
} else if ( last[0] != '1' || last[1] ) { if ( prev_type != TOK_REDIRECT_FD )
out.append( last); out.append( L" " );
out.append( last );
}
break;
}
case TOK_END:
{
if( prev_type != TOK_END || prev_prev_type != TOK_END )
out.append( L"\n" );
do_indent = 1;
is_command = 1;
break;
}
case TOK_PIPE:
{
out.append( L" " );
if ( last[0] == '2' && !last[1] ) {
out.append( L"^" );
} else if ( last[0] != '1' || last[1] ) {
out.append( last);
out.append( L">" ); out.append( L">" );
} }
out.append( L" | " ); out.append( L" | " );
is_command = 1; is_command = 1;
break; break;
} }
case TOK_REDIRECT_OUT:
{
out.append( L" " );
if ( wcscmp( last, L"2" ) == 0 ) {
out.append( L"^" );
} else {
if ( wcscmp( last, L"1" ) != 0 )
out.append( last );
out.append( L"> " );
}
break;
}
case TOK_REDIRECT_APPEND:
{
out.append( L" " );
if ( wcscmp( last, L"2" ) == 0 ) {
out.append( L"^^" );
} else {
if ( wcscmp( last, L"1" ) != 0 )
out.append( last );
out.append( L">> " );
}
break;
}
case TOK_REDIRECT_IN:
{
out.append( L" " );
if ( wcscmp( last, L"0" ) != 0 )
out.append( last );
out.append( L"< " );
break;
}
case TOK_REDIRECT_FD:
{
out.append( L" " );
if ( wcscmp( last, L"1" ) != 0 )
out.append( last );
out.append( L">& " );
break;
}
case TOK_BACKGROUND: case TOK_REDIRECT_OUT:
{ {
out.append( L"&\n" ); out.append( L" " );
do_indent = 1; if ( wcscmp( last, L"2" ) == 0 ) {
is_command = 1; out.append( L"^" );
break; } else {
} if ( wcscmp( last, L"1" ) != 0 )
out.append( last );
case TOK_COMMENT: out.append( L"> " );
{ }
if( do_indent && flags) break;
{ }
insert_tabs( out, indent );
}
append_format( out, L"%ls", last );
do_indent = 1;
break;
}
default:
{
debug( 0, L"Unknown token '%ls'", last );
exit(1);
}
}
prev_prev_type = prev_type;
prev_type = type;
}
tok_destroy( &tok );
return res; case TOK_REDIRECT_APPEND:
{
out.append( L" " );
if ( wcscmp( last, L"2" ) == 0 ) {
out.append( L"^^" );
} else {
if ( wcscmp( last, L"1" ) != 0 )
out.append( last );
out.append( L">> " );
}
break;
}
case TOK_REDIRECT_IN:
{
out.append( L" " );
if ( wcscmp( last, L"0" ) != 0 )
out.append( last );
out.append( L"< " );
break;
}
case TOK_REDIRECT_FD:
{
out.append( L" " );
if ( wcscmp( last, L"1" ) != 0 )
out.append( last );
out.append( L">& " );
break;
}
case TOK_BACKGROUND:
{
out.append( L"&\n" );
do_indent = 1;
is_command = 1;
break;
}
case TOK_COMMENT:
{
if( do_indent && flags)
{
insert_tabs( out, indent );
}
append_format( out, L"%ls", last );
do_indent = 1;
break;
}
default:
{
debug( 0, L"Unknown token '%ls'", last );
exit(1);
}
}
prev_prev_type = prev_type;
prev_type = type;
}
tok_destroy( &tok );
return res;
} }
/** /**
@ -267,7 +267,7 @@ static void trim( wcstring &str )
size_t pos = str.find_first_not_of(L" \n"); size_t pos = str.find_first_not_of(L" \n");
if (pos > 0) if (pos > 0)
str.erase(0, pos); str.erase(0, pos);
pos = str.find_last_not_of(L" \n"); pos = str.find_last_not_of(L" \n");
if (pos != wcstring::npos && pos + 1 < str.length()) if (pos != wcstring::npos && pos + 1 < str.length())
str.erase(pos + 1); str.erase(pos + 1);
@ -278,106 +278,106 @@ static void trim( wcstring &str )
The main mathod. Run the program. The main mathod. Run the program.
*/ */
int main( int argc, char **argv ) int main( int argc, char **argv )
{ {
int do_indent=1; int do_indent=1;
set_main_thread(); set_main_thread();
setup_fork_guards(); setup_fork_guards();
wsetlocale( LC_ALL, L"" );
program_name=L"fish_indent";
while( 1 ) wsetlocale( LC_ALL, L"" );
{ program_name=L"fish_indent";
static struct option
long_options[] =
{
{
"no-indent", no_argument, 0, 'i'
}
,
{
"help", no_argument, 0, 'h'
}
,
{
"version", no_argument, 0, 'v'
}
,
{
0, 0, 0, 0
}
}
;
int opt_index = 0;
int opt = getopt_long( argc,
argv,
GETOPT_STRING,
long_options,
&opt_index );
if( opt == -1 )
break;
switch( opt )
{
case 0:
{
break;
}
case 'h':
{
print_help( "fish_indent", 1 );
exit( 0 );
break;
}
case 'v':
{
fwprintf( stderr,
_(L"%ls, version %s\n"),
program_name,
PACKAGE_VERSION );
exit( 0 );
}
case 'i': while( 1 )
{ {
do_indent = 0; static struct option
break; long_options[] =
} {
{
"no-indent", no_argument, 0, 'i'
case '?': }
{ ,
exit( 1 ); {
} "help", no_argument, 0, 'h'
}
} ,
} {
"version", no_argument, 0, 'v'
}
,
{
0, 0, 0, 0
}
}
;
int opt_index = 0;
int opt = getopt_long( argc,
argv,
GETOPT_STRING,
long_options,
&opt_index );
if( opt == -1 )
break;
switch( opt )
{
case 0:
{
break;
}
case 'h':
{
print_help( "fish_indent", 1 );
exit( 0 );
break;
}
case 'v':
{
fwprintf( stderr,
_(L"%ls, version %s\n"),
program_name,
PACKAGE_VERSION );
exit( 0 );
}
case 'i':
{
do_indent = 0;
break;
}
case '?':
{
exit( 1 );
}
}
}
wcstring sb_in, sb_out; wcstring sb_in, sb_out;
read_file( stdin, sb_in ); read_file( stdin, sb_in );
wutil_init();
if( !indent( sb_out, sb_in, do_indent ) ) wutil_init();
{
if( !indent( sb_out, sb_in, do_indent ) )
{
trim(sb_out); trim(sb_out);
fwprintf( stdout, L"%ls", sb_out.c_str() ); fwprintf( stdout, L"%ls", sb_out.c_str() );
} }
else else
{ {
/* /*
Indenting failed - print original input Indenting failed - print original input
*/ */
fwprintf( stdout, L"%ls", sb_in.c_str() ); fwprintf( stdout, L"%ls", sb_in.c_str() );
} }
wutil_destroy();
return 0; wutil_destroy();
return 0;
} }

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

1172
fishd.cpp

File diff suppressed because it is too large Load diff

View file

@ -1,10 +1,10 @@
/** \file function.c /** \file function.c
Prototypes for functions for storing and retrieving function Prototypes for functions for storing and retrieving function
information. These functions also take care of autoloading information. These functions also take care of autoloading
functions in the $fish_function_path. Actual function evaluation functions in the $fish_function_path. Actual function evaluation
is taken care of by the parser and to some degree the builtin is taken care of by the parser and to some degree the builtin
handling library. handling library.
*/ */
#include "config.h" #include "config.h"
@ -89,18 +89,18 @@ static int load( const wcstring &name )
{ {
ASSERT_IS_MAIN_THREAD(); ASSERT_IS_MAIN_THREAD();
scoped_lock lock(functions_lock); scoped_lock lock(functions_lock);
bool was_autoload = is_autoload; bool was_autoload = is_autoload;
int res; int res;
function_map_t::iterator iter = loaded_functions.find(name); function_map_t::iterator iter = loaded_functions.find(name);
if( iter != loaded_functions.end() && !iter->second.is_autoload ) { if( iter != loaded_functions.end() && !iter->second.is_autoload ) {
/* We have a non-autoload version already */ /* We have a non-autoload version already */
return 0; return 0;
} }
is_autoload = true; is_autoload = true;
res = function_autoloader.load( name, true ); res = function_autoloader.load( name, true );
is_autoload = was_autoload; is_autoload = was_autoload;
return res; return res;
} }
/** /**
@ -109,41 +109,41 @@ static int load( const wcstring &name )
*/ */
static void autoload_names( std::set<wcstring> &names, int get_hidden ) static void autoload_names( std::set<wcstring> &names, int get_hidden )
{ {
size_t i; size_t i;
const env_var_t path_var_wstr = env_get_string( L"fish_function_path" ); const env_var_t path_var_wstr = env_get_string( L"fish_function_path" );
if (path_var_wstr.missing()) if (path_var_wstr.missing())
return; return;
const wchar_t *path_var = path_var_wstr.c_str(); const wchar_t *path_var = path_var_wstr.c_str();
wcstring_list_t path_list; wcstring_list_t path_list;
tokenize_variable_array( path_var, path_list ); tokenize_variable_array( path_var, path_list );
for( i=0; i<path_list.size(); i++ ) for( i=0; i<path_list.size(); i++ )
{ {
const wcstring &ndir_str = path_list.at(i); const wcstring &ndir_str = path_list.at(i);
const wchar_t *ndir = (wchar_t *)ndir_str.c_str(); const wchar_t *ndir = (wchar_t *)ndir_str.c_str();
DIR *dir = wopendir( ndir ); DIR *dir = wopendir( ndir );
if( !dir ) if( !dir )
continue; continue;
wcstring name; wcstring name;
while (wreaddir(dir, name)) while (wreaddir(dir, name))
{ {
const wchar_t *fn = name.c_str(); const wchar_t *fn = name.c_str();
const wchar_t *suffix; const wchar_t *suffix;
if( !get_hidden && fn[0] == L'_' ) if( !get_hidden && fn[0] == L'_' )
continue; continue;
suffix = wcsrchr( fn, L'.' ); suffix = wcsrchr( fn, L'.' );
if( suffix && (wcscmp( suffix, L".fish" ) == 0 ) ) if( suffix && (wcscmp( suffix, L".fish" ) == 0 ) )
{ {
wcstring name(fn, suffix - fn); wcstring name(fn, suffix - fn);
names.insert(name); names.insert(name);
} }
} }
closedir(dir); closedir(dir);
} }
} }
void function_init() void function_init()
@ -181,53 +181,53 @@ function_info_t::function_info_t(const function_info_t &data, const wchar_t *fil
void function_add( const function_data_t &data, const parser_t &parser ) void function_add( const function_data_t &data, const parser_t &parser )
{ {
ASSERT_IS_MAIN_THREAD(); ASSERT_IS_MAIN_THREAD();
CHECK( ! data.name.empty(), ); CHECK( ! data.name.empty(), );
CHECK( data.definition, ); CHECK( data.definition, );
scoped_lock lock(functions_lock); scoped_lock lock(functions_lock);
/* Remove the old function */ /* Remove the old function */
function_remove( data.name ); function_remove( data.name );
/* Create and store a new function */ /* Create and store a new function */
const wchar_t *filename = reader_current_filename(); const wchar_t *filename = reader_current_filename();
int def_offset = parser.line_number_of_character_at_offset(parser.current_block->tok_pos) - 1; int def_offset = parser.line_number_of_character_at_offset(parser.current_block->tok_pos) - 1;
const function_map_t::value_type new_pair(data.name, function_info_t(data, filename, def_offset, is_autoload)); const function_map_t::value_type new_pair(data.name, function_info_t(data, filename, def_offset, is_autoload));
loaded_functions.insert(new_pair); loaded_functions.insert(new_pair);
/* Add event handlers */ /* Add event handlers */
for( std::vector<event_t>::const_iterator iter = data.events.begin(); iter != data.events.end(); ++iter ) for( std::vector<event_t>::const_iterator iter = data.events.begin(); iter != data.events.end(); ++iter )
{ {
event_add_handler( &*iter ); event_add_handler( &*iter );
} }
} }
int function_exists( const wcstring &cmd ) int function_exists( const wcstring &cmd )
{ {
if( parser_keywords_is_reserved(cmd) ) if( parser_keywords_is_reserved(cmd) )
return 0; return 0;
scoped_lock lock(functions_lock); scoped_lock lock(functions_lock);
load(cmd); load(cmd);
return loaded_functions.find(cmd) != loaded_functions.end(); return loaded_functions.find(cmd) != loaded_functions.end();
} }
int function_exists_no_autoload( const wcstring &cmd, const env_vars_snapshot_t &vars ) int function_exists_no_autoload( const wcstring &cmd, const env_vars_snapshot_t &vars )
{ {
if( parser_keywords_is_reserved(cmd) ) if( parser_keywords_is_reserved(cmd) )
return 0; return 0;
scoped_lock lock(functions_lock); scoped_lock lock(functions_lock);
return loaded_functions.find(cmd) != loaded_functions.end() || function_autoloader.can_load(cmd, vars); return loaded_functions.find(cmd) != loaded_functions.end() || function_autoloader.can_load(cmd, vars);
} }
static bool function_remove_ignore_autoload(const wcstring &name) static bool function_remove_ignore_autoload(const wcstring &name)
{ {
scoped_lock lock(functions_lock); scoped_lock lock(functions_lock);
bool erased = (loaded_functions.erase(name) > 0); bool erased = (loaded_functions.erase(name) > 0);
if (erased) { if (erased) {
event_t ev(EVENT_ANY); event_t ev(EVENT_ANY);
ev.function_name=name; ev.function_name=name;
event_remove( &ev ); event_remove( &ev );
} }
return erased; return erased;
@ -252,7 +252,7 @@ static const function_info_t *function_get(const wcstring &name)
return &iter->second; return &iter->second;
} }
} }
bool function_get_definition(const wcstring &name, wcstring *out_definition) bool function_get_definition(const wcstring &name, wcstring *out_definition)
{ {
scoped_lock lock(functions_lock); scoped_lock lock(functions_lock);
@ -277,7 +277,7 @@ int function_get_shadows(const wcstring &name)
return func ? func->shadows : false; return func ? func->shadows : false;
} }
bool function_get_desc(const wcstring &name, wcstring *out_desc) bool function_get_desc(const wcstring &name, wcstring *out_desc)
{ {
/* Empty length string goes to NULL */ /* Empty length string goes to NULL */
@ -293,7 +293,7 @@ bool function_get_desc(const wcstring &name, wcstring *out_desc)
void function_set_desc(const wcstring &name, const wcstring &desc) void function_set_desc(const wcstring &name, const wcstring &desc)
{ {
load(name); load(name);
scoped_lock lock(functions_lock); scoped_lock lock(functions_lock);
function_map_t::iterator iter = loaded_functions.find(name); function_map_t::iterator iter = loaded_functions.find(name);
if (iter != loaded_functions.end()) { if (iter != loaded_functions.end()) {
@ -307,24 +307,24 @@ bool function_copy(const wcstring &name, const wcstring &new_name)
scoped_lock lock(functions_lock); scoped_lock lock(functions_lock);
function_map_t::const_iterator iter = loaded_functions.find(name); function_map_t::const_iterator iter = loaded_functions.find(name);
if (iter != loaded_functions.end()) { 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. // 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)); const function_map_t::value_type new_pair(new_name, function_info_t(iter->second, NULL, 0, false));
loaded_functions.insert(new_pair); loaded_functions.insert(new_pair);
result = true; result = true;
} }
return result; return result;
} }
wcstring_list_t function_get_names(int get_hidden) wcstring_list_t function_get_names(int get_hidden)
{ {
std::set<wcstring> names; std::set<wcstring> names;
scoped_lock lock(functions_lock); scoped_lock lock(functions_lock);
autoload_names(names, get_hidden); autoload_names(names, get_hidden);
function_map_t::const_iterator iter; function_map_t::const_iterator iter;
for (iter = loaded_functions.begin(); iter != loaded_functions.end(); ++iter) { for (iter = loaded_functions.begin(); iter != loaded_functions.end(); ++iter) {
const wcstring &name = iter->first; const wcstring &name = iter->first;
/* Maybe skip hidden */ /* Maybe skip hidden */
if (! get_hidden) { if (! get_hidden) {
if (name.empty() || name.at(0) == L'_') continue; if (name.empty() || name.at(0) == L'_') continue;

View file

@ -1,10 +1,10 @@
/** \file function.h /** \file function.h
Prototypes for functions for storing and retrieving function Prototypes for functions for storing and retrieving function
information. These functions also take care of autoloading information. These functions also take care of autoloading
functions in the $fish_function_path. Actual function evaluation functions in the $fish_function_path. Actual function evaluation
is taken care of by the parser and to some degree the builtin is taken care of by the parser and to some degree the builtin
handling library. handling library.
*/ */
#ifndef FISH_FUNCTION_H #ifndef FISH_FUNCTION_H
@ -28,66 +28,66 @@ class env_vars_snapshot_t;
*/ */
struct function_data_t struct function_data_t
{ {
/** /**
Name of function Name of function
*/ */
wcstring name; wcstring name;
/** /**
Description of function Description of function
*/ */
wcstring description; wcstring description;
/** /**
Function definition Function definition
*/ */
wchar_t *definition; wchar_t *definition;
/** /**
List of all event handlers for this function List of all event handlers for this function
*/ */
std::vector<event_t> events; std::vector<event_t> events;
/** /**
List of all named arguments for this function List of all named arguments for this function
*/ */
wcstring_list_t named_arguments; wcstring_list_t named_arguments;
/** /**
Set to non-zero if invoking this function shadows the variables Set to non-zero if invoking this function shadows the variables
of the underlying function. of the underlying function.
*/ */
int shadows; int shadows;
}; };
class function_info_t { class function_info_t {
public: public:
/** Constructs relevant information from the function_data */ /** Constructs relevant information from the function_data */
function_info_t(const function_data_t &data, const wchar_t *filename, int def_offset, bool autoload); function_info_t(const function_data_t &data, const wchar_t *filename, int def_offset, bool autoload);
/** Used by function_copy */ /** Used by function_copy */
function_info_t(const function_info_t &data, const wchar_t *filename, int def_offset, bool autoload); function_info_t(const function_info_t &data, const wchar_t *filename, int def_offset, bool autoload);
/** Function definition */ /** Function definition */
const wcstring definition; const wcstring definition;
/** Function description. Only the description may be changed after the function is created. */ /** Function description. Only the description may be changed after the function is created. */
wcstring description; wcstring description;
/** File where this function was defined (intern'd string) */ /** File where this function was defined (intern'd string) */
const wchar_t * const definition_file; const wchar_t * const definition_file;
/** Line where definition started */ /** Line where definition started */
const int definition_offset; const int definition_offset;
/** List of all named arguments for this function */ /** List of all named arguments for this function */
const wcstring_list_t named_arguments; const wcstring_list_t named_arguments;
/** Flag for specifying that this function was automatically loaded */ /** Flag for specifying that this function was automatically loaded */
const bool is_autoload; const bool is_autoload;
/** Set to true if invoking this function shadows the variables of the underlying function. */ /** Set to true if invoking this function shadows the variables of the underlying function. */
const bool shadows; const bool shadows;
}; };
/** /**
Initialize function data Initialize function data
*/ */
void function_init(); void function_init();
@ -128,7 +128,7 @@ int function_exists_no_autoload( const wcstring &name, const env_vars_snapshot_t
/** /**
Returns all function names. Returns all function names.
\param get_hidden whether to include hidden functions, i.e. ones starting with an underscore \param get_hidden whether to include hidden functions, i.e. ones starting with an underscore
*/ */
wcstring_list_t function_get_names( int get_hidden ); wcstring_list_t function_get_names( int get_hidden );
@ -139,7 +139,7 @@ wcstring_list_t function_get_names( int get_hidden );
This function does not autoload functions, it will only work on This function does not autoload functions, it will only work on
functions that have already been defined. functions that have already been defined.
This returns an intern'd string. This returns an intern'd string.
*/ */
const wchar_t *function_get_definition_file( const wcstring &name ); const wchar_t *function_get_definition_file( const wcstring &name );

File diff suppressed because it is too large Load diff

View file

@ -1,5 +1,5 @@
/** \file highlight.h /** \file highlight.h
Prototypes for functions for syntax highlighting Prototypes for functions for syntax highlighting
*/ */
#ifndef FISH_HIGHLIGHT_H #ifndef FISH_HIGHLIGHT_H
@ -59,7 +59,7 @@
/** /**
Internal value representing highlighting of an IO redirection Internal value representing highlighting of an IO redirection
*/ */
#define HIGHLIGHT_REDIRECTION 0x800 #define HIGHLIGHT_REDIRECTION 0x800
/** /**
Internal value representing highlighting a potentially valid path Internal value representing highlighting a potentially valid path
*/ */
@ -79,7 +79,7 @@ struct file_detection_context_t;
for each character in buff. for each character in buff.
\param buff The buffer on which to perform syntax highlighting \param buff The buffer on which to perform syntax highlighting
\param color The array in wchich to store the color codes. The first 8 bits are used for fg color, the next 8 bits for bg color. \param color The array in wchich to store the color codes. The first 8 bits are used for fg color, the next 8 bits for bg color.
\param pos the cursor position. Used for quote matching, etc. \param pos the cursor position. Used for quote matching, etc.
\param error a list in which a description of each error will be inserted. May be 0, in whcich case no error descriptions will be generated. \param error a list in which a description of each error will be inserted. May be 0, in whcich case no error descriptions will be generated.
*/ */
@ -91,7 +91,7 @@ void highlight_shell( const wcstring &buffstr, std::vector<int> &color, size_t p
for each character in buff. for each character in buff.
\param buff The buffer on which to perform syntax highlighting \param buff The buffer on which to perform syntax highlighting
\param color The array in wchich to store the color codes. The first 8 bits are used for fg color, the next 8 bits for bg color. \param color The array in wchich to store the color codes. The first 8 bits are used for fg color, the next 8 bits for bg color.
\param pos the cursor position. Used for quote matching, etc. \param pos the cursor position. Used for quote matching, etc.
\param error a list in which a description of each error will be inserted. May be 0, in whcich case no error descriptions will be generated. \param error a list in which a description of each error will be inserted. May be 0, in whcich case no error descriptions will be generated.
*/ */
@ -101,7 +101,7 @@ void highlight_universal( const wcstring &buffstr, std::vector<int> &color, size
Translate from HIGHLIGHT_* to FISH_COLOR_* according to environment Translate from HIGHLIGHT_* to FISH_COLOR_* according to environment
variables. Defaults to FISH_COLOR_NORMAL. variables. Defaults to FISH_COLOR_NORMAL.
Example: Example:
If the environment variable FISH_FISH_COLOR_ERROR is set to 'red', a If the environment variable FISH_FISH_COLOR_ERROR is set to 'red', a
call to highlight_get_color( HIGHLIGHT_ERROR) will return call to highlight_get_color( HIGHLIGHT_ERROR) will return
@ -125,7 +125,7 @@ bool autosuggest_suggest_special(const wcstring &str, const wcstring &working_di
enum { enum {
/* The path must be to a directory */ /* The path must be to a directory */
PATH_REQUIRE_DIR = 1 << 0, PATH_REQUIRE_DIR = 1 << 0,
/* Expand any leading tilde in the path */ /* Expand any leading tilde in the path */
PATH_EXPAND_TILDE = 1 << 1 PATH_EXPAND_TILDE = 1 << 1
}; };

View file

@ -1,5 +1,5 @@
/** \file history.c /** \file history.c
History functions, part of the user interface. History functions, part of the user interface.
*/ */
#include "config.h" #include "config.h"
@ -42,7 +42,7 @@ Our history format is intended to be valid YAML. Here it is:
paths: paths:
- /path/to/something - /path/to/something
- /path/to/something_else - /path/to/something_else
Newlines are replaced by \n. Backslashes are replaced by \\. Newlines are replaced by \n. Backslashes are replaced by \\.
*/ */
@ -64,14 +64,14 @@ class time_profiler_t {
const char *what; const char *what;
double start; double start;
public: public:
time_profiler_t(const char *w) { time_profiler_t(const char *w) {
if (LOG_TIMES) { if (LOG_TIMES) {
what = w; what = w;
start = timef(); start = timef();
} }
} }
~time_profiler_t() { ~time_profiler_t() {
if (LOG_TIMES) { if (LOG_TIMES) {
double end = timef(); double end = timef();
@ -90,13 +90,13 @@ class history_lru_node_t : public lru_node_t {
timestamp(item.timestamp()), timestamp(item.timestamp()),
required_paths(item.required_paths) required_paths(item.required_paths)
{} {}
bool write_yaml_to_file(FILE *f) const; bool write_yaml_to_file(FILE *f) const;
}; };
class history_lru_cache_t : public lru_cache_t<history_lru_node_t> { class history_lru_cache_t : public lru_cache_t<history_lru_node_t> {
protected: protected:
/* Override to delete evicted nodes */ /* Override to delete evicted nodes */
virtual void node_was_evicted(history_lru_node_t *node) { virtual void node_was_evicted(history_lru_node_t *node) {
delete node; delete node;
@ -104,13 +104,13 @@ class history_lru_cache_t : public lru_cache_t<history_lru_node_t> {
public: public:
history_lru_cache_t(size_t max) : lru_cache_t<history_lru_node_t>(max) { } history_lru_cache_t(size_t max) : lru_cache_t<history_lru_node_t>(max) { }
/* Function to add a history item */ /* Function to add a history item */
void add_item(const history_item_t &item) { void add_item(const history_item_t &item) {
/* Skip empty items */ /* Skip empty items */
if (item.empty()) if (item.empty())
return; return;
/* See if it's in the cache. If it is, update the timestamp. If not, we create a new node and add it. Note that calling get_node promotes the node to the front. */ /* See if it's in the cache. If it is, update the timestamp. If not, we create a new node and add it. Note that calling get_node promotes the node to the front. */
history_lru_node_t *node = this->get_node(item.str()); history_lru_node_t *node = this->get_node(item.str());
if (node != NULL) { if (node != NULL) {
@ -159,15 +159,15 @@ history_item_t::history_item_t(const wcstring &str, time_t when, const path_list
bool history_item_t::matches_search(const wcstring &term, enum history_search_type_t type) const { bool history_item_t::matches_search(const wcstring &term, enum history_search_type_t type) const {
switch (type) { switch (type) {
case HISTORY_SEARCH_TYPE_CONTAINS: case HISTORY_SEARCH_TYPE_CONTAINS:
/* We consider equal strings to NOT match a contains search (so that you don't have to see history equal to what you typed). The length check ensures that. */ /* We consider equal strings to NOT match a contains search (so that you don't have to see history equal to what you typed). The length check ensures that. */
return contents.size() > term.size() && contents.find(term) != wcstring::npos; return contents.size() > term.size() && contents.find(term) != wcstring::npos;
case HISTORY_SEARCH_TYPE_PREFIX: case HISTORY_SEARCH_TYPE_PREFIX:
/* We consider equal strings to match a prefix search, so that autosuggest will allow suggesting what you've typed */ /* We consider equal strings to match a prefix search, so that autosuggest will allow suggesting what you've typed */
return string_prefixes_string(term, contents); return string_prefixes_string(term, contents);
default: default:
sanity_lose(); sanity_lose();
return false; return false;
@ -180,18 +180,18 @@ bool history_lru_node_t::write_yaml_to_file(FILE *f) const {
escape_yaml(cmd); escape_yaml(cmd);
if (fprintf(f, "- cmd: %s\n", cmd.c_str()) < 0) if (fprintf(f, "- cmd: %s\n", cmd.c_str()) < 0)
return false; return false;
if (fprintf(f, " when: %ld\n", (long)timestamp) < 0) if (fprintf(f, " when: %ld\n", (long)timestamp) < 0)
return false; return false;
if (! required_paths.empty()) { if (! required_paths.empty()) {
if (fputs(" paths:\n", f) < 0) if (fputs(" paths:\n", f) < 0)
return false; return false;
for (path_list_t::const_iterator iter = required_paths.begin(); iter != required_paths.end(); ++iter) { for (path_list_t::const_iterator iter = required_paths.begin(); iter != required_paths.end(); ++iter) {
std::string path = wcs2string(*iter); std::string path = wcs2string(*iter);
escape_yaml(path); escape_yaml(path);
if (fprintf(f, " - %s\n", path.c_str()) < 0) if (fprintf(f, " - %s\n", path.c_str()) < 0)
return false; return false;
} }
@ -207,17 +207,17 @@ static bool parse_timestamp(const char *str, time_t *out_when) {
/* Advance past spaces */ /* Advance past spaces */
while (*cursor == ' ') while (*cursor == ' ')
cursor++; cursor++;
/* Look for "when:" */ /* Look for "when:" */
size_t when_len = 5; size_t when_len = 5;
if (strncmp(cursor, "when:", when_len) != 0) if (strncmp(cursor, "when:", when_len) != 0)
return false; return false;
cursor += when_len; cursor += when_len;
/* Advance past spaces */ /* Advance past spaces */
while (*cursor == ' ') while (*cursor == ' ')
cursor++; cursor++;
/* Try to parse a timestamp. */ /* Try to parse a timestamp. */
long timestamp = 0; long timestamp = 0;
if (isdigit(*cursor) && (timestamp = strtol(cursor, NULL, 0)) > 0) { if (isdigit(*cursor) && (timestamp = strtol(cursor, NULL, 0)) > 0) {
@ -234,10 +234,10 @@ static const char *next_line(const char *start, size_t length) {
/* Handle the hopeless case */ /* Handle the hopeless case */
if (length < 1) if (length < 1)
return NULL; return NULL;
/* Get a pointer to the end, that we must not pass */ /* Get a pointer to the end, that we must not pass */
const char * const end = start + length; const char * const end = start + length;
/* Skip past the next newline */ /* Skip past the next newline */
const char *nextline = (const char *)memchr(start, '\n', length); const char *nextline = (const char *)memchr(start, '\n', length);
if (! nextline || nextline >= end) { if (! nextline || nextline >= end) {
@ -247,13 +247,13 @@ static const char *next_line(const char *start, size_t length) {
if (++nextline >= end) { if (++nextline >= end) {
return NULL; return NULL;
} }
/* Make sure this new line is itself "newline terminated". If it's not, return NULL; */ /* Make sure this new line is itself "newline terminated". If it's not, return NULL; */
const char *next_newline = (const char *)memchr(nextline, '\n', end - nextline); const char *next_newline = (const char *)memchr(nextline, '\n', end - nextline);
if (! next_newline) { if (! next_newline) {
return NULL; return NULL;
} }
/* Done */ /* Done */
return nextline; return nextline;
} }
@ -269,12 +269,12 @@ static size_t offset_of_next_item_fish_2_0(const char *begin, size_t mmap_length
size_t result = (size_t)(-1); size_t result = (size_t)(-1);
while (cursor < mmap_length) { while (cursor < mmap_length) {
const char * const line_start = begin + cursor; const char * const line_start = begin + cursor;
/* Advance the cursor to the next line */ /* Advance the cursor to the next line */
const char *newline = (const char *)memchr(line_start, '\n', mmap_length - cursor); const char *newline = (const char *)memchr(line_start, '\n', mmap_length - cursor);
if (newline == NULL) if (newline == NULL)
break; break;
/* Advance the cursor past this line. +1 is for the newline */ /* Advance the cursor past this line. +1 is for the newline */
size_t line_len = newline - line_start; size_t line_len = newline - line_start;
cursor += line_len + 1; cursor += line_len + 1;
@ -282,11 +282,11 @@ static size_t offset_of_next_item_fish_2_0(const char *begin, size_t mmap_length
/* Skip lines with a leading space, since these are in the interior of one of our items */ /* Skip lines with a leading space, since these are in the interior of one of our items */
if (line_start[0] == ' ') if (line_start[0] == ' ')
continue; continue;
/* Skip very short lines to make one of the checks below easier */ /* Skip very short lines to make one of the checks below easier */
if (line_len < 3) if (line_len < 3)
continue; continue;
/* Try to be a little YAML compatible. Skip lines with leading %, ---, or ... */ /* Try to be a little YAML compatible. Skip lines with leading %, ---, or ... */
if (! memcmp(line_start, "%", 1) || if (! memcmp(line_start, "%", 1) ||
! memcmp(line_start, "---", 3) || ! memcmp(line_start, "---", 3) ||
@ -298,7 +298,7 @@ static size_t offset_of_next_item_fish_2_0(const char *begin, size_t mmap_length
/* Hackish fast way to skip items created after our timestamp. This is the mechanism by which we avoid "seeing" commands from other sessions that started after we started. We try hard to ensure that our items are sorted by their timestamps, so in theory we could just break, but I don't think that works well if (for example) the clock changes. So we'll read all subsequent items. /* Hackish fast way to skip items created after our timestamp. This is the mechanism by which we avoid "seeing" commands from other sessions that started after we started. We try hard to ensure that our items are sorted by their timestamps, so in theory we could just break, but I don't think that works well if (for example) the clock changes. So we'll read all subsequent items.
*/ */
const char * const end = begin + mmap_length; const char * const end = begin + mmap_length;
/* Walk over lines that we think are interior. These lines are not null terminated, but are guaranteed to contain a newline. */ /* Walk over lines that we think are interior. These lines are not null terminated, but are guaranteed to contain a newline. */
bool has_timestamp = false; bool has_timestamp = false;
time_t timestamp; time_t timestamp;
@ -306,18 +306,18 @@ static size_t offset_of_next_item_fish_2_0(const char *begin, size_t mmap_length
for (interior_line = next_line(line_start, end - line_start); for (interior_line = next_line(line_start, end - line_start);
interior_line != NULL && ! has_timestamp; interior_line != NULL && ! has_timestamp;
interior_line = next_line(interior_line, end - interior_line)) { interior_line = next_line(interior_line, end - interior_line)) {
/* If the first character is not a space, it's not an interior line, so we're done */ /* If the first character is not a space, it's not an interior line, so we're done */
if (interior_line[0] != ' ') if (interior_line[0] != ' ')
break; break;
/* Hackish optimization: since we just stepped over some interior line, update the cursor so we don't have to look at these lines next time */ /* Hackish optimization: since we just stepped over some interior line, update the cursor so we don't have to look at these lines next time */
cursor = interior_line - begin; cursor = interior_line - begin;
/* Try parsing a timestamp from this line. If we succeed, the loop will break. */ /* Try parsing a timestamp from this line. If we succeed, the loop will break. */
has_timestamp = parse_timestamp(interior_line, &timestamp); has_timestamp = parse_timestamp(interior_line, &timestamp);
} }
/* Skip this item if the timestamp is at or after our cutoff. */ /* Skip this item if the timestamp is at or after our cutoff. */
if (has_timestamp && timestamp >= cutoff_timestamp) { if (has_timestamp && timestamp >= cutoff_timestamp) {
continue; continue;
@ -338,47 +338,47 @@ static size_t offset_of_next_item_fish_2_0(const char *begin, size_t mmap_length
static size_t offset_of_next_item_fish_1_x(const char *begin, size_t mmap_length, size_t *inout_cursor, time_t cutoff_timestamp) { static size_t offset_of_next_item_fish_1_x(const char *begin, size_t mmap_length, size_t *inout_cursor, time_t cutoff_timestamp) {
if (mmap_length == 0 || *inout_cursor >= mmap_length) if (mmap_length == 0 || *inout_cursor >= mmap_length)
return (size_t)(-1); return (size_t)(-1);
const char *end = begin + mmap_length;
const char *pos;
bool ignore_newline = false; const char *end = begin + mmap_length;
bool do_push = true; const char *pos;
bool ignore_newline = false;
bool do_push = true;
bool all_done = false; bool all_done = false;
size_t result = *inout_cursor; size_t result = *inout_cursor;
for( pos = begin + *inout_cursor; pos < end && ! all_done; pos++ ) for( pos = begin + *inout_cursor; pos < end && ! all_done; pos++ )
{ {
if( do_push ) if( do_push )
{ {
ignore_newline = (*pos == '#'); ignore_newline = (*pos == '#');
do_push = false; do_push = false;
} }
switch( *pos ) switch( *pos )
{ {
case '\\': case '\\':
{ {
pos++; pos++;
break; break;
} }
case '\n': case '\n':
{ {
if( ignore_newline ) if( ignore_newline )
{ {
ignore_newline = false; ignore_newline = false;
} }
else else
{ {
/* Note: pos will be left pointing just after this newline, because of the ++ in the loop */ /* Note: pos will be left pointing just after this newline, because of the ++ in the loop */
all_done = true; all_done = true;
} }
break; break;
} }
} }
} }
*inout_cursor = (pos - begin); *inout_cursor = (pos - begin);
return result; return result;
} }
@ -390,11 +390,11 @@ static size_t offset_of_next_item(const char *begin, size_t mmap_length, history
case history_type_fish_2_0: case history_type_fish_2_0:
result = offset_of_next_item_fish_2_0(begin, mmap_length, inout_cursor, cutoff_timestamp); result = offset_of_next_item_fish_2_0(begin, mmap_length, inout_cursor, cutoff_timestamp);
break; break;
case history_type_fish_1_x: case history_type_fish_1_x:
result = offset_of_next_item_fish_1_x(begin, mmap_length, inout_cursor, cutoff_timestamp); result = offset_of_next_item_fish_1_x(begin, mmap_length, inout_cursor, cutoff_timestamp);
break; break;
default: default:
case history_type_unknown: case history_type_unknown:
// Oh well // Oh well
@ -433,7 +433,7 @@ history_t::~history_t()
void history_t::add(const history_item_t &item) void history_t::add(const history_item_t &item)
{ {
scoped_lock locker(lock); scoped_lock locker(lock);
/* Try merging with the last item */ /* Try merging with the last item */
if (! new_items.empty() && new_items.back().merge(item)) { if (! new_items.empty() && new_items.back().merge(item)) {
/* We merged, so we don't have to add anything */ /* We merged, so we don't have to add anything */
@ -444,16 +444,16 @@ void history_t::add(const history_item_t &item)
new_items.push_back(item); new_items.push_back(item);
unsaved_item_count++; unsaved_item_count++;
} }
/* Prevent the first write from always triggering a save */ /* Prevent the first write from always triggering a save */
time_t now = time(NULL); time_t now = time(NULL);
if (! save_timestamp) if (! save_timestamp)
save_timestamp = now; save_timestamp = now;
/* This might be a good candidate for moving to a background thread */ /* This might be a good candidate for moving to a background thread */
if((now > save_timestamp + SAVE_INTERVAL) || (unsaved_item_count >= SAVE_COUNT)) { if((now > save_timestamp + SAVE_INTERVAL) || (unsaved_item_count >= SAVE_COUNT)) {
time_profiler_t profiler("save_internal"); time_profiler_t profiler("save_internal");
this->save_internal(); this->save_internal();
} }
} }
@ -467,7 +467,7 @@ void history_t::remove(const wcstring &str)
{ {
/* Add to our list of deleted items */ /* Add to our list of deleted items */
deleted_items.insert(str); deleted_items.insert(str);
/* Remove from our list of new items */ /* Remove from our list of new items */
for (std::vector<history_item_t>::iterator iter = new_items.begin(); iter != new_items.end();) for (std::vector<history_item_t>::iterator iter = new_items.begin(); iter != new_items.end();)
{ {
@ -482,9 +482,9 @@ void history_t::remove(const wcstring &str)
void history_t::get_string_representation(wcstring &result, const wcstring &separator) void history_t::get_string_representation(wcstring &result, const wcstring &separator)
{ {
scoped_lock locker(lock); scoped_lock locker(lock);
bool first = true; bool first = true;
/* Append new items */ /* Append new items */
for (std::vector<history_item_t>::const_reverse_iterator iter=new_items.rbegin(); iter < new_items.rend(); ++iter) { for (std::vector<history_item_t>::const_reverse_iterator iter=new_items.rbegin(); iter < new_items.rend(); ++iter) {
if (! first) if (! first)
@ -492,10 +492,10 @@ void history_t::get_string_representation(wcstring &result, const wcstring &sepa
result.append(iter->str()); result.append(iter->str());
first = false; first = false;
} }
/* Append old items */ /* Append old items */
load_old_if_needed(); load_old_if_needed();
for (std::vector<size_t>::const_reverse_iterator iter = old_item_offsets.rbegin(); iter != old_item_offsets.rend(); ++iter) { for (std::vector<size_t>::const_reverse_iterator iter = old_item_offsets.rbegin(); iter != old_item_offsets.rend(); ++iter) {
size_t offset = *iter; size_t offset = *iter;
const history_item_t item = history_t::decode_item(mmap_start + offset, mmap_length - offset, mmap_type); const history_item_t item = history_t::decode_item(mmap_start + offset, mmap_length - offset, mmap_type);
if (! first) if (! first)
@ -507,17 +507,17 @@ void history_t::get_string_representation(wcstring &result, const wcstring &sepa
history_item_t history_t::item_at_index(size_t idx) { history_item_t history_t::item_at_index(size_t idx) {
scoped_lock locker(lock); scoped_lock locker(lock);
/* 0 is considered an invalid index */ /* 0 is considered an invalid index */
assert(idx > 0); assert(idx > 0);
idx--; idx--;
/* idx=0 corresponds to last item in new_items */ /* idx=0 corresponds to last item in new_items */
size_t new_item_count = new_items.size(); size_t new_item_count = new_items.size();
if (idx < new_item_count) { if (idx < new_item_count) {
return new_items.at(new_item_count - idx - 1); return new_items.at(new_item_count - idx - 1);
} }
/* Now look in our old items */ /* Now look in our old items */
idx -= new_item_count; idx -= new_item_count;
load_old_if_needed(); load_old_if_needed();
@ -527,7 +527,7 @@ history_item_t history_t::item_at_index(size_t idx) {
size_t offset = old_item_offsets.at(old_item_count - idx - 1); size_t offset = old_item_offsets.at(old_item_count - idx - 1);
return history_t::decode_item(mmap_start + offset, mmap_length - offset, mmap_type); return history_t::decode_item(mmap_start + offset, mmap_length - offset, mmap_type);
} }
/* Index past the valid range, so return an empty history item */ /* Index past the valid range, so return an empty history item */
return history_item_t(wcstring(), 0); return history_item_t(wcstring(), 0);
} }
@ -541,7 +541,7 @@ static size_t read_line(const char *base, size_t cursor, size_t len, std::string
if (newline != NULL) { if (newline != NULL) {
/* We found a newline. */ /* We found a newline. */
result.assign(start, newline - start); result.assign(start, newline - start);
/* Return the amount to advance the cursor; skip over the newline */ /* Return the amount to advance the cursor; skip over the newline */
return newline - start + 1; return newline - start + 1;
} else { } else {
@ -564,13 +564,13 @@ static bool extract_prefix(std::string &key, std::string &value, const std::stri
size_t where = line.find(":"); size_t where = line.find(":");
if (where != std::string::npos) { if (where != std::string::npos) {
key = line.substr(0, where); key = line.substr(0, where);
// skip a space after the : if necessary // skip a space after the : if necessary
size_t val_start = where + 1; size_t val_start = where + 1;
if (val_start < line.size() && line.at(val_start) == ' ') if (val_start < line.size() && line.at(val_start) == ' ')
val_start++; val_start++;
value = line.substr(val_start); value = line.substr(val_start);
unescape_yaml(key); unescape_yaml(key);
unescape_yaml(value); unescape_yaml(value);
} }
@ -582,16 +582,16 @@ history_item_t history_t::decode_item_fish_2_0(const char *base, size_t len) {
wcstring cmd; wcstring cmd;
time_t when = 0; time_t when = 0;
path_list_t paths; path_list_t paths;
size_t indent = 0, cursor = 0; size_t indent = 0, cursor = 0;
std::string key, value, line; std::string key, value, line;
/* Read the "- cmd:" line */ /* Read the "- cmd:" line */
size_t advance = read_line(base, cursor, len, line); size_t advance = read_line(base, cursor, len, line);
trim_leading_spaces(line); trim_leading_spaces(line);
if (! extract_prefix(key, value, line) || key != "- cmd") if (! extract_prefix(key, value, line) || key != "- cmd")
goto done; goto done;
cursor += advance; cursor += advance;
cmd = str2wcstring(value); cmd = str2wcstring(value);
@ -599,22 +599,22 @@ history_item_t history_t::decode_item_fish_2_0(const char *base, size_t len) {
for (;;) { for (;;) {
/* Read a line */ /* Read a line */
size_t advance = read_line(base, cursor, len, line); size_t advance = read_line(base, cursor, len, line);
/* Count and trim leading spaces */ /* Count and trim leading spaces */
size_t this_indent = trim_leading_spaces(line); size_t this_indent = trim_leading_spaces(line);
if (indent == 0) if (indent == 0)
indent = this_indent; indent = this_indent;
if (this_indent == 0 || indent != this_indent) if (this_indent == 0 || indent != this_indent)
break; break;
if (! extract_prefix(key, value, line)) if (! extract_prefix(key, value, line))
break; break;
/* We are definitely going to consume this line */ /* We are definitely going to consume this line */
unescape_yaml(value); unescape_yaml(value);
cursor += advance; cursor += advance;
if (key == "when") { if (key == "when") {
/* Parse an int from the timestamp */ /* Parse an int from the timestamp */
long tmp = 0; long tmp = 0;
@ -627,13 +627,13 @@ history_item_t history_t::decode_item_fish_2_0(const char *base, size_t len) {
size_t advance = read_line(base, cursor, len, line); size_t advance = read_line(base, cursor, len, line);
if (trim_leading_spaces(line) <= indent) if (trim_leading_spaces(line) <= indent)
break; break;
if (strncmp(line.c_str(), "- ", 2)) if (strncmp(line.c_str(), "- ", 2))
break; break;
/* We're going to consume this line */ /* We're going to consume this line */
cursor += advance; cursor += advance;
/* Skip the leading dash-space and then store this path it */ /* Skip the leading dash-space and then store this path it */
line.erase(0, 2); line.erase(0, 2);
unescape_yaml(line); unescape_yaml(line);
@ -660,21 +660,21 @@ history_item_t history_t::decode_item(const char *base, size_t len, history_file
static wcstring history_unescape_newlines_fish_1_x( const wcstring &in_str ) static wcstring history_unescape_newlines_fish_1_x( const wcstring &in_str )
{ {
wcstring out; wcstring out;
for (const wchar_t *in = in_str.c_str(); *in; in++) for (const wchar_t *in = in_str.c_str(); *in; in++)
{ {
if( *in == L'\\' ) if( *in == L'\\' )
{ {
if( *(in+1)!= L'\n') if( *(in+1)!= L'\n')
{ {
out.push_back(*in); out.push_back(*in);
} }
} }
else else
{ {
out.push_back(*in); out.push_back(*in);
} }
} }
return out; return out;
} }
@ -689,7 +689,7 @@ history_item_t history_t::decode_item_fish_1_x(const char *begin, size_t length)
bool first_char = true; bool first_char = true;
bool timestamp_mode = false; bool timestamp_mode = false;
time_t timestamp = 0; time_t timestamp = 0;
while( 1 ) while( 1 )
{ {
wchar_t c; wchar_t c;
@ -761,7 +761,7 @@ history_item_t history_t::decode_item_fish_1_x(const char *begin, size_t length)
was_backslash = ( (c == L'\\') && !was_backslash); was_backslash = ( (c == L'\\') && !was_backslash);
} }
out = history_unescape_newlines_fish_1_x(out); out = history_unescape_newlines_fish_1_x(out);
return history_item_t(out, timestamp); return history_item_t(out, timestamp);
} }
@ -793,7 +793,7 @@ void history_t::populate_from_mmap(void)
break; break;
// Remember this item // Remember this item
old_item_offsets.push_back(offset); old_item_offsets.push_back(offset);
} }
} }
@ -805,25 +805,25 @@ static bool map_file(const wcstring &name, const char **out_map_start, size_t *o
if (! filename.empty()) if (! filename.empty())
{ {
int fd; int fd;
if((fd = wopen_cloexec(filename, O_RDONLY)) > 0) if((fd = wopen_cloexec(filename, O_RDONLY)) > 0)
{ {
off_t len = lseek( fd, 0, SEEK_END ); off_t len = lseek( fd, 0, SEEK_END );
if(len != (off_t)-1) if(len != (off_t)-1)
{ {
size_t mmap_length = (size_t)len; size_t mmap_length = (size_t)len;
if(lseek(fd, 0, SEEK_SET) == 0) if(lseek(fd, 0, SEEK_SET) == 0)
{ {
char *mmap_start; char *mmap_start;
if ((mmap_start = (char *)mmap(0, mmap_length, PROT_READ, MAP_PRIVATE, fd, 0)) != MAP_FAILED) if ((mmap_start = (char *)mmap(0, mmap_length, PROT_READ, MAP_PRIVATE, fd, 0)) != MAP_FAILED)
{ {
result = true; result = true;
*out_map_start = mmap_start; *out_map_start = mmap_start;
*out_map_len = mmap_length; *out_map_len = mmap_length;
} }
} }
} }
close( fd ); close( fd );
} }
} }
return result; return result;
} }
@ -832,20 +832,20 @@ bool history_t::load_old_if_needed(void)
{ {
if (loaded_old) return true; if (loaded_old) return true;
loaded_old = true; loaded_old = true;
// PCA not sure why signals were blocked here // PCA not sure why signals were blocked here
//signal_block(); //signal_block();
bool ok = false; bool ok = false;
if (map_file(name, &mmap_start, &mmap_length)) { if (map_file(name, &mmap_start, &mmap_length)) {
// Here we've mapped the file // Here we've mapped the file
ok = true; ok = true;
time_profiler_t profiler("populate_from_mmap"); time_profiler_t profiler("populate_from_mmap");
this->populate_from_mmap(); this->populate_from_mmap();
} }
//signal_unblock(); //signal_unblock();
return ok; return ok;
} }
@ -874,10 +874,10 @@ bool history_search_t::go_backwards() {
size_t idx = 0; size_t idx = 0;
if (! prev_matches.empty()) if (! prev_matches.empty())
idx = prev_matches.back().first; idx = prev_matches.back().first;
if (idx == max_idx) if (idx == max_idx)
return false; return false;
while (++idx < max_idx) { while (++idx < max_idx) {
const history_item_t item = history->item_at_index(idx); const history_item_t item = history->item_at_index(idx);
/* We're done if it's empty */ /* We're done if it's empty */
@ -915,7 +915,7 @@ void history_search_t::go_to_beginning(void) {
history_item_t history_search_t::current_item() const { history_item_t history_search_t::current_item() const {
assert(! prev_matches.empty()); assert(! prev_matches.empty());
return prev_matches.back().second; return prev_matches.back().second;
} }
@ -974,7 +974,7 @@ static wcstring history_filename(const wcstring &name, const wcstring &suffix)
wcstring path; wcstring path;
if (! path_get_config(path)) if (! path_get_config(path))
return L""; return L"";
wcstring result = path; wcstring result = path;
result.append(L"/"); result.append(L"/");
result.append(name); result.append(name);
@ -1014,25 +1014,25 @@ void history_t::save_internal()
{ {
/* This must be called while locked */ /* This must be called while locked */
ASSERT_IS_LOCKED(lock); ASSERT_IS_LOCKED(lock);
/* Nothing to do if there's no new items */ /* Nothing to do if there's no new items */
if (new_items.empty() && deleted_items.empty()) if (new_items.empty() && deleted_items.empty())
return; return;
/* Compact our new items so we don't have duplicates */ /* Compact our new items so we don't have duplicates */
this->compact_new_items(); this->compact_new_items();
bool ok = true; bool ok = true;
wcstring tmp_name = history_filename(name, L".tmp"); wcstring tmp_name = history_filename(name, L".tmp");
if( ! tmp_name.empty() ) if( ! tmp_name.empty() )
{ {
/* Make an LRU cache to save only the last N elements */ /* Make an LRU cache to save only the last N elements */
history_lru_cache_t lru(HISTORY_SAVE_MAX); history_lru_cache_t lru(HISTORY_SAVE_MAX);
/* Insert old items in, from old to new. Merge them with our new items, inserting items with earlier timestamps first. */ /* Insert old items in, from old to new. Merge them with our new items, inserting items with earlier timestamps first. */
std::vector<history_item_t>::const_iterator new_item_iter = new_items.begin(); std::vector<history_item_t>::const_iterator new_item_iter = new_items.begin();
/* Map in existing items (which may have changed out from underneath us, so don't trust our old mmap'd data) */ /* Map in existing items (which may have changed out from underneath us, so don't trust our old mmap'd data) */
const char *local_mmap_start = NULL; const char *local_mmap_start = NULL;
size_t local_mmap_size = 0; size_t local_mmap_size = 0;
@ -1062,24 +1062,24 @@ void history_t::save_internal()
break; break;
} }
} }
/* Now add this old item */ /* Now add this old item */
lru.add_item(old_item); lru.add_item(old_item);
} }
munmap((void *)local_mmap_start, local_mmap_size); munmap((void *)local_mmap_start, local_mmap_size);
} }
/* Insert any remaining new items */ /* Insert any remaining new items */
for (; new_item_iter != new_items.end(); ++new_item_iter) for (; new_item_iter != new_items.end(); ++new_item_iter)
{ {
lru.add_item(*new_item_iter); lru.add_item(*new_item_iter);
} }
signal_block(); signal_block();
FILE *out; FILE *out;
if( (out=wfopen( tmp_name, "w" ) ) ) if( (out=wfopen( tmp_name, "w" ) ) )
{ {
/* Write them out */ /* Write them out */
for (history_lru_cache_t::iterator iter = lru.begin(); iter != lru.end(); ++iter) { for (history_lru_cache_t::iterator iter = lru.begin(); iter != lru.end(); ++iter) {
const history_lru_node_t *node = *iter; const history_lru_node_t *node = *iter;
@ -1088,36 +1088,36 @@ void history_t::save_internal()
break; break;
} }
} }
if( fclose( out ) || !ok ) if( fclose( out ) || !ok )
{ {
/* /*
This message does not have high enough priority to This message does not have high enough priority to
be shown by default. be shown by default.
*/ */
debug( 2, L"Error when writing history file" ); debug( 2, L"Error when writing history file" );
} }
else else
{ {
wcstring new_name = history_filename(name, wcstring()); wcstring new_name = history_filename(name, wcstring());
wrename(tmp_name, new_name); wrename(tmp_name, new_name);
} }
} }
signal_unblock(); signal_unblock();
/* Make sure we clear all nodes, since this doesn't happen automatically */ /* Make sure we clear all nodes, since this doesn't happen automatically */
lru.evict_all_nodes(); lru.evict_all_nodes();
/* We've saved everything, so we have no more unsaved items */ /* We've saved everything, so we have no more unsaved items */
unsaved_item_count = 0; unsaved_item_count = 0;
} }
if( ok ) if( ok )
{ {
/* Our history has been written to the file, so clear our state so we can re-reference the file. */ /* Our history has been written to the file, so clear our state so we can re-reference the file. */
this->clear_file_state(); this->clear_file_state();
} }
} }
void history_t::save(void) void history_t::save(void)
@ -1137,7 +1137,7 @@ void history_t::clear(void)
if (! filename.empty()) if (! filename.empty())
wunlink(filename); wunlink(filename);
this->clear_file_state(); this->clear_file_state();
} }
bool history_t::is_empty(void) bool history_t::is_empty(void)
@ -1157,24 +1157,24 @@ static bool should_import_bash_history_line(const std::string &line)
{ {
if (line.empty()) if (line.empty())
return false; return false;
/* Very naive tests! Skip export; probably should skip others. */ /* Very naive tests! Skip export; probably should skip others. */
const char * const ignore_prefixes[] = { const char * const ignore_prefixes[] = {
"export ", "export ",
"#" "#"
}; };
for (size_t i=0; i < sizeof ignore_prefixes / sizeof *ignore_prefixes; i++) { for (size_t i=0; i < sizeof ignore_prefixes / sizeof *ignore_prefixes; i++) {
const char *prefix = ignore_prefixes[i]; const char *prefix = ignore_prefixes[i];
if (! line.compare(0, strlen(prefix), prefix)) { if (! line.compare(0, strlen(prefix), prefix)) {
return false; return false;
} }
} }
/* Skip lines with backticks */ /* Skip lines with backticks */
if (line.find('`') != std::string::npos) if (line.find('`') != std::string::npos)
return false; return false;
return true; return true;
} }
@ -1187,7 +1187,7 @@ void history_t::populate_from_bash(FILE *stream)
for (;;) { for (;;) {
line.clear(); line.clear();
bool success = false, has_newline = false; bool success = false, has_newline = false;
/* Loop until we've read a line */ /* Loop until we've read a line */
do { do {
char buff[128]; char buff[128];
@ -1197,17 +1197,17 @@ void history_t::populate_from_bash(FILE *stream)
char *newline = strchr(buff, '\n'); char *newline = strchr(buff, '\n');
if (newline) *newline = '\0'; if (newline) *newline = '\0';
has_newline = (newline != NULL); has_newline = (newline != NULL);
/* Append what we've got */ /* Append what we've got */
line.append(buff); line.append(buff);
} }
} while (success && ! has_newline); } while (success && ! has_newline);
/* Maybe add this line */ /* Maybe add this line */
if (should_import_bash_history_line(line)) { if (should_import_bash_history_line(line)) {
this->add(str2wcstring(line)); this->add(str2wcstring(line));
} }
if (line.empty()) if (line.empty())
break; break;
} }
@ -1229,16 +1229,16 @@ void history_destroy()
void history_sanity_check() void history_sanity_check()
{ {
/* /*
No sanity checking implemented yet... No sanity checking implemented yet...
*/ */
} }
int file_detection_context_t::perform_file_detection(bool test_all) { int file_detection_context_t::perform_file_detection(bool test_all) {
ASSERT_IS_BACKGROUND_THREAD(); ASSERT_IS_BACKGROUND_THREAD();
valid_paths.clear(); valid_paths.clear();
int result = 1; int result = 1;
for (path_list_t::const_iterator iter = potential_paths.begin(); iter != potential_paths.end(); ++iter) { for (path_list_t::const_iterator iter = potential_paths.begin(); iter != potential_paths.end(); ++iter) {
if (path_is_valid(*iter, working_directory)) { if (path_is_valid(*iter, working_directory)) {
/* Push the original (possibly relative) path */ /* Push the original (possibly relative) path */
valid_paths.push_back(*iter); valid_paths.push_back(*iter);
@ -1274,7 +1274,7 @@ static int threaded_perform_file_detection(file_detection_context_t *ctx) {
static void perform_file_detection_done(file_detection_context_t *ctx, int success) { static void perform_file_detection_done(file_detection_context_t *ctx, int success) {
/* Now that file detection is done, create the history item */ /* Now that file detection is done, create the history item */
ctx->history->add(ctx->command, ctx->valid_paths); ctx->history->add(ctx->command, ctx->valid_paths);
/* Done with the context. */ /* Done with the context. */
delete ctx; delete ctx;
} }
@ -1290,7 +1290,7 @@ void history_t::add_with_file_detection(const wcstring &str)
{ {
ASSERT_IS_MAIN_THREAD(); ASSERT_IS_MAIN_THREAD();
path_list_t potential_paths; path_list_t potential_paths;
tokenizer tokenizer; tokenizer tokenizer;
for( tok_init( &tokenizer, str.c_str(), TOK_SQUASH_ERRORS ); for( tok_init( &tokenizer, str.c_str(), TOK_SQUASH_ERRORS );
tok_has_next( &tokenizer ); tok_has_next( &tokenizer );
@ -1308,11 +1308,11 @@ void history_t::add_with_file_detection(const wcstring &str)
} }
} }
tok_destroy(&tokenizer); tok_destroy(&tokenizer);
if (! potential_paths.empty()) { if (! potential_paths.empty()) {
/* We have some paths. Make a context. */ /* We have some paths. Make a context. */
file_detection_context_t *context = new file_detection_context_t(this, str); file_detection_context_t *context = new file_detection_context_t(this, str);
/* Store the potential paths. Reverse them to put them in the same order as in the command. */ /* Store the potential paths. Reverse them to put them in the same order as in the command. */
context->potential_paths.swap(potential_paths); context->potential_paths.swap(potential_paths);
iothread_perform(threaded_perform_file_detection, perform_file_detection_done, context); iothread_perform(threaded_perform_file_detection, perform_file_detection_done, context);

142
history.h
View file

@ -18,7 +18,7 @@ typedef std::vector<wcstring> path_list_t;
enum history_search_type_t { enum history_search_type_t {
/** The history searches for strings containing the given string */ /** The history searches for strings containing the given string */
HISTORY_SEARCH_TYPE_CONTAINS, HISTORY_SEARCH_TYPE_CONTAINS,
/** The history searches for strings starting with the given string */ /** The history searches for strings starting with the given string */
HISTORY_SEARCH_TYPE_PREFIX HISTORY_SEARCH_TYPE_PREFIX
}; };
@ -31,30 +31,30 @@ class history_item_t {
private: private:
explicit history_item_t(const wcstring &); explicit history_item_t(const wcstring &);
explicit history_item_t(const wcstring &, time_t, const path_list_t &paths = path_list_t()); explicit history_item_t(const wcstring &, time_t, const path_list_t &paths = path_list_t());
/** Attempts to merge two compatible history items together */ /** Attempts to merge two compatible history items together */
bool merge(const history_item_t &item); bool merge(const history_item_t &item);
/** The actual contents of the entry */ /** The actual contents of the entry */
wcstring contents; wcstring contents;
/** Original creation time for the entry */ /** Original creation time for the entry */
time_t creation_timestamp; time_t creation_timestamp;
/** Paths that we require to be valid for this item to be autosuggested */ /** Paths that we require to be valid for this item to be autosuggested */
path_list_t required_paths; path_list_t required_paths;
public: public:
const wcstring &str() const { return contents; } const wcstring &str() const { return contents; }
bool empty() const { return contents.empty(); } bool empty() const { return contents.empty(); }
/* Whether our contents matches a search term. */ /* Whether our contents matches a search term. */
bool matches_search(const wcstring &term, enum history_search_type_t type) const; bool matches_search(const wcstring &term, enum history_search_type_t type) const;
time_t timestamp() const { return creation_timestamp; } time_t timestamp() const { return creation_timestamp; }
const path_list_t &get_required_paths() const { return required_paths; } const path_list_t &get_required_paths() const { return required_paths; }
bool operator==(const history_item_t &other) const { bool operator==(const history_item_t &other) const {
return contents == other.contents && return contents == other.contents &&
creation_timestamp == other.creation_timestamp && creation_timestamp == other.creation_timestamp &&
@ -78,36 +78,36 @@ private:
/** Private creator */ /** Private creator */
history_t(const wcstring &pname); history_t(const wcstring &pname);
/** Privately add an item */ /** Privately add an item */
void add(const history_item_t &item); void add(const history_item_t &item);
/** Destructor */ /** Destructor */
~history_t(); ~history_t();
/** Lock for thread safety */ /** Lock for thread safety */
pthread_mutex_t lock; pthread_mutex_t lock;
/** Internal function */ /** Internal function */
void clear_file_state(); void clear_file_state();
/** The name of this list. Used for picking a suitable filename and for switching modes. */ /** The name of this list. Used for picking a suitable filename and for switching modes. */
const wcstring name; const wcstring name;
/** New items. */
std::vector<history_item_t> new_items;
/** Deleted item contents. */ /** New items. */
std::set<wcstring> deleted_items; std::vector<history_item_t> new_items;
/** How many items we've added without saving */ /** Deleted item contents. */
size_t unsaved_item_count; std::set<wcstring> deleted_items;
/** The mmaped region for the history file */
const char *mmap_start;
/** The size of the mmap'd region */ /** How many items we've added without saving */
size_t mmap_length; size_t unsaved_item_count;
/** The mmaped region for the history file */
const char *mmap_start;
/** The size of the mmap'd region */
size_t mmap_length;
/** The type of file we mmap'd */ /** The type of file we mmap'd */
history_file_type_t mmap_type; history_file_type_t mmap_type;
@ -115,23 +115,23 @@ private:
/** Timestamp of when this history was created */ /** Timestamp of when this history was created */
const time_t birth_timestamp; const time_t birth_timestamp;
/** Timestamp of last save */ /** Timestamp of last save */
time_t save_timestamp; time_t save_timestamp;
void populate_from_mmap(void); void populate_from_mmap(void);
/** List of old items, as offsets into out mmap data */ /** List of old items, as offsets into out mmap data */
std::vector<size_t> old_item_offsets; std::vector<size_t> old_item_offsets;
/** Whether we've loaded old items */ /** Whether we've loaded old items */
bool loaded_old; bool loaded_old;
/** Loads old if necessary */ /** Loads old if necessary */
bool load_old_if_needed(void); bool load_old_if_needed(void);
/** Deletes duplicates in new_items. */ /** Deletes duplicates in new_items. */
void compact_new_items(); void compact_new_items();
/** Saves history */ /** Saves history */
void save_internal(); void save_internal();
@ -139,38 +139,38 @@ private:
static history_item_t decode_item_fish_2_0(const char *base, size_t len); static history_item_t decode_item_fish_2_0(const char *base, size_t len);
static history_item_t decode_item_fish_1_x(const char *base, size_t len); static history_item_t decode_item_fish_1_x(const char *base, size_t len);
static history_item_t decode_item(const char *base, size_t len, history_file_type_t type); static history_item_t decode_item(const char *base, size_t len, history_file_type_t type);
public: public:
/** Returns history with the given name, creating it if necessary */ /** Returns history with the given name, creating it if necessary */
static history_t & history_with_name(const wcstring &name); static history_t & history_with_name(const wcstring &name);
/** Determines whether the history is empty. Unfortunately this cannot be const, since it may require populating the history. */ /** Determines whether the history is empty. Unfortunately this cannot be const, since it may require populating the history. */
bool is_empty(void); bool is_empty(void);
/** Add a new history item to the end */ /** Add a new history item to the end */
void add(const wcstring &str, const path_list_t &valid_paths = path_list_t()); void add(const wcstring &str, const path_list_t &valid_paths = path_list_t());
/** Remove a history item */ /** Remove a history item */
void remove(const wcstring &str); void remove(const wcstring &str);
/** Add a new history item to the end */ /** Add a new history item to the end */
void add_with_file_detection(const wcstring &str); void add_with_file_detection(const wcstring &str);
/** Saves history */ /** Saves history */
void save(); void save();
/** Irreversibly clears history */ /** Irreversibly clears history */
void clear(); void clear();
/** Populates from a bash history file */ /** Populates from a bash history file */
void populate_from_bash(FILE *f); void populate_from_bash(FILE *f);
/* Gets all the history into a string with ARRAY_SEP_STR. This is intended for the $history environment variable. This may be long! */ /* Gets all the history into a string with ARRAY_SEP_STR. This is intended for the $history environment variable. This may be long! */
void get_string_representation(wcstring &str, const wcstring &separator); void get_string_representation(wcstring &str, const wcstring &separator);
/** Return the specified history at the specified index. 0 is the index of the current commandline. (So the most recent item is at index 1.) */ /** Return the specified history at the specified index. 0 is the index of the current commandline. (So the most recent item is at index 1.) */
history_item_t item_at_index(size_t idx); history_item_t item_at_index(size_t idx);
bool is_deleted(const history_item_t &item) const; bool is_deleted(const history_item_t &item) const;
}; };
@ -178,10 +178,10 @@ class history_search_t {
/** The history in which we are searching */ /** The history in which we are searching */
history_t * history; history_t * history;
/** Our type */ /** Our type */
enum history_search_type_t search_type; enum history_search_type_t search_type;
/** Our list of previous matches as index, value. The end is the current match. */ /** Our list of previous matches as index, value. The end is the current match. */
typedef std::pair<size_t, history_item_t> prev_match_t; typedef std::pair<size_t, history_item_t> prev_match_t;
std::vector<prev_match_t> prev_matches; std::vector<prev_match_t> prev_matches;
@ -191,14 +191,14 @@ class history_search_t {
/** The search term */ /** The search term */
wcstring term; wcstring term;
/** Additional strings to skip (sorted) */ /** Additional strings to skip (sorted) */
wcstring_list_t external_skips; wcstring_list_t external_skips;
bool should_skip_match(const wcstring &str) const; bool should_skip_match(const wcstring &str) const;
public: public:
/** Gets the search term */ /** Gets the search term */
const wcstring &get_term() const { return term; } const wcstring &get_term() const { return term; }
@ -207,22 +207,22 @@ class history_search_t {
/** Finds the next search term (forwards in time). Returns true if one was found. */ /** Finds the next search term (forwards in time). Returns true if one was found. */
bool go_forwards(void); bool go_forwards(void);
/** Finds the previous search result (backwards in time). Returns true if one was found. */ /** Finds the previous search result (backwards in time). Returns true if one was found. */
bool go_backwards(void); bool go_backwards(void);
/** Goes to the end (forwards) */ /** Goes to the end (forwards) */
void go_to_end(void); void go_to_end(void);
/** Returns if we are at the end. We start out at the end. */ /** Returns if we are at the end. We start out at the end. */
bool is_at_end(void) const; bool is_at_end(void) const;
/** Goes to the beginning (backwards) */ /** Goes to the beginning (backwards) */
void go_to_beginning(void); void go_to_beginning(void);
/** Returns the current search result item. asserts if there is no current item. */ /** Returns the current search result item. asserts if there is no current item. */
history_item_t current_item(void) const; history_item_t current_item(void) const;
/** Returns the current search result item contents. asserts if there is no current item. */ /** Returns the current search result item contents. asserts if there is no current item. */
wcstring current_string(void) const; wcstring current_string(void) const;
@ -233,14 +233,14 @@ class history_search_t {
search_type(type), search_type(type),
term(str) term(str)
{} {}
/* Default constructor */ /* Default constructor */
history_search_t() : history_search_t() :
history(), history(),
search_type(HISTORY_SEARCH_TYPE_CONTAINS), search_type(HISTORY_SEARCH_TYPE_CONTAINS),
term() term()
{} {}
}; };
@ -266,31 +266,31 @@ struct file_detection_context_t {
/* Constructor */ /* Constructor */
file_detection_context_t(history_t *hist, const wcstring &cmd); file_detection_context_t(history_t *hist, const wcstring &cmd);
/* Determine which of potential_paths are valid, and put them in valid_paths */ /* Determine which of potential_paths are valid, and put them in valid_paths */
int perform_file_detection(); int perform_file_detection();
/* The history associated with this context */ /* The history associated with this context */
history_t *history; history_t *history;
/* The command */ /* The command */
wcstring command; wcstring command;
/* When the command was issued */ /* When the command was issued */
time_t when; time_t when;
/* The working directory at the time the command was issued */ /* The working directory at the time the command was issued */
wcstring working_directory; wcstring working_directory;
/* Paths to test */ /* Paths to test */
path_list_t potential_paths; path_list_t potential_paths;
/* Paths that were found to be valid */ /* Paths that were found to be valid */
path_list_t valid_paths; path_list_t valid_paths;
/* Performs file detection. Returns 1 if every path in potential_paths is valid, 0 otherwise. If test_all is true, tests every path; otherwise stops as soon as it reaches an invalid path. */ /* Performs file detection. Returns 1 if every path in potential_paths is valid, 0 otherwise. If test_all is true, tests every path; otherwise stops as soon as it reaches an invalid path. */
int perform_file_detection(bool test_all); int perform_file_detection(bool test_all);
/* Determine whether the given paths are all valid */ /* Determine whether the given paths are all valid */
bool paths_are_valid(const path_list_t &paths); bool paths_are_valid(const path_list_t &paths);
}; };

774
input.cpp
View file

@ -74,10 +74,10 @@
*/ */
struct input_mapping_t struct input_mapping_t
{ {
wcstring seq; /**< Character sequence which generates this event */ wcstring seq; /**< Character sequence which generates this event */
wcstring command; /**< command that should be evaluated by this mapping */ wcstring command; /**< command that should be evaluated by this mapping */
input_mapping_t(const wcstring &s, const wcstring &c) : seq(s), command(c) {} input_mapping_t(const wcstring &s, const wcstring &c) : seq(s), command(c) {}
}; };
@ -86,9 +86,9 @@ struct input_mapping_t
*/ */
struct terminfo_mapping_t struct terminfo_mapping_t
{ {
const wchar_t *name; /**< Name of key */ const wchar_t *name; /**< Name of key */
const char *seq; /**< Character sequence generated on keypress. Constant string. */ const char *seq; /**< Character sequence generated on keypress. Constant string. */
}; };
@ -97,43 +97,43 @@ struct terminfo_mapping_t
*/ */
static const wchar_t * const name_arr[] = static const wchar_t * const name_arr[] =
{ {
L"beginning-of-line", L"beginning-of-line",
L"end-of-line", L"end-of-line",
L"forward-char", L"forward-char",
L"backward-char", L"backward-char",
L"forward-word", L"forward-word",
L"backward-word", L"backward-word",
L"history-search-backward", L"history-search-backward",
L"history-search-forward", L"history-search-forward",
L"delete-char", L"delete-char",
L"backward-delete-char", L"backward-delete-char",
L"kill-line", L"kill-line",
L"yank", L"yank",
L"yank-pop", L"yank-pop",
L"complete", L"complete",
L"beginning-of-history", L"beginning-of-history",
L"end-of-history", L"end-of-history",
L"backward-kill-line", L"backward-kill-line",
L"kill-whole-line", L"kill-whole-line",
L"kill-word", L"kill-word",
L"backward-kill-word", L"backward-kill-word",
L"dump-functions", L"dump-functions",
L"history-token-search-backward", L"history-token-search-backward",
L"history-token-search-forward", L"history-token-search-forward",
L"self-insert", L"self-insert",
L"null", L"null",
L"eof", L"eof",
L"vi-arg-digit", L"vi-arg-digit",
L"execute", L"execute",
L"beginning-of-buffer", L"beginning-of-buffer",
L"end-of-buffer", L"end-of-buffer",
L"repaint", L"repaint",
L"up-line", L"up-line",
L"down-line", L"down-line",
L"suppress-autosuggestion", L"suppress-autosuggestion",
L"accept-autosuggestion" L"accept-autosuggestion"
} }
; ;
/** /**
Description of each supported input function Description of each supported input function
@ -141,82 +141,82 @@ static const wchar_t * const name_arr[] =
/* /*
static const wchar_t *desc_arr[] = static const wchar_t *desc_arr[] =
{ {
L"Move to beginning of line", L"Move to beginning of line",
L"Move to end of line", L"Move to end of line",
L"Move forward one character", L"Move forward one character",
L"Move backward one character", L"Move backward one character",
L"Move forward one word", L"Move forward one word",
L"Move backward one word", L"Move backward one word",
L"Search backward through list of previous commands", L"Search backward through list of previous commands",
L"Search forward through list of previous commands", L"Search forward through list of previous commands",
L"Delete one character forward", L"Delete one character forward",
L"Delete one character backward", L"Delete one character backward",
L"Move contents from cursor to end of line to killring", L"Move contents from cursor to end of line to killring",
L"Paste contents of killring", L"Paste contents of killring",
L"Rotate to previous killring entry", L"Rotate to previous killring entry",
L"Guess the rest of the next input token", L"Guess the rest of the next input token",
L"Move to first item of history", L"Move to first item of history",
L"Move to last item of history", L"Move to last item of history",
L"Clear current line", L"Clear current line",
L"Move contents from beginning of line to cursor to killring", L"Move contents from beginning of line to cursor to killring",
L"Move entire line to killring", L"Move entire line to killring",
L"Move next word to killring", L"Move next word to killring",
L"Move previous word to killring", L"Move previous word to killring",
L"Write out key bindings", L"Write out key bindings",
L"Clear entire screen", L"Clear entire screen",
L"Quit the running program", L"Quit the running program",
L"Search backward through list of previous commands for matching token", L"Search backward through list of previous commands for matching token",
L"Search forward through list of previous commands for matching token", L"Search forward through list of previous commands for matching token",
L"Insert the pressed key", L"Insert the pressed key",
L"Do nothing", L"Do nothing",
L"End of file", L"End of file",
L"Repeat command" L"Repeat command"
} }
; ;
*/ */
/** /**
Internal code for each supported input function Internal code for each supported input function
*/ */
static const wchar_t code_arr[] = static const wchar_t code_arr[] =
{ {
R_BEGINNING_OF_LINE, R_BEGINNING_OF_LINE,
R_END_OF_LINE, R_END_OF_LINE,
R_FORWARD_CHAR, R_FORWARD_CHAR,
R_BACKWARD_CHAR, R_BACKWARD_CHAR,
R_FORWARD_WORD, R_FORWARD_WORD,
R_BACKWARD_WORD, R_BACKWARD_WORD,
R_HISTORY_SEARCH_BACKWARD, R_HISTORY_SEARCH_BACKWARD,
R_HISTORY_SEARCH_FORWARD, R_HISTORY_SEARCH_FORWARD,
R_DELETE_CHAR, R_DELETE_CHAR,
R_BACKWARD_DELETE_CHAR, R_BACKWARD_DELETE_CHAR,
R_KILL_LINE, R_KILL_LINE,
R_YANK, R_YANK,
R_YANK_POP, R_YANK_POP,
R_COMPLETE, R_COMPLETE,
R_BEGINNING_OF_HISTORY, R_BEGINNING_OF_HISTORY,
R_END_OF_HISTORY, R_END_OF_HISTORY,
R_BACKWARD_KILL_LINE, R_BACKWARD_KILL_LINE,
R_KILL_WHOLE_LINE, R_KILL_WHOLE_LINE,
R_KILL_WORD, R_KILL_WORD,
R_BACKWARD_KILL_WORD, R_BACKWARD_KILL_WORD,
R_DUMP_FUNCTIONS, R_DUMP_FUNCTIONS,
R_HISTORY_TOKEN_SEARCH_BACKWARD, R_HISTORY_TOKEN_SEARCH_BACKWARD,
R_HISTORY_TOKEN_SEARCH_FORWARD, R_HISTORY_TOKEN_SEARCH_FORWARD,
R_SELF_INSERT, R_SELF_INSERT,
R_NULL, R_NULL,
R_EOF, R_EOF,
R_VI_ARG_DIGIT, R_VI_ARG_DIGIT,
R_EXECUTE, R_EXECUTE,
R_BEGINNING_OF_BUFFER, R_BEGINNING_OF_BUFFER,
R_END_OF_BUFFER, R_END_OF_BUFFER,
R_REPAINT, R_REPAINT,
R_UP_LINE, R_UP_LINE,
R_DOWN_LINE, R_DOWN_LINE,
R_SUPPRESS_AUTOSUGGESTION, R_SUPPRESS_AUTOSUGGESTION,
R_ACCEPT_AUTOSUGGESTION R_ACCEPT_AUTOSUGGESTION
} }
; ;
/** Mappings for the current input mode */ /** Mappings for the current input mode */
static std::vector<input_mapping_t> mapping_list; static std::vector<input_mapping_t> mapping_list;
@ -234,7 +234,7 @@ static std::vector<terminfo_mapping_t> mappings;
/** /**
Set to one when the input subsytem has been initialized. Set to one when the input subsytem has been initialized.
*/ */
static int is_init = 0; static int is_init = 0;
@ -249,25 +249,25 @@ static void input_terminfo_init();
*/ */
void input_mapping_add( const wchar_t *sequence, void input_mapping_add( const wchar_t *sequence,
const wchar_t *command ) const wchar_t *command )
{ {
size_t i; size_t i;
CHECK( sequence, ); CHECK( sequence, );
CHECK( command, ); CHECK( command, );
// debug( 0, L"Add mapping from %ls to %ls", escape(sequence, 1), escape(command, 1 ) );
for( i=0; i<mapping_list.size(); i++ ) // debug( 0, L"Add mapping from %ls to %ls", escape(sequence, 1), escape(command, 1 ) );
{
input_mapping_t &m = mapping_list.at(i);
if( m.seq == sequence ) for( i=0; i<mapping_list.size(); i++ )
{ {
m.command = command; input_mapping_t &m = mapping_list.at(i);
return; if( m.seq == sequence )
} {
} m.command = command;
mapping_list.push_back(input_mapping_t(sequence, command)); return;
}
}
mapping_list.push_back(input_mapping_t(sequence, command));
} }
/** /**
@ -276,29 +276,29 @@ void input_mapping_add( const wchar_t *sequence,
*/ */
static int interrupt_handler() static int interrupt_handler()
{ {
/* /*
Fire any pending events Fire any pending events
*/ */
event_fire( NULL ); event_fire( NULL );
/*
Reap stray processes, including printing exit status messages
*/
if( job_reap( 1 ) )
reader_repaint_needed();
/*
Tell the reader an event occured
*/
if( reader_interrupted() )
{
/*
Return 3, i.e. the character read by a Control-C.
*/
return 3;
}
return R_NULL; /*
Reap stray processes, including printing exit status messages
*/
if( job_reap( 1 ) )
reader_repaint_needed();
/*
Tell the reader an event occured
*/
if( reader_interrupted() )
{
/*
Return 3, i.e. the character read by a Control-C.
*/
return 3;
}
return R_NULL;
} }
void update_fish_term256(void) void update_fish_term256(void)
@ -329,54 +329,54 @@ void update_fish_term256(void)
int input_init() int input_init()
{ {
if( is_init ) if( is_init )
return 1; return 1;
is_init = 1;
input_common_init( &interrupt_handler ); is_init = 1;
if( setupterm( 0, STDOUT_FILENO, 0) == ERR ) input_common_init( &interrupt_handler );
{
debug( 0, _( L"Could not set up terminal" ) ); if( setupterm( 0, STDOUT_FILENO, 0) == ERR )
exit_without_destructors(1); {
} debug( 0, _( L"Could not set up terminal" ) );
exit_without_destructors(1);
}
const env_var_t term = env_get_string( L"TERM" ); const env_var_t term = env_get_string( L"TERM" );
assert(! term.missing()); assert(! term.missing());
output_set_term( term.c_str() ); output_set_term( term.c_str() );
input_terminfo_init(); input_terminfo_init();
update_fish_term256(); update_fish_term256();
/* If we have no keybindings, add a few simple defaults */ /* If we have no keybindings, add a few simple defaults */
if( mapping_list.empty() ) if( mapping_list.empty() )
{ {
input_mapping_add( L"", L"self-insert" ); input_mapping_add( L"", L"self-insert" );
input_mapping_add( L"\n", L"execute" ); input_mapping_add( L"\n", L"execute" );
input_mapping_add( L"\t", L"complete" ); input_mapping_add( L"\t", L"complete" );
input_mapping_add( L"\x3", L"commandline \"\"" ); input_mapping_add( L"\x3", L"commandline \"\"" );
input_mapping_add( L"\x4", L"exit" ); input_mapping_add( L"\x4", L"exit" );
input_mapping_add( L"\x5", L"bind" ); input_mapping_add( L"\x5", L"bind" );
} }
return 1; return 1;
} }
void input_destroy() void input_destroy()
{ {
if( !is_init ) if( !is_init )
return; return;
is_init=0;
is_init=0;
input_common_destroy();
input_common_destroy();
if( del_curterm( cur_term ) == ERR )
{ if( del_curterm( cur_term ) == ERR )
debug( 0, _(L"Error while closing terminfo") ); {
} debug( 0, _(L"Error while closing terminfo") );
}
} }
/** /**
@ -384,50 +384,50 @@ void input_destroy()
*/ */
static wint_t input_exec_binding( const input_mapping_t &m, const wcstring &seq ) static wint_t input_exec_binding( const input_mapping_t &m, const wcstring &seq )
{ {
wchar_t code = input_function_get_code( m.command ); wchar_t code = input_function_get_code( m.command );
if( code != -1 ) if( code != -1 )
{ {
switch( code ) switch( code )
{ {
case R_SELF_INSERT: case R_SELF_INSERT:
{ {
return seq[0]; return seq[0];
} }
default:
{
return code;
}
}
}
else
{
/*
This key sequence is bound to a command, which
is sent to the parser for evaluation.
*/
int last_status = proc_get_last_status();
parser_t::principal_parser().eval( m.command.c_str(), io_chain_t(), TOP );
proc_set_last_status( last_status );
/*
We still need to return something to the caller, R_NULL
tells the reader that no key press needs to be handled,
and no repaint is needed.
Bindings that produce output should emit a R_REPAINT default:
function by calling 'commandline -f repaint' to tell {
fish that a repaint is in order. return code;
*/ }
return R_NULL;
} }
}
else
{
/*
This key sequence is bound to a command, which
is sent to the parser for evaluation.
*/
int last_status = proc_get_last_status();
parser_t::principal_parser().eval( m.command.c_str(), io_chain_t(), TOP );
proc_set_last_status( last_status );
/*
We still need to return something to the caller, R_NULL
tells the reader that no key press needs to be handled,
and no repaint is needed.
Bindings that produce output should emit a R_REPAINT
function by calling 'commandline -f repaint' to tell
fish that a repaint is in order.
*/
return R_NULL;
}
} }
@ -437,18 +437,18 @@ static wint_t input_exec_binding( const input_mapping_t &m, const wcstring &seq
*/ */
static wint_t input_try_mapping( const input_mapping_t &m) static wint_t input_try_mapping( const input_mapping_t &m)
{ {
wint_t c=0; wint_t c=0;
int j; int j;
/* /*
Check if the actual function code of this mapping is on the stack Check if the actual function code of this mapping is on the stack
*/ */
c = input_common_readch( 0 ); c = input_common_readch( 0 );
if( c == input_function_get_code( m.command ) ) if( c == input_function_get_code( m.command ) )
{ {
return input_exec_binding( m, m.seq ); return input_exec_binding( m, m.seq );
} }
input_unreadch( c ); input_unreadch( c );
const wchar_t *str = m.seq.c_str(); const wchar_t *str = m.seq.c_str();
for (j=0; str[j] != L'\0'; j++) for (j=0; str[j] != L'\0'; j++)
@ -458,7 +458,7 @@ static wint_t input_try_mapping( const input_mapping_t &m)
if (str[j] != c) if (str[j] != c)
break; break;
} }
if( str[j] == L'\0' ) if( str[j] == L'\0' )
{ {
/* We matched the entire sequence */ /* We matched the entire sequence */
@ -476,130 +476,130 @@ static wint_t input_try_mapping( const input_mapping_t &m)
input_unreadch( m.seq[k] ); input_unreadch( m.seq[k] );
} }
} }
return 0; return 0;
} }
void input_unreadch( wint_t ch ) void input_unreadch( wint_t ch )
{ {
input_common_unreadch( ch ); input_common_unreadch( ch );
} }
wint_t input_readch() wint_t input_readch()
{ {
size_t i; size_t i;
CHECK_BLOCK( R_NULL ); CHECK_BLOCK( R_NULL );
/* /*
Clear the interrupted flag Clear the interrupted flag
*/ */
reader_interrupted(); reader_interrupted();
/* /*
Search for sequence in mapping tables Search for sequence in mapping tables
*/ */
while( 1 ) while( 1 )
{ {
const input_mapping_t *generic = 0; const input_mapping_t *generic = 0;
for( i=0; i<mapping_list.size(); i++ ) for( i=0; i<mapping_list.size(); i++ )
{ {
const input_mapping_t &m = mapping_list.at(i); const input_mapping_t &m = mapping_list.at(i);
wint_t res = input_try_mapping( m ); wint_t res = input_try_mapping( m );
if( res ) if( res )
return res; return res;
if( m.seq.length() == 0 ) if( m.seq.length() == 0 )
{ {
generic = &m; generic = &m;
} }
} }
/* /*
No matching exact mapping, try to find generic mapping. No matching exact mapping, try to find generic mapping.
*/ */
if( generic ) if( generic )
{ {
wchar_t arr[2]= wchar_t arr[2]=
{ {
0, 0,
0 0
} }
; ;
arr[0] = input_common_readch(0); arr[0] = input_common_readch(0);
return input_exec_binding( *generic, arr ); return input_exec_binding( *generic, arr );
} }
/* /*
No action to take on specified character, ignore it No action to take on specified character, ignore it
and move to next one. and move to next one.
*/ */
wchar_t c = input_common_readch( 0 ); wchar_t c = input_common_readch( 0 );
/* If it's closed, then just return */ /* If it's closed, then just return */
if (c == R_EOF) if (c == R_EOF)
{ {
return WEOF; return WEOF;
} }
} }
} }
void input_mapping_get_names( wcstring_list_t &lst ) void input_mapping_get_names( wcstring_list_t &lst )
{ {
size_t i; size_t i;
for( i=0; i<mapping_list.size(); i++ ) for( i=0; i<mapping_list.size(); i++ )
{ {
const input_mapping_t &m = mapping_list.at(i); const input_mapping_t &m = mapping_list.at(i);
lst.push_back(wcstring(m.seq)); lst.push_back(wcstring(m.seq));
} }
} }
bool input_mapping_erase( const wchar_t *sequence ) bool input_mapping_erase( const wchar_t *sequence )
{ {
ASSERT_IS_MAIN_THREAD(); ASSERT_IS_MAIN_THREAD();
bool result = false; bool result = false;
size_t i, sz = mapping_list.size(); size_t i, sz = mapping_list.size();
for( i=0; i<sz; i++ ) for( i=0; i<sz; i++ )
{ {
const input_mapping_t &m = mapping_list.at(i); const input_mapping_t &m = mapping_list.at(i);
if( sequence == m.seq ) if( sequence == m.seq )
{ {
if( i != (sz-1 ) ) if( i != (sz-1 ) )
{ {
mapping_list[i] = mapping_list[sz-1]; mapping_list[i] = mapping_list[sz-1];
} }
mapping_list.pop_back(); mapping_list.pop_back();
result = true; result = true;
break; break;
} }
} }
return result; return result;
} }
bool input_mapping_get( const wcstring &sequence, wcstring &cmd ) bool input_mapping_get( const wcstring &sequence, wcstring &cmd )
{ {
size_t i, sz = mapping_list.size(); size_t i, sz = mapping_list.size();
for( i=0; i<sz; i++ ) for( i=0; i<sz; i++ )
{ {
const input_mapping_t &m = mapping_list.at(i); const input_mapping_t &m = mapping_list.at(i);
if( sequence == m.seq ) if( sequence == m.seq )
{ {
cmd = m.command; cmd = m.command;
return true; return true;
} }
} }
return false; return false;
} }
/** /**
@ -655,11 +655,11 @@ static void input_terminfo_init()
TERMINFO_ADD(key_f19), TERMINFO_ADD(key_f19),
TERMINFO_ADD(key_f20), TERMINFO_ADD(key_f20),
/* /*
I know of no keyboard with more than 20 function keys, so I know of no keyboard with more than 20 function keys, so
adding the rest here makes very little sense, since it will adding the rest here makes very little sense, since it will
take up a lot of room in any listings (like tab completions), take up a lot of room in any listings (like tab completions),
but with no benefit. but with no benefit.
*/ */
/* /*
TERMINFO_ADD(key_f21), TERMINFO_ADD(key_f21),
TERMINFO_ADD(key_f22), TERMINFO_ADD(key_f22),
@ -773,76 +773,76 @@ static void input_terminfo_init()
const wchar_t *input_terminfo_get_sequence( const wchar_t *name ) const wchar_t *input_terminfo_get_sequence( const wchar_t *name )
{ {
ASSERT_IS_MAIN_THREAD(); ASSERT_IS_MAIN_THREAD();
const char *res = 0; const char *res = 0;
static wcstring buff; static wcstring buff;
int err = ENOENT; int err = ENOENT;
CHECK( name, 0 ); CHECK( name, 0 );
input_init(); input_init();
for( size_t i=0; i<terminfo_mappings.size(); i++ ) for( size_t i=0; i<terminfo_mappings.size(); i++ )
{ {
const terminfo_mapping_t &m = terminfo_mappings.at(i); const terminfo_mapping_t &m = terminfo_mappings.at(i);
if( !wcscmp( name, m.name ) ) if( !wcscmp( name, m.name ) )
{ {
res = m.seq; res = m.seq;
err = EILSEQ; err = EILSEQ;
break; break;
} }
} }
if( !res ) if( !res )
{ {
errno = err; errno = err;
return 0; return 0;
} }
buff = format_string(L"%s", res); buff = format_string(L"%s", res);
return buff.c_str(); return buff.c_str();
} }
bool input_terminfo_get_name( const wcstring &seq, wcstring &name ) bool input_terminfo_get_name( const wcstring &seq, wcstring &name )
{ {
input_init(); input_init();
for( size_t i=0; i<terminfo_mappings.size(); i++ ) for( size_t i=0; i<terminfo_mappings.size(); i++ )
{ {
terminfo_mapping_t &m = terminfo_mappings.at(i); terminfo_mapping_t &m = terminfo_mappings.at(i);
if( !m.seq ) if( !m.seq )
{ {
continue; continue;
} }
const wcstring map_buf = format_string(L"%s", m.seq); const wcstring map_buf = format_string(L"%s", m.seq);
if (map_buf == seq) { if (map_buf == seq) {
name = m.name; name = m.name;
return true; return true;
} }
} }
return false; return false;
} }
wcstring_list_t input_terminfo_get_names( bool skip_null ) wcstring_list_t input_terminfo_get_names( bool skip_null )
{ {
wcstring_list_t result; wcstring_list_t result;
result.reserve(terminfo_mappings.size()); result.reserve(terminfo_mappings.size());
input_init(); input_init();
for( size_t i=0; i<terminfo_mappings.size(); i++ ) for( size_t i=0; i<terminfo_mappings.size(); i++ )
{ {
terminfo_mapping_t &m = terminfo_mappings.at(i); terminfo_mapping_t &m = terminfo_mappings.at(i);
if( skip_null && !m.seq ) if( skip_null && !m.seq )
{ {
continue; continue;
} }
result.push_back(wcstring(m.name)); result.push_back(wcstring(m.name));
} }
return result; return result;
} }
@ -855,13 +855,13 @@ wcstring_list_t input_function_get_names( void )
wchar_t input_function_get_code( const wcstring &name ) wchar_t input_function_get_code( const wcstring &name )
{ {
size_t i; size_t i;
for( i = 0; i<(sizeof( code_arr )/sizeof(wchar_t)) ; i++ ) for( i = 0; i<(sizeof( code_arr )/sizeof(wchar_t)) ; i++ )
{ {
if( name == name_arr[i] ) if( name == name_arr[i] )
{ {
return code_arr[i]; return code_arr[i];
} }
} }
return -1; return -1;
} }

70
input.h
View file

@ -17,40 +17,40 @@ inputrc information for key bindings.
*/ */
enum enum
{ {
R_BEGINNING_OF_LINE = R_NULL+10, /* This give input_common ten slots for lowlevel keycodes */ R_BEGINNING_OF_LINE = R_NULL+10, /* This give input_common ten slots for lowlevel keycodes */
R_END_OF_LINE, R_END_OF_LINE,
R_FORWARD_CHAR, R_FORWARD_CHAR,
R_BACKWARD_CHAR, R_BACKWARD_CHAR,
R_FORWARD_WORD, R_FORWARD_WORD,
R_BACKWARD_WORD, R_BACKWARD_WORD,
R_HISTORY_SEARCH_BACKWARD, R_HISTORY_SEARCH_BACKWARD,
R_HISTORY_SEARCH_FORWARD, R_HISTORY_SEARCH_FORWARD,
R_DELETE_CHAR, R_DELETE_CHAR,
R_BACKWARD_DELETE_CHAR, R_BACKWARD_DELETE_CHAR,
R_KILL_LINE, R_KILL_LINE,
R_YANK, R_YANK,
R_YANK_POP, R_YANK_POP,
R_COMPLETE, R_COMPLETE,
R_BEGINNING_OF_HISTORY, R_BEGINNING_OF_HISTORY,
R_END_OF_HISTORY, R_END_OF_HISTORY,
R_BACKWARD_KILL_LINE, R_BACKWARD_KILL_LINE,
R_KILL_WHOLE_LINE, R_KILL_WHOLE_LINE,
R_KILL_WORD, R_KILL_WORD,
R_BACKWARD_KILL_WORD, R_BACKWARD_KILL_WORD,
R_DUMP_FUNCTIONS, R_DUMP_FUNCTIONS,
R_HISTORY_TOKEN_SEARCH_BACKWARD, R_HISTORY_TOKEN_SEARCH_BACKWARD,
R_HISTORY_TOKEN_SEARCH_FORWARD, R_HISTORY_TOKEN_SEARCH_FORWARD,
R_SELF_INSERT, R_SELF_INSERT,
R_VI_ARG_DIGIT, R_VI_ARG_DIGIT,
R_VI_DELETE_TO, R_VI_DELETE_TO,
R_EXECUTE, R_EXECUTE,
R_BEGINNING_OF_BUFFER, R_BEGINNING_OF_BUFFER,
R_END_OF_BUFFER, R_END_OF_BUFFER,
R_REPAINT, R_REPAINT,
R_UP_LINE, R_UP_LINE,
R_DOWN_LINE, R_DOWN_LINE,
R_SUPPRESS_AUTOSUGGESTION, R_SUPPRESS_AUTOSUGGESTION,
R_ACCEPT_AUTOSUGGESTION R_ACCEPT_AUTOSUGGESTION
} }
; ;
@ -85,7 +85,7 @@ wint_t input_readch();
/** /**
Push a character or a readline function onto the stack of unread Push a character or a readline function onto the stack of unread
characters that input_readch will return before actually reading from fd characters that input_readch will return before actually reading from fd
0. 0.
*/ */
void input_unreadch( wint_t ch ); void input_unreadch( wint_t ch );

View file

@ -1,5 +1,5 @@
/** \file input_common.c /** \file input_common.c
Implementation file for the low level input library Implementation file for the low level input library
*/ */
@ -53,7 +53,7 @@ static void (*poll_handler)();
void input_common_init( int (*ih)() ) void input_common_init( int (*ih)() )
{ {
interrupt_handler = ih; interrupt_handler = ih;
} }
void input_common_set_poll_callback(void (*handler)(void)) void input_common_set_poll_callback(void (*handler)(void))
@ -63,7 +63,7 @@ void input_common_set_poll_callback(void (*handler)(void))
void input_common_destroy() void input_common_destroy()
{ {
} }
/** /**
@ -73,192 +73,192 @@ void input_common_destroy()
static wint_t readb() static wint_t readb()
{ {
/* do_loop must be set on every path through the loop; leaving it uninitialized allows the static analyzer to assist in catching mistakes. */ /* do_loop must be set on every path through the loop; leaving it uninitialized allows the static analyzer to assist in catching mistakes. */
unsigned char arr[1]; unsigned char arr[1];
bool do_loop; bool do_loop;
do do
{ {
/* Invoke any poll handler */ /* Invoke any poll handler */
if (poll_handler) if (poll_handler)
poll_handler(); poll_handler();
fd_set fdset; fd_set fdset;
int fd_max=0; int fd_max=0;
int ioport = iothread_port(); int ioport = iothread_port();
int res; int res;
FD_ZERO( &fdset ); FD_ZERO( &fdset );
FD_SET( 0, &fdset ); FD_SET( 0, &fdset );
if( env_universal_server.fd > 0 ) if( env_universal_server.fd > 0 )
{ {
FD_SET( env_universal_server.fd, &fdset ); FD_SET( env_universal_server.fd, &fdset );
if (fd_max < env_universal_server.fd) fd_max = env_universal_server.fd; if (fd_max < env_universal_server.fd) fd_max = env_universal_server.fd;
} }
if (ioport > 0) { if (ioport > 0) {
FD_SET( ioport, &fdset ); FD_SET( ioport, &fdset );
if (fd_max < ioport) fd_max = ioport; if (fd_max < ioport) fd_max = ioport;
} }
res = select( fd_max + 1, &fdset, 0, 0, 0 ); res = select( fd_max + 1, &fdset, 0, 0, 0 );
if( res==-1 ) if( res==-1 )
{ {
switch( errno ) switch( errno )
{ {
case EINTR: case EINTR:
case EAGAIN: case EAGAIN:
{ {
if( interrupt_handler ) if( interrupt_handler )
{ {
int res = interrupt_handler(); int res = interrupt_handler();
if( res ) if( res )
{ {
return res; return res;
} }
if( lookahead_count ) if( lookahead_count )
{ {
return lookahead_arr[--lookahead_count]; return lookahead_arr[--lookahead_count];
} }
} }
do_loop = true; do_loop = true;
break; break;
} }
default: default:
{ {
/* /*
The terminal has been closed. Save and exit. The terminal has been closed. Save and exit.
*/ */
return R_EOF; return R_EOF;
} }
} }
} }
else else
{ {
/* Assume we loop unless we see a character in stdin */ /* Assume we loop unless we see a character in stdin */
do_loop = true; do_loop = true;
if( env_universal_server.fd > 0 && FD_ISSET( env_universal_server.fd, &fdset ) ) if( env_universal_server.fd > 0 && FD_ISSET( env_universal_server.fd, &fdset ) )
{ {
debug( 3, L"Wake up on universal variable event" ); debug( 3, L"Wake up on universal variable event" );
env_universal_read_all(); env_universal_read_all();
if( lookahead_count ) if( lookahead_count )
{ {
return lookahead_arr[--lookahead_count]; return lookahead_arr[--lookahead_count];
} }
} }
if ( ioport > 0 && FD_ISSET(ioport, &fdset)) if ( ioport > 0 && FD_ISSET(ioport, &fdset))
{ {
iothread_service_completion(); iothread_service_completion();
if( lookahead_count ) if( lookahead_count )
{ {
return lookahead_arr[--lookahead_count]; return lookahead_arr[--lookahead_count];
} }
} }
if( FD_ISSET( STDIN_FILENO, &fdset ) ) if( FD_ISSET( STDIN_FILENO, &fdset ) )
{ {
if( read_blocked( 0, arr, 1 ) != 1 ) if( read_blocked( 0, arr, 1 ) != 1 )
{ {
/* The teminal has been closed. Save and exit. */ /* The teminal has been closed. Save and exit. */
return R_EOF; return R_EOF;
} }
/* We read from stdin, so don't loop */ /* We read from stdin, so don't loop */
do_loop = false; do_loop = false;
} }
} }
} }
while( do_loop ); while( do_loop );
return arr[0]; return arr[0];
} }
wchar_t input_common_readch( int timed ) wchar_t input_common_readch( int timed )
{ {
if( lookahead_count == 0 ) if( lookahead_count == 0 )
{ {
if( timed ) if( timed )
{ {
int count; int count;
fd_set fds; fd_set fds;
struct timeval tm= struct timeval tm=
{ {
0, 0,
1000 * WAIT_ON_ESCAPE 1000 * WAIT_ON_ESCAPE
} }
; ;
FD_ZERO( &fds );
FD_SET( 0, &fds );
count = select(1, &fds, 0, 0, &tm);
switch( count )
{
case 0:
return WEOF;
case -1:
return WEOF;
break;
default:
break;
} FD_ZERO( &fds );
} FD_SET( 0, &fds );
count = select(1, &fds, 0, 0, &tm);
wchar_t res;
static mbstate_t state;
while(1) switch( count )
{ {
wint_t b = readb(); case 0:
char bb; return WEOF;
size_t sz;
if( (b >= R_NULL) && (b < R_NULL + 1000) )
return b;
bb=b; case -1:
return WEOF;
sz = mbrtowc( &res, &bb, 1, &state ); break;
default:
switch( sz ) break;
{
case (size_t)(-1): }
memset (&state, '\0', sizeof (state)); }
debug( 2, L"Illegal input" );
return R_NULL; wchar_t res;
case (size_t)(-2): static mbstate_t state;
break;
case 0: while(1)
return 0; {
default: wint_t b = readb();
char bb;
return res;
} size_t sz;
}
} if( (b >= R_NULL) && (b < R_NULL + 1000) )
else return b;
{
if( !timed ) bb=b;
{
while( (lookahead_count >= 0) && (lookahead_arr[lookahead_count-1] == WEOF) ) sz = mbrtowc( &res, &bb, 1, &state );
lookahead_count--;
if( lookahead_count == 0 ) switch( sz )
return input_common_readch(0); {
} case (size_t)(-1):
memset (&state, '\0', sizeof (state));
return lookahead_arr[--lookahead_count]; debug( 2, L"Illegal input" );
} return R_NULL;
case (size_t)(-2):
break;
case 0:
return 0;
default:
return res;
}
}
}
else
{
if( !timed )
{
while( (lookahead_count >= 0) && (lookahead_arr[lookahead_count-1] == WEOF) )
lookahead_count--;
if( lookahead_count == 0 )
return input_common_readch(0);
}
return lookahead_arr[--lookahead_count];
}
} }
void input_common_unreadch( wint_t ch ) void input_common_unreadch( wint_t ch )
{ {
lookahead_arr[lookahead_count++] = ch; lookahead_arr[lookahead_count++] = ch;
} }

View file

@ -1,5 +1,5 @@
/** \file input_common.h /** \file input_common.h
Header file for the low level input library Header file for the low level input library
*/ */
@ -15,15 +15,15 @@ Header file for the low level input library
enum enum
{ {
/** /**
R_NULL is sometimes returned by the input when a character was R_NULL is sometimes returned by the input when a character was
requested but none could be delivered, or when an exception requested but none could be delivered, or when an exception
happened. happened.
*/ */
R_NULL = INPUT_COMMON_RESERVED, R_NULL = INPUT_COMMON_RESERVED,
R_EOF R_EOF
} }
; ;
/** /**
Init the library Init the library

View file

@ -45,17 +45,17 @@ static pthread_mutex_t intern_lock = PTHREAD_MUTEX_INITIALIZER;
static const wchar_t *intern_with_dup( const wchar_t *in, bool dup ) static const wchar_t *intern_with_dup( const wchar_t *in, bool dup )
{ {
if( !in ) if( !in )
return NULL; return NULL;
// debug( 0, L"intern %ls", in ); // debug( 0, L"intern %ls", in );
scoped_lock lock(intern_lock); scoped_lock lock(intern_lock);
const wchar_t *result; const wchar_t *result;
#if USE_SET #if USE_SET
string_table_t::const_iterator iter = string_table.find(in); string_table_t::const_iterator iter = string_table.find(in);
if (iter != string_table.end()) { if (iter != string_table.end()) {
result = *iter; result = *iter;
} else { } else {
result = dup ? wcsdup(in) : in; result = dup ? wcsdup(in) : in;
string_table.insert(result); string_table.insert(result);
@ -74,11 +74,11 @@ static const wchar_t *intern_with_dup( const wchar_t *in, bool dup )
const wchar_t *intern( const wchar_t *in ) const wchar_t *intern( const wchar_t *in )
{ {
return intern_with_dup(in, true); return intern_with_dup(in, true);
} }
const wchar_t *intern_static( const wchar_t *in ) const wchar_t *intern_static( const wchar_t *in )
{ {
return intern_with_dup(in, false); return intern_with_dup(in, false);
} }

166
io.cpp
View file

@ -1,7 +1,7 @@
/** \file io.c /** \file io.c
Utilities for io redirection. Utilities for io redirection.
*/ */
#include "config.h" #include "config.h"
@ -53,105 +53,105 @@ Utilities for io redirection.
void io_buffer_read( io_data_t *d ) void io_buffer_read( io_data_t *d )
{ {
exec_close(d->param1.pipe_fd[1] ); exec_close(d->param1.pipe_fd[1] );
if( d->io_mode == IO_BUFFER ) if( d->io_mode == IO_BUFFER )
{ {
/* if( fcntl( d->param1.pipe_fd[0], F_SETFL, 0 ) ) /* if( fcntl( d->param1.pipe_fd[0], F_SETFL, 0 ) )
{ {
wperror( L"fcntl" ); wperror( L"fcntl" );
return; return;
} */ } */
debug( 4, L"io_buffer_read: blocking read on fd %d", d->param1.pipe_fd[0] ); debug( 4, L"io_buffer_read: blocking read on fd %d", d->param1.pipe_fd[0] );
while(1) while(1)
{ {
char b[4096]; char b[4096];
long l; long l;
l=read_blocked( d->param1.pipe_fd[0], b, 4096 ); l=read_blocked( d->param1.pipe_fd[0], b, 4096 );
if( l==0 ) if( l==0 )
{ {
break; break;
} }
else if( l<0 ) else if( l<0 )
{ {
/* /*
exec_read_io_buffer is only called on jobs that have exec_read_io_buffer is only called on jobs that have
exited, and will therefore never block. But a broken exited, and will therefore never block. But a broken
pipe seems to cause some flags to reset, causing the pipe seems to cause some flags to reset, causing the
EOF flag to not be set. Therefore, EAGAIN is ignored EOF flag to not be set. Therefore, EAGAIN is ignored
and we exit anyway. and we exit anyway.
*/ */
if( errno != EAGAIN ) if( errno != EAGAIN )
{ {
debug( 1, debug( 1,
_(L"An error occured while reading output from code block on file descriptor %d"), _(L"An error occured while reading output from code block on file descriptor %d"),
d->param1.pipe_fd[0] ); d->param1.pipe_fd[0] );
wperror( L"io_buffer_read" ); wperror( L"io_buffer_read" );
} }
break; break;
} }
else else
{ {
d->out_buffer_append( b, l ); d->out_buffer_append( b, l );
} }
} }
} }
} }
io_data_t *io_buffer_create( bool is_input ) io_data_t *io_buffer_create( bool is_input )
{ {
bool success = true; bool success = true;
io_data_t *buffer_redirect = new io_data_t; io_data_t *buffer_redirect = new io_data_t;
buffer_redirect->out_buffer_create(); buffer_redirect->out_buffer_create();
buffer_redirect->io_mode = IO_BUFFER; buffer_redirect->io_mode = IO_BUFFER;
buffer_redirect->is_input = is_input ? true : false; buffer_redirect->is_input = is_input ? true : false;
buffer_redirect->fd=is_input?0:1; buffer_redirect->fd=is_input?0:1;
if( exec_pipe( buffer_redirect->param1.pipe_fd ) == -1 ) if( exec_pipe( buffer_redirect->param1.pipe_fd ) == -1 )
{ {
debug( 1, PIPE_ERROR ); debug( 1, PIPE_ERROR );
wperror (L"pipe"); wperror (L"pipe");
success = false; success = false;
} }
else if( fcntl( buffer_redirect->param1.pipe_fd[0], else if( fcntl( buffer_redirect->param1.pipe_fd[0],
F_SETFL, F_SETFL,
O_NONBLOCK ) ) O_NONBLOCK ) )
{ {
debug( 1, PIPE_ERROR ); debug( 1, PIPE_ERROR );
wperror( L"fcntl" ); wperror( L"fcntl" );
success = false; success = false;
} }
if (! success) if (! success)
{ {
delete buffer_redirect; delete buffer_redirect;
buffer_redirect = NULL; buffer_redirect = NULL;
} }
return buffer_redirect; return buffer_redirect;
} }
void io_buffer_destroy( io_data_t *io_buffer ) void io_buffer_destroy( io_data_t *io_buffer )
{ {
/** /**
If this is an input buffer, then io_read_buffer will not have If this is an input buffer, then io_read_buffer will not have
been called, and we need to close the output fd as well. been called, and we need to close the output fd as well.
*/ */
if( io_buffer->is_input ) if( io_buffer->is_input )
{ {
exec_close(io_buffer->param1.pipe_fd[1] ); exec_close(io_buffer->param1.pipe_fd[1] );
} }
exec_close( io_buffer->param1.pipe_fd[0] ); exec_close( io_buffer->param1.pipe_fd[0] );
/* /*
Dont free fd for writing. This should already be free'd before Dont free fd for writing. This should already be free'd before
calling exec_read_io_buffer on the buffer calling exec_read_io_buffer on the buffer
*/ */
delete io_buffer; delete io_buffer;
} }
void io_chain_t::remove(const io_data_t *element) void io_chain_t::remove(const io_data_t *element)
@ -216,7 +216,7 @@ void io_print(const io_chain_t &chain)
fprintf(stderr, "Empty chain %p\n", &chain); fprintf(stderr, "Empty chain %p\n", &chain);
return; return;
} }
fprintf(stderr, "Chain %p (%ld items):\n", &chain, (long)chain.size()); fprintf(stderr, "Chain %p (%ld items):\n", &chain, (long)chain.size());
for (size_t i=0; i < chain.size(); i++) { for (size_t i=0; i < chain.size(); i++) {
const io_data_t *io = chain.at(i); const io_data_t *io = chain.at(i);
@ -247,7 +247,7 @@ void io_duplicate_prepend( const io_chain_t &src, io_chain_t &dst )
return dst.duplicate_prepend(src); return dst.duplicate_prepend(src);
} }
void io_chain_destroy(io_chain_t &chain) void io_chain_destroy(io_chain_t &chain)
{ {
chain.destroy(); chain.destroy();
} }

78
io.h
View file

@ -10,7 +10,7 @@ using std::tr1::shared_ptr;
*/ */
enum io_mode enum io_mode
{ {
IO_FILE, IO_PIPE, IO_FD, IO_BUFFER, IO_CLOSE IO_FILE, IO_PIPE, IO_FD, IO_BUFFER, IO_CLOSE
}; };
/** Represents an FD redirection */ /** Represents an FD redirection */
@ -24,35 +24,35 @@ private:
void operator=(const io_data_t &rhs); void operator=(const io_data_t &rhs);
public: public:
/** Type of redirect */ /** Type of redirect */
int io_mode; int io_mode;
/** FD to redirect */ /** FD to redirect */
int fd; int fd;
/** /**
Type-specific parameter for redirection Type-specific parameter for redirection
*/ */
union union
{ {
/** Fds for IO_PIPE and for IO_BUFFER */ /** Fds for IO_PIPE and for IO_BUFFER */
int pipe_fd[2]; int pipe_fd[2];
/** fd to redirect specified fd to, for IO_FD */ /** fd to redirect specified fd to, for IO_FD */
int old_fd; int old_fd;
} param1; } param1;
/** Second type-specific paramter for redirection */
union
{
/** file creation flags to send to open for IO_FILE */
int flags;
/** Whether to close old_fd for IO_FD */
int close_old;
} param2;
/** Second type-specific paramter for redirection */
union
{
/** file creation flags to send to open for IO_FILE */
int flags;
/** Whether to close old_fd for IO_FD */
int close_old;
} param2;
/** Filename IO_FILE. malloc'd. This needs to be used after fork, so don't use wcstring here. */ /** Filename IO_FILE. malloc'd. This needs to be used after fork, so don't use wcstring here. */
const char *filename_cstr; const char *filename_cstr;
/** Convenience to set filename_cstr via wcstring */ /** Convenience to set filename_cstr via wcstring */
void set_filename(const wcstring &str) { void set_filename(const wcstring &str) {
free((void *)filename_cstr); free((void *)filename_cstr);
@ -63,33 +63,33 @@ public:
void out_buffer_create() { void out_buffer_create() {
out_buffer.reset(new std::vector<char>); out_buffer.reset(new std::vector<char>);
} }
/** Function to append to the buffer */ /** Function to append to the buffer */
void out_buffer_append(const char *ptr, size_t count) { void out_buffer_append(const char *ptr, size_t count) {
assert(out_buffer.get() != NULL); assert(out_buffer.get() != NULL);
out_buffer->insert(out_buffer->end(), ptr, ptr + count); out_buffer->insert(out_buffer->end(), ptr, ptr + count);
} }
/** Function to get a pointer to the buffer */ /** Function to get a pointer to the buffer */
char *out_buffer_ptr(void) { char *out_buffer_ptr(void) {
assert(out_buffer.get() != NULL); assert(out_buffer.get() != NULL);
return out_buffer->empty() ? NULL : &out_buffer->at(0); return out_buffer->empty() ? NULL : &out_buffer->at(0);
} }
const char *out_buffer_ptr(void) const { const char *out_buffer_ptr(void) const {
assert(out_buffer.get() != NULL); assert(out_buffer.get() != NULL);
return out_buffer->empty() ? NULL : &out_buffer->at(0); return out_buffer->empty() ? NULL : &out_buffer->at(0);
} }
/** Function to get the size of the buffer */ /** Function to get the size of the buffer */
size_t out_buffer_size(void) const { size_t out_buffer_size(void) const {
assert(out_buffer.get() != NULL); assert(out_buffer.get() != NULL);
return out_buffer->size(); return out_buffer->size();
} }
/** Set to true if this is an input io redirection */ /** Set to true if this is an input io redirection */
bool is_input; bool is_input;
io_data_t() : io_data_t() :
out_buffer(), out_buffer(),
io_mode(0), io_mode(0),
@ -100,7 +100,7 @@ public:
is_input(0) is_input(0)
{ {
} }
io_data_t(const io_data_t &rhs) : io_data_t(const io_data_t &rhs) :
out_buffer(rhs.out_buffer), out_buffer(rhs.out_buffer),
io_mode(rhs.io_mode), io_mode(rhs.io_mode),
@ -111,7 +111,7 @@ public:
is_input(rhs.is_input) is_input(rhs.is_input)
{ {
} }
~io_data_t() { ~io_data_t() {
free((void *)filename_cstr); free((void *)filename_cstr);
} }
@ -121,15 +121,15 @@ class io_chain_t : public std::vector<io_data_t *> {
public: public:
io_chain_t(); io_chain_t();
io_chain_t(io_data_t *); io_chain_t(io_data_t *);
void remove(const io_data_t *element); void remove(const io_data_t *element);
io_chain_t duplicate() const; io_chain_t duplicate() const;
void duplicate_prepend(const io_chain_t &src); void duplicate_prepend(const io_chain_t &src);
void destroy(); void destroy();
const io_data_t *get_io_for_fd(int fd) const; const io_data_t *get_io_for_fd(int fd) const;
io_data_t *get_io_for_fd(int fd); io_data_t *get_io_for_fd(int fd);
}; };
/** /**

View file

@ -27,24 +27,24 @@ static int s_active_thread_count;
typedef unsigned char ThreadIndex_t; typedef unsigned char ThreadIndex_t;
static struct WorkerThread_t { static struct WorkerThread_t {
ThreadIndex_t idx; ThreadIndex_t idx;
pthread_t thread; pthread_t thread;
} threads[IO_MAX_THREADS]; } threads[IO_MAX_THREADS];
struct ThreadedRequest_t { struct ThreadedRequest_t {
int sequenceNumber; int sequenceNumber;
int (*handler)(void *); int (*handler)(void *);
void (*completionCallback)(void *, int); void (*completionCallback)(void *, int);
void *context; void *context;
int handlerResult; int handlerResult;
}; };
static struct WorkerThread_t *next_vacant_thread_slot(void) { static struct WorkerThread_t *next_vacant_thread_slot(void) {
for (ThreadIndex_t i=0; i < IO_MAX_THREADS; i++) { for (ThreadIndex_t i=0; i < IO_MAX_THREADS; i++) {
if (! threads[i].thread) return &threads[i]; if (! threads[i].thread) return &threads[i];
} }
return NULL; return NULL;
} }
static pthread_mutex_t s_request_queue_lock; static pthread_mutex_t s_request_queue_lock;
@ -53,32 +53,32 @@ static int s_last_sequence_number;
static int s_read_pipe, s_write_pipe; static int s_read_pipe, s_write_pipe;
static void iothread_init(void) { static void iothread_init(void) {
static bool inited = false; static bool inited = false;
if (! inited) { if (! inited) {
inited = true; inited = true;
/* Initialize the queue lock */ /* Initialize the queue lock */
VOMIT_ON_FAILURE(pthread_mutex_init(&s_request_queue_lock, NULL)); VOMIT_ON_FAILURE(pthread_mutex_init(&s_request_queue_lock, NULL));
/* Initialize the completion pipes */ /* Initialize the completion pipes */
int pipes[2] = {0, 0}; int pipes[2] = {0, 0};
VOMIT_ON_FAILURE(pipe(pipes)); VOMIT_ON_FAILURE(pipe(pipes));
s_read_pipe = pipes[0]; s_read_pipe = pipes[0];
s_write_pipe = pipes[1]; s_write_pipe = pipes[1];
// 0 means success to VOMIT_ON_FAILURE. Arrange to pass 0 if fcntl returns anything other than -1. // 0 means success to VOMIT_ON_FAILURE. Arrange to pass 0 if fcntl returns anything other than -1.
VOMIT_ON_FAILURE(-1 == fcntl(s_read_pipe, F_SETFD, FD_CLOEXEC)); VOMIT_ON_FAILURE(-1 == fcntl(s_read_pipe, F_SETFD, FD_CLOEXEC));
VOMIT_ON_FAILURE(-1 == fcntl(s_write_pipe, F_SETFD, FD_CLOEXEC)); VOMIT_ON_FAILURE(-1 == fcntl(s_write_pipe, F_SETFD, FD_CLOEXEC));
/* Tell each thread its index */ /* Tell each thread its index */
for (ThreadIndex_t i=0; i < IO_MAX_THREADS; i++) { for (ThreadIndex_t i=0; i < IO_MAX_THREADS; i++) {
threads[i].idx = i; threads[i].idx = i;
} }
} }
} }
static void add_to_queue(struct ThreadedRequest_t *req) { static void add_to_queue(struct ThreadedRequest_t *req) {
ASSERT_IS_LOCKED(s_request_queue_lock); ASSERT_IS_LOCKED(s_request_queue_lock);
s_request_queue.push(req); s_request_queue.push(req);
} }
@ -94,117 +94,117 @@ static ThreadedRequest_t *dequeue_request(void) {
/* The function that does thread work. */ /* The function that does thread work. */
static void *iothread_worker(void *threadPtr) { static void *iothread_worker(void *threadPtr) {
assert(threadPtr != NULL); assert(threadPtr != NULL);
struct WorkerThread_t *thread = (struct WorkerThread_t *)threadPtr; struct WorkerThread_t *thread = (struct WorkerThread_t *)threadPtr;
/* Grab a request off of the queue */
struct ThreadedRequest_t *req = dequeue_request();
/* Run the handler and store the result */ /* Grab a request off of the queue */
if (req) { struct ThreadedRequest_t *req = dequeue_request();
req->handlerResult = req->handler(req->context);
} /* Run the handler and store the result */
if (req) {
/* Write our index to wake up the main thread */ req->handlerResult = req->handler(req->context);
VOMIT_ON_FAILURE(! write_loop(s_write_pipe, (const char *)&thread->idx, sizeof thread->idx)); }
/* We're done */ /* Write our index to wake up the main thread */
return req; VOMIT_ON_FAILURE(! write_loop(s_write_pipe, (const char *)&thread->idx, sizeof thread->idx));
/* We're done */
return req;
} }
/* Spawn another thread if there's work to be done. */ /* Spawn another thread if there's work to be done. */
static void iothread_spawn_if_needed(void) { static void iothread_spawn_if_needed(void) {
ASSERT_IS_LOCKED(s_request_queue_lock); ASSERT_IS_LOCKED(s_request_queue_lock);
if (! s_request_queue.empty() && s_active_thread_count < IO_MAX_THREADS) { if (! s_request_queue.empty() && s_active_thread_count < IO_MAX_THREADS) {
struct WorkerThread_t *thread = next_vacant_thread_slot(); struct WorkerThread_t *thread = next_vacant_thread_slot();
assert(thread != NULL); assert(thread != NULL);
/* The spawned thread inherits our signal mask. We don't want the thread to ever receive signals on the spawned thread, so temporarily block all signals, spawn the thread, and then restore it. */ /* The spawned thread inherits our signal mask. We don't want the thread to ever receive signals on the spawned thread, so temporarily block all signals, spawn the thread, and then restore it. */
sigset_t newSet, savedSet; sigset_t newSet, savedSet;
sigfillset(&newSet); sigfillset(&newSet);
VOMIT_ON_FAILURE(pthread_sigmask(SIG_BLOCK, &newSet, &savedSet)); VOMIT_ON_FAILURE(pthread_sigmask(SIG_BLOCK, &newSet, &savedSet));
/* Spawn a thread. */ /* Spawn a thread. */
int err; int err;
do { do {
err = 0; err = 0;
if (pthread_create(&thread->thread, NULL, iothread_worker, thread)) { if (pthread_create(&thread->thread, NULL, iothread_worker, thread)) {
err = errno; err = errno;
} }
} while (err == EAGAIN); } while (err == EAGAIN);
/* Need better error handling - perhaps try again later. */ /* Need better error handling - perhaps try again later. */
assert(err == 0); assert(err == 0);
/* Note that we are spawned another thread */ /* Note that we are spawned another thread */
s_active_thread_count += 1; s_active_thread_count += 1;
/* Restore our sigmask */ /* Restore our sigmask */
VOMIT_ON_FAILURE(pthread_sigmask(SIG_SETMASK, &savedSet, NULL)); VOMIT_ON_FAILURE(pthread_sigmask(SIG_SETMASK, &savedSet, NULL));
} }
} }
int iothread_perform_base(int (*handler)(void *), void (*completionCallback)(void *, int), void *context) { int iothread_perform_base(int (*handler)(void *), void (*completionCallback)(void *, int), void *context) {
ASSERT_IS_MAIN_THREAD(); ASSERT_IS_MAIN_THREAD();
ASSERT_IS_NOT_FORKED_CHILD(); ASSERT_IS_NOT_FORKED_CHILD();
iothread_init(); iothread_init();
/* Create and initialize a request. */ /* Create and initialize a request. */
struct ThreadedRequest_t *req = new ThreadedRequest_t(); struct ThreadedRequest_t *req = new ThreadedRequest_t();
req->handler = handler; req->handler = handler;
req->completionCallback = completionCallback; req->completionCallback = completionCallback;
req->context = context; req->context = context;
req->sequenceNumber = ++s_last_sequence_number; req->sequenceNumber = ++s_last_sequence_number;
/* Take our lock */ /* Take our lock */
scoped_lock lock(s_request_queue_lock); scoped_lock lock(s_request_queue_lock);
/* Add to the queue */ /* Add to the queue */
add_to_queue(req); add_to_queue(req);
/* Spawn a thread if necessary */ /* Spawn a thread if necessary */
iothread_spawn_if_needed(); iothread_spawn_if_needed();
return 0; return 0;
} }
int iothread_port(void) { int iothread_port(void) {
iothread_init(); iothread_init();
return s_read_pipe; return s_read_pipe;
} }
void iothread_service_completion(void) { void iothread_service_completion(void) {
ASSERT_IS_MAIN_THREAD(); ASSERT_IS_MAIN_THREAD();
ThreadIndex_t threadIdx = (ThreadIndex_t)-1; ThreadIndex_t threadIdx = (ThreadIndex_t)-1;
VOMIT_ON_FAILURE(1 != read_loop(iothread_port(), &threadIdx, sizeof threadIdx)); VOMIT_ON_FAILURE(1 != read_loop(iothread_port(), &threadIdx, sizeof threadIdx));
assert(threadIdx < IO_MAX_THREADS); assert(threadIdx < IO_MAX_THREADS);
struct WorkerThread_t *thread = &threads[threadIdx]; struct WorkerThread_t *thread = &threads[threadIdx];
assert(thread->thread != 0); assert(thread->thread != 0);
struct ThreadedRequest_t *req = NULL; struct ThreadedRequest_t *req = NULL;
VOMIT_ON_FAILURE(pthread_join(thread->thread, (void **)&req)); VOMIT_ON_FAILURE(pthread_join(thread->thread, (void **)&req));
/* Free up this thread */ /* Free up this thread */
thread->thread = 0; thread->thread = 0;
assert(s_active_thread_count > 0); assert(s_active_thread_count > 0);
s_active_thread_count -= 1; s_active_thread_count -= 1;
/* Handle the request */ /* Handle the request */
if (req) { if (req) {
if (req->completionCallback) if (req->completionCallback)
req->completionCallback(req->context, req->handlerResult); req->completionCallback(req->context, req->handlerResult);
delete req; delete req;
} }
/* Maybe spawn another thread, if there's more work to be done. */ /* Maybe spawn another thread, if there's more work to be done. */
VOMIT_ON_FAILURE(pthread_mutex_lock(&s_request_queue_lock)); VOMIT_ON_FAILURE(pthread_mutex_lock(&s_request_queue_lock));
iothread_spawn_if_needed(); iothread_spawn_if_needed();
VOMIT_ON_FAILURE(pthread_mutex_unlock(&s_request_queue_lock)); VOMIT_ON_FAILURE(pthread_mutex_unlock(&s_request_queue_lock));
} }
void iothread_drain_all(void) { void iothread_drain_all(void) {
ASSERT_IS_MAIN_THREAD(); ASSERT_IS_MAIN_THREAD();
ASSERT_IS_NOT_FORKED_CHILD(); ASSERT_IS_NOT_FORKED_CHILD();
if (s_active_thread_count == 0) if (s_active_thread_count == 0)
return; return;
#define TIME_DRAIN 0 #define TIME_DRAIN 0

View file

@ -7,7 +7,7 @@
/** /**
Runs a command on a thread. Runs a command on a thread.
\param handler The function to execute on a background thread. Accepts an arbitrary context pointer, and returns an int, which is passed to the completionCallback. \param handler The function to execute on a background thread. Accepts an arbitrary context pointer, and returns an int, which is passed to the completionCallback.
\param completionCallback The function to execute on the main thread once the background thread is complete. Accepts an int (the return value of handler) and the context. \param completionCallback The function to execute on the main thread once the background thread is complete. Accepts an int (the return value of handler) and the context.
\param context A arbitary context pointer to pass to the handler and completion callback. \param context A arbitary context pointer to pass to the handler and completion callback.
@ -15,9 +15,9 @@
*/ */
int iothread_perform_base(int (*handler)(void *), void (*completionCallback)(void *, int), void *context); int iothread_perform_base(int (*handler)(void *), void (*completionCallback)(void *, int), void *context);
/** /**
Gets the fd on which to listen for completion callbacks. Gets the fd on which to listen for completion callbacks.
\return A file descriptor on which to listen for completion callbacks. \return A file descriptor on which to listen for completion callbacks.
*/ */
int iothread_port(void); int iothread_port(void);

View file

@ -1,9 +1,9 @@
/* /*
A small utility to print the resulting key codes from pressing a A small utility to print the resulting key codes from pressing a
key. Servers the same function as hitting ^V in bash, but I prefer key. Servers the same function as hitting ^V in bash, but I prefer
the way key_reader works. the way key_reader works.
Type ^C to exit the program. Type ^C to exit the program.
*/ */
#include "config.h" #include "config.h"
@ -22,76 +22,76 @@
int writestr( char *str ) int writestr( char *str )
{ {
write( 1, str, strlen(str) ); write( 1, str, strlen(str) );
return 0; return 0;
} }
int main( int argc, char **argv) int main( int argc, char **argv)
{ {
set_main_thread(); set_main_thread();
setup_fork_guards(); setup_fork_guards();
setlocale( LC_ALL, "" ); setlocale( LC_ALL, "" );
if( argc == 2 )
{
static char term_buffer[2048];
char *termtype = getenv ("TERM");
char *tbuff = new char[9999];
char *res;
tgetent( term_buffer, termtype );
res = tgetstr( argv[1], &tbuff );
if( res != 0 )
{
while( *res != 0 )
{
printf("%d ", *res );
res++; if( argc == 2 )
} {
printf( "\n" ); static char term_buffer[2048];
} char *termtype = getenv ("TERM");
else char *tbuff = new char[9999];
{ char *res;
printf("Undefined sequence\n");
}
}
else
{
char scratch[1024];
unsigned int c;
struct termios modes, /* so we can change the modes */ tgetent( term_buffer, termtype );
savemodes; /* so we can reset the modes when we're done */ res = tgetstr( argv[1], &tbuff );
if( res != 0 )
{
while( *res != 0 )
{
printf("%d ", *res );
input_common_init(0);
tcgetattr(0,&modes); /* get the current terminal modes */ res++;
savemodes = modes; /* save a copy so we can reset them */ }
printf( "\n" );
modes.c_lflag &= ~ICANON; /* turn off canonical mode */ }
modes.c_lflag &= ~ECHO; /* turn off echo mode */ else
modes.c_cc[VMIN]=1; {
modes.c_cc[VTIME]=0; printf("Undefined sequence\n");
tcsetattr(0,TCSANOW,&modes); /* set the new modes */ }
while(1) }
{ else
if( (c=input_common_readch(0)) == EOF ) {
break; char scratch[1024];
if( (c > 31) && (c != 127) ) unsigned int c;
sprintf( scratch, "dec: %d hex: %x char: %c\n", c, c, c );
else
sprintf( scratch, "dec: %d hex: %x\n", c, c );
writestr( scratch );
}
/* reset the terminal to the saved mode */
tcsetattr(0,TCSANOW,&savemodes);
input_common_destroy(); struct termios modes, /* so we can change the modes */
} savemodes; /* so we can reset the modes when we're done */
return 0; input_common_init(0);
tcgetattr(0,&modes); /* get the current terminal modes */
savemodes = modes; /* save a copy so we can reset them */
modes.c_lflag &= ~ICANON; /* turn off canonical mode */
modes.c_lflag &= ~ECHO; /* turn off echo mode */
modes.c_cc[VMIN]=1;
modes.c_cc[VTIME]=0;
tcsetattr(0,TCSANOW,&modes); /* set the new modes */
while(1)
{
if( (c=input_common_readch(0)) == EOF )
break;
if( (c > 31) && (c != 127) )
sprintf( scratch, "dec: %d hex: %x char: %c\n", c, c, c );
else
sprintf( scratch, "dec: %d hex: %x\n", c, c );
writestr( scratch );
}
/* reset the terminal to the saved mode */
tcsetattr(0,TCSANOW,&savemodes);
input_common_destroy();
}
return 0;
} }

168
kill.cpp
View file

@ -1,9 +1,9 @@
/** \file kill.c /** \file kill.c
The killring. The killring.
Works like the killring in emacs and readline. The killring is cut Works like the killring in emacs and readline. The killring is cut
and paste with a memory of previous cuts. It supports integration and paste with a memory of previous cuts. It supports integration
with the X clipboard. with the X clipboard.
*/ */
#include "config.h" #include "config.h"
@ -57,12 +57,12 @@ static wchar_t *cut_buffer=0;
*/ */
static int has_xsel() static int has_xsel()
{ {
static int res=-1; static int res=-1;
if (res < 0) { if (res < 0) {
res = !! path_get_path(L"xsel", NULL); res = !! path_get_path(L"xsel", NULL);
} }
return res; return res;
} }
void kill_add( const wcstring &str ) void kill_add( const wcstring &str )
@ -70,57 +70,57 @@ void kill_add( const wcstring &str )
ASSERT_IS_MAIN_THREAD(); ASSERT_IS_MAIN_THREAD();
if (str.empty()) if (str.empty())
return; return;
wcstring cmd; wcstring cmd;
wchar_t *escaped_str = NULL; wchar_t *escaped_str = NULL;
kill_list.push_front(str); kill_list.push_front(str);
/* /*
Check to see if user has set the FISH_CLIPBOARD_CMD variable, Check to see if user has set the FISH_CLIPBOARD_CMD variable,
and, if so, use it instead of checking the display, etc. and, if so, use it instead of checking the display, etc.
I couldn't think of a safe way to allow overide of the echo I couldn't think of a safe way to allow overide of the echo
command too, so, the command used must accept the input via stdin. command too, so, the command used must accept the input via stdin.
*/ */
const env_var_t clipboard_wstr = env_get_string(L"FISH_CLIPBOARD_CMD"); const env_var_t clipboard_wstr = env_get_string(L"FISH_CLIPBOARD_CMD");
if( !clipboard_wstr.missing() ) if( !clipboard_wstr.missing() )
{ {
escaped_str = escape( str.c_str(), 1 ); escaped_str = escape( str.c_str(), 1 );
cmd.assign(L"echo -n "); cmd.assign(L"echo -n ");
cmd.append(escaped_str); cmd.append(escaped_str);
cmd.append(clipboard_wstr); cmd.append(clipboard_wstr);
} }
else else
{ {
/* This is for sending the kill to the X copy-and-paste buffer */ /* This is for sending the kill to the X copy-and-paste buffer */
if( !has_xsel() ) { if( !has_xsel() ) {
return; return;
} }
const env_var_t disp_wstr = env_get_string( L"DISPLAY" ); const env_var_t disp_wstr = env_get_string( L"DISPLAY" );
if( !disp_wstr.missing() ) if( !disp_wstr.missing() )
{ {
escaped_str = escape( str.c_str(), 1 ); escaped_str = escape( str.c_str(), 1 );
cmd.assign(L"echo "); cmd.assign(L"echo ");
cmd.append(escaped_str); cmd.append(escaped_str);
cmd.append(L"|xsel -b" ); cmd.append(L"|xsel -b" );
} }
} }
if (! cmd.empty()) if (! cmd.empty())
{ {
if( exec_subshell(cmd) == -1 ) if( exec_subshell(cmd) == -1 )
{ {
/* /*
Do nothing on failiure Do nothing on failiure
*/ */
} }
free( cut_buffer ); free( cut_buffer );
cut_buffer = escaped_str; cut_buffer = escaped_str;
} }
} }
/** /**
@ -133,13 +133,13 @@ static void kill_remove( const wcstring &s )
if (iter != kill_list.end()) if (iter != kill_list.end())
kill_list.erase(iter); kill_list.erase(iter);
} }
void kill_replace( const wcstring &old, const wcstring &newv ) void kill_replace( const wcstring &old, const wcstring &newv )
{ {
kill_remove( old ); kill_remove( old );
kill_add( newv ); kill_add( newv );
} }
const wchar_t *kill_yank_rotate() const wchar_t *kill_yank_rotate()
@ -159,50 +159,50 @@ const wchar_t *kill_yank_rotate()
clipboard contents to the fish killring. clipboard contents to the fish killring.
*/ */
static void kill_check_x_buffer() static void kill_check_x_buffer()
{ {
if( !has_xsel() ) if( !has_xsel() )
return; return;
const env_var_t disp = env_get_string(L"DISPLAY"); const env_var_t disp = env_get_string(L"DISPLAY");
if( ! disp.missing()) if( ! disp.missing())
{ {
size_t i; size_t i;
wcstring cmd = L"xsel -t 500 -b"; wcstring cmd = L"xsel -t 500 -b";
wcstring new_cut_buffer=L""; wcstring new_cut_buffer=L"";
wcstring_list_t list; wcstring_list_t list;
if( exec_subshell( cmd, list ) != -1 ) if( exec_subshell( cmd, list ) != -1 )
{ {
for( i=0; i<list.size(); i++ ) for( i=0; i<list.size(); i++ )
{ {
wcstring next_line = escape_string( list.at(i), 0 ); wcstring next_line = escape_string( list.at(i), 0 );
if (i > 0) new_cut_buffer += L"\\n"; if (i > 0) new_cut_buffer += L"\\n";
new_cut_buffer += next_line; new_cut_buffer += next_line;
} }
if( new_cut_buffer.size() > 0 ) if( new_cut_buffer.size() > 0 )
{ {
/* /*
The buffer is inserted with backslash escapes, The buffer is inserted with backslash escapes,
since we don't really like tabs, newlines, since we don't really like tabs, newlines,
etc. anyway. etc. anyway.
*/ */
if (cut_buffer == NULL || cut_buffer != new_cut_buffer) if (cut_buffer == NULL || cut_buffer != new_cut_buffer)
{ {
free(cut_buffer); free(cut_buffer);
cut_buffer = wcsdup(new_cut_buffer.c_str()); cut_buffer = wcsdup(new_cut_buffer.c_str());
kill_list.push_front( new_cut_buffer ); kill_list.push_front( new_cut_buffer );
} }
} }
} }
} }
} }
const wchar_t *kill_yank() const wchar_t *kill_yank()
{ {
kill_check_x_buffer(); kill_check_x_buffer();
if (kill_list.empty()) { if (kill_list.empty()) {
return L""; return L"";
} else { } else {
@ -220,7 +220,7 @@ void kill_init()
void kill_destroy() void kill_destroy()
{ {
if( cut_buffer ) if( cut_buffer )
free( cut_buffer ); free( cut_buffer );
} }

4
kill.h
View file

@ -1,7 +1,7 @@
/** \file kill.h /** \file kill.h
Prototypes for the killring. Prototypes for the killring.
Works like the killring in emacs and readline. The killring is cut and paste whith a memory of previous cuts. Works like the killring in emacs and readline. The killring is cut and paste whith a memory of previous cuts.
*/ */
#ifndef FISH_KILL_H #ifndef FISH_KILL_H

72
lru.h
View file

@ -20,17 +20,17 @@ struct dereference_less_t {
class lru_node_t { class lru_node_t {
template<class T> friend class lru_cache_t; template<class T> friend class lru_cache_t;
/** Our linked list pointer */ /** Our linked list pointer */
lru_node_t *prev, *next; lru_node_t *prev, *next;
public: public:
/** The key used to look up in the cache */ /** The key used to look up in the cache */
const wcstring key; const wcstring key;
/** Constructor */ /** Constructor */
lru_node_t(const wcstring &pkey) : prev(NULL), next(NULL), key(pkey) { } lru_node_t(const wcstring &pkey) : prev(NULL), next(NULL), key(pkey) { }
/** operator< for std::set */ /** operator< for std::set */
bool operator<(const lru_node_t &other) const { return key < other.key; } bool operator<(const lru_node_t &other) const { return key < other.key; }
}; };
@ -41,29 +41,29 @@ class lru_cache_t {
/** Max node count */ /** Max node count */
const size_t max_node_count; const size_t max_node_count;
/** Count of nodes */ /** Count of nodes */
size_t node_count; size_t node_count;
/** The set of nodes */ /** The set of nodes */
typedef std::set<lru_node_t *, dereference_less_t> node_set_t; typedef std::set<lru_node_t *, dereference_less_t> node_set_t;
node_set_t node_set; node_set_t node_set;
void promote_node(node_type_t *node) { void promote_node(node_type_t *node) {
/* We should never promote the mouth */ /* We should never promote the mouth */
assert(node != &mouth); assert(node != &mouth);
/* First unhook us */ /* First unhook us */
node->prev->next = node->next; node->prev->next = node->next;
node->next->prev = node->prev; node->next->prev = node->prev;
/* Put us after the mouth */ /* Put us after the mouth */
node->next = mouth.next; node->next = mouth.next;
node->next->prev = node; node->next->prev = node;
node->prev = &mouth; node->prev = &mouth;
mouth.next = node; mouth.next = node;
} }
void evict_node(node_type_t *condemned_node) { void evict_node(node_type_t *condemned_node) {
/* We should never evict the mouth */ /* We should never evict the mouth */
assert(condemned_node != NULL && condemned_node != &mouth); assert(condemned_node != NULL && condemned_node != &mouth);
@ -79,42 +79,42 @@ class lru_cache_t {
/* Tell ourselves */ /* Tell ourselves */
this->node_was_evicted(condemned_node); this->node_was_evicted(condemned_node);
} }
void evict_last_node(void) { void evict_last_node(void) {
/* Simple */ /* Simple */
evict_node((node_type_t *)mouth.prev); evict_node((node_type_t *)mouth.prev);
} }
protected: protected:
/** Head of the linked list */ /** Head of the linked list */
lru_node_t mouth; lru_node_t mouth;
/** Overridable callback for when a node is evicted */ /** Overridable callback for when a node is evicted */
virtual void node_was_evicted(node_type_t *node) { } virtual void node_was_evicted(node_type_t *node) { }
public: public:
/** Constructor */ /** Constructor */
lru_cache_t(size_t max_size = 1024 ) : max_node_count(max_size), node_count(0), mouth(wcstring()) { lru_cache_t(size_t max_size = 1024 ) : max_node_count(max_size), node_count(0), mouth(wcstring()) {
/* Hook up the mouth to itself: a one node circularly linked list! */ /* Hook up the mouth to itself: a one node circularly linked list! */
mouth.prev = mouth.next = &mouth; mouth.prev = mouth.next = &mouth;
} }
/** Note that we do not evict nodes in our destructor (even though they typically need to be deleted by their creator). */ /** Note that we do not evict nodes in our destructor (even though they typically need to be deleted by their creator). */
virtual ~lru_cache_t() { } virtual ~lru_cache_t() { }
/** Returns the node for a given key, or NULL */ /** Returns the node for a given key, or NULL */
node_type_t *get_node(const wcstring &key) { node_type_t *get_node(const wcstring &key) {
node_type_t *result = NULL; node_type_t *result = NULL;
/* Construct a fake node as our key */ /* Construct a fake node as our key */
lru_node_t node_key(key); lru_node_t node_key(key);
/* Look for it in the set */ /* Look for it in the set */
node_set_t::iterator iter = node_set.find(&node_key); node_set_t::iterator iter = node_set.find(&node_key);
/* If we found a node, promote and return it */ /* If we found a node, promote and return it */
if (iter != node_set.end()) { if (iter != node_set.end()) {
result = static_cast<node_type_t*>(*iter); result = static_cast<node_type_t*>(*iter);
@ -122,73 +122,73 @@ class lru_cache_t {
} }
return result; return result;
} }
/** Evicts the node for a given key, returning true if a node was evicted. */ /** Evicts the node for a given key, returning true if a node was evicted. */
bool evict_node(const wcstring &key) { bool evict_node(const wcstring &key) {
/* Construct a fake node as our key */ /* Construct a fake node as our key */
lru_node_t node_key(key); lru_node_t node_key(key);
/* Look for it in the set */ /* Look for it in the set */
node_set_t::iterator iter = node_set.find(&node_key); node_set_t::iterator iter = node_set.find(&node_key);
if (iter == node_set.end()) if (iter == node_set.end())
return false; return false;
/* Evict the given node */ /* Evict the given node */
evict_node(static_cast<node_type_t*>(*iter)); evict_node(static_cast<node_type_t*>(*iter));
return true; return true;
} }
/** Adds a node under the given key. Returns true if the node was added, false if the node was not because a node with that key is already in the set. */ /** Adds a node under the given key. Returns true if the node was added, false if the node was not because a node with that key is already in the set. */
bool add_node(node_type_t *node) { bool add_node(node_type_t *node) {
/* Add our node without eviction */ /* Add our node without eviction */
if (! this->add_node_without_eviction(node)) if (! this->add_node_without_eviction(node))
return false; return false;
/* Evict */ /* Evict */
while (node_count > max_node_count) while (node_count > max_node_count)
evict_last_node(); evict_last_node();
/* Success */ /* Success */
return true; return true;
} }
/** Adds a node under the given key without triggering eviction. Returns true if the node was added, false if the node was not because a node with that key is already in the set. */ /** Adds a node under the given key without triggering eviction. Returns true if the node was added, false if the node was not because a node with that key is already in the set. */
bool add_node_without_eviction(node_type_t *node) { bool add_node_without_eviction(node_type_t *node) {
assert(node != NULL && node != &mouth); assert(node != NULL && node != &mouth);
/* Try inserting; return false if it was already in the set */ /* Try inserting; return false if it was already in the set */
if (! node_set.insert(node).second) if (! node_set.insert(node).second)
return false; return false;
/* Add the node after the mouth */ /* Add the node after the mouth */
node->next = mouth.next; node->next = mouth.next;
node->next->prev = node; node->next->prev = node;
node->prev = &mouth; node->prev = &mouth;
mouth.next = node; mouth.next = node;
/* Update the count */ /* Update the count */
node_count++; node_count++;
/* Evict */ /* Evict */
while (node_count > max_node_count) while (node_count > max_node_count)
evict_last_node(); evict_last_node();
/* Success */ /* Success */
return true; return true;
} }
/** Counts nodes */ /** Counts nodes */
size_t size(void) { size_t size(void) {
return node_count; return node_count;
} }
/** Evicts all nodes */ /** Evicts all nodes */
void evict_all_nodes(void) { void evict_all_nodes(void) {
while (node_count > 0) { while (node_count > 0) {
evict_last_node(); evict_last_node();
} }
} }
/** Iterator for walking nodes, from least recently used to most */ /** Iterator for walking nodes, from least recently used to most */
class iterator { class iterator {
lru_node_t *node; lru_node_t *node;
@ -200,7 +200,7 @@ class lru_cache_t {
bool operator!=(const iterator &other) { return !(*this == other); } bool operator!=(const iterator &other) { return !(*this == other); }
node_type_t *operator*() { return static_cast<node_type_t *>(node); } node_type_t *operator*() { return static_cast<node_type_t *>(node); }
}; };
iterator begin() { return iterator(mouth.prev); } iterator begin() { return iterator(mouth.prev); }
iterator end() { return iterator(&mouth); } iterator end() { return iterator(&mouth); }
}; };

2108
mimedb.cpp

File diff suppressed because it is too large Load diff

View file

@ -65,21 +65,21 @@
static int writeb_internal( char c ); static int writeb_internal( char c );
/** /**
Names of different colors. Names of different colors.
*/ */
static const wchar_t *col[]= static const wchar_t *col[]=
{ {
L"black", L"black",
L"red", L"red",
L"green", L"green",
L"brown", L"brown",
L"yellow", L"yellow",
L"blue", L"blue",
L"magenta", L"magenta",
L"purple", L"purple",
L"cyan", L"cyan",
L"white" L"white"
L"normal" L"normal"
} }
; ;
@ -91,17 +91,17 @@ static const wchar_t *col[]=
*/ */
static const int col_idx[]= static const int col_idx[]=
{ {
0, 0,
1, 1,
2, 2,
3, 3,
3, 3,
4, 4,
5, 5,
5, 5,
6, 6,
7, 7,
FISH_COLOR_NORMAL, FISH_COLOR_NORMAL,
}; };
/** /**
@ -121,13 +121,13 @@ static bool support_term256 = false;
void output_set_writer( int (*writer)(char) ) void output_set_writer( int (*writer)(char) )
{ {
CHECK( writer, ); CHECK( writer, );
out = writer; out = writer;
} }
int (*output_get_writer())(char) int (*output_get_writer())(char)
{ {
return out; return out;
} }
static bool term256_support_is_native(void) { static bool term256_support_is_native(void) {
@ -166,14 +166,14 @@ static bool write_color(char *todo, unsigned char idx, bool is_fg) {
strcat(buff, is_fg ? "38;5;" : "48;5;"); strcat(buff, is_fg ? "38;5;" : "48;5;");
strcat(buff, stridx); strcat(buff, stridx);
strcat(buff, "m"); strcat(buff, "m");
int (*writer)(char) = output_get_writer(); int (*writer)(char) = output_get_writer();
if (writer) { if (writer) {
for (size_t i=0; buff[i]; i++) { for (size_t i=0; buff[i]; i++) {
writer(buff[i]); writer(buff[i]);
} }
} }
result = true; result = true;
} }
return result; return result;
@ -202,188 +202,188 @@ static bool write_background_color(unsigned char idx) {
void set_color(rgb_color_t c, rgb_color_t c2) void set_color(rgb_color_t c, rgb_color_t c2)
{ {
#if 0 #if 0
wcstring tmp = c.description(); wcstring tmp = c.description();
wcstring tmp2 = c2.description(); wcstring tmp2 = c2.description();
printf("set_color %ls : %ls\n", tmp.c_str(), tmp2.c_str()); printf("set_color %ls : %ls\n", tmp.c_str(), tmp2.c_str());
#endif #endif
ASSERT_IS_MAIN_THREAD(); ASSERT_IS_MAIN_THREAD();
const rgb_color_t normal = rgb_color_t::normal(); const rgb_color_t normal = rgb_color_t::normal();
static rgb_color_t last_color = rgb_color_t::normal(); static rgb_color_t last_color = rgb_color_t::normal();
static rgb_color_t last_color2 = rgb_color_t::normal(); static rgb_color_t last_color2 = rgb_color_t::normal();
static int was_bold=0; static int was_bold=0;
static int was_underline=0; static int was_underline=0;
int bg_set=0, last_bg_set=0; int bg_set=0, last_bg_set=0;
int is_bold = 0; int is_bold = 0;
int is_underline = 0; int is_underline = 0;
/* /*
Test if we have at least basic support for setting fonts, colors Test if we have at least basic support for setting fonts, colors
and related bits - otherwise just give up... and related bits - otherwise just give up...
*/ */
if( !exit_attribute_mode ) if( !exit_attribute_mode )
{ {
return; return;
} }
is_bold |= c.is_bold(); is_bold |= c.is_bold();
is_bold |= c2.is_bold(); is_bold |= c2.is_bold();
is_underline |= c.is_underline(); is_underline |= c.is_underline();
is_underline |= c2.is_underline(); is_underline |= c2.is_underline();
if( c.is_reset() || c2.is_reset()) if( c.is_reset() || c2.is_reset())
{ {
c = c2 = normal; c = c2 = normal;
was_bold=0; was_bold=0;
was_underline=0; was_underline=0;
/* /*
If we exit attibute mode, we must first set a color, or If we exit attibute mode, we must first set a color, or
previously coloured text might lose it's previously coloured text might lose it's
color. Terminals are weird... color. Terminals are weird...
*/ */
write_foreground_color(0); write_foreground_color(0);
writembs( exit_attribute_mode ); writembs( exit_attribute_mode );
return; return;
} }
if( was_bold && !is_bold ) if( was_bold && !is_bold )
{ {
/* /*
Only way to exit bold mode is a reset of all attributes. Only way to exit bold mode is a reset of all attributes.
*/ */
writembs( exit_attribute_mode ); writembs( exit_attribute_mode );
last_color = normal; last_color = normal;
last_color2 = normal; last_color2 = normal;
was_bold=0; was_bold=0;
was_underline=0; was_underline=0;
} }
if( ! last_color2.is_normal() && if( ! last_color2.is_normal() &&
! last_color2.is_reset() && ! last_color2.is_reset() &&
! last_color2.is_ignore() ) ! last_color2.is_ignore() )
{ {
/* /*
Background was set Background was set
*/ */
last_bg_set=1; last_bg_set=1;
} }
if( ! c2.is_normal() && if( ! c2.is_normal() &&
! c2.is_ignore()) ! c2.is_ignore())
{ {
/* /*
Background is set Background is set
*/ */
bg_set=1; bg_set=1;
if ( c==c2 ) if ( c==c2 )
c = (c2==rgb_color_t::white())?rgb_color_t::black():rgb_color_t::white(); c = (c2==rgb_color_t::white())?rgb_color_t::black():rgb_color_t::white();
} }
if( (enter_bold_mode != 0) && (strlen(enter_bold_mode) > 0)) if( (enter_bold_mode != 0) && (strlen(enter_bold_mode) > 0))
{ {
if(bg_set && !last_bg_set) if(bg_set && !last_bg_set)
{ {
/* /*
Background color changed and is set, so we enter bold Background color changed and is set, so we enter bold
mode to make reading easier. This means bold mode is mode to make reading easier. This means bold mode is
_always_ on when the background color is set. _always_ on when the background color is set.
*/ */
writembs( enter_bold_mode ); writembs( enter_bold_mode );
} }
if(!bg_set && last_bg_set) if(!bg_set && last_bg_set)
{ {
/* /*
Background color changed and is no longer set, so we Background color changed and is no longer set, so we
exit bold mode exit bold mode
*/ */
writembs( exit_attribute_mode ); writembs( exit_attribute_mode );
was_bold=0; was_bold=0;
was_underline=0; was_underline=0;
/* /*
We don't know if exit_attribute_mode resets colors, so We don't know if exit_attribute_mode resets colors, so
we set it to something known. we set it to something known.
*/ */
if( write_foreground_color(0)) if( write_foreground_color(0))
{ {
last_color=rgb_color_t::black(); last_color=rgb_color_t::black();
} }
} }
} }
if( last_color != c ) if( last_color != c )
{ {
if( c.is_normal() ) if( c.is_normal() )
{ {
write_foreground_color(0); write_foreground_color(0);
writembs( exit_attribute_mode ); writembs( exit_attribute_mode );
last_color2 = rgb_color_t::normal(); last_color2 = rgb_color_t::normal();
was_bold=0; was_bold=0;
was_underline=0; was_underline=0;
} }
else if( ! c.is_special() ) else if( ! c.is_special() )
{ {
write_foreground_color(index_for_color(c)); write_foreground_color(index_for_color(c));
} }
} }
last_color = c; last_color = c;
if( last_color2 != c2 ) if( last_color2 != c2 )
{ {
if( c2.is_normal() ) if( c2.is_normal() )
{ {
write_background_color(0); write_background_color(0);
writembs( exit_attribute_mode ); writembs( exit_attribute_mode );
if( ! last_color.is_normal()) if( ! last_color.is_normal())
{ {
write_foreground_color(index_for_color(last_color)); write_foreground_color(index_for_color(last_color));
} }
was_bold=0; was_bold=0;
was_underline=0; was_underline=0;
last_color2 = c2; last_color2 = c2;
} }
else if ( ! c2.is_special() ) else if ( ! c2.is_special() )
{ {
write_background_color(index_for_color(c2)); write_background_color(index_for_color(c2));
last_color2 = c2; last_color2 = c2;
} }
} }
/* /*
Lastly, we set bold mode and underline mode correctly Lastly, we set bold mode and underline mode correctly
*/ */
if( (enter_bold_mode != 0) && (strlen(enter_bold_mode) > 0) && !bg_set ) if( (enter_bold_mode != 0) && (strlen(enter_bold_mode) > 0) && !bg_set )
{ {
if( is_bold && !was_bold ) if( is_bold && !was_bold )
{ {
if( enter_bold_mode ) if( enter_bold_mode )
{ {
writembs( tparm( enter_bold_mode ) ); writembs( tparm( enter_bold_mode ) );
} }
} }
was_bold = is_bold; was_bold = is_bold;
} }
if( was_underline && !is_underline ) if( was_underline && !is_underline )
{ {
writembs( exit_underline_mode ); writembs( exit_underline_mode );
} }
if( !was_underline && is_underline ) if( !was_underline && is_underline )
{ {
writembs( enter_underline_mode ); writembs( enter_underline_mode );
} }
was_underline = is_underline; was_underline = is_underline;
} }
/** /**
@ -391,80 +391,80 @@ void set_color(rgb_color_t c, rgb_color_t c2)
*/ */
static int writeb_internal( char c ) static int writeb_internal( char c )
{ {
write_loop( 1, &c, 1 ); write_loop( 1, &c, 1 );
return 0; return 0;
} }
int writeb( tputs_arg_t b ) int writeb( tputs_arg_t b )
{ {
out( b ); out( b );
return 0; return 0;
} }
int writembs_internal( char *str ) int writembs_internal( char *str )
{ {
CHECK( str, 1 ); CHECK( str, 1 );
return tputs(str,1,&writeb)==ERR?1:0; return tputs(str,1,&writeb)==ERR?1:0;
} }
int writech( wint_t ch ) int writech( wint_t ch )
{ {
mbstate_t state; mbstate_t state;
size_t i; size_t i;
char buff[MB_LEN_MAX+1]; char buff[MB_LEN_MAX+1];
size_t bytes; size_t bytes;
if( ( ch >= ENCODE_DIRECT_BASE) && if( ( ch >= ENCODE_DIRECT_BASE) &&
( ch < ENCODE_DIRECT_BASE+256) ) ( ch < ENCODE_DIRECT_BASE+256) )
{ {
buff[0] = ch - ENCODE_DIRECT_BASE; buff[0] = ch - ENCODE_DIRECT_BASE;
bytes=1; bytes=1;
} }
else else
{ {
memset( &state, 0, sizeof(state) ); memset( &state, 0, sizeof(state) );
bytes= wcrtomb( buff, ch, &state ); bytes= wcrtomb( buff, ch, &state );
switch( bytes ) switch( bytes )
{ {
case (size_t)(-1): case (size_t)(-1):
{ {
return 1; return 1;
} }
} }
} }
for( i=0; i<bytes; i++ ) for( i=0; i<bytes; i++ )
{ {
out( buff[i] ); out( buff[i] );
} }
return 0; return 0;
} }
void writestr( const wchar_t *str ) void writestr( const wchar_t *str )
{ {
char *pos; char *pos;
CHECK( str, ); CHECK( str, );
// while( *str ) // while( *str )
// writech( *str++ ); // writech( *str++ );
/* /*
Check amount of needed space Check amount of needed space
*/ */
size_t len = wcstombs( 0, str, 0 ); size_t len = wcstombs( 0, str, 0 );
if( len == (size_t)-1 ) if( len == (size_t)-1 )
{ {
debug( 1, L"Tried to print invalid wide character string" ); debug( 1, L"Tried to print invalid wide character string" );
return; return;
} }
len++; len++;
/* /*
Convert Convert
*/ */
char *buffer, static_buffer[256]; char *buffer, static_buffer[256];
@ -472,19 +472,19 @@ void writestr( const wchar_t *str )
buffer = static_buffer; buffer = static_buffer;
else else
buffer = new char[len]; buffer = new char[len];
wcstombs( buffer, wcstombs( buffer,
str, str,
len ); len );
/* /*
Write Write
*/ */
for( pos = buffer; *pos; pos++ ) for( pos = buffer; *pos; pos++ )
{ {
out( *pos ); out( *pos );
} }
if (buffer != static_buffer) if (buffer != static_buffer)
delete[] buffer; delete[] buffer;
} }
@ -492,93 +492,93 @@ void writestr( const wchar_t *str )
void writestr_ellipsis( const wchar_t *str, int max_width ) void writestr_ellipsis( const wchar_t *str, int max_width )
{ {
int written=0; int written=0;
int tot; int tot;
CHECK( str, ); CHECK( str, );
tot = my_wcswidth(str); tot = my_wcswidth(str);
if( tot <= max_width ) if( tot <= max_width )
{ {
writestr( str ); writestr( str );
return; return;
} }
while( *str != 0 ) while( *str != 0 )
{ {
int w = fish_wcwidth( *str ); int w = fish_wcwidth( *str );
if( written+w+fish_wcwidth( ellipsis_char )>max_width ) if( written+w+fish_wcwidth( ellipsis_char )>max_width )
{ {
break; break;
} }
written+=w; written+=w;
writech( *(str++) ); writech( *(str++) );
} }
written += fish_wcwidth( ellipsis_char ); written += fish_wcwidth( ellipsis_char );
writech( ellipsis_char ); writech( ellipsis_char );
while( written < max_width ) while( written < max_width )
{ {
written++; written++;
writestr( L" " ); writestr( L" " );
} }
} }
int write_escaped_str( const wchar_t *str, int max_len ) int write_escaped_str( const wchar_t *str, int max_len )
{ {
wchar_t *out; wchar_t *out;
int i; int i;
int len; int len;
int written=0; int written=0;
CHECK( str, 0 ); CHECK( str, 0 );
out = escape( str, 1 ); out = escape( str, 1 );
len = my_wcswidth( out ); len = my_wcswidth( out );
if( max_len && (max_len < len)) if( max_len && (max_len < len))
{ {
for( i=0; (written+fish_wcwidth(out[i]))<=(max_len-1); i++ ) for( i=0; (written+fish_wcwidth(out[i]))<=(max_len-1); i++ )
{ {
writech( out[i] ); writech( out[i] );
written += fish_wcwidth( out[i] ); written += fish_wcwidth( out[i] );
} }
writech( ellipsis_char ); writech( ellipsis_char );
written += fish_wcwidth( ellipsis_char ); written += fish_wcwidth( ellipsis_char );
for( i=written; i<max_len; i++ ) for( i=written; i<max_len; i++ )
{ {
writech( L' ' ); writech( L' ' );
written++; written++;
} }
} }
else else
{ {
written = len; written = len;
writestr( out ); writestr( out );
} }
free( out ); free( out );
return written; return written;
} }
int output_color_code( const wcstring &val, bool is_background ) { int output_color_code( const wcstring &val, bool is_background ) {
size_t i; size_t i;
int color=FISH_COLOR_NORMAL; int color=FISH_COLOR_NORMAL;
int is_bold=0; int is_bold=0;
int is_underline=0; int is_underline=0;
if (val.empty()) if (val.empty())
return FISH_COLOR_NORMAL; return FISH_COLOR_NORMAL;
wcstring_list_t el; wcstring_list_t el;
tokenize_variable_array( val, el ); tokenize_variable_array( val, el );
for(size_t j=0; j < el.size(); j++ ) { for(size_t j=0; j < el.size(); j++ ) {
const wcstring &next = el.at(j); const wcstring &next = el.at(j);
wcstring color_name; wcstring color_name;
if (is_background) { if (is_background) {
@ -587,7 +587,7 @@ int output_color_code( const wcstring &val, bool is_background ) {
if (string_prefixes_string(prefix, next)) { if (string_prefixes_string(prefix, next)) {
color_name = wcstring(next, prefix.size()); color_name = wcstring(next, prefix.size());
} }
} else { } else {
if (next == L"--bold" || next == L"-o") if (next == L"--bold" || next == L"-o")
is_bold = true; is_bold = true;
@ -596,7 +596,7 @@ int output_color_code( const wcstring &val, bool is_background ) {
else else
color_name = next; color_name = next;
} }
if (! color_name.empty()) { if (! color_name.empty()) {
for( i=0; i<COLORS; i++ ) for( i=0; i<COLORS; i++ )
{ {
@ -607,22 +607,22 @@ int output_color_code( const wcstring &val, bool is_background ) {
} }
} }
} }
} }
return color | (is_bold?FISH_COLOR_BOLD:0) | (is_underline?FISH_COLOR_UNDERLINE:0); return color | (is_bold?FISH_COLOR_BOLD:0) | (is_underline?FISH_COLOR_UNDERLINE:0);
} }
rgb_color_t parse_color( const wcstring &val, bool is_background ) { rgb_color_t parse_color( const wcstring &val, bool is_background ) {
int is_bold=0; int is_bold=0;
int is_underline=0; int is_underline=0;
std::vector<rgb_color_t> candidates; std::vector<rgb_color_t> candidates;
wcstring_list_t el; wcstring_list_t el;
tokenize_variable_array( val, el ); tokenize_variable_array( val, el );
for(size_t j=0; j < el.size(); j++ ) { for(size_t j=0; j < el.size(); j++ ) {
const wcstring &next = el.at(j); const wcstring &next = el.at(j);
wcstring color_name; wcstring color_name;
if (is_background) { if (is_background) {
@ -639,7 +639,7 @@ rgb_color_t parse_color( const wcstring &val, bool is_background ) {
else else
color_name = next; color_name = next;
} }
if (! color_name.empty()) { if (! color_name.empty()) {
rgb_color_t color = rgb_color_t(color_name); rgb_color_t color = rgb_color_t(color_name);
if (! color.is_none()) { if (! color.is_none()) {
@ -647,7 +647,7 @@ rgb_color_t parse_color( const wcstring &val, bool is_background ) {
} }
} }
} }
// Pick the best candidate // Pick the best candidate
rgb_color_t first_rgb = rgb_color_t::none(), first_named = rgb_color_t::none(); rgb_color_t first_rgb = rgb_color_t::none(), first_named = rgb_color_t::none();
for (size_t i=0; i < candidates.size(); i++) { for (size_t i=0; i < candidates.size(); i++) {
@ -657,7 +657,7 @@ rgb_color_t parse_color( const wcstring &val, bool is_background ) {
if (color.is_named() && first_named.is_none()) if (color.is_named() && first_named.is_none())
first_named = color; first_named = color;
} }
// If we have both RGB and named colors, then prefer rgb if term256 is supported // If we have both RGB and named colors, then prefer rgb if term256 is supported
rgb_color_t result; rgb_color_t result;
if ((!first_rgb.is_none() && output_get_supports_term256()) || first_named.is_none()) { if ((!first_rgb.is_none() && output_get_supports_term256()) || first_named.is_none()) {
@ -665,24 +665,24 @@ rgb_color_t parse_color( const wcstring &val, bool is_background ) {
} else { } else {
result = first_named; result = first_named;
} }
if (result.is_none()) if (result.is_none())
result = rgb_color_t::normal(); result = rgb_color_t::normal();
result.set_bold(is_bold); result.set_bold(is_bold);
result.set_underline(is_underline); result.set_underline(is_underline);
#if 0 #if 0
wcstring desc = result.description(); wcstring desc = result.description();
printf("Parsed %ls from %ls (%s)\n", desc.c_str(), val.c_str(), is_background ? "background" : "foreground"); printf("Parsed %ls from %ls (%s)\n", desc.c_str(), val.c_str(), is_background ? "background" : "foreground");
#endif #endif
return result; return result;
} }
void output_set_term( const wchar_t *term ) void output_set_term( const wchar_t *term )
{ {
current_term = term; current_term = term;
} }
const wchar_t *output_get_term() const wchar_t *output_get_term()

View file

@ -1,5 +1,5 @@
/** \file output.h /** \file output.h
Generic output functions Generic output functions
*/ */
/** /**
Constants for various character classifications. Each character of a command string can be classified as one of the following types. Constants for various character classifications. Each character of a command string can be classified as one of the following types.
@ -13,22 +13,22 @@
#include "color.h" #include "color.h"
/** /**
Constants for various colors as used by the set_color function. Constants for various colors as used by the set_color function.
*/ */
enum enum
{ {
FISH_COLOR_BLACK, FISH_COLOR_BLACK,
FISH_COLOR_RED, FISH_COLOR_RED,
FISH_COLOR_GREEN, FISH_COLOR_GREEN,
FISH_COLOR_YELLOW, FISH_COLOR_YELLOW,
FISH_COLOR_BLUE, FISH_COLOR_BLUE,
FISH_COLOR_MAGENTA, FISH_COLOR_MAGENTA,
FISH_COLOR_CYAN, FISH_COLOR_CYAN,
FISH_COLOR_WHITE, FISH_COLOR_WHITE,
/** The default fg color of the terminal */ /** The default fg color of the terminal */
FISH_COLOR_NORMAL, FISH_COLOR_NORMAL,
FISH_COLOR_IGNORE, FISH_COLOR_IGNORE,
FISH_COLOR_RESET FISH_COLOR_RESET
} }
; ;
@ -81,21 +81,21 @@ void set_color(rgb_color_t c, rgb_color_t c2);
#define writembs( mbs ) \ #define writembs( mbs ) \
{ \ { \
char *tmp = mbs; \ char *tmp = mbs; \
if( tmp ) \ if( tmp ) \
{ \ { \
writembs_internal( tmp ); \ writembs_internal( tmp ); \
} \ } \
else \ else \
{ \ { \
debug( 0, \ debug( 0, \
_(L"Tried to use terminfo string %s on line %d of %s, which is undefined in terminal of type \"%ls\". Please report this error to %s"), \ _(L"Tried to use terminfo string %s on line %d of %s, which is undefined in terminal of type \"%ls\". Please report this error to %s"), \
#mbs, \ #mbs, \
__LINE__, \ __LINE__, \
__FILE__, \ __FILE__, \
output_get_term(), \ output_get_term(), \
PACKAGE_BUGREPORT); \ PACKAGE_BUGREPORT); \
} \ } \
} }
/** /**

File diff suppressed because it is too large Load diff

View file

@ -14,7 +14,7 @@
/** /**
Find the beginning and end of the first subshell in the specified string. Find the beginning and end of the first subshell in the specified string.
\param in the string to search for subshells \param in the string to search for subshells
\param begin the starting paranthesis of the subshell \param begin the starting paranthesis of the subshell
\param end the ending paranthesis of the subshell \param end the ending paranthesis of the subshell
@ -22,10 +22,10 @@
\return -1 on syntax error, 0 if no subshells exist and 1 on sucess \return -1 on syntax error, 0 if no subshells exist and 1 on sucess
*/ */
int parse_util_locate_cmdsubst( const wchar_t *in, int parse_util_locate_cmdsubst( const wchar_t *in,
wchar_t **begin, wchar_t **begin,
wchar_t **end, wchar_t **end,
int flags ); int flags );
/** /**
Find the beginning and end of the command substitution under the Find the beginning and end of the command substitution under the
@ -40,9 +40,9 @@ int parse_util_locate_cmdsubst( const wchar_t *in,
\param b the end of the searched string \param b the end of the searched string
*/ */
void parse_util_cmdsubst_extent( const wchar_t *buff, void parse_util_cmdsubst_extent( const wchar_t *buff,
size_t cursor_pos, size_t cursor_pos,
const wchar_t **a, const wchar_t **a,
const wchar_t **b ); const wchar_t **b );
/** /**
Find the beginning and end of the process definition under the cursor Find the beginning and end of the process definition under the cursor
@ -53,9 +53,9 @@ void parse_util_cmdsubst_extent( const wchar_t *buff,
\param b the end of the searched string \param b the end of the searched string
*/ */
void parse_util_process_extent( const wchar_t *buff, void parse_util_process_extent( const wchar_t *buff,
size_t cursor_pos, size_t cursor_pos,
const wchar_t **a, const wchar_t **a,
const wchar_t **b ); const wchar_t **b );
/** /**
@ -67,9 +67,9 @@ void parse_util_process_extent( const wchar_t *buff,
\param b the end of the searched string \param b the end of the searched string
*/ */
void parse_util_job_extent( const wchar_t *buff, void parse_util_job_extent( const wchar_t *buff,
size_t cursor_pos, size_t cursor_pos,
const wchar_t **a, const wchar_t **a,
const wchar_t **b ); const wchar_t **b );
/** /**
Find the beginning and end of the token under the cursor and the Find the beginning and end of the token under the cursor and the
@ -84,11 +84,11 @@ void parse_util_job_extent( const wchar_t *buff,
\param prev_end the end of the token before the current token \param prev_end the end of the token before the current token
*/ */
void parse_util_token_extent( const wchar_t *buff, void parse_util_token_extent( const wchar_t *buff,
size_t cursor_pos, size_t cursor_pos,
const wchar_t **tok_begin, const wchar_t **tok_begin,
const wchar_t **tok_end, const wchar_t **tok_end,
const wchar_t **prev_begin, const wchar_t **prev_begin,
const wchar_t **prev_end ); const wchar_t **prev_end );
/** /**
@ -114,7 +114,7 @@ size_t parse_util_get_offset( const wcstring &str, int line, long line_offset );
/** /**
Set the argv environment variable to the specified null-terminated Set the argv environment variable to the specified null-terminated
array of strings. array of strings.
*/ */
void parse_util_set_argv( const wchar_t * const *argv, const wcstring_list_t &named_arguments ); void parse_util_set_argv( const wchar_t * const *argv, const wcstring_list_t &named_arguments );

5142
parser.cpp

File diff suppressed because it is too large Load diff

304
parser.h
View file

@ -1,5 +1,5 @@
/** \file parser.h /** \file parser.h
The fish parser. The fish parser.
*/ */
#ifndef FISH_PARSER_H #ifndef FISH_PARSER_H
@ -21,15 +21,15 @@
*/ */
struct event_blockage_t struct event_blockage_t
{ {
/** /**
The types of events to block. This is interpreted as a bitset The types of events to block. This is interpreted as a bitset
whete the value is 1 for every bit corresponding to a blocked whete the value is 1 for every bit corresponding to a blocked
event type. For example, if EVENT_VARIABLE type events should event type. For example, if EVENT_VARIABLE type events should
be blocked, (type & 1<<EVENT_BLOCKED) should be set. be blocked, (type & 1<<EVENT_BLOCKED) should be set.
Note that EVENT_ANY can be used to specify any event. Note that EVENT_ANY can be used to specify any event.
*/ */
unsigned int typemask; unsigned int typemask;
}; };
typedef std::list<event_blockage_t> event_blockage_list_t; typedef std::list<event_blockage_t> event_blockage_list_t;
@ -45,30 +45,30 @@ inline bool event_block_list_blocks_type(const event_blockage_list_t &ebls, int
} }
/** /**
Types of blocks Types of blocks
*/ */
enum block_type_t enum block_type_t
{ {
WHILE, /**< While loop block */ WHILE, /**< While loop block */
FOR, /**< For loop block */ FOR, /**< For loop block */
IF, /**< If block */ IF, /**< If block */
FUNCTION_DEF, /**< Function definition block */ FUNCTION_DEF, /**< Function definition block */
FUNCTION_CALL, /**< Function invocation block */ FUNCTION_CALL, /**< Function invocation block */
FUNCTION_CALL_NO_SHADOW, /**< Function invocation block with no variable shadowing */ FUNCTION_CALL_NO_SHADOW, /**< Function invocation block with no variable shadowing */
SWITCH, /**< Switch block */ SWITCH, /**< Switch block */
FAKE, /**< Fake block */ FAKE, /**< Fake block */
SUBST, /**< Command substitution scope */ SUBST, /**< Command substitution scope */
TOP, /**< Outermost block */ TOP, /**< Outermost block */
BEGIN, /**< Unconditional block */ BEGIN, /**< Unconditional block */
SOURCE, /**< Block created by the . (source) builtin */ SOURCE, /**< Block created by the . (source) builtin */
EVENT, /**< Block created on event notifier invocation */ EVENT, /**< Block created on event notifier invocation */
BREAKPOINT, /**< Breakpoint block */ BREAKPOINT, /**< Breakpoint block */
} }
; ;
/** /**
block_t represents a block of commands. block_t represents a block of commands.
*/ */
struct block_t struct block_t
{ {
@ -77,63 +77,63 @@ struct block_t
block_t(block_type_t t); block_t(block_type_t t);
private: private:
const block_type_t block_type; /**< Type of block. */ const block_type_t block_type; /**< Type of block. */
bool made_fake; bool made_fake;
public: public:
block_type_t type() const { return this->made_fake ? FAKE : this->block_type; } block_type_t type() const { return this->made_fake ? FAKE : this->block_type; }
/** Mark a block as fake; this is used by the return statement. */ /** Mark a block as fake; this is used by the return statement. */
void mark_as_fake() { this->made_fake = true; } void mark_as_fake() { this->made_fake = true; }
bool skip; /**< Whether execution of the commands in this block should be skipped */ bool skip; /**< Whether execution of the commands in this block should be skipped */
bool had_command; /**< Set to non-zero once a command has been executed in this block */ bool had_command; /**< Set to non-zero once a command has been executed in this block */
int tok_pos; /**< The start index of the block */ int tok_pos; /**< The start index of the block */
/**
Status for the current loop block. Can be any of the values from the loop_status enum.
*/
int loop_status;
/** /**
The job that is currently evaluated in the specified block. Status for the current loop block. Can be any of the values from the loop_status enum.
*/ */
job_t *job; int loop_status;
/**
The job that is currently evaluated in the specified block.
*/
job_t *job;
#if 0 #if 0
union union
{ {
int while_state; /**< True if the loop condition has not yet been evaluated*/ int while_state; /**< True if the loop condition has not yet been evaluated*/
wchar_t *for_variable; /**< Name of the variable to loop over */ wchar_t *for_variable; /**< Name of the variable to loop over */
int if_state; /**< The state of the if block, can be one of IF_STATE_UNTESTED, IF_STATE_FALSE, IF_STATE_TRUE */ int if_state; /**< The state of the if block, can be one of IF_STATE_UNTESTED, IF_STATE_FALSE, IF_STATE_TRUE */
wchar_t *switch_value; /**< The value to test in a switch block */ wchar_t *switch_value; /**< The value to test in a switch block */
const wchar_t *source_dest; /**< The name of the file to source*/ const wchar_t *source_dest; /**< The name of the file to source*/
event_t *event; /**<The event that triggered this block */ event_t *event; /**<The event that triggered this block */
wchar_t *function_call_name; wchar_t *function_call_name;
} param1; } param1;
#endif #endif
/** /**
Name of file that created this block Name of file that created this block
*/ */
const wchar_t *src_filename; const wchar_t *src_filename;
/** /**
Line number where this block was created Line number where this block was created
*/ */
int src_lineno; int src_lineno;
/** Whether we should pop the environment variable stack when we're popped off of the block stack */ /** Whether we should pop the environment variable stack when we're popped off of the block stack */
bool wants_pop_env; bool wants_pop_env;
/** List of event blocks. */ /** List of event blocks. */
event_blockage_list_t event_blocks; event_blockage_list_t event_blocks;
/** /**
Next outer block Next outer block
*/ */
block_t *outer; block_t *outer;
/** Destructor */ /** Destructor */
virtual ~block_t(); virtual ~block_t();
}; };
@ -144,7 +144,7 @@ struct if_block_t : public block_t
bool is_elseif_entry; // whether we're at the beginning of an ELSEIF branch bool is_elseif_entry; // whether we're at the beginning of an ELSEIF branch
bool any_branch_taken; // whether the clause of the if statement or any elseif has been found to be true bool any_branch_taken; // whether the clause of the if statement or any elseif has been found to be true
bool else_evaluated; // whether we've encountered a terminal else block bool else_evaluated; // whether we've encountered a terminal else block
if_block_t(); if_block_t();
}; };
@ -211,11 +211,11 @@ struct breakpoint_block_t : public block_t
/** /**
Possible states for a loop Possible states for a loop
*/ */
enum loop_status enum loop_status
{ {
LOOP_NORMAL, /**< Current loop block executed as normal */ LOOP_NORMAL, /**< Current loop block executed as normal */
LOOP_BREAK, /**< Current loop block should be removed */ LOOP_BREAK, /**< Current loop block should be removed */
LOOP_CONTINUE, /**< Current loop block should be skipped */ LOOP_CONTINUE, /**< Current loop block should be skipped */
}; };
@ -224,9 +224,9 @@ enum loop_status
*/ */
enum while_status enum while_status
{ {
WHILE_TEST_FIRST, /**< This is the first command of the first lap of a while loop */ WHILE_TEST_FIRST, /**< This is the first command of the first lap of a while loop */
WHILE_TEST_AGAIN, /**< This is not the first lap of the while loop, but it is the first command of the loop */ WHILE_TEST_AGAIN, /**< This is not the first lap of the while loop, but it is the first command of the loop */
WHILE_TESTED, /**< This is not the first command in the loop */ WHILE_TESTED, /**< This is not the first command in the loop */
} }
; ;
@ -234,24 +234,24 @@ enum while_status
/** /**
Errors that can be generated by the parser Errors that can be generated by the parser
*/ */
enum parser_error enum parser_error
{ {
/** /**
No error No error
*/ */
NO_ERR=0, NO_ERR=0,
/** /**
An error in the syntax An error in the syntax
*/ */
SYNTAX_ERROR, SYNTAX_ERROR,
/** /**
Error occured while evaluating commands Error occured while evaluating commands
*/ */
EVAL_ERROR, EVAL_ERROR,
/** /**
Error while evaluating cmdsubst Error while evaluating cmdsubst
*/ */
CMDSUBST_ERROR, CMDSUBST_ERROR,
}; };
enum parser_type_t { enum parser_type_t {
@ -259,30 +259,30 @@ enum parser_type_t {
PARSER_TYPE_GENERAL, PARSER_TYPE_GENERAL,
PARSER_TYPE_FUNCTIONS_ONLY, PARSER_TYPE_FUNCTIONS_ONLY,
PARSER_TYPE_COMPLETIONS_ONLY, PARSER_TYPE_COMPLETIONS_ONLY,
PARSER_TYPE_ERRORS_ONLY PARSER_TYPE_ERRORS_ONLY
}; };
struct profile_item_t { struct profile_item_t {
/** /**
Time spent executing the specified command, including parse time for nested blocks. Time spent executing the specified command, including parse time for nested blocks.
*/ */
int exec; int exec;
/** /**
Time spent parsing the specified command, including execution time for command substitutions. Time spent parsing the specified command, including execution time for command substitutions.
*/ */
int parse; int parse;
/** /**
The block level of the specified command. nested blocks and command substitutions both increase the block level. The block level of the specified command. nested blocks and command substitutions both increase the block level.
*/ */
size_t level; size_t level;
/** /**
If the execution of this command was skipped. If the execution of this command was skipped.
*/ */
int skipped; int skipped;
/** /**
The command string. The command string.
*/ */
wcstring cmd; wcstring cmd;
}; };
struct tokenizer; struct tokenizer;
@ -291,48 +291,48 @@ class parser_t {
private: private:
enum parser_type_t parser_type; enum parser_type_t parser_type;
std::vector<block_t> blocks; std::vector<block_t> blocks;
/** Whether or not we output errors */ /** Whether or not we output errors */
const bool show_errors; const bool show_errors;
/** Last error code */ /** Last error code */
int error_code; int error_code;
/** Position of last error */ /** Position of last error */
int err_pos; int err_pos;
/** Description of last error */ /** Description of last error */
wcstring err_buff; wcstring err_buff;
/** Pointer to the current tokenizer */ /** Pointer to the current tokenizer */
tokenizer *current_tokenizer; tokenizer *current_tokenizer;
/** String for representing the current line */ /** String for representing the current line */
wcstring lineinfo; wcstring lineinfo;
/** This is the position of the beginning of the currently parsed command */ /** This is the position of the beginning of the currently parsed command */
int current_tokenizer_pos; int current_tokenizer_pos;
/** List of called functions, used to help prevent infinite recursion */ /** List of called functions, used to help prevent infinite recursion */
wcstring_list_t forbidden_function; wcstring_list_t forbidden_function;
/** String index where the current job started. */ /** String index where the current job started. */
int job_start_pos; int job_start_pos;
/** The jobs associated with this parser */ /** The jobs associated with this parser */
job_list_t my_job_list; job_list_t my_job_list;
/** /**
Keeps track of how many recursive eval calls have been made. Eval Keeps track of how many recursive eval calls have been made. Eval
doesn't call itself directly, recursion happens on blocks and on doesn't call itself directly, recursion happens on blocks and on
command substitutions. command substitutions.
*/ */
int eval_level; int eval_level;
/* No copying allowed */ /* No copying allowed */
parser_t(const parser_t&); parser_t(const parser_t&);
parser_t& operator=(const parser_t&); parser_t& operator=(const parser_t&);
void parse_job_argument_list( process_t *p, job_t *j, tokenizer *tok, std::vector<completion_t>&, bool ); void parse_job_argument_list( process_t *p, job_t *j, tokenizer *tok, std::vector<completion_t>&, bool );
int parse_job( process_t *p, job_t *j, tokenizer *tok ); int parse_job( process_t *p, job_t *j, tokenizer *tok );
void skipped_exec( job_t * j ); void skipped_exec( job_t * j );
@ -340,10 +340,10 @@ class parser_t {
int parser_test_argument( const wchar_t *arg, wcstring *out, const wchar_t *prefix, int offset ); int parser_test_argument( const wchar_t *arg, wcstring *out, const wchar_t *prefix, int offset );
void print_errors( wcstring &target, const wchar_t *prefix ); void print_errors( wcstring &target, const wchar_t *prefix );
void print_errors_stderr(); void print_errors_stderr();
public: public:
std::vector<profile_item_t> profile_items; std::vector<profile_item_t> profile_items;
/** /**
Returns the name of the currently evaluated function if we are Returns the name of the currently evaluated function if we are
currently evaluating a function, null otherwise. This is tested by currently evaluating a function, null otherwise. This is tested by
@ -351,27 +351,27 @@ class parser_t {
type FUNCTION_CALL. type FUNCTION_CALL.
*/ */
const wchar_t *is_function() const; const wchar_t *is_function() const;
/** Get the "principal" parser, whatever that is */ /** Get the "principal" parser, whatever that is */
static parser_t &principal_parser(); static parser_t &principal_parser();
/** Indicates that execution of all blocks in the principal parser should stop. /** Indicates that execution of all blocks in the principal parser should stop.
This is called from signal handlers! This is called from signal handlers!
*/ */
static void skip_all_blocks(); static void skip_all_blocks();
/** Create a parser of the given type */ /** Create a parser of the given type */
parser_t(enum parser_type_t type, bool show_errors); parser_t(enum parser_type_t type, bool show_errors);
/** The current innermost block, allocated with new */ /** The current innermost block, allocated with new */
block_t *current_block; block_t *current_block;
/** Global event blocks */ /** Global event blocks */
event_blockage_list_t global_event_blocks; event_blockage_list_t global_event_blocks;
/** Current block level io redirections */ /** Current block level io redirections */
io_chain_t block_io; io_chain_t block_io;
/** /**
Evaluate the expressions contained in cmd. Evaluate the expressions contained in cmd.
@ -382,7 +382,7 @@ class parser_t {
\return 0 on success, 1 otherwise \return 0 on success, 1 otherwise
*/ */
int eval( const wcstring &cmdStr, const io_chain_t &io, enum block_type_t block_type ); int eval( const wcstring &cmdStr, const io_chain_t &io, enum block_type_t block_type );
/** /**
Evaluate line as a list of parameters, i.e. tokenize it and perform parameter expansion and cmdsubst execution on the tokens. Evaluate line as a list of parameters, i.e. tokenize it and perform parameter expansion and cmdsubst execution on the tokens.
The output is inserted into output, and should be freed by the caller. The output is inserted into output, and should be freed by the caller.
@ -390,24 +390,24 @@ class parser_t {
\param line Line to evaluate \param line Line to evaluate
\param output List to insert output to \param output List to insert output to
*/ */
/** /**
\param line Line to evaluate \param line Line to evaluate
\param output List to insert output to \param output List to insert output to
*/ */
int eval_args( const wchar_t *line, std::vector<completion_t> &output ); int eval_args( const wchar_t *line, std::vector<completion_t> &output );
/** /**
Sets the current evaluation error. This function should only be used by libraries that are called by Sets the current evaluation error. This function should only be used by libraries that are called by
\param ec The new error code \param ec The new error code
\param p The character offset at which the error occured \param p The character offset at which the error occured
\param str The printf-style error message filter \param str The printf-style error message filter
*/ */
void error( int ec, int p, const wchar_t *str, ... ); void error( int ec, int p, const wchar_t *str, ... );
/** /**
Returns a string describing the current parser pisition in the format 'FILENAME (line LINE_NUMBER): LINE'. Returns a string describing the current parser pisition in the format 'FILENAME (line LINE_NUMBER): LINE'.
Example: Example:
init.fish (line 127): ls|grep pancake init.fish (line 127): ls|grep pancake
*/ */
@ -415,7 +415,7 @@ class parser_t {
/** Returns the current line number */ /** Returns the current line number */
int get_lineno() const; int get_lineno() const;
/** Returns the line number for the character at the given index */ /** Returns the line number for the character at the given index */
int line_number_of_character_at_offset(size_t idx) const; int line_number_of_character_at_offset(size_t idx) const;
@ -430,7 +430,7 @@ class parser_t {
/** Get the string currently parsed */ /** Get the string currently parsed */
const wchar_t *get_buffer() const; const wchar_t *get_buffer() const;
/** Get the list of jobs */ /** Get the list of jobs */
job_list_t &job_list() { return my_job_list; } job_list_t &job_list() { return my_job_list; }
@ -445,16 +445,16 @@ class parser_t {
/** Create a job */ /** Create a job */
job_t *job_create(); job_t *job_create();
/** Removes a job */ /** Removes a job */
bool job_remove(job_t *job); bool job_remove(job_t *job);
/** Promotes a job to the front of the list */ /** Promotes a job to the front of the list */
void job_promote(job_t *job); void job_promote(job_t *job);
/** Return the job with the specified job id. If id is 0 or less, return the last job used. */ /** Return the job with the specified job id. If id is 0 or less, return the last job used. */
job_t *job_get(int job_id); job_t *job_get(int job_id);
/** Returns the job with the given pid */ /** Returns the job with the given pid */
job_t *job_get_from_pid( int pid ); job_t *job_get_from_pid( int pid );
@ -502,7 +502,7 @@ class parser_t {
void destroy(); void destroy();
/** /**
This function checks if the specified string is a help option. This function checks if the specified string is a help option.
\param s the string to test \param s the string to test
\param min_match is the minimum number of characters that must match in a long style option, i.e. the longest common prefix between --help and any other option. If less than 3, 3 will be assumed. \param min_match is the minimum number of characters that must match in a long style option, i.e. the longest common prefix between --help and any other option. If less than 3, 3 will be assumed.

View file

@ -16,8 +16,8 @@ Functions having to do with parser keywords, like testing if a function is a blo
bool parser_keywords_is_switch( const wcstring &cmd ) bool parser_keywords_is_switch( const wcstring &cmd )
{ {
if (cmd == L"--") { if (cmd == L"--") {
return ARG_SKIP; return ARG_SKIP;
} else if (! cmd.empty() && cmd.at(0) == L'-') { } else if (! cmd.empty() && cmd.at(0) == L'-') {
return ARG_SWITCH; return ARG_SWITCH;
} else { } else {
@ -27,49 +27,49 @@ bool parser_keywords_is_switch( const wcstring &cmd )
bool parser_keywords_skip_arguments( const wcstring &cmd ) bool parser_keywords_skip_arguments( const wcstring &cmd )
{ {
return contains( cmd, return contains( cmd,
L"else", L"else",
L"begin" ); L"begin" );
} }
bool parser_keywords_is_subcommand( const wcstring &cmd ) bool parser_keywords_is_subcommand( const wcstring &cmd )
{ {
return parser_keywords_skip_arguments( cmd ) || return parser_keywords_skip_arguments( cmd ) ||
contains( cmd, contains( cmd,
L"command", L"command",
L"builtin", L"builtin",
L"while", L"while",
L"exec", L"exec",
L"if", L"if",
L"and", L"and",
L"or", L"or",
L"not" ); L"not" );
} }
bool parser_keywords_is_block( const wcstring &word) bool parser_keywords_is_block( const wcstring &word)
{ {
return contains( word, return contains( word,
L"for", L"for",
L"while", L"while",
L"if", L"if",
L"function", L"function",
L"switch", L"switch",
L"begin" ); L"begin" );
} }
bool parser_keywords_is_reserved( const wcstring &word) bool parser_keywords_is_reserved( const wcstring &word)
{ {
return parser_keywords_is_block(word) || return parser_keywords_is_block(word) ||
parser_keywords_is_subcommand( word ) || parser_keywords_is_subcommand( word ) ||
contains( word, contains( word,
L"end", L"end",
L"case", L"case",
L"else", L"else",
L"return", L"return",
L"continue", L"continue",
L"break" ); L"break" );
} }

View file

@ -9,11 +9,11 @@ Functions having to do with parser keywords, like testing if a function is a blo
/** /**
Return values for parser_keywords_is_switch() Return values for parser_keywords_is_switch()
*/ */
enum enum
{ {
ARG_NON_SWITCH, ARG_NON_SWITCH,
ARG_SWITCH, ARG_SWITCH,
ARG_SKIP ARG_SKIP
}; };

392
path.cpp
View file

@ -25,110 +25,110 @@
static bool path_get_path_core(const wcstring &cmd, wcstring *out_path, const env_var_t &bin_path_var) static bool path_get_path_core(const wcstring &cmd, wcstring *out_path, const env_var_t &bin_path_var)
{ {
int err = ENOENT; int err = ENOENT;
debug( 3, L"path_get_path( '%ls' )", cmd.c_str() ); debug( 3, L"path_get_path( '%ls' )", cmd.c_str() );
/* If the command has a slash, it must be a full path */ /* If the command has a slash, it must be a full path */
if (cmd.find(L'/') != wcstring::npos) if (cmd.find(L'/') != wcstring::npos)
{ {
if( waccess( cmd, X_OK )==0 ) if( waccess( cmd, X_OK )==0 )
{ {
struct stat buff; struct stat buff;
if(wstat( cmd, &buff )) if(wstat( cmd, &buff ))
{ {
return false; return false;
} }
if( S_ISREG(buff.st_mode) ) if( S_ISREG(buff.st_mode) )
{ {
if (out_path) if (out_path)
out_path->assign(cmd); out_path->assign(cmd);
return true; return true;
} }
else else
{ {
errno = EACCES; errno = EACCES;
return false; return false;
} }
} }
else else
{ {
struct stat buff; struct stat buff;
wstat( cmd, &buff ); wstat( cmd, &buff );
return false; return false;
} }
} }
else else
{ {
wcstring bin_path; wcstring bin_path;
if (! bin_path_var.missing()) if (! bin_path_var.missing())
{ {
bin_path = bin_path_var; bin_path = bin_path_var;
} }
else else
{ {
if (contains( PREFIX L"/bin", L"/bin", L"/usr/bin" )) if (contains( PREFIX L"/bin", L"/bin", L"/usr/bin" ))
{ {
bin_path = L"/bin" ARRAY_SEP_STR L"/usr/bin"; bin_path = L"/bin" ARRAY_SEP_STR L"/usr/bin";
} }
else else
{ {
bin_path = L"/bin" ARRAY_SEP_STR L"/usr/bin" ARRAY_SEP_STR PREFIX L"/bin"; bin_path = L"/bin" ARRAY_SEP_STR L"/usr/bin" ARRAY_SEP_STR PREFIX L"/bin";
} }
} }
wcstring nxt_path; wcstring nxt_path;
wcstokenizer tokenizer(bin_path, ARRAY_SEP_STR); wcstokenizer tokenizer(bin_path, ARRAY_SEP_STR);
while (tokenizer.next(nxt_path)) while (tokenizer.next(nxt_path))
{ {
if (nxt_path.empty()) if (nxt_path.empty())
continue; continue;
append_path_component(nxt_path, cmd); append_path_component(nxt_path, cmd);
if( waccess( nxt_path, X_OK )==0 ) if( waccess( nxt_path, X_OK )==0 )
{ {
struct stat buff; struct stat buff;
if( wstat( nxt_path, &buff )==-1 ) if( wstat( nxt_path, &buff )==-1 )
{ {
if( errno != EACCES ) if( errno != EACCES )
{ {
wperror( L"stat" ); wperror( L"stat" );
} }
continue; continue;
} }
if( S_ISREG(buff.st_mode) ) if( S_ISREG(buff.st_mode) )
{ {
if (out_path) if (out_path)
out_path->swap(nxt_path); out_path->swap(nxt_path);
return true; return true;
} }
err = EACCES; err = EACCES;
} }
else else
{ {
switch( errno ) switch( errno )
{ {
case ENOENT: case ENOENT:
case ENAMETOOLONG: case ENAMETOOLONG:
case EACCES: case EACCES:
case ENOTDIR: case ENOTDIR:
break; break;
default: default:
{ {
debug( 1, debug( 1,
MISSING_COMMAND_ERR_MSG, MISSING_COMMAND_ERR_MSG,
nxt_path.c_str() ); nxt_path.c_str() );
wperror( L"access" ); wperror( L"access" );
} }
} }
} }
} }
} }
errno = err; errno = err;
return false; return false;
} }
bool path_get_path(const wcstring &cmd, wcstring *out_path, const env_vars_snapshot_t &vars) bool path_get_path(const wcstring &cmd, wcstring *out_path, const env_vars_snapshot_t &vars)
@ -143,94 +143,94 @@ bool path_get_path(const wcstring &cmd, wcstring *out_path)
bool path_get_cdpath_string(const wcstring &dir_str, wcstring &result, const env_var_t &cdpath) bool path_get_cdpath_string(const wcstring &dir_str, wcstring &result, const env_var_t &cdpath)
{ {
wchar_t *res = 0; wchar_t *res = 0;
int err = ENOENT; int err = ENOENT;
bool success = false; bool success = false;
const wchar_t *const dir = dir_str.c_str(); const wchar_t *const dir = dir_str.c_str();
if( dir[0] == L'/'|| (wcsncmp( dir, L"./", 2 )==0) ) if( dir[0] == L'/'|| (wcsncmp( dir, L"./", 2 )==0) )
{ {
struct stat buf; struct stat buf;
if( wstat( dir, &buf ) == 0 ) if( wstat( dir, &buf ) == 0 )
{ {
if( S_ISDIR(buf.st_mode) ) if( S_ISDIR(buf.st_mode) )
{ {
result = dir_str; result = dir_str;
success = true; success = true;
} }
else else
{ {
err = ENOTDIR; err = ENOTDIR;
} }
} }
} }
else else
{ {
wcstring path = L"."; wcstring path = L".";
// Respect CDPATH // Respect CDPATH
env_var_t cdpath = env_get_string(L"CDPATH"); env_var_t cdpath = env_get_string(L"CDPATH");
if (! cdpath.missing_or_empty()) { if (! cdpath.missing_or_empty()) {
path = cdpath.c_str(); path = cdpath.c_str();
} }
wcstokenizer tokenizer(path, ARRAY_SEP_STR); wcstokenizer tokenizer(path, ARRAY_SEP_STR);
wcstring next_path; wcstring next_path;
while (tokenizer.next(next_path)) while (tokenizer.next(next_path))
{ {
expand_tilde(next_path); expand_tilde(next_path);
if (next_path.size() == 0) continue; if (next_path.size() == 0) continue;
wcstring whole_path = next_path; wcstring whole_path = next_path;
append_path_component(whole_path, dir); append_path_component(whole_path, dir);
struct stat buf; struct stat buf;
if( wstat( whole_path, &buf ) == 0 ) if( wstat( whole_path, &buf ) == 0 )
{ {
if( S_ISDIR(buf.st_mode) ) if( S_ISDIR(buf.st_mode) )
{ {
result = whole_path; result = whole_path;
success = true; success = true;
break; break;
} }
else else
{ {
err = ENOTDIR; err = ENOTDIR;
} }
} }
else else
{ {
if( lwstat( whole_path, &buf ) == 0 ) if( lwstat( whole_path, &buf ) == 0 )
{ {
err = EROTTEN; err = EROTTEN;
} }
} }
} }
} }
if( !success ) if( !success )
{ {
errno = err; errno = err;
} }
return res; return res;
} }
bool path_get_cdpath(const wcstring &dir, wcstring *out, const wchar_t *wd, const env_vars_snapshot_t &env_vars) bool path_get_cdpath(const wcstring &dir, wcstring *out, const wchar_t *wd, const env_vars_snapshot_t &env_vars)
{ {
int err = ENOENT; int err = ENOENT;
if (dir.empty()) if (dir.empty())
return false; return false;
if (wd) if (wd)
{ {
size_t len = wcslen(wd); size_t len = wcslen(wd);
assert(wd[len - 1] == L'/'); assert(wd[len - 1] == L'/');
} }
wcstring_list_t paths; wcstring_list_t paths;
if (dir.at(0) == L'/') { if (dir.at(0) == L'/') {
/* Absolute path */ /* Absolute path */
@ -254,7 +254,7 @@ bool path_get_cdpath(const wcstring &dir, wcstring *out, const wchar_t *wd, cons
wcstokenizer tokenizer(path, ARRAY_SEP_STR); wcstokenizer tokenizer(path, ARRAY_SEP_STR);
while (tokenizer.next(nxt_path)) while (tokenizer.next(nxt_path))
{ {
if (nxt_path == L"." && wd != NULL) { if (nxt_path == L"." && wd != NULL) {
// nxt_path is just '.', and we have a working directory, so use the wd instead // nxt_path is just '.', and we have a working directory, so use the wd instead
// TODO: if nxt_path starts with ./ we need to replace the . with the wd // TODO: if nxt_path starts with ./ we need to replace the . with the wd
@ -262,39 +262,39 @@ bool path_get_cdpath(const wcstring &dir, wcstring *out, const wchar_t *wd, cons
} }
expand_tilde(nxt_path); expand_tilde(nxt_path);
// debug( 2, L"woot %ls\n", expanded_path.c_str() ); // debug( 2, L"woot %ls\n", expanded_path.c_str() );
if (nxt_path.empty()) if (nxt_path.empty())
continue; continue;
wcstring whole_path = nxt_path; wcstring whole_path = nxt_path;
append_path_component(whole_path, dir); append_path_component(whole_path, dir);
paths.push_back(whole_path); paths.push_back(whole_path);
} }
} }
bool success = false; bool success = false;
for (wcstring_list_t::const_iterator iter = paths.begin(); iter != paths.end(); ++iter) { for (wcstring_list_t::const_iterator iter = paths.begin(); iter != paths.end(); ++iter) {
struct stat buf; struct stat buf;
const wcstring &dir = *iter; const wcstring &dir = *iter;
if( wstat( dir, &buf ) == 0 ) if( wstat( dir, &buf ) == 0 )
{ {
if( S_ISDIR(buf.st_mode) ) if( S_ISDIR(buf.st_mode) )
{ {
success = true; success = true;
if (out) if (out)
out->assign(dir); out->assign(dir);
break; break;
} }
else else
{ {
err = ENOTDIR; err = ENOTDIR;
} }
}
} }
}
if (! success) if (! success)
errno = err; errno = err;
return success; return success;
} }
@ -302,7 +302,7 @@ bool path_can_be_implicit_cd(const wcstring &path, wcstring *out_path, const wch
{ {
wcstring exp_path = path; wcstring exp_path = path;
expand_tilde(exp_path); expand_tilde(exp_path);
bool result = false; bool result = false;
if (string_prefixes_string(L"/", exp_path) || if (string_prefixes_string(L"/", exp_path) ||
string_prefixes_string(L"./", exp_path) || string_prefixes_string(L"./", exp_path) ||
@ -317,41 +317,41 @@ bool path_can_be_implicit_cd(const wcstring &path, wcstring *out_path, const wch
bool path_get_config(wcstring &path) bool path_get_config(wcstring &path)
{ {
int done = 0; int done = 0;
wcstring res; wcstring res;
const env_var_t xdg_dir = env_get_string( L"XDG_CONFIG_HOME" ); const env_var_t xdg_dir = env_get_string( L"XDG_CONFIG_HOME" );
if( ! xdg_dir.missing() ) if( ! xdg_dir.missing() )
{ {
res = xdg_dir + L"/fish"; res = xdg_dir + L"/fish";
if( !create_directory( res ) ) if( !create_directory( res ) )
{ {
done = 1; done = 1;
} }
} }
else else
{ {
const env_var_t home = env_get_string( L"HOME" ); const env_var_t home = env_get_string( L"HOME" );
if( ! home.missing() ) if( ! home.missing() )
{ {
res = home + L"/.config/fish"; res = home + L"/.config/fish";
if( !create_directory( res ) ) if( !create_directory( res ) )
{ {
done = 1; done = 1;
} }
} }
} }
if( done ) if( done )
{ {
path = res; path = res;
return true; return true;
} }
else else
{ {
debug( 0, _(L"Unable to create a configuration directory for fish. Your personal settings will not be saved. Please set the $XDG_CONFIG_HOME variable to a directory where the current user has write access." )); debug( 0, _(L"Unable to create a configuration directory for fish. Your personal settings will not be saved. Please set the $XDG_CONFIG_HOME variable to a directory where the current user has write access." ));
return false; return false;
} }
} }
@ -375,7 +375,7 @@ void path_make_canonical( wcstring &path )
size = path.size(); size = path.size();
replace_all(path, L"//", L"/"); replace_all(path, L"//", L"/");
} while (path.size() != size); } while (path.size() != size);
/* Remove trailing slashes, except don't remove the first one */ /* Remove trailing slashes, except don't remove the first one */
while (size-- > 1) { while (size-- > 1) {
if (path.at(size) != L'/') if (path.at(size) != L'/')
@ -410,7 +410,7 @@ bool path_is_valid(const wcstring &path, const wcstring &working_directory)
bool paths_are_same_file(const wcstring &path1, const wcstring &path2) { bool paths_are_same_file(const wcstring &path1, const wcstring &path2) {
if (path1 == path2) if (path1 == path2)
return true; return true;
struct stat s1, s2; struct stat s1, s2;
if (wstat(path1, &s1) == 0 && wstat(path2, &s2) == 0) { if (wstat(path1, &s1) == 0 && wstat(path2, &s2) == 0) {
return s1.st_ino == s2.st_ino && s1.st_dev == s2.st_dev; return s1.st_ino == s2.st_ino && s1.st_dev == s2.st_dev;

14
path.h
View file

@ -1,9 +1,9 @@
/** \file path.h /** \file path.h
Directory utilities. This library contains functions for locating Directory utilities. This library contains functions for locating
configuration directories, for testing if a command with a given configuration directories, for testing if a command with a given
name can be found in the PATH, and various other path-related name can be found in the PATH, and various other path-related
issues. issues.
*/ */
#ifndef FISH_PATH_H #ifndef FISH_PATH_H
@ -27,7 +27,7 @@ bool path_get_config(wcstring &path);
/** /**
Finds the full path of an executable. Returns YES if successful. Finds the full path of an executable. Returns YES if successful.
\param cmd The name of the executable. \param cmd The name of the executable.
\param output_or_NULL If non-NULL, store the full path. \param output_or_NULL If non-NULL, store the full path.
\param vars The environment variables snapshot to use \param vars The environment variables snapshot to use
@ -42,14 +42,14 @@ bool path_get_path(const wcstring &cmd,
variable as a list of base directories for relative paths. The variable as a list of base directories for relative paths. The
returned string is allocated using halloc and the specified returned string is allocated using halloc and the specified
context. context.
If no valid path is found, null is returned and errno is set to If no valid path is found, null is returned and errno is set to
ENOTDIR if at least one such path was found, but it did not point ENOTDIR if at least one such path was found, but it did not point
to a directory, EROTTEN if a arotten symbolic link was found, or to a directory, EROTTEN if a arotten symbolic link was found, or
ENOENT if no file of the specified name was found. If both a rotten ENOENT if no file of the specified name was found. If both a rotten
symlink and a file are found, it is undefined which error status symlink and a file are found, it is undefined which error status
will be returned. will be returned.
\param dir The name of the directory. \param dir The name of the directory.
\param out_or_NULL If non-NULL, return the path to the resolved directory \param out_or_NULL If non-NULL, return the path to the resolved directory
\param wd The working directory, or NULL to use the default. The working directory should have a slash appended at the end. \param wd The working directory, or NULL to use the default. The working directory should have a slash appended at the end.

View file

@ -1,6 +1,6 @@
/** \file postfork.cpp /** \file postfork.cpp
Functions that we may safely call after fork(). Functions that we may safely call after fork().
*/ */
#include <fcntl.h> #include <fcntl.h>
@ -44,61 +44,61 @@ static void debug_safe_int(int level, const char *format, int val)
// PCA These calls to debug are rather sketchy because they may allocate memory. Fortunately they only occur if an error occurs. // PCA These calls to debug are rather sketchy because they may allocate memory. Fortunately they only occur if an error occurs.
int set_child_group( job_t *j, process_t *p, int print_errors ) int set_child_group( job_t *j, process_t *p, int print_errors )
{ {
int res = 0; int res = 0;
if( job_get_flag( j, JOB_CONTROL ) ) if( job_get_flag( j, JOB_CONTROL ) )
{ {
if (!j->pgid) if (!j->pgid)
{ {
j->pgid = p->pid; j->pgid = p->pid;
} }
if( setpgid (p->pid, j->pgid) ) if( setpgid (p->pid, j->pgid) )
{ {
if( getpgid( p->pid) != j->pgid && print_errors ) if( getpgid( p->pid) != j->pgid && print_errors )
{ {
char pid_buff[128]; char pid_buff[128];
char job_id_buff[128]; char job_id_buff[128];
char getpgid_buff[128]; char getpgid_buff[128];
char job_pgid_buff[128]; char job_pgid_buff[128];
format_long_safe(pid_buff, p->pid); format_long_safe(pid_buff, p->pid);
format_long_safe(job_id_buff, j->job_id); format_long_safe(job_id_buff, j->job_id);
format_long_safe(getpgid_buff, getpgid( p->pid)); format_long_safe(getpgid_buff, getpgid( p->pid));
format_long_safe(job_pgid_buff, j->pgid); format_long_safe(job_pgid_buff, j->pgid);
debug_safe( 1,
"Could not send process %s, '%s' in job %s, '%s' from group %s to group %s",
pid_buff,
p->argv0_cstr(),
job_id_buff,
j->command_cstr(),
getpgid_buff,
job_pgid_buff );
wperror( L"setpgid" ); debug_safe( 1,
res = -1; "Could not send process %s, '%s' in job %s, '%s' from group %s to group %s",
} pid_buff,
} p->argv0_cstr(),
} job_id_buff,
else j->command_cstr(),
{ getpgid_buff,
j->pgid = getpid(); job_pgid_buff );
}
if( job_get_flag( j, JOB_TERMINAL ) && job_get_flag( j, JOB_FOREGROUND ) ) wperror( L"setpgid" );
{ res = -1;
if( tcsetpgrp (0, j->pgid) && print_errors ) }
{ }
}
else
{
j->pgid = getpid();
}
if( job_get_flag( j, JOB_TERMINAL ) && job_get_flag( j, JOB_FOREGROUND ) )
{
if( tcsetpgrp (0, j->pgid) && print_errors )
{
char job_id_buff[128]; char job_id_buff[128];
format_long_safe(job_id_buff, j->job_id); format_long_safe(job_id_buff, j->job_id);
debug_safe( 1, "Could not send job %s ('%s') to foreground", job_id_buff, j->command_cstr() ); debug_safe( 1, "Could not send job %s ('%s') to foreground", job_id_buff, j->command_cstr() );
wperror( L"tcsetpgrp" ); wperror( L"tcsetpgrp" );
res = -1; res = -1;
} }
} }
return res; return res;
} }
/** Make sure the fd used by each redirection is not used by a pipe. */ /** Make sure the fd used by each redirection is not used by a pipe. */
@ -108,7 +108,7 @@ static void free_redirected_fds_from_pipes(io_chain_t &io_chain)
for (size_t i = 0; i < max; i++) for (size_t i = 0; i < max; i++)
{ {
int fd_to_free = io_chain.at(i)->fd; int fd_to_free = io_chain.at(i)->fd;
/* We only have to worry about fds beyond the three standard ones */ /* We only have to worry about fds beyond the three standard ones */
if (fd_to_free <= 2) if (fd_to_free <= 2)
continue; continue;
@ -120,14 +120,14 @@ static void free_redirected_fds_from_pipes(io_chain_t &io_chain)
io_data_t *possible_conflict = io_chain.at(j); io_data_t *possible_conflict = io_chain.at(j);
if (possible_conflict->io_mode != IO_PIPE && possible_conflict->io_mode != IO_BUFFER) if (possible_conflict->io_mode != IO_PIPE && possible_conflict->io_mode != IO_BUFFER)
continue; continue;
/* If the pipe is a conflict, dup it to some other value */ /* If the pipe is a conflict, dup it to some other value */
for (int k=0; k<2; k++) for (int k=0; k<2; k++)
{ {
/* If it's not a conflict, we don't care */ /* If it's not a conflict, we don't care */
if (possible_conflict->param1.pipe_fd[k] != fd_to_free) if (possible_conflict->param1.pipe_fd[k] != fd_to_free)
continue; continue;
/* Repeat until we have a replacement fd */ /* Repeat until we have a replacement fd */
int replacement_fd = -1; int replacement_fd = -1;
while (replacement_fd < 0) while (replacement_fd < 0)
@ -143,7 +143,7 @@ static void free_redirected_fds_from_pipes(io_chain_t &io_chain)
possible_conflict->param1.pipe_fd[k] = replacement_fd; possible_conflict->param1.pipe_fd[k] = replacement_fd;
} }
} }
} }
} }
@ -162,149 +162,149 @@ static void free_redirected_fds_from_pipes(io_chain_t &io_chain)
static int handle_child_io( io_chain_t &io_chain ) static int handle_child_io( io_chain_t &io_chain )
{ {
close_unused_internal_pipes( io_chain ); close_unused_internal_pipes( io_chain );
free_redirected_fds_from_pipes(io_chain); free_redirected_fds_from_pipes(io_chain);
for (size_t idx = 0; idx < io_chain.size(); idx++) for (size_t idx = 0; idx < io_chain.size(); idx++)
{ {
io_data_t *io = io_chain.at(idx); io_data_t *io = io_chain.at(idx);
int tmp; int tmp;
if( io->io_mode == IO_FD && io->fd == io->param1.old_fd )
{
continue;
}
switch( io->io_mode )
{
case IO_CLOSE:
{
if( close(io->fd) )
{
debug_safe_int( 0, "Failed to close file descriptor %s", io->fd );
wperror( L"close" );
}
break;
}
case IO_FILE: if( io->io_mode == IO_FD && io->fd == io->param1.old_fd )
{ {
// Here we definitely do not want to set CLO_EXEC because our child needs access continue;
if( (tmp=open( io->filename_cstr, }
io->param2.flags, OPEN_MASK ) )==-1 )
{
if( ( io->param2.flags & O_EXCL ) &&
( errno ==EEXIST ) )
{
debug_safe( 1, NOCLOB_ERROR, io->filename_cstr );
}
else
{
debug_safe( 1, FILE_ERROR, io->filename_cstr );
perror( "open" );
}
return -1;
}
else if( tmp != io->fd)
{
/*
This call will sometimes fail, but that is ok,
this is just a precausion.
*/
close(io->fd);
if(dup2( tmp, io->fd ) == -1 )
{
debug_safe_int( 1, FD_ERROR, io->fd );
perror( "dup2" );
return -1;
}
exec_close( tmp );
}
break;
}
case IO_FD:
{
/*
This call will sometimes fail, but that is ok,
this is just a precausion.
*/
close(io->fd);
if( dup2( io->param1.old_fd, io->fd ) == -1 ) switch( io->io_mode )
{ {
debug_safe_int( 1, FD_ERROR, io->fd ); case IO_CLOSE:
wperror( L"dup2" ); {
return -1; if( close(io->fd) )
} {
break; debug_safe_int( 0, "Failed to close file descriptor %s", io->fd );
} wperror( L"close" );
}
case IO_BUFFER: break;
case IO_PIPE: }
{
case IO_FILE:
{
// Here we definitely do not want to set CLO_EXEC because our child needs access
if( (tmp=open( io->filename_cstr,
io->param2.flags, OPEN_MASK ) )==-1 )
{
if( ( io->param2.flags & O_EXCL ) &&
( errno ==EEXIST ) )
{
debug_safe( 1, NOCLOB_ERROR, io->filename_cstr );
}
else
{
debug_safe( 1, FILE_ERROR, io->filename_cstr );
perror( "open" );
}
return -1;
}
else if( tmp != io->fd)
{
/*
This call will sometimes fail, but that is ok,
this is just a precausion.
*/
close(io->fd);
if(dup2( tmp, io->fd ) == -1 )
{
debug_safe_int( 1, FD_ERROR, io->fd );
perror( "dup2" );
return -1;
}
exec_close( tmp );
}
break;
}
case IO_FD:
{
/*
This call will sometimes fail, but that is ok,
this is just a precausion.
*/
close(io->fd);
if( dup2( io->param1.old_fd, io->fd ) == -1 )
{
debug_safe_int( 1, FD_ERROR, io->fd );
wperror( L"dup2" );
return -1;
}
break;
}
case IO_BUFFER:
case IO_PIPE:
{
/* If write_pipe_idx is 0, it means we're connecting to the read end (first pipe fd). If it's 1, we're connecting to the write end (second pipe fd). */ /* If write_pipe_idx is 0, it means we're connecting to the read end (first pipe fd). If it's 1, we're connecting to the write end (second pipe fd). */
unsigned int write_pipe_idx = (io->is_input ? 0 : 1); unsigned int write_pipe_idx = (io->is_input ? 0 : 1);
/* /*
debug( 0, debug( 0,
L"%ls %ls on fd %d (%d %d)", L"%ls %ls on fd %d (%d %d)",
write_pipe?L"write":L"read", write_pipe?L"write":L"read",
(io->io_mode == IO_BUFFER)?L"buffer":L"pipe", (io->io_mode == IO_BUFFER)?L"buffer":L"pipe",
io->fd, io->fd,
io->param1.pipe_fd[0], io->param1.pipe_fd[0],
io->param1.pipe_fd[1]); io->param1.pipe_fd[1]);
*/ */
if( dup2( io->param1.pipe_fd[write_pipe_idx], io->fd ) != io->fd ) if( dup2( io->param1.pipe_fd[write_pipe_idx], io->fd ) != io->fd )
{ {
debug_safe( 1, LOCAL_PIPE_ERROR ); debug_safe( 1, LOCAL_PIPE_ERROR );
perror( "dup2" ); perror( "dup2" );
return -1; return -1;
} }
if (io->param1.pipe_fd[0] >= 0) if (io->param1.pipe_fd[0] >= 0)
exec_close( io->param1.pipe_fd[0]); exec_close( io->param1.pipe_fd[0]);
if (io->param1.pipe_fd[1] >= 0) if (io->param1.pipe_fd[1] >= 0)
exec_close( io->param1.pipe_fd[1]); exec_close( io->param1.pipe_fd[1]);
break; break;
} }
} }
} }
return 0;
return 0;
} }
int setup_child_process( job_t *j, process_t *p ) int setup_child_process( job_t *j, process_t *p )
{ {
bool ok=true; bool ok=true;
if( p ) if( p )
{ {
ok = (0 == set_child_group( j, p, 1 )); ok = (0 == set_child_group( j, p, 1 ));
} }
if( ok ) if( ok )
{ {
ok = (0 == handle_child_io( j->io )); ok = (0 == handle_child_io( j->io ));
if( p != 0 && ! ok ) if( p != 0 && ! ok )
{ {
exit_without_destructors( 1 ); exit_without_destructors( 1 );
} }
} }
/* Set the handling for job control signals back to the default. */ /* Set the handling for job control signals back to the default. */
if( ok ) if( ok )
{ {
signal_reset_handlers(); signal_reset_handlers();
} }
/* Remove all signal blocks */ /* Remove all signal blocks */
signal_unblock(); signal_unblock();
return ok ? 0 : -1; return ok ? 0 : -1;
} }
int g_fork_count = 0; int g_fork_count = 0;
@ -318,47 +318,47 @@ int g_fork_count = 0;
pid_t execute_fork(bool wait_for_threads_to_die) pid_t execute_fork(bool wait_for_threads_to_die)
{ {
ASSERT_IS_MAIN_THREAD(); ASSERT_IS_MAIN_THREAD();
if (wait_for_threads_to_die) { if (wait_for_threads_to_die) {
/* Make sure we have no outstanding threads before we fork. This is a pretty sketchy thing to do here, both because exec.cpp shouldn't have to know about iothreads, and because the completion handlers may do unexpected things. */ /* Make sure we have no outstanding threads before we fork. This is a pretty sketchy thing to do here, both because exec.cpp shouldn't have to know about iothreads, and because the completion handlers may do unexpected things. */
iothread_drain_all(); iothread_drain_all();
} }
pid_t pid; pid_t pid;
struct timespec pollint; struct timespec pollint;
int i; int i;
g_fork_count++; g_fork_count++;
for( i=0; i<FORK_LAPS; i++ )
{
pid = fork();
if( pid >= 0)
{
return pid;
}
if( errno != EAGAIN )
{
break;
}
pollint.tv_sec = 0; for( i=0; i<FORK_LAPS; i++ )
pollint.tv_nsec = FORK_SLEEP_TIME; {
pid = fork();
if( pid >= 0)
{
return pid;
}
/* if( errno != EAGAIN )
Don't sleep on the final lap - sleeping might change the {
value of errno, which will break the error reporting below. break;
*/ }
if( i != FORK_LAPS-1 )
{ pollint.tv_sec = 0;
nanosleep( &pollint, NULL ); pollint.tv_nsec = FORK_SLEEP_TIME;
}
} /*
Don't sleep on the final lap - sleeping might change the
debug_safe( 0, FORK_ERROR ); value of errno, which will break the error reporting below.
wperror (L"fork"); */
FATAL_EXIT(); if( i != FORK_LAPS-1 )
{
nanosleep( &pollint, NULL );
}
}
debug_safe( 0, FORK_ERROR );
wperror (L"fork");
FATAL_EXIT();
return 0; return 0;
} }
@ -369,18 +369,18 @@ bool fork_actions_make_spawn_properties(posix_spawnattr_t *attr, posix_spawn_fil
if (posix_spawnattr_init(attr) != 0) { if (posix_spawnattr_init(attr) != 0) {
return false; return false;
} }
if (posix_spawn_file_actions_init(actions) != 0) { if (posix_spawn_file_actions_init(actions) != 0) {
posix_spawnattr_destroy(attr); posix_spawnattr_destroy(attr);
return false; return false;
} }
bool should_set_parent_group_id = false; bool should_set_parent_group_id = false;
int desired_parent_group_id = 0; int desired_parent_group_id = 0;
if (job_get_flag(j, JOB_CONTROL)) if (job_get_flag(j, JOB_CONTROL))
{ {
should_set_parent_group_id = true; should_set_parent_group_id = true;
// PCA: I'm quite fuzzy on process groups, // PCA: I'm quite fuzzy on process groups,
// but I believe that the default value of 0 // but I believe that the default value of 0
// means that the process becomes its own // means that the process becomes its own
@ -388,13 +388,13 @@ bool fork_actions_make_spawn_properties(posix_spawnattr_t *attr, posix_spawn_fil
// in this case. So we want this to be 0 if j->pgid is 0. // in this case. So we want this to be 0 if j->pgid is 0.
desired_parent_group_id = j->pgid; desired_parent_group_id = j->pgid;
} }
/* Set the handling for job control signals back to the default. */ /* Set the handling for job control signals back to the default. */
bool reset_signal_handlers = true; bool reset_signal_handlers = true;
/* Remove all signal blocks */ /* Remove all signal blocks */
bool reset_sigmask = true; bool reset_sigmask = true;
/* Set our flags */ /* Set our flags */
short flags = 0; short flags = 0;
if (reset_signal_handlers) if (reset_signal_handlers)
@ -403,7 +403,7 @@ bool fork_actions_make_spawn_properties(posix_spawnattr_t *attr, posix_spawn_fil
flags |= POSIX_SPAWN_SETSIGMASK; flags |= POSIX_SPAWN_SETSIGMASK;
if (should_set_parent_group_id) if (should_set_parent_group_id)
flags |= POSIX_SPAWN_SETPGROUP; flags |= POSIX_SPAWN_SETPGROUP;
int err = 0; int err = 0;
if (! err) if (! err)
err = posix_spawnattr_setflags(attr, flags); err = posix_spawnattr_setflags(attr, flags);
@ -418,16 +418,16 @@ bool fork_actions_make_spawn_properties(posix_spawnattr_t *attr, posix_spawn_fil
get_signals_with_handlers(&sigdefault); get_signals_with_handlers(&sigdefault);
err = posix_spawnattr_setsigdefault(attr, &sigdefault); err = posix_spawnattr_setsigdefault(attr, &sigdefault);
} }
/* No signals blocked */ /* No signals blocked */
sigset_t sigmask; sigset_t sigmask;
sigemptyset(&sigmask); sigemptyset(&sigmask);
if (! err && reset_sigmask) if (! err && reset_sigmask)
err = posix_spawnattr_setsigmask(attr, &sigmask); err = posix_spawnattr_setsigmask(attr, &sigmask);
/* Make sure that our pipes don't use an fd that the redirection itself wants to use */ /* Make sure that our pipes don't use an fd that the redirection itself wants to use */
free_redirected_fds_from_pipes(j->io); free_redirected_fds_from_pipes(j->io);
/* Close unused internal pipes */ /* Close unused internal pipes */
std::vector<int> files_to_close; std::vector<int> files_to_close;
get_unused_internal_pipes(files_to_close, j->io); get_unused_internal_pipes(files_to_close, j->io);
@ -435,22 +435,22 @@ bool fork_actions_make_spawn_properties(posix_spawnattr_t *attr, posix_spawn_fil
{ {
err = posix_spawn_file_actions_addclose(actions, files_to_close.at(i)); err = posix_spawn_file_actions_addclose(actions, files_to_close.at(i));
} }
for (size_t idx = 0; idx < j->io.size(); idx++) for (size_t idx = 0; idx < j->io.size(); idx++)
{ {
const io_data_t *io = j->io.at(idx); const io_data_t *io = j->io.at(idx);
if( io->io_mode == IO_FD && io->fd == io->param1.old_fd ) if( io->io_mode == IO_FD && io->fd == io->param1.old_fd )
{ {
continue; continue;
} }
if( io->fd > 2 ) if( io->fd > 2 )
{ {
/* Make sure the fd used by this redirection is not used by e.g. a pipe. */ /* Make sure the fd used by this redirection is not used by e.g. a pipe. */
// free_fd(io_chain, io->fd ); // free_fd(io_chain, io->fd );
// PCA I don't think we need to worry about this. fd redirection is pretty uncommon anyways. // PCA I don't think we need to worry about this. fd redirection is pretty uncommon anyways.
} }
switch (io->io_mode) switch (io->io_mode)
{ {
case IO_CLOSE: case IO_CLOSE:
@ -459,141 +459,141 @@ bool fork_actions_make_spawn_properties(posix_spawnattr_t *attr, posix_spawn_fil
err = posix_spawn_file_actions_addclose(actions, io->fd); err = posix_spawn_file_actions_addclose(actions, io->fd);
break; break;
} }
case IO_FILE: case IO_FILE:
{ {
if (! err) if (! err)
err = posix_spawn_file_actions_addopen(actions, io->fd, io->filename_cstr, io->param2.flags /* mode */, OPEN_MASK); err = posix_spawn_file_actions_addopen(actions, io->fd, io->filename_cstr, io->param2.flags /* mode */, OPEN_MASK);
break; break;
} }
case IO_FD: case IO_FD:
{ {
if (! err) if (! err)
err = posix_spawn_file_actions_adddup2(actions, io->param1.old_fd /* from */, io->fd /* to */); err = posix_spawn_file_actions_adddup2(actions, io->param1.old_fd /* from */, io->fd /* to */);
break; break;
} }
case IO_BUFFER: case IO_BUFFER:
case IO_PIPE: case IO_PIPE:
{ {
unsigned int write_pipe_idx = (io->is_input ? 0 : 1); unsigned int write_pipe_idx = (io->is_input ? 0 : 1);
int from_fd = io->param1.pipe_fd[write_pipe_idx]; int from_fd = io->param1.pipe_fd[write_pipe_idx];
int to_fd = io->fd; int to_fd = io->fd;
if (! err) if (! err)
err = posix_spawn_file_actions_adddup2(actions, from_fd, to_fd); err = posix_spawn_file_actions_adddup2(actions, from_fd, to_fd);
if( write_pipe_idx > 0 ) if( write_pipe_idx > 0 )
{ {
if (! err) if (! err)
err = posix_spawn_file_actions_addclose(actions, io->param1.pipe_fd[0]); err = posix_spawn_file_actions_addclose(actions, io->param1.pipe_fd[0]);
if (! err) if (! err)
err = posix_spawn_file_actions_addclose(actions, io->param1.pipe_fd[1]); err = posix_spawn_file_actions_addclose(actions, io->param1.pipe_fd[1]);
} }
else else
{ {
if (! err) if (! err)
err = posix_spawn_file_actions_addclose(actions, io->param1.pipe_fd[0]); err = posix_spawn_file_actions_addclose(actions, io->param1.pipe_fd[0]);
} }
break; break;
} }
} }
} }
/* Clean up on error */ /* Clean up on error */
if (err) { if (err) {
posix_spawnattr_destroy(attr); posix_spawnattr_destroy(attr);
posix_spawn_file_actions_destroy(actions); posix_spawn_file_actions_destroy(actions);
} }
return ! err; return ! err;
} }
#endif //FISH_USE_POSIX_SPAWN #endif //FISH_USE_POSIX_SPAWN
void safe_report_exec_error(int err, const char *actual_cmd, char **argv, char **envv) void safe_report_exec_error(int err, const char *actual_cmd, char **argv, char **envv)
{ {
debug_safe( 0, "Failed to execute process '%s'. Reason:", actual_cmd ); debug_safe( 0, "Failed to execute process '%s'. Reason:", actual_cmd );
switch( err ) switch( err )
{ {
case E2BIG: case E2BIG:
{ {
char sz1[128], sz2[128]; char sz1[128], sz2[128];
long arg_max = -1; long arg_max = -1;
size_t sz = 0;
char **p;
for(p=argv; *p; p++)
{
sz += strlen(*p)+1;
}
for(p=envv; *p; p++)
{
sz += strlen(*p)+1;
}
size_t sz = 0;
char **p;
for(p=argv; *p; p++)
{
sz += strlen(*p)+1;
}
for(p=envv; *p; p++)
{
sz += strlen(*p)+1;
}
format_size_safe(sz1, sz); format_size_safe(sz1, sz);
arg_max = sysconf( _SC_ARG_MAX ); arg_max = sysconf( _SC_ARG_MAX );
if( arg_max > 0 ) if( arg_max > 0 )
{ {
format_size_safe(sz2, sz); format_size_safe(sz2, sz);
debug_safe(0, "The total size of the argument and environment lists %s exceeds the operating system limit of %s.", sz1, sz2); debug_safe(0, "The total size of the argument and environment lists %s exceeds the operating system limit of %s.", sz1, sz2);
} }
else else
{ {
debug_safe( 0, "The total size of the argument and environment lists (%s) exceeds the operating system limit.", sz1); debug_safe( 0, "The total size of the argument and environment lists (%s) exceeds the operating system limit.", sz1);
} }
debug_safe(0, "Try running the command again with fewer arguments.");
break;
}
case ENOEXEC: debug_safe(0, "Try running the command again with fewer arguments.");
{ break;
}
case ENOEXEC:
{
/* Hope strerror doesn't allocate... */ /* Hope strerror doesn't allocate... */
const char *err = strerror(errno); const char *err = strerror(errno);
debug_safe(0, "exec: %s", err); debug_safe(0, "exec: %s", err);
debug_safe(0, "The file '%s' is marked as an executable but could not be run by the operating system.", actual_cmd);
break;
}
case ENOENT: debug_safe(0, "The file '%s' is marked as an executable but could not be run by the operating system.", actual_cmd);
{ break;
}
case ENOENT:
{
/* ENOENT is returned by exec() when the path fails, but also returned by posix_spawn if an open file action fails. These cases appear to be impossible to distinguish. We address this by not using posix_spawn for file redirections, so all the ENOENTs we find must be errors from exec(). */ /* ENOENT is returned by exec() when the path fails, but also returned by posix_spawn if an open file action fails. These cases appear to be impossible to distinguish. We address this by not using posix_spawn for file redirections, so all the ENOENTs we find must be errors from exec(). */
char interpreter_buff[128] = {}, *interpreter; char interpreter_buff[128] = {}, *interpreter;
interpreter = get_interpreter(actual_cmd, interpreter_buff, sizeof interpreter_buff); interpreter = get_interpreter(actual_cmd, interpreter_buff, sizeof interpreter_buff);
if( interpreter && 0 != access( interpreter, X_OK ) ) if( interpreter && 0 != access( interpreter, X_OK ) )
{ {
debug_safe(0, "The file '%s' specified the interpreter '%s', which is not an executable command.", actual_cmd, interpreter ); debug_safe(0, "The file '%s' specified the interpreter '%s', which is not an executable command.", actual_cmd, interpreter );
} }
else else
{ {
debug_safe(0, "The file '%s' does not exist or could not be executed.", actual_cmd); debug_safe(0, "The file '%s' does not exist or could not be executed.", actual_cmd);
} }
break; break;
} }
case ENOMEM: case ENOMEM:
{ {
debug_safe(0, "Out of memory"); debug_safe(0, "Out of memory");
break; break;
} }
default: default:
{ {
/* Hope strerror doesn't allocate... */ /* Hope strerror doesn't allocate... */
const char *err = strerror(errno); const char *err = strerror(errno);
debug_safe(0, "exec: %s", err); debug_safe(0, "exec: %s", err);
// debug(0, L"The file '%ls' is marked as an executable but could not be run by the operating system.", p->actual_cmd); // debug(0, L"The file '%ls' is marked as an executable but could not be run by the operating system.", p->actual_cmd);
break; break;
} }
} }
} }

View file

@ -1,6 +1,6 @@
/** \file postfork.h /** \file postfork.h
Functions that we may safely call after fork(), of which there are very few. In particular we cannot allocate memory, since we're insane enough to call fork from a multithreaded process. Functions that we may safely call after fork(), of which there are very few. In particular we cannot allocate memory, since we're insane enough to call fork from a multithreaded process.
*/ */
#ifndef FISH_POSTFORK_H #ifndef FISH_POSTFORK_H
@ -38,7 +38,7 @@
exit. The parent process may safely ignore the exit status of this exit. The parent process may safely ignore the exit status of this
call. call.
Returns 0 on sucess, -1 on failiure. Returns 0 on sucess, -1 on failiure.
*/ */
int set_child_group( job_t *j, process_t *p, int print_errors ); int set_child_group( job_t *j, process_t *p, int print_errors );

View file

@ -1,6 +1,6 @@
/** \file print_help.c /** \file print_help.c
Print help message for the specified command Print help message for the specified command
*/ */
#include <stdlib.h> #include <stdlib.h>
@ -19,16 +19,16 @@ ssize_t write_loop(int fd, const char *buff, size_t count);
void print_help( const char *c, int fd ) void print_help( const char *c, int fd )
{ {
char cmd[ CMD_LEN]; char cmd[ CMD_LEN];
int printed = snprintf( cmd, CMD_LEN, "fish -c '__fish_print_help %s >&%d'", c, fd ); int printed = snprintf( cmd, CMD_LEN, "fish -c '__fish_print_help %s >&%d'", c, fd );
if( printed < CMD_LEN ) if( printed < CMD_LEN )
{ {
if( (system( cmd ) == -1) ) if( (system( cmd ) == -1) )
{ {
write_loop(2, HELP_ERR, strlen(HELP_ERR)); write_loop(2, HELP_ERR, strlen(HELP_ERR));
} }
} }
} }

View file

@ -1,13 +1,13 @@
/** \file print_help.h /** \file print_help.h
Print help message for the specified command Print help message for the specified command
*/ */
#ifndef FISH_PRINT_HELP_H #ifndef FISH_PRINT_HELP_H
#define FISH_PRINT_HELP_H #define FISH_PRINT_HELP_H
/** /**
Print help message for the specified command Print help message for the specified command
*/ */
void print_help( const char *cmd, int fd ); void print_help( const char *cmd, int fd );

1586
proc.cpp

File diff suppressed because it is too large Load diff

356
proc.h
View file

@ -1,11 +1,11 @@
/** \file proc.h /** \file proc.h
Prototypes for utilities for keeping track of jobs, processes and subshells, as Prototypes for utilities for keeping track of jobs, processes and subshells, as
well as signal handling functions for tracking children. These well as signal handling functions for tracking children. These
functions do not themselves launch new processes, the exec library functions do not themselves launch new processes, the exec library
will call proc to create representations of the running jobs as will call proc to create representations of the running jobs as
needed. needed.
*/ */
#ifndef FISH_PROC_H #ifndef FISH_PROC_H
@ -56,194 +56,194 @@
*/ */
enum enum
{ {
/** /**
A regular external command A regular external command
*/ */
EXTERNAL, EXTERNAL,
/** /**
A builtin command A builtin command
*/ */
INTERNAL_BUILTIN, INTERNAL_BUILTIN,
/** /**
A shellscript function A shellscript function
*/ */
INTERNAL_FUNCTION, INTERNAL_FUNCTION,
/** /**
A block of commands A block of commands
*/ */
INTERNAL_BLOCK, INTERNAL_BLOCK,
/** /**
The exec builtin The exec builtin
*/ */
INTERNAL_EXEC, INTERNAL_EXEC,
/** /**
A buffer A buffer
*/ */
INTERNAL_BUFFER, INTERNAL_BUFFER,
} }
; ;
enum enum
{ {
JOB_CONTROL_ALL, JOB_CONTROL_ALL,
JOB_CONTROL_INTERACTIVE, JOB_CONTROL_INTERACTIVE,
JOB_CONTROL_NONE, JOB_CONTROL_NONE,
} }
; ;
/** /**
A structure representing a single fish process. Contains variables A structure representing a single fish process. Contains variables
for tracking process state and the process argument for tracking process state and the process argument
list. Actually, a fish process can be either a regular externa list. Actually, a fish process can be either a regular externa
lrocess, an internal builtin which may or may not spawn a fake IO lrocess, an internal builtin which may or may not spawn a fake IO
process during execution, a shellscript function or a block of process during execution, a shellscript function or a block of
commands to be evaluated by calling eval. Lastly, this process can commands to be evaluated by calling eval. Lastly, this process can
be the result of an exec command. The role of this process_t is be the result of an exec command. The role of this process_t is
determined by the type field, which can be one of EXTERNAL, determined by the type field, which can be one of EXTERNAL,
INTERNAL_BUILTIN, INTERNAL_FUNCTION, INTERNAL_BLOCK and INTERNAL_BUILTIN, INTERNAL_FUNCTION, INTERNAL_BLOCK and
INTERNAL_EXEC, INTERNAL_BUFFER INTERNAL_EXEC, INTERNAL_BUFFER
The process_t contains information on how the process should be The process_t contains information on how the process should be
started, such as command name and arguments, as well as runtime started, such as command name and arguments, as well as runtime
information on the status of the actual physical process which information on the status of the actual physical process which
represents it. Shellscript functions, builtins and blocks of code represents it. Shellscript functions, builtins and blocks of code
may all need to spawn an external process that handles the piping may all need to spawn an external process that handles the piping
and redirecting of IO for them. and redirecting of IO for them.
If the process is of type EXTERNAL or INTERNAL_EXEC, argv is the If the process is of type EXTERNAL or INTERNAL_EXEC, argv is the
argument array and actual_cmd is the absolute path of the command argument array and actual_cmd is the absolute path of the command
to execute. to execute.
If the process is of type INTERNAL_BUILTIN, argv is the argument If the process is of type INTERNAL_BUILTIN, argv is the argument
vector, and argv[0] is the name of the builtin command. vector, and argv[0] is the name of the builtin command.
If the process is of type INTERNAL_FUNCTION, argv is the argument If the process is of type INTERNAL_FUNCTION, argv is the argument
vector, and argv[0] is the name of the shellscript function. vector, and argv[0] is the name of the shellscript function.
If the process is of type INTERNAL_BLOCK, argv has exactly one If the process is of type INTERNAL_BLOCK, argv has exactly one
element, which is the block of commands to execute. element, which is the block of commands to execute.
*/ */
class process_t class process_t
{ {
private: private:
null_terminated_array_t<wchar_t> argv_array; null_terminated_array_t<wchar_t> argv_array;
/* narrow copy of argv0 so we don't have to convert after fork */ /* narrow copy of argv0 so we don't have to convert after fork */
narrow_string_rep_t argv0_narrow; narrow_string_rep_t argv0_narrow;
/* No copying */ /* No copying */
process_t(const process_t &rhs); process_t(const process_t &rhs);
void operator=(const process_t &rhs); void operator=(const process_t &rhs);
public: public:
process_t(); process_t();
~process_t(); ~process_t();
/** /**
Type of process. Can be one of \c EXTERNAL, \c Type of process. Can be one of \c EXTERNAL, \c
INTERNAL_BUILTIN, \c INTERNAL_FUNCTION, \c INTERNAL_BLOCK, INTERNAL_BUILTIN, \c INTERNAL_FUNCTION, \c INTERNAL_BLOCK,
INTERNAL_EXEC, or INTERNAL_BUFFER INTERNAL_EXEC, or INTERNAL_BUFFER
*/ */
int type; int type;
/** Sets argv */ /** Sets argv */
void set_argv(const wcstring_list_t &argv) { void set_argv(const wcstring_list_t &argv) {
argv_array.set(argv); argv_array.set(argv);
argv0_narrow.set(argv.empty() ? L"" : argv[0]); argv0_narrow.set(argv.empty() ? L"" : argv[0]);
} }
/** Returns argv */ /** Returns argv */
const wchar_t * const *get_argv(void) const { return argv_array.get(); } const wchar_t * const *get_argv(void) const { return argv_array.get(); }
const null_terminated_array_t<wchar_t> &get_argv_array(void) const { return argv_array; } const null_terminated_array_t<wchar_t> &get_argv_array(void) const { return argv_array; }
/** Returns argv[idx] */ /** Returns argv[idx] */
const wchar_t *argv(size_t idx) const { const wchar_t *argv(size_t idx) const {
const wchar_t * const *argv = argv_array.get(); const wchar_t * const *argv = argv_array.get();
assert(argv != NULL); assert(argv != NULL);
return argv[idx]; return argv[idx];
} }
/** Returns argv[0], or NULL */ /** Returns argv[0], or NULL */
const wchar_t *argv0(void) const { const wchar_t *argv0(void) const {
const wchar_t * const *argv = argv_array.get(); const wchar_t * const *argv = argv_array.get();
return argv ? argv[0] : NULL; return argv ? argv[0] : NULL;
} }
/** Returns argv[0] as a char * */ /** Returns argv[0] as a char * */
const char *argv0_cstr(void) const { const char *argv0_cstr(void) const {
return argv0_narrow.get(); return argv0_narrow.get();
} }
/** actual command to pass to exec in case of EXTERNAL or INTERNAL_EXEC. */ /** actual command to pass to exec in case of EXTERNAL or INTERNAL_EXEC. */
wcstring actual_cmd; wcstring actual_cmd;
/** process ID */ /** process ID */
pid_t pid; pid_t pid;
/** File descriptor that pipe output should bind to */ /** File descriptor that pipe output should bind to */
int pipe_write_fd; int pipe_write_fd;
/** File descriptor that the _next_ process pipe input should bind to */ /** File descriptor that the _next_ process pipe input should bind to */
int pipe_read_fd; int pipe_read_fd;
/** true if process has completed */ /** true if process has completed */
volatile int completed; volatile int completed;
/** true if process has stopped */ /** true if process has stopped */
volatile int stopped; volatile int stopped;
/** reported status value */ /** reported status value */
volatile int status; volatile int status;
/** Special flag to tell the evaluation function for count to print the help information */ /** Special flag to tell the evaluation function for count to print the help information */
int count_help_magic; int count_help_magic;
/** Next process in pipeline. We own this and we are responsible for deleting it. */ /** Next process in pipeline. We own this and we are responsible for deleting it. */
process_t *next; process_t *next;
#ifdef HAVE__PROC_SELF_STAT #ifdef HAVE__PROC_SELF_STAT
/** Last time of cpu time check */ /** Last time of cpu time check */
struct timeval last_time; struct timeval last_time;
/** Number of jiffies spent in process at last cpu time check */ /** Number of jiffies spent in process at last cpu time check */
unsigned long last_jiffies; unsigned long last_jiffies;
#endif #endif
}; };
/* Constants for the flag variable in the job struct */ /* Constants for the flag variable in the job struct */
enum { enum {
/** true if user was told about stopped job */ /** true if user was told about stopped job */
JOB_NOTIFIED = 1 << 0, JOB_NOTIFIED = 1 << 0,
/** Whether this job is in the foreground */ /** Whether this job is in the foreground */
JOB_FOREGROUND = 1 << 1, JOB_FOREGROUND = 1 << 1,
/** /**
Whether the specified job is completely constructed, Whether the specified job is completely constructed,
i.e. completely parsed, and every process in the job has been i.e. completely parsed, and every process in the job has been
forked, etc. forked, etc.
*/ */
JOB_CONSTRUCTED = 1 << 2, JOB_CONSTRUCTED = 1 << 2,
/** Whether the specified job is a part of a subshell, event handler or some other form of special job that should not be reported */ /** Whether the specified job is a part of a subshell, event handler or some other form of special job that should not be reported */
JOB_SKIP_NOTIFICATION = 1 << 3, JOB_SKIP_NOTIFICATION = 1 << 3,
/** Should the exit status be negated? This flag can only be set by the not builtin. */ /** Should the exit status be negated? This flag can only be set by the not builtin. */
JOB_NEGATE = 1 << 4, JOB_NEGATE = 1 << 4,
/** Should the exit status be used to reevaluate the condition in an if block? This is only used by elseif and is a big hack. */ /** Should the exit status be used to reevaluate the condition in an if block? This is only used by elseif and is a big hack. */
JOB_ELSEIF = 1 << 5, JOB_ELSEIF = 1 << 5,
/** This flag is set to one on wildcard expansion errors. It means that the current command should not be executed */ /** This flag is set to one on wildcard expansion errors. It means that the current command should not be executed */
JOB_WILDCARD_ERROR = 1 << 6, JOB_WILDCARD_ERROR = 1 << 6,
/** Skip executing this job. This flag is set by the short-circut builtins, i.e. and and or */ /** Skip executing this job. This flag is set by the short-circut builtins, i.e. and and or */
JOB_SKIP = 1 << 7, JOB_SKIP = 1 << 7,
/** Whether the job is under job control */ /** Whether the job is under job control */
JOB_CONTROL = 1 << 8, JOB_CONTROL = 1 << 8,
@ -251,7 +251,7 @@ enum {
JOB_TERMINAL = 1 << 9 JOB_TERMINAL = 1 << 9
}; };
/** /**
A struct represeting a job. A job is basically a pipeline of one A struct represeting a job. A job is basically a pipeline of one
or more processes and a couple of flags. or more processes and a couple of flags.
*/ */
@ -261,105 +261,105 @@ void release_job_id(job_id_t jobid);
class job_t class job_t
{ {
/** /**
The original command which led to the creation of this The original command which led to the creation of this
job. It is used for displaying messages about job status job. It is used for displaying messages about job status
on the terminal. on the terminal.
*/ */
wcstring command_str; wcstring command_str;
/* narrow copy so we don't have to convert after fork */ /* narrow copy so we don't have to convert after fork */
narrow_string_rep_t command_narrow; narrow_string_rep_t command_narrow;
/* No copying */ /* No copying */
job_t(const job_t &rhs); job_t(const job_t &rhs);
void operator=(const job_t &); void operator=(const job_t &);
public: public:
job_t(job_id_t jobid); job_t(job_id_t jobid);
~job_t(); ~job_t();
/** Returns whether the command is empty. */ /** Returns whether the command is empty. */
bool command_is_empty() const { return command_str.empty(); } bool command_is_empty() const { return command_str.empty(); }
/** Returns the command as a wchar_t *. */ /** Returns the command as a wchar_t *. */
const wchar_t *command_wcstr() const { return command_str.c_str(); } const wchar_t *command_wcstr() const { return command_str.c_str(); }
/** Returns the command */ /** Returns the command */
const wcstring &command() const { return command_str; } const wcstring &command() const { return command_str; }
/** Returns the command as a char *. */ /** Returns the command as a char *. */
const char *command_cstr() const { return command_narrow.get(); } const char *command_cstr() const { return command_narrow.get(); }
/** Sets the command */ /** Sets the command */
void set_command(const wcstring &cmd) { void set_command(const wcstring &cmd) {
command_str = cmd; command_str = cmd;
command_narrow.set(cmd); command_narrow.set(cmd);
} }
/**
A linked list of all the processes in this job. We are responsible for deleting this when we are deallocated.
*/
process_t *first_process;
/**
process group ID for the process group that this job is
running in.
*/
pid_t pgid;
/**
The saved terminal modes of this job. This needs to be
saved so that we can restore the terminal to the same
state after temporarily taking control over the terminal
when a job stops.
*/
struct termios tmodes;
/**
The job id of the job. This is a small integer that is a
unique identifier of the job within this shell, and is
used e.g. in process expansion.
*/
const job_id_t job_id;
/** List of all IO redirections for this job. */
io_chain_t io;
/** /**
Bitset containing information about the job. A combination of the JOB_* constants. A linked list of all the processes in this job. We are responsible for deleting this when we are deallocated.
*/ */
unsigned int flags; process_t *first_process;
/**
process group ID for the process group that this job is
running in.
*/
pid_t pgid;
/**
The saved terminal modes of this job. This needs to be
saved so that we can restore the terminal to the same
state after temporarily taking control over the terminal
when a job stops.
*/
struct termios tmodes;
/**
The job id of the job. This is a small integer that is a
unique identifier of the job within this shell, and is
used e.g. in process expansion.
*/
const job_id_t job_id;
/** List of all IO redirections for this job. */
io_chain_t io;
/**
Bitset containing information about the job. A combination of the JOB_* constants.
*/
unsigned int flags;
}; };
/** /**
Whether we are running a subshell command Whether we are running a subshell command
*/ */
extern int is_subshell; extern int is_subshell;
/** /**
Whether we are running a block of commands Whether we are running a block of commands
*/ */
extern int is_block; extern int is_block;
/** /**
Whether we are reading from the keyboard right now Whether we are reading from the keyboard right now
*/ */
int get_is_interactive(void); int get_is_interactive(void);
/** /**
Whether this shell is attached to the keyboard at all Whether this shell is attached to the keyboard at all
*/ */
extern int is_interactive_session; extern int is_interactive_session;
/** /**
Whether we are a login shell Whether we are a login shell
*/ */
extern int is_login; extern int is_login;
/** /**
Whether we are running an event handler Whether we are running an event handler
*/ */
extern int is_event; extern int is_event;
@ -375,9 +375,9 @@ class job_iterator_t {
job_list_t * const job_list; job_list_t * const job_list;
job_list_t::iterator current, end; job_list_t::iterator current, end;
public: public:
void reset(void); void reset(void);
job_t *next() { job_t *next() {
job_t *job = NULL; job_t *job = NULL;
if (current != end) { if (current != end) {
@ -386,7 +386,7 @@ class job_iterator_t {
} }
return job; return job;
} }
job_iterator_t(job_list_t &jobs); job_iterator_t(job_list_t &jobs);
job_iterator_t(); job_iterator_t();
}; };
@ -471,7 +471,7 @@ job_t *job_get(job_id_t id);
job_t *job_get_from_pid(int pid); job_t *job_get_from_pid(int pid);
/** /**
Tests if the job is stopped Tests if the job is stopped
*/ */
int job_is_stopped( const job_t *j ); int job_is_stopped( const job_t *j );

3594
reader.cpp

File diff suppressed because it is too large Load diff

View file

@ -1,9 +1,9 @@
/** \file reader.h /** \file reader.h
Prototypes for functions for reading data from stdin and passing Prototypes for functions for reading data from stdin and passing
to the parser. If stdin is a keyboard, it supplies a killring, to the parser. If stdin is a keyboard, it supplies a killring,
history, syntax highlighting, tab-completion and various other history, syntax highlighting, tab-completion and various other
features. features.
*/ */
#ifndef FISH_READER_H #ifndef FISH_READER_H
@ -53,7 +53,7 @@ const wchar_t *reader_current_filename();
/** /**
Push a new filename on the stack of read files Push a new filename on the stack of read files
\param fn The fileanme to push \param fn The fileanme to push
*/ */
void reader_push_current_filename( const wchar_t *fn ); void reader_push_current_filename( const wchar_t *fn );
@ -125,7 +125,7 @@ int reader_interrupted();
const wchar_t *reader_readline(); const wchar_t *reader_readline();
/** /**
Push a new reader environment. Push a new reader environment.
*/ */
void reader_push( const wchar_t *name ); void reader_push( const wchar_t *name );
@ -135,7 +135,7 @@ void reader_push( const wchar_t *name );
void reader_pop(); void reader_pop();
/** /**
Specify function to use for finding possible tab completions. The function must take these arguments: Specify function to use for finding possible tab completions. The function must take these arguments:
- The command to be completed as a null terminated array of wchar_t - The command to be completed as a null terminated array of wchar_t
- An array_list_t in which completions will be inserted. - An array_list_t in which completions will be inserted.
@ -151,7 +151,7 @@ typedef void (*highlight_function_t)( const wcstring &, std::vector<int> &, size
/** /**
Specify function for syntax highlighting. The function must take these arguments: Specify function for syntax highlighting. The function must take these arguments:
- The command to be highlighted as a null terminated array of wchar_t - The command to be highlighted as a null terminated array of wchar_t
- The color code of each character as an array of ints - The color code of each character as an array of ints
- The cursor position - The cursor position
@ -178,7 +178,7 @@ void reader_set_left_prompt( const wcstring &prompt );
void reader_set_right_prompt( const wcstring &prompt ); void reader_set_right_prompt( const wcstring &prompt );
/** /**
Returns true if the shell is exiting, 0 otherwise. Returns true if the shell is exiting, 0 otherwise.
*/ */
int exit_status(); int exit_status();

View file

@ -1,5 +1,5 @@
/** \file sanity.c /** \file sanity.c
Functions for performing sanity checks on the program state Functions for performing sanity checks on the program state
*/ */
#include "config.h" #include "config.h"
@ -34,43 +34,43 @@ static int insane;
void sanity_lose() void sanity_lose()
{ {
debug( 0, _(L"Errors detected, shutting down. Break on sanity_lose() to debug.") ); debug( 0, _(L"Errors detected, shutting down. Break on sanity_lose() to debug.") );
insane = 1; insane = 1;
} }
int sanity_check() int sanity_check()
{ {
if( !insane ) if( !insane )
if( get_is_interactive() ) if( get_is_interactive() )
history_sanity_check(); history_sanity_check();
if( !insane ) if( !insane )
reader_sanity_check(); reader_sanity_check();
if( !insane ) if( !insane )
kill_sanity_check(); kill_sanity_check();
if( !insane ) if( !insane )
proc_sanity_check(); proc_sanity_check();
return insane; return insane;
} }
void validate_pointer( const void *ptr, const wchar_t *err, int null_ok ) void validate_pointer( const void *ptr, const wchar_t *err, int null_ok )
{ {
/* /*
Test if the pointer data crosses a segment boundary. Test if the pointer data crosses a segment boundary.
*/ */
if( (0x00000003l & (intptr_t)ptr) != 0 ) if( (0x00000003l & (intptr_t)ptr) != 0 )
{ {
debug( 0, _(L"The pointer '%ls' is invalid"), err ); debug( 0, _(L"The pointer '%ls' is invalid"), err );
sanity_lose(); sanity_lose();
} }
if((!null_ok) && (ptr==0)) if((!null_ok) && (ptr==0))
{ {
debug( 0, _(L"The pointer '%ls' is null"), err ); debug( 0, _(L"The pointer '%ls' is null"), err );
sanity_lose(); sanity_lose();
} }
} }

View file

@ -1,5 +1,5 @@
/** \file sanity.h /** \file sanity.h
Prototypes for functions for performing sanity checks on the program state Prototypes for functions for performing sanity checks on the program state
*/ */
#ifndef FISH_SANITY_H #ifndef FISH_SANITY_H
@ -19,7 +19,7 @@ int sanity_check();
/** /**
Try and determine if ptr is a valid pointer. If not, loose sanity. Try and determine if ptr is a valid pointer. If not, loose sanity.
\param ptr The pointer to validate \param ptr The pointer to validate
\param err A description of what the pointer refers to, for use in error messages \param err A description of what the pointer refers to, for use in error messages
\param null_ok Wheter the pointer is allowed to point to 0 \param null_ok Wheter the pointer is allowed to point to 0

1128
screen.cpp

File diff suppressed because it is too large Load diff

View file

@ -1,13 +1,13 @@
/** \file screen.h High level library for handling the terminal screen /** \file screen.h High level library for handling the terminal screen
The screen library allows the interactive reader to write its The screen library allows the interactive reader to write its
output to screen efficiently by keeping an inetrnal representation output to screen efficiently by keeping an inetrnal representation
of the current screen contents and trying to find a reasonably of the current screen contents and trying to find a reasonably
efficient way for transforming that to the desired screen content. efficient way for transforming that to the desired screen content.
The current implementation is less smart than ncurses allows The current implementation is less smart than ncurses allows
and can not for example move blocks of text around to handle text and can not for example move blocks of text around to handle text
insertion. insertion.
*/ */
#ifndef FISH_SCREEN_H #ifndef FISH_SCREEN_H
#define FISH_SCREEN_H #define FISH_SCREEN_H
@ -22,38 +22,38 @@ struct line_t
std::vector<wchar_t> text; std::vector<wchar_t> text;
std::vector<int> colors; std::vector<int> colors;
bool is_soft_wrapped; bool is_soft_wrapped;
line_t() : text(), colors(), is_soft_wrapped(false) line_t() : text(), colors(), is_soft_wrapped(false)
{ {
} }
void clear(void) void clear(void)
{ {
text.clear(); text.clear();
colors.clear(); colors.clear();
} }
void append(wchar_t txt, int color) void append(wchar_t txt, int color)
{ {
text.push_back(txt); text.push_back(txt);
colors.push_back(color); colors.push_back(color);
} }
size_t size(void) const size_t size(void) const
{ {
return text.size(); return text.size();
} }
wchar_t char_at(size_t idx) const wchar_t char_at(size_t idx) const
{ {
return text.at(idx); return text.at(idx);
} }
int color_at(size_t idx) const int color_at(size_t idx) const
{ {
return colors.at(idx); return colors.at(idx);
} }
}; };
/** /**
@ -62,36 +62,36 @@ struct line_t
class screen_data_t class screen_data_t
{ {
std::vector<line_t> line_datas; std::vector<line_t> line_datas;
public: public:
struct cursor_t { struct cursor_t {
int x; int x;
int y; int y;
cursor_t() : x(0), y(0) { } cursor_t() : x(0), y(0) { }
cursor_t(int a, int b) : x(a), y(b) { } cursor_t(int a, int b) : x(a), y(b) { }
} cursor; } cursor;
line_t &add_line(void) { line_t &add_line(void) {
line_datas.resize(line_datas.size() + 1); line_datas.resize(line_datas.size() + 1);
return line_datas.back(); return line_datas.back();
} }
void resize(size_t size) { void resize(size_t size) {
line_datas.resize(size); line_datas.resize(size);
} }
line_t &create_line(size_t idx) { line_t &create_line(size_t idx) {
if (idx >= line_datas.size()) { if (idx >= line_datas.size()) {
line_datas.resize(idx + 1); line_datas.resize(idx + 1);
} }
return line_datas.at(idx); return line_datas.at(idx);
} }
line_t &line(size_t idx) { line_t &line(size_t idx) {
return line_datas.at(idx); return line_datas.at(idx);
} }
size_t line_count(void) { size_t line_count(void) {
return line_datas.size(); return line_datas.size();
} }
@ -121,7 +121,7 @@ class screen_t
the screen. the screen.
*/ */
wcstring actual_left_prompt; wcstring actual_left_prompt;
/** Last right prompt width */ /** Last right prompt width */
size_t last_right_prompt_width; size_t last_right_prompt_width;
@ -130,26 +130,26 @@ class screen_t
write. write.
*/ */
int actual_width; int actual_width;
/** If we support soft wrapping, we can output to this location without any cursor motion. */ /** If we support soft wrapping, we can output to this location without any cursor motion. */
screen_data_t::cursor_t soft_wrap_location; screen_data_t::cursor_t soft_wrap_location;
/** /**
This flag is set to true when there is reason to suspect that This flag is set to true when there is reason to suspect that
the parts of the screen lines where the actual content is not the parts of the screen lines where the actual content is not
filled in may be non-empty. This means that a clr_eol command filled in may be non-empty. This means that a clr_eol command
has to be sent to the terminal at the end of each line. has to be sent to the terminal at the end of each line.
*/ */
bool need_clear; bool need_clear;
/** If we need to clear, this is how many lines the actual screen had, before we reset it. This is used when resizing the window larger: if the cursor jumps to the line above, we need to remember to clear the subsequent lines. */ /** If we need to clear, this is how many lines the actual screen had, before we reset it. This is used when resizing the window larger: if the cursor jumps to the line above, we need to remember to clear the subsequent lines. */
size_t actual_lines_before_reset; size_t actual_lines_before_reset;
/** /**
These status buffers are used to check if any output has occurred These status buffers are used to check if any output has occurred
other than from fish's main loop, in which case we need to redraw. other than from fish's main loop, in which case we need to redraw.
*/ */
struct stat prev_buff_1, prev_buff_2, post_buff_1, post_buff_2; struct stat prev_buff_1, prev_buff_2, post_buff_1, post_buff_2;
}; };
/** /**
@ -158,7 +158,7 @@ class screen_t
will use it's knowlege of the current contents of the screen in will use it's knowlege of the current contents of the screen in
order to render the desired output using as few terminal commands order to render the desired output using as few terminal commands
as possible. as possible.
\param s the screen on which to write \param s the screen on which to write
\param left_prompt the prompt to prepend to the command line \param left_prompt the prompt to prepend to the command line
\param right_prompt the right prompt, or NULL if none \param right_prompt the right prompt, or NULL if none
@ -168,14 +168,14 @@ class screen_t
\param indent the indent to use for the command line \param indent the indent to use for the command line
\param cursor_pos where the cursor is \param cursor_pos where the cursor is
*/ */
void s_write( screen_t *s, void s_write( screen_t *s,
const wchar_t *left_prompt, const wchar_t *left_prompt,
const wchar_t *right_prompt, const wchar_t *right_prompt,
const wchar_t *commandline, const wchar_t *commandline,
size_t explicit_len, size_t explicit_len,
const int *colors, const int *colors,
const int *indent, const int *indent,
size_t cursor_pos ); size_t cursor_pos );
void s_write( screen_t *s, void s_write( screen_t *s,
@ -187,7 +187,7 @@ void s_write( screen_t *s,
const int *indent, const int *indent,
size_t cursor_pos ); size_t cursor_pos );
/** /**
This function resets the screen buffers internal knowledge about This function resets the screen buffers internal knowledge about
the contents of the screen. Use this function when some other the contents of the screen. Use this function when some other
function than s_write has written to the screen. function than s_write has written to the screen.

View file

@ -70,53 +70,53 @@
const char *col[]= const char *col[]=
{ {
"black", "black",
"red", "red",
"green", "green",
"brown", "brown",
"yellow", "yellow",
"blue", "blue",
"magenta", "magenta",
"purple", "purple",
"cyan", "cyan",
"white", "white",
"normal" "normal"
}; };
const int col_idx[]= const int col_idx[]=
{ {
0, 0,
1, 1,
2, 2,
3, 3,
3, 3,
4, 4,
5, 5,
5, 5,
6, 6,
7, 7,
8 8
}; };
void print_colors() void print_colors()
{ {
size_t i; size_t i;
for( i=0; i<COLORS; i++ ) for( i=0; i<COLORS; i++ )
{ {
printf( "%s\n", col[i] ); printf( "%s\n", col[i] );
} }
} }
static void check_locale_init() static void check_locale_init()
{ {
static int is_init = 0; static int is_init = 0;
if( is_init ) if( is_init )
return; return;
is_init = 1; is_init = 1;
setlocale( LC_ALL, "" ); setlocale( LC_ALL, "" );
bindtextdomain( PACKAGE_NAME, LOCALEDIR ); bindtextdomain( PACKAGE_NAME, LOCALEDIR );
textdomain( PACKAGE_NAME ); textdomain( PACKAGE_NAME );
} }
/* A lot of this code is taken straight from output.cpp; it sure would be nice to factor these together. */ /* A lot of this code is taken straight from output.cpp; it sure would be nice to factor these together. */
@ -185,111 +185,111 @@ int main( int argc, char **argv )
/* Some code passes variables to set_color that don't exist, like $fish_user_whatever. As a hack, quietly return failure. */ /* Some code passes variables to set_color that don't exist, like $fish_user_whatever. As a hack, quietly return failure. */
if (argc <= 1) if (argc <= 1)
return EXIT_FAILURE; return EXIT_FAILURE;
char *bgcolor=0;
char *fgcolor=0;
bool bold=false;
bool underline=false;
while( 1 ) char *bgcolor=0;
{ char *fgcolor=0;
static struct option bool bold=false;
long_options[] = bool underline=false;
{
{
"background", required_argument, 0, 'b'
}
,
{
"help", no_argument, 0, 'h'
}
,
{
"bold", no_argument, 0, 'o'
}
,
{
"underline", no_argument, 0, 'u'
}
,
{
"version", no_argument, 0, 'v'
}
,
{
"print-colors", no_argument, 0, 'c'
}
,
{
0, 0, 0, 0
}
}
;
int opt_index = 0;
int opt = getopt_long( argc,
argv,
GETOPT_STRING,
long_options,
&opt_index );
if( opt == -1 ) while( 1 )
break; {
static struct option
switch( opt ) long_options[] =
{ {
case 0: {
break; "background", required_argument, 0, 'b'
}
case 'b': ,
bgcolor = optarg; {
break; "help", no_argument, 0, 'h'
case 'h': }
print_help( argv[0], 1 ); ,
exit(0); {
"bold", no_argument, 0, 'o'
case 'o': }
bold=true; ,
break; {
"underline", no_argument, 0, 'u'
case 'u': }
underline=true; ,
break; {
"version", no_argument, 0, 'v'
case 'v': }
check_locale_init(); ,
fprintf( stderr, _("%s, version %s\n"), SET_COLOR, PACKAGE_VERSION ); {
exit( 0 ); "print-colors", no_argument, 0, 'c'
}
,
{
0, 0, 0, 0
}
}
;
int opt_index = 0;
int opt = getopt_long( argc,
argv,
GETOPT_STRING,
long_options,
&opt_index );
if( opt == -1 )
break;
switch( opt )
{
case 0:
break;
case 'b':
bgcolor = optarg;
break;
case 'h':
print_help( argv[0], 1 );
exit(0);
case 'o':
bold=true;
break;
case 'u':
underline=true;
break;
case 'v':
check_locale_init();
fprintf( stderr, _("%s, version %s\n"), SET_COLOR, PACKAGE_VERSION );
exit( 0 );
case 'c':
print_colors();
exit(0);
case '?':
return 1;
}
}
switch( argc-optind)
{
case 0:
// printf( "no fg\n" );
break;
case 1:
fgcolor=argv[optind];
// printf( "fg %s\n", fgcolor );
break;
default:
check_locale_init();
printf( _("%s: Too many arguments\n"), SET_COLOR );
return 1;
}
case 'c':
print_colors();
exit(0);
case '?':
return 1;
}
}
switch( argc-optind)
{
case 0:
// printf( "no fg\n" );
break;
case 1:
fgcolor=argv[optind];
// printf( "fg %s\n", fgcolor );
break;
default:
check_locale_init();
printf( _("%s: Too many arguments\n"), SET_COLOR );
return 1;
}
/* Infer term256 support */ /* Infer term256 support */
char *fish_term256 = getenv("fish_term256"); char *fish_term256 = getenv("fish_term256");
if (fish_term256) { if (fish_term256) {
@ -299,77 +299,77 @@ int main( int argc, char **argv )
support_term256 = term && strstr(term, "256color"); support_term256 = term && strstr(term, "256color");
} }
if( !fgcolor && !bgcolor && !bold && !underline ) if( !fgcolor && !bgcolor && !bold && !underline )
{ {
check_locale_init(); check_locale_init();
fprintf( stderr, _("%s: Expected an argument\n"), SET_COLOR ); fprintf( stderr, _("%s: Expected an argument\n"), SET_COLOR );
print_help( argv[0], 2 ); print_help( argv[0], 2 );
return 1; return 1;
} }
rgb_color_t fg = rgb_color_t(fgcolor ? fgcolor : ""); rgb_color_t fg = rgb_color_t(fgcolor ? fgcolor : "");
if( fgcolor && fg.is_none()) if( fgcolor && fg.is_none())
{ {
check_locale_init(); check_locale_init();
fprintf( stderr, _("%s: Unknown color '%s'\n"), SET_COLOR, fgcolor ); fprintf( stderr, _("%s: Unknown color '%s'\n"), SET_COLOR, fgcolor );
return 1; return 1;
} }
rgb_color_t bg = rgb_color_t(bgcolor ? bgcolor : ""); rgb_color_t bg = rgb_color_t(bgcolor ? bgcolor : "");
if( bgcolor && bg.is_none()) if( bgcolor && bg.is_none())
{ {
check_locale_init(); check_locale_init();
fprintf( stderr, _("%s: Unknown color '%s'\n"), SET_COLOR, bgcolor ); fprintf( stderr, _("%s: Unknown color '%s'\n"), SET_COLOR, bgcolor );
return 1; return 1;
} }
setupterm( 0, STDOUT_FILENO, 0); setupterm( 0, STDOUT_FILENO, 0);
if( bold ) if( bold )
{ {
if( enter_bold_mode ) if( enter_bold_mode )
putp( enter_bold_mode ); putp( enter_bold_mode );
} }
if( underline ) if( underline )
{ {
if( enter_underline_mode ) if( enter_underline_mode )
putp( enter_underline_mode ); putp( enter_underline_mode );
} }
if( bgcolor ) if( bgcolor )
{ {
if( bg.is_normal() ) if( bg.is_normal() )
{ {
write_background_color(0); write_background_color(0);
putp( tparm(exit_attribute_mode) ); putp( tparm(exit_attribute_mode) );
} }
} }
if( fgcolor ) if( fgcolor )
{ {
if( fg.is_normal() ) if( fg.is_normal() )
{ {
write_foreground_color(0); write_foreground_color(0);
putp( tparm(exit_attribute_mode) ); putp( tparm(exit_attribute_mode) );
} }
else else
{ {
write_foreground_color(index_for_color(fg)); write_foreground_color(index_for_color(fg));
} }
} }
if( bgcolor )
{
if( ! bg.is_normal() )
{
write_background_color(index_for_color(bg));
}
}
if( del_curterm( cur_term ) == ERR ) if( bgcolor )
{ {
fprintf( stderr, "%s", _("Error while closing terminfo") ); if( ! bg.is_normal() )
} {
write_background_color(index_for_color(bg));
}
}
if( del_curterm( cur_term ) == ERR )
{
fprintf( stderr, "%s", _("Error while closing terminfo") );
}
} }

View file

@ -3,7 +3,7 @@ complete -c abook -s h -d 'Show usage'
complete -c abook -s C -l config -d 'Use an alternative configuration file' -r complete -c abook -s C -l config -d 'Use an alternative configuration file' -r
complete -c abook -l datafile -d 'Use an alternative addressbook file' -r complete -c abook -l datafile -d 'Use an alternative addressbook file' -r
complete -c abook -l mutt-query -d 'Make a query for mutt' -x complete -c abook -l mutt-query -d 'Make a query for mutt' -x
complete -c abook -l add-email -d 'Read email message from stdin and add the sender' complete -c abook -l add-email -d 'Read email message from stdin and add the sender'
complete -c abook -l add-email-quiet -d 'Same as --add-email. Without confirmation' complete -c abook -l add-email-quiet -d 'Same as --add-email. Without confirmation'
complete -c abook -l convert -d 'Convert address book files' complete -c abook -l convert -d 'Convert address book files'

View file

@ -14,7 +14,7 @@ complete -c adduser -l gecos --description 'Set the gecos field for the new entr
complete -c adduser -l gid --description 'When creating a group, this option forces the new groupid to be the given number' -r complete -c adduser -l gid --description 'When creating a group, this option forces the new groupid to be the given number' -r
complete -c adduser -l group --description 'When combined with --system, a group with the same name and ID as the system user is created' complete -c adduser -l group --description 'When combined with --system, a group with the same name and ID as the system user is created'
complete -c adduser -l help --description 'Display brief instructions' complete -c adduser -l help --description 'Display brief instructions'
complete -c adduser -l home --description 'Use specified directory as the user\'s home directory' -x -a '(__fish_complete_directories)' complete -c adduser -l home --description 'Use specified directory as the user\'s home directory' -x -a '(__fish_complete_directories)'
complete -c adduser -l shell --description 'Use shell as the user\'s login shell, rather than the default specified by the configuration file' -x -a '(cat /etc/shells)' complete -c adduser -l shell --description 'Use shell as the user\'s login shell, rather than the default specified by the configuration file' -x -a '(cat /etc/shells)'
complete -c adduser -l ingroup --description 'Add the new user to GROUP instead of a usergroup or the default group defined by USERS_GID in the configuration file' -x -a '(cat /etc/group|cut -d : -f 1)' complete -c adduser -l ingroup --description 'Add the new user to GROUP instead of a usergroup or the default group defined by USERS_GID in the configuration file' -x -a '(cat /etc/group|cut -d : -f 1)'
complete -c adduser -l no-create-home --description 'Do not create the home directory, even if it doesni\'t exist' complete -c adduser -l no-create-home --description 'Do not create the home directory, even if it doesni\'t exist'

View file

@ -1,3 +1,3 @@
complete -c dvipdf -x -a "( complete -c dvipdf -x -a "(
__fish_complete_suffix .dvi __fish_complete_suffix .dvi
)" )"

View file

@ -1,4 +1,4 @@
complete -c dvipdfm -x -a " complete -c dvipdfm -x -a "
( (
__fish_complete_suffix .dvi __fish_complete_suffix .dvi
)" )"

View file

@ -36,9 +36,9 @@ complete -c find -o cmin --description "File status last changed specified numbe
complete -c find -o cnewer --description "File status last changed more recently than file was modified" -r complete -c find -o cnewer --description "File status last changed more recently than file was modified" -r
complete -c find -o ctime --description "File status last changed specified number of days ago" -r complete -c find -o ctime --description "File status last changed specified number of days ago" -r
complete -c find -o empty --description "File is empty and is either a regular file or a directory" complete -c find -o empty --description "File is empty and is either a regular file or a directory"
complete -c find -o executable --description "File is executable" complete -c find -o executable --description "File is executable"
complete -c find -o false --description "Always false" complete -c find -o false --description "Always false"
complete -c find -o fstype --description "File is on filesystem of specified type" -a "(__fish_print_filesystems)" -r complete -c find -o fstype --description "File is on filesystem of specified type" -a "(__fish_print_filesystems)" -r
complete -c find -o gid --description "Numeric group id of file" -r complete -c find -o gid --description "Numeric group id of file" -r
complete -c find -o group --description "Group name of file" -a "(__fish_complete_groups)" complete -c find -o group --description "Group name of file" -a "(__fish_complete_groups)"

View file

@ -6,7 +6,7 @@ for i in $__kill_signals
complete -c kill -o $number -d $name complete -c kill -o $number -d $name
complete -c kill -o $name -d $name complete -c kill -o $name -d $name
complete -c kill -o s -x -a \"$number\tSend\ $name\ signal\" complete -c kill -o s -x -a \"$number\tSend\ $name\ signal\"
complete -c kill -o s -x -a \"$name\tSend\ $name\ signal\" complete -c kill -o s -x -a \"$name\tSend\ $name\ signal\"
end end
complete -c kill -xa '(__fish_complete_pids)' complete -c kill -xa '(__fish_complete_pids)'

View file

@ -6,7 +6,7 @@ for i in $__kill_signals
complete -c killall -o $number -d $name complete -c killall -o $number -d $name
complete -c killall -o $name -d $name complete -c killall -o $name -d $name
complete -c killall -o s -x -a \"$number\tSend\ $name\ signal\" complete -c killall -o s -x -a \"$number\tSend\ $name\ signal\"
complete -c killall -o s -x -a \"$name\tSend\ $name\ signal\" complete -c killall -o s -x -a \"$name\tSend\ $name\ signal\"
end end
complete -c killall -xa '(__fish_complete_proc)' complete -c killall -xa '(__fish_complete_proc)'

View file

@ -48,7 +48,7 @@ complete -c latexmk -o print=ps -d 'when file is to be printed, print the ps
complete -c latexmk -o print=pdf -d 'when file is to be printed, print the pdf file' complete -c latexmk -o print=pdf -d 'when file is to be printed, print the pdf file'
complete -c latexmk -o pv -d 'preview document' complete -c latexmk -o pv -d 'preview document'
complete -c latexmk -o pv- -d 'turn off preview mode' complete -c latexmk -o pv- -d 'turn off preview mode'
complete -c latexmk -o pvc -d 'preview document and continuously update' complete -c latexmk -o pvc -d 'preview document and continuously update'
complete -c latexmk -o pvc- -d 'turn off -pvc' complete -c latexmk -o pvc- -d 'turn off -pvc'
complete -c latexmk -o quiet -d 'silence progress messages from called programs' complete -c latexmk -o quiet -d 'silence progress messages from called programs'
complete -c latexmk -o r -r -d 'Read custom RC file' complete -c latexmk -o r -r -d 'Read custom RC file'

View file

@ -6,8 +6,8 @@ complete -c lpadmin -s r -d 'Removes the named printer from class. If the result
complete -c lpadmin -s v -d 'Sets the device-uri attribute of the printer queue' -r complete -c lpadmin -s v -d 'Sets the device-uri attribute of the printer queue' -r
complete -c lpadmin -s D -d 'Provides a textual description of the destination' -x complete -c lpadmin -s D -d 'Provides a textual description of the destination' -x
complete -c lpadmin -s E -d 'Enables the destination and accepts jobs' complete -c lpadmin -s E -d 'Enables the destination and accepts jobs'
complete -c lpadmin -s L -d 'Provides a textual location of the destination' -x complete -c lpadmin -s L -d 'Provides a textual location of the destination' -x
complete -c lpadmin -s P -d 'Specifies a PostScript Printer Description file to use with the printer' -xa "(__fish_complete_suffix .ppd; __fish_complete_suffix .ppd.gz)" complete -c lpadmin -s P -d 'Specifies a PostScript Printer Description file to use with the printer' -xa "(__fish_complete_suffix .ppd; __fish_complete_suffix .ppd.gz)"
complete -c lpadmin -s o -xa cupsIPPSupplies=true -d 'Specifies whether IPP supply level values should be reported' complete -c lpadmin -s o -xa cupsIPPSupplies=true -d 'Specifies whether IPP supply level values should be reported'
complete -c lpadmin -s o -xa cupsIPPSupplies=false -d 'Specifies whether IPP supply level values should be reported' complete -c lpadmin -s o -xa cupsIPPSupplies=false -d 'Specifies whether IPP supply level values should be reported'

View file

@ -23,7 +23,7 @@ complete -c mocp -s U -l unpause -d "Unpause"
complete -c mocp -s G -l toggle-pause -d "Toggle between play/pause" complete -c mocp -s G -l toggle-pause -d "Toggle between play/pause"
complete -c mocp -s v -l volume -d "(+/-)LEVEL Adjust PCM volume" -xa '+ -' complete -c mocp -s v -l volume -d "(+/-)LEVEL Adjust PCM volume" -xa '+ -'
complete -c mocp -s y -l sync -d "Synchronize the playlist with other clients" complete -c mocp -s y -l sync -d "Synchronize the playlist with other clients"
complete -c mocp -s n -l nosync -d "Don't synchronize the playlist with other client's" complete -c mocp -s n -l nosync -d "Don't synchronize the playlist with other client's"
complete -c mocp -s A -l ascii -d "Use ASCII characters to draw lines" complete -c mocp -s A -l ascii -d "Use ASCII characters to draw lines"
complete -c mocp -s i -l info -d "Print the information about the currently played file" complete -c mocp -s i -l info -d "Print the information about the currently played file"
complete -c mocp -s Q -l format -rf -d "Print the formatted information about the currently played file" complete -c mocp -s Q -l format -rf -d "Print the formatted information about the currently played file"

View file

@ -1,4 +1,4 @@
complete -c pactree -xa "(pacman -Sl | cut --delim ' ' --fields 2- | tr ' ' \t | sort)" complete -c pactree -xa "(pacman -Sl | cut --delim ' ' --fields 2- | tr ' ' \t | sort)"
complete -c pactree -s b -l dbpath -d 'Set an alternate database location' -xa '(__fish_complete_directories)' complete -c pactree -s b -l dbpath -d 'Set an alternate database location' -xa '(__fish_complete_directories)'
complete -c pactree -s c -l color -d 'Colorize output' complete -c pactree -s c -l color -d 'Colorize output'
complete -c pactree -s d -l depth -d 'Limit the depth of recursion' -x complete -c pactree -s d -l depth -d 'Limit the depth of recursion' -x

View file

@ -3,7 +3,7 @@ complete -c pkgfile -s h -l help -d 'show this help message and exit'
complete -c pkgfile -s b -l binaries -d 'only show files in a {s}bin/ directory. Works with -s, -l' complete -c pkgfile -s b -l binaries -d 'only show files in a {s}bin/ directory. Works with -s, -l'
complete -c pkgfile -s c -l case-sensitive -d 'make searches case sensitive' complete -c pkgfile -s c -l case-sensitive -d 'make searches case sensitive'
complete -c pkgfile -s g -l glob -d 'allow the use of * and ? as wildcards' complete -c pkgfile -s g -l glob -d 'allow the use of * and ? as wildcards'
complete -c pkgfile -s r -l regex -d 'allow the use of regex in searches' complete -c pkgfile -s r -l regex -d 'allow the use of regex in searches'
complete -c pkgfile -s R -l repo -d 'search only in the specified repository' -xa '(cat /etc/pacman.conf|grep "^\[" | sed "s/\[\|]//g")' complete -c pkgfile -s R -l repo -d 'search only in the specified repository' -xa '(cat /etc/pacman.conf|grep "^\[" | sed "s/\[\|]//g")'
complete -c pkgfile -s v -l verbose -d 'enable verbose output' complete -c pkgfile -s v -l verbose -d 'enable verbose output'
complete -c pkgfile -s i -l info -d 'provides information about the package owning a file' -r complete -c pkgfile -s i -l info -d 'provides information about the package owning a file' -r

View file

@ -1,3 +1,3 @@
complete -c ps2pdf -x -a "( complete -c ps2pdf -x -a "(
__fish_complete_suffix .ps __fish_complete_suffix .ps
)" )"

View file

@ -1,4 +1,4 @@
complete -c rfkill -xa 'block unblock list event help' -n 'not __fish_seen_subcommand_from block unblock list event help' complete -c rfkill -xa 'block unblock list event help' -n 'not __fish_seen_subcommand_from block unblock list event help'
complete -c rfkill -n '__fish_seen_subcommand_from block unblock list' -d 'device group' -xa "all wifi wlan bluetooth uwb ultrawideband wimax wwan gps fm (rfkill list | tr : \t)" complete -c rfkill -n '__fish_seen_subcommand_from block unblock list' -d 'device group' -xa "all wifi wlan bluetooth uwb ultrawideband wimax wwan gps fm (rfkill list | tr : \t)"
complete -c rfkill -l version -d 'Print version' complete -c rfkill -l version -d 'Print version'

View file

@ -13,7 +13,7 @@ complete -c setxkbmap -o keymap -d 'Specifies name of keymap to load' -xa "(c
complete -c setxkbmap -o layout -d 'Specifies layout used to choose component names' -xa "(__fish_complete_setxkbmap layout)" complete -c setxkbmap -o layout -d 'Specifies layout used to choose component names' -xa "(__fish_complete_setxkbmap layout)"
complete -c setxkbmap -o model -d 'Specifies model used to choose component names' -xa "(__fish_complete_setxkbmap model)" complete -c setxkbmap -o model -d 'Specifies model used to choose component names' -xa "(__fish_complete_setxkbmap model)"
complete -c setxkbmap -o option -d 'Adds an option used to choose component names' -xa "(__fish_complete_list , '__fish_complete_setxkbmap option')" complete -c setxkbmap -o option -d 'Adds an option used to choose component names' -xa "(__fish_complete_list , '__fish_complete_setxkbmap option')"
complete -c setxkbmap -o print -d 'Print a complete xkb_keymap description and exit' complete -c setxkbmap -o print -d 'Print a complete xkb_keymap description and exit'
complete -c setxkbmap -o query -d 'Print the current layout settings and exit' complete -c setxkbmap -o query -d 'Print the current layout settings and exit'
complete -c setxkbmap -o rules -d 'Name of rules file to use' -x complete -c setxkbmap -o rules -d 'Name of rules file to use' -x
complete -c setxkbmap -o symbols -d 'Specifies symbols component name' -xa "(cat /usr/share/X11/xkb/symbols.dir | sed -r $filter)" complete -c setxkbmap -o symbols -d 'Specifies symbols component name' -xa "(cat /usr/share/X11/xkb/symbols.dir | sed -r $filter)"

View file

@ -1,5 +1,5 @@
## Listing options ## Listing options
complete -c tree -s a -d 'All files are listed' complete -c tree -s a -d 'All files are listed'
complete -c tree -s d -d 'List directories only' complete -c tree -s d -d 'List directories only'
@ -16,7 +16,7 @@ complete -c tree -l filelimit -r -d 'Do not descend dirs with more than # files
complete -c tree -l timefmt -x -d 'Print and format time according to the format <f>' complete -c tree -l timefmt -x -d 'Print and format time according to the format <f>'
complete -c tree -s o -r -d 'Output to file instead of stdout' complete -c tree -s o -r -d 'Output to file instead of stdout'
## File options ## File options
complete -c tree -s q -d 'Print non-printable characters as \'?\'' complete -c tree -s q -d 'Print non-printable characters as \'?\''
complete -c tree -s N -d 'Print non-printable characters as is' complete -c tree -s N -d 'Print non-printable characters as is'
complete -c tree -s Q -d 'Quote filenames with double quotes' complete -c tree -s Q -d 'Quote filenames with double quotes'
@ -32,27 +32,27 @@ complete -c tree -l inodes -d 'Print inode number of each file'
complete -c tree -l device -d 'Print device ID number to which each file belongs' complete -c tree -l device -d 'Print device ID number to which each file belongs'
## Sorting options ## Sorting options
complete -c tree -s v -d 'Sort files alphanumerically by version' complete -c tree -s v -d 'Sort files alphanumerically by version'
complete -c tree -s r -d 'Sort files in reverse alphanumeric order' complete -c tree -s r -d 'Sort files in reverse alphanumeric order'
complete -c tree -s t -d 'Sort files by last modification time' complete -c tree -s t -d 'Sort files by last modification time'
complete -c tree -s c -d 'Sort files by last status change time' complete -c tree -s c -d 'Sort files by last status change time'
complete -c tree -s U -d 'Leave files unsorted' complete -c tree -s U -d 'Leave files unsorted'
complete -c tree -l dirsfirst -d 'List directories before files (-U disables)' complete -c tree -l dirsfirst -d 'List directories before files (-U disables)'
## Graphics options ## Graphics options
complete -c tree -s i -d 'Don\'t print indentation lines' complete -c tree -s i -d 'Don\'t print indentation lines'
complete -c tree -s A -d 'Print ANSI lines graphic indentation lines' complete -c tree -s A -d 'Print ANSI lines graphic indentation lines'
complete -c tree -s S -d 'Print with ASCII graphics indentation lines' complete -c tree -s S -d 'Print with ASCII graphics indentation lines'
complete -c tree -s n -d 'Turn colorization off always (-C overrides)' complete -c tree -s n -d 'Turn colorization off always (-C overrides)'
complete -c tree -s C -d 'Turn colorization on always' complete -c tree -s C -d 'Turn colorization on always'
## XML/HTML options ## XML/HTML options
complete -c tree -s X -d 'Prints out an XML representation of the tree' complete -c tree -s X -d 'Prints out an XML representation of the tree'
complete -c tree -s H -r -d 'Prints out HTML format with baseHREF as top directory' complete -c tree -s H -r -d 'Prints out HTML format with baseHREF as top directory'
complete -c tree -s T -r -d 'Replace the default HTML title and H1 header with string' complete -c tree -s T -r -d 'Replace the default HTML title and H1 header with string'
complete -c tree -l nolinks -d 'Turn off hyperlinks in HTML output' complete -c tree -l nolinks -d 'Turn off hyperlinks in HTML output'
## Miscellaneous options ## Miscellaneous options
complete -c tree -l version -d 'Print version and exit' complete -c tree -l version -d 'Print version and exit'
complete -c tree -l help -d 'Print usage and this help message and exit' complete -c tree -l help -d 'Print usage and this help message and exit'

View file

@ -18,7 +18,7 @@ complete -c xdg-mime -d 'Mimetype' -n '__fish_seen_subcommand_from def
# complete xdg-mime install # complete xdg-mime install
complete -c xdg-mime -d 'Add filetype description' -n 'contains_seq xdg-mime install -- (commandline -cop)' -r complete -c xdg-mime -d 'Add filetype description' -n 'contains_seq xdg-mime install -- (commandline -cop)' -r
complete -c xdg-mime -d 'Set mode' -n 'contains_seq xdg-mime install -- (commandline -cop)' -l mode -xa 'user system' complete -c xdg-mime -d 'Set mode' -n 'contains_seq xdg-mime install -- (commandline -cop)' -l mode -xa 'user system'
complete -c xdg-mime -d 'Disable vendor check' -n 'contains_seq xdg-mime install -- (commandline -cop)' -l novendor complete -c xdg-mime -d 'Disable vendor check' -n 'contains_seq xdg-mime install -- (commandline -cop)' -l novendor
# complete xdg-mime uninstall # complete xdg-mime uninstall
complete -c xdg-mime -d 'Remove filetype description' -n 'contains_seq xdg-mime uninstall -- (commandline -cop)' -r complete -c xdg-mime -d 'Remove filetype description' -n 'contains_seq xdg-mime uninstall -- (commandline -cop)' -r

View file

@ -6,11 +6,11 @@ complete -c xrandr -s v -l version -d 'Print out the RandR version reported by t
complete -c xrandr -s q -l query -d 'Display the current state of the system' complete -c xrandr -s q -l query -d 'Display the current state of the system'
complete -c xrandr -s d -o display -d 'Select X display to use' -x complete -c xrandr -s d -o display -d 'Select X display to use' -x
complete -c xrandr -l screen -d 'Select which screen to manipulate' -x complete -c xrandr -l screen -d 'Select which screen to manipulate' -x
complete -c xrandr -l q1 -d 'Use RandR version 1.1 protocol' complete -c xrandr -l q1 -d 'Use RandR version 1.1 protocol'
complete -c xrandr -l q12 -d 'Use RandR version 1.2 protocol' complete -c xrandr -l q12 -d 'Use RandR version 1.2 protocol'
set -l ver (xrandr -v | grep RandR | sed 's/^.\+\s\([0-9\.]\+\)$/\1/') set -l ver (xrandr -v | grep RandR | sed 's/^.\+\s\([0-9\.]\+\)$/\1/')
if not expr match $ver '^[0-9.]*$' >/dev/null if not expr match $ver '^[0-9.]*$' >/dev/null
set ver 10 set ver 10
end end
@ -25,7 +25,7 @@ if expr $ver '>' 1.1
complete -c xrandr -l prop -l properties -d 'Display the contents of properties for each output' complete -c xrandr -l prop -l properties -d 'Display the contents of properties for each output'
complete -c xrandr -l fb -d 'Set screen size' -x complete -c xrandr -l fb -d 'Set screen size' -x
complete -c xrandr -l fbmm -d 'Set reported physical screen size' -x complete -c xrandr -l fbmm -d 'Set reported physical screen size' -x
complete -c xrandr -l dpi -d 'Set dpi to calculate reported physical screen size' complete -c xrandr -l dpi -d 'Set dpi to calculate reported physical screen size'
complete -c xrandr -l newmode -d 'Add new mode' -r complete -c xrandr -l newmode -d 'Add new mode' -r
complete -c xrandr -l rmmode -d 'Removes a mode from the server' -xa '(__fish_print_xrandr_modes)' complete -c xrandr -l rmmode -d 'Removes a mode from the server' -xa '(__fish_print_xrandr_modes)'
complete -c xrandr -l addmode -d 'Add a mode to the set of valid modes for an output' -xa '(__fish_print_xrandr_outputs)' complete -c xrandr -l addmode -d 'Add a mode to the set of valid modes for an output' -xa '(__fish_print_xrandr_outputs)'
@ -53,7 +53,7 @@ end
if expr $ver '>' 1.2 if expr $ver '>' 1.2
complete -c xrandr -l noprimary -d 'Don\'t define a primary output.' complete -c xrandr -l noprimary -d 'Don\'t define a primary output.'
complete -c xrandr -l current -d 'Print current screen configuration' complete -c xrandr -l current -d 'Print current screen configuration'
complete -c xrandr -l panning -d 'Set panning: widthxheight[+x+y[/track_widthxtrack_height+track_x+track_y[/border_left/border_top/border_right/border_bottom]]]' -x complete -c xrandr -l panning -d 'Set panning: widthxheight[+x+y[/track_widthxtrack_height+track_x+track_y[/border_left/border_top/border_right/border_bottom]]]' -x
complete -c xrandr -l transform -d 'Set transformation matrix: a,b,c,d,e,f,g,h,i for [ [a,b,c], [d,e,f], [g,h,i] ]' -x complete -c xrandr -l transform -d 'Set transformation matrix: a,b,c,d,e,f,g,h,i for [ [a,b,c], [d,e,f], [g,h,i] ]' -x
complete -c xrandr -l scale -d 'Set scren scale' -x complete -c xrandr -l scale -d 'Set scren scale' -x
complete -c xrandr -l primary -d 'Set the output as primary' complete -c xrandr -l primary -d 'Set the output as primary'

View file

@ -16,6 +16,6 @@ complete -c xrdb -o symbols -d 'show preprocessor symbols'
complete -c xrdb -o remove -d 'remove resources' complete -c xrdb -o remove -d 'remove resources'
complete -c xrdb -o retain -d 'avoid server reset (avoid using this)' complete -c xrdb -o retain -d 'avoid server reset (avoid using this)'
complete -c xrdb -o quiet -d 'don\'t warn about duplicates' complete -c xrdb -o quiet -d 'don\'t warn about duplicates'
#complete -c xrdb -s Dname[=value], #complete -c xrdb -s Dname[=value],
#complete -c xrdb -s Uname, #complete -c xrdb -s Uname,
#complete -c xrdb -s Idirectory -d 'passed to preprocessor' #complete -c xrdb -s Idirectory -d 'passed to preprocessor'

Some files were not shown because too many files have changed in this diff Show more