aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--Makefile.in3
-rw-r--r--builtin.c147
-rw-r--r--env.c181
-rw-r--r--event.c428
-rw-r--r--event.h107
-rw-r--r--exec.c3
-rw-r--r--fish_tests.c2
-rw-r--r--function.c29
-rw-r--r--function.h1
-rw-r--r--highlight.c6
-rw-r--r--input.c1
-rw-r--r--main.c6
-rw-r--r--parser.c6
-rw-r--r--parser.h11
-rw-r--r--proc.c152
-rw-r--r--proc.h2
-rw-r--r--reader.c154
-rw-r--r--reader.h4
-rw-r--r--tokenizer.c4
-rw-r--r--util.c10
-rw-r--r--util.h2
-rw-r--r--wildcard.h1
-rw-r--r--wutil.h1
23 files changed, 839 insertions, 422 deletions
diff --git a/Makefile.in b/Makefile.in
index e763cbb1..e3c242cd 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -51,7 +51,8 @@ docdir = @docdir@
COMMON_OBJS := function.o builtin.o common.o complete.o env.o exec.o \
expand.o highlight.o history.o kill.o parser.o proc.o reader.o \
sanity.o tokenizer.o util.o wildcard.o wgetopt.o wutil.o input.o \
- output.o intern.o env_universal.o env_universal_common.o input_common.o
+ output.o intern.o env_universal.o env_universal_common.o \
+ input_common.o event.o signal.o
# builtin_help.h exists, but builtin_help.c is autogenerated
COMMON_OBJS_WITH_HEADER := builtin_help.o
diff --git a/builtin.c b/builtin.c
index 3eb679c9..152bb670 100644
--- a/builtin.c
+++ b/builtin.c
@@ -57,6 +57,7 @@
#include "input_common.h"
#include "input.h"
#include "intern.h"
+#include "event.h"
/**
The default prompt for the read command
@@ -726,7 +727,8 @@ static int builtin_function( wchar_t **argv )
int res=0;
wchar_t *desc=0;
int is_binding=0;
-
+ array_list_t *events = al_new();
+
woptind=0;
const static struct woption
@@ -740,19 +742,31 @@ static int builtin_function( wchar_t **argv )
L"key-binding", no_argument, 0, 'b'
}
,
+ {
+ L"on-signal", required_argument, 0, 's'
+ }
+ ,
+ {
+ L"on-exit", required_argument, 0, 'x'
+ }
+ ,
+ {
+ L"on-variable", required_argument, 0, 'v'
+ }
+ ,
{
0, 0, 0, 0
}
}
;
- while( 1 )
+ while( 1 && (!res ) )
{
int opt_index = 0;
int opt = wgetopt_long( argc,
argv,
- L"d:b",
+ L"bd:s:x:v:",
long_options,
&opt_index );
if( opt == -1 )
@@ -772,7 +786,9 @@ static int builtin_function( wchar_t **argv )
(void *)0);
builtin_print_help( argv[0], sb_err );
- return 1;
+ res = 1;
+ break;
+
case 'd':
desc=woptarg;
@@ -782,46 +798,84 @@ static int builtin_function( wchar_t **argv )
is_binding=1;
break;
-
+ case 's':
+ {
+ event_t *e = malloc( sizeof(event_t));
+ if( !e )
+ die_mem();
+ e->type = EVENT_SIGNAL;
+ e->signal = wcs2sig( woptarg );
+ e->function_name=0;
+ al_push( events, e );
+ break;
+ }
+
+ case 'v':
+ {
+ event_t *e = malloc( sizeof(event_t));
+ if( !e )
+ die_mem();
+ e->type = EVENT_VARIABLE;
+ e->variable = wcsdup( woptarg );
+ e->function_name=0;
+ al_push( events, e );
+ break;
+ }
+
+ case 'x':
+ {
+ event_t *e = malloc( sizeof(event_t));
+ if( !e )
+ die_mem();
+ e->type = EVENT_EXIT;
+ e->pid = wcstol( woptarg, 0, 10 );
+ e->function_name=0;
+ al_push( events, e );
+ break;
+ }
+
case '?':
builtin_print_help( argv[0], sb_err );
-
- return 1;
+ res = 1;
+ break;
}
}
-
- if( argc-woptind != 1 )
- {
- sb_printf( sb_err,
- L"%ls: Expected one argument, got %d\n",
- argv[0],
- argc-woptind );
- res=1;
+
+ if( !res )
+ {
+ if( argc-woptind != 1 )
+ {
+ sb_printf( sb_err,
+ L"%ls: Expected one argument, got %d\n",
+ argv[0],
+ argc-woptind );
+ res=1;
+ }
+ else if( !(is_binding?wcsbindingname( argv[woptind] ) : wcsvarname( argv[woptind] ) ))
+ {
+ sb_append2( sb_err,
+ argv[0],
+ L": illegal function name \'",
+ argv[woptind],
+ L"\'\n",
+ (void *)0 );
+
+ res=1;
+ }
+ else if( parser_is_reserved(argv[woptind] ) )
+ {
+
+ sb_append2( sb_err,
+ argv[0],
+ L": the name \'",
+ argv[woptind],
+ L"\' is reserved,\nand can not be used as a function name\n",
+ (void *)0 );
+ res=1;
+ }
}
- else if( !(is_binding?wcsbindingname( argv[woptind] ) : wcsvarname( argv[woptind] ) ))
- {
- sb_append2( sb_err,
- argv[0],
- L": illegal function name \'",
- argv[woptind],
- L"\'\n",
- (void *)0 );
-
- res=1;
- }
- else if( parser_is_reserved(argv[woptind] ) )
- {
-
- sb_append2( sb_err,
- argv[0],
- L": the name \'",
- argv[woptind],
- L"\' is reserved,\nand can not be used as a function name\n",
- (void *)0 );
- res=1;
- }
if( res )
{
@@ -859,13 +913,26 @@ static int builtin_function( wchar_t **argv )
sb_append( sb_err, L"\n" );
parser_push_block( FAKE );
+
+ al_foreach( events, (void (*)(const void *))&event_free );
+ al_destroy( events );
+
}
else
{
+ int i;
+
parser_push_block( FUNCTION_DEF );
current_block->function_name=wcsdup(argv[woptind]);
current_block->function_description=desc?wcsdup(desc):0;
current_block->function_is_binding = is_binding;
+ current_block->function_events = events;
+ for( i=0; i<al_get_count( events ); i++ )
+ {
+ event_t *e = (event_t *)al_get( events, i );
+ e->function_name = wcsdup( current_block->function_name );
+ }
+
}
current_block->tok_pos = parser_get_pos();
@@ -2388,6 +2455,8 @@ static int builtin_end( wchar_t **argv )
case FUNCTION_DEF:
{
+ int i;
+
/**
Copy the text from the beginning of the function
until the end command and use as the new definition
@@ -2402,8 +2471,10 @@ static int builtin_end( wchar_t **argv )
function_add( current_block->function_name,
def,
current_block->function_description,
+ current_block->function_events,
current_block->function_is_binding );
- }
+ }
+
free(def);
}
break;
diff --git a/env.c b/env.c
index 10d232a4..e5a38dd3 100644
--- a/env.c
+++ b/env.c
@@ -40,6 +40,7 @@
#include "parser.h"
#include "env_universal.h"
#include "input_common.h"
+#include "event.h"
/**
Command used to start fishd
@@ -80,7 +81,7 @@ typedef struct env_node
*/
struct env_node *next;
}
-env_node_t;
+ env_node_t;
/**
A variable entry. Stores the value of a variable and whether it
@@ -92,7 +93,7 @@ typedef struct var_entry
int export; /**< Whether the variable should be exported */
wchar_t val[0]; /**< The value of the variable */
}
-var_entry_t;
+ var_entry_t;
/**
Top node on the function stack
@@ -328,7 +329,11 @@ void env_set( const wchar_t *key,
env_node_t *node;
int has_changed_old = has_changed;
int has_changed_new = 0;
- var_entry_t *e=0;
+ var_entry_t *e=0;
+ int done=0;
+
+ event_t ev;
+ array_list_t ev_list;
if( (var_mode & ENV_USER ) &&
hash_get( &env_read_only, key ) )
@@ -363,112 +368,124 @@ void env_set( const wchar_t *key,
env_universal_set( key, val, export );
- return;
}
-
- if( val == 0 )
+ else
{
- wchar_t *prev_val;
- free_val = 1;
- prev_val = env_get( key );
- val = wcsdup( prev_val?prev_val:L"" );
- }
+
+ if( val == 0 )
+ {
+ wchar_t *prev_val;
+ free_val = 1;
+ prev_val = env_get( key );
+ val = wcsdup( prev_val?prev_val:L"" );
+ }
- node = env_get_node( key );
- if( node && &node->env != 0 )
- {
- e = (var_entry_t *) hash_get( &node->env,
- key );
+ node = env_get_node( key );
+ if( node && &node->env != 0 )
+ {
+ e = (var_entry_t *) hash_get( &node->env,
+ key );
- if( e->export )
- has_changed_new = 1;
+ if( e->export )
+ has_changed_new = 1;
- }
+ }
- if( (var_mode & ENV_LOCAL) ||
- (var_mode & ENV_GLOBAL) )
- {
- node = ( var_mode & ENV_GLOBAL )?global_env:top;
- }
- else
- {
- if( node )
+ if( (var_mode & ENV_LOCAL) ||
+ (var_mode & ENV_GLOBAL) )
{
- if( !(var_mode & ENV_EXPORT ) &&
- !(var_mode & ENV_UNEXPORT ) )
- {
- var_mode = e->export?ENV_EXPORT:0;
- }
+ node = ( var_mode & ENV_GLOBAL )?global_env:top;
}
else
{
- if( !proc_had_barrier)
- env_universal_barrier();
-
- if( env_universal_get( key ) )
+ if( node )
{
- int export = 0;
-
if( !(var_mode & ENV_EXPORT ) &&
!(var_mode & ENV_UNEXPORT ) )
- {
- env_universal_get_export( key );
+ {
+ var_mode = e->export?ENV_EXPORT:0;
}
- else
- export = (var_mode & ENV_EXPORT );
-
- env_universal_set( key, val, export );
-
- return;
}
else
{
- /*
- New variable with unspecified scope. The default scope is the innermost scope that is shadowing
- */
- node = top;
- while( node->next && !node->new_scope )
- node = node->next;
+ if( !proc_had_barrier)
+ env_universal_barrier();
+
+ if( env_universal_get( key ) )
+ {
+ int export = 0;
+
+ if( !(var_mode & ENV_EXPORT ) &&
+ !(var_mode & ENV_UNEXPORT ) )
+ {
+ env_universal_get_export( key );
+ }
+ else
+ export = (var_mode & ENV_EXPORT );
+
+ env_universal_set( key, val, export );
+ done = 1;
+
+ }
+ else
+ {
+ /*
+ New variable with unspecified scope. The default scope is the innermost scope that is shadowing
+ */
+ node = top;
+ while( node->next && !node->new_scope )
+ node = node->next;
+
+ }
}
}
- }
-// env_remove( key, 0 );
- void *k, *v;
- hash_remove( &node->env, key, (const void **)&k, (const void **)&v );
- free( k );
- free( v );
+ if( !done )
+ {
+ void *k, *v;
+ hash_remove( &node->env, key, (const void **)&k, (const void **)&v );
+ free( k );
+ free( v );
- entry = malloc( sizeof( var_entry_t ) +
- sizeof(wchar_t )*(wcslen(val)+1));
+ entry = malloc( sizeof( var_entry_t ) +
+ sizeof(wchar_t )*(wcslen(val)+1));
- if( var_mode & ENV_EXPORT)
- {
- entry->export = 1;
- has_changed_new = 1;
- }
- else
- entry->export = 0;
+ if( var_mode & ENV_EXPORT)
+ {
+ entry->export = 1;
+ has_changed_new = 1;
+ }
+ else
+ entry->export = 0;
- wcscpy( entry->val, val );
+ wcscpy( entry->val, val );
- hash_put( &node->env, wcsdup(key), entry );
+ hash_put( &node->env, wcsdup(key), entry );
- if( entry->export )
- {
- node->export=1;
- }
-
- if( free_val )
- free((void *)val);
+ if( entry->export )
+ {
+ node->export=1;
+ }
+ if( free_val )
+ free((void *)val);
- has_changed = has_changed_old || has_changed_new;
+ has_changed = has_changed_old || has_changed_new;
+ }
+
+ }
-/* if( has_changed_new && !has_changed_old )
- fwprintf( stderr, L"Reexport after setting %ls to %ls, %d %d %d\n", key, val, has_changed_old, has_changed_new, has_changed );
-*/
+ ev.type=EVENT_VARIABLE;
+ ev.variable = key;
+ ev.function_name = 0;
+
+ al_init( &ev_list );
+ al_push( &ev_list, L"VARIABLE" );
+ al_push( &ev_list, key );
+
+ event_fire( &ev, &ev_list );
+ al_destroy( &ev_list );
}
/**
@@ -560,7 +577,7 @@ wchar_t *env_get( const wchar_t *key )
while( env != 0 )
{
res = (var_entry_t *) hash_get( &env->env,
- key );
+ key );
if( res != 0 )
{
if( wcscmp( res->val, ENV_NULL )==0)
@@ -602,7 +619,7 @@ int env_exist( const wchar_t *key )
while( env != 0 )
{
res = (var_entry_t *) hash_get( &env->env,
- key );
+ key );
if( res != 0 )
{
return 1;
diff --git a/event.c b/event.c
new file mode 100644
index 00000000..acc8d873
--- /dev/null
+++ b/event.c
@@ -0,0 +1,428 @@
+/** \file function.c
+
+ Functions for storing and retrieving function information.
+
+*/
+#include <stdlib.h>
+#include <stdio.h>
+#include <wchar.h>
+#include <unistd.h>
+#include <termios.h>
+#include <signal.h>
+#include <string.h>
+
+#include "config.h"
+#include "util.h"
+#include "function.h"
+#include "proc.h"
+#include "parser.h"
+#include "common.h"
+#include "intern.h"
+#include "event.h"
+#include "signal.h"
+
+/**
+ Number of signals that can be queued before an overflow occurs
+*/
+#define SIG_UNHANDLED_MAX 64
+
+/**
+ This struct contains a list of generated signals waiting to be
+ dispatched
+*/
+typedef struct
+{
+ int count;
+ int overflow;
+ int signal[SIG_UNHANDLED_MAX];
+}
+ signal_list_t;
+
+/*
+ The signal event list. Actually two separate lists. One which is
+ active, which is the one that new events is written to. The inactive
+ one contains the events that are currently beeing performed.
+*/
+static signal_list_t sig_list[2];
+
+/**
+ The index of sig_list that is the list of signals currently written to
+*/
+static int active_list=0;
+
+/**
+ List of event handlers
+*/
+static array_list_t *events;
+/**
+ List of event handlers that should be removed
+*/
+static array_list_t *killme;
+
+/**
+ Tests if one event instance matches the definition of a event
+ class. If the class defines a function name, that will also be a
+ match criterion.
+
+*/
+static int event_match( event_t *class, event_t *instance )
+{
+ if( class->function_name && instance->function_name )
+ {
+ if( wcscmp( class->function_name, instance->function_name ) != 0 )
+ return 0;
+ }
+
+ if( class->type == EVENT_ANY )
+ return 1;
+
+ if( class->type != instance->type )
+ return 0;
+
+
+ switch( class->type )
+ {
+
+ case EVENT_SIGNAL:
+ if( class->signal == EVENT_ANY_SIGNAL )
+ return 1;
+ return class->signal == instance->signal;
+
+ case EVENT_VARIABLE:
+ return wcscmp( instance->variable, class->variable )==0;
+
+ case EVENT_EXIT:
+ if( class->pid == EVENT_ANY_PID )
+ return 1;
+ return class->pid == instance->pid;
+ }
+
+ /**
+ This should never be reached
+ */
+ return 0;
+}
+
+
+/**
+ Create an identical copy of an event. Use deep copying, i.e. make
+ duplicates of any strings used as well.
+*/
+static event_t *event_copy( event_t *event )
+{
+ event_t *e = malloc( sizeof( event_t ) );
+ if( !e )
+ die_mem();
+ memcpy( e, event, sizeof(event_t));
+
+ if( e->function_name )
+ e->function_name = wcsdup( e->function_name );
+
+ if( e->type == EVENT_VARIABLE )
+ e->variable = wcsdup( e->variable );
+
+ return e;
+}
+
+void event_add_handler( event_t *event )
+{
+ event_t *e = event_copy( event );
+
+ if( !events )
+ events = al_new();
+
+ debug( 1, L"add event of type %d", e->type );
+
+ al_push( events, e );
+}
+
+void event_remove( event_t *criterion )
+{
+ int i;
+ array_list_t *new_list = al_new();
+
+ /*
+ Because of concurrency issues, env_remove does not actually free
+ anything - instead it simply moves all events that should be
+ removed to the killme list.
+ */
+
+ if( !events )
+ return;
+
+ for( i=0; i<al_get_count( events); i++ )
+ {
+ event_t *n = (event_t *)al_get( events, i );
+ if( event_match( criterion, n ) )
+ {
+ if( !killme )
+ killme = al_new();
+
+ al_push( killme, n );
+ }
+ else
+ {
+ al_push( new_list, n );
+ }
+ }
+ al_destroy( events );
+ events = new_list;
+}
+
+void event_get( event_t *criterion, array_list_t *out )
+{
+ int i;
+
+ if( !events )
+ return;
+
+ for( i=0; i<al_get_count( events); i++ )
+ {
+ event_t *n = (event_t *)al_get( events, i );
+ if( event_match(criterion, n ) )
+ al_push( out, n );
+
+ }
+}
+
+/**
+ Free all events in the kill list
+*/
+static void event_free_kills()
+{
+ int i;
+ if( !killme )
+ return;
+
+ for( i=0; i<al_get_count( killme ); i++ )
+ {
+ event_t *roadkill = (event_t *)al_get( events, i );
+ event_free( roadkill );
+ }
+ al_truncate( killme, 0 );
+}
+
+/**
+ Test if the specified event is waiting to be killed
+*/
+static int event_is_killed( event_t *e )
+{
+ int i;
+ if( !killme )
+ return 0;
+
+ for( i=0; i<al_get_count( killme ); i++ )
+ {
+ event_t *roadkill = (event_t *)al_get( events, i );
+ if( roadkill ==e )
+ return 1;
+
+ }
+ return 0;
+}
+
+/**
+ Perform the specified event. Since almost all event firings will
+ not match a single event handler, we make sureto optimize the 'no
+ matches' path. This means that nothing is allocated/initialized
+ unless that is needed.
+*/
+static void event_fire_internal( event_t *event, array_list_t *arguments )
+{
+ int i, j;
+ string_buffer_t *b=0;
+ array_list_t *fire=0;
+
+ if( !events )
+ 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<al_get_count( events ); i++ )
+ {
+ event_t *criterion = (event_t *)al_get( events, i );
+
+ /*
+ Check if this event is a match
+ */
+ if(event_match( criterion, event ) )
+ {
+ if( !fire )
+ fire = al_new();
+ al_push( fire, criterion );
+ }
+ }
+
+ /*
+ No matches. Time to return.
+ */
+ if( !fire )
+ return;
+
+ /*
+ Iterate over our list of matching events
+ */
+
+ for( i=0; i<al_get_count( fire ); i++ )
+ {
+ event_t *criterion = (event_t *)al_get( fire, i );
+
+ /*
+ Check if this event has been removed, if so, dont fire it
+ */
+ if( event_is_killed( criterion ) )
+ continue;
+
+ /*
+ Fire event
+ */
+ if( !b )
+ b = sb_new();
+ else
+ sb_clear( b );
+
+ sb_append( b, criterion->function_name );
+
+ for( j=0; j<al_get_count(arguments); j++ )
+ {
+ wchar_t *arg_esc = escape( wcsdup( (wchar_t *)al_get( arguments, j)), 0 );
+ sb_append( b, L" " );
+ sb_append( b, arg_esc );
+ free( arg_esc );
+ }
+
+ eval( (wchar_t *)b->buff, 0, TOP );
+ }
+
+ if( b )
+ {
+ sb_destroy( b );
+ free( b );
+ }
+
+ if( fire )
+ {
+ al_destroy( fire );
+ free( fire );
+ }
+
+ /*
+ Free killed events
+ */
+ event_free_kills();
+
+}
+
+/**
+ Perform all pending signal events
+*/
+static void event_fire_signal_events()
+{
+ while( sig_list[active_list].count > 0 )
+ {
+ int i;
+ signal_list_t *lst;
+ event_t e;
+ array_list_t a;
+ al_init( &a );
+
+ sig_list[1-active_list].count=0;
+ sig_list[1-active_list].overflow=0;
+ active_list=1-active_list;
+
+ e.type=EVENT_SIGNAL;
+ e.function_name=0;
+
+ lst = &sig_list[1-active_list];
+
+ if( lst->overflow )
+ {
+ debug( 0, L"Signal overflow. Signals have been ignored" );
+ }
+
+ for( i=0; i<lst->count; i++ )
+ {
+ e.signal = lst->signal[i];
+ al_set( &a, 0, sig2wcs( e.signal ) );
+ event_fire_internal( &e, &a );
+ }
+
+ al_destroy( &a );
+
+ }
+}
+
+
+void event_fire( event_t *event, array_list_t *arguments )
+{
+
+ int is_event_old = is_event;
+ is_event=1;
+
+ if( event && (event->type == EVENT_SIGNAL) )
+ {
+ /*
+ This means we are in a signal handler. We must be very
+ careful not do do anything that could cause a memory
+ allocation or something else that might be illegal in a
+ signal handler.
+ */
+ if( sig_list[active_list].count < SIG_UNHANDLED_MAX )
+ sig_list[active_list].signal[sig_list[active_list].count++]=event->signal;
+ else
+ sig_list[active_list].overflow=1;
+
+ return;
+ }
+ else
+ {
+ event_fire_signal_events();
+
+ if( event )
+ event_fire_internal( event, arguments );
+
+ }
+ is_event = is_event_old;
+
+}
+
+
+void event_init()
+{
+ sig_list[active_list].count=0;
+}
+
+void event_destroy()
+{
+ if( events )
+ {
+ al_foreach( events, (void (*)(const void *))&event_free );
+ al_destroy( events );
+ events=0;
+ }
+ if( killme )
+ {
+ al_foreach( killme, (void (*)(const void *))&event_free );
+ al_destroy( killme );
+ killme=0;
+ }
+}
+
+void event_free( event_t *e )
+{
+ free( e->function_name );
+ if( e->type == EVENT_VARIABLE )
+ free( e->variable );
+ free( e );
+}
+
diff --git a/event.h b/event.h
new file mode 100644
index 00000000..7e032cdc
--- /dev/null
+++ b/event.h
@@ -0,0 +1,107 @@
+/** \file event.h
+
+Event handling library
+
+*/
+#ifndef FISH_EVENT_H
+#define FISH_EVENT_H
+
+
+/**
+ The signal number that is used to match any signal
+*/
+#define EVENT_ANY_SIGNAL -1
+
+/**
+ The process id that is used to match any process id
+*/
+#define EVENT_ANY_PID 0
+
+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 */
+}
+ ;
+
+/**
+ The structure which represents an event. The event_t struct has
+ several event-related use-cases:
+
+ - When used as a parameter to event_add, it represents a class of events, and function_name is the name of the function which will be called whenever an event matching the specified class occurs. This is also how events are stored internally.
+ - When used as a parameter to event_get, event_remove and event_fire, it represents a class of events, and if the function_name field is non-zero, only events which call the specified function will be returned.
+*/
+typedef struct
+{
+ /**
+ Type of event
+ */
+ int type;
+ union
+ {
+ /**
+ Signal number for signal-type events.Use EVENT_ANY_SIGNAL to match any signal
+ */
+ int signal;
+ /**
+ Variable name for variable-type events.
+ */
+ const wchar_t *variable;
+ /**
+ Process id for process-type events. Use EVENT_ANY_PID to match any pid.
+ */
+ pid_t pid;
+ }
+ ;
+
+ const wchar_t *function_name;
+}
+ event_t;
+
+/**
+ Add an event handler
+*/
+void event_add_handler( event_t *event );
+
+/**
+ Remove all events matching the specified criterion.
+*/
+void event_remove( event_t *event );
+
+/**
+ Return all events which match the specified event class
+
+ \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.
+*/
+void event_get( event_t *criterion, array_list_t *out );
+
+/**
+ Fire the specified event. The function_name field of the event must
+ be set to 0. If the event is of type EVENT_SIGNAL, no the event is
+ queued, and will be dispatched the next time event_fire is
+ called. If event is a null-pointer, all pending events are
+ dispatched.
+
+ \param event the specific event whose handlers should fire
+ \param arguments the argument string to send to the event handler function
+*/
+void event_fire( event_t *event, array_list_t *arguments );
+
+/**
+ Initialize the event-handling library
+*/
+void event_init();
+
+/**
+ Destroy the event-handling library
+*/
+void event_destroy();
+
+/**
+ Free all memory used by event
+*/
+void event_free( event_t *e );
+
+#endif
diff --git a/exec.c b/exec.c
index ae41c225..b3d34a73 100644
--- a/exec.c
+++ b/exec.c
@@ -649,7 +649,8 @@ static int internal_exec_helper( const wchar_t *def,
buff->out_buffer->used );
*/
io_untransmogrify( io, io_internal );
- job_do_notification();
+ if( !is_event )
+ job_do_notification();
is_block=is_block_old;
return res;
}
diff --git a/fish_tests.c b/fish_tests.c
index 7cf8066a..4a29ddef 100644
--- a/fish_tests.c
+++ b/fish_tests.c
@@ -615,6 +615,7 @@ int main( int argc, char **argv )
say( L"Testing low-level functionality");
say( L"Lines beginning with 'fish:' are not errors, they are warning messages\ngenerated by the fish parser library when given broken input, and can be\nignored. All errors begin with 'Error:'." );
+ event_init();
exec_init();
parser_init();
function_init();
@@ -644,5 +645,6 @@ int main( int argc, char **argv )
complete_destroy();
wutil_destroy();
exec_destroy();
+ event_destroy();
}
diff --git a/function.c b/function.c
index b71eab88..c57760e4 100644
--- a/function.c
+++ b/function.c
@@ -15,11 +15,8 @@
#include "parser.h"
#include "common.h"
#include "intern.h"
+#include "event.h"
-/**
- Table containing all functions
-*/
-static hash_table_t function;
/**
Struct describing a function
@@ -32,7 +29,12 @@ typedef struct
wchar_t *desc;
int is_binding;
}
-function_data_t;
+ function_data_t;
+
+/**
+ Table containing all functions
+*/
+static hash_table_t function;
/**
Free all contents of an entry to the function hash table
@@ -62,8 +64,11 @@ void function_destroy()
void function_add( const wchar_t *name,
const wchar_t *val,
const wchar_t *desc,
- int is_binding)
+ array_list_t *events,
+ int is_binding )
{
+ int i;
+
if( function_exists( name ) )
function_remove( name );
@@ -72,6 +77,12 @@ void function_add( const wchar_t *name,
d->desc = desc?wcsdup( desc ):0;
d->is_binding = is_binding;
hash_put( &function, intern(name), d );
+
+ for( i=0; i<al_get_count( events ); i++ )
+ {
+ event_add_handler( (event_t *)al_get( events, i ) );
+ }
+
}
int function_exists( const wchar_t *cmd )
@@ -84,6 +95,11 @@ void function_remove( const wchar_t *name )
void *key;
function_data_t *d;
+ event_t ev;
+ ev.type=EVENT_ANY;
+ ev.function_name=name;
+ event_remove( &ev );
+
hash_remove( &function,
name,
(const void **) &key,
@@ -146,3 +162,4 @@ void function_get_names( array_list_t *list, int get_hidden )
hash_foreach2( &function, &get_names_internal, list );
}
+
diff --git a/function.h b/function.h
index b8512696..30b1d0d9 100644
--- a/function.h
+++ b/function.h
@@ -27,6 +27,7 @@ void function_destroy();
void function_add( const wchar_t *name,
const wchar_t *val,
const wchar_t *desc,
+ array_list_t *events,
int is_binding );
/**
diff --git a/highlight.c b/highlight.c
index 8630f464..e3783eda 100644
--- a/highlight.c
+++ b/highlight.c
@@ -3,17 +3,11 @@
*/
#include <stdlib.h>
#include <stdio.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <unistd.h>
-#include <dirent.h>
-#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <errno.h>
#include <wchar.h>
#include <wctype.h>
-#include <sys/types.h>
#include <termios.h>
#include <signal.h>
diff --git a/input.c b/input.c
index 8176c783..d609b863 100644
--- a/input.c
+++ b/input.c
@@ -999,7 +999,6 @@ void input_parse_inputrc_line( wchar_t *cmd )
wchar_t *key;
wchar_t *val;
wchar_t *sequence;
- wchar_t prev=0;
key=cmd;
diff --git a/main.c b/main.c
index b54d7e0f..88f671db 100644
--- a/main.c
+++ b/main.c
@@ -31,7 +31,6 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#include <errno.h>
#include <unistd.h>
#include <termios.h>
-#include <sys/types.h>
#include <fcntl.h>
#ifdef HAVE_GETOPT_H
@@ -54,6 +53,8 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#include "parser.h"
#include "expand.h"
#include "intern.h"
+#include "exec.h"
+#include "event.h"
/**
Parse init files
@@ -206,6 +207,7 @@ int main( int argc, char **argv )
if( force_interactive )
is_interactive_session=1;
+ event_init();
exec_init();
parser_init();
builtin_init();
@@ -301,6 +303,8 @@ int main( int argc, char **argv )
wutil_destroy();
common_destroy();
exec_destroy();
+ event_destroy();
+
intern_free_all();
diff --git a/parser.c b/parser.c
index 00919822..a50dfe81 100644
--- a/parser.c
+++ b/parser.c
@@ -34,6 +34,7 @@ The fish parser. Contains functions for parsing code.
#include "reader.h"
#include "sanity.h"
#include "env_universal.h"
+#include "event.h"
/** Length of the lineinfo string used for describing the current tokenizer position */
#define LINEINFO_SIZE 128
@@ -209,6 +210,9 @@ void parser_pop_block()
{
free( current_block->function_name );
free( current_block->function_description );
+ al_foreach( current_block->function_events,
+ (void (*)(const void *))&event_free );
+ free( current_block->function_events );
break;
}
@@ -1799,7 +1803,7 @@ static void eval_job( tokenizer *tok )
}
}
- if( is_subshell || is_block )
+ if(( is_subshell || is_block ) && (!is_event))
job_do_notification();
// debug( 2, L"end eval_job()\n" );
}
diff --git a/parser.h b/parser.h
index 04b091e0..a6101eff 100644
--- a/parser.h
+++ b/parser.h
@@ -55,6 +55,17 @@ typedef struct block
int function_is_binding; /**< Whether a function is a keybinding */
};
+ /**
+ Fourth block type specific variable
+ */
+ union
+ {
+ array_list_t *function_events;
+ }
+ ;
+
+
+
/**
Next outer block
*/
diff --git a/proc.c b/proc.c
index a2245e68..a892eae2 100644
--- a/proc.c
+++ b/proc.c
@@ -46,6 +46,7 @@ Some of the code in this file is based on code from the Glibc manual.
#include "sanity.h"
#include "env.h"
#include "parser.h"
+#include "signal.h"
/**
Size of message buffer
@@ -69,6 +70,7 @@ int is_interactive_session=0;
int is_subshell=0;
int is_block=0;
int is_login=0;
+int is_event=0;
int proc_had_barrier;
pid_t proc_last_bg_pid = 0;
@@ -349,156 +351,6 @@ static int job_last_is_completed( const job_t *j )
return p->completed;
}
-/**
- Get string representation of a signal
-*/
-static wchar_t *sig2wcs( int sig )
-{
- switch( sig )
- {
- case SIGHUP:
- return L"SIGHUP";
- case SIGINT:
- return L"SIGINT";
- case SIGQUIT:
- return L"SIGQUIT";
- case SIGILL:
- return L"SIGILL";
- case SIGTRAP:
- return L"SIGTRAP";
- case SIGABRT:
- return L"SIGABRT";
- case SIGBUS:
- return L"SIGBUS";
- case SIGFPE:
- return L"SIGFPE";
- case SIGKILL:
- return L"SIGKILL";
- case SIGUSR1:
- return L"SIGUSR1";
- case SIGSEGV:
- return L"SIGSEGV";
- case SIGUSR2:
- return L"SIGUSR2";
- case SIGPIPE:
- return L"SIGPIPE";
- case SIGALRM:
- return L"SIGALRM";
- case SIGTERM:
- return L"SIGTERM";
- case SIGCHLD:
- return L"SIGCHLD";
- case SIGCONT:
- return L"SIGCONT";
- case SIGSTOP:
- return L"SIGSTOP";
- case SIGTSTP:
- return L"SIGTSTP";
- case SIGTTIN:
- return L"SIGTTIN";
- case SIGTTOU:
- return L"SIGTTOU";
- case SIGURG:
- return L"SIGURG";
- case SIGXCPU:
- return L"SIGXCPU";
- case SIGXFSZ:
- return L"SIGFXSZ";
- case SIGVTALRM:
- return L"SIGVTALRM";
- case SIGPROF:
- return L"SIGPROF";
- case SIGWINCH:
- return L"SIGWINCH";
- case SIGIO:
- return L"SIGIO";
-#ifdef SIGPWR
- case SIGPWR:
- return L"SIGPWR";
-#endif
- case SIGSYS:
- return L"SIGSYS";
- default:
- return L"Unknown";
- }
-
-}
-
-/**
- Returns a description of the specified signal.
-*/
-static wchar_t *sig_description( int sig )
-{
- switch( sig )
- {
- case SIGHUP:
- return L"Terminal hung up";
- case SIGINT:
- return L"Quit request from job control (^C)";
- case SIGQUIT:
- return L"Quit request from job control with core dump (^\\)";
- case SIGILL:
- return L"Illegal instruction";
- case SIGTRAP:
- return L"Trace or breakpoint trap";
- case SIGABRT:
- return L"Abort";
- case SIGBUS:
- return L"Misaligned address error";
- case SIGFPE:
- return L"Floating point exception";
- case SIGKILL:
- return L"Forced quit";
- case SIGUSR1:
- return L"User defined signal 1";
- case SIGUSR2:
- return L"User defined signal 2";
- case SIGSEGV:
- return L"Address boundary error";
- case SIGPIPE:
- return L"Broken pipe";
- case SIGALRM:
- return L"Timer expired";
- case SIGTERM:
- return L"Polite quit request";
- case SIGCHLD:
- return L"Child process status changed";
- case SIGCONT:
- return L"Continue previously stopped process";
- case SIGSTOP:
- return L"Forced stop";
- case SIGTSTP:
- return L"Stop request from job control (^Z)";
- case SIGTTIN:
- return L"Stop from terminal input";
- case SIGTTOU:
- return L"Stop from terminal output";
- case SIGURG:
- return L"Urgent socket condition";
- case SIGXCPU:
- return L"CPU time limit exceeded";
- case SIGXFSZ:
- return L"File size limit exceeded";
- case SIGVTALRM:
- return L"Virtual timer expired";
- case SIGPROF:
- return L"Profiling timer expired";
- case SIGWINCH:
- return L"Window size change";
- case SIGIO:
- return L"Asynchronous IO event";
-#ifdef SIGPWR
- case SIGPWR:
- return L"Power failure";
-#endif
- case SIGSYS:
- return L"Bad system call";
- default:
- return L"Unknown";
- }
-
-}
-
/**
Store the status of the process pid that was returned by waitpid.
diff --git a/proc.h b/proc.h
index c92a6d10..3bdf1db7 100644
--- a/proc.h
+++ b/proc.h
@@ -156,6 +156,8 @@ extern int is_interactive;
extern int is_interactive_session;
/** Whether we are a login shell*/
extern int is_login;
+/** Whether we are a event handler*/
+extern int is_event;
/** Linked list of all jobs */
extern job_t *first_job;
diff --git a/reader.c b/reader.c
index a8a2672e..78e14993 100644
--- a/reader.c
+++ b/reader.c
@@ -236,12 +236,8 @@ static int end_loop = 0;
*/
static struct winsize termsize;
-/**
- This flag is set when a WINCH signal was recieved.
-*/
static int new_size=0;
-
/**
The list containing names of files that are being parsed
*/
@@ -272,10 +268,7 @@ static struct termios saved_modes;
*/
static pid_t original_pid;
-/**
- Interrupted flag. Set to 1 when the user presses \^C.
-*/
-static int interupted;
+static int interupted=0;
/*
Prototypes for a bunch of functions defined later on.
@@ -284,9 +277,7 @@ static int interupted;
static void reader_save_status();
static void reader_check_status();
static void reader_super_highlight_me_plenty( wchar_t * buff, int *color, int pos, array_list_t *error );
-static void handle_winch( int sig );
-
-
+static void check_winch();
static struct termios old_modes;
@@ -333,7 +324,8 @@ static void term_steal()
break;
}
- handle_winch( 0 );
+ reader_handle_winch(0 );
+ check_winch();
if( tcsetattr(0,TCSANOW,&old_modes)) /* return to previous mode */
{
@@ -360,6 +352,19 @@ static int room_for_usec(struct stat *st)
*/
static string_buffer_t *readline_buffer=0;
+void reader_handle_int( int sig )
+{
+ block_t *c = current_block;
+ while( c )
+ {
+ c->skip=1;
+ c=c->outer;
+ }
+ interupted = 1;
+
+}
+
+
int reader_get_width()
{
return termsize.ws_col;
@@ -1410,11 +1415,9 @@ static int handle_completions( array_list_t *comp )
}
}
-/**
- Respond to a winch signal by checking the terminal size
-*/
-static void handle_winch( int sig )
+void reader_handle_winch( int signal )
{
+
if (ioctl(1,TIOCGWINSZ,&termsize)!=0)
{
return;
@@ -1422,8 +1425,7 @@ static void handle_winch( int sig )
new_size=1;
}
-
-void check_winch()
+static void check_winch()
{
if( new_size )
{
@@ -1436,22 +1438,6 @@ void check_winch()
}
}
-/**
- Interactive mode ^C handler. Respond to int signal by setting
- interrupted-flag and stopping all loops and conditionals.
-*/
-static void handle_int( int sig )
-{
- interupted=1;
-
- block_t *c = current_block;
- while( c )
- {
- c->skip=1;
- c=c->outer;
- }
-
-}
/**
Reset the terminal. This function is placed in the list of
@@ -1467,98 +1453,6 @@ static void exit_func()
}
/**
- Sets appropriate signal handlers.
-*/
-static void set_signal_handlers()
-{
- struct sigaction act;
- sigemptyset( & act.sa_mask );
- act.sa_flags=0;
- act.sa_handler=SIG_DFL;
-
- /*
- First reset everything
- */
- sigaction( SIGINT, &act, 0);
- sigaction( SIGQUIT, &act, 0);
- sigaction( SIGTSTP, &act, 0);
- sigaction( SIGTTIN, &act, 0);
- sigaction( SIGTTOU, &act, 0);
- sigaction( SIGCHLD, &act, 0);
-
- /*
- Ignore sigpipe, it is generated if fishd dies, but we can
- recover.
- */
- act.sa_handler=SIG_IGN;
- sigaction( SIGPIPE, &act, 0);
-
- if( is_interactive )
- {
-
- /*
- Interactive mode. Ignore interactive signals. We are a
- shell, we know whats best for the user. ;-)
- */
-
- act.sa_handler=SIG_IGN;
-
- sigaction( SIGINT, &act, 0);
- sigaction( SIGQUIT, &act, 0);
- sigaction( SIGTSTP, &act, 0);
- sigaction( SIGTTIN, &act, 0);
- sigaction( SIGTTOU, &act, 0);
-
- act.sa_handler = &handle_int;
- act.sa_flags = 0;
- if( sigaction( SIGINT, &act, 0) )
- {
- wperror( L"sigaction" );
- exit(1);
- }
-
- act.sa_sigaction = &job_handle_signal;
- act.sa_flags = SA_SIGINFO;
- if( sigaction( SIGCHLD, &act, 0) )
- {
- wperror( L"sigaction" );
- exit(1);
- }
-
- act.sa_flags = 0;
- act.sa_handler= &handle_winch;
- if( sigaction( SIGWINCH, &act, 0 ) )
- {
- wperror( L"sigaction" );
- exit(1);
- }
-
- }
- else
- {
- /*
- Non-interactive. Ignore interrupt, check exit status of
- processes to determine result instead.
- */
- act.sa_handler=SIG_IGN;
-
- sigaction( SIGINT, &act, 0);
- sigaction( SIGQUIT, &act, 0);
-
- act.sa_handler=SIG_DFL;
-
- act.sa_sigaction = &job_handle_signal;
- act.sa_flags = SA_SIGINFO;
- if( sigaction( SIGCHLD, &act, 0) )
- {
- wperror( L"sigaction" );
- exit(1);
- }
- }
-}
-
-
-/**
Initialize data for interactive use
*/
static void reader_interactive_init()
@@ -1603,7 +1497,7 @@ static void reader_interactive_init()
history_init();
- handle_winch( 0 ); /* Set handler for window change events */
+ reader_handle_winch(0);
check_winch();
tcgetattr(0,&shell_modes); /* get the current terminal modes */
@@ -3034,8 +2928,8 @@ int reader_read()
*/
int shell_was_interactive = is_interactive;
is_interactive = isatty(STDIN_FILENO);
- set_signal_handlers();
-
+ signal_set_handlers();
+
res= is_interactive?read_i():read_ni();
/*
@@ -3045,6 +2939,6 @@ int reader_read()
end_loop = 0;
is_interactive = shell_was_interactive;
- set_signal_handlers();
+ signal_set_handlers();
return res;
}
diff --git a/reader.h b/reader.h
index 167fdbab..ac61098d 100644
--- a/reader.h
+++ b/reader.h
@@ -191,4 +191,8 @@ void reader_current_token_extent( wchar_t **a, wchar_t **b, wchar_t **pa, wchar_
*/
void reader_replace_current_token( wchar_t *new_token );
+void reader_handle_winch( int signal );
+void reader_handle_int( int signal );
+
+
#endif
diff --git a/tokenizer.c b/tokenizer.c
index a5bb9790..f105e96e 100644
--- a/tokenizer.c
+++ b/tokenizer.c
@@ -13,10 +13,6 @@
#include <wchar.h>
#include <wctype.h>
#include <string.h>
-#include <sys/types.h>
-#include <dirent.h>
-#include <sys/types.h>
-#include <sys/stat.h>
#include <unistd.h>
#include "util.h"
diff --git a/util.c b/util.c
index fe41033c..31ccf7b4 100644
--- a/util.c
+++ b/util.c
@@ -810,6 +810,16 @@ void sb_init( string_buffer_t * b)
b->used -= sizeof(wchar_t);
}
+string_buffer_t *sb_new()
+{
+ string_buffer_t *res = malloc( sizeof( string_buffer_t ) );
+ if( !res )
+ die_mem();
+ sb_init( res );
+ return res;
+}
+
+
void sb_append( string_buffer_t *b, const wchar_t * s)
{
// fwprintf( stderr, L"Append string \'%ls\'\n", s );
diff --git a/util.h b/util.h
index 88d828bf..04c76bac 100644
--- a/util.h
+++ b/util.h
@@ -409,6 +409,8 @@ int wcsfilecmp( const wchar_t *a, const wchar_t *b );
*/
void sb_init( string_buffer_t * );
+string_buffer_t *sb_new();
+
/**
Append a string to the buffer
*/
diff --git a/wildcard.h b/wildcard.h
index c7478b0b..5db4e38c 100644
--- a/wildcard.h
+++ b/wildcard.h
@@ -64,7 +64,6 @@ int wildcard_expand( const wchar_t *wc,
\param str The string to test
\param wc The wildcard to test against
- \param wc_unescaped if wc_unescaped is true, \c wildcard_match uses the ANY_CHAR and ANY_STRING characters for globbing, otherwise, the '?' and '*' characters are used
\return true if the wildcard matched
*/
int wildcard_match( const wchar_t *str,
diff --git a/wutil.h b/wutil.h
index c0f57ef9..8ce2ac6a 100644
--- a/wutil.h
+++ b/wutil.h
@@ -11,6 +11,7 @@
#include <dirent.h>
#include <unistd.h>
#include <sys/stat.h>
+#include <sys/types.h>
/**