diff --git a/builtin.c b/builtin.c index f621611a4..394231b90 100644 --- a/builtin.c +++ b/builtin.c @@ -748,7 +748,11 @@ static int builtin_function( wchar_t **argv ) } , { - L"on-exit", required_argument, 0, 'x' + L"on-job-exit", required_argument, 0, 'j' + } + , + { + L"on-process-exit", required_argument, 0, 'p' } , { @@ -767,7 +771,7 @@ static int builtin_function( wchar_t **argv ) int opt = wgetopt_long( argc, argv, - L"bd:s:x:v:", + L"bd:s:j:p:v:", long_options, &opt_index ); if( opt == -1 ) @@ -848,7 +852,8 @@ static int builtin_function( wchar_t **argv ) break; } - case 'x': + case 'j': + case 'p': { pid_t pid; wchar_t *end; @@ -864,15 +869,13 @@ static int builtin_function( wchar_t **argv ) woptarg ); res=1; break; - } - - + } e = malloc( sizeof(event_t)); if( !e ) die_mem(); e->type = EVENT_EXIT; - e->pid = pid; + e->pid = (opt=='j'?-1:1)*abs(pid); e->function_name=0; al_push( events, e ); break; @@ -2252,7 +2255,7 @@ static int builtin_jobs( wchar_t **argv ) /* Ignore unconstructed jobs, i.e. ourself. */ - if( j->constructed ) + if( j->constructed /*&& j->skip_notification*/ ) { if( !found ) { @@ -2269,12 +2272,12 @@ static int builtin_jobs( wchar_t **argv ) found = 1; sb_printf( sb_out, L"%d\t%d\t", j->job_id, j->pgid ); - - + #ifdef HAVE__PROC_SELF_STAT sb_printf( sb_out, L"%d\t", cpu_use(j) ); #endif sb_append2( sb_out, job_is_stopped(j)?L"stopped\t":L"running\t", +// job_is_completed(j)?L"completed\t":L"unfinished\t", j->command, L"\n", (void *)0 ); } diff --git a/doc_src/function.txt b/doc_src/function.txt index 4eb555c92..085072efc 100644 --- a/doc_src/function.txt +++ b/doc_src/function.txt @@ -27,6 +27,7 @@ are inserted into the environment variable function ll ls -l $argv +end will run the \c ls command, using the \c -l option, while passing on any additional files and switches to \c ls. diff --git a/doc_src/set.txt b/doc_src/set.txt index c06e62fa5..fdcfc3502 100644 --- a/doc_src/set.txt +++ b/doc_src/set.txt @@ -6,7 +6,7 @@ The set builtin causes fish to assign the variable VARIABLE_NAME the values VALUES.... \subsection set-description Description -- -e or --erase causes the specified environment variables to be erased +- -e or --erase causes the specified environment variable to be erased - -g or --global causes the specified environment variable to be made global. If this option is not supplied, the specified variable will dissapear when the current block ends - -l or --local forces the specified environment variable to be made local to the current block, even if the variable already exists and is non-local - -n or --names List only the names of all defined variables @@ -23,7 +23,7 @@ with the given name will be changed as specified, but it's value will remain the same. If the variable did not previously exist, it's value will be an empty string. -If the \c -e or \c --erase option is specified, all the variables +If the \c -e or \c --erase option is specified, the variable specified by the following arguments will be erased If a variable is set to more than one value, the variable will be an diff --git a/env.c b/env.c index e5a38dd3f..8ffa95657 100644 --- a/env.c +++ b/env.c @@ -194,6 +194,43 @@ static void start_fishd() sb_destroy( &cmd ); } +static void universal_callback( int type, + const wchar_t *name, + const wchar_t *val ) +{ + wchar_t *str=0; + + switch( type ) + { + case SET: + case SET_EXPORT: + str=L"SET"; + break; + case ERASE: + str=L"ERASE"; + break; + } + + if( str ) + { + array_list_t arg; + event_t ev; + + has_changed=1; + + ev.type=EVENT_VARIABLE; + ev.variable=name; + ev.function_name=0; + + al_init( &arg ); + al_push( &arg, L"VARIABLE" ); + al_push( &arg, str ); + al_push( &arg, name ); + event_fire( &ev, &arg ); + al_destroy( &arg ); + } +} + void env_init() { char **p; @@ -267,7 +304,8 @@ void env_init() env_universal_init( env_get( L"FISHD_SOKET_DIR"), env_get( L"USER" ), - &start_fishd ); + &start_fishd, + &universal_callback ); } @@ -334,6 +372,7 @@ void env_set( const wchar_t *key, event_t ev; array_list_t ev_list; + int is_universal = 0; if( (var_mode & ENV_USER ) && hash_get( &env_read_only, key ) ) @@ -367,7 +406,8 @@ void env_set( const wchar_t *key, export = (var_mode & ENV_EXPORT ); env_universal_set( key, val, export ); - + is_universal = 1; + } else { @@ -424,7 +464,8 @@ void env_set( const wchar_t *key, export = (var_mode & ENV_EXPORT ); env_universal_set( key, val, export ); - + is_universal = 1; + done = 1; } @@ -476,16 +517,22 @@ void env_set( const wchar_t *key, } - ev.type=EVENT_VARIABLE; - ev.variable = key; - ev.function_name = 0; + if( !is_universal ) + { + 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 ); + +// debug( 1, L"env_set: fire events on variable %ls", key ); + event_fire( &ev, &ev_list ); +// debug( 1, L"env_set: return from event firing" ); + al_destroy( &ev_list ); + } - al_init( &ev_list ); - al_push( &ev_list, L"VARIABLE" ); - al_push( &ev_list, key ); - - event_fire( &ev, &ev_list ); - al_destroy( &ev_list ); } /** @@ -843,7 +890,7 @@ char **env_export_arr( int recalc) if( recalc && !proc_had_barrier) env_universal_barrier(); - if( has_changed || env_universal_update ) + if( has_changed ) { array_list_t uni; hash_table_t vals; @@ -896,7 +943,6 @@ char **env_export_arr( int recalc) } export_arr[pos]=0; has_changed=0; - env_universal_update=0; } return export_arr; diff --git a/env_universal.c b/env_universal.c index bb03c7fa5..9e9d8839e 100644 --- a/env_universal.c +++ b/env_universal.c @@ -45,8 +45,7 @@ static int get_socket_count = 0; static wchar_t * path; static wchar_t *user; static void (*start_fishd)(); - -int env_universal_update=0; +static void (*external_callback)( int type, const wchar_t *name, const wchar_t *val ); /** Flag set to 1 when a barrier reply is recieved @@ -149,8 +148,7 @@ static int get_socket( int fork_ok ) Callback function used whenever a new fishd message is recieved */ static void callback( int type, const wchar_t *name, const wchar_t *val ) -{ - +{ if( type == BARRIER_REPLY ) { debug( 3, L"Got barrier reply" ); @@ -158,7 +156,8 @@ static void callback( int type, const wchar_t *name, const wchar_t *val ) } else { - env_universal_update=1; + if( external_callback ) + external_callback( type, name, val ); } } @@ -173,7 +172,7 @@ static void check_connection() if( env_universal_server.killme ) { - debug( 3, L"Lost connection to universal variable server." ); + debug( 2, L"Lost connection to universal variable server." ); close( env_universal_server.fd ); env_universal_server.fd = -1; env_universal_server.killme=0; @@ -204,12 +203,17 @@ static void reconnect() } -void env_universal_init( wchar_t * p, wchar_t *u, void (*sf)() ) +void env_universal_init( wchar_t * p, + wchar_t *u, + void (*sf)(), + void (*cb)( int type, const wchar_t *name, const wchar_t *val )) { debug( 2, L"env_universal_init()" ); path=p; user=u; start_fishd=sf; + external_callback = cb; + env_universal_server.fd = -1; env_universal_server.killme = 0; env_universal_server.fd = get_socket(1); diff --git a/env_universal.h b/env_universal.h index 0b42bb725..549235540 100644 --- a/env_universal.h +++ b/env_universal.h @@ -14,11 +14,6 @@ */ extern connection_t env_universal_server; -/** - Update flag. Set to 1 whenever an update has occured. -*/ -extern int env_universal_update; - /** Initialize the envuni library */ diff --git a/env_universal_common.c b/env_universal_common.c index d4a31fcd4..226859f18 100644 --- a/env_universal_common.c +++ b/env_universal_common.c @@ -163,9 +163,20 @@ void read_message( connection_t *src ) { if( res == L'\n' ) { - parse_message( (wchar_t *)src->input.buff, src ); + /* + Before calling parse_message, we must empty reset + everything, since the callback function could + potentially call read_message. + */ + + wchar_t *msg = wcsdup( (wchar_t *)src->input.buff ); sb_clear( &src->input ); - memset (&src->wstate, '\0', sizeof (mbstate_t)); + memset (&src->wstate, '\0', sizeof (mbstate_t)); + + + parse_message( msg, src ); + free( msg ); + } else { @@ -203,7 +214,7 @@ static void parse_message( wchar_t *msg, connection_t *src ) { debug( 2, L"parse_message( %ls );", msg ); - + if( msg[0] == L'#' ) return; diff --git a/event.c b/event.c index 665406574..47d93c003 100644 --- a/event.c +++ b/event.c @@ -280,7 +280,7 @@ static void event_fire_internal( event_t *event, array_list_t *arguments ) for( i=0; ibuff ); + is_subshell=1; + is_interactive=1; + eval( (wchar_t *)b->buff, 0, TOP ); + is_subshell=0; + is_interactive=1; + } if( b ) @@ -349,7 +357,7 @@ static void event_fire_internal( event_t *event, array_list_t *arguments ) Free killed events */ event_free_kills(); - + } /** @@ -394,9 +402,8 @@ static void event_fire_signal_events() void event_fire( event_t *event, array_list_t *arguments ) { - - int is_event_old = is_event; - is_event=1; + //int is_event_old = is_event; + is_event++; if( event && (event->type == EVENT_SIGNAL) ) { @@ -411,20 +418,16 @@ void event_fire( event_t *event, array_list_t *arguments ) 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; - - if( !is_event ) - job_do_notification(); + is_event--;// = is_event_old; } diff --git a/exec.c b/exec.c index 90b07393c..dc0ca1ee3 100644 --- a/exec.c +++ b/exec.c @@ -548,8 +548,7 @@ static int internal_exec_helper( const wchar_t *def, buff->out_buffer->used ); */ io_untransmogrify( io, io_internal ); - if( !is_event ) - job_do_notification(); + job_reap( 0 ); is_block=is_block_old; return res; } diff --git a/fish_pager.c b/fish_pager.c index d200205d3..c7ebb49ae 100644 --- a/fish_pager.c +++ b/fish_pager.c @@ -857,7 +857,7 @@ static void init() - env_universal_init( 0, 0, 0); + env_universal_init( 0, 0, 0, 0); input_common_init( &interrupt_handler ); sigemptyset( & act.sa_mask ); diff --git a/input.c b/input.c index 41578ae2c..9871bb85c 100644 --- a/input.c +++ b/input.c @@ -1244,7 +1244,7 @@ static void add_vi_bindings() static int interrupt_handler() { - if( job_do_notification() ) + if( job_reap( 1 ) ) repaint(); if( reader_interupted() ) { @@ -1256,7 +1256,7 @@ static int interrupt_handler() int input_init() { wchar_t *fn; - + input_common_init( &interrupt_handler ); if( setupterm( 0, STDOUT_FILENO, 0) == ERR ) diff --git a/input_common.c b/input_common.c index 55d9580ce..908dfc72e 100644 --- a/input_common.c +++ b/input_common.c @@ -116,6 +116,7 @@ static wint_t readb() { debug( 3, L"Wake up on universal variable event" ); env_universal_read_all(); + debug( 3, L"Return R_NULL" ); return R_NULL; } } diff --git a/main.c b/main.c index 88f671db2..a899d0814 100644 --- a/main.c +++ b/main.c @@ -289,7 +289,6 @@ int main( int argc, char **argv ) { eval( L"fish_on_exit", 0, TOP ); } - job_do_notification(); reader_pop_current_filename(); diff --git a/parser.c b/parser.c index b12c358dc..17ce457a0 100644 --- a/parser.c +++ b/parser.c @@ -1680,8 +1680,8 @@ static void eval_job( tokenizer *tok ) j->command=0; j->fg=1; j->constructed=0; - j->skip_notification = is_subshell; - + j->skip_notification = is_subshell || is_block || is_event || (!is_interactive); + proc_had_barrier=0; if( is_interactive ) @@ -1694,7 +1694,7 @@ static void eval_job( tokenizer *tok ) break; } } - + j->first_process = calloc( 1, sizeof( process_t ) ); /* Copy the command name */ @@ -1773,13 +1773,8 @@ static void eval_job( tokenizer *tok ) if( (!current_block->if_state) && (!current_block->skip) ) { - /* - We need to call job_do_notification, - since this is the function which sets - the status of the last process to exit - */ // debug( 2, L"Result of if block is %d\n", proc_get_last_status() ); - + current_block->skip = proc_get_last_status()!= 0; current_block->if_state++; } @@ -1824,6 +1819,8 @@ static void eval_job( tokenizer *tok ) } } + job_reap( 0 ); + // debug( 2, L"end eval_job()\n" ); } @@ -1835,16 +1832,16 @@ int eval( const wchar_t *cmd, io_data_t *io, int block_type ) block_t *start_current_block = current_block; io_data_t *prev_io = block_io; block_io = io; - + debug( 2, L"Eval command %ls", cmd ); - + + job_reap( 0 ); + if( !cmd ) { debug( 1, L"Tried to evaluate null pointer\n" BUGREPORT_MSG, PACKAGE_BUGREPORT ); - - return 1; } @@ -1945,6 +1942,7 @@ int eval( const wchar_t *cmd, io_data_t *io, int block_type ) eval_level--; + job_reap( 0 ); return code; } diff --git a/proc.c b/proc.c index 0e73f518a..1389c6ccb 100644 --- a/proc.c +++ b/proc.c @@ -47,6 +47,7 @@ Some of the code in this file is based on code from the Glibc manual. #include "env.h" #include "parser.h" #include "signal.h" +#include "event.h" /** Size of message buffer @@ -460,33 +461,82 @@ static void format_job_info( const job_t *j, const wchar_t *status ) fwprintf (stdout, L"\n" ); } -int job_do_notification() +static void fire_process_event( const wchar_t *msg, pid_t pid, int status ) +{ + static event_t ev; + static array_list_t event_arg; + static string_buffer_t event_pid, event_status; + static int init=0; + + event_t e; + + if( !init ) + { + al_init( &event_arg ); + sb_init( &event_pid ); + sb_init( &event_status ); + init=1; + } + + e.function_name=0; + + ev.type=EVENT_EXIT; + ev.pid = pid; + + al_push( &event_arg, msg ); + + sb_printf( &event_pid, L"%d", pid ); + al_push( &event_arg, event_pid.buff ); + + sb_printf( &event_status, L"%d", status ); + al_push( &event_arg, event_status.buff ); + + event_fire( &ev, &event_arg ); + al_truncate( &event_arg, 0 ); + sb_clear( &event_pid ); + sb_clear( &event_status ); +} + +int job_reap( int interactive ) { job_t *j, *jnext; int found=0; + static int locked = 0; + + locked++; + if( locked>1 ) + return; + for( j=first_job; j; j=jnext) { process_t *p; jnext = j->next; - - + + if( (!j->skip_notification) && (!interactive) ) + { + continue; + } + for( p=j->first_process; p; p=p->next ) { + int s; if( !p->completed ) continue; - if( p->type ) - continue; - + if( !p->pid ) + continue; - if( WIFSIGNALED(p->status) ) + fire_process_event( L"PROCESS_EXIT", p->pid, WEXITSTATUS( s ) ); + s = p->status; + + if( WIFSIGNALED(s) ) { /* Ignore signal SIGPIPE.We issue it ourselves to the pipe writer when the pipe reader dies. */ - if( WTERMSIG(p->status) != SIGPIPE ) + if( WTERMSIG(s) != SIGPIPE ) { int proc_is_job = ((p==j->first_process) && (p->next == 0)); if( proc_is_job ) @@ -519,40 +569,47 @@ int job_do_notification() */ p->status = 0; } - } + } } /* If all processes have completed, tell the user the job has completed and delete it from the active job list. */ - if( job_is_completed(j) ) { + if( job_is_completed( j ) ) + { if( !j->fg && !j->notified ) { if( !j->skip_notification ) { - format_job_info (j, L"ended"); + format_job_info( j, L"ended" ); found=1; } } job_free(j); + + fire_process_event( L"JOB_EXIT", -j->pgid, 0 ); } - else if(job_is_stopped (j) && !j->notified) { + else if( job_is_stopped( j ) && !j->notified ) + { /* Notify the user about newly stopped jobs. */ if( !j->skip_notification ) { - format_job_info(j, L"stopped"); + format_job_info( j, L"stopped" ); found=1; } j->notified = 1; } } + if( found ) fflush( stdout ); - return found; + + locked = 0; + return found; } diff --git a/proc.h b/proc.h index 9f8e85d6a..9c11a3860 100644 --- a/proc.h +++ b/proc.h @@ -92,7 +92,7 @@ typedef struct job */ int constructed; /** - Whether the specified job is a part of a subshell or some other form of special job that should not be reported + Whether the specified job is a part of a subshell, event handler or some other form of special job that should not be reported */ int skip_notification; @@ -185,8 +185,10 @@ void job_continue( job_t *j, int cont ); /** Notify user of nog events. Notify the user about stopped or terminated jobs. Delete terminated jobs from the active job list. + + \param interactive whether interactive jobs should be reaped as well */ -int job_do_notification(); +int job_reap( int interactive ); /** Signal handler for SIGCHLD. Mark any processes with relevant information. diff --git a/reader.c b/reader.c index 0807cce9b..2b283865e 100644 --- a/reader.c +++ b/reader.c @@ -623,7 +623,6 @@ void reader_write_title() if( exec_subshell( title, &l ) != -1 ) { int i; - job_do_notification(); writestr( L"\e]2;" ); for( i=0; iprompt, &prompt_list ) == -1 ) { - job_do_notification(); /* If executing the prompt fails, make sure we at least don't print any junk */ al_foreach( &prompt_list, (void (*)(const void *))&free ); al_destroy( &prompt_list ); @@ -2154,7 +2152,7 @@ void reader_run_command( wchar_t *cmd ) term_donate(); eval( cmd, 0, TOP ); - job_do_notification(); + job_reap( 1 ); term_steal();