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
* Added a changelog file
* removed unescaping if the 'commandline' builtin is called without the -o (tokenise) flag
* Added a changelog file
* removed unescaping if the 'commandline' builtin is called without the -o (tokenise) flag

View file

@ -1,5 +1,5 @@
/** \file autoload.cpp
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;
}
}
// 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.last_checked = time(NULL);
@ -56,7 +56,7 @@ autoload_t::~autoload_t() {
void autoload_t::node_was_evicted(autoload_function_t *node) {
// This should only ever happen on the main thread
ASSERT_IS_MAIN_THREAD();
// Tell ourselves that the command was removed if it was loaded
if (! node->is_loaded)
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 res;
CHECK_BLOCK( 0 );
int res;
CHECK_BLOCK( 0 );
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?
*/
if( path_var.empty() )
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. */
if( path_var != this->last_path )
{
@ -89,32 +89,32 @@ int autoload_t::load( const wcstring &cmd, bool reload )
scoped_lock locker(lock);
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. */
if (this->is_loading(cmd))
{
debug( 0,
_( L"Could not autoload item '%ls', it is already being autoloaded. "
L"This is a circular dependency in the autoloading scripts, please remove it."),
debug( 0,
_( L"Could not autoload item '%ls', it is already being autoloaded. "
L"This is a circular dependency in the autoloading scripts, please remove it."),
cmd.c_str() );
return 1;
}
/* Mark that we're loading this */
is_loading_set.insert(cmd);
/* Get the list of paths from which we will try to load */
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 */
bool erased = !! is_loading_set.erase(cmd);
assert(erased);
return res;
return res;
}
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;
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 );
}
@ -141,7 +141,7 @@ void autoload_t::unload_all(void) {
/** Check whether the given command is loaded. */
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);
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
functions, the internal function can return on various places in
the code, and the caller can take care of various cleanup work.
cmd: the command name ('grep')
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
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.
*/
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! */
size_t i;
bool reloaded = 0;
size_t i;
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. */
{
bool allow_stale_functions = ! reload;
/* Take a lock */
scoped_lock locker(lock);
/* Get the function */
autoload_function_t * func = this->get_node(cmd);
/* Determine if we can use this cached function */
bool use_cached;
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 */
use_cached = true;
}
/* If we can use this function, return whether we were able to access it */
if (use_cached) {
return func->is_internalized || func->access.accessible;
}
}
}
/* The source of the script will end up here */
wcstring script_source;
bool has_script_source = false;
/* Whether we found an accessible file */
bool found_file = false;
/* Look for built-in scripts via a binary search */
const builtin_script_t *matching_builtin_script = NULL;
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) {
has_script_source = true;
script_source = str2wcstring(matching_builtin_script->def);
/* Make a node representing this function */
scoped_lock locker(lock);
autoload_function_t *func = this->get_autoloaded_function_with_creation(cmd, really_load);
/* This function is internalized */
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. */
if (really_load) func->is_loaded = true;
}
if (! has_script_source)
{
/* 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) {
/* Found it! */
found_file = true;
/* Now we're actually going to take the lock. */
scoped_lock locker(lock);
autoload_function_t *func = this->get_node(cmd);
/* 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);
if (need_to_load_function) {
/* Generate the script source */
wcstring esc = escape_string(path, 1);
script_source = L". " + esc;
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. */
if (func && func->is_loaded) {
command_removed(cmd);
func->is_placeholder = false;
}
/* Mark that we're reloading it */
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. */
if (! func) {
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. */
if (need_to_load_function) func->is_loaded = true;
/* Unconditionally record our access time */
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);
}
}
/* If we have a script, either built-in or a file source, then run it */
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);
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) { }
file_access_attempt_t access; /** The last access attempt */
bool is_loaded; /** Whether we have actually loaded this function */
@ -48,61 +48,61 @@ private:
/** The environment variable name */
const wcstring env_var_name;
/** Builtin script array */
const struct builtin_script_t *const builtin_scripts;
/** Builtin script count */
const size_t builtin_script_count;
/** The path from which we most recently autoloaded */
wcstring last_path;
/**
A table containing all the files that are currently being
loaded. This is here to help prevent recursion.
*/
/**
A table containing all the files that are currently being
loaded. This is here to help prevent recursion.
*/
std::set<wcstring> is_loading_set;
bool is_loading(const wcstring &name) const {
return is_loading_set.find(name) != is_loading_set.end();
}
void remove_all_functions(void) {
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 );
virtual void node_was_evicted(autoload_function_t *node);
autoload_function_t *get_autoloaded_function_with_creation(const wcstring &cmd, bool allow_eviction);
protected:
protected:
/** Overridable callback for when a command is removed */
virtual void command_removed(const wcstring &cmd) { }
public:
/** 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 );
/** Destructor */
virtual ~autoload_t();
/**
Autoload the specified file, if it exists in the specified path. Do
not load it multiple times unless it's timestamp changes or
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 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
*/
int load( const wcstring &cmd, bool reload );
/** Check whether we have tried loading the given command. Does not do any I/O. */
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
*/
int unload( const wcstring &cmd );
/**
Unloads all files.
*/
void unload_all( );
/** 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 );

View file

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

File diff suppressed because it is too large Load diff

View file

@ -1,5 +1,5 @@
/** \file builtin.h
Prototypes for functions for executing builtin functions.
Prototypes for functions for executing builtin functions.
*/
#ifndef FISH_BUILTIN_H
@ -15,9 +15,9 @@ class parser_t;
enum
{
COMMAND_NOT_BUILTIN,
BUILTIN_REGULAR,
BUILTIN_FUNCTION
COMMAND_NOT_BUILTIN,
BUILTIN_REGULAR,
BUILTIN_FUNCTION
}
;
@ -49,7 +49,7 @@ enum
/**
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
@ -67,7 +67,7 @@ enum
#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")
@ -113,7 +113,7 @@ extern int builtin_err_redirect;
/**
Initialize builtin data.
Initialize builtin data.
*/
void builtin_init();
@ -128,12 +128,12 @@ void builtin_destroy();
int builtin_exists( const wcstring &cmd );
/**
Execute a builtin command
Execute a builtin command
\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
null pointer. This syntax resembles the syntax
null pointer. This syntax resembles the syntax
for exec.
\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
Functions for executing the jobs builtin.
Functions for executing the jobs builtin.
*/
#include "config.h"
@ -30,12 +30,12 @@
*/
enum
{
JOBS_DEFAULT, /**< Print lots of general info */
JOBS_PRINT_PID, /**< Print pid 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_DEFAULT, /**< Print lots of general info */
JOBS_PRINT_PID, /**< Print pid of each process in job */
JOBS_PRINT_COMMAND, /**< Print command name of each process in job */
JOBS_PRINT_GROUP, /**< Print group id of job */
}
;
;
@ -45,26 +45,26 @@ enum
*/
static int cpu_use( const job_t *j )
{
double u=0;
process_t *p;
double u=0;
process_t *p;
for( p=j->first_process; p; p=p->next )
{
struct timeval t;
int jiffies;
gettimeofday( &t, 0 );
jiffies = proc_get_jiffies( p );
for( p=j->first_process; p; p=p->next )
{
struct timeval t;
int jiffies;
gettimeofday( &t, 0 );
jiffies = proc_get_jiffies( p );
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 t1 = 1000000.0*p->last_time.tv_sec+p->last_time.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 );
*/
u += ((double)(jiffies-p->last_jiffies))/(t2-t1);
}
return u*1000000;
u += ((double)(jiffies-p->last_jiffies))/(t2-t1);
}
return u*1000000;
}
#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 )
{
process_t *p;
switch( mode )
{
case JOBS_DEFAULT:
{
process_t *p;
switch( mode )
{
case JOBS_DEFAULT:
{
if( header )
{
/*
Print table header before first job
*/
stdout_buffer.append( _( L"Job\tGroup\t" ));
if( header )
{
/*
Print table header before first job
*/
stdout_buffer.append( _( L"Job\tGroup\t" ));
#ifdef HAVE__PROC_SELF_STAT
stdout_buffer.append( _( L"CPU\t" ) );
stdout_buffer.append( _( L"CPU\t" ) );
#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
append_format(stdout_buffer, L"%d%%\t", cpu_use(j) );
append_format(stdout_buffer, L"%d%%\t", cpu_use(j) );
#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(j->command_wcstr());
stdout_buffer.append(L"\n");
break;
}
break;
}
case JOBS_PRINT_GROUP:
{
if( header )
{
/*
Print table header before first job
*/
stdout_buffer.append( _( L"Group\n" ));
}
append_format(stdout_buffer, L"%d\n", j->pgid );
break;
}
case JOBS_PRINT_GROUP:
{
if( header )
{
/*
Print table header before first job
*/
stdout_buffer.append( _( L"Group\n" ));
}
append_format(stdout_buffer, L"%d\n", j->pgid );
break;
}
case JOBS_PRINT_PID:
{
if( header )
{
/*
Print table header before first job
*/
stdout_buffer.append( _( L"Procces\n" ));
}
case JOBS_PRINT_PID:
{
if( header )
{
/*
Print table header before first job
*/
stdout_buffer.append( _( L"Procces\n" ));
}
for( p=j->first_process; p; p=p->next )
{
append_format(stdout_buffer, L"%d\n", p->pid );
}
break;
}
for( p=j->first_process; p; p=p->next )
{
append_format(stdout_buffer, L"%d\n", p->pid );
}
break;
}
case JOBS_PRINT_COMMAND:
{
if( header )
{
/*
Print table header before first job
*/
stdout_buffer.append( _( L"Command\n" ));
}
case JOBS_PRINT_COMMAND:
{
if( header )
{
/*
Print table header before first job
*/
stdout_buffer.append( _( L"Command\n" ));
}
for( p=j->first_process; p; p=p->next )
{
append_format(stdout_buffer, L"%ls\n", p->argv0() );
}
break;
}
}
for( p=j->first_process; p; p=p->next )
{
append_format(stdout_buffer, L"%ls\n", p->argv0() );
}
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 )
{
int argc=0;
int found=0;
int mode=JOBS_DEFAULT;
int print_last = 0;
const job_t *j;
int argc=0;
int found=0;
int mode=JOBS_DEFAULT;
int print_last = 0;
const job_t *j;
argc = builtin_count_args( argv );
woptind=0;
argc = builtin_count_args( argv );
woptind=0;
while( 1 )
{
static const struct woption
long_options[] =
{
{
L"pid", no_argument, 0, 'p'
}
,
{
L"command", no_argument, 0, 'c'
}
,
{
L"group", no_argument, 0, 'g'
}
,
{
L"last", no_argument, 0, 'l'
}
,
{
L"help", no_argument, 0, 'h'
}
,
{
0, 0, 0, 0
}
}
;
while( 1 )
{
static const struct woption
long_options[] =
{
{
L"pid", no_argument, 0, 'p'
}
,
{
L"command", no_argument, 0, 'c'
}
,
{
L"group", no_argument, 0, 'g'
}
,
{
L"last", no_argument, 0, 'l'
}
,
{
L"help", no_argument, 0, 'h'
}
,
{
0, 0, 0, 0
}
}
;
int opt_index = 0;
int opt_index = 0;
int opt = wgetopt_long( argc,
argv,
L"pclgh",
long_options,
&opt_index );
if( opt == -1 )
break;
int opt = wgetopt_long( argc,
argv,
L"pclgh",
long_options,
&opt_index );
if( opt == -1 )
break;
switch( opt )
{
case 0:
if(long_options[opt_index].flag != 0)
break;
switch( opt )
{
case 0:
if(long_options[opt_index].flag != 0)
break;
append_format(stderr_buffer,
BUILTIN_ERR_UNKNOWN,
argv[0],
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':
mode=JOBS_PRINT_PID;
break;
case 'p':
mode=JOBS_PRINT_PID;
break;
case 'c':
mode=JOBS_PRINT_COMMAND;
break;
case 'c':
mode=JOBS_PRINT_COMMAND;
break;
case 'g':
mode=JOBS_PRINT_GROUP;
break;
case 'g':
mode=JOBS_PRINT_GROUP;
break;
case 'l':
{
print_last = 1;
break;
}
case 'l':
{
print_last = 1;
break;
}
case 'h':
builtin_print_help( parser, argv[0], stdout_buffer );
return 0;
case 'h':
builtin_print_help( parser, argv[0], stdout_buffer );
return 0;
case '?':
builtin_unknown_option( parser, argv[0], argv[woptind-1] );
return 1;
case '?':
builtin_unknown_option( parser, argv[0], argv[woptind-1] );
return 1;
}
}
}
}
/*
Do not babble if not interactive
*/
if( builtin_out_redirect )
{
found=1;
}
/*
Do not babble if not interactive
*/
if( builtin_out_redirect )
{
found=1;
}
if( print_last )
{
/*
Ignore unconstructed jobs, i.e. ourself.
*/
if( print_last )
{
/*
Ignore unconstructed jobs, i.e. ourself.
*/
job_iterator_t jobs;
const job_t *j;
while ((j = jobs.next()))
{
if( (j->flags & JOB_CONSTRUCTED) && !job_is_completed(j) )
{
builtin_jobs_print( j, mode, !found );
return 0;
}
}
{
}
else
{
if( woptind < argc )
{
int i;
if( (j->flags & JOB_CONSTRUCTED) && !job_is_completed(j) )
{
builtin_jobs_print( j, mode, !found );
return 0;
}
}
found = 1;
}
else
{
if( woptind < argc )
{
int i;
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;
}
found = 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 ) )
{
builtin_jobs_print( j, mode, !found );
}
else
{
append_format(stderr_buffer,
_( L"%ls: No suitable job: %d\n" ),
argv[0],
pid );
return 1;
}
}
}
else
{
j = job_get_from_pid( pid );
if( j && !job_is_completed( j ) )
{
builtin_jobs_print( j, mode, !found );
}
else
{
append_format(stderr_buffer,
_( L"%ls: No suitable job: %d\n" ),
argv[0],
pid );
return 1;
}
}
}
else
{
job_iterator_t jobs;
const job_t *j;
while ((j = jobs.next()))
{
/*
Ignore unconstructed jobs, i.e. ourself.
*/
if( (j->flags & JOB_CONSTRUCTED) && !job_is_completed(j) )
{
builtin_jobs_print( j, mode, !found );
found = 1;
}
}
}
}
/*
Ignore unconstructed jobs, i.e. ourself.
*/
if( (j->flags & JOB_CONSTRUCTED) && !job_is_completed(j) )
{
builtin_jobs_print( j, mode, !found );
found = 1;
}
}
}
}
if( !found )
{
append_format(stdout_buffer,
_( L"%ls: There are no jobs\n" ),
argv[0] );
}
if( !found )
{
append_format(stdout_buffer,
_( L"%ls: There are no jobs\n" ),
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
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.
*/
@ -28,12 +28,12 @@ static const wchar_t * const condstr[] = {
};
namespace test_expressions {
enum token_t {
test_unknown, // arbitrary string
test_bang, // "!", inverts sense
test_filetype_b, // "-b", for block special files
test_filetype_c, // "-c" for character special files
test_filetype_d, // "-d" for directories
@ -44,44 +44,44 @@ namespace test_expressions {
test_filetype_L, // "-L", same as -h
test_filetype_p, // "-p", for FIFO
test_filetype_S, // "-S", socket
test_filesize_s, // "-s", size greater than zero
test_filedesc_t, // "-t", whether the fd is associated with a terminal
test_fileperm_r, // "-r", read permission
test_fileperm_u, // "-u", whether file is setuid
test_fileperm_w, // "-w", whether file write permission is allowed
test_fileperm_x, // "-x", whether file execute/search is allowed
test_string_n, // "-n", non-empty string
test_string_z, // "-z", true if length of string is 0
test_string_equal, // "=", true if strings are identical
test_string_not_equal, // "!=", true if strings are not identical
test_number_equal, // "-eq", true if numbers are 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_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_equal, // "-le", true if first number is at most second
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_paren_open, // "(", open 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 unary_primary_evaluate(test_expressions::token_t token, const wcstring &arg, wcstring_list_t &errors);
enum {
UNARY_PRIMARY = 1 << 0,
BINARY_PRIMARY = 1 << 1
};
static const struct token_info_t { token_t tok; const wchar_t *string; unsigned int flags; } token_infos[] =
{
{test_unknown, L"", 0},
@ -117,7 +117,7 @@ namespace test_expressions {
{test_paren_open, L"(", 0},
{test_paren_close, L")", 0}
};
const token_info_t *token_for_string(const wcstring &str) {
for (size_t i=0; i < sizeof token_infos / sizeof *token_infos; i++) {
if (str == token_infos[i].string) {
@ -127,55 +127,55 @@ namespace test_expressions {
return &token_infos[0]; //unknown
}
/* Grammar.
<expr> = <combining_expr>
<combining_expr> = <unary_expr> and/or <combining_expr> |
<unary_expr>
<unary_expr> = bang <unary_expr> |
<primary>
<primary> = <unary_primary> arg |
arg <binary_primary> arg |
'(' <expr> ')'
*/
class expression;
class test_parser {
private:
wcstring_list_t strings;
wcstring_list_t errors;
expression *error(const wchar_t *fmt, ...);
void add_error(const wchar_t *fmt, ...);
const wcstring &arg(unsigned int idx) { return strings.at(idx); }
public:
test_parser(const wcstring_list_t &val) : strings(val)
{ }
expression *parse_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_primary(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_binary_primary(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);
};
struct range_t {
unsigned int start;
unsigned int end;
range_t(unsigned s, unsigned e) : start(s), end(e) { }
};
@ -184,13 +184,13 @@ namespace test_expressions {
class expression {
protected:
expression(token_t what, range_t where) : token(what), range(where) { }
public:
const token_t token;
range_t range;
virtual ~expression() { }
// evaluate returns true if the expression is true (i.e. BUILTIN_TEST_SUCCESS)
virtual bool evaluate(wcstring_list_t &errors) = 0;
};
@ -210,59 +210,59 @@ namespace test_expressions {
public:
wcstring arg_left;
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)
{ }
bool evaluate(wcstring_list_t &errors);
};
/* Unary operator like bang */
class unary_operator : public expression {
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) { }
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. */
class combining_expression : public expression {
public:
const std::vector<expression *> subjects;
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)
{
/* We should have one more subject than combiner */
assert(subjects.size() == combiners.size() + 1);
}
/* We are responsible for destroying our expressions */
virtual ~combining_expression() {
for (size_t i=0; i < subjects.size(); i++) {
delete subjects[i];
}
}
bool evaluate(wcstring_list_t &errors);
};
/* Parenthetical expression */
class parenthetical_expression : public expression {
public:
expr_ref_t contents;
parenthetical_expression(token_t tok, range_t where, expr_ref_t &expr) : expression(tok, where), contents(expr) { }
virtual bool evaluate(wcstring_list_t &errors);
};
void test_parser::add_error(const wchar_t *fmt, ...) {
assert(fmt != NULL);
va_list va;
va_start(va, fmt);
this->errors.push_back(vformat_string(fmt, va));
va_end(va);
va_end(va);
}
expression *test_parser::error(const wchar_t *fmt, ...) {
assert(fmt != NULL);
va_list va;
@ -271,7 +271,7 @@ namespace test_expressions {
va_end(va);
return NULL;
}
expression *test_parser::parse_unary_expression(unsigned int start, unsigned int end) {
if (start >= end) {
return error(L"Missing argument at index %u", start);
@ -288,18 +288,18 @@ namespace test_expressions {
return parse_primary(start, end);
}
}
/* Parse a combining expression (AND, OR) */
expression *test_parser::parse_combining_expression(unsigned int start, unsigned int end) {
if (start >= end)
return NULL;
std::vector<expression *> subjects;
std::vector<token_t> combiners;
unsigned int idx = start;
while (idx < end) {
if (! subjects.empty()) {
/* This is not the first expression, so we expect a combiner. */
token_t combiner = token_for_string(arg(idx))->tok;
@ -310,19 +310,19 @@ namespace test_expressions {
combiners.push_back(combiner);
idx++;
}
/* Parse another expression */
expression *expr = parse_unary_expression(idx, end);
if (! expr) {
add_error(L"Missing argument at index %u", idx);
break;
}
/* Go to the end of this expression */
idx = expr->range.end;
subjects.push_back(expr);
}
if (! subjects.empty()) {
/* 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);
@ -331,7 +331,7 @@ namespace test_expressions {
return NULL;
}
}
expression *test_parser::parse_unary_primary(unsigned int start, unsigned int end) {
/* We need two arguments */
if (start >= end) {
@ -340,54 +340,54 @@ namespace test_expressions {
if (start + 1 >= end) {
return error(L"Missing argument at index %u", start + 1);
}
/* All our unary primaries are prefix, so the operator is at start. */
const token_info_t *info = token_for_string(arg(start));
if (! (info->flags & UNARY_PRIMARY))
return NULL;
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) {
/* 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
We handle this with a unary primary of test_string_n
*/
/* We need one arguments */
if (start >= end) {
return error(L"Missing argument at index %u", start);
}
const token_info_t *info = token_for_string(arg(start));
if (info->tok != test_unknown) {
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 */
return new unary_primary(test_string_n, range_t(start, start + 1), arg(start));
}
#if 0
expression *test_parser::parse_unary_primary(unsigned int start, unsigned int end) {
/* We need either one or two arguments */
if (start >= end) {
return error(L"Missing argument at index %u", start);
}
/* The index of the argument to the unary primary */
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. */
const token_info_t *info = token_for_string(arg(start));
if (info->flags & UNARY_PRIMARY) {
/* We have an operator. Skip the operator argument */
arg_idx = start + 1;
/* 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. */
} else if (info->tok == test_unknown) {
/* "Just a string. */
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. */
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 */
if (arg_idx >= end) {
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));
}
#endif
expression *test_parser::parse_binary_primary(unsigned int start, unsigned int end) {
/* We need three arguments */
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);
}
}
/* All our binary primaries are infix, so the operator is at start + 1. */
const token_info_t *info = token_for_string(arg(start + 1));
if (! (info->flags & BINARY_PRIMARY))
return NULL;
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) {
/* We need at least three arguments: open paren, argument, close paren */
if (start + 3 >= end)
return NULL;
/* Must start with an open expression */
const token_info_t *open_paren = token_for_string(arg(start));
if (open_paren->tok != test_paren_open)
return NULL;
/* Parse a subexpression */
expression *subexr_ptr = parse_expression(start + 1, end);
if (! subexr_ptr)
return NULL;
expr_ref_t subexpr(subexr_ptr);
/* Parse a close paren */
unsigned close_index = subexpr->range.end;
assert(close_index <= end);
@ -447,7 +447,7 @@ namespace test_expressions {
if (close_paren->tok != test_paren_close) {
return error(L"Expected close paren at index %u", close_index);
}
/* Success */
return new parenthetical_expression(test_paren_open, range_t(start, close_index+1), subexpr);
}
@ -456,7 +456,7 @@ namespace test_expressions {
if (start >= end) {
return error(L"Missing argument at index %u", start);
}
expression *expr = NULL;
if (! expr) expr = parse_parenthentical(start, end);
if (! expr) expr = parse_unary_primary(start, end);
@ -469,17 +469,17 @@ namespace test_expressions {
if (start >= end) {
return error(L"Missing argument at index %u", start);
}
return parse_combining_expression(start, end);
}
expression *test_parser::parse_args(const wcstring_list_t &args, wcstring &err) {
/* Empty list and one-arg list should be handled by caller */
assert(args.size() > 1);
test_parser parser(args);
expression *result = parser.parse_expression(0, (unsigned int)args.size());
/* Handle errors */
bool errored = false;
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
break;
}
if (! errored && result) {
/* It's also an error if there are any unused arguments. This is not detected by parse_expression() */
assert(result->range.end <= args.size());
@ -501,19 +501,19 @@ namespace test_expressions {
errored = true;
}
}
return result;
}
bool unary_primary::evaluate(wcstring_list_t &errors) {
return unary_primary_evaluate(token, arg, errors);
}
bool binary_primary::evaluate(wcstring_list_t &errors) {
return binary_primary_evaluate(token, arg_left, arg_right, errors);
}
bool unary_operator::evaluate(wcstring_list_t &errors) {
switch (token) {
case test_bang:
@ -525,7 +525,7 @@ namespace test_expressions {
}
}
bool combining_expression::evaluate(wcstring_list_t &errors) {
switch (token) {
case test_combine_and:
@ -534,11 +534,11 @@ namespace test_expressions {
/* One-element case */
if (subjects.size() == 1)
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. */
assert(combiners.size() + 1 == subjects.size());
assert(! subjects.empty());
size_t idx = 0, max = subjects.size();
bool or_result = false;
while (idx < max) {
@ -546,33 +546,33 @@ namespace test_expressions {
/* Short circuit */
break;
}
/* Evaluate a stream of AND starting at given subject index. It may only have one element. */
bool and_result = true;
for (; idx < max; idx++) {
/* Evaluate it, short-circuiting */
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 (idx + 1 < max && combiners.at(idx) != test_combine_and) {
idx++;
break;
}
}
/* OR it in */
or_result = or_result || and_result;
}
return or_result;
}
default:
errors.push_back(format_string(L"Unknown token type in %s", __func__));
return BUILTIN_TEST_FAIL;
}
}
bool parenthetical_expression::evaluate(wcstring_list_t &errors) {
return contents->evaluate(errors);
}
@ -591,28 +591,28 @@ namespace test_expressions {
switch (token) {
case test_string_equal:
return left == right;
case test_string_not_equal:
return left != right;
case test_number_equal:
return parse_number(left, &left_num) && parse_number(right, &right_num) && left_num == right_num;
case test_number_not_equal:
return parse_number(left, &left_num) && parse_number(right, &right_num) && left_num != right_num;
case test_number_greater:
return parse_number(left, &left_num) && parse_number(right, &right_num) && left_num > right_num;
case test_number_greater_equal:
return parse_number(left, &left_num) && parse_number(right, &right_num) && left_num >= right_num;
case test_number_lesser:
return parse_number(left, &left_num) && parse_number(right, &right_num) && left_num < right_num;
case test_number_lesser_equal:
return parse_number(left, &left_num) && parse_number(right, &right_num) && left_num <= right_num;
default:
errors.push_back(format_string(L"Unknown token type in %s", __func__));
return false;
@ -627,60 +627,60 @@ namespace test_expressions {
switch (token) {
case test_filetype_b: // "-b", for block special files
return !wstat(arg, &buf) && S_ISBLK(buf.st_mode);
case test_filetype_c: // "-c" for character special files
return !wstat(arg, &buf) && S_ISCHR(buf.st_mode);
case test_filetype_d: // "-d" for directories
return !wstat(arg, &buf) && S_ISDIR(buf.st_mode);
case test_filetype_e: // "-e" for files that exist
return !wstat(arg, &buf);
case test_filetype_f: // "-f" for for regular files
return !wstat(arg, &buf) && S_ISREG(buf.st_mode);
case test_filetype_g: // "-g" for set-group-id
return !wstat(arg, &buf) && (S_ISGID & buf.st_mode);
case test_filetype_h: // "-h" for symbolic links
case test_filetype_L: // "-L", same as -h
return !lwstat(arg, &buf) && S_ISLNK(buf.st_mode);
case test_filetype_p: // "-p", for FIFO
return !wstat(arg, &buf) && S_ISFIFO(buf.st_mode);
case test_filetype_S: // "-S", socket
return !wstat(arg, &buf) && S_ISSOCK(buf.st_mode);
case test_filesize_s: // "-s", size greater than zero
return !wstat(arg, &buf) && buf.st_size > 0;
case test_filedesc_t: // "-t", whether the fd is associated with a terminal
return parse_number(arg, &num) && num == (int)num && isatty((int)num);
case test_fileperm_r: // "-r", read permission
return !waccess(arg, R_OK);
case test_fileperm_u: // "-u", whether file is setuid
return !wstat(arg, &buf) && (S_ISUID & buf.st_mode);
case test_fileperm_w: // "-w", whether file write permission is allowed
return !waccess(arg, W_OK);
case test_fileperm_x: // "-x", whether file execute/search is allowed
return !waccess(arg, X_OK);
case test_string_n: // "-n", non-empty string
return ! arg.empty();
case test_string_z: // "-z", true if length of string is 0
return arg.empty();
default:
errors.push_back(format_string(L"Unknown token type in %s", __func__));
return false;
}
}
}
};
@ -698,11 +698,11 @@ namespace test_expressions {
int builtin_test( parser_t &parser, wchar_t **argv )
{
using namespace test_expressions;
/* The first argument should be the name of the command ('test') */
if (! argv[0])
return BUILTIN_TEST_FAIL;
size_t argc = 0;
while (argv[argc + 1])
argc++;

View file

@ -1,6 +1,6 @@
/** \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"
@ -27,99 +27,99 @@ Functions used for implementing the ulimit builtin.
*/
struct resource_t
{
/**
Resource id
*/
int resource;
/**
Description of resource
*/
const wchar_t *desc;
/**
Switch used on commandline to specify resource
*/
wchar_t switch_char;
/**
The implicit multiplier used when setting getting values
*/
int multiplier;
/**
Resource id
*/
int resource;
/**
Description of resource
*/
const wchar_t *desc;
/**
Switch used on commandline to specify resource
*/
wchar_t switch_char;
/**
The implicit multiplier used when setting getting values
*/
int multiplier;
}
;
;
/**
Array of resource_t structs, describing all known resource types.
*/
static const struct resource_t resource_arr[] =
{
{
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_FSIZE, L"Maximum size of files created by the shell", L'f', 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_FSIZE, L"Maximum size of files created by the shell", L'f', 1024
}
,
#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
#ifdef RLIMIT_RSS
{
RLIMIT_RSS, L"Maximum resident set size", L'm', 1024
}
,
{
RLIMIT_RSS, L"Maximum resident set size", L'm', 1024
}
,
#endif
{
RLIMIT_NOFILE, L"Maximum number of open file descriptors", L'n', 1
}
,
{
RLIMIT_STACK, L"Maximum stack size", L's', 1024
}
,
{
RLIMIT_CPU, L"Maximum amount of cpu time in seconds", L't', 1
}
,
{
RLIMIT_NOFILE, L"Maximum number of open file descriptors", L'n', 1
}
,
{
RLIMIT_STACK, L"Maximum stack size", L's', 1024
}
,
{
RLIMIT_CPU, L"Maximum amount of cpu time in seconds", L't', 1
}
,
#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
#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
{
0, 0, 0, 0
}
{
0, 0, 0, 0
}
}
;
;
/**
Get the implicit multiplication factor for the specified resource limit
*/
static int get_multiplier( int what )
{
int i;
for( i=0; resource_arr[i].desc; i++ )
{
if( resource_arr[i].resource == what )
{
return resource_arr[i].multiplier;
}
}
return -1;
int i;
for( i=0; resource_arr[i].desc; i++ )
{
if( resource_arr[i].resource == what )
{
return resource_arr[i].multiplier;
}
}
return -1;
}
/**
@ -129,11 +129,11 @@ static int get_multiplier( int what )
*/
static rlim_t get( int resource, int hard )
{
struct rlimit ls;
getrlimit( resource, &ls );
return hard ? ls.rlim_max:ls.rlim_cur;
struct rlimit ls;
getrlimit( resource, &ls );
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 )
{
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 )
{
int i;
int w=0;
for( i=0; resource_arr[i].desc; i++ )
{
w=maxi( w, my_wcswidth(resource_arr[i].desc));
}
for( i=0; resource_arr[i].desc; i++ )
{
struct rlimit ls;
rlim_t l;
getrlimit( resource_arr[i].resource, &ls );
l = hard ? ls.rlim_max:ls.rlim_cur;
int i;
int w=0;
for( i=0; resource_arr[i].desc; i++ )
{
w=maxi( w, my_wcswidth(resource_arr[i].desc));
}
for( i=0; resource_arr[i].desc; i++ )
{
struct rlimit ls;
rlim_t l;
getrlimit( resource_arr[i].resource, &ls );
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 )
{
int i;
for( i=0; resource_arr[i].desc; i++ )
{
if( resource_arr[i].resource == what )
{
return resource_arr[i].desc;
}
}
return L"Not a resource";
int i;
for( i=0; resource_arr[i].desc; i++ )
{
if( resource_arr[i].resource == what )
{
return resource_arr[i].desc;
}
}
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 )
{
struct rlimit ls;
getrlimit( resource, &ls );
if( hard )
{
ls.rlim_max = value;
}
if( soft )
{
ls.rlim_cur = value;
/*
Do not attempt to set the soft limit higher than the hard limit
*/
if( ( value == RLIM_INFINITY && ls.rlim_max != RLIM_INFINITY ) ||
( value != RLIM_INFINITY && ls.rlim_max != RLIM_INFINITY && value > ls.rlim_max))
{
ls.rlim_cur = ls.rlim_max;
}
}
if( setrlimit( resource, &ls ) )
{
if( errno == EPERM )
append_format(stderr_buffer, L"ulimit: Permission denied when changing resource of type '%ls'\n", get_desc( resource ) );
else
builtin_wperror( L"ulimit" );
return 1;
}
return 0;
struct rlimit ls;
getrlimit( resource, &ls );
if( hard )
{
ls.rlim_max = value;
}
if( soft )
{
ls.rlim_cur = value;
/*
Do not attempt to set the soft limit higher than the hard limit
*/
if( ( value == RLIM_INFINITY && ls.rlim_max != RLIM_INFINITY ) ||
( value != RLIM_INFINITY && ls.rlim_max != RLIM_INFINITY && value > ls.rlim_max))
{
ls.rlim_cur = ls.rlim_max;
}
}
if( setrlimit( resource, &ls ) )
{
if( errno == EPERM )
append_format(stderr_buffer, L"ulimit: Permission denied when changing resource of type '%ls'\n", get_desc( resource ) );
else
builtin_wperror( L"ulimit" );
return 1;
}
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 )
{
int hard=0;
int soft=0;
int what = RLIMIT_FSIZE;
int report_all = 0;
int hard=0;
int soft=0;
int argc = builtin_count_args( argv );
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 what = RLIMIT_FSIZE;
int report_all = 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;
int argc = builtin_count_args( argv );
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 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,
BUILTIN_ERR_UNKNOWN,
argv[0],
long_options[opt_index].name );
builtin_print_help( parser, argv[0], stderr_buffer );
builtin_print_help( parser, argv[0], stderr_buffer );
return 1;
case L'a':
report_all=1;
break;
return 1;
case L'H':
hard=1;
break;
case L'a':
report_all=1;
break;
case L'S':
soft=1;
break;
case L'H':
hard=1;
break;
case L'c':
what=RLIMIT_CORE;
break;
case L'd':
what=RLIMIT_DATA;
break;
case L'f':
what=RLIMIT_FSIZE;
break;
case L'S':
soft=1;
break;
case L'c':
what=RLIMIT_CORE;
break;
case L'd':
what=RLIMIT_DATA;
break;
case L'f':
what=RLIMIT_FSIZE;
break;
#ifdef RLIMIT_MEMLOCK
case L'l':
what=RLIMIT_MEMLOCK;
break;
case L'l':
what=RLIMIT_MEMLOCK;
break;
#endif
#ifdef RLIMIT_RSS
case L'm':
what=RLIMIT_RSS;
break;
#ifdef RLIMIT_RSS
case L'm':
what=RLIMIT_RSS;
break;
#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'?':
builtin_unknown_option( parser, argv[0], argv[woptind-1] );
return 1;
}
}
case L'n':
what=RLIMIT_NOFILE;
break;
if( report_all )
{
if( argc - woptind == 0 )
{
print_all( hard );
}
else
{
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'?':
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(L": Too many arguments\n");
builtin_print_help( parser, argv[0], stderr_buffer );
return 1;
}
builtin_print_help( parser, argv[0], stderr_buffer );
return 1;
}
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;
return 0;
}
/*
Set both hard and soft limits if nothing else was specified
*/
if( !(hard+soft) )
{
hard=soft=1;
}
if( wcscasecmp( argv[woptind], L"unlimited" )==0)
{
new_limit = RLIM_INFINITY;
}
else if( wcscasecmp( argv[woptind], L"hard" )==0)
{
new_limit = get( what, 1 );
}
else if( wcscasecmp( argv[woptind], L"soft" )==0)
{
new_limit = get( what, soft );
}
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:
{
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;
/*
Set both hard and soft limits if nothing else was specified
*/
if( !(hard+soft) )
{
hard=soft=1;
}
if( wcscasecmp( argv[woptind], L"unlimited" )==0)
{
new_limit = RLIM_INFINITY;
}
else if( wcscasecmp( argv[woptind], L"hard" )==0)
{
new_limit = get( what, 1 );
}
else if( wcscasecmp( argv[woptind], L"soft" )==0)
{
new_limit = get( what, soft );
}
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(L": Too many arguments\n");
builtin_print_help( parser, argv[0], stderr_buffer );
return 1;
}
}
return 0;
builtin_print_help( parser, argv[0], stderr_buffer );
return 1;
}
}
return 0;
}

View file

@ -72,13 +72,13 @@ bool rgb_color_t::try_parse_rgb(const wcstring &name) {
FA3
F3A035
*/
size_t digit_idx = 0, len = name.size();
/* Skip any leading # */
if (len > 0 && name.at(0) == L'#')
digit_idx++;
bool success = false;
size_t i;
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;
}
success = (i == 3);
}
}
if (success) {
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;
}
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::ignore() { return rgb_color_t(type_ignore); }
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,
0x5faf87, 0x5fafaf, 0x5fafd7, 0x5fafff, 0x5fd700, 0x5fd75f, 0x5fd787, 0x5fd7af,
0x5fd7d7, 0x5fd7ff, 0x5fff00, 0x5fff5f, 0x5fff87, 0x5fffaf, 0x5fffd7, 0x5fffff,
0x870000, 0x87005f, 0x870087, 0x8700af, 0x8700d7, 0x8700ff, 0x875f00, 0x875f5f,
0x875f87, 0x875faf, 0x875fd7, 0x875fff, 0x878700, 0x87875f, 0x878787, 0x8787af,
0x8787d7, 0x8787ff, 0x87af00, 0x87af5f, 0x87af87, 0x87afaf, 0x87afd7, 0x87afff,
0x87d700, 0x87d75f, 0x87d787, 0x87d7af, 0x87d7d7, 0x87d7ff, 0x87ff00, 0x87ff5f,
0x87ff87, 0x87ffaf, 0x87ffd7, 0x87ffff, 0xaf0000, 0xaf005f, 0xaf0087, 0xaf00af,
0xaf00d7, 0xaf00ff, 0xaf5f00, 0xaf5f5f, 0xaf5f87, 0xaf5faf, 0xaf5fd7, 0xaf5fff,
0xaf8700, 0xaf875f, 0xaf8787, 0xaf87af, 0xaf87d7, 0xaf87ff, 0xafaf00, 0xafaf5f,
0xafaf87, 0xafafaf, 0xafafd7, 0xafafff, 0xafd700, 0xafd75f, 0xafd787, 0xafd7af,
0xafd7d7, 0xafd7ff, 0xafff00, 0xafff5f, 0xafff87, 0xafffaf, 0xafffd7, 0xafffff,
0xd70000, 0xd7005f, 0xd70087, 0xd700af, 0xd700d7, 0xd700ff, 0xd75f00, 0xd75f5f,
0xd75f87, 0xd75faf, 0xd75fd7, 0xd75fff, 0xd78700, 0xd7875f, 0xd78787, 0xd787af,
0xd787d7, 0xd787ff, 0xd7af00, 0xd7af5f, 0xd7af87, 0xd7afaf, 0xd7afd7, 0xd7afff,
0xd7d700, 0xd7d75f, 0xd7d787, 0xd7d7af, 0xd7d7d7, 0xd7d7ff, 0xd7ff00, 0xd7ff5f,
0xd7ff87, 0xd7ffaf, 0xd7ffd7, 0xd7ffff, 0xff0000, 0xff005f, 0xff0087, 0xff00af,
0xff00d7, 0xff00ff, 0xff5f00, 0xff5f5f, 0xff5f87, 0xff5faf, 0xff5fd7, 0xff5fff,
0xff8700, 0xff875f, 0xff8787, 0xff87af, 0xff87d7, 0xff87ff, 0xffaf00, 0xffaf5f,
0xffaf87, 0xffafaf, 0xffafd7, 0xffafff, 0xffd700, 0xffd75f, 0xffd787, 0xffd7af,
0xffd7d7, 0xffd7ff, 0xffff00, 0xffff5f, 0xffff87, 0xffffaf, 0xffffd7, 0xffffff,
0x080808, 0x121212, 0x1c1c1c, 0x262626, 0x303030, 0x3a3a3a, 0x444444, 0x4e4e4e,
0x585858, 0x626262, 0x6c6c6c, 0x767676, 0x808080, 0x8a8a8a, 0x949494, 0x9e9e9e,
0x870000, 0x87005f, 0x870087, 0x8700af, 0x8700d7, 0x8700ff, 0x875f00, 0x875f5f,
0x875f87, 0x875faf, 0x875fd7, 0x875fff, 0x878700, 0x87875f, 0x878787, 0x8787af,
0x8787d7, 0x8787ff, 0x87af00, 0x87af5f, 0x87af87, 0x87afaf, 0x87afd7, 0x87afff,
0x87d700, 0x87d75f, 0x87d787, 0x87d7af, 0x87d7d7, 0x87d7ff, 0x87ff00, 0x87ff5f,
0x87ff87, 0x87ffaf, 0x87ffd7, 0x87ffff, 0xaf0000, 0xaf005f, 0xaf0087, 0xaf00af,
0xaf00d7, 0xaf00ff, 0xaf5f00, 0xaf5f5f, 0xaf5f87, 0xaf5faf, 0xaf5fd7, 0xaf5fff,
0xaf8700, 0xaf875f, 0xaf8787, 0xaf87af, 0xaf87d7, 0xaf87ff, 0xafaf00, 0xafaf5f,
0xafaf87, 0xafafaf, 0xafafd7, 0xafafff, 0xafd700, 0xafd75f, 0xafd787, 0xafd7af,
0xafd7d7, 0xafd7ff, 0xafff00, 0xafff5f, 0xafff87, 0xafffaf, 0xafffd7, 0xafffff,
0xd70000, 0xd7005f, 0xd70087, 0xd700af, 0xd700d7, 0xd700ff, 0xd75f00, 0xd75f5f,
0xd75f87, 0xd75faf, 0xd75fd7, 0xd75fff, 0xd78700, 0xd7875f, 0xd78787, 0xd787af,
0xd787d7, 0xd787ff, 0xd7af00, 0xd7af5f, 0xd7af87, 0xd7afaf, 0xd7afd7, 0xd7afff,
0xd7d700, 0xd7d75f, 0xd7d787, 0xd7d7af, 0xd7d7d7, 0xd7d7ff, 0xd7ff00, 0xd7ff5f,
0xd7ff87, 0xd7ffaf, 0xd7ffd7, 0xd7ffff, 0xff0000, 0xff005f, 0xff0087, 0xff00af,
0xff00d7, 0xff00ff, 0xff5f00, 0xff5f5f, 0xff5f87, 0xff5faf, 0xff5fd7, 0xff5fff,
0xff8700, 0xff875f, 0xff8787, 0xff87af, 0xff87d7, 0xff87ff, 0xffaf00, 0xffaf5f,
0xffaf87, 0xffafaf, 0xffafd7, 0xffafff, 0xffd700, 0xffd75f, 0xffd787, 0xffd7af,
0xffd7d7, 0xffd7ff, 0xffff00, 0xffff5f, 0xffff87, 0xffffaf, 0xffffd7, 0xffffff,
0x080808, 0x121212, 0x1c1c1c, 0x262626, 0x303030, 0x3a3a3a, 0x444444, 0x4e4e4e,
0x585858, 0x626262, 0x6c6c6c, 0x767676, 0x808080, 0x8a8a8a, 0x949494, 0x9e9e9e,
0xa8a8a8, 0xb2b2b2, 0xbcbcbc, 0xc6c6c6, 0xd0d0d0, 0xdadada, 0xe4e4e4, 0xeeeeee
};
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
};
unsigned char type:4;
/* Flags */
enum {
flag_bold = 1 << 0,
flag_underline = 1 << 1
};
unsigned char flags:4;
union {
unsigned char name_idx; //0-10
unsigned char rgb[3];
} data;
/** Try parsing a special color name like "normal" */
bool try_parse_special(const wcstring &str);
/** Try parsing an rgb color like "#F0A030" */
bool try_parse_rgb(const wcstring &str);
@ -49,52 +49,52 @@ class rgb_color_t {
/** Private constructor */
explicit rgb_color_t(unsigned char t, unsigned char i=0);
public:
/** Default constructor of type none */
explicit rgb_color_t() : type(type_none), flags(), data() {}
/** Parse a color from a string */
explicit rgb_color_t(const wcstring &str);
explicit rgb_color_t(const std::string &str);
/** Returns white */
/** Returns white */
static rgb_color_t white();
/** Returns black */
static rgb_color_t black();
/** Returns the reset special color */
static rgb_color_t reset();
/** Returns the normal special color */
static rgb_color_t normal();
/** Returns the ignore special color */
static rgb_color_t ignore();
/** Returns the none special color */
static rgb_color_t none();
/** Returns whether the color is the ignore special color */
bool is_ignore(void) const { return type == type_ignore; }
/** Returns whether the color is the normal special color */
bool is_normal(void) const { return type == type_normal; }
/** Returns whether the color is the reset special color */
bool is_reset(void) const { return type == type_reset; }
/** Returns whether the color is the none special color */
bool is_none(void) const { return type == type_none; }
/** Returns whether the color is a named color (like "magenta") */
bool is_named(void) const { return type == type_named; }
/** Returns whether the color is specified via RGB components */
bool is_rgb(void) const { return type == type_rgb; }
/** Returns whether the color is special, that is, not rgb or named */
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. */
unsigned char to_name_index() const;
/** Returns the term256 index for the given color. Requires that the color be named or RGB. */
unsigned char to_term256_index() const;
/** Returns whether the color is bold */
bool is_bold() const { return flags & flag_bold; }
/** Set whether the color is bold */
void set_bold(bool x) { if (x) flags |= flag_bold; else flags &= ~flag_bold; }
/** Returns whether the color is underlined */
bool is_underline() const { return !! (flags & flag_underline); }
/** Set whether the color is underlined */
void set_underline(bool x) { if (x) flags |= flag_underline; else flags &= ~flag_underline; }
/** Compare two colors for equality */
bool operator==(const rgb_color_t &other) const {
return type == other.type && ! memcmp(&data, &other.data, sizeof data);
}
/** Compare two colors for inequality */
bool operator!=(const rgb_color_t &other) const {
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
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
@ -67,10 +67,10 @@ typedef std::vector<wcstring> wcstring_list_t;
enum {
/** Escape all characters, including magic characters like the semicolon */
ESCAPE_ALL = 1 << 0,
/** Do not try to use 'simplified' quoted escapes, and do not use empty quotes as the empty string */
ESCAPE_NO_QUOTED = 1 << 1,
/** Do not escape tildes */
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. */
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
@ -118,57 +118,57 @@ extern const wchar_t *program_name;
failiure, the current function is ended at once. The second
parameter is the return value of the current function on failiure.
*/
#define CHECK( arg, retval ) \
if( !(arg) ) \
{ \
debug( 0, \
"function %s called with null value for argument %s. ", \
__func__, \
#arg ); \
bugreport(); \
show_stackframe(); \
return retval; \
}
#define CHECK( arg, retval ) \
if( !(arg) ) \
{ \
debug( 0, \
"function %s called with null value for argument %s. ", \
__func__, \
#arg ); \
bugreport(); \
show_stackframe(); \
return retval; \
}
/**
Pause for input, then exit the program. If supported, print a backtrace first.
*/
#define FATAL_EXIT() \
{ \
char exit_read_buff; \
show_stackframe(); \
read( 0, &exit_read_buff, 1 ); \
exit_without_destructors( 1 ); \
} \
#define FATAL_EXIT() \
{ \
char exit_read_buff; \
show_stackframe(); \
read( 0, &exit_read_buff, 1 ); \
exit_without_destructors( 1 ); \
} \
/**
Exit program at once, leaving an error message about running out of memory.
*/
#define DIE_MEM() \
{ \
fwprintf( stderr, \
L"fish: Out of memory on line %ld of file %s, shutting down fish\n", \
(long)__LINE__, \
__FILE__ ); \
FATAL_EXIT(); \
}
#define DIE_MEM() \
{ \
fwprintf( stderr, \
L"fish: Out of memory on line %ld of file %s, shutting down fish\n", \
(long)__LINE__, \
__FILE__ ); \
FATAL_EXIT(); \
}
/**
Check if signals are blocked. If so, print an error message and
return from the function performing this check.
*/
#define CHECK_BLOCK( retval ) \
if( signal_is_blocked() ) \
{ \
debug( 0, \
"function %s called while blocking signals. ", \
__func__); \
bugreport(); \
show_stackframe(); \
return retval; \
}
#define CHECK_BLOCK( retval ) \
if( signal_is_blocked() ) \
{ \
debug( 0, \
"function %s called while blocking signals. ", \
__func__); \
bugreport(); \
show_stackframe(); \
return retval; \
}
/**
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,
even though it is not directly sent to wgettext.
even though it is not directly sent to wgettext.
*/
#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.
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
specified multibyte character string
This function encodes illegal character sequences in a reversible
way using the private use area.
*/
@ -344,7 +344,7 @@ inline wcstring to_string(const int &x) {
template <typename CharType_t>
class null_terminated_array_t {
CharType_t **array;
typedef std::basic_string<CharType_t> string_t;
typedef std::vector<string_t> string_list_t;
@ -356,7 +356,7 @@ class null_terminated_array_t {
size_t len;
for (len=0; arr[len] != T(0); len++)
;
return len;
return len;
}
size_t size() const {
@ -372,24 +372,24 @@ class null_terminated_array_t {
array = NULL;
}
}
public:
null_terminated_array_t() : array(NULL) { }
null_terminated_array_t(const string_list_t &argv) : array(NULL) { this->set(argv); }
~null_terminated_array_t() { this->free(); }
/** operator=. Notice the pass-by-value parameter. */
null_terminated_array_t& operator=(null_terminated_array_t rhs) {
if (this != &rhs)
this->swap(rhs);
return *this;
}
/* Copy constructor. */
null_terminated_array_t(const null_terminated_array_t &him) : array(NULL) {
this->set(him.array);
}
void set(const string_list_t &argv) {
/* Get rid of the old argv */
this->free();
@ -405,14 +405,14 @@ class null_terminated_array_t {
}
this->array[count] = NULL;
}
void set(const CharType_t * const *new_array) {
if (new_array == array)
return;
/* Get rid of the old argv */
this->free();
/* Copy the new one */
if (new_array) {
size_t i, count = count_not_null(new_array);
@ -426,10 +426,10 @@ class null_terminated_array_t {
this->array[count] = NULL;
}
}
CharType_t **get() { return array; }
const CharType_t * const *get() const { return array; }
string_list_t to_list() const {
string_list_t lst;
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 {
private:
const char *str;
/* No copying */
narrow_string_rep_t &operator=(const narrow_string_rep_t &);
narrow_string_rep_t(const narrow_string_rep_t &x);
public:
~narrow_string_rep_t() {
free((void *)str);
}
narrow_string_rep_t() : str(NULL) {}
void set(const wcstring &s) {
free((void *)str);
str = wcs2str(s.c_str());
}
const char *get() const {
return str;
}
@ -476,7 +476,7 @@ bool is_forked_child();
class scoped_lock {
pthread_mutex_t *lock_obj;
bool locked;
/* No copying */
scoped_lock &operator=(const scoped_lock &);
scoped_lock(const scoped_lock &);
@ -492,18 +492,18 @@ public:
class wcstokenizer {
wchar_t *buffer, *str, *state;
const wcstring sep;
/* No copying */
wcstokenizer &operator=(const wcstokenizer &);
wcstokenizer(const wcstokenizer &);
public:
wcstokenizer(const wcstring &s, const wcstring &separator);
bool next(wcstring &result);
~wcstokenizer();
};
/**
/**
Appends a path component, with a / if necessary
*/
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 );
/**
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
*/
@ -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
*/
@ -536,7 +536,7 @@ wchar_t *wcsvarname( 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
*/
@ -572,14 +572,14 @@ void error_reset();
This function behaves exactly like a wide character equivalent of
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
unicode ellipsis character as ellipsis, instead of '$'.
unicode ellipsis character as ellipsis, instead of '$'.
*/
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.
\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
*/
@ -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,
before using wperror to give a specific error message, debug will
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 msg the message format string.
\param msg the message format string.
Example:
@ -629,7 +629,7 @@ void debug( int level, const wchar_t *msg, ... );
/**
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 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.
*/
wchar_t *unescape( const wchar_t * in,
int escape_special );
wchar_t *unescape( const wchar_t * in,
int escape_special );
bool unescape_string( wcstring &str,
bool unescape_string( wcstring &str,
int escape_special );
/**
/**
Returns the width of the terminal window, so that not all
functions that use these values continually have to keep track of
it separately.
@ -688,9 +688,9 @@ void common_handle_winch( int signal );
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 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);
@ -717,7 +717,7 @@ void bugreport();
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().
*/
void set_main_thread();

File diff suppressed because it is too large Load diff

View file

@ -1,8 +1,8 @@
/** \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
data, as well as for performing tab-completion.
These functions are used for storing and retrieving tab-completion
data, as well as for performing tab-completion.
*/
#ifndef FISH_COMPLETE_H
@ -17,40 +17,40 @@
#include "util.h"
#include "common.h"
/**
Use all completions
/**
Use all completions
*/
#define SHARED 0
/**
Do not use file completion
/**
Do not use file completion
*/
#define NO_FILES 1
/**
Require a parameter after completion
/**
Require a parameter after completion
*/
#define NO_COMMON 2
/**
Only use the argument list specifies with completion after
option. This is the same as (NO_FILES & NO_COMMON)
/**
Only use the argument list specifies with completion after
option. This is the same as (NO_FILES & NO_COMMON)
*/
#define EXCLUSIVE 3
/**
Command is a path
/**
Command is a path
*/
#define PATH 1
/**
Command is not a path
/**
Command is not a path
*/
#define COMMAND 0
/**
Separator between completion and description
/**
Separator between completion and description
*/
#define COMPLETE_SEP L'\004'
/**
Separator between completion and description
/**
Separator between completion and description
*/
#define COMPLETE_SEP_STR L"\004"
@ -75,7 +75,7 @@ enum {
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
different if this flag is set! Specifically, the completion string
@ -109,42 +109,42 @@ class completion_t
private:
/* No public default constructor */
completion_t();
completion_t();
public:
/**
The completion string
*/
wcstring completion;
/**
The completion string
*/
wcstring completion;
/**
The description for this completion
*/
wcstring description;
/**
The description for this completion
*/
wcstring description;
/**
Flags determining the completion behaviour.
/**
Flags determining the completion behaviour.
Determines whether a space should be inserted after this
compeltion if it is the only possible completion using the
COMPLETE_NO_SPACE flag.
Determines whether a space should be inserted after this
compeltion if it is the only possible completion using the
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); }
/* 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 completion_t &);
completion_t &operator=(const completion_t &);
/* 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 {
@ -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
disposed by the caller.
Examples:
Examples:
The command 'gcc -o' requires that a file follows it, so the
NO_COMMON option is suitable. This can be done using the following
line:
complete -c gcc -s o -r
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.
\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_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
completion has been succesfully matched. If result_mode is SHARED,
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 flags A set of completion flags
*/
void complete_add( const wchar_t *cmd,
void complete_add( const wchar_t *cmd,
bool cmd_is_path,
wchar_t short_opt,
const wchar_t *long_opt,
@ -209,7 +209,7 @@ void complete_add( const wchar_t *cmd,
const wchar_t *condition,
const wchar_t *comp,
const wchar_t *desc,
int flags );
int flags );
/**
Sets whether the completion list for this command is complete. If
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
*/
void complete_remove( const wchar_t *cmd,
bool cmd_is_path,
void complete_remove( const wchar_t *cmd,
bool cmd_is_path,
wchar_t short_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 );
/**
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
*/
@ -240,17 +240,17 @@ void complete_print( wcstring &out );
Tests if the specified option is defined for the specified command
*/
int complete_is_valid_option( const wcstring &str,
const wcstring &opt,
wcstring_list_t *inErrorsOrNull,
bool allow_autoload );
const wcstring &opt,
wcstring_list_t *inErrorsOrNull,
bool allow_autoload );
/**
Tests if the specified argument is valid for the specified option
and command
*/
bool complete_is_valid_argument( const wcstring &str,
const wcstring &opt,
const wcstring &arg );
const wcstring &opt,
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
Prototypes for functions for setting and getting environment variables.
Prototypes for functions for setting and getting environment variables.
*/
#ifndef FISH_ENV_H
@ -47,8 +47,8 @@
Error code for trying to alter read-only variable
*/
enum{
ENV_PERM = 1,
ENV_INVALID
ENV_PERM = 1,
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
@ -119,7 +119,7 @@ public:
wcstring::operator=(s);
return *this;
}
bool operator==(const env_var_t &s) const {
if (is_missing && s.is_missing)
return true;
@ -146,7 +146,7 @@ int env_exist( const wchar_t *key, int mode );
/**
Remove environemnt variable
\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.
@ -188,12 +188,12 @@ class env_vars_snapshot_t {
public:
env_vars_snapshot_t(const wchar_t * const * keys);
env_vars_snapshot_t(void);
env_var_t get(const wcstring &key) const;
// Returns the fake snapshot representing the live variables array
static const env_vars_snapshot_t &current();
// vars necessary for highlighting
static const wchar_t * const highlighting_keys[];
};

View file

@ -72,7 +72,7 @@ void env_universal_barrier();
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 )
{
int s, len;
struct sockaddr_un local;
char *name;
wchar_t *wdir;
wchar_t *wuname;
char *dir =0, *uname=0;
int s, len;
struct sockaddr_un local;
get_socket_count++;
wdir = path;
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;
}
char *name;
wchar_t *wdir;
wchar_t *wuname;
char *dir =0, *uname=0;
debug( 3, L"Connected to fd %d", s );
return s;
get_socket_count++;
wdir = path;
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
*/
static void callback( fish_message_type_t type, const wchar_t *name, const wchar_t *val )
{
if( type == BARRIER_REPLY )
{
barrier_reply = 1;
}
else
{
if( external_callback )
external_callback( type, name, val );
}
{
if( type == BARRIER_REPLY )
{
barrier_reply = 1;
}
else
{
if( external_callback )
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()
{
if( !init )
return;
if( env_universal_server.killme )
{
debug( 3, L"Lost connection to universal variable server." );
if( close( env_universal_server.fd ) )
{
wperror( L"close" );
}
env_universal_server.fd = -1;
env_universal_server.killme=0;
env_universal_server.input.clear();
env_universal_read_all();
}
if( !init )
return;
if( env_universal_server.killme )
{
debug( 3, L"Lost connection to universal variable server." );
if( close( env_universal_server.fd ) )
{
wperror( L"close" );
}
env_universal_server.fd = -1;
env_universal_server.killme=0;
env_universal_server.input.clear();
env_universal_read_all();
}
}
/**
@ -208,18 +208,18 @@ static void check_connection()
*/
static void env_universal_remove_all()
{
size_t i;
wcstring_list_t lst;
env_universal_common_get_names( lst,
1,
1 );
for( i=0; i<lst.size(); i++ )
{
const wcstring &key = lst.at(i);
env_universal_common_remove( key );
}
size_t i;
wcstring_list_t lst;
env_universal_common_get_names( lst,
1,
1 );
for( i=0; i<lst.size(); i++ )
{
const wcstring &key = lst.at(i);
env_universal_common_remove( key );
}
}
@ -230,63 +230,63 @@ static void env_universal_remove_all()
*/
static void reconnect()
{
if( get_socket_count >= RECONNECT_COUNT )
return;
debug( 3, L"Get new fishd connection" );
init = 0;
env_universal_server.buffer_consumed = env_universal_server.buffer_used = 0;
env_universal_server.fd = get_socket(1);
init = 1;
if( env_universal_server.fd >= 0 )
{
env_universal_remove_all();
env_universal_barrier();
}
if( get_socket_count >= RECONNECT_COUNT )
return;
debug( 3, L"Get new fishd connection" );
init = 0;
env_universal_server.buffer_consumed = env_universal_server.buffer_used = 0;
env_universal_server.fd = get_socket(1);
init = 1;
if( env_universal_server.fd >= 0 )
{
env_universal_remove_all();
env_universal_barrier();
}
}
void env_universal_init( wchar_t * p,
wchar_t *u,
void (*sf)(),
void (*cb)( fish_message_type_t type, const wchar_t *name, const wchar_t *val ))
void env_universal_init( wchar_t * p,
wchar_t *u,
void (*sf)(),
void (*cb)( fish_message_type_t type, const wchar_t *name, const wchar_t *val ))
{
path=p;
user=u;
start_fishd=sf;
external_callback = cb;
path=p;
user=u;
start_fishd=sf;
external_callback = cb;
connection_init( &env_universal_server, -1 );
env_universal_server.fd = get_socket(1);
env_universal_common_init( &callback );
env_universal_read_all();
init = 1;
if( env_universal_server.fd >= 0 )
{
env_universal_barrier();
}
connection_init( &env_universal_server, -1 );
env_universal_server.fd = get_socket(1);
env_universal_common_init( &callback );
env_universal_read_all();
init = 1;
if( env_universal_server.fd >= 0 )
{
env_universal_barrier();
}
}
void env_universal_destroy()
{
/*
Go into blocking mode and send all data before exiting
*/
if( env_universal_server.fd >= 0 )
{
if( fcntl( env_universal_server.fd, F_SETFL, 0 ) != 0 )
{
wperror( L"fcntl" );
}
try_send_all( &env_universal_server );
}
/*
Go into blocking mode and send all data before exiting
*/
if( env_universal_server.fd >= 0 )
{
if( fcntl( env_universal_server.fd, F_SETFL, 0 ) != 0 )
{
wperror( L"fcntl" );
}
try_send_all( &env_universal_server );
}
connection_destroy( &env_universal_server );
env_universal_server.fd =-1;
env_universal_common_destroy();
init = 0;
connection_destroy( &env_universal_server );
env_universal_server.fd =-1;
env_universal_common_destroy();
init = 0;
}
@ -295,177 +295,177 @@ void env_universal_destroy()
*/
int env_universal_read_all()
{
if( !init)
return 0;
if( !init)
return 0;
if( env_universal_server.fd == -1 )
{
reconnect();
if( env_universal_server.fd == -1 )
return 0;
}
if( env_universal_server.fd != -1 )
{
read_message( &env_universal_server );
check_connection();
return 1;
}
else
{
debug( 2, L"No connection to universal variable server" );
return 0;
}
if( env_universal_server.fd == -1 )
{
reconnect();
if( env_universal_server.fd == -1 )
return 0;
}
if( env_universal_server.fd != -1 )
{
read_message( &env_universal_server );
check_connection();
return 1;
}
else
{
debug( 2, L"No connection to universal variable server" );
return 0;
}
}
wchar_t *env_universal_get( const wcstring &name )
{
if( !init)
return 0;
if( !init)
return 0;
return env_universal_common_get( name );
return env_universal_common_get( name );
}
int env_universal_get_export( const wcstring &name )
{
if( !init)
return 0;
return env_universal_common_get_export( name );
if( !init)
return 0;
return env_universal_common_get_export( name );
}
void env_universal_barrier()
{
ASSERT_IS_MAIN_THREAD();
message_t *msg;
fd_set fds;
message_t *msg;
fd_set fds;
if( !init || is_dead() )
return;
if( !init || is_dead() )
return;
barrier_reply = 0;
barrier_reply = 0;
/*
Create barrier request
*/
msg= create_message( BARRIER, 0, 0);
msg->count=1;
/*
Create barrier request
*/
msg= create_message( BARRIER, 0, 0);
msg->count=1;
env_universal_server.unsent->push(msg);
/*
Wait until barrier request has been sent
*/
debug( 3, L"Create barrier" );
while( 1 )
{
try_send_all( &env_universal_server );
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 );
}
/*
Wait until barrier request has been sent
*/
debug( 3, L"Create barrier" );
while( 1 )
{
try_send_all( &env_universal_server );
check_connection();
/*
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 );
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 );
}
/*
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 );
env_universal_read_all();
}
debug( 3, L"End barrier" );
env_universal_read_all();
}
debug( 3, L"End barrier" );
}
void env_universal_set( const wcstring &name, const wcstring &value, int exportv )
{
message_t *msg;
if( !init )
return;
message_t *msg;
debug( 3, L"env_universal_set( \"%ls\", \"%ls\" )", name.c_str(), value.c_str() );
if( !init )
return;
if( is_dead() )
{
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());
debug( 3, L"env_universal_set( \"%ls\", \"%ls\" )", name.c_str(), value.c_str() );
if( !msg )
{
debug( 1, L"Could not create universal variable message" );
return;
}
msg->count=1;
if( is_dead() )
{
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 )
{
debug( 1, L"Could not create universal variable message" );
return;
}
msg->count=1;
env_universal_server.unsent->push(msg);
env_universal_barrier();
}
env_universal_barrier();
}
}
int env_universal_remove( const wchar_t *name )
{
int res;
message_t *msg;
if( !init )
return 1;
CHECK( name, 1 );
int res;
res = !env_universal_common_get( name );
debug( 3,
L"env_universal_remove( \"%ls\" )",
name );
if( is_dead() )
{
env_universal_common_remove( wcstring(name) );
}
else
{
msg= create_message( ERASE, name, 0);
msg->count=1;
message_t *msg;
if( !init )
return 1;
CHECK( name, 1 );
res = !env_universal_common_get( name );
debug( 3,
L"env_universal_remove( \"%ls\" )",
name );
if( is_dead() )
{
env_universal_common_remove( wcstring(name) );
}
else
{
msg= create_message( ERASE, name, 0);
msg->count=1;
env_universal_server.unsent->push(msg);
env_universal_barrier();
}
return res;
env_universal_barrier();
}
return res;
}
void env_universal_get_names2( wcstring_list_t &lst,
int show_exported,
int show_unexported )
{
if( !init )
return;
if( !init )
return;
env_universal_common_get_names( lst,
show_exported,
show_unexported );
env_universal_common_get_names( lst,
show_exported,
show_unexported );
}

View file

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

File diff suppressed because it is too large Load diff

View file

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

694
event.cpp
View file

@ -1,6 +1,6 @@
/** \file event.c
Functions for handling event triggers
Functions for handling event triggers
*/
#include "config.h"
@ -36,20 +36,20 @@
*/
typedef struct
{
/**
Number of delivered signals
*/
int count;
/**
Whether signals have been skipped
*/
int overflow;
/**
Array of signal events
*/
int signal[SIG_UNHANDLED_MAX];
/**
Number of delivered signals
*/
int count;
/**
Whether signals have been skipped
*/
int overflow;
/**
Array of signal events
*/
int signal[SIG_UNHANDLED_MAX];
}
signal_list_t;
signal_list_t;
/**
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;
typedef std::vector<event_t *> event_list_t;
typedef std::vector<event_t *> event_list_t;
/**
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( ! classv->function_name.empty() &&
if( ! classv->function_name.empty() &&
! instance->function_name.empty() &&
classv->function_name != instance->function_name)
{
{
return 0;
}
}
if( classv->type == EVENT_ANY )
return 1;
if( classv->type != instance->type )
return 0;
if( classv->type == EVENT_ANY )
return 1;
switch( classv->type )
{
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;
if( classv->type != instance->type )
return 0;
case EVENT_JOB_ID:
return classv->param1.job_id == instance->param1.job_id;
case EVENT_GENERIC:
return instance->str_param1 == classv->str_param1;
switch( classv->type )
{
}
/**
This should never be reached
*/
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:
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 )
{
event_t *e = new event_t(*event);
e->arguments.reset(new wcstring_list_t);
if( copy_arguments && event->arguments.get() != NULL )
{
*(e->arguments) = *(event->arguments);
}
return e;
if( copy_arguments && event->arguments.get() != NULL )
{
*(e->arguments) = *(event->arguments);
}
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 )
{
block_t *block;
parser_t &parser = parser_t::principal_parser();
for( block = parser.current_block; block; block = block->outer )
{
block_t *block;
parser_t &parser = parser_t::principal_parser();
for( block = parser.current_block; block; block = block->outer )
{
if (event_block_list_blocks_type(block->event_blocks, e->type))
return true;
}
}
return event_block_list_blocks_type(parser.global_event_blocks, e->type);
}
wcstring event_get_desc( const event_t *e )
{
CHECK( e, 0 );
CHECK( e, 0 );
wcstring result;
switch( e->type )
{
case EVENT_SIGNAL:
wcstring result;
switch( e->type )
{
case EVENT_SIGNAL:
result = format_string(_(L"signal handler for %ls (%ls)"), sig2wcs(e->param1.signal ), signal_get_desc( e->param1.signal ));
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;
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;
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_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
@ -237,17 +237,17 @@ static void show_all_handlers(void) {
void event_add_handler( const event_t *event )
{
event_t *e;
event_t *e;
CHECK( event, );
e = event_copy( event, 0 );
CHECK( event, );
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
signal_block();
events.push_back(e);
@ -257,75 +257,75 @@ void event_add_handler( const event_t *event )
void event_remove( event_t *criterion )
{
size_t i;
event_list_t new_list;
CHECK( criterion, );
size_t i;
event_list_t new_list;
/*
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;
CHECK( criterion, );
for( i=0; i<events.size(); i++ )
{
event_t *n = events.at(i);
if( event_match( criterion, n ) )
{
/*
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++ )
{
event_t *n = events.at(i);
if( event_match( criterion, n ) )
{
killme.push_back(n);
/*
If this event was a signal handler and no other handler handles
the specified signal type, do not handle that type of signal any
more.
*/
if( n->type == EVENT_SIGNAL )
{
/*
If this event was a signal handler and no other handler handles
the specified signal type, do not handle that type of signal any
more.
*/
if( n->type == EVENT_SIGNAL )
{
event_t e = event_t::signal_event(n->param1.signal);
if( event_get( &e, 0 ) == 1 )
{
signal_handle( e.param1.signal, 0 );
}
}
}
else
{
if( event_get( &e, 0 ) == 1 )
{
signal_handle( e.param1.signal, 0 );
}
}
}
else
{
new_list.push_back(n);
}
}
}
}
signal_block();
events.swap(new_list);
events.swap(new_list);
signal_unblock();
}
int event_get( event_t *criterion, std::vector<event_t *> *out )
{
size_t i;
int found = 0;
if( events.empty() )
return 0;
size_t i;
int found = 0;
CHECK( criterion, 0 );
for( i=0; i<events.size(); i++ )
{
event_t *n = events.at(i);
if( event_match(criterion, n ) )
{
found++;
if( out )
if( events.empty() )
return 0;
CHECK( criterion, 0 );
for( i=0; i<events.size(); i++ )
{
event_t *n = events.at(i);
if( event_match(criterion, n ) )
{
found++;
if( out )
out->push_back(n);
}
}
return found;
}
}
return found;
}
bool event_is_signal_observed(int sig)
@ -365,7 +365,7 @@ static void event_free_kills()
static int event_is_killed( event_t *e )
{
return std::find(killme.begin(), killme.end(), e) != killme.end();
}
}
/**
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 )
{
size_t i, j;
event_list_t fire;
/*
First we free all events that have been removed
*/
event_free_kills();
size_t i, j;
event_list_t fire;
if( events.empty() )
return;
/*
First we free all events that have been removed
*/
event_free_kills();
/*
Then we iterate over all events, adding events that should be
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
events list.
*/
for( i=0; i<events.size(); i++ )
{
event_t *criterion = events.at(i);
/*
Check if this event is a match
*/
if(event_match( criterion, event ) )
{
if( events.empty() )
return;
/*
Then we iterate over all events, adding events that should be
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
events list.
*/
for( i=0; i<events.size(); i++ )
{
event_t *criterion = events.at(i);
/*
Check if this event is a match
*/
if(event_match( criterion, event ) )
{
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
*/
if( event_is_killed( criterion ) )
continue;
/*
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
*/
if( event_is_killed( criterion ) )
continue;
/*
Fire event
*/
wcstring buffer = criterion->function_name;
/*
Fire event
*/
wcstring buffer = criterion->function_name;
if (event->arguments.get())
{
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 += arg_esc;
}
}
// debug( 1, L"Event handler fires command '%ls'", buffer.c_str() );
/*
Event handlers are not part of the main flow of code, so
they are marked as non-interactive
*/
proc_push_interactive(0);
prev_status = proc_get_last_status();
parser_t &parser = parser_t::principal_parser();
block_t *block = new event_block_t(event);
parser.push_block(block);
parser.eval( buffer, io_chain_t(), TOP );
parser.pop_block();
proc_pop_interactive();
proc_set_last_status( prev_status );
}
// debug( 1, L"Event handler fires command '%ls'", buffer.c_str() );
/*
Event handlers are not part of the main flow of code, so
they are marked as non-interactive
*/
proc_push_interactive(0);
prev_status = proc_get_last_status();
parser_t &parser = parser_t::principal_parser();
block_t *block = new event_block_t(event);
parser.push_block(block);
parser.eval( buffer, io_chain_t(), TOP );
parser.pop_block();
proc_pop_interactive();
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()
{
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
another event, we do not want to fire delayed events because of
concurrency problems.
*/
if( ! blocked.empty() && is_event==1)
{
event_list_t new_blocked;
for( i=0; i<blocked.size(); i++ )
{
event_t *e = blocked.at(i);
if( event_is_blocked( e ) )
{
When the event handler has called a piece of code that triggers
another event, we do not want to fire delayed events because of
concurrency problems.
*/
if( ! blocked.empty() && is_event==1)
{
event_list_t new_blocked;
for( i=0; i<blocked.size(); i++ )
{
event_t *e = blocked.at(i);
if( event_is_blocked( e ) )
{
new_blocked.push_back(e);
}
else
{
event_fire_internal( e );
event_free( e );
}
}
}
else
{
event_fire_internal( e );
event_free( e );
}
}
blocked.swap(new_blocked);
}
while( sig_list[active_list].count > 0 )
{
signal_list_t *lst;
}
/*
Switch signal lists
*/
sig_list[1-active_list].count=0;
sig_list[1-active_list].overflow=0;
active_list=1-active_list;
while( sig_list[active_list].count > 0 )
{
signal_list_t *lst;
/*
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);
e.arguments.reset(new wcstring_list_t(1)); //one element
lst = &sig_list[1-active_list];
if( lst->overflow )
{
debug( 0, _( L"Signal list overflow. Signals have been ignored." ) );
}
/*
Send all signals in our private list
*/
for( int i=0; i < lst->count; i++ )
{
e.param1.signal = lst->signal[i];
lst = &sig_list[1-active_list];
if( lst->overflow )
{
debug( 0, _( L"Signal list overflow. Signals have been ignored." ) );
}
/*
Send all signals in our private list
*/
for( int i=0; i < lst->count; i++ )
{
e.param1.signal = lst->signal[i];
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));
}
else
{
event_fire_internal( &e );
}
}
}
else
{
event_fire_internal( &e );
}
}
e.arguments.reset(NULL);
}
}
}
void event_fire_signal(int signal)
@ -565,33 +565,33 @@ void event_fire_signal(int signal)
void event_fire( event_t *event )
{
if( event && (event->type == EVENT_SIGNAL) )
{
if( event && (event->type == EVENT_SIGNAL) )
{
event_fire_signal(event->param1.signal);
}
else
{
}
else
{
is_event++;
/*
Fire events triggered by signals
*/
event_fire_delayed();
if( event )
{
if( event_is_blocked( event ) )
{
/*
Fire events triggered by signals
*/
event_fire_delayed();
if( event )
{
if( event_is_blocked( event ) )
{
blocked.push_back(event_copy(event, 1));
}
else
{
event_fire_internal( event );
}
}
}
else
{
event_fire_internal( event );
}
}
is_event--;
}
}
}
@ -604,36 +604,36 @@ void event_destroy()
for_each(events.begin(), events.end(), event_free);
events.clear();
for_each(killme.begin(), killme.end(), event_free);
killme.clear();
}
void event_free( event_t *e )
{
CHECK( e, );
CHECK( e, );
delete e;
}
void event_fire_generic_internal(const wchar_t *name, ...)
{
va_list va;
wchar_t *arg;
va_list va;
wchar_t *arg;
CHECK( name, );
CHECK( name, );
event_t ev(EVENT_GENERIC);
ev.str_param1 = name;
event_t ev(EVENT_GENERIC);
ev.str_param1 = name;
ev.arguments.reset(new wcstring_list_t);
va_start( va, name );
while( (arg=va_arg(va, wchar_t *) )!= 0 )
{
va_start( va, name );
while( (arg=va_arg(va, wchar_t *) )!= 0 )
{
ev.arguments->push_back(arg);
}
va_end( va );
event_fire( &ev );
}
va_end( va );
event_fire( &ev );
ev.arguments.reset(NULL);
}

78
event.h
View file

@ -1,13 +1,13 @@
/** \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
#define FISH_EVENT_H
@ -31,14 +31,14 @@
*/
enum
{
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_VARIABLE, /**< An event triggered by a variable update */
EVENT_EXIT, /**< An event triggered by a job or process exit */
EVENT_JOB_ID, /**< An event triggered by a job exit */
EVENT_GENERIC, /**< A generic event */
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_VARIABLE, /**< An event triggered by a variable update */
EVENT_EXIT, /**< An event triggered by a job or process exit */
EVENT_JOB_ID, /**< An event triggered by a job exit */
EVENT_GENERIC, /**< A generic event */
}
;
;
/**
The structure which represents an event. The event_t struct has
@ -49,75 +49,75 @@ enum
*/
struct event_t
{
/**
Type of event
*/
int type;
/**
Type of event
*/
int type;
/** 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
/** 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
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
*/
*/
union {
int signal;
int job_id;
pid_t pid;
} param1;
/** The string types are one of the following:
variable: Variable name for variable-type events.
param: The parameter describing this generic event.
*/
wcstring str_param1;
/**
The name of the event handler function
*/
wcstring function_name;
/**
The name of the event handler function
*/
wcstring function_name;
/**
The argument list. Only used when sending a new event using
event_fire. In all other situations, the value of this variable
is ignored.
*/
/**
The argument list. Only used when sending a new event using
event_fire. In all other situations, the value of this variable
is ignored.
*/
std::auto_ptr<wcstring_list_t> arguments;
event_t(int t) : type(t), param1(), str_param1(), function_name(), arguments() { }
/** Copy constructor */
event_t(const event_t &x);
static event_t signal_event(int sig);
static event_t variable_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.
*/
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.
*/
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
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 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
*/
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
Prototypes for functions for executing a program
Prototypes for functions for executing a program
*/
#ifndef FISH_EXEC_H
@ -21,7 +21,7 @@
#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
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
an evil combination of talloc, string buffers and reference
counting.
*/
#ifndef FISH_EXPAND_H
@ -24,10 +24,10 @@
enum {
/** Flag specifying that cmdsubst expansion should be skipped */
EXPAND_SKIP_CMDSUBST = 1 << 0,
/** Flag specifying that variable expansion should be skipped */
EXPAND_SKIP_VARIABLES = 1 << 1,
/** Flag specifying that wildcard expansion should be skipped */
EXPAND_SKIP_WILDCARDS = 1 << 2,
@ -36,21 +36,21 @@ enum {
completion). An incomplete match is a wildcard that matches a
prefix of the filename. If accept_incomplete is true, only the
remainder of the string is returned.
*/
*/
ACCEPT_INCOMPLETE = 1 << 3,
/** Only match files that are executable by the current user. Only applicable together with ACCEPT_INCOMPLETE. */
EXECUTABLES_ONLY = 1 << 4,
/** Only match directories. Only applicable together with ACCEPT_INCOMPLETE. */
DIRECTORIES_ONLY = 1 << 5,
/** Don't generate descriptions */
EXPAND_NO_DESCRIPTIONS = 1 << 6,
/** Don't do process expansion */
EXPAND_SKIP_PROCESS = 1 << 7,
/** Don't expand jobs (but you can still expand processes). This is because job expansion is not thread safe. */
EXPAND_SKIP_JOBS = 1 << 8
};
@ -69,33 +69,33 @@ class completion_t;
enum
{
/** Character represeting a home directory */
HOME_DIRECTORY = EXPAND_RESERVED,
/** Character represeting a home directory */
HOME_DIRECTORY = EXPAND_RESERVED,
/** Character represeting process expansion */
PROCESS_EXPAND,
/** Character representing variable expansion */
VARIABLE_EXPAND,
/** Character represeting process expansion */
PROCESS_EXPAND,
/** Character rpresenting variable expansion into a single element*/
VARIABLE_EXPAND_SINGLE,
/** Character representing variable expansion */
VARIABLE_EXPAND,
/** Character representing the start of a bracket expansion */
BRACKET_BEGIN,
/** Character rpresenting variable expansion into a single element*/
VARIABLE_EXPAND_SINGLE,
/** Character representing the end of a bracket expansion */
BRACKET_END,
/** Character representing the start of a bracket expansion */
BRACKET_BEGIN,
/** Character representing separation between two bracket elements */
BRACKET_SEP,
/**
Separate subtokens in a token with this character.
*/
INTERNAL_SEPARATOR,
/** Character representing the end of a bracket expansion */
BRACKET_END,
/** Character representing separation between two bracket elements */
BRACKET_SEP,
/**
Separate subtokens in a token with this character.
*/
INTERNAL_SEPARATOR,
}
;
;
/**
@ -103,14 +103,14 @@ enum
*/
enum
{
/** Error */
EXPAND_ERROR,
/** Ok */
EXPAND_OK,
/** Ok, a wildcard in the string matched no files */
EXPAND_WILDCARD_NO_MATCH,
/* Ok, a wildcard in the string matched a file */
EXPAND_WILDCARD_MATCH
/** Error */
EXPAND_ERROR,
/** Ok */
EXPAND_OK,
/** Ok, a wildcard in the string matched no files */
EXPAND_WILDCARD_NO_MATCH,
/* Ok, a wildcard in the string matched a file */
EXPAND_WILDCARD_MATCH
};
/** 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),
cmdsubst expansion and wildcard expansion. The results are inserted
into the list out.
If the parameter does not need expansion, it is copied into the list
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
expands to more than one string. This is used for expanding command
names.
\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
\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
*/
struct winsize
struct winsize
{
/**
Number of rows
*/
unsigned short ws_row;
/**
Number of columns
*/
unsigned short ws_col;
/**
Number of rows
*/
unsigned short ws_row;
/**
Number of columns
*/
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
function, but the prototype for this function is missing in some libc
implementations.
implementations.
Fish has a fallback implementation in case the implementation is
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 *);
#define wcsdup(a) wcsdup_use_weak((a))
#define wcscasecmp(a, b) wcscasecmp_use_weak((a), (b))
#else
#ifndef HAVE_WCSDUP
@ -312,8 +312,8 @@ long convert_digit( wchar_t d, int base );
supported.
*/
long wcstol(const wchar_t *nptr,
wchar_t **endptr,
int base);
wchar_t **endptr,
int base);
#endif
#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.
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 );
@ -361,10 +361,10 @@ size_t wcslcpy( wchar_t *dst, const wchar_t *src, size_t siz );
*/
struct drand48_data
{
/**
Seed value
*/
unsigned int seed;
/**
Seed value
*/
unsigned int seed;
}
;
@ -410,9 +410,9 @@ char * textdomain( const char * domainname );
/**
Fallback implementation of dcgettext. Just returns the original string.
*/
char * dcgettext ( const char * domainname,
const char * msgid,
int category );
char * dcgettext ( const char * domainname,
const char * msgid,
int category );
#endif
@ -441,44 +441,44 @@ int killpg( int pgr, int sig );
/**
Struct describing a long getopt option
*/
struct option
struct option
{
/**
Name of option
*/
const char *name;
/**
Flag
*/
int has_arg;
/**
Flag
*/
int *flag;
/**
Return value
*/
int val;
/**
Name of option
*/
const char *name;
/**
Flag
*/
int has_arg;
/**
Flag
*/
int *flag;
/**
Return value
*/
int val;
}
;
#ifndef no_argument
#define no_argument 0
#define no_argument 0
#endif
#ifndef required_argument
#define required_argument 1
#define required_argument 1
#endif
#ifndef optional_argument
#define optional_argument 2
#define optional_argument 2
#endif
int getopt_long(int argc,
char * const argv[],
const char *optstring,
const struct option *longopts,
int *longindex);
int getopt_long(int argc,
char * const argv[],
const char *optstring,
const struct option *longopts,
int *longindex);
#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
The main loop of <tt>fish</tt>.
The main loop of <tt>fish</tt>.
*/
#include "config.h"
@ -104,12 +104,12 @@ static std::string get_executable_path(const char *argv0)
uint32_t buffSize = sizeof buff;
if (0 == _NSGetExecutablePath(buff, &buffSize))
return std::string(buff);
/* Loop until we're big enough */
char *mbuff = (char *)malloc(buffSize);
while (0 > _NSGetExecutablePath(mbuff, &buffSize))
mbuff = (char *)realloc(mbuff, buffSize);
/* Return the string */
std::string result = mbuff;
free(mbuff);
@ -125,7 +125,7 @@ static std::string get_executable_path(const char *argv0)
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. */
return std::string(argv0 ? argv0 : "");
}
@ -137,9 +137,9 @@ static struct config_paths_t determine_config_directory_paths(const char *argv0)
bool done = false;
std::string exec_path = get_executable_path(argv0);
if (get_realpath(exec_path))
{
{
#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.
*/
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);
wide_resolved_path.resize(exec_path.size() - suffixlen);
wide_resolved_path.append(L"/Contents/Resources/");
/* Append share, etc, doc */
paths.data = wide_resolved_path + L"share/fish";
paths.sysconf = wide_resolved_path + L"etc/fish";
paths.doc = wide_resolved_path + L"doc/fish";
/* But the bin_dir is the resolved_path, minus fish (aka the MacOS directory) */
paths.bin = str2wcstring(exec_path);
paths.bin.resize(paths.bin.size() - strlen("/fish"));
done = true;
}
}
#endif
if (! done)
{
/* The next check is that we are in a reloctable directory tree like this:
bin/fish
etc/fish
share/fish
Check it!
*/
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);
base_path.resize(base_path.size() - strlen(suffix));
paths.data = base_path + L"/share/fish";
paths.sysconf = base_path + L"/etc/fish";
paths.doc = base_path + L"/share/doc/fish";
paths.bin = base_path + L"/bin";
struct stat 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)
{
/* 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.doc = L"" DATADIR "/doc/fish";
paths.bin = L"" PREFIX "/bin";
done = true;
}
return paths;
}
@ -217,27 +217,27 @@ static int read_init(const struct config_paths_t &paths)
{
parser_t &parser = parser_t::principal_parser();
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.sysconf + 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 );
/*
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
and no custom config to load.
*/
/*
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
and no custom config to load.
*/
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());
parser.eval( eval_buff, empty_ios, TOP );
}
return 1;
parser.eval( eval_buff, empty_ios, TOP );
}
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 )
{
int my_optind;
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;
int my_optind;
int force_interactive=0;
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 );
}
}
}
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
}
}
;
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);
int opt_index = 0;
/*
We are also an interactive session if we have are forced-
*/
is_interactive_session |= force_interactive;
int opt = getopt_long( argc,
argv,
GETOPT_STRING,
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 )
{
wcstring out;
for( ; *in; in++ )
{
if( *in < 32 )
{
append_format( out, L"\\x%.2x", *in );
}
else if( *in < 128 )
{
out.push_back(*in);
}
else if( *in < 65536 )
{
append_format( out, L"\\u%.4x", *in );
}
else
{
append_format( out, L"\\U%.8x", *in );
}
}
return out;
wcstring out;
for( ; *in; in++ )
{
if( *in < 32 )
{
append_format( out, L"\\x%.2x", *in );
}
else if( *in < 128 )
{
out.push_back(*in);
}
else if( *in < 65536 )
{
append_format( out, L"\\u%.4x", *in );
}
else
{
append_format( out, L"\\U%.8x", *in );
}
}
return out;
}
extern int g_fork_count;
int main( int argc, char **argv )
{
int res=1;
const char *cmd=0;
int my_optind=0;
{
int res=1;
const char *cmd=0;
int my_optind=0;
set_main_thread();
set_main_thread();
setup_fork_guards();
wsetlocale( LC_ALL, L"" );
is_interactive_session=1;
program_name=L"fish";
wsetlocale( LC_ALL, L"" );
is_interactive_session=1;
program_name=L"fish";
//struct stat 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
*/
if( is_interactive_session && no_exec)
{
debug( 1, _(L"Can not use the no-execute mode when running an interactive session") );
no_exec = 0;
}
const struct config_paths_t paths = determine_config_directory_paths(argv[0]);
proc_init();
event_init();
wutil_init();
//parser_init();
builtin_init();
function_init();
env_init(&paths);
reader_init();
history_init();
/*
No-exec is prohibited when in interactive mode
*/
if( is_interactive_session && no_exec)
{
debug( 1, _(L"Can not use the no-execute mode when running an interactive session") );
no_exec = 0;
}
const struct config_paths_t paths = determine_config_directory_paths(argv[0]);
proc_init();
event_init();
wutil_init();
//parser_init();
builtin_init();
function_init();
env_init(&paths);
reader_init();
history_init();
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);
const io_chain_t empty_ios;
if( read_init(paths) )
{
if( cmd != 0 )
{
wchar_t *cmd_wcs = str2wcs( cmd );
res = parser.eval( cmd_wcs, empty_ios, TOP );
free(cmd_wcs);
reader_exit(0, 0);
}
else
{
if( my_optind == argc )
{
res = reader_read( STDIN_FILENO, empty_ios );
}
else
{
char **ptr;
char *file = *(argv+(my_optind++));
int i;
int fd;
wchar_t *rel_filename, *abs_filename;
if( read_init(paths) )
{
if( cmd != 0 )
{
wchar_t *cmd_wcs = str2wcs( cmd );
res = parser.eval( cmd_wcs, empty_ios, TOP );
free(cmd_wcs);
reader_exit(0, 0);
}
else
{
if( my_optind == argc )
{
res = reader_read( STDIN_FILENO, empty_ios );
}
else
{
char **ptr;
char *file = *(argv+(my_optind++));
int i;
int fd;
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
set_cloexec(fd);
if( *(argv+my_optind))
{
if( *(argv+my_optind))
{
wcstring sb;
for( i=1,ptr = argv+my_optind; *ptr; i++, ptr++ )
{
if( i != 1 )
for( i=1,ptr = argv+my_optind; *ptr; i++, ptr++ )
{
if( i != 1 )
sb.append( ARRAY_SEP_STR );
sb.append( str2wcstring( *ptr ));
}
env_set( L"argv", sb.c_str(), 0 );
}
}
rel_filename = str2wcs( file );
abs_filename = wrealpath( rel_filename, 0 );
env_set( L"argv", sb.c_str(), 0 );
}
if( !abs_filename )
{
abs_filename = wcsdup(rel_filename);
}
rel_filename = str2wcs( file );
abs_filename = wrealpath( rel_filename, 0 );
reader_push_current_filename( intern( abs_filename ) );
free( rel_filename );
free( abs_filename );
if( !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)
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
The fish_indent proegram.
The fish_indent proegram.
*/
#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 )
{
while( 1 )
{
errno=0;
wint_t c = fgetwc( f );
if( c == WEOF )
{
if( errno )
{
wperror(L"fgetwc");
exit(1);
}
break;
}
b.push_back((wchar_t)c);
}
while( 1 )
{
errno=0;
wint_t c = fgetwc( f );
if( c == WEOF )
{
if( errno )
{
wperror(L"fgetwc");
exit(1);
}
break;
}
b.push_back((wchar_t)c);
}
}
/**
@ -76,7 +76,7 @@ static void insert_tabs( wcstring &out, int indent )
{
if (indent > 0)
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 )
{
tokenizer tok;
int res=0;
int is_command = 1;
int indent = 0;
int do_indent = 1;
int prev_type = 0;
int prev_prev_type = 0;
tokenizer tok;
int res=0;
int is_command = 1;
int indent = 0;
int do_indent = 1;
int 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;
unescape_string(unesc, UNESCAPE_SPECIAL);
if( parser_keywords_is_block(unesc))
{
next_indent++;
}
else if (unesc == L"else")
{
indent--;
}
/* case should have the same indent level as switch*/
else if (unesc == L"case")
{
indent--;
}
else if (unesc == L"end")
{
indent--;
next_indent--;
}
if( do_indent && flags && prev_type != TOK_PIPE )
{
insert_tabs( out, indent );
}
if( parser_keywords_is_block(unesc))
{
next_indent++;
}
else if (unesc == L"else")
{
indent--;
}
/* case should have the same indent level as switch*/
else if (unesc == L"case")
{
indent--;
}
else if (unesc == L"end")
{
indent--;
next_indent--;
}
if( do_indent && flags && prev_type != TOK_PIPE )
{
insert_tabs( out, indent );
}
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:
{
out.append( L" " );
if ( last[0] == '2' && !last[1] ) {
out.append( L"^" );
} else if ( last[0] != '1' || last[1] ) {
out.append( 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:
{
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" | " );
is_command = 1;
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;
}
}
out.append( L" | " );
is_command = 1;
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 );
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;
}
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");
if (pos > 0)
str.erase(0, pos);
pos = str.find_last_not_of(L" \n");
if (pos != wcstring::npos && pos + 1 < str.length())
str.erase(pos + 1);
@ -278,106 +278,106 @@ static void trim( wcstring &str )
The main mathod. Run the program.
*/
int main( int argc, char **argv )
{
int do_indent=1;
set_main_thread();
{
int do_indent=1;
set_main_thread();
setup_fork_guards();
wsetlocale( LC_ALL, L"" );
program_name=L"fish_indent";
while( 1 )
{
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 );
}
wsetlocale( LC_ALL, L"" );
program_name=L"fish_indent";
case 'i':
{
do_indent = 0;
break;
}
case '?':
{
exit( 1 );
}
}
}
while( 1 )
{
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':
{
do_indent = 0;
break;
}
case '?':
{
exit( 1 );
}
}
}
wcstring sb_in, sb_out;
read_file( stdin, sb_in );
wutil_init();
read_file( stdin, sb_in );
if( !indent( sb_out, sb_in, do_indent ) )
{
wutil_init();
if( !indent( sb_out, sb_in, do_indent ) )
{
trim(sb_out);
fwprintf( stdout, L"%ls", sb_out.c_str() );
}
else
{
/*
Indenting failed - print original input
*/
fwprintf( stdout, L"%ls", sb_in.c_str() );
}
fwprintf( stdout, L"%ls", sb_out.c_str() );
}
else
{
/*
Indenting failed - print original input
*/
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
Prototypes for functions for storing and retrieving function
information. These functions also take care of autoloading
functions in the $fish_function_path. Actual function evaluation
is taken care of by the parser and to some degree the builtin
handling library.
information. These functions also take care of autoloading
functions in the $fish_function_path. Actual function evaluation
is taken care of by the parser and to some degree the builtin
handling library.
*/
#include "config.h"
@ -89,18 +89,18 @@ static int load( const wcstring &name )
{
ASSERT_IS_MAIN_THREAD();
scoped_lock lock(functions_lock);
bool was_autoload = is_autoload;
int res;
bool was_autoload = is_autoload;
int res;
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 */
return 0;
return 0;
}
is_autoload = true;
res = function_autoloader.load( name, true );
is_autoload = was_autoload;
return res;
is_autoload = true;
res = function_autoloader.load( name, true );
is_autoload = was_autoload;
return res;
}
/**
@ -109,41 +109,41 @@ static int load( const wcstring &name )
*/
static void autoload_names( std::set<wcstring> &names, int get_hidden )
{
size_t i;
const env_var_t path_var_wstr = env_get_string( L"fish_function_path" );
size_t i;
const env_var_t path_var_wstr = env_get_string( L"fish_function_path" );
if (path_var_wstr.missing())
return;
const wchar_t *path_var = path_var_wstr.c_str();
const wchar_t *path_var = path_var_wstr.c_str();
wcstring_list_t path_list;
tokenize_variable_array( path_var, path_list );
for( i=0; i<path_list.size(); i++ )
{
tokenize_variable_array( path_var, path_list );
for( i=0; i<path_list.size(); i++ )
{
const wcstring &ndir_str = path_list.at(i);
const wchar_t *ndir = (wchar_t *)ndir_str.c_str();
DIR *dir = wopendir( ndir );
if( !dir )
continue;
wcstring name;
while (wreaddir(dir, name))
{
const wchar_t *fn = name.c_str();
const wchar_t *suffix;
if( !get_hidden && fn[0] == L'_' )
continue;
const wchar_t *ndir = (wchar_t *)ndir_str.c_str();
DIR *dir = wopendir( ndir );
if( !dir )
continue;
wcstring name;
while (wreaddir(dir, name))
{
const wchar_t *fn = name.c_str();
const wchar_t *suffix;
if( !get_hidden && fn[0] == L'_' )
continue;
suffix = wcsrchr( fn, L'.' );
if( suffix && (wcscmp( suffix, L".fish" ) == 0 ) )
{
if( suffix && (wcscmp( suffix, L".fish" ) == 0 ) )
{
wcstring name(fn, suffix - fn);
names.insert(name);
}
}
closedir(dir);
}
}
}
closedir(dir);
}
}
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 )
{
ASSERT_IS_MAIN_THREAD();
CHECK( ! data.name.empty(), );
CHECK( data.definition, );
scoped_lock lock(functions_lock);
CHECK( ! data.name.empty(), );
CHECK( data.definition, );
scoped_lock lock(functions_lock);
/* Remove the old function */
function_remove( data.name );
function_remove( data.name );
/* Create and store a new function */
const wchar_t *filename = reader_current_filename();
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));
loaded_functions.insert(new_pair);
/* Add event handlers */
for( std::vector<event_t>::const_iterator iter = data.events.begin(); iter != data.events.end(); ++iter )
{
event_add_handler( &*iter );
}
for( std::vector<event_t>::const_iterator iter = data.events.begin(); iter != data.events.end(); ++iter )
{
event_add_handler( &*iter );
}
}
int function_exists( const wcstring &cmd )
{
if( parser_keywords_is_reserved(cmd) )
return 0;
if( parser_keywords_is_reserved(cmd) )
return 0;
scoped_lock lock(functions_lock);
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 )
{
if( parser_keywords_is_reserved(cmd) )
return 0;
if( parser_keywords_is_reserved(cmd) )
return 0;
scoped_lock lock(functions_lock);
return loaded_functions.find(cmd) != loaded_functions.end() || function_autoloader.can_load(cmd, vars);
return loaded_functions.find(cmd) != loaded_functions.end() || function_autoloader.can_load(cmd, vars);
}
static bool function_remove_ignore_autoload(const wcstring &name)
{
scoped_lock lock(functions_lock);
bool erased = (loaded_functions.erase(name) > 0);
if (erased) {
if (erased) {
event_t ev(EVENT_ANY);
ev.function_name=name;
ev.function_name=name;
event_remove( &ev );
}
return erased;
@ -252,7 +252,7 @@ static const function_info_t *function_get(const wcstring &name)
return &iter->second;
}
}
bool function_get_definition(const wcstring &name, wcstring *out_definition)
{
scoped_lock lock(functions_lock);
@ -277,7 +277,7 @@ int function_get_shadows(const wcstring &name)
return func ? func->shadows : false;
}
bool function_get_desc(const wcstring &name, wcstring *out_desc)
{
/* 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)
{
load(name);
load(name);
scoped_lock lock(functions_lock);
function_map_t::iterator iter = loaded_functions.find(name);
if (iter != loaded_functions.end()) {
@ -307,24 +307,24 @@ bool function_copy(const wcstring &name, const wcstring &new_name)
scoped_lock lock(functions_lock);
function_map_t::const_iterator iter = loaded_functions.find(name);
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));
loaded_functions.insert(new_pair);
result = true;
}
return result;
return result;
}
wcstring_list_t function_get_names(int get_hidden)
{
std::set<wcstring> names;
scoped_lock lock(functions_lock);
autoload_names(names, get_hidden);
autoload_names(names, get_hidden);
function_map_t::const_iterator iter;
for (iter = loaded_functions.begin(); iter != loaded_functions.end(); ++iter) {
const wcstring &name = iter->first;
/* Maybe skip hidden */
if (! get_hidden) {
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
information. These functions also take care of autoloading
functions in the $fish_function_path. Actual function evaluation
is taken care of by the parser and to some degree the builtin
handling library.
information. These functions also take care of autoloading
functions in the $fish_function_path. Actual function evaluation
is taken care of by the parser and to some degree the builtin
handling library.
*/
#ifndef FISH_FUNCTION_H
@ -28,66 +28,66 @@ class env_vars_snapshot_t;
*/
struct function_data_t
{
/**
Name of function
*/
wcstring name;
/**
Description of function
*/
wcstring description;
/**
Function definition
*/
wchar_t *definition;
/**
List of all event handlers for this function
*/
std::vector<event_t> events;
/**
List of all named arguments for this function
*/
wcstring_list_t named_arguments;
/**
Set to non-zero if invoking this function shadows the variables
of the underlying function.
*/
int shadows;
/**
Name of function
*/
wcstring name;
/**
Description of function
*/
wcstring description;
/**
Function definition
*/
wchar_t *definition;
/**
List of all event handlers for this function
*/
std::vector<event_t> events;
/**
List of all named arguments for this function
*/
wcstring_list_t named_arguments;
/**
Set to non-zero if invoking this function shadows the variables
of the underlying function.
*/
int shadows;
};
class function_info_t {
public:
/** Constructs relevant information from the function_data */
function_info_t(const function_data_t &data, const wchar_t *filename, int def_offset, bool autoload);
/** Used by function_copy */
function_info_t(const function_info_t &data, const wchar_t *filename, int def_offset, bool autoload);
/** Function definition */
const wcstring definition;
/** Function description. Only the description may be changed after the function is created. */
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;
/** Line where definition started */
const int definition_offset;
/** List of all named arguments for this function */
/** Line where definition started */
const int definition_offset;
/** List of all named arguments for this function */
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;
/** Set to true if invoking this function shadows the variables of the underlying function. */
const bool shadows;
/** Set to true if invoking this function shadows the variables of the underlying function. */
const bool shadows;
};
/**
Initialize function data
Initialize function data
*/
void function_init();
@ -128,7 +128,7 @@ int function_exists_no_autoload( const wcstring &name, const env_vars_snapshot_t
/**
Returns all function names.
\param get_hidden whether to include hidden functions, i.e. ones starting with an underscore
*/
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
functions that have already been defined.
This returns an intern'd string.
*/
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
Prototypes for functions for syntax highlighting
Prototypes for functions for syntax highlighting
*/
#ifndef FISH_HIGHLIGHT_H
@ -59,7 +59,7 @@
/**
Internal value representing highlighting of an IO redirection
*/
#define HIGHLIGHT_REDIRECTION 0x800
#define HIGHLIGHT_REDIRECTION 0x800
/**
Internal value representing highlighting a potentially valid path
*/
@ -79,7 +79,7 @@ struct file_detection_context_t;
for each character in buff.
\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 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.
\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 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
variables. Defaults to FISH_COLOR_NORMAL.
Example:
Example:
If the environment variable FISH_FISH_COLOR_ERROR is set to 'red', a
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 {
/* The path must be to a directory */
PATH_REQUIRE_DIR = 1 << 0,
/* Expand any leading tilde in the path */
PATH_EXPAND_TILDE = 1 << 1
};

View file

@ -1,5 +1,5 @@
/** \file history.c
History functions, part of the user interface.
History functions, part of the user interface.
*/
#include "config.h"
@ -42,7 +42,7 @@ Our history format is intended to be valid YAML. Here it is:
paths:
- /path/to/something
- /path/to/something_else
Newlines are replaced by \n. Backslashes are replaced by \\.
*/
@ -64,14 +64,14 @@ class time_profiler_t {
const char *what;
double start;
public:
time_profiler_t(const char *w) {
if (LOG_TIMES) {
what = w;
start = timef();
}
}
~time_profiler_t() {
if (LOG_TIMES) {
double end = timef();
@ -90,13 +90,13 @@ class history_lru_node_t : public lru_node_t {
timestamp(item.timestamp()),
required_paths(item.required_paths)
{}
bool write_yaml_to_file(FILE *f) const;
};
class history_lru_cache_t : public lru_cache_t<history_lru_node_t> {
protected:
/* Override to delete evicted nodes */
virtual void node_was_evicted(history_lru_node_t *node) {
delete node;
@ -104,13 +104,13 @@ class history_lru_cache_t : public lru_cache_t<history_lru_node_t> {
public:
history_lru_cache_t(size_t max) : lru_cache_t<history_lru_node_t>(max) { }
/* Function to add a history item */
void add_item(const history_item_t &item) {
/* Skip empty items */
if (item.empty())
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. */
history_lru_node_t *node = this->get_node(item.str());
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 {
switch (type) {
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. */
return contents.size() > term.size() && contents.find(term) != wcstring::npos;
case HISTORY_SEARCH_TYPE_PREFIX:
/* 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);
default:
sanity_lose();
return false;
@ -180,18 +180,18 @@ bool history_lru_node_t::write_yaml_to_file(FILE *f) const {
escape_yaml(cmd);
if (fprintf(f, "- cmd: %s\n", cmd.c_str()) < 0)
return false;
if (fprintf(f, " when: %ld\n", (long)timestamp) < 0)
return false;
if (! required_paths.empty()) {
if (fputs(" paths:\n", f) < 0)
return false;
for (path_list_t::const_iterator iter = required_paths.begin(); iter != required_paths.end(); ++iter) {
std::string path = wcs2string(*iter);
escape_yaml(path);
if (fprintf(f, " - %s\n", path.c_str()) < 0)
return false;
}
@ -207,17 +207,17 @@ static bool parse_timestamp(const char *str, time_t *out_when) {
/* Advance past spaces */
while (*cursor == ' ')
cursor++;
/* Look for "when:" */
size_t when_len = 5;
if (strncmp(cursor, "when:", when_len) != 0)
return false;
cursor += when_len;
/* Advance past spaces */
while (*cursor == ' ')
cursor++;
/* Try to parse a timestamp. */
long timestamp = 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 */
if (length < 1)
return NULL;
/* Get a pointer to the end, that we must not pass */
const char * const end = start + length;
/* Skip past the next newline */
const char *nextline = (const char *)memchr(start, '\n', length);
if (! nextline || nextline >= end) {
@ -247,13 +247,13 @@ static const char *next_line(const char *start, size_t length) {
if (++nextline >= end) {
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);
if (! next_newline) {
return NULL;
}
/* Done */
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);
while (cursor < mmap_length) {
const char * const line_start = begin + cursor;
/* Advance the cursor to the next line */
const char *newline = (const char *)memchr(line_start, '\n', mmap_length - cursor);
if (newline == NULL)
break;
/* Advance the cursor past this line. +1 is for the newline */
size_t line_len = newline - line_start;
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 */
if (line_start[0] == ' ')
continue;
/* Skip very short lines to make one of the checks below easier */
if (line_len < 3)
continue;
/* Try to be a little YAML compatible. Skip lines with leading %, ---, or ... */
if (! memcmp(line_start, "%", 1) ||
! 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.
*/
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. */
bool has_timestamp = false;
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);
interior_line != NULL && ! has_timestamp;
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 (interior_line[0] != ' ')
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 */
cursor = interior_line - begin;
/* Try parsing a timestamp from this line. If we succeed, the loop will break. */
has_timestamp = parse_timestamp(interior_line, &timestamp);
}
/* Skip this item if the timestamp is at or after our cutoff. */
if (has_timestamp && timestamp >= cutoff_timestamp) {
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) {
if (mmap_length == 0 || *inout_cursor >= mmap_length)
return (size_t)(-1);
const char *end = begin + mmap_length;
const char *pos;
bool ignore_newline = false;
bool do_push = true;
const char *end = begin + mmap_length;
const char *pos;
bool ignore_newline = false;
bool do_push = true;
bool all_done = false;
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 )
{
ignore_newline = (*pos == '#');
do_push = false;
}
if( do_push )
{
ignore_newline = (*pos == '#');
do_push = false;
}
switch( *pos )
{
case '\\':
{
pos++;
break;
}
switch( *pos )
{
case '\\':
{
pos++;
break;
}
case '\n':
{
if( ignore_newline )
{
ignore_newline = false;
}
else
{
case '\n':
{
if( ignore_newline )
{
ignore_newline = false;
}
else
{
/* Note: pos will be left pointing just after this newline, because of the ++ in the loop */
all_done = true;
}
break;
}
}
}
}
break;
}
}
}
*inout_cursor = (pos - begin);
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:
result = offset_of_next_item_fish_2_0(begin, mmap_length, inout_cursor, cutoff_timestamp);
break;
case history_type_fish_1_x:
result = offset_of_next_item_fish_1_x(begin, mmap_length, inout_cursor, cutoff_timestamp);
break;
default:
case history_type_unknown:
// Oh well
@ -433,7 +433,7 @@ history_t::~history_t()
void history_t::add(const history_item_t &item)
{
scoped_lock locker(lock);
/* Try merging with the last item */
if (! new_items.empty() && new_items.back().merge(item)) {
/* 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);
unsaved_item_count++;
}
/* Prevent the first write from always triggering a save */
time_t now = time(NULL);
if (! save_timestamp)
save_timestamp = now;
/* This might be a good candidate for moving to a background thread */
if((now > save_timestamp + SAVE_INTERVAL) || (unsaved_item_count >= SAVE_COUNT)) {
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 */
deleted_items.insert(str);
/* Remove from our list of new items */
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)
{
scoped_lock locker(lock);
bool first = true;
/* Append new items */
for (std::vector<history_item_t>::const_reverse_iterator iter=new_items.rbegin(); iter < new_items.rend(); ++iter) {
if (! first)
@ -492,10 +492,10 @@ void history_t::get_string_representation(wcstring &result, const wcstring &sepa
result.append(iter->str());
first = false;
}
/* Append old items */
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;
const history_item_t item = history_t::decode_item(mmap_start + offset, mmap_length - offset, mmap_type);
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) {
scoped_lock locker(lock);
/* 0 is considered an invalid index */
assert(idx > 0);
idx--;
/* idx=0 corresponds to last item in new_items */
size_t new_item_count = new_items.size();
if (idx < new_item_count) {
return new_items.at(new_item_count - idx - 1);
}
/* Now look in our old items */
idx -= new_item_count;
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);
return history_t::decode_item(mmap_start + offset, mmap_length - offset, mmap_type);
}
/* Index past the valid range, so return an empty history item */
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) {
/* We found a newline. */
result.assign(start, newline - start);
/* Return the amount to advance the cursor; skip over the newline */
return newline - start + 1;
} else {
@ -564,13 +564,13 @@ static bool extract_prefix(std::string &key, std::string &value, const std::stri
size_t where = line.find(":");
if (where != std::string::npos) {
key = line.substr(0, where);
// skip a space after the : if necessary
size_t val_start = where + 1;
if (val_start < line.size() && line.at(val_start) == ' ')
val_start++;
value = line.substr(val_start);
unescape_yaml(key);
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;
time_t when = 0;
path_list_t paths;
size_t indent = 0, cursor = 0;
std::string key, value, line;
/* Read the "- cmd:" line */
size_t advance = read_line(base, cursor, len, line);
trim_leading_spaces(line);
if (! extract_prefix(key, value, line) || key != "- cmd")
goto done;
cursor += advance;
cmd = str2wcstring(value);
@ -599,22 +599,22 @@ history_item_t history_t::decode_item_fish_2_0(const char *base, size_t len) {
for (;;) {
/* Read a line */
size_t advance = read_line(base, cursor, len, line);
/* Count and trim leading spaces */
size_t this_indent = trim_leading_spaces(line);
if (indent == 0)
indent = this_indent;
if (this_indent == 0 || indent != this_indent)
break;
if (! extract_prefix(key, value, line))
break;
/* We are definitely going to consume this line */
unescape_yaml(value);
cursor += advance;
if (key == "when") {
/* Parse an int from the timestamp */
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);
if (trim_leading_spaces(line) <= indent)
break;
if (strncmp(line.c_str(), "- ", 2))
break;
/* We're going to consume this line */
cursor += advance;
/* Skip the leading dash-space and then store this path it */
line.erase(0, 2);
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 )
{
wcstring out;
for (const wchar_t *in = in_str.c_str(); *in; in++)
{
if( *in == L'\\' )
{
if( *(in+1)!= L'\n')
{
out.push_back(*in);
}
}
else
{
out.push_back(*in);
}
}
return out;
for (const wchar_t *in = in_str.c_str(); *in; in++)
{
if( *in == L'\\' )
{
if( *(in+1)!= L'\n')
{
out.push_back(*in);
}
}
else
{
out.push_back(*in);
}
}
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 timestamp_mode = false;
time_t timestamp = 0;
while( 1 )
{
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);
}
out = history_unescape_newlines_fish_1_x(out);
return history_item_t(out, timestamp);
}
@ -793,7 +793,7 @@ void history_t::populate_from_mmap(void)
break;
// 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())
{
int fd;
if((fd = wopen_cloexec(filename, O_RDONLY)) > 0)
{
off_t len = lseek( fd, 0, SEEK_END );
if(len != (off_t)-1)
{
size_t mmap_length = (size_t)len;
if(lseek(fd, 0, SEEK_SET) == 0)
{
if((fd = wopen_cloexec(filename, O_RDONLY)) > 0)
{
off_t len = lseek( fd, 0, SEEK_END );
if(len != (off_t)-1)
{
size_t mmap_length = (size_t)len;
if(lseek(fd, 0, SEEK_SET) == 0)
{
char *mmap_start;
if ((mmap_start = (char *)mmap(0, mmap_length, PROT_READ, MAP_PRIVATE, fd, 0)) != MAP_FAILED)
{
result = true;
if ((mmap_start = (char *)mmap(0, mmap_length, PROT_READ, MAP_PRIVATE, fd, 0)) != MAP_FAILED)
{
result = true;
*out_map_start = mmap_start;
*out_map_len = mmap_length;
}
}
}
close( fd );
}
}
}
}
close( fd );
}
}
return result;
}
@ -832,20 +832,20 @@ bool history_t::load_old_if_needed(void)
{
if (loaded_old) return true;
loaded_old = true;
// PCA not sure why signals were blocked here
//signal_block();
bool ok = false;
//signal_block();
bool ok = false;
if (map_file(name, &mmap_start, &mmap_length)) {
// Here we've mapped the file
ok = true;
time_profiler_t profiler("populate_from_mmap");
this->populate_from_mmap();
}
//signal_unblock();
}
//signal_unblock();
return ok;
}
@ -874,10 +874,10 @@ bool history_search_t::go_backwards() {
size_t idx = 0;
if (! prev_matches.empty())
idx = prev_matches.back().first;
if (idx == max_idx)
return false;
while (++idx < max_idx) {
const history_item_t item = history->item_at_index(idx);
/* 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 {
assert(! prev_matches.empty());
assert(! prev_matches.empty());
return prev_matches.back().second;
}
@ -974,7 +974,7 @@ static wcstring history_filename(const wcstring &name, const wcstring &suffix)
wcstring path;
if (! path_get_config(path))
return L"";
wcstring result = path;
result.append(L"/");
result.append(name);
@ -1014,25 +1014,25 @@ void history_t::save_internal()
{
/* This must be called while locked */
ASSERT_IS_LOCKED(lock);
/* Nothing to do if there's no new items */
if (new_items.empty() && deleted_items.empty())
return;
/* Compact our new items so we don't have duplicates */
this->compact_new_items();
bool ok = true;
wcstring tmp_name = history_filename(name, L".tmp");
if( ! tmp_name.empty() )
{
bool ok = true;
wcstring tmp_name = history_filename(name, L".tmp");
if( ! tmp_name.empty() )
{
/* Make an LRU cache to save only the last N elements */
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. */
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) */
const char *local_mmap_start = NULL;
size_t local_mmap_size = 0;
@ -1062,24 +1062,24 @@ void history_t::save_internal()
break;
}
}
/* Now add this old item */
lru.add_item(old_item);
}
munmap((void *)local_mmap_start, local_mmap_size);
}
/* Insert any remaining new items */
for (; new_item_iter != new_items.end(); ++new_item_iter)
{
lru.add_item(*new_item_iter);
}
signal_block();
FILE *out;
if( (out=wfopen( tmp_name, "w" ) ) )
{
if( (out=wfopen( tmp_name, "w" ) ) )
{
/* Write them out */
for (history_lru_cache_t::iterator iter = lru.begin(); iter != lru.end(); ++iter) {
const history_lru_node_t *node = *iter;
@ -1088,36 +1088,36 @@ void history_t::save_internal()
break;
}
}
if( fclose( out ) || !ok )
{
/*
This message does not have high enough priority to
be shown by default.
*/
debug( 2, L"Error when writing history file" );
}
else
{
if( fclose( out ) || !ok )
{
/*
This message does not have high enough priority to
be shown by default.
*/
debug( 2, L"Error when writing history file" );
}
else
{
wcstring new_name = history_filename(name, wcstring());
wrename(tmp_name, new_name);
}
}
wrename(tmp_name, new_name);
}
}
signal_unblock();
/* Make sure we clear all nodes, since this doesn't happen automatically */
lru.evict_all_nodes();
/* We've saved everything, so we have no more unsaved items */
unsaved_item_count = 0;
}
}
if( ok )
{
/* Our history has been written to the file, so clear our state so we can re-reference the file. */
this->clear_file_state();
}
if( ok )
{
/* Our history has been written to the file, so clear our state so we can re-reference the file. */
this->clear_file_state();
}
}
void history_t::save(void)
@ -1137,7 +1137,7 @@ void history_t::clear(void)
if (! filename.empty())
wunlink(filename);
this->clear_file_state();
}
bool history_t::is_empty(void)
@ -1157,24 +1157,24 @@ static bool should_import_bash_history_line(const std::string &line)
{
if (line.empty())
return false;
/* Very naive tests! Skip export; probably should skip others. */
const char * const ignore_prefixes[] = {
"export ",
"#"
};
for (size_t i=0; i < sizeof ignore_prefixes / sizeof *ignore_prefixes; i++) {
const char *prefix = ignore_prefixes[i];
if (! line.compare(0, strlen(prefix), prefix)) {
return false;
}
}
/* Skip lines with backticks */
if (line.find('`') != std::string::npos)
return false;
return true;
}
@ -1187,7 +1187,7 @@ void history_t::populate_from_bash(FILE *stream)
for (;;) {
line.clear();
bool success = false, has_newline = false;
/* Loop until we've read a line */
do {
char buff[128];
@ -1197,17 +1197,17 @@ void history_t::populate_from_bash(FILE *stream)
char *newline = strchr(buff, '\n');
if (newline) *newline = '\0';
has_newline = (newline != NULL);
/* Append what we've got */
line.append(buff);
}
} while (success && ! has_newline);
/* Maybe add this line */
if (should_import_bash_history_line(line)) {
this->add(str2wcstring(line));
}
if (line.empty())
break;
}
@ -1229,16 +1229,16 @@ void history_destroy()
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) {
ASSERT_IS_BACKGROUND_THREAD();
valid_paths.clear();
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)) {
/* Push the original (possibly relative) path */
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) {
/* Now that file detection is done, create the history item */
ctx->history->add(ctx->command, ctx->valid_paths);
/* Done with the context. */
delete ctx;
}
@ -1290,7 +1290,7 @@ void history_t::add_with_file_detection(const wcstring &str)
{
ASSERT_IS_MAIN_THREAD();
path_list_t potential_paths;
tokenizer tokenizer;
for( tok_init( &tokenizer, str.c_str(), TOK_SQUASH_ERRORS );
tok_has_next( &tokenizer );
@ -1308,11 +1308,11 @@ void history_t::add_with_file_detection(const wcstring &str)
}
}
tok_destroy(&tokenizer);
if (! potential_paths.empty()) {
/* We have some paths. Make a context. */
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. */
context->potential_paths.swap(potential_paths);
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 {
/** The history searches for strings containing the given string */
HISTORY_SEARCH_TYPE_CONTAINS,
/** The history searches for strings starting with the given string */
HISTORY_SEARCH_TYPE_PREFIX
};
@ -31,30 +31,30 @@ class history_item_t {
private:
explicit history_item_t(const wcstring &);
explicit history_item_t(const wcstring &, time_t, const path_list_t &paths = path_list_t());
/** Attempts to merge two compatible history items together */
bool merge(const history_item_t &item);
/** The actual contents of the entry */
wcstring contents;
/** Original creation time for the entry */
time_t creation_timestamp;
/** The actual contents of the entry */
wcstring contents;
/** Original creation time for the entry */
time_t creation_timestamp;
/** Paths that we require to be valid for this item to be autosuggested */
path_list_t required_paths;
public:
const wcstring &str() const { return contents; }
bool empty() const { return contents.empty(); }
/* Whether our contents matches a search term. */
bool matches_search(const wcstring &term, enum history_search_type_t type) const;
time_t timestamp() const { return creation_timestamp; }
const path_list_t &get_required_paths() const { return required_paths; }
bool operator==(const history_item_t &other) const {
return contents == other.contents &&
creation_timestamp == other.creation_timestamp &&
@ -78,36 +78,36 @@ private:
/** Private creator */
history_t(const wcstring &pname);
/** Privately add an item */
void add(const history_item_t &item);
/** Destructor */
~history_t();
/** Lock for thread safety */
pthread_mutex_t lock;
/** Internal function */
void clear_file_state();
/** The name of this list. Used for picking a suitable filename and for switching modes. */
const wcstring name;
/** New items. */
std::vector<history_item_t> new_items;
/** The name of this list. Used for picking a suitable filename and for switching modes. */
const wcstring name;
/** Deleted item contents. */
std::set<wcstring> deleted_items;
/** New items. */
std::vector<history_item_t> new_items;
/** How many items we've added without saving */
size_t unsaved_item_count;
/** The mmaped region for the history file */
const char *mmap_start;
/** Deleted item contents. */
std::set<wcstring> deleted_items;
/** The size of the mmap'd region */
size_t mmap_length;
/** How many items we've added without saving */
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 */
history_file_type_t mmap_type;
@ -115,23 +115,23 @@ private:
/** Timestamp of when this history was created */
const time_t birth_timestamp;
/** Timestamp of last save */
time_t save_timestamp;
/** Timestamp of last save */
time_t save_timestamp;
void populate_from_mmap(void);
/** List of old items, as offsets into out mmap data */
std::vector<size_t> old_item_offsets;
/** Whether we've loaded old items */
bool loaded_old;
/** Loads old if necessary */
bool load_old_if_needed(void);
/** Deletes duplicates in new_items. */
void compact_new_items();
/** Saves history */
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_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);
public:
/** Returns history with the given name, creating it if necessary */
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. */
bool is_empty(void);
/** Add a new history item to the end */
void add(const wcstring &str, const path_list_t &valid_paths = path_list_t());
/** Remove a history item */
void remove(const wcstring &str);
/** Add a new history item to the end */
void add_with_file_detection(const wcstring &str);
/** Saves history */
void save();
/** Irreversibly clears history */
void clear();
/** Populates from a bash history file */
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! */
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.) */
history_item_t item_at_index(size_t idx);
bool is_deleted(const history_item_t &item) const;
};
@ -178,10 +178,10 @@ class history_search_t {
/** The history in which we are searching */
history_t * history;
/** Our type */
enum history_search_type_t search_type;
/** 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;
std::vector<prev_match_t> prev_matches;
@ -191,14 +191,14 @@ class history_search_t {
/** The search term */
wcstring term;
/** Additional strings to skip (sorted) */
wcstring_list_t external_skips;
bool should_skip_match(const wcstring &str) const;
public:
/** Gets the search 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. */
bool go_forwards(void);
/** Finds the previous search result (backwards in time). Returns true if one was found. */
bool go_backwards(void);
/** Goes to the end (forwards) */
void go_to_end(void);
/** Returns if we are at the end. We start out at the end. */
bool is_at_end(void) const;
/** Goes to the beginning (backwards) */
void go_to_beginning(void);
/** Returns the current search result item. asserts if there is no current item. */
history_item_t current_item(void) const;
/** Returns the current search result item contents. asserts if there is no current item. */
wcstring current_string(void) const;
@ -233,14 +233,14 @@ class history_search_t {
search_type(type),
term(str)
{}
/* Default constructor */
history_search_t() :
history(),
search_type(HISTORY_SEARCH_TYPE_CONTAINS),
term()
{}
};
@ -266,31 +266,31 @@ struct file_detection_context_t {
/* Constructor */
file_detection_context_t(history_t *hist, const wcstring &cmd);
/* Determine which of potential_paths are valid, and put them in valid_paths */
int perform_file_detection();
/* The history associated with this context */
history_t *history;
/* The command */
wcstring command;
/* When the command was issued */
time_t when;
/* The working directory at the time the command was issued */
wcstring working_directory;
/* Paths to test */
path_list_t potential_paths;
/* Paths that were found to be valid */
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. */
int perform_file_detection(bool test_all);
/* Determine whether the given paths are all valid */
bool paths_are_valid(const path_list_t &paths);
};

774
input.cpp
View file

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

70
input.h
View file

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

View file

@ -1,5 +1,5 @@
/** \file input_common.c
Implementation file for the low level input library
*/
@ -53,7 +53,7 @@ static void (*poll_handler)();
void input_common_init( int (*ih)() )
{
interrupt_handler = ih;
interrupt_handler = ih;
}
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()
{
}
/**
@ -73,192 +73,192 @@ void input_common_destroy()
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. */
unsigned char arr[1];
bool do_loop;
do
{
unsigned char arr[1];
bool do_loop;
do
{
/* Invoke any poll handler */
if (poll_handler)
poll_handler();
fd_set fdset;
int fd_max=0;
int ioport = iothread_port();
int res;
FD_ZERO( &fdset );
FD_SET( 0, &fdset );
if( env_universal_server.fd > 0 )
{
FD_SET( env_universal_server.fd, &fdset );
if (fd_max < env_universal_server.fd) fd_max = env_universal_server.fd;
}
if (ioport > 0) {
FD_SET( ioport, &fdset );
if (fd_max < ioport) fd_max = ioport;
}
res = select( fd_max + 1, &fdset, 0, 0, 0 );
if( res==-1 )
{
switch( errno )
{
case EINTR:
case EAGAIN:
{
if( interrupt_handler )
{
int res = interrupt_handler();
if( res )
{
return res;
}
if( lookahead_count )
{
return lookahead_arr[--lookahead_count];
}
}
do_loop = true;
break;
}
default:
{
/*
The terminal has been closed. Save and exit.
*/
return R_EOF;
}
}
}
else
{
fd_set fdset;
int fd_max=0;
int ioport = iothread_port();
int res;
FD_ZERO( &fdset );
FD_SET( 0, &fdset );
if( env_universal_server.fd > 0 )
{
FD_SET( env_universal_server.fd, &fdset );
if (fd_max < env_universal_server.fd) fd_max = env_universal_server.fd;
}
if (ioport > 0) {
FD_SET( ioport, &fdset );
if (fd_max < ioport) fd_max = ioport;
}
res = select( fd_max + 1, &fdset, 0, 0, 0 );
if( res==-1 )
{
switch( errno )
{
case EINTR:
case EAGAIN:
{
if( interrupt_handler )
{
int res = interrupt_handler();
if( res )
{
return res;
}
if( lookahead_count )
{
return lookahead_arr[--lookahead_count];
}
}
do_loop = true;
break;
}
default:
{
/*
The terminal has been closed. Save and exit.
*/
return R_EOF;
}
}
}
else
{
/* Assume we loop unless we see a character in stdin */
do_loop = true;
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();
if( lookahead_count )
{
return lookahead_arr[--lookahead_count];
}
}
if ( ioport > 0 && FD_ISSET(ioport, &fdset))
{
}
if ( ioport > 0 && FD_ISSET(ioport, &fdset))
{
iothread_service_completion();
if( lookahead_count )
{
return lookahead_arr[--lookahead_count];
}
}
if( FD_ISSET( STDIN_FILENO, &fdset ) )
{
if( read_blocked( 0, arr, 1 ) != 1 )
{
/* The teminal has been closed. Save and exit. */
return R_EOF;
}
}
if( FD_ISSET( STDIN_FILENO, &fdset ) )
{
if( read_blocked( 0, arr, 1 ) != 1 )
{
/* The teminal has been closed. Save and exit. */
return R_EOF;
}
/* We read from stdin, so don't loop */
do_loop = false;
}
}
}
while( do_loop );
return arr[0];
do_loop = false;
}
}
}
while( do_loop );
return arr[0];
}
wchar_t input_common_readch( int timed )
{
if( lookahead_count == 0 )
{
if( timed )
{
int count;
fd_set fds;
struct timeval tm=
{
0,
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;
if( lookahead_count == 0 )
{
if( timed )
{
int count;
fd_set fds;
struct timeval tm=
{
0,
1000 * WAIT_ON_ESCAPE
}
;
}
}
wchar_t res;
static mbstate_t state;
FD_ZERO( &fds );
FD_SET( 0, &fds );
count = select(1, &fds, 0, 0, &tm);
while(1)
{
wint_t b = readb();
char bb;
size_t sz;
if( (b >= R_NULL) && (b < R_NULL + 1000) )
return b;
switch( count )
{
case 0:
return WEOF;
bb=b;
sz = mbrtowc( &res, &bb, 1, &state );
switch( sz )
{
case (size_t)(-1):
memset (&state, '\0', sizeof (state));
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];
}
case -1:
return WEOF;
break;
default:
break;
}
}
wchar_t res;
static mbstate_t state;
while(1)
{
wint_t b = readb();
char bb;
size_t sz;
if( (b >= R_NULL) && (b < R_NULL + 1000) )
return b;
bb=b;
sz = mbrtowc( &res, &bb, 1, &state );
switch( sz )
{
case (size_t)(-1):
memset (&state, '\0', sizeof (state));
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 )
{
lookahead_arr[lookahead_count++] = ch;
lookahead_arr[lookahead_count++] = ch;
}

View file

@ -1,5 +1,5 @@
/** \file input_common.h
Header file for the low level input library
*/
@ -15,15 +15,15 @@ Header file for the low level input library
enum
{
/**
R_NULL is sometimes returned by the input when a character was
requested but none could be delivered, or when an exception
happened.
*/
R_NULL = INPUT_COMMON_RESERVED,
R_EOF
/**
R_NULL is sometimes returned by the input when a character was
requested but none could be delivered, or when an exception
happened.
*/
R_NULL = INPUT_COMMON_RESERVED,
R_EOF
}
;
;
/**
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 )
{
if( !in )
return NULL;
// debug( 0, L"intern %ls", in );
if( !in )
return NULL;
// debug( 0, L"intern %ls", in );
scoped_lock lock(intern_lock);
const wchar_t *result;
#if USE_SET
string_table_t::const_iterator iter = string_table.find(in);
if (iter != string_table.end()) {
result = *iter;
result = *iter;
} else {
result = dup ? wcsdup(in) : in;
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 )
{
return intern_with_dup(in, true);
return intern_with_dup(in, true);
}
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
Utilities for io redirection.
*/
#include "config.h"
@ -53,105 +53,105 @@ Utilities for io redirection.
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( fcntl( d->param1.pipe_fd[0], F_SETFL, 0 ) )
{
wperror( L"fcntl" );
return;
} */
debug( 4, L"io_buffer_read: blocking read on fd %d", d->param1.pipe_fd[0] );
while(1)
{
char b[4096];
long l;
l=read_blocked( d->param1.pipe_fd[0], b, 4096 );
if( l==0 )
{
break;
}
else if( l<0 )
{
/*
exec_read_io_buffer is only called on jobs that have
exited, and will therefore never block. But a broken
pipe seems to cause some flags to reset, causing the
EOF flag to not be set. Therefore, EAGAIN is ignored
and we exit anyway.
*/
if( errno != EAGAIN )
{
debug( 1,
_(L"An error occured while reading output from code block on file descriptor %d"),
d->param1.pipe_fd[0] );
wperror( L"io_buffer_read" );
}
break;
}
else
{
d->out_buffer_append( b, l );
}
}
}
if( d->io_mode == IO_BUFFER )
{
/* if( fcntl( d->param1.pipe_fd[0], F_SETFL, 0 ) )
{
wperror( L"fcntl" );
return;
} */
debug( 4, L"io_buffer_read: blocking read on fd %d", d->param1.pipe_fd[0] );
while(1)
{
char b[4096];
long l;
l=read_blocked( d->param1.pipe_fd[0], b, 4096 );
if( l==0 )
{
break;
}
else if( l<0 )
{
/*
exec_read_io_buffer is only called on jobs that have
exited, and will therefore never block. But a broken
pipe seems to cause some flags to reset, causing the
EOF flag to not be set. Therefore, EAGAIN is ignored
and we exit anyway.
*/
if( errno != EAGAIN )
{
debug( 1,
_(L"An error occured while reading output from code block on file descriptor %d"),
d->param1.pipe_fd[0] );
wperror( L"io_buffer_read" );
}
break;
}
else
{
d->out_buffer_append( b, l );
}
}
}
}
io_data_t *io_buffer_create( bool is_input )
{
bool success = true;
io_data_t *buffer_redirect = new io_data_t;
buffer_redirect->out_buffer_create();
buffer_redirect->io_mode = IO_BUFFER;
buffer_redirect->is_input = is_input ? true : false;
buffer_redirect->fd=is_input?0:1;
if( exec_pipe( buffer_redirect->param1.pipe_fd ) == -1 )
{
debug( 1, PIPE_ERROR );
wperror (L"pipe");
success = false;
}
else if( fcntl( buffer_redirect->param1.pipe_fd[0],
F_SETFL,
O_NONBLOCK ) )
{
debug( 1, PIPE_ERROR );
wperror( L"fcntl" );
success = false;
}
io_data_t *buffer_redirect = new io_data_t;
buffer_redirect->out_buffer_create();
buffer_redirect->io_mode = IO_BUFFER;
buffer_redirect->is_input = is_input ? true : false;
buffer_redirect->fd=is_input?0:1;
if( exec_pipe( buffer_redirect->param1.pipe_fd ) == -1 )
{
debug( 1, PIPE_ERROR );
wperror (L"pipe");
success = false;
}
else if( fcntl( buffer_redirect->param1.pipe_fd[0],
F_SETFL,
O_NONBLOCK ) )
{
debug( 1, PIPE_ERROR );
wperror( L"fcntl" );
success = false;
}
if (! success)
{
delete buffer_redirect;
buffer_redirect = NULL;
}
return buffer_redirect;
return buffer_redirect;
}
void io_buffer_destroy( io_data_t *io_buffer )
{
/**
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.
*/
if( io_buffer->is_input )
{
exec_close(io_buffer->param1.pipe_fd[1] );
}
/**
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.
*/
if( io_buffer->is_input )
{
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
calling exec_read_io_buffer on the buffer
*/
delete io_buffer;
/*
Dont free fd for writing. This should already be free'd before
calling exec_read_io_buffer on the buffer
*/
delete io_buffer;
}
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);
return;
}
fprintf(stderr, "Chain %p (%ld items):\n", &chain, (long)chain.size());
for (size_t i=0; i < chain.size(); 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);
}
void io_chain_destroy(io_chain_t &chain)
void io_chain_destroy(io_chain_t &chain)
{
chain.destroy();
}

78
io.h
View file

@ -10,7 +10,7 @@ using std::tr1::shared_ptr;
*/
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 */
@ -24,35 +24,35 @@ private:
void operator=(const io_data_t &rhs);
public:
/** Type of redirect */
int io_mode;
/** FD to redirect */
int fd;
/** Type of redirect */
int io_mode;
/** FD to redirect */
int fd;
/**
Type-specific parameter for redirection
*/
union
{
/** Fds for IO_PIPE and for IO_BUFFER */
int pipe_fd[2];
/** fd to redirect specified fd to, for IO_FD */
int old_fd;
} param1;
/**
Type-specific parameter for redirection
*/
union
{
/** Fds for IO_PIPE and for IO_BUFFER */
int pipe_fd[2];
/** fd to redirect specified fd to, for IO_FD */
int old_fd;
} 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. */
const char *filename_cstr;
/** Convenience to set filename_cstr via wcstring */
void set_filename(const wcstring &str) {
free((void *)filename_cstr);
@ -63,33 +63,33 @@ public:
void out_buffer_create() {
out_buffer.reset(new std::vector<char>);
}
/** Function to append to the buffer */
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);
}
/** Function to get a pointer to the buffer */
char *out_buffer_ptr(void) {
assert(out_buffer.get() != NULL);
return out_buffer->empty() ? NULL : &out_buffer->at(0);
}
const char *out_buffer_ptr(void) const {
assert(out_buffer.get() != NULL);
return out_buffer->empty() ? NULL : &out_buffer->at(0);
}
/** Function to get the size of the buffer */
size_t out_buffer_size(void) const {
assert(out_buffer.get() != NULL);
return out_buffer->size();
}
/** Set to true if this is an input io redirection */
bool is_input;
/** Set to true if this is an input io redirection */
bool is_input;
io_data_t() :
out_buffer(),
io_mode(0),
@ -100,7 +100,7 @@ public:
is_input(0)
{
}
io_data_t(const io_data_t &rhs) :
out_buffer(rhs.out_buffer),
io_mode(rhs.io_mode),
@ -111,7 +111,7 @@ public:
is_input(rhs.is_input)
{
}
~io_data_t() {
free((void *)filename_cstr);
}
@ -121,15 +121,15 @@ class io_chain_t : public std::vector<io_data_t *> {
public:
io_chain_t();
io_chain_t(io_data_t *);
void remove(const io_data_t *element);
io_chain_t duplicate() const;
void duplicate_prepend(const io_chain_t &src);
void destroy();
const io_data_t *get_io_for_fd(int fd) const;
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;
static struct WorkerThread_t {
ThreadIndex_t idx;
pthread_t thread;
ThreadIndex_t idx;
pthread_t thread;
} threads[IO_MAX_THREADS];
struct ThreadedRequest_t {
int sequenceNumber;
int (*handler)(void *);
void (*completionCallback)(void *, int);
void *context;
int handlerResult;
int sequenceNumber;
int (*handler)(void *);
void (*completionCallback)(void *, int);
void *context;
int handlerResult;
};
static struct WorkerThread_t *next_vacant_thread_slot(void) {
for (ThreadIndex_t i=0; i < IO_MAX_THREADS; i++) {
if (! threads[i].thread) return &threads[i];
}
return NULL;
for (ThreadIndex_t i=0; i < IO_MAX_THREADS; i++) {
if (! threads[i].thread) return &threads[i];
}
return NULL;
}
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 void iothread_init(void) {
static bool inited = false;
if (! inited) {
inited = true;
/* Initialize the queue lock */
VOMIT_ON_FAILURE(pthread_mutex_init(&s_request_queue_lock, NULL));
/* Initialize the completion pipes */
int pipes[2] = {0, 0};
VOMIT_ON_FAILURE(pipe(pipes));
s_read_pipe = pipes[0];
s_write_pipe = pipes[1];
static bool inited = false;
if (! inited) {
inited = true;
/* Initialize the queue lock */
VOMIT_ON_FAILURE(pthread_mutex_init(&s_request_queue_lock, NULL));
/* Initialize the completion pipes */
int pipes[2] = {0, 0};
VOMIT_ON_FAILURE(pipe(pipes));
s_read_pipe = pipes[0];
s_write_pipe = pipes[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_write_pipe, F_SETFD, FD_CLOEXEC));
/* Tell each thread its index */
for (ThreadIndex_t i=0; i < IO_MAX_THREADS; i++) {
threads[i].idx = i;
}
}
/* Tell each thread its index */
for (ThreadIndex_t i=0; i < IO_MAX_THREADS; i++) {
threads[i].idx = i;
}
}
}
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);
}
@ -94,117 +94,117 @@ static ThreadedRequest_t *dequeue_request(void) {
/* The function that does thread work. */
static void *iothread_worker(void *threadPtr) {
assert(threadPtr != NULL);
struct WorkerThread_t *thread = (struct WorkerThread_t *)threadPtr;
/* Grab a request off of the queue */
struct ThreadedRequest_t *req = dequeue_request();
assert(threadPtr != NULL);
struct WorkerThread_t *thread = (struct WorkerThread_t *)threadPtr;
/* Run the handler and store the result */
if (req) {
req->handlerResult = req->handler(req->context);
}
/* Write our index to wake up the main thread */
VOMIT_ON_FAILURE(! write_loop(s_write_pipe, (const char *)&thread->idx, sizeof thread->idx));
/* We're done */
return req;
/* Grab a request off of the queue */
struct ThreadedRequest_t *req = dequeue_request();
/* Run the handler and store the result */
if (req) {
req->handlerResult = req->handler(req->context);
}
/* Write our index to wake up the main thread */
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. */
static void iothread_spawn_if_needed(void) {
ASSERT_IS_LOCKED(s_request_queue_lock);
if (! s_request_queue.empty() && s_active_thread_count < IO_MAX_THREADS) {
struct WorkerThread_t *thread = next_vacant_thread_slot();
assert(thread != NULL);
if (! s_request_queue.empty() && s_active_thread_count < IO_MAX_THREADS) {
struct WorkerThread_t *thread = next_vacant_thread_slot();
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. */
sigset_t newSet, savedSet;
sigfillset(&newSet);
VOMIT_ON_FAILURE(pthread_sigmask(SIG_BLOCK, &newSet, &savedSet));
/* Spawn a thread. */
int err;
do {
err = 0;
if (pthread_create(&thread->thread, NULL, iothread_worker, thread)) {
err = errno;
}
} while (err == EAGAIN);
/* Spawn a thread. */
int err;
do {
err = 0;
if (pthread_create(&thread->thread, NULL, iothread_worker, thread)) {
err = errno;
}
} while (err == EAGAIN);
/* Need better error handling - perhaps try again later. */
assert(err == 0);
/* Note that we are spawned another thread */
s_active_thread_count += 1;
assert(err == 0);
/* Note that we are spawned another thread */
s_active_thread_count += 1;
/* Restore our sigmask */
VOMIT_ON_FAILURE(pthread_sigmask(SIG_SETMASK, &savedSet, NULL));
}
}
}
int iothread_perform_base(int (*handler)(void *), void (*completionCallback)(void *, int), void *context) {
ASSERT_IS_MAIN_THREAD();
ASSERT_IS_NOT_FORKED_CHILD();
iothread_init();
/* Create and initialize a request. */
struct ThreadedRequest_t *req = new ThreadedRequest_t();
req->handler = handler;
req->completionCallback = completionCallback;
req->context = context;
req->sequenceNumber = ++s_last_sequence_number;
iothread_init();
/* Create and initialize a request. */
struct ThreadedRequest_t *req = new ThreadedRequest_t();
req->handler = handler;
req->completionCallback = completionCallback;
req->context = context;
req->sequenceNumber = ++s_last_sequence_number;
/* Take our lock */
scoped_lock lock(s_request_queue_lock);
/* Add to the queue */
add_to_queue(req);
/* Spawn a thread if necessary */
iothread_spawn_if_needed();
return 0;
}
int iothread_port(void) {
iothread_init();
return s_read_pipe;
iothread_init();
return s_read_pipe;
}
void iothread_service_completion(void) {
ASSERT_IS_MAIN_THREAD();
ThreadIndex_t threadIdx = (ThreadIndex_t)-1;
VOMIT_ON_FAILURE(1 != read_loop(iothread_port(), &threadIdx, sizeof threadIdx));
assert(threadIdx < IO_MAX_THREADS);
struct WorkerThread_t *thread = &threads[threadIdx];
assert(thread->thread != 0);
struct ThreadedRequest_t *req = NULL;
VOMIT_ON_FAILURE(pthread_join(thread->thread, (void **)&req));
/* Free up this thread */
thread->thread = 0;
assert(s_active_thread_count > 0);
s_active_thread_count -= 1;
/* Handle the request */
ThreadIndex_t threadIdx = (ThreadIndex_t)-1;
VOMIT_ON_FAILURE(1 != read_loop(iothread_port(), &threadIdx, sizeof threadIdx));
assert(threadIdx < IO_MAX_THREADS);
struct WorkerThread_t *thread = &threads[threadIdx];
assert(thread->thread != 0);
struct ThreadedRequest_t *req = NULL;
VOMIT_ON_FAILURE(pthread_join(thread->thread, (void **)&req));
/* Free up this thread */
thread->thread = 0;
assert(s_active_thread_count > 0);
s_active_thread_count -= 1;
/* Handle the request */
if (req) {
if (req->completionCallback)
if (req->completionCallback)
req->completionCallback(req->context, req->handlerResult);
delete req;
}
/* Maybe spawn another thread, if there's more work to be done. */
VOMIT_ON_FAILURE(pthread_mutex_lock(&s_request_queue_lock));
iothread_spawn_if_needed();
VOMIT_ON_FAILURE(pthread_mutex_unlock(&s_request_queue_lock));
/* Maybe spawn another thread, if there's more work to be done. */
VOMIT_ON_FAILURE(pthread_mutex_lock(&s_request_queue_lock));
iothread_spawn_if_needed();
VOMIT_ON_FAILURE(pthread_mutex_unlock(&s_request_queue_lock));
}
void iothread_drain_all(void) {
ASSERT_IS_MAIN_THREAD();
ASSERT_IS_NOT_FORKED_CHILD();
ASSERT_IS_NOT_FORKED_CHILD();
if (s_active_thread_count == 0)
return;
#define TIME_DRAIN 0

View file

@ -7,7 +7,7 @@
/**
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 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.
@ -15,9 +15,9 @@
*/
int iothread_perform_base(int (*handler)(void *), void (*completionCallback)(void *, int), void *context);
/**
/**
Gets the fd on which to listen for completion callbacks.
\return A file descriptor on which to listen for completion callbacks.
*/
int iothread_port(void);

View file

@ -1,9 +1,9 @@
/*
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
the way key_reader works.
key. Servers the same function as hitting ^V in bash, but I prefer
the way key_reader works.
Type ^C to exit the program.
Type ^C to exit the program.
*/
#include "config.h"
@ -22,76 +22,76 @@
int writestr( char *str )
{
write( 1, str, strlen(str) );
return 0;
write( 1, str, strlen(str) );
return 0;
}
int main( int argc, char **argv)
{
set_main_thread();
set_main_thread();
setup_fork_guards();
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 );
setlocale( LC_ALL, "" );
res++;
}
printf( "\n" );
}
else
{
printf("Undefined sequence\n");
}
}
else
{
char scratch[1024];
unsigned int c;
if( argc == 2 )
{
static char term_buffer[2048];
char *termtype = getenv ("TERM");
char *tbuff = new char[9999];
char *res;
struct termios modes, /* so we can change the modes */
savemodes; /* so we can reset the modes when we're done */
tgetent( term_buffer, termtype );
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 */
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);
res++;
}
printf( "\n" );
}
else
{
printf("Undefined sequence\n");
}
}
else
{
char scratch[1024];
unsigned int c;
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
The killring.
The killring.
Works like the killring in emacs and readline. The killring is cut
and paste with a memory of previous cuts. It supports integration
with the X clipboard.
Works like the killring in emacs and readline. The killring is cut
and paste with a memory of previous cuts. It supports integration
with the X clipboard.
*/
#include "config.h"
@ -57,12 +57,12 @@ static wchar_t *cut_buffer=0;
*/
static int has_xsel()
{
static int res=-1;
if (res < 0) {
res = !! path_get_path(L"xsel", NULL);
static int res=-1;
if (res < 0) {
res = !! path_get_path(L"xsel", NULL);
}
return res;
return res;
}
void kill_add( const wcstring &str )
@ -70,57 +70,57 @@ void kill_add( const wcstring &str )
ASSERT_IS_MAIN_THREAD();
if (str.empty())
return;
wcstring cmd;
wcstring cmd;
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,
and, if so, use it instead of checking the display, etc.
/*
Check to see if user has set the FISH_CLIPBOARD_CMD variable,
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
command too, so, the command used must accept the input via stdin.
*/
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.
*/
const env_var_t clipboard_wstr = env_get_string(L"FISH_CLIPBOARD_CMD");
if( !clipboard_wstr.missing() )
{
escaped_str = escape( str.c_str(), 1 );
const env_var_t clipboard_wstr = env_get_string(L"FISH_CLIPBOARD_CMD");
if( !clipboard_wstr.missing() )
{
escaped_str = escape( str.c_str(), 1 );
cmd.assign(L"echo -n ");
cmd.append(escaped_str);
cmd.append(clipboard_wstr);
}
else
{
/* This is for sending the kill to the X copy-and-paste buffer */
if( !has_xsel() ) {
return;
}
}
else
{
/* This is for sending the kill to the X copy-and-paste buffer */
if( !has_xsel() ) {
return;
}
const env_var_t disp_wstr = env_get_string( L"DISPLAY" );
if( !disp_wstr.missing() )
{
escaped_str = escape( str.c_str(), 1 );
const env_var_t disp_wstr = env_get_string( L"DISPLAY" );
if( !disp_wstr.missing() )
{
escaped_str = escape( str.c_str(), 1 );
cmd.assign(L"echo ");
cmd.append(escaped_str);
cmd.append(L"|xsel -b" );
}
}
}
}
if (! cmd.empty())
{
if( exec_subshell(cmd) == -1 )
{
/*
Do nothing on failiure
*/
}
free( cut_buffer );
cut_buffer = escaped_str;
}
if (! cmd.empty())
{
if( exec_subshell(cmd) == -1 )
{
/*
Do nothing on failiure
*/
}
free( cut_buffer );
cut_buffer = escaped_str;
}
}
/**
@ -133,13 +133,13 @@ static void kill_remove( const wcstring &s )
if (iter != kill_list.end())
kill_list.erase(iter);
}
void kill_replace( const wcstring &old, const wcstring &newv )
{
kill_remove( old );
kill_add( newv );
kill_remove( old );
kill_add( newv );
}
const wchar_t *kill_yank_rotate()
@ -159,50 +159,50 @@ const wchar_t *kill_yank_rotate()
clipboard contents to the fish killring.
*/
static void kill_check_x_buffer()
{
if( !has_xsel() )
return;
const env_var_t disp = env_get_string(L"DISPLAY");
if( ! disp.missing())
{
size_t i;
wcstring cmd = L"xsel -t 500 -b";
wcstring new_cut_buffer=L"";
{
if( !has_xsel() )
return;
const env_var_t disp = env_get_string(L"DISPLAY");
if( ! disp.missing())
{
size_t i;
wcstring cmd = L"xsel -t 500 -b";
wcstring new_cut_buffer=L"";
wcstring_list_t list;
if( exec_subshell( cmd, list ) != -1 )
{
for( i=0; i<list.size(); i++ )
{
wcstring next_line = escape_string( list.at(i), 0 );
if( exec_subshell( cmd, list ) != -1 )
{
for( i=0; i<list.size(); i++ )
{
wcstring next_line = escape_string( list.at(i), 0 );
if (i > 0) new_cut_buffer += L"\\n";
new_cut_buffer += next_line;
}
if( new_cut_buffer.size() > 0 )
{
/*
The buffer is inserted with backslash escapes,
since we don't really like tabs, newlines,
etc. anyway.
*/
}
if( new_cut_buffer.size() > 0 )
{
/*
The buffer is inserted with backslash escapes,
since we don't really like tabs, newlines,
etc. anyway.
*/
if (cut_buffer == NULL || cut_buffer != new_cut_buffer)
{
free(cut_buffer);
cut_buffer = wcsdup(new_cut_buffer.c_str());
kill_list.push_front( new_cut_buffer );
}
}
}
}
}
}
}
}
const wchar_t *kill_yank()
{
kill_check_x_buffer();
kill_check_x_buffer();
if (kill_list.empty()) {
return L"";
} else {
@ -220,7 +220,7 @@ void kill_init()
void kill_destroy()
{
if( cut_buffer )
free( cut_buffer );
if( cut_buffer )
free( cut_buffer );
}

4
kill.h
View file

@ -1,7 +1,7 @@
/** \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

72
lru.h
View file

@ -20,17 +20,17 @@ struct dereference_less_t {
class lru_node_t {
template<class T> friend class lru_cache_t;
/** Our linked list pointer */
lru_node_t *prev, *next;
public:
/** The key used to look up in the cache */
const wcstring key;
/** Constructor */
lru_node_t(const wcstring &pkey) : prev(NULL), next(NULL), key(pkey) { }
/** operator< for std::set */
bool operator<(const lru_node_t &other) const { return key < other.key; }
};
@ -41,29 +41,29 @@ class lru_cache_t {
/** Max node count */
const size_t max_node_count;
/** Count of nodes */
size_t node_count;
/** The set of nodes */
typedef std::set<lru_node_t *, dereference_less_t> node_set_t;
node_set_t node_set;
void promote_node(node_type_t *node) {
/* We should never promote the mouth */
assert(node != &mouth);
/* First unhook us */
node->prev->next = node->next;
node->next->prev = node->prev;
/* Put us after the mouth */
node->next = mouth.next;
node->next->prev = node;
node->prev = &mouth;
mouth.next = node;
}
void evict_node(node_type_t *condemned_node) {
/* We should never evict the mouth */
assert(condemned_node != NULL && condemned_node != &mouth);
@ -79,42 +79,42 @@ class lru_cache_t {
/* Tell ourselves */
this->node_was_evicted(condemned_node);
}
void evict_last_node(void) {
/* Simple */
evict_node((node_type_t *)mouth.prev);
}
protected:
/** Head of the linked list */
lru_node_t mouth;
/** Overridable callback for when a node is evicted */
virtual void node_was_evicted(node_type_t *node) { }
public:
/** Constructor */
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! */
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). */
virtual ~lru_cache_t() { }
/** Returns the node for a given key, or NULL */
node_type_t *get_node(const wcstring &key) {
node_type_t *result = NULL;
/* Construct a fake node as our key */
lru_node_t node_key(key);
/* Look for it in the set */
node_set_t::iterator iter = node_set.find(&node_key);
/* If we found a node, promote and return it */
if (iter != node_set.end()) {
result = static_cast<node_type_t*>(*iter);
@ -122,73 +122,73 @@ class lru_cache_t {
}
return result;
}
/** Evicts the node for a given key, returning true if a node was evicted. */
bool evict_node(const wcstring &key) {
/* Construct a fake node as our key */
lru_node_t node_key(key);
/* Look for it in the set */
node_set_t::iterator iter = node_set.find(&node_key);
if (iter == node_set.end())
return false;
/* Evict the given node */
evict_node(static_cast<node_type_t*>(*iter));
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. */
bool add_node(node_type_t *node) {
/* Add our node without eviction */
if (! this->add_node_without_eviction(node))
return false;
/* Evict */
while (node_count > max_node_count)
evict_last_node();
/* Success */
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. */
bool add_node_without_eviction(node_type_t *node) {
assert(node != NULL && node != &mouth);
/* Try inserting; return false if it was already in the set */
if (! node_set.insert(node).second)
return false;
/* Add the node after the mouth */
node->next = mouth.next;
node->next->prev = node;
node->prev = &mouth;
mouth.next = node;
/* Update the count */
node_count++;
/* Evict */
while (node_count > max_node_count)
evict_last_node();
/* Success */
return true;
}
/** Counts nodes */
size_t size(void) {
return node_count;
}
/** Evicts all nodes */
void evict_all_nodes(void) {
while (node_count > 0) {
evict_last_node();
}
}
/** Iterator for walking nodes, from least recently used to most */
class iterator {
lru_node_t *node;
@ -200,7 +200,7 @@ class lru_cache_t {
bool operator!=(const iterator &other) { return !(*this == other); }
node_type_t *operator*() { return static_cast<node_type_t *>(node); }
};
iterator begin() { return iterator(mouth.prev); }
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 );
/**
Names of different colors.
Names of different colors.
*/
static const wchar_t *col[]=
{
L"black",
L"red",
L"green",
L"brown",
L"yellow",
L"blue",
L"magenta",
L"purple",
L"cyan",
L"white"
L"normal"
L"black",
L"red",
L"green",
L"brown",
L"yellow",
L"blue",
L"magenta",
L"purple",
L"cyan",
L"white"
L"normal"
}
;
@ -91,17 +91,17 @@ static const wchar_t *col[]=
*/
static const int col_idx[]=
{
0,
1,
2,
3,
3,
4,
5,
5,
6,
7,
FISH_COLOR_NORMAL,
0,
1,
2,
3,
3,
4,
5,
5,
6,
7,
FISH_COLOR_NORMAL,
};
/**
@ -121,13 +121,13 @@ static bool support_term256 = false;
void output_set_writer( int (*writer)(char) )
{
CHECK( writer, );
out = writer;
CHECK( writer, );
out = writer;
}
int (*output_get_writer())(char)
{
return out;
return out;
}
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, stridx);
strcat(buff, "m");
int (*writer)(char) = output_get_writer();
if (writer) {
for (size_t i=0; buff[i]; i++) {
writer(buff[i]);
}
}
result = true;
}
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)
{
#if 0
wcstring tmp = c.description();
wcstring tmp2 = c2.description();
printf("set_color %ls : %ls\n", tmp.c_str(), tmp2.c_str());
#endif
#endif
ASSERT_IS_MAIN_THREAD();
const rgb_color_t normal = 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 int was_bold=0;
static int was_underline=0;
int bg_set=0, last_bg_set=0;
int is_bold = 0;
int is_underline = 0;
/*
static rgb_color_t last_color2 = rgb_color_t::normal();
static int was_bold=0;
static int was_underline=0;
int bg_set=0, last_bg_set=0;
int is_bold = 0;
int is_underline = 0;
/*
Test if we have at least basic support for setting fonts, colors
and related bits - otherwise just give up...
*/
if( !exit_attribute_mode )
{
return;
}
is_bold |= c.is_bold();
is_bold |= c2.is_bold();
is_underline |= c.is_underline();
is_underline |= c2.is_underline();
if( c.is_reset() || c2.is_reset())
{
c = c2 = normal;
was_bold=0;
was_underline=0;
if( !exit_attribute_mode )
{
return;
}
is_bold |= c.is_bold();
is_bold |= c2.is_bold();
is_underline |= c.is_underline();
is_underline |= c2.is_underline();
if( c.is_reset() || c2.is_reset())
{
c = c2 = normal;
was_bold=0;
was_underline=0;
/*
If we exit attibute mode, we must first set a color, or
previously coloured text might lose it's
color. Terminals are weird...
*/
write_foreground_color(0);
writembs( exit_attribute_mode );
return;
}
if( was_bold && !is_bold )
{
/*
Only way to exit bold mode is a reset of all attributes.
writembs( exit_attribute_mode );
return;
}
if( was_bold && !is_bold )
{
/*
Only way to exit bold mode is a reset of all attributes.
*/
writembs( exit_attribute_mode );
last_color = normal;
last_color2 = normal;
was_bold=0;
was_underline=0;
}
if( ! last_color2.is_normal() &&
writembs( exit_attribute_mode );
last_color = normal;
last_color2 = normal;
was_bold=0;
was_underline=0;
}
if( ! last_color2.is_normal() &&
! last_color2.is_reset() &&
! last_color2.is_ignore() )
{
/*
{
/*
Background was set
*/
last_bg_set=1;
}
if( ! c2.is_normal() &&
last_bg_set=1;
}
if( ! c2.is_normal() &&
! c2.is_ignore())
{
/*
{
/*
Background is set
*/
bg_set=1;
bg_set=1;
if ( c==c2 )
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(bg_set && !last_bg_set)
{
/*
}
if( (enter_bold_mode != 0) && (strlen(enter_bold_mode) > 0))
{
if(bg_set && !last_bg_set)
{
/*
Background color changed and is set, so we enter bold
mode to make reading easier. This means bold mode is
_always_ on when the background color is set.
*/
writembs( enter_bold_mode );
}
if(!bg_set && last_bg_set)
{
/*
writembs( enter_bold_mode );
}
if(!bg_set && last_bg_set)
{
/*
Background color changed and is no longer set, so we
exit bold mode
*/
writembs( exit_attribute_mode );
was_bold=0;
was_underline=0;
/*
writembs( exit_attribute_mode );
was_bold=0;
was_underline=0;
/*
We don't know if exit_attribute_mode resets colors, so
we set it to something known.
*/
if( write_foreground_color(0))
{
last_color=rgb_color_t::black();
}
}
}
if( last_color != c )
{
if( c.is_normal() )
{
write_foreground_color(0);
writembs( exit_attribute_mode );
last_color2 = rgb_color_t::normal();
was_bold=0;
was_underline=0;
}
else if( ! c.is_special() )
{
if( write_foreground_color(0))
{
last_color=rgb_color_t::black();
}
}
}
if( last_color != c )
{
if( c.is_normal() )
{
write_foreground_color(0);
writembs( exit_attribute_mode );
last_color2 = rgb_color_t::normal();
was_bold=0;
was_underline=0;
}
else if( ! c.is_special() )
{
write_foreground_color(index_for_color(c));
}
}
last_color = c;
if( last_color2 != c2 )
{
if( c2.is_normal() )
{
}
}
last_color = c;
if( last_color2 != c2 )
{
if( c2.is_normal() )
{
write_background_color(0);
writembs( exit_attribute_mode );
if( ! last_color.is_normal())
{
writembs( exit_attribute_mode );
if( ! last_color.is_normal())
{
write_foreground_color(index_for_color(last_color));
}
was_bold=0;
was_underline=0;
last_color2 = c2;
}
else if ( ! c2.is_special() )
{
}
was_bold=0;
was_underline=0;
last_color2 = c2;
}
else if ( ! c2.is_special() )
{
write_background_color(index_for_color(c2));
last_color2 = c2;
}
}
/*
last_color2 = c2;
}
}
/*
Lastly, we set bold mode and underline mode correctly
*/
if( (enter_bold_mode != 0) && (strlen(enter_bold_mode) > 0) && !bg_set )
{
if( is_bold && !was_bold )
{
if( enter_bold_mode )
{
writembs( tparm( enter_bold_mode ) );
}
}
was_bold = is_bold;
}
if( was_underline && !is_underline )
{
writembs( exit_underline_mode );
}
if( !was_underline && is_underline )
{
writembs( enter_underline_mode );
}
was_underline = is_underline;
if( (enter_bold_mode != 0) && (strlen(enter_bold_mode) > 0) && !bg_set )
{
if( is_bold && !was_bold )
{
if( enter_bold_mode )
{
writembs( tparm( enter_bold_mode ) );
}
}
was_bold = is_bold;
}
if( was_underline && !is_underline )
{
writembs( exit_underline_mode );
}
if( !was_underline && is_underline )
{
writembs( enter_underline_mode );
}
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 )
{
write_loop( 1, &c, 1 );
return 0;
write_loop( 1, &c, 1 );
return 0;
}
int writeb( tputs_arg_t b )
{
out( b );
return 0;
out( b );
return 0;
}
int writembs_internal( char *str )
{
CHECK( str, 1 );
return tputs(str,1,&writeb)==ERR?1:0;
CHECK( str, 1 );
return tputs(str,1,&writeb)==ERR?1:0;
}
int writech( wint_t ch )
{
mbstate_t state;
size_t i;
char buff[MB_LEN_MAX+1];
size_t bytes;
if( ( ch >= ENCODE_DIRECT_BASE) &&
mbstate_t state;
size_t i;
char buff[MB_LEN_MAX+1];
size_t bytes;
if( ( ch >= ENCODE_DIRECT_BASE) &&
( ch < ENCODE_DIRECT_BASE+256) )
{
buff[0] = ch - ENCODE_DIRECT_BASE;
bytes=1;
}
else
{
memset( &state, 0, sizeof(state) );
bytes= wcrtomb( buff, ch, &state );
switch( bytes )
{
case (size_t)(-1):
{
return 1;
}
}
}
for( i=0; i<bytes; i++ )
{
out( buff[i] );
}
return 0;
{
buff[0] = ch - ENCODE_DIRECT_BASE;
bytes=1;
}
else
{
memset( &state, 0, sizeof(state) );
bytes= wcrtomb( buff, ch, &state );
switch( bytes )
{
case (size_t)(-1):
{
return 1;
}
}
}
for( i=0; i<bytes; i++ )
{
out( buff[i] );
}
return 0;
}
void writestr( const wchar_t *str )
{
char *pos;
CHECK( str, );
// while( *str )
// writech( *str++ );
/*
char *pos;
CHECK( str, );
// while( *str )
// writech( *str++ );
/*
Check amount of needed space
*/
size_t len = wcstombs( 0, str, 0 );
if( len == (size_t)-1 )
{
debug( 1, L"Tried to print invalid wide character string" );
return;
}
len++;
/*
size_t len = wcstombs( 0, str, 0 );
if( len == (size_t)-1 )
{
debug( 1, L"Tried to print invalid wide character string" );
return;
}
len++;
/*
Convert
*/
char *buffer, static_buffer[256];
@ -472,19 +472,19 @@ void writestr( const wchar_t *str )
buffer = static_buffer;
else
buffer = new char[len];
wcstombs( buffer,
wcstombs( buffer,
str,
len );
/*
/*
Write
*/
for( pos = buffer; *pos; pos++ )
{
out( *pos );
}
for( pos = buffer; *pos; pos++ )
{
out( *pos );
}
if (buffer != static_buffer)
delete[] buffer;
}
@ -492,93 +492,93 @@ void writestr( const wchar_t *str )
void writestr_ellipsis( const wchar_t *str, int max_width )
{
int written=0;
int tot;
CHECK( str, );
tot = my_wcswidth(str);
if( tot <= max_width )
{
writestr( str );
return;
}
while( *str != 0 )
{
int w = fish_wcwidth( *str );
if( written+w+fish_wcwidth( ellipsis_char )>max_width )
{
break;
}
written+=w;
writech( *(str++) );
}
written += fish_wcwidth( ellipsis_char );
writech( ellipsis_char );
while( written < max_width )
{
written++;
writestr( L" " );
}
int written=0;
int tot;
CHECK( str, );
tot = my_wcswidth(str);
if( tot <= max_width )
{
writestr( str );
return;
}
while( *str != 0 )
{
int w = fish_wcwidth( *str );
if( written+w+fish_wcwidth( ellipsis_char )>max_width )
{
break;
}
written+=w;
writech( *(str++) );
}
written += fish_wcwidth( ellipsis_char );
writech( ellipsis_char );
while( written < max_width )
{
written++;
writestr( L" " );
}
}
int write_escaped_str( const wchar_t *str, int max_len )
{
wchar_t *out;
int i;
int len;
int written=0;
CHECK( str, 0 );
out = escape( str, 1 );
len = my_wcswidth( out );
if( max_len && (max_len < len))
{
for( i=0; (written+fish_wcwidth(out[i]))<=(max_len-1); i++ )
{
writech( out[i] );
written += fish_wcwidth( out[i] );
}
writech( ellipsis_char );
written += fish_wcwidth( ellipsis_char );
for( i=written; i<max_len; i++ )
{
writech( L' ' );
written++;
}
}
else
{
written = len;
writestr( out );
}
free( out );
return written;
wchar_t *out;
int i;
int len;
int written=0;
CHECK( str, 0 );
out = escape( str, 1 );
len = my_wcswidth( out );
if( max_len && (max_len < len))
{
for( i=0; (written+fish_wcwidth(out[i]))<=(max_len-1); i++ )
{
writech( out[i] );
written += fish_wcwidth( out[i] );
}
writech( ellipsis_char );
written += fish_wcwidth( ellipsis_char );
for( i=written; i<max_len; i++ )
{
writech( L' ' );
written++;
}
}
else
{
written = len;
writestr( out );
}
free( out );
return written;
}
int output_color_code( const wcstring &val, bool is_background ) {
size_t i;
size_t i;
int color=FISH_COLOR_NORMAL;
int is_bold=0;
int is_underline=0;
if (val.empty())
return FISH_COLOR_NORMAL;
int is_bold=0;
int is_underline=0;
if (val.empty())
return FISH_COLOR_NORMAL;
wcstring_list_t el;
tokenize_variable_array( val, el );
for(size_t j=0; j < el.size(); j++ ) {
tokenize_variable_array( val, el );
for(size_t j=0; j < el.size(); j++ ) {
const wcstring &next = el.at(j);
wcstring color_name;
if (is_background) {
@ -587,7 +587,7 @@ int output_color_code( const wcstring &val, bool is_background ) {
if (string_prefixes_string(prefix, next)) {
color_name = wcstring(next, prefix.size());
}
} else {
if (next == L"--bold" || next == L"-o")
is_bold = true;
@ -596,7 +596,7 @@ int output_color_code( const wcstring &val, bool is_background ) {
else
color_name = next;
}
if (! color_name.empty()) {
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 ) {
int is_bold=0;
int is_underline=0;
int is_underline=0;
std::vector<rgb_color_t> candidates;
wcstring_list_t el;
tokenize_variable_array( val, el );
for(size_t j=0; j < el.size(); j++ ) {
tokenize_variable_array( val, el );
for(size_t j=0; j < el.size(); j++ ) {
const wcstring &next = el.at(j);
wcstring color_name;
if (is_background) {
@ -639,7 +639,7 @@ rgb_color_t parse_color( const wcstring &val, bool is_background ) {
else
color_name = next;
}
if (! color_name.empty()) {
rgb_color_t color = rgb_color_t(color_name);
if (! color.is_none()) {
@ -647,7 +647,7 @@ rgb_color_t parse_color( const wcstring &val, bool is_background ) {
}
}
}
// Pick the best candidate
rgb_color_t first_rgb = rgb_color_t::none(), first_named = rgb_color_t::none();
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())
first_named = color;
}
// If we have both RGB and named colors, then prefer rgb if term256 is supported
rgb_color_t result;
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 {
result = first_named;
}
if (result.is_none())
result = rgb_color_t::normal();
result.set_bold(is_bold);
result.set_underline(is_underline);
#if 0
wcstring desc = result.description();
printf("Parsed %ls from %ls (%s)\n", desc.c_str(), val.c_str(), is_background ? "background" : "foreground");
#endif
return result;
}
void output_set_term( const wchar_t *term )
{
current_term = term;
current_term = term;
}
const wchar_t *output_get_term()

View file

@ -1,5 +1,5 @@
/** \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.
@ -13,22 +13,22 @@
#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
{
FISH_COLOR_BLACK,
FISH_COLOR_RED,
FISH_COLOR_GREEN,
FISH_COLOR_YELLOW,
FISH_COLOR_BLUE,
FISH_COLOR_MAGENTA,
FISH_COLOR_CYAN,
FISH_COLOR_WHITE,
/** The default fg color of the terminal */
FISH_COLOR_NORMAL,
FISH_COLOR_IGNORE,
FISH_COLOR_RESET
FISH_COLOR_BLACK,
FISH_COLOR_RED,
FISH_COLOR_GREEN,
FISH_COLOR_YELLOW,
FISH_COLOR_BLUE,
FISH_COLOR_MAGENTA,
FISH_COLOR_CYAN,
FISH_COLOR_WHITE,
/** The default fg color of the terminal */
FISH_COLOR_NORMAL,
FISH_COLOR_IGNORE,
FISH_COLOR_RESET
}
;
@ -81,21 +81,21 @@ void set_color(rgb_color_t c, rgb_color_t c2);
#define writembs( mbs ) \
{ \
char *tmp = mbs; \
if( tmp ) \
{ \
writembs_internal( tmp ); \
} \
else \
{ \
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"), \
#mbs, \
__LINE__, \
__FILE__, \
output_get_term(), \
PACKAGE_BUGREPORT); \
} \
}
if( tmp ) \
{ \
writembs_internal( tmp ); \
} \
else \
{ \
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"), \
#mbs, \
__LINE__, \
__FILE__, \
output_get_term(), \
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.
\param in the string to search for subshells
\param begin the starting 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
*/
int parse_util_locate_cmdsubst( const wchar_t *in,
wchar_t **begin,
wchar_t **end,
int flags );
int parse_util_locate_cmdsubst( const wchar_t *in,
wchar_t **begin,
wchar_t **end,
int flags );
/**
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
*/
void parse_util_cmdsubst_extent( const wchar_t *buff,
size_t cursor_pos,
const wchar_t **a,
const wchar_t **b );
size_t cursor_pos,
const wchar_t **a,
const wchar_t **b );
/**
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
*/
void parse_util_process_extent( const wchar_t *buff,
size_t cursor_pos,
const wchar_t **a,
const wchar_t **b );
size_t cursor_pos,
const wchar_t **a,
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
*/
void parse_util_job_extent( const wchar_t *buff,
size_t cursor_pos,
const wchar_t **a,
const wchar_t **b );
size_t cursor_pos,
const wchar_t **a,
const wchar_t **b );
/**
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
*/
void parse_util_token_extent( const wchar_t *buff,
size_t cursor_pos,
const wchar_t **tok_begin,
const wchar_t **tok_end,
const wchar_t **prev_begin,
const wchar_t **prev_end );
size_t cursor_pos,
const wchar_t **tok_begin,
const wchar_t **tok_end,
const wchar_t **prev_begin,
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
array of strings.
array of strings.
*/
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
The fish parser.
The fish parser.
*/
#ifndef FISH_PARSER_H
@ -21,15 +21,15 @@
*/
struct event_blockage_t
{
/**
The types of events to block. This is interpreted as a bitset
whete the value is 1 for every bit corresponding to a blocked
event type. For example, if EVENT_VARIABLE type events should
be blocked, (type & 1<<EVENT_BLOCKED) should be set.
/**
The types of events to block. This is interpreted as a bitset
whete the value is 1 for every bit corresponding to a blocked
event type. For example, if EVENT_VARIABLE type events should
be blocked, (type & 1<<EVENT_BLOCKED) should be set.
Note that EVENT_ANY can be used to specify any event.
*/
unsigned int typemask;
Note that EVENT_ANY can be used to specify any event.
*/
unsigned int typemask;
};
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
{
WHILE, /**< While loop block */
FOR, /**< For loop block */
IF, /**< If block */
FUNCTION_DEF, /**< Function definition block */
FUNCTION_CALL, /**< Function invocation block */
FUNCTION_CALL_NO_SHADOW, /**< Function invocation block with no variable shadowing */
SWITCH, /**< Switch block */
FAKE, /**< Fake block */
SUBST, /**< Command substitution scope */
TOP, /**< Outermost block */
BEGIN, /**< Unconditional block */
SOURCE, /**< Block created by the . (source) builtin */
EVENT, /**< Block created on event notifier invocation */
BREAKPOINT, /**< Breakpoint block */
WHILE, /**< While loop block */
FOR, /**< For loop block */
IF, /**< If block */
FUNCTION_DEF, /**< Function definition block */
FUNCTION_CALL, /**< Function invocation block */
FUNCTION_CALL_NO_SHADOW, /**< Function invocation block with no variable shadowing */
SWITCH, /**< Switch block */
FAKE, /**< Fake block */
SUBST, /**< Command substitution scope */
TOP, /**< Outermost block */
BEGIN, /**< Unconditional block */
SOURCE, /**< Block created by the . (source) builtin */
EVENT, /**< Block created on event notifier invocation */
BREAKPOINT, /**< Breakpoint block */
}
;
/**
block_t represents a block of commands.
block_t represents a block of commands.
*/
struct block_t
{
@ -77,63 +77,63 @@ struct block_t
block_t(block_type_t t);
private:
const block_type_t block_type; /**< Type of block. */
const block_type_t block_type; /**< Type of block. */
bool made_fake;
public:
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. */
void mark_as_fake() { this->made_fake = true; }
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 */
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;
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 */
int tok_pos; /**< The start index of the block */
/**
The job that is currently evaluated in the specified block.
*/
job_t *job;
/**
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.
*/
job_t *job;
#if 0
union
{
int while_state; /**< True if the loop condition has not yet been evaluated*/
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 */
wchar_t *switch_value; /**< The value to test in a switch block */
const wchar_t *source_dest; /**< The name of the file to source*/
event_t *event; /**<The event that triggered this block */
wchar_t *function_call_name;
} param1;
union
{
int while_state; /**< True if the loop condition has not yet been evaluated*/
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 */
wchar_t *switch_value; /**< The value to test in a switch block */
const wchar_t *source_dest; /**< The name of the file to source*/
event_t *event; /**<The event that triggered this block */
wchar_t *function_call_name;
} param1;
#endif
/**
Name of file that created this block
*/
const wchar_t *src_filename;
/**
Line number where this block was created
*/
int src_lineno;
/**
Name of file that created this block
*/
const wchar_t *src_filename;
/**
Line number where this block was created
*/
int src_lineno;
/** Whether we should pop the environment variable stack when we're popped off of the block stack */
bool wants_pop_env;
/** List of event blocks. */
event_blockage_list_t event_blocks;
/** List of event blocks. */
event_blockage_list_t event_blocks;
/**
Next outer block
*/
block_t *outer;
Next outer block
*/
block_t *outer;
/** Destructor */
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 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
if_block_t();
};
@ -211,11 +211,11 @@ struct breakpoint_block_t : public block_t
/**
Possible states for a loop
*/
enum loop_status
enum loop_status
{
LOOP_NORMAL, /**< Current loop block executed as normal */
LOOP_BREAK, /**< Current loop block should be removed */
LOOP_CONTINUE, /**< Current loop block should be skipped */
LOOP_NORMAL, /**< Current loop block executed as normal */
LOOP_BREAK, /**< Current loop block should be removed */
LOOP_CONTINUE, /**< Current loop block should be skipped */
};
@ -224,9 +224,9 @@ enum loop_status
*/
enum while_status
{
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_TESTED, /**< This is not the first command in the 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_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
*/
enum parser_error
enum parser_error
{
/**
No error
*/
NO_ERR=0,
/**
An error in the syntax
*/
SYNTAX_ERROR,
/**
Error occured while evaluating commands
*/
EVAL_ERROR,
/**
Error while evaluating cmdsubst
*/
CMDSUBST_ERROR,
/**
No error
*/
NO_ERR=0,
/**
An error in the syntax
*/
SYNTAX_ERROR,
/**
Error occured while evaluating commands
*/
EVAL_ERROR,
/**
Error while evaluating cmdsubst
*/
CMDSUBST_ERROR,
};
enum parser_type_t {
@ -259,30 +259,30 @@ enum parser_type_t {
PARSER_TYPE_GENERAL,
PARSER_TYPE_FUNCTIONS_ONLY,
PARSER_TYPE_COMPLETIONS_ONLY,
PARSER_TYPE_ERRORS_ONLY
PARSER_TYPE_ERRORS_ONLY
};
struct profile_item_t {
/**
Time spent executing the specified command, including parse time for nested blocks.
*/
int exec;
/**
Time spent parsing the specified command, including execution time for command substitutions.
*/
int parse;
/**
The block level of the specified command. nested blocks and command substitutions both increase the block level.
*/
size_t level;
/**
If the execution of this command was skipped.
*/
int skipped;
/**
The command string.
*/
wcstring cmd;
/**
Time spent executing the specified command, including parse time for nested blocks.
*/
int exec;
/**
Time spent parsing the specified command, including execution time for command substitutions.
*/
int parse;
/**
The block level of the specified command. nested blocks and command substitutions both increase the block level.
*/
size_t level;
/**
If the execution of this command was skipped.
*/
int skipped;
/**
The command string.
*/
wcstring cmd;
};
struct tokenizer;
@ -291,48 +291,48 @@ class parser_t {
private:
enum parser_type_t parser_type;
std::vector<block_t> blocks;
/** Whether or not we output errors */
const bool show_errors;
/** Last error code */
int error_code;
/** Position of last error */
int err_pos;
/** Description of last error */
wcstring err_buff;
/** Pointer to the current tokenizer */
tokenizer *current_tokenizer;
/** String for representing the current line */
wcstring lineinfo;
/** This is the position of the beginning of the currently parsed command */
int current_tokenizer_pos;
/** List of called functions, used to help prevent infinite recursion */
wcstring_list_t forbidden_function;
/** String index where the current job started. */
int job_start_pos;
/** The jobs associated with this parser */
job_list_t my_job_list;
/**
Keeps track of how many recursive eval calls have been made. Eval
doesn't call itself directly, recursion happens on blocks and on
command substitutions.
*/
int eval_level;
/* No copying allowed */
parser_t(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 );
int parse_job( process_t *p, job_t *j, tokenizer *tok );
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 );
void print_errors( wcstring &target, const wchar_t *prefix );
void print_errors_stderr();
public:
std::vector<profile_item_t> profile_items;
/**
Returns the name of the currently evaluated function if we are
currently evaluating a function, null otherwise. This is tested by
@ -351,27 +351,27 @@ class parser_t {
type FUNCTION_CALL.
*/
const wchar_t *is_function() const;
/** Get the "principal" parser, whatever that is */
static parser_t &principal_parser();
/** Indicates that execution of all blocks in the principal parser should stop.
This is called from signal handlers!
*/
static void skip_all_blocks();
/** Create a parser of the given type */
parser_t(enum parser_type_t type, bool show_errors);
/** The current innermost block, allocated with new */
block_t *current_block;
/** Global event blocks */
event_blockage_list_t global_event_blocks;
/** Current block level io redirections */
io_chain_t block_io;
/**
Evaluate the expressions contained in cmd.
@ -382,7 +382,7 @@ class parser_t {
\return 0 on success, 1 otherwise
*/
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.
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 output List to insert output to
*/
/**
\param line Line to evaluate
\param output List to insert output to
*/
int eval_args( const wchar_t *line, std::vector<completion_t> &output );
/**
\param line Line to evaluate
\param output List to insert output to
*/
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 p The character offset at which the error occured
\param str The printf-style error message filter
*/
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'.
Example:
Example:
init.fish (line 127): ls|grep pancake
*/
@ -415,7 +415,7 @@ class parser_t {
/** Returns the current line number */
int get_lineno() const;
/** Returns the line number for the character at the given index */
int line_number_of_character_at_offset(size_t idx) const;
@ -430,7 +430,7 @@ class parser_t {
/** Get the string currently parsed */
const wchar_t *get_buffer() const;
/** Get the list of jobs */
job_list_t &job_list() { return my_job_list; }
@ -445,16 +445,16 @@ class parser_t {
/** Create a job */
job_t *job_create();
/** Removes a job */
bool job_remove(job_t *job);
/** Promotes a job to the front of the list */
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. */
job_t *job_get(int job_id);
/** Returns the job with the given pid */
job_t *job_get_from_pid( int pid );
@ -502,7 +502,7 @@ class parser_t {
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 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 )
{
if (cmd == L"--") {
return ARG_SKIP;
if (cmd == L"--") {
return ARG_SKIP;
} else if (! cmd.empty() && cmd.at(0) == L'-') {
return ARG_SWITCH;
} else {
@ -27,49 +27,49 @@ bool parser_keywords_is_switch( const wcstring &cmd )
bool parser_keywords_skip_arguments( const wcstring &cmd )
{
return contains( cmd,
L"else",
L"begin" );
return contains( cmd,
L"else",
L"begin" );
}
bool parser_keywords_is_subcommand( const wcstring &cmd )
{
return parser_keywords_skip_arguments( cmd ) ||
contains( cmd,
L"command",
L"builtin",
L"while",
L"exec",
L"if",
L"and",
L"or",
L"not" );
return parser_keywords_skip_arguments( cmd ) ||
contains( cmd,
L"command",
L"builtin",
L"while",
L"exec",
L"if",
L"and",
L"or",
L"not" );
}
bool parser_keywords_is_block( const wcstring &word)
{
return contains( word,
L"for",
L"while",
L"if",
L"function",
L"switch",
L"begin" );
return contains( word,
L"for",
L"while",
L"if",
L"function",
L"switch",
L"begin" );
}
bool parser_keywords_is_reserved( const wcstring &word)
{
return parser_keywords_is_block(word) ||
parser_keywords_is_subcommand( word ) ||
contains( word,
L"end",
L"case",
L"else",
L"return",
L"continue",
L"break" );
return parser_keywords_is_block(word) ||
parser_keywords_is_subcommand( word ) ||
contains( word,
L"end",
L"case",
L"else",
L"return",
L"continue",
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()
*/
enum
enum
{
ARG_NON_SWITCH,
ARG_SWITCH,
ARG_SKIP
ARG_NON_SWITCH,
ARG_SWITCH,
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)
{
int err = ENOENT;
debug( 3, L"path_get_path( '%ls' )", cmd.c_str() );
int err = ENOENT;
debug( 3, L"path_get_path( '%ls' )", cmd.c_str() );
/* If the command has a slash, it must be a full path */
if (cmd.find(L'/') != wcstring::npos)
{
if( waccess( cmd, X_OK )==0 )
{
struct stat buff;
if(wstat( cmd, &buff ))
{
return false;
}
if( S_ISREG(buff.st_mode) )
if (cmd.find(L'/') != wcstring::npos)
{
if( waccess( cmd, X_OK )==0 )
{
struct stat buff;
if(wstat( cmd, &buff ))
{
return false;
}
if( S_ISREG(buff.st_mode) )
{
if (out_path)
if (out_path)
out_path->assign(cmd);
return true;
}
else
{
errno = EACCES;
return false;
}
}
else
{
struct stat buff;
wstat( cmd, &buff );
return false;
}
}
else
{
else
{
errno = EACCES;
return false;
}
}
else
{
struct stat buff;
wstat( cmd, &buff );
return false;
}
}
else
{
wcstring bin_path;
if (! bin_path_var.missing())
if (! bin_path_var.missing())
{
bin_path = bin_path_var;
}
else
{
if (contains( PREFIX L"/bin", L"/bin", L"/usr/bin" ))
{
bin_path = L"/bin" ARRAY_SEP_STR L"/usr/bin";
}
else
{
bin_path = L"/bin" ARRAY_SEP_STR L"/usr/bin" ARRAY_SEP_STR PREFIX L"/bin";
}
}
{
if (contains( PREFIX L"/bin", L"/bin", L"/usr/bin" ))
{
bin_path = L"/bin" ARRAY_SEP_STR L"/usr/bin";
}
else
{
bin_path = L"/bin" ARRAY_SEP_STR L"/usr/bin" ARRAY_SEP_STR PREFIX L"/bin";
}
}
wcstring nxt_path;
wcstokenizer tokenizer(bin_path, ARRAY_SEP_STR);
while (tokenizer.next(nxt_path))
{
while (tokenizer.next(nxt_path))
{
if (nxt_path.empty())
continue;
append_path_component(nxt_path, cmd);
if( waccess( nxt_path, X_OK )==0 )
{
struct stat buff;
if( wstat( nxt_path, &buff )==-1 )
{
if( errno != EACCES )
{
wperror( L"stat" );
}
continue;
}
if( S_ISREG(buff.st_mode) )
{
if( waccess( nxt_path, X_OK )==0 )
{
struct stat buff;
if( wstat( nxt_path, &buff )==-1 )
{
if( errno != EACCES )
{
wperror( L"stat" );
}
continue;
}
if( S_ISREG(buff.st_mode) )
{
if (out_path)
out_path->swap(nxt_path);
return true;
}
err = EACCES;
}
else
{
switch( errno )
{
case ENOENT:
case ENAMETOOLONG:
case EACCES:
case ENOTDIR:
break;
default:
{
debug( 1,
return true;
}
err = EACCES;
}
else
{
switch( errno )
{
case ENOENT:
case ENAMETOOLONG:
case EACCES:
case ENOTDIR:
break;
default:
{
debug( 1,
MISSING_COMMAND_ERR_MSG,
nxt_path.c_str() );
wperror( L"access" );
}
}
}
}
}
errno = err;
return false;
wperror( L"access" );
}
}
}
}
}
errno = err;
return false;
}
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)
{
wchar_t *res = 0;
int err = ENOENT;
wchar_t *res = 0;
int err = ENOENT;
bool success = false;
const wchar_t *const dir = dir_str.c_str();
if( dir[0] == L'/'|| (wcsncmp( dir, L"./", 2 )==0) )
{
struct stat buf;
if( wstat( dir, &buf ) == 0 )
{
if( S_ISDIR(buf.st_mode) )
{
result = dir_str;
if( dir[0] == L'/'|| (wcsncmp( dir, L"./", 2 )==0) )
{
struct stat buf;
if( wstat( dir, &buf ) == 0 )
{
if( S_ISDIR(buf.st_mode) )
{
result = dir_str;
success = true;
}
else
{
err = ENOTDIR;
}
}
}
else
{
}
else
{
err = ENOTDIR;
}
}
}
else
{
wcstring path = L".";
// Respect CDPATH
env_var_t cdpath = env_get_string(L"CDPATH");
if (! cdpath.missing_or_empty()) {
path = cdpath.c_str();
}
wcstokenizer tokenizer(path, ARRAY_SEP_STR);
wcstring next_path;
while (tokenizer.next(next_path))
{
expand_tilde(next_path);
if (next_path.size() == 0) continue;
wcstring whole_path = next_path;
append_path_component(whole_path, dir);
struct stat buf;
if( wstat( whole_path, &buf ) == 0 )
{
if( S_ISDIR(buf.st_mode) )
{
struct stat buf;
if( wstat( whole_path, &buf ) == 0 )
{
if( S_ISDIR(buf.st_mode) )
{
result = whole_path;
success = true;
break;
}
else
{
err = ENOTDIR;
}
}
else
{
if( lwstat( whole_path, &buf ) == 0 )
{
err = EROTTEN;
}
}
break;
}
else
{
err = ENOTDIR;
}
}
else
{
if( lwstat( whole_path, &buf ) == 0 )
{
err = EROTTEN;
}
}
}
}
if( !success )
{
errno = err;
}
return res;
if( !success )
{
errno = err;
}
return res;
}
bool path_get_cdpath(const wcstring &dir, wcstring *out, const wchar_t *wd, const env_vars_snapshot_t &env_vars)
{
int err = ENOENT;
if (dir.empty())
return false;
int err = ENOENT;
if (dir.empty())
return false;
if (wd)
{
size_t len = wcslen(wd);
assert(wd[len - 1] == L'/');
}
wcstring_list_t paths;
if (dir.at(0) == L'/') {
/* 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);
while (tokenizer.next(nxt_path))
{
if (nxt_path == L"." && wd != NULL) {
// 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
@ -262,39 +262,39 @@ bool path_get_cdpath(const wcstring &dir, wcstring *out, const wchar_t *wd, cons
}
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())
continue;
wcstring whole_path = nxt_path;
append_path_component(whole_path, dir);
paths.push_back(whole_path);
}
}
bool success = false;
for (wcstring_list_t::const_iterator iter = paths.begin(); iter != paths.end(); ++iter) {
struct stat buf;
struct stat buf;
const wcstring &dir = *iter;
if( wstat( dir, &buf ) == 0 )
{
if( S_ISDIR(buf.st_mode) )
{
if( wstat( dir, &buf ) == 0 )
{
if( S_ISDIR(buf.st_mode) )
{
success = true;
if (out)
out->assign(dir);
break;
}
else
{
err = ENOTDIR;
}
}
}
else
{
err = ENOTDIR;
}
}
}
if (! success)
errno = err;
errno = err;
return success;
}
@ -302,7 +302,7 @@ bool path_can_be_implicit_cd(const wcstring &path, wcstring *out_path, const wch
{
wcstring exp_path = path;
expand_tilde(exp_path);
bool result = false;
if (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)
{
int done = 0;
wcstring res;
const env_var_t xdg_dir = env_get_string( L"XDG_CONFIG_HOME" );
if( ! xdg_dir.missing() )
{
res = xdg_dir + L"/fish";
if( !create_directory( res ) )
{
done = 1;
}
}
else
{
const env_var_t home = env_get_string( L"HOME" );
if( ! home.missing() )
{
res = home + L"/.config/fish";
if( !create_directory( res ) )
{
done = 1;
}
}
}
if( done )
{
int done = 0;
wcstring res;
const env_var_t xdg_dir = env_get_string( L"XDG_CONFIG_HOME" );
if( ! xdg_dir.missing() )
{
res = xdg_dir + L"/fish";
if( !create_directory( res ) )
{
done = 1;
}
}
else
{
const env_var_t home = env_get_string( L"HOME" );
if( ! home.missing() )
{
res = home + L"/.config/fish";
if( !create_directory( res ) )
{
done = 1;
}
}
}
if( done )
{
path = res;
return true;
}
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." ));
return false;
}
}
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." ));
return false;
}
}
@ -375,7 +375,7 @@ void path_make_canonical( wcstring &path )
size = path.size();
replace_all(path, L"//", L"/");
} while (path.size() != size);
/* Remove trailing slashes, except don't remove the first one */
while (size-- > 1) {
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) {
if (path1 == path2)
return true;
struct stat s1, s2;
if (wstat(path1, &s1) == 0 && wstat(path2, &s2) == 0) {
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
Directory utilities. This library contains functions for locating
configuration directories, for testing if a command with a given
name can be found in the PATH, and various other path-related
issues.
Directory utilities. This library contains functions for locating
configuration directories, for testing if a command with a given
name can be found in the PATH, and various other path-related
issues.
*/
#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.
\param cmd The name of the executable.
\param output_or_NULL If non-NULL, store the full path.
\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
returned string is allocated using halloc and the specified
context.
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
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
symlink and a file are found, it is undefined which error status
will be returned.
\param dir The name of the 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.

View file

@ -1,6 +1,6 @@
/** \file postfork.cpp
Functions that we may safely call after fork().
Functions that we may safely call after fork().
*/
#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.
int set_child_group( job_t *j, process_t *p, int print_errors )
{
int res = 0;
if( job_get_flag( j, JOB_CONTROL ) )
{
if (!j->pgid)
{
j->pgid = p->pid;
}
if( setpgid (p->pid, j->pgid) )
{
if( getpgid( p->pid) != j->pgid && print_errors )
{
int res = 0;
if( job_get_flag( j, JOB_CONTROL ) )
{
if (!j->pgid)
{
j->pgid = p->pid;
}
if( setpgid (p->pid, j->pgid) )
{
if( getpgid( p->pid) != j->pgid && print_errors )
{
char pid_buff[128];
char job_id_buff[128];
char getpgid_buff[128];
char job_pgid_buff[128];
format_long_safe(pid_buff, p->pid);
format_long_safe(job_id_buff, j->job_id);
format_long_safe(getpgid_buff, getpgid( p->pid));
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" );
res = -1;
}
}
}
else
{
j->pgid = getpid();
}
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 );
if( job_get_flag( j, JOB_TERMINAL ) && job_get_flag( j, JOB_FOREGROUND ) )
{
if( tcsetpgrp (0, j->pgid) && print_errors )
{
wperror( L"setpgid" );
res = -1;
}
}
}
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];
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() );
wperror( L"tcsetpgrp" );
res = -1;
}
}
debug_safe( 1, "Could not send job %s ('%s') to foreground", job_id_buff, j->command_cstr() );
wperror( L"tcsetpgrp" );
res = -1;
}
}
return res;
return res;
}
/** 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++)
{
int fd_to_free = io_chain.at(i)->fd;
/* We only have to worry about fds beyond the three standard ones */
if (fd_to_free <= 2)
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);
if (possible_conflict->io_mode != IO_PIPE && possible_conflict->io_mode != IO_BUFFER)
continue;
/* If the pipe is a conflict, dup it to some other value */
for (int k=0; k<2; k++)
{
/* If it's not a conflict, we don't care */
if (possible_conflict->param1.pipe_fd[k] != fd_to_free)
continue;
/* Repeat until we have a replacement fd */
int replacement_fd = -1;
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;
}
}
}
}
}
@ -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 )
{
close_unused_internal_pipes( io_chain );
close_unused_internal_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);
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;
}
int tmp;
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( io->io_mode == IO_FD && io->fd == io->param1.old_fd )
{
continue;
}
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:
{
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:
{
// 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). */
unsigned int write_pipe_idx = (io->is_input ? 0 : 1);
unsigned int write_pipe_idx = (io->is_input ? 0 : 1);
/*
debug( 0,
L"%ls %ls on fd %d (%d %d)",
write_pipe?L"write":L"read",
(io->io_mode == IO_BUFFER)?L"buffer":L"pipe",
io->fd,
io->param1.pipe_fd[0],
io->param1.pipe_fd[1]);
debug( 0,
L"%ls %ls on fd %d (%d %d)",
write_pipe?L"write":L"read",
(io->io_mode == IO_BUFFER)?L"buffer":L"pipe",
io->fd,
io->param1.pipe_fd[0],
io->param1.pipe_fd[1]);
*/
if( dup2( io->param1.pipe_fd[write_pipe_idx], io->fd ) != io->fd )
{
debug_safe( 1, LOCAL_PIPE_ERROR );
perror( "dup2" );
return -1;
}
if( dup2( io->param1.pipe_fd[write_pipe_idx], io->fd ) != io->fd )
{
debug_safe( 1, LOCAL_PIPE_ERROR );
perror( "dup2" );
return -1;
}
if (io->param1.pipe_fd[0] >= 0)
exec_close( io->param1.pipe_fd[0]);
if (io->param1.pipe_fd[1] >= 0)
exec_close( io->param1.pipe_fd[1]);
break;
}
}
}
break;
}
}
}
return 0;
return 0;
}
int setup_child_process( job_t *j, process_t *p )
{
bool ok=true;
if( p )
{
ok = (0 == set_child_group( j, p, 1 ));
}
if( ok )
{
ok = (0 == handle_child_io( j->io ));
if( p != 0 && ! ok )
{
exit_without_destructors( 1 );
}
}
/* Set the handling for job control signals back to the default. */
if( ok )
{
signal_reset_handlers();
}
/* Remove all signal blocks */
signal_unblock();
return ok ? 0 : -1;
bool ok=true;
if( p )
{
ok = (0 == set_child_group( j, p, 1 ));
}
if( ok )
{
ok = (0 == handle_child_io( j->io ));
if( p != 0 && ! ok )
{
exit_without_destructors( 1 );
}
}
/* Set the handling for job control signals back to the default. */
if( ok )
{
signal_reset_handlers();
}
/* Remove all signal blocks */
signal_unblock();
return ok ? 0 : -1;
}
int g_fork_count = 0;
@ -318,47 +318,47 @@ int g_fork_count = 0;
pid_t execute_fork(bool wait_for_threads_to_die)
{
ASSERT_IS_MAIN_THREAD();
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. */
iothread_drain_all();
}
pid_t pid;
struct timespec pollint;
int i;
pid_t pid;
struct timespec pollint;
int i;
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;
pollint.tv_nsec = FORK_SLEEP_TIME;
for( i=0; i<FORK_LAPS; i++ )
{
pid = fork();
if( pid >= 0)
{
return pid;
}
/*
Don't sleep on the final lap - sleeping might change the
value of errno, which will break the error reporting below.
*/
if( i != FORK_LAPS-1 )
{
nanosleep( &pollint, NULL );
}
}
debug_safe( 0, FORK_ERROR );
wperror (L"fork");
FATAL_EXIT();
if( errno != EAGAIN )
{
break;
}
pollint.tv_sec = 0;
pollint.tv_nsec = FORK_SLEEP_TIME;
/*
Don't sleep on the final lap - sleeping might change the
value of errno, which will break the error reporting below.
*/
if( i != FORK_LAPS-1 )
{
nanosleep( &pollint, NULL );
}
}
debug_safe( 0, FORK_ERROR );
wperror (L"fork");
FATAL_EXIT();
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) {
return false;
}
if (posix_spawn_file_actions_init(actions) != 0) {
posix_spawnattr_destroy(attr);
return false;
}
bool should_set_parent_group_id = false;
int desired_parent_group_id = 0;
if (job_get_flag(j, JOB_CONTROL))
{
should_set_parent_group_id = true;
// PCA: I'm quite fuzzy on process groups,
// but I believe that the default value of 0
// 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.
desired_parent_group_id = j->pgid;
}
/* Set the handling for job control signals back to the default. */
bool reset_signal_handlers = true;
/* Remove all signal blocks */
bool reset_sigmask = true;
/* Set the handling for job control signals back to the default. */
bool reset_signal_handlers = true;
/* Remove all signal blocks */
bool reset_sigmask = true;
/* Set our flags */
short flags = 0;
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;
if (should_set_parent_group_id)
flags |= POSIX_SPAWN_SETPGROUP;
int err = 0;
if (! err)
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);
err = posix_spawnattr_setsigdefault(attr, &sigdefault);
}
/* No signals blocked */
sigset_t sigmask;
sigemptyset(&sigmask);
if (! err && reset_sigmask)
err = posix_spawnattr_setsigmask(attr, &sigmask);
/* Make sure that our pipes don't use an fd that the redirection itself wants to use */
free_redirected_fds_from_pipes(j->io);
/* Close unused internal pipes */
std::vector<int> files_to_close;
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));
}
for (size_t idx = 0; idx < j->io.size(); idx++)
{
const io_data_t *io = j->io.at(idx);
if( io->io_mode == IO_FD && io->fd == io->param1.old_fd )
{
continue;
}
if( io->fd > 2 )
{
/* Make sure the fd used by this redirection is not used by e.g. a pipe. */
if( io->io_mode == IO_FD && io->fd == io->param1.old_fd )
{
continue;
}
if( io->fd > 2 )
{
/* Make sure the fd used by this redirection is not used by e.g. a pipe. */
// free_fd(io_chain, io->fd );
// PCA I don't think we need to worry about this. fd redirection is pretty uncommon anyways.
}
}
switch (io->io_mode)
{
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);
break;
}
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);
break;
}
case IO_FD:
{
if (! err)
err = posix_spawn_file_actions_adddup2(actions, io->param1.old_fd /* from */, io->fd /* to */);
break;
}
case IO_BUFFER:
case IO_PIPE:
{
case IO_BUFFER:
case IO_PIPE:
{
unsigned int write_pipe_idx = (io->is_input ? 0 : 1);
int from_fd = io->param1.pipe_fd[write_pipe_idx];
int to_fd = io->fd;
if (! err)
err = posix_spawn_file_actions_adddup2(actions, from_fd, to_fd);
if( write_pipe_idx > 0 )
{
if (! err)
if( write_pipe_idx > 0 )
{
if (! err)
err = posix_spawn_file_actions_addclose(actions, io->param1.pipe_fd[0]);
if (! err)
err = posix_spawn_file_actions_addclose(actions, io->param1.pipe_fd[1]);
}
else
{
if (! err)
}
else
{
if (! err)
err = posix_spawn_file_actions_addclose(actions, io->param1.pipe_fd[0]);
}
}
break;
}
}
}
/* Clean up on error */
if (err) {
posix_spawnattr_destroy(attr);
posix_spawn_file_actions_destroy(actions);
}
return ! err;
}
#endif //FISH_USE_POSIX_SPAWN
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 );
switch( err )
{
case E2BIG:
{
char sz1[128], sz2[128];
long arg_max = -1;
debug_safe( 0, "Failed to execute process '%s'. Reason:", actual_cmd );
switch( err )
{
case E2BIG:
{
char sz1[128], sz2[128];
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);
arg_max = sysconf( _SC_ARG_MAX );
if( arg_max > 0 )
{
arg_max = sysconf( _SC_ARG_MAX );
if( arg_max > 0 )
{
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);
}
else
{
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;
}
}
else
{
debug_safe( 0, "The total size of the argument and environment lists (%s) exceeds the operating system limit.", sz1);
}
case ENOEXEC:
{
debug_safe(0, "Try running the command again with fewer arguments.");
break;
}
case ENOEXEC:
{
/* Hope strerror doesn't allocate... */
const char *err = strerror(errno);
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(). */
char interpreter_buff[128] = {}, *interpreter;
interpreter = get_interpreter(actual_cmd, interpreter_buff, sizeof interpreter_buff);
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 );
}
else
{
debug_safe(0, "The file '%s' does not exist or could not be executed.", actual_cmd);
}
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 );
}
else
{
debug_safe(0, "The file '%s' does not exist or could not be executed.", actual_cmd);
}
break;
}
}
case ENOMEM:
{
debug_safe(0, "Out of memory");
case ENOMEM:
{
debug_safe(0, "Out of memory");
break;
}
}
default:
{
default:
{
/* Hope strerror doesn't allocate... */
const char *err = strerror(errno);
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;
}
}
}
}
}

View file

@ -1,6 +1,6 @@
/** \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
@ -38,7 +38,7 @@
exit. The parent process may safely ignore the exit status of this
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 );

View file

@ -1,6 +1,6 @@
/** \file print_help.c
Print help message for the specified command
Print help message for the specified command
*/
#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 )
{
char cmd[ CMD_LEN];
int printed = snprintf( cmd, CMD_LEN, "fish -c '__fish_print_help %s >&%d'", c, fd );
if( printed < CMD_LEN )
{
if( (system( cmd ) == -1) )
{
write_loop(2, HELP_ERR, strlen(HELP_ERR));
}
}
char cmd[ CMD_LEN];
int printed = snprintf( cmd, CMD_LEN, "fish -c '__fish_print_help %s >&%d'", c, fd );
if( printed < CMD_LEN )
{
if( (system( cmd ) == -1) )
{
write_loop(2, HELP_ERR, strlen(HELP_ERR));
}
}
}

View file

@ -1,13 +1,13 @@
/** \file print_help.h
Print help message for the specified command
Print help message for the specified command
*/
#ifndef 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 );

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
well as signal handling functions for tracking children. These
functions do not themselves launch new processes, the exec library
will call proc to create representations of the running jobs as
needed.
well as signal handling functions for tracking children. These
functions do not themselves launch new processes, the exec library
will call proc to create representations of the running jobs as
needed.
*/
#ifndef FISH_PROC_H
@ -56,194 +56,194 @@
*/
enum
{
/**
A regular external command
*/
EXTERNAL,
/**
A builtin command
*/
INTERNAL_BUILTIN,
/**
A shellscript function
*/
INTERNAL_FUNCTION,
/**
A block of commands
*/
INTERNAL_BLOCK,
/**
The exec builtin
*/
INTERNAL_EXEC,
/**
A buffer
*/
INTERNAL_BUFFER,
/**
A regular external command
*/
EXTERNAL,
/**
A builtin command
*/
INTERNAL_BUILTIN,
/**
A shellscript function
*/
INTERNAL_FUNCTION,
/**
A block of commands
*/
INTERNAL_BLOCK,
/**
The exec builtin
*/
INTERNAL_EXEC,
/**
A buffer
*/
INTERNAL_BUFFER,
}
;
;
enum
{
JOB_CONTROL_ALL,
JOB_CONTROL_INTERACTIVE,
JOB_CONTROL_NONE,
JOB_CONTROL_ALL,
JOB_CONTROL_INTERACTIVE,
JOB_CONTROL_NONE,
}
;
;
/**
A structure representing a single fish process. Contains variables
for tracking process state and the process argument
list. Actually, a fish process can be either a regular externa
lrocess, an internal builtin which may or may not spawn a fake IO
process during execution, a shellscript function or a block of
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
determined by the type field, which can be one of EXTERNAL,
INTERNAL_BUILTIN, INTERNAL_FUNCTION, INTERNAL_BLOCK and
INTERNAL_EXEC, INTERNAL_BUFFER
/**
A structure representing a single fish process. Contains variables
for tracking process state and the process argument
list. Actually, a fish process can be either a regular externa
lrocess, an internal builtin which may or may not spawn a fake IO
process during execution, a shellscript function or a block of
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
determined by the type field, which can be one of EXTERNAL,
INTERNAL_BUILTIN, INTERNAL_FUNCTION, INTERNAL_BLOCK and
INTERNAL_EXEC, INTERNAL_BUFFER
The process_t contains information on how the process should be
started, such as command name and arguments, as well as runtime
information on the status of the actual physical process which
represents it. Shellscript functions, builtins and blocks of code
may all need to spawn an external process that handles the piping
and redirecting of IO for them.
The process_t contains information on how the process should be
started, such as command name and arguments, as well as runtime
information on the status of the actual physical process which
represents it. Shellscript functions, builtins and blocks of code
may all need to spawn an external process that handles the piping
and redirecting of IO for them.
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
to execute.
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
to execute.
If the process is of type INTERNAL_BUILTIN, argv is the argument
vector, and argv[0] is the name of the builtin command.
If the process is of type INTERNAL_BUILTIN, argv is the argument
vector, and argv[0] is the name of the builtin command.
If the process is of type INTERNAL_FUNCTION, argv is the argument
vector, and argv[0] is the name of the shellscript function.
If the process is of type INTERNAL_FUNCTION, argv is the argument
vector, and argv[0] is the name of the shellscript function.
If the process is of type INTERNAL_BLOCK, argv has exactly one
element, which is the block of commands to execute.
If the process is of type INTERNAL_BLOCK, argv has exactly one
element, which is the block of commands to execute.
*/
class process_t
{
private:
null_terminated_array_t<wchar_t> argv_array;
/* narrow copy of argv0 so we don't have to convert after fork */
narrow_string_rep_t argv0_narrow;
/* No copying */
process_t(const process_t &rhs);
void operator=(const process_t &rhs);
public:
process_t();
~process_t();
/**
Type of process. Can be one of \c EXTERNAL, \c
INTERNAL_BUILTIN, \c INTERNAL_FUNCTION, \c INTERNAL_BLOCK,
INTERNAL_EXEC, or INTERNAL_BUFFER
*/
int type;
/**
Type of process. Can be one of \c EXTERNAL, \c
INTERNAL_BUILTIN, \c INTERNAL_FUNCTION, \c INTERNAL_BLOCK,
INTERNAL_EXEC, or INTERNAL_BUFFER
*/
int type;
/** Sets argv */
void set_argv(const wcstring_list_t &argv) {
argv_array.set(argv);
argv0_narrow.set(argv.empty() ? L"" : argv[0]);
}
/** Returns argv */
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; }
/** Returns argv[idx] */
const wchar_t *argv(size_t idx) const {
const wchar_t * const *argv = argv_array.get();
assert(argv != NULL);
return argv[idx];
}
/** Returns argv[0], or NULL */
const wchar_t *argv0(void) const {
const wchar_t * const *argv = argv_array.get();
return argv ? argv[0] : NULL;
}
/** Returns argv[0] as a char * */
const char *argv0_cstr(void) const {
return argv0_narrow.get();
}
/** actual command to pass to exec in case of EXTERNAL or INTERNAL_EXEC. */
wcstring actual_cmd;
/** actual command to pass to exec in case of EXTERNAL or INTERNAL_EXEC. */
wcstring actual_cmd;
/** process ID */
pid_t pid;
/** process ID */
pid_t pid;
/** File descriptor that pipe output should bind to */
int pipe_write_fd;
/** File descriptor that pipe output should bind to */
int pipe_write_fd;
/** File descriptor that the _next_ process pipe input should bind to */
int pipe_read_fd;
/** File descriptor that the _next_ process pipe input should bind to */
int pipe_read_fd;
/** true if process has completed */
volatile int completed;
/** true if process has completed */
volatile int completed;
/** true if process has stopped */
volatile int stopped;
/** true if process has stopped */
volatile int stopped;
/** reported status value */
volatile int status;
/** reported status value */
volatile int status;
/** Special flag to tell the evaluation function for count to print the help information */
int count_help_magic;
/** Special flag to tell the evaluation function for count to print the help information */
int count_help_magic;
/** Next process in pipeline. We own this and we are responsible for deleting it. */
process_t *next;
/** Next process in pipeline. We own this and we are responsible for deleting it. */
process_t *next;
#ifdef HAVE__PROC_SELF_STAT
/** Last time of cpu time check */
struct timeval last_time;
/** Number of jiffies spent in process at last cpu time check */
unsigned long last_jiffies;
/** Last time of cpu time check */
struct timeval last_time;
/** Number of jiffies spent in process at last cpu time check */
unsigned long last_jiffies;
#endif
};
/* Constants for the flag variable in the job struct */
/* Constants for the flag variable in the job struct */
enum {
/** true if user was told about stopped job */
JOB_NOTIFIED = 1 << 0,
/** Whether this job is in the foreground */
JOB_FOREGROUND = 1 << 1,
/**
Whether the specified job is completely constructed,
i.e. completely parsed, and every process in the job has been
forked, etc.
Whether the specified job is completely constructed,
i.e. completely parsed, and every process in the job has been
forked, etc.
*/
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 */
JOB_SKIP_NOTIFICATION = 1 << 3,
/** Should the exit status be negated? This flag can only be set by the not builtin. */
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. */
JOB_ELSEIF = 1 << 5,
/** 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,
/** Skip executing this job. This flag is set by the short-circut builtins, i.e. and and or */
JOB_SKIP = 1 << 7,
/** Whether the job is under job control */
JOB_CONTROL = 1 << 8,
@ -251,7 +251,7 @@ enum {
JOB_TERMINAL = 1 << 9
};
/**
/**
A struct represeting a job. A job is basically a pipeline of one
or more processes and a couple of flags.
*/
@ -261,105 +261,105 @@ void release_job_id(job_id_t jobid);
class job_t
{
/**
The original command which led to the creation of this
job. It is used for displaying messages about job status
on the terminal.
*/
wcstring command_str;
/**
The original command which led to the creation of this
job. It is used for displaying messages about job status
on the terminal.
*/
wcstring command_str;
/* narrow copy so we don't have to convert after fork */
narrow_string_rep_t command_narrow;
/* No copying */
job_t(const job_t &rhs);
void operator=(const job_t &);
public:
job_t(job_id_t jobid);
~job_t();
/** Returns whether the command is empty. */
bool command_is_empty() const { return command_str.empty(); }
/** Returns the command as a wchar_t *. */
const wchar_t *command_wcstr() const { return command_str.c_str(); }
/** Returns the command */
const wcstring &command() const { return command_str; }
/** Returns the command as a char *. */
const char *command_cstr() const { return command_narrow.get(); }
/** Sets the command */
void set_command(const wcstring &cmd) {
command_str = 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.
*/
unsigned int flags;
/**
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.
*/
unsigned int flags;
};
/**
Whether we are running a subshell command
/**
Whether we are running a subshell command
*/
extern int is_subshell;
/**
Whether we are running a block of commands
/**
Whether we are running a block of commands
*/
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);
/**
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;
/**
Whether we are a login shell
/**
Whether we are a login shell
*/
extern int is_login;
/**
Whether we are running an event handler
/**
Whether we are running an event handler
*/
extern int is_event;
@ -375,9 +375,9 @@ class job_iterator_t {
job_list_t * const job_list;
job_list_t::iterator current, end;
public:
void reset(void);
job_t *next() {
job_t *job = NULL;
if (current != end) {
@ -386,7 +386,7 @@ class job_iterator_t {
}
return job;
}
job_iterator_t(job_list_t &jobs);
job_iterator_t();
};
@ -471,7 +471,7 @@ job_t *job_get(job_id_t id);
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 );

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
to the parser. If stdin is a keyboard, it supplies a killring,
history, syntax highlighting, tab-completion and various other
features.
to the parser. If stdin is a keyboard, it supplies a killring,
history, syntax highlighting, tab-completion and various other
features.
*/
#ifndef FISH_READER_H
@ -53,7 +53,7 @@ const wchar_t *reader_current_filename();
/**
Push a new filename on the stack of read files
\param fn The fileanme to push
*/
void reader_push_current_filename( const wchar_t *fn );
@ -125,7 +125,7 @@ int reader_interrupted();
const wchar_t *reader_readline();
/**
Push a new reader environment.
Push a new reader environment.
*/
void reader_push( const wchar_t *name );
@ -135,7 +135,7 @@ void reader_push( const wchar_t *name );
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
- 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:
- 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 cursor position
@ -178,7 +178,7 @@ void reader_set_left_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();

View file

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

View file

@ -1,5 +1,5 @@
/** \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
@ -19,7 +19,7 @@ int sanity_check();
/**
Try and determine if ptr is a valid pointer. If not, loose sanity.
\param ptr The pointer to validate
\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

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
The screen library allows the interactive reader to write its
output to screen efficiently by keeping an inetrnal representation
of the current screen contents and trying to find a reasonably
efficient way for transforming that to the desired screen content.
The screen library allows the interactive reader to write its
output to screen efficiently by keeping an inetrnal representation
of the current screen contents and trying to find a reasonably
efficient way for transforming that to the desired screen content.
The current implementation is less smart than ncurses allows
and can not for example move blocks of text around to handle text
insertion.
The current implementation is less smart than ncurses allows
and can not for example move blocks of text around to handle text
insertion.
*/
#ifndef FISH_SCREEN_H
#define FISH_SCREEN_H
@ -22,38 +22,38 @@ struct line_t
std::vector<wchar_t> text;
std::vector<int> colors;
bool is_soft_wrapped;
line_t() : text(), colors(), is_soft_wrapped(false)
{
}
void clear(void)
{
text.clear();
colors.clear();
}
void append(wchar_t txt, int color)
{
text.push_back(txt);
colors.push_back(color);
}
size_t size(void) const
{
return text.size();
}
wchar_t char_at(size_t idx) const
{
return text.at(idx);
}
int color_at(size_t idx) const
{
return colors.at(idx);
}
};
/**
@ -62,36 +62,36 @@ struct line_t
class screen_data_t
{
std::vector<line_t> line_datas;
public:
struct cursor_t {
int x;
int y;
cursor_t() : x(0), y(0) { }
cursor_t(int a, int b) : x(a), y(b) { }
} cursor;
line_t &add_line(void) {
line_datas.resize(line_datas.size() + 1);
return line_datas.back();
}
void resize(size_t size) {
line_datas.resize(size);
}
line_t &create_line(size_t idx) {
if (idx >= line_datas.size()) {
line_datas.resize(idx + 1);
}
return line_datas.at(idx);
}
line_t &line(size_t idx) {
return line_datas.at(idx);
}
size_t line_count(void) {
return line_datas.size();
}
@ -121,7 +121,7 @@ class screen_t
the screen.
*/
wcstring actual_left_prompt;
/** Last right prompt width */
size_t last_right_prompt_width;
@ -130,26 +130,26 @@ class screen_t
write.
*/
int actual_width;
/** If we support soft wrapping, we can output to this location without any cursor motion. */
screen_data_t::cursor_t soft_wrap_location;
/**
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
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.
*/
bool need_clear;
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
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.
*/
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. */
size_t actual_lines_before_reset;
/**
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.
*/
struct stat prev_buff_1, prev_buff_2, post_buff_1, post_buff_2;
/**
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.
*/
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
order to render the desired output using as few terminal commands
as possible.
\param s the screen on which to write
\param left_prompt the prompt to prepend to the command line
\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 cursor_pos where the cursor is
*/
void s_write( screen_t *s,
const wchar_t *left_prompt,
const wchar_t *right_prompt,
const wchar_t *commandline,
size_t explicit_len,
const int *colors,
const int *indent,
size_t cursor_pos );
void s_write( screen_t *s,
const wchar_t *left_prompt,
const wchar_t *right_prompt,
const wchar_t *commandline,
size_t explicit_len,
const int *colors,
const int *indent,
size_t cursor_pos );
void s_write( screen_t *s,
@ -187,7 +187,7 @@ void s_write( screen_t *s,
const int *indent,
size_t cursor_pos );
/**
/**
This function resets the screen buffers internal knowledge about
the contents of the screen. Use this function when some other
function than s_write has written to the screen.

View file

@ -70,53 +70,53 @@
const char *col[]=
{
"black",
"red",
"green",
"brown",
"yellow",
"blue",
"magenta",
"purple",
"cyan",
"white",
"normal"
"black",
"red",
"green",
"brown",
"yellow",
"blue",
"magenta",
"purple",
"cyan",
"white",
"normal"
};
const int col_idx[]=
{
0,
1,
2,
3,
3,
4,
5,
5,
6,
7,
8
0,
1,
2,
3,
3,
4,
5,
5,
6,
7,
8
};
void print_colors()
{
size_t i;
for( i=0; i<COLORS; i++ )
{
printf( "%s\n", col[i] );
}
size_t i;
for( i=0; i<COLORS; i++ )
{
printf( "%s\n", col[i] );
}
}
static void check_locale_init()
{
static int is_init = 0;
if( is_init )
return;
is_init = 1;
setlocale( LC_ALL, "" );
bindtextdomain( PACKAGE_NAME, LOCALEDIR );
textdomain( PACKAGE_NAME );
static int is_init = 0;
if( is_init )
return;
is_init = 1;
setlocale( LC_ALL, "" );
bindtextdomain( PACKAGE_NAME, LOCALEDIR );
textdomain( PACKAGE_NAME );
}
/* 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. */
if (argc <= 1)
return EXIT_FAILURE;
char *bgcolor=0;
char *fgcolor=0;
bool bold=false;
bool underline=false;
while( 1 )
{
static struct option
long_options[] =
{
{
"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 );
char *bgcolor=0;
char *fgcolor=0;
bool bold=false;
bool underline=false;
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 );
while( 1 )
{
static struct option
long_options[] =
{
{
"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 )
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 */
char *fish_term256 = getenv("fish_term256");
if (fish_term256) {
@ -299,77 +299,77 @@ int main( int argc, char **argv )
support_term256 = term && strstr(term, "256color");
}
if( !fgcolor && !bgcolor && !bold && !underline )
{
check_locale_init();
fprintf( stderr, _("%s: Expected an argument\n"), SET_COLOR );
print_help( argv[0], 2 );
return 1;
}
if( !fgcolor && !bgcolor && !bold && !underline )
{
check_locale_init();
fprintf( stderr, _("%s: Expected an argument\n"), SET_COLOR );
print_help( argv[0], 2 );
return 1;
}
rgb_color_t fg = rgb_color_t(fgcolor ? fgcolor : "");
if( fgcolor && fg.is_none())
{
check_locale_init();
fprintf( stderr, _("%s: Unknown color '%s'\n"), SET_COLOR, fgcolor );
return 1;
}
if( fgcolor && fg.is_none())
{
check_locale_init();
fprintf( stderr, _("%s: Unknown color '%s'\n"), SET_COLOR, fgcolor );
return 1;
}
rgb_color_t bg = rgb_color_t(bgcolor ? bgcolor : "");
if( bgcolor && bg.is_none())
{
check_locale_init();
fprintf( stderr, _("%s: Unknown color '%s'\n"), SET_COLOR, bgcolor );
return 1;
}
if( bgcolor && bg.is_none())
{
check_locale_init();
fprintf( stderr, _("%s: Unknown color '%s'\n"), SET_COLOR, bgcolor );
return 1;
}
setupterm( 0, STDOUT_FILENO, 0);
setupterm( 0, STDOUT_FILENO, 0);
if( bold )
{
if( enter_bold_mode )
putp( enter_bold_mode );
}
if( bold )
{
if( enter_bold_mode )
putp( enter_bold_mode );
}
if( underline )
{
if( enter_underline_mode )
putp( enter_underline_mode );
}
if( underline )
{
if( enter_underline_mode )
putp( enter_underline_mode );
}
if( bgcolor )
{
if( bg.is_normal() )
{
if( bgcolor )
{
if( bg.is_normal() )
{
write_background_color(0);
putp( tparm(exit_attribute_mode) );
}
}
putp( tparm(exit_attribute_mode) );
}
}
if( fgcolor )
{
if( fg.is_normal() )
{
if( fgcolor )
{
if( fg.is_normal() )
{
write_foreground_color(0);
putp( tparm(exit_attribute_mode) );
}
else
{
putp( tparm(exit_attribute_mode) );
}
else
{
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 )
{
fprintf( stderr, "%s", _("Error while closing terminfo") );
}
if( bgcolor )
{
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 -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 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 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 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 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 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'

View file

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

View file

@ -1,4 +1,4 @@
complete -c dvipdfm -x -a "
(
__fish_complete_suffix .dvi
)"
complete -c dvipdfm -x -a "
(
__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 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 executable --description "File is executable"
complete -c find -o false --description "Always false"
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 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 gid --description "Numeric group id of file" -r
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 $name -d $name
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
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 $name -d $name
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
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 pv -d 'preview document'
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 quiet -d 'silence progress messages from called programs'
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 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 L -d 'Provides a textual location of the destination' -x
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 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=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 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 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 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"

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 c -l color -d 'Colorize output'
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 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 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 v -l verbose -d 'enable verbose output'
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 "(
__fish_complete_suffix .ps
)"
complete -c ps2pdf -x -a "(
__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 -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 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 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 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)"

View file

@ -1,5 +1,5 @@
## Listing options
complete -c tree -s a -d 'All files are listed'
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 -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 N -d 'Print non-printable characters as is'
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'
## Sorting options
## Sorting options
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 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 U -d 'Leave files unsorted'
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 A -d 'Print ANSI lines graphic 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 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 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 -l nolinks -d 'Turn off hyperlinks in HTML output'
## Miscellaneous options
complete -c tree -l version -d 'Print version 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 -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 '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 -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 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 q1 -d 'Use RandR version 1.1 protocol'
complete -c xrandr -l q12 -d 'Use RandR version 1.2 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'
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
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 fb -d 'Set 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 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)'
@ -53,7 +53,7 @@ end
if expr $ver '>' 1.2
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 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 scale -d 'Set scren scale' -x
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 retain -d 'avoid server reset (avoid using this)'
complete -c xrdb -o quiet -d 'don\'t warn about duplicates'
#complete -c xrdb -s Dname[=value],
#complete -c xrdb -s Uname,
#complete -c xrdb -s Dname[=value],
#complete -c xrdb -s Uname,
#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