diff --git a/CHANGELOG b/CHANGELOG index da7b35942..1d877a480 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -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 diff --git a/autoload.cpp b/autoload.cpp index 8f77f74f2..d57e69d9b 100644 --- a/autoload.cpp +++ b/autoload.cpp @@ -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 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 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) { diff --git a/autoload.h b/autoload.h index 4b0b1139e..3635d5f20 100644 --- a/autoload.h +++ b/autoload.h @@ -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 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 ); diff --git a/build_tools/fish_shell.pmdoc/index.xml b/build_tools/fish_shell.pmdoc/index.xml index 4dcfeacb9..0da74990a 100644 --- a/build_tools/fish_shell.pmdoc/index.xml +++ b/build_tools/fish_shell.pmdoc/index.xml @@ -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 \ diff --git a/builtin.cpp b/builtin.cpp index 21d164027..707d0cd80 100644 --- a/builtin.cpp +++ b/builtin.cpp @@ -1,19 +1,19 @@ /** \file builtin.c - Functions for executing builtin functions. + Functions for executing builtin functions. - How to add a new builtin function: + How to add a new builtin function: - 1). Create a function in builtin.c with the following signature: + 1). Create a function in builtin.c with the following signature: - static int builtin_NAME( parser_t &parser, wchar_t ** args ) + static int builtin_NAME( parser_t &parser, wchar_t ** args ) - where NAME is the name of the builtin, and args is a zero-terminated list of arguments. + where NAME is the name of the builtin, and args is a zero-terminated list of arguments. - 2). Add a line like { L"NAME", &builtin_NAME, N_(L"Bla bla bla") }, to the builtin_data_t variable. The description is used by the completion system. Note that this array is sorted! + 2). Add a line like { L"NAME", &builtin_NAME, N_(L"Bla bla bla") }, to the builtin_data_t variable. The description is used by the completion system. Note that this array is sorted! - 3). Create a file doc_src/NAME.txt, containing the manual for the builtin in Doxygen-format. Check the other builtin manuals for proper syntax. + 3). Create a file doc_src/NAME.txt, containing the manual for the builtin in Doxygen-format. Check the other builtin manuals for proper syntax. - 4). Use 'darcs add doc_src/NAME.txt' to start tracking changes to the documentation file. + 4). Use 'darcs add doc_src/NAME.txt' to start tracking changes to the documentation file. */ @@ -82,23 +82,23 @@ #define FG_MSG _( L"Send job %d, '%ls' to foreground\n" ) /** - Datastructure to describe a builtin. + Datastructure to describe a builtin. */ struct builtin_data_t { - /** - Name of the builtin - */ - const wchar_t *name; - /** - Function pointer tothe builtin implementation - */ - int (*func)(parser_t &parser, wchar_t **argv); - /** - Description of what the builtin does - */ - const wchar_t *desc; - + /** + Name of the builtin + */ + const wchar_t *name; + /** + Function pointer tothe builtin implementation + */ + int (*func)(parser_t &parser, wchar_t **argv); + /** + Description of what the builtin does + */ + const wchar_t *desc; + bool operator<(const wcstring &) const; bool operator<(const builtin_data_t *) const; }; @@ -162,35 +162,35 @@ static const io_chain_t *real_io; */ static int builtin_count_args( wchar_t **argv ) { - int argc = 1; - while( argv[argc] != 0 ) - { - argc++; - } - return argc; + int argc = 1; + while( argv[argc] != 0 ) + { + argc++; + } + return argc; } -/** - This function works like wperror, but it prints its result into - the sb_err string instead of to stderr. Used by the builtin - commands. +/** + This function works like wperror, but it prints its result into + the sb_err string instead of to stderr. Used by the builtin + commands. */ static void builtin_wperror( const wchar_t *s) { - if( s != 0 ) - { + if( s != 0 ) + { stderr_buffer.append(s); stderr_buffer.append(L": "); - } - char *err = strerror( errno ); - wchar_t *werr = str2wcs( err ); - if( werr ) - { + } + char *err = strerror( errno ); + wchar_t *werr = str2wcs( err ); + if( werr ) + { stderr_buffer.append(werr); stderr_buffer.push_back(L'\n'); - free( werr ); - } + free( werr ); + } } /** @@ -198,12 +198,12 @@ static void builtin_wperror( const wchar_t *s) */ static int count_char( const wchar_t *str, wchar_t c ) { - int res = 0; - for( ; *str; str++ ) - { - res += (*str==c); - } - return res; + int res = 0; + for( ; *str; str++ ) + { + res += (*str==c); + } + return res; } wcstring builtin_help_get( parser_t &parser, const wchar_t *name ) @@ -212,15 +212,15 @@ wcstring builtin_help_get( parser_t &parser, const wchar_t *name ) wcstring out; const wcstring name_esc = escape_string(name, 1); const wcstring cmd = format_string(L"__fish_print_help %ls", name_esc.c_str()); - if( exec_subshell( cmd, lst ) >= 0 ) - { - for( size_t i=0; i= 0 ) + { + for( size_t i=0; i 2*screen_height/3) ) - { - wchar_t *pos; - int cut=0; - int i; - - is_short = 1; - - /* - First move down 4 lines - */ - - pos = str; - for( i=0; (i<4) && pos && *pos; i++ ) - { - pos = wcschr( pos+1, L'\n' ); - } - - if( pos && *pos ) - { - - /* - Then find the next empty line - */ - for( ; *pos; pos++ ) - { - if( *pos == L'\n' ) - { - wchar_t *pos2; - int is_empty = 1; - - for( pos2 = pos+1; *pos2; pos2++ ) - { - if( *pos2 == L'\n' ) - break; - - if( *pos2 != L'\t' && *pos2 !=L' ' ) - { - is_empty = 0; - break; - } - } - if( is_empty ) - { - /* - And cut it - */ - *(pos2+1)=L'\0'; - cut = 1; - break; - } - } - } - } - - /* - We did not find a good place to cut message to - shorten it - so we make sure we don't print - anything. - */ - if( !cut ) - { - *str = 0; - } + /* + Interactive mode help to screen - only print synopsis if + the rest won't fit + */ + + int screen_height, lines; + + screen_height = common_get_height(); + lines = count_char( str, L'\n' ); + if( !get_is_interactive() || (lines > 2*screen_height/3) ) + { + wchar_t *pos; + int cut=0; + int i; + + is_short = 1; + + /* + First move down 4 lines + */ + + pos = str; + for( i=0; (i<4) && pos && *pos; i++ ) + { + pos = wcschr( pos+1, L'\n' ); + } + + if( pos && *pos ) + { + + /* + Then find the next empty line + */ + for( ; *pos; pos++ ) + { + if( *pos == L'\n' ) + { + wchar_t *pos2; + int is_empty = 1; + + for( pos2 = pos+1; *pos2; pos2++ ) + { + if( *pos2 == L'\n' ) + break; + + if( *pos2 != L'\t' && *pos2 !=L' ' ) + { + is_empty = 0; + break; + } + } + if( is_empty ) + { + /* + And cut it + */ + *(pos2+1)=L'\0'; + cut = 1; + break; + } + } + } + } + + /* + We did not find a good place to cut message to + shorten it - so we make sure we don't print + anything. + */ + if( !cut ) + { + *str = 0; + } + + } + } - } - } - b.append(str); - if( is_short ) - { + if( is_short ) + { append_format(b, _(L"%ls: Type 'help %ls' for related documentation\n\n"), cmd, cmd); - } - - free( str ); - } + } + + free( str ); + } } /** @@ -349,7 +349,7 @@ static void builtin_print_help( parser_t &parser, const wchar_t *cmd, wcstring & static void builtin_unknown_option( parser_t &parser, const wchar_t *cmd, const wchar_t *opt ) { append_format(stderr_buffer, BUILTIN_ERR_UNKNOWN, cmd, opt); - builtin_print_help( parser, cmd, stderr_buffer ); + builtin_print_help( parser, cmd, stderr_buffer ); } /** @@ -358,7 +358,7 @@ static void builtin_unknown_option( parser_t &parser, const wchar_t *cmd, const static void builtin_missing_argument( parser_t &parser, const wchar_t *cmd, const wchar_t *opt ) { append_format(stderr_buffer, BUILTIN_ERR_MISSING, cmd, opt); - builtin_print_help( parser, cmd, stderr_buffer ); + builtin_print_help( parser, cmd, stderr_buffer ); } /* @@ -396,29 +396,29 @@ int builtin_test( parser_t &parser, wchar_t **argv ); */ static void builtin_bind_list() { - size_t i; + size_t i; wcstring_list_t lst; - input_mapping_get_names( lst ); - - for( i=0; iouter ) - block=0; - break; - } - case GLOBAL: - { - block=0; - } - case UNSET: - { - while( block && - block->type() != FUNCTION_CALL && - block->type() != FUNCTION_CALL_NO_SHADOW ) - block = block->outer; - } - } - if( block ) - { + switch( scope ) + { + case LOCAL: + { + if( !block->outer ) + block=0; + break; + } + case GLOBAL: + { + block=0; + } + case UNSET: + { + while( block && + block->type() != FUNCTION_CALL && + block->type() != FUNCTION_CALL_NO_SHADOW ) + block = block->outer; + } + } + if( block ) + { block->event_blocks.push_front(eb); - } - else - { + } + else + { parser.global_event_blocks.push_front(eb); - } - } + } + } - return STATUS_BUILTIN_OK; + return STATUS_BUILTIN_OK; } @@ -869,83 +869,83 @@ static int builtin_block( parser_t &parser, wchar_t **argv ) */ static int builtin_builtin( parser_t &parser, wchar_t **argv ) { - int argc=builtin_count_args( argv ); - int list=0; + int argc=builtin_count_args( argv ); + int list=0; - woptind=0; + woptind=0; - static const struct woption - long_options[] = - { - { - L"names", no_argument, 0, 'n' - } - , - { - L"help", no_argument, 0, 'h' - } - , - { - 0, 0, 0, 0 - } - } - ; + static const struct woption + long_options[] = + { + { + L"names", no_argument, 0, 'n' + } + , + { + L"help", no_argument, 0, 'h' + } + , + { + 0, 0, 0, 0 + } + } + ; - while( 1 ) - { - int opt_index = 0; + while( 1 ) + { + int opt_index = 0; - int opt = wgetopt_long( argc, - argv, - L"nh", - long_options, - &opt_index ); - if( opt == -1 ) - break; + int opt = wgetopt_long( argc, + argv, + L"nh", + 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 STATUS_BUILTIN_ERROR; - case 'h': - builtin_print_help( parser, argv[0], stdout_buffer ); - return STATUS_BUILTIN_OK; + return STATUS_BUILTIN_ERROR; + case 'h': + builtin_print_help( parser, argv[0], stdout_buffer ); + return STATUS_BUILTIN_OK; - case 'n': - list=1; - break; + case 'n': + list=1; + break; - case '?': - builtin_unknown_option( parser, argv[0], argv[woptind-1] ); - return STATUS_BUILTIN_ERROR; + case '?': + builtin_unknown_option( parser, argv[0], argv[woptind-1] ); + return STATUS_BUILTIN_ERROR; - } + } - } + } - if( list ) - { + if( list ) + { wcstring_list_t names = builtin_get_names(); sort(names.begin(), names.end()); - - for( size_t i=0; i ev; - event_get( &search, &ev ); + std::vector ev; + event_get( &search, &ev ); out.append(L"function "); - + /* Typically we prefer to specify the function name first, e.g. "function foo --description bar" But If the function name starts with a -, we'll need to output it after all the options. */ bool defer_function_name = (name.at(0) == L'-'); @@ -1107,82 +1107,82 @@ static void functions_def( const wcstring &name, wcstring &out ) out.append(name); } - if (! desc.empty()) - { + if (! desc.empty()) + { wcstring esc_desc = escape_string(desc, true); - out.append(L" --description "); + out.append(L" --description "); out.append(esc_desc); - } + } - if( !function_get_shadows( name ) ) - { - out.append(L" --no-scope-shadowing" ); - } + if( !function_get_shadows( name ) ) + { + out.append(L" --no-scope-shadowing" ); + } - for( size_t i=0; itype ) - { - case EVENT_SIGNAL: - { - append_format( out, L" --on-signal %ls", sig2wcs( next->param1.signal ) ); - break; - } + for( size_t i=0; itype ) + { + case EVENT_SIGNAL: + { + append_format( out, L" --on-signal %ls", sig2wcs( next->param1.signal ) ); + break; + } - case EVENT_VARIABLE: - { - append_format( out, L" --on-variable %ls", next->str_param1.c_str() ); - break; - } + case EVENT_VARIABLE: + { + append_format( out, L" --on-variable %ls", next->str_param1.c_str() ); + break; + } - case EVENT_EXIT: - { - if( next->param1.pid > 0 ) - append_format( out, L" --on-process-exit %d", next->param1.pid ); - else - append_format( out, L" --on-job-exit %d", -next->param1.pid ); - break; - } + case EVENT_EXIT: + { + if( next->param1.pid > 0 ) + append_format( out, L" --on-process-exit %d", next->param1.pid ); + else + append_format( out, L" --on-job-exit %d", -next->param1.pid ); + break; + } - case EVENT_JOB_ID: - { - const job_t *j = job_get( next->param1.job_id ); - if( j ) - append_format( out, L" --on-job-exit %d", j->pgid ); - break; - } + case EVENT_JOB_ID: + { + const job_t *j = job_get( next->param1.job_id ); + if( j ) + append_format( out, L" --on-job-exit %d", j->pgid ); + break; + } - case EVENT_GENERIC: - { - append_format( out, L" --on-event %ls", next->str_param1.c_str() ); - break; - } - - } + case EVENT_GENERIC: + { + append_format( out, L" --on-event %ls", next->str_param1.c_str() ); + break; + } + + } + + } - } - wcstring_list_t named = function_get_named_arguments( name ); - if( named.size() > 0 ) - { - append_format( out, L" --argument" ); - for( size_t i=0; i < named.size(); i++ ) - { - append_format( out, L" %ls", named.at(i).c_str() ); - } - } + if( named.size() > 0 ) + { + append_format( out, L" --argument" ); + for( size_t i=0; i < named.size(); i++ ) + { + append_format( out, L" %ls", named.at(i).c_str() ); + } + } /* Output the function name if we deferred it */ if ( defer_function_name ){ out.append(L" -- "); out.append(name); } - + /* This forced tab is sort of crummy - not all functions start with a tab */ append_format( out, L"\n\t%ls", def.c_str()); - + /* Append a newline before the 'end', unless there already is one there */ if (! string_suffixes_string(L"\n", def)) { out.push_back(L'\n'); @@ -1196,271 +1196,271 @@ static void functions_def( const wcstring &name, wcstring &out ) */ static int builtin_functions( parser_t &parser, wchar_t **argv ) { - int i; - int erase=0; - wchar_t *desc=0; + int i; + int erase=0; + wchar_t *desc=0; - int argc=builtin_count_args( argv ); - int list=0; - int show_hidden=0; - int res = STATUS_BUILTIN_OK; - int query = 0; - int copy = 0; + int argc=builtin_count_args( argv ); + int list=0; + int show_hidden=0; + int res = STATUS_BUILTIN_OK; + int query = 0; + int copy = 0; - woptind=0; + woptind=0; - static const struct woption - long_options[] = - { - { - L"erase", no_argument, 0, 'e' - } - , - { - L"description", required_argument, 0, 'd' - } - , - { - L"names", no_argument, 0, 'n' - } - , - { - L"all", no_argument, 0, 'a' - } - , - { - L"help", no_argument, 0, 'h' - } - , - { - L"query", no_argument, 0, 'q' - } - , - { - L"copy", no_argument, 0, 'c' - } - , - { - 0, 0, 0, 0 - } - } - ; + static const struct woption + long_options[] = + { + { + L"erase", no_argument, 0, 'e' + } + , + { + L"description", required_argument, 0, 'd' + } + , + { + L"names", no_argument, 0, 'n' + } + , + { + L"all", no_argument, 0, 'a' + } + , + { + L"help", no_argument, 0, 'h' + } + , + { + L"query", no_argument, 0, 'q' + } + , + { + L"copy", no_argument, 0, 'c' + } + , + { + 0, 0, 0, 0 + } + } + ; - while( 1 ) - { - int opt_index = 0; + while( 1 ) + { + int opt_index = 0; - int opt = wgetopt_long( argc, - argv, - L"ed:nahqc", - long_options, - &opt_index ); - if( opt == -1 ) - break; + int opt = wgetopt_long( argc, + argv, + L"ed:nahqc", + 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 STATUS_BUILTIN_ERROR; + return STATUS_BUILTIN_ERROR; - case 'e': - erase=1; - break; + case 'e': + erase=1; + break; - case 'd': - desc=woptarg; - break; + case 'd': + desc=woptarg; + break; - case 'n': - list=1; - break; + case 'n': + list=1; + break; - case 'a': - show_hidden=1; - break; + case 'a': + show_hidden=1; + break; - case 'h': - builtin_print_help( parser, argv[0], stdout_buffer ); - return STATUS_BUILTIN_OK; + case 'h': + builtin_print_help( parser, argv[0], stdout_buffer ); + return STATUS_BUILTIN_OK; - case 'q': - query = 1; - break; + case 'q': + query = 1; + break; - case 'c': - copy = 1; - break; + case 'c': + copy = 1; + break; - case '?': - builtin_unknown_option( parser, argv[0], argv[woptind-1] ); - return STATUS_BUILTIN_ERROR; + case '?': + builtin_unknown_option( parser, argv[0], argv[woptind-1] ); + return STATUS_BUILTIN_ERROR; - } + } - } + } - /* - Erase, desc, query, copy and list are mutually exclusive - */ - if( (erase + (!!desc) + list + query + copy) > 1 ) - { - append_format(stderr_buffer, - _( L"%ls: Invalid combination of options\n" ), - argv[0] ); + /* + Erase, desc, query, copy and list are mutually exclusive + */ + if( (erase + (!!desc) + list + query + copy) > 1 ) + { + append_format(stderr_buffer, + _( L"%ls: Invalid combination of options\n" ), + argv[0] ); - builtin_print_help( parser, argv[0], stderr_buffer ); + builtin_print_help( parser, argv[0], stderr_buffer ); - return STATUS_BUILTIN_ERROR; - } + return STATUS_BUILTIN_ERROR; + } - if( erase ) - { - int i; - for( i=woptind; i start) { @@ -1558,7 +1558,7 @@ static int builtin_echo( parser_t &parser, wchar_t **argv ) /* Skip first arg */ if (! *argv++) return STATUS_BUILTIN_ERROR; - + /* Process options */ bool print_newline = true, print_spaces = true, interpret_special_chars = false; while (*argv) { @@ -1575,10 +1575,10 @@ static int builtin_echo( parser_t &parser, wchar_t **argv ) } argv++; } - + /* The special character \c can be used to indicate no more output */ bool continue_output = true; - + for (size_t idx = 0; continue_output && argv[idx] != NULL; idx++) { if (print_spaces && idx > 0) @@ -1608,9 +1608,9 @@ static int builtin_echo( parser_t &parser, wchar_t **argv ) case L't': wc = L'\t'; break; case L'v': wc = L'\v'; break; case L'\\': wc = L'\\'; break; - + case L'c': wc = 0; continue_output = false; break; - + default: { /* Octal and hex escape sequences */ @@ -1629,10 +1629,10 @@ static int builtin_echo( parser_t &parser, wchar_t **argv ) break; } } - + /* Skip over characters that were part of this escape sequence (but not the backslash, which will be handled by the loop increment */ j += consumed; - + if (continue_output) stdout_buffer.push_back(wc); } @@ -1646,8 +1646,8 @@ static int builtin_echo( parser_t &parser, wchar_t **argv ) /** The pwd builtin. We don't respect -P to resolve symbolic links because we try to always resolve them. */ static int builtin_pwd( parser_t &parser, wchar_t **argv ) { - wchar_t dir_path[4096]; - wchar_t *res = wgetcwd( dir_path, 4096 ); + wchar_t dir_path[4096]; + wchar_t *res = wgetcwd( dir_path, 4096 ); if (res == NULL) { return STATUS_BUILTIN_ERROR; } else { @@ -1663,348 +1663,348 @@ static int builtin_pwd( parser_t &parser, wchar_t **argv ) */ static int builtin_function( parser_t &parser, wchar_t **argv ) { - int argc = builtin_count_args( argv ); - int res=STATUS_BUILTIN_OK; - wchar_t *desc=0; - std::vector events; - + int argc = builtin_count_args( argv ); + int res=STATUS_BUILTIN_OK; + wchar_t *desc=0; + std::vector events; + std::auto_ptr named_arguments(NULL); - wchar_t *name = 0; - bool shadows = true; - - woptind=0; + wchar_t *name = 0; + bool shadows = true; + + woptind=0; function_def_block_t * const fdb = new function_def_block_t(); - parser.push_block( fdb ); + parser.push_block( fdb ); - static const struct woption - long_options[] = - { - { - L"description", required_argument, 0, 'd' - } - , - { - L"on-signal", required_argument, 0, 's' - } - , - { - L"on-job-exit", required_argument, 0, 'j' - } - , - { - L"on-process-exit", required_argument, 0, 'p' - } - , - { - L"on-variable", required_argument, 0, 'v' - } - , - { - L"on-event", required_argument, 0, 'e' - } - , - { - L"help", no_argument, 0, 'h' - } - , - { - L"argument-names", no_argument, 0, 'a' - } - , - { - L"no-scope-shadowing", no_argument, 0, 'S' - } - , - { - 0, 0, 0, 0 - } - } - ; + static const struct woption + long_options[] = + { + { + L"description", required_argument, 0, 'd' + } + , + { + L"on-signal", required_argument, 0, 's' + } + , + { + L"on-job-exit", required_argument, 0, 'j' + } + , + { + L"on-process-exit", required_argument, 0, 'p' + } + , + { + L"on-variable", required_argument, 0, 'v' + } + , + { + L"on-event", required_argument, 0, 'e' + } + , + { + L"help", no_argument, 0, 'h' + } + , + { + L"argument-names", no_argument, 0, 'a' + } + , + { + L"no-scope-shadowing", no_argument, 0, 'S' + } + , + { + 0, 0, 0, 0 + } + } + ; - while( 1 && (!res ) ) - { - int opt_index = 0; + while( 1 && (!res ) ) + { + int opt_index = 0; - int opt = wgetopt_long( argc, - argv, - L"d:s:j:p:v:e:haS", - long_options, - &opt_index ); - if( opt == -1 ) - break; + int opt = wgetopt_long( argc, + argv, + L"d:s:j:p:v:e:haS", + 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 ); - res = 1; - break; + res = 1; + break; - case 'd': - desc=woptarg; - break; + case 'd': + desc=woptarg; + break; - case 's': - { - int sig = wcs2sig( woptarg ); + case 's': + { + int sig = wcs2sig( woptarg ); - if( sig < 0 ) - { - append_format(stderr_buffer, - _( L"%ls: Unknown signal '%ls'\n" ), - argv[0], - woptarg ); - res=1; - break; - } + if( sig < 0 ) + { + append_format(stderr_buffer, + _( L"%ls: Unknown signal '%ls'\n" ), + argv[0], + woptarg ); + res=1; + break; + } events.push_back(event_t::signal_event(sig)); - break; - } + break; + } - case 'v': - { - if( wcsvarname( woptarg ) ) - { - append_format(stderr_buffer, - _( L"%ls: Invalid variable name '%ls'\n" ), - argv[0], - woptarg ); - res=STATUS_BUILTIN_ERROR; - break; - } + case 'v': + { + if( wcsvarname( woptarg ) ) + { + append_format(stderr_buffer, + _( L"%ls: Invalid variable name '%ls'\n" ), + argv[0], + woptarg ); + res=STATUS_BUILTIN_ERROR; + break; + } events.push_back(event_t::variable_event(woptarg)); - break; - } + break; + } - case 'e': - { + case 'e': + { events.push_back(event_t::generic_event(woptarg)); - break; - } + break; + } - case 'j': - case 'p': - { - pid_t pid; - wchar_t *end; + case 'j': + case 'p': + { + pid_t pid; + wchar_t *end; event_t e(EVENT_ANY); - - if( ( opt == 'j' ) && - ( wcscasecmp( woptarg, L"caller" ) == 0 ) ) - { - int job_id = -1; - if( is_subshell ) - { - block_t *b = parser.current_block; + if( ( opt == 'j' ) && + ( wcscasecmp( woptarg, L"caller" ) == 0 ) ) + { + int job_id = -1; - while( b && (b->type() != SUBST) ) - b = b->outer; + if( is_subshell ) + { + block_t *b = parser.current_block; - if( b ) - { - b=b->outer; - } - if( b->job ) - { - job_id = b->job->job_id; - } - } + while( b && (b->type() != SUBST) ) + b = b->outer; - if( job_id == -1 ) - { - append_format(stderr_buffer, - _( L"%ls: Cannot find calling job for event handler\n" ), - argv[0] ); - res=1; - } - else - { - e.type = EVENT_JOB_ID; - e.param1.job_id = job_id; - } + if( b ) + { + b=b->outer; + } + if( b->job ) + { + job_id = b->job->job_id; + } + } - } - else - { - errno = 0; - pid = fish_wcstoi( woptarg, &end, 10 ); - if( errno || !end || *end ) - { - append_format(stderr_buffer, - _( L"%ls: Invalid process id %ls\n" ), - argv[0], - woptarg ); - res=1; - break; - } + if( job_id == -1 ) + { + append_format(stderr_buffer, + _( L"%ls: Cannot find calling job for event handler\n" ), + argv[0] ); + res=1; + } + else + { + e.type = EVENT_JOB_ID; + e.param1.job_id = job_id; + } + + } + else + { + errno = 0; + pid = fish_wcstoi( woptarg, &end, 10 ); + if( errno || !end || *end ) + { + append_format(stderr_buffer, + _( L"%ls: Invalid process id %ls\n" ), + argv[0], + woptarg ); + res=1; + break; + } - e.type = EVENT_EXIT; - e.param1.pid = (opt=='j'?-1:1)*abs(pid); - } - if( res ) - { + e.type = EVENT_EXIT; + e.param1.pid = (opt=='j'?-1:1)*abs(pid); + } + if( res ) + { /* nothing */ - } - else - { + } + else + { events.push_back(e); - } - break; - } + } + break; + } - case 'a': - if( named_arguments.get() == NULL ) - named_arguments.reset(new wcstring_list_t); - break; - - case 'S': - shadows = 0; - break; - - case 'h': - parser.pop_block(); - parser.push_block( new fake_block_t() ); - builtin_print_help( parser, argv[0], stdout_buffer ); - return STATUS_BUILTIN_OK; - - case '?': - builtin_unknown_option( parser, argv[0], argv[woptind-1] ); - res = 1; - break; + case 'a': + if( named_arguments.get() == NULL ) + named_arguments.reset(new wcstring_list_t); + break; - } + case 'S': + shadows = 0; + break; - } + case 'h': + parser.pop_block(); + parser.push_block( new fake_block_t() ); + builtin_print_help( parser, argv[0], stdout_buffer ); + return STATUS_BUILTIN_OK; - if( !res ) - { - - if( argc == woptind ) - { - append_format(stderr_buffer, - _( L"%ls: Expected function name\n" ), - argv[0] ); - res=1; - } - else if( wcsfuncname( argv[woptind] ) ) - { - append_format(stderr_buffer, - _( L"%ls: Illegal function name '%ls'\n" ), - argv[0], - argv[woptind] ); + case '?': + builtin_unknown_option( parser, argv[0], argv[woptind-1] ); + res = 1; + break; - res=1; - } - else if( parser_keywords_is_reserved(argv[woptind] ) ) - { + } - append_format(stderr_buffer, - _( L"%ls: The name '%ls' is reserved,\nand can not be used as a function name\n" ), - argv[0], - argv[woptind] ); + } - res=1; - } - else - { + if( !res ) + { + + if( argc == woptind ) + { + append_format(stderr_buffer, + _( L"%ls: Expected function name\n" ), + argv[0] ); + res=1; + } + else if( wcsfuncname( argv[woptind] ) ) + { + append_format(stderr_buffer, + _( L"%ls: Illegal function name '%ls'\n" ), + argv[0], + argv[woptind] ); + + res=1; + } + else if( parser_keywords_is_reserved(argv[woptind] ) ) + { + + append_format(stderr_buffer, + _( L"%ls: The name '%ls' is reserved,\nand can not be used as a function name\n" ), + argv[0], + argv[woptind] ); + + res=1; + } + else + { + + name = argv[woptind++]; + + if( named_arguments.get() ) + { + while( woptind < argc ) + { + if( wcsvarname( argv[woptind] ) ) + { + append_format(stderr_buffer, + _( L"%ls: Invalid variable name '%ls'\n" ), + argv[0], + argv[woptind] ); + res = STATUS_BUILTIN_ERROR; + break; + } - name = argv[woptind++]; - - if( named_arguments.get() ) - { - while( woptind < argc ) - { - if( wcsvarname( argv[woptind] ) ) - { - append_format(stderr_buffer, - _( L"%ls: Invalid variable name '%ls'\n" ), - argv[0], - argv[woptind] ); - res = STATUS_BUILTIN_ERROR; - break; - } - named_arguments->push_back(argv[woptind++]); - } - } - else if( woptind != argc ) - { - append_format(stderr_buffer, - _( L"%ls: Expected one argument, got %d\n" ), - argv[0], - argc ); - res=1; - - } - } - } + } + } + else if( woptind != argc ) + { + append_format(stderr_buffer, + _( L"%ls: Expected one argument, got %d\n" ), + argv[0], + argc ); + res=1; - if( res ) - { - size_t i; - size_t chars=0; + } + } + } - builtin_print_help( parser, argv[0], stderr_buffer ); - const wchar_t *cfa = _( L"Current functions are: " ); - stderr_buffer.append( cfa ); - chars += wcslen( cfa ); + if( res ) + { + size_t i; + size_t chars=0; + + builtin_print_help( parser, argv[0], stderr_buffer ); + const wchar_t *cfa = _( L"Current functions are: " ); + stderr_buffer.append( cfa ); + chars += wcslen( cfa ); wcstring_list_t names = function_get_names(0); sort(names.begin(), names.end()); - for( i=0; i common_get_width() ) - { - chars = 0; + for( i=0; i common_get_width() ) + { + chars = 0; stderr_buffer.push_back(L'\n'); - } + } stderr_buffer.append(nxt); stderr_buffer.append(L" "); - } + } stderr_buffer.push_back(L'\n'); - parser.pop_block(); - parser.push_block( new fake_block_t() ); - } - else - { - function_data_t &d = fdb->function_data; - + parser.pop_block(); + parser.push_block( new fake_block_t() ); + } + else + { + function_data_t &d = fdb->function_data; + d.name = name; if (desc) d.description = desc; - d.events.swap(events); - d.shadows = shadows; + d.events.swap(events); + d.shadows = shadows; if (named_arguments.get()) d.named_arguments.swap(*named_arguments); - - for( size_t i=0; itok_pos = parser.get_pos(); - parser.current_block->skip = 1; - return STATUS_BUILTIN_OK; + for( size_t i=0; itok_pos = parser.get_pos(); + parser.current_block->skip = 1; + + return STATUS_BUILTIN_OK; } @@ -2013,113 +2013,113 @@ static int builtin_function( parser_t &parser, wchar_t **argv ) */ static int builtin_random( parser_t &parser, wchar_t **argv ) { - static int seeded=0; - static struct drand48_data seed_buffer; - - int argc = builtin_count_args( argv ); + static int seeded=0; + static struct drand48_data seed_buffer; - woptind=0; + int argc = builtin_count_args( argv ); - static const struct woption - long_options[] = - { - { - L"help", no_argument, 0, 'h' - } - , - { - 0, 0, 0, 0 - } - } - ; + woptind=0; - while( 1 ) - { - int opt_index = 0; + static const struct woption + long_options[] = + { + { + L"help", no_argument, 0, 'h' + } + , + { + 0, 0, 0, 0 + } + } + ; - int opt = wgetopt_long( argc, - argv, - L"h", - long_options, - &opt_index ); - if( opt == -1 ) - break; + while( 1 ) + { + int opt_index = 0; - switch( opt ) - { - case 0: - if(long_options[opt_index].flag != 0) - break; + int opt = wgetopt_long( argc, + argv, + L"h", + 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 STATUS_BUILTIN_ERROR; + return STATUS_BUILTIN_ERROR; - case 'h': - builtin_print_help( parser, argv[0], stdout_buffer ); - break; + case 'h': + builtin_print_help( parser, argv[0], stdout_buffer ); + break; - case '?': - builtin_unknown_option( parser, argv[0], argv[woptind-1] ); - return STATUS_BUILTIN_ERROR; + case '?': + builtin_unknown_option( parser, argv[0], argv[woptind-1] ); + return STATUS_BUILTIN_ERROR; - } + } - } + } - switch( argc-woptind ) - { + switch( argc-woptind ) + { - case 0: - { - long res; - - if( !seeded ) - { - seeded=1; - srand48_r(time(0), &seed_buffer); - } - lrand48_r( &seed_buffer, &res ); - - append_format(stdout_buffer, L"%ld\n", labs(res%32767) ); - break; - } + case 0: + { + long res; - case 1: - { - long foo; - wchar_t *end=0; + if( !seeded ) + { + seeded=1; + srand48_r(time(0), &seed_buffer); + } + lrand48_r( &seed_buffer, &res ); - errno=0; - foo = wcstol( argv[woptind], &end, 10 ); - if( errno || *end ) - { - append_format(stderr_buffer, - _( L"%ls: Seed value '%ls' is not a valid number\n" ), - argv[0], - argv[woptind] ); + append_format(stdout_buffer, L"%ld\n", labs(res%32767) ); + break; + } - return STATUS_BUILTIN_ERROR; - } - seeded=1; - srand48_r( foo, &seed_buffer); - break; - } + case 1: + { + long foo; + wchar_t *end=0; - default: - { - append_format(stderr_buffer, - _( L"%ls: Expected zero or one argument, got %d\n" ), - argv[0], - argc-woptind ); - builtin_print_help( parser, argv[0], stderr_buffer ); - return STATUS_BUILTIN_ERROR; - } - } - return STATUS_BUILTIN_OK; + errno=0; + foo = wcstol( argv[woptind], &end, 10 ); + if( errno || *end ) + { + append_format(stderr_buffer, + _( L"%ls: Seed value '%ls' is not a valid number\n" ), + argv[0], + argv[woptind] ); + + return STATUS_BUILTIN_ERROR; + } + seeded=1; + srand48_r( foo, &seed_buffer); + break; + } + + default: + { + append_format(stderr_buffer, + _( L"%ls: Expected zero or one argument, got %d\n" ), + argv[0], + argc-woptind ); + builtin_print_help( parser, argv[0], stderr_buffer ); + return STATUS_BUILTIN_ERROR; + } + } + return STATUS_BUILTIN_OK; } @@ -2128,308 +2128,308 @@ static int builtin_random( parser_t &parser, wchar_t **argv ) */ static int builtin_read( parser_t &parser, wchar_t **argv ) { - wchar_t *buff=0; - int i, argc = builtin_count_args( argv ); - int place = ENV_USER; - wchar_t *nxt; - const wchar_t *prompt = DEFAULT_READ_PROMPT; - const wchar_t *commandline = L""; - int exit_res=STATUS_BUILTIN_OK; - const wchar_t *mode_name = READ_MODE_NAME; - int shell = 0; - - woptind=0; - - while( 1 ) - { - static const struct woption - long_options[] = - { - { - L"export", no_argument, 0, 'x' - } - , - { - L"global", no_argument, 0, 'g' - } - , - { - L"local", no_argument, 0, 'l' - } - , - { - L"universal", no_argument, 0, 'U' - } - , - { - L"unexport", no_argument, 0, 'u' - } - , - { - L"prompt", required_argument, 0, 'p' - } - , - { - L"command", required_argument, 0, 'c' - } - , - { - L"mode-name", required_argument, 0, 'm' - } - , - { - L"shell", no_argument, 0, 's' - } - , - { - L"help", no_argument, 0, 'h' - } - , - { - 0, 0, 0, 0 - } - } - ; + wchar_t *buff=0; + int i, argc = builtin_count_args( argv ); + int place = ENV_USER; + wchar_t *nxt; + const wchar_t *prompt = DEFAULT_READ_PROMPT; + const wchar_t *commandline = L""; + int exit_res=STATUS_BUILTIN_OK; + const wchar_t *mode_name = READ_MODE_NAME; + int shell = 0; - int opt_index = 0; + woptind=0; - int opt = wgetopt_long( argc, - argv, - L"xglUup:c:hm:s", - long_options, - &opt_index ); - if( opt == -1 ) - break; + while( 1 ) + { + static const struct woption + long_options[] = + { + { + L"export", no_argument, 0, 'x' + } + , + { + L"global", no_argument, 0, 'g' + } + , + { + L"local", no_argument, 0, 'l' + } + , + { + L"universal", no_argument, 0, 'U' + } + , + { + L"unexport", no_argument, 0, 'u' + } + , + { + L"prompt", required_argument, 0, 'p' + } + , + { + L"command", required_argument, 0, 'c' + } + , + { + L"mode-name", required_argument, 0, 'm' + } + , + { + L"shell", no_argument, 0, 's' + } + , + { + L"help", no_argument, 0, 'h' + } + , + { + 0, 0, 0, 0 + } + } + ; - switch( opt ) - { - case 0: - if(long_options[opt_index].flag != 0) - break; + int opt_index = 0; + + int opt = wgetopt_long( argc, + argv, + L"xglUup:c:hm:s", + 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 STATUS_BUILTIN_ERROR; + return STATUS_BUILTIN_ERROR; - case L'x': - place |= ENV_EXPORT; - break; + case L'x': + place |= ENV_EXPORT; + break; - case L'g': - place |= ENV_GLOBAL; - break; + case L'g': + place |= ENV_GLOBAL; + break; - case L'l': - place |= ENV_LOCAL; - break; + case L'l': + place |= ENV_LOCAL; + break; - case L'U': - place |= ENV_UNIVERSAL; - break; + case L'U': + place |= ENV_UNIVERSAL; + break; - case L'u': - place |= ENV_UNEXPORT; - break; + case L'u': + place |= ENV_UNEXPORT; + break; - case L'p': - prompt = woptarg; - break; + case L'p': + prompt = woptarg; + break; - case L'c': - commandline = woptarg; - break; + case L'c': + commandline = woptarg; + break; - case L'm': - mode_name = woptarg; - break; + case L'm': + mode_name = woptarg; + break; - case 's': - shell = 1; - break; - - case 'h': - builtin_print_help( parser, argv[0], stdout_buffer ); - return STATUS_BUILTIN_OK; + case 's': + shell = 1; + break; - case L'?': - builtin_unknown_option( parser, argv[0], argv[woptind-1] ); - return STATUS_BUILTIN_ERROR; - } + case 'h': + builtin_print_help( parser, argv[0], stdout_buffer ); + return STATUS_BUILTIN_OK; - } + case L'?': + builtin_unknown_option( parser, argv[0], argv[woptind-1] ); + return STATUS_BUILTIN_ERROR; + } - if( ( place & ENV_UNEXPORT ) && ( place & ENV_EXPORT ) ) - { - append_format(stderr_buffer, - BUILTIN_ERR_EXPUNEXP, - argv[0] ); + } + + if( ( place & ENV_UNEXPORT ) && ( place & ENV_EXPORT ) ) + { + append_format(stderr_buffer, + BUILTIN_ERR_EXPUNEXP, + argv[0] ); - builtin_print_help( parser, argv[0], stderr_buffer ); - return STATUS_BUILTIN_ERROR; - } + builtin_print_help( parser, argv[0], stderr_buffer ); + return STATUS_BUILTIN_ERROR; + } - if( (place&ENV_LOCAL?1:0) + (place & ENV_GLOBAL?1:0) + (place & ENV_UNIVERSAL?1:0) > 1) - { - append_format(stderr_buffer, - BUILTIN_ERR_GLOCAL, - argv[0] ); - builtin_print_help( parser, argv[0], stderr_buffer ); + if( (place&ENV_LOCAL?1:0) + (place & ENV_GLOBAL?1:0) + (place & ENV_UNIVERSAL?1:0) > 1) + { + append_format(stderr_buffer, + BUILTIN_ERR_GLOCAL, + argv[0] ); + builtin_print_help( parser, argv[0], stderr_buffer ); - return STATUS_BUILTIN_ERROR; - } + return STATUS_BUILTIN_ERROR; + } - /* - Verify all variable names - */ - for( i=woptind; i= 0 && (size_t)opt_index < sizeof long_options / sizeof *long_options); - 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 ); - return STATUS_BUILTIN_ERROR; + 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 ); + return STATUS_BUILTIN_ERROR; - case 'h': - builtin_print_help( parser, argv[0], stdout_buffer ); - return STATUS_BUILTIN_OK; + case 'h': + builtin_print_help( parser, argv[0], stdout_buffer ); + return STATUS_BUILTIN_OK; - case ':': - builtin_missing_argument( parser, argv[0], argv[woptind-1] ); - return STATUS_BUILTIN_ERROR; + case ':': + builtin_missing_argument( parser, argv[0], argv[woptind-1] ); + return STATUS_BUILTIN_ERROR; - case '?': - builtin_unknown_option( parser, argv[0], argv[woptind-1] ); - return STATUS_BUILTIN_ERROR; + case '?': + builtin_unknown_option( parser, argv[0], argv[woptind-1] ); + return STATUS_BUILTIN_ERROR; - case 'i': - index=1; - break; - } - - } + case 'i': + index=1; + break; + } + + } - needle = argv[woptind]; - if (!needle) - { - append_format(stderr_buffer, _( L"%ls: Key not specified\n" ), argv[0] ); - } - + needle = argv[woptind]; + if (!needle) + { + append_format(stderr_buffer, _( L"%ls: Key not specified\n" ), argv[0] ); + } - for( i=woptind+1; i2)?(argv+2):(argv+1), wcstring_list_t()); - - res = reader_read( fd, real_io ? *real_io : io_chain_t() ); - - parser.pop_block(); - - if( res ) - { - append_format(stderr_buffer, - _( L"%ls: Error while reading file '%ls'\n" ), - argv[0], - fn_intern == intern_static(L"-") ? L"" : fn_intern ); - } - else - { - res = proc_get_last_status(); - } - - /* - Do not close fd after calling reader_read. reader_read - automatically closes it before calling eval. - */ - - reader_pop_current_filename(); + if( !S_ISREG(buf.st_mode) ) + { + close(fd); + append_format(stderr_buffer, _( L"%ls: '%ls' is not a file\n" ), argv[0], argv[1] ); + return STATUS_BUILTIN_ERROR; + } - return res; + fn = wrealpath( argv[1], 0 ); + + if( !fn ) + { + fn_intern = intern( argv[1] ); + } + else + { + fn_intern = intern(fn); + free( (void *)fn ); + } + + } + + parser.push_block( new source_block_t(fn_intern) ); + reader_push_current_filename( fn_intern ); + + parse_util_set_argv( (argc>2)?(argv+2):(argv+1), wcstring_list_t()); + + res = reader_read( fd, real_io ? *real_io : io_chain_t() ); + + parser.pop_block(); + + if( res ) + { + append_format(stderr_buffer, + _( L"%ls: Error while reading file '%ls'\n" ), + argv[0], + fn_intern == intern_static(L"-") ? L"" : fn_intern ); + } + else + { + res = proc_get_last_status(); + } + + /* + Do not close fd after calling reader_read. reader_read + automatically closes it before calling eval. + */ + + reader_pop_current_filename(); + + return res; } /** @@ -3059,146 +3059,146 @@ static void make_first( job_t *j ) */ static int builtin_fg( parser_t &parser, wchar_t **argv ) { - job_t *j=NULL; + job_t *j=NULL; + + if( argv[1] == 0 ) + { + /* + Select last constructed job (I.e. first job in the job que) + that is possible to put in the foreground + */ - if( argv[1] == 0 ) - { - /* - Select last constructed job (I.e. first job in the job que) - that is possible to put in the foreground - */ - job_iterator_t jobs; while ((j = jobs.next())) - { - if( job_get_flag( j, JOB_CONSTRUCTED ) && (!job_is_completed(j)) && - ( (job_is_stopped(j) || (!job_get_flag(j, JOB_FOREGROUND)) ) && job_get_flag( j, JOB_CONTROL) ) ) - { - break; - } - } - if( !j ) - { - append_format(stderr_buffer, - _( L"%ls: There are no suitable jobs\n" ), - argv[0] ); - builtin_print_help( parser, argv[0], stderr_buffer ); - } - } - else if( argv[2] != 0 ) - { - /* - Specifying what more than one job to put to the foreground - is a syntax error, we still try to locate the job argv[1], - since we want to know if this is an ambigous job - specification or if this is an malformed job id - */ - wchar_t *endptr; - int pid; - int found_job = 0; - - errno = 0; - pid = fish_wcstoi( argv[1], &endptr, 10 ); - if( !( *endptr || errno ) ) - { - j = job_get_from_pid( pid ); - if( j ) - found_job = 1; - } - - if( found_job ) - { - append_format(stderr_buffer, - _( L"%ls: Ambiguous job\n" ), - argv[0] ); - } - else - { - append_format(stderr_buffer, - _( L"%ls: '%ls' is not a job\n" ), - argv[0], - argv[1] ); - } + { + if( job_get_flag( j, JOB_CONSTRUCTED ) && (!job_is_completed(j)) && + ( (job_is_stopped(j) || (!job_get_flag(j, JOB_FOREGROUND)) ) && job_get_flag( j, JOB_CONTROL) ) ) + { + break; + } + } + if( !j ) + { + append_format(stderr_buffer, + _( L"%ls: There are no suitable jobs\n" ), + argv[0] ); + builtin_print_help( parser, argv[0], stderr_buffer ); + } + } + else if( argv[2] != 0 ) + { + /* + Specifying what more than one job to put to the foreground + is a syntax error, we still try to locate the job argv[1], + since we want to know if this is an ambigous job + specification or if this is an malformed job id + */ + wchar_t *endptr; + int pid; + int found_job = 0; - builtin_print_help( parser, argv[0], stderr_buffer ); + errno = 0; + pid = fish_wcstoi( argv[1], &endptr, 10 ); + if( !( *endptr || errno ) ) + { + j = job_get_from_pid( pid ); + if( j ) + found_job = 1; + } - j=0; + if( found_job ) + { + append_format(stderr_buffer, + _( L"%ls: Ambiguous job\n" ), + argv[0] ); + } + else + { + append_format(stderr_buffer, + _( L"%ls: '%ls' is not a job\n" ), + argv[0], + argv[1] ); + } - } - else - { - wchar_t *end; - int pid; - errno = 0; - pid = abs(fish_wcstoi( argv[1], &end, 10 )); - - if( *end || errno ) - { - append_format(stderr_buffer, - BUILTIN_ERR_NOT_NUMBER, - argv[0], - argv[1] ); - builtin_print_help( parser, argv[0], stderr_buffer ); - } - else - { - j = job_get_from_pid( pid ); - if( !j || !job_get_flag( j, JOB_CONSTRUCTED ) || job_is_completed( j )) - { - append_format(stderr_buffer, - _( L"%ls: No suitable job: %d\n" ), - argv[0], - pid ); - builtin_print_help( parser, argv[0], stderr_buffer ); - j=0; - } - else if( !job_get_flag( j, JOB_CONTROL) ) - { - append_format(stderr_buffer, - _( L"%ls: Can't put job %d, '%ls' to foreground because it is not under job control\n" ), - argv[0], - pid, - j->command_wcstr() ); - builtin_print_help( parser, argv[0], stderr_buffer ); - j=0; - } - } - } + builtin_print_help( parser, argv[0], stderr_buffer ); - if( j ) - { - if( builtin_err_redirect ) - { - append_format(stderr_buffer, - FG_MSG, - j->job_id, - j->command_wcstr() ); - } - else - { - /* - If we aren't redirecting, send output to real stderr, - since stuff in sb_err won't get printed until the - command finishes. - */ - fwprintf( stderr, - FG_MSG, - j->job_id, - j->command_wcstr() ); - } + j=0; - wchar_t *ft = tok_first( j->command_wcstr() ); - if( ft != 0 ) - env_set( L"_", ft, ENV_EXPORT ); - free(ft); - reader_write_title(); + } + else + { + wchar_t *end; + int pid; + errno = 0; + pid = abs(fish_wcstoi( argv[1], &end, 10 )); - make_first( j ); - job_set_flag( j, JOB_FOREGROUND, 1 ); + if( *end || errno ) + { + append_format(stderr_buffer, + BUILTIN_ERR_NOT_NUMBER, + argv[0], + argv[1] ); + builtin_print_help( parser, argv[0], stderr_buffer ); + } + else + { + j = job_get_from_pid( pid ); + if( !j || !job_get_flag( j, JOB_CONSTRUCTED ) || job_is_completed( j )) + { + append_format(stderr_buffer, + _( L"%ls: No suitable job: %d\n" ), + argv[0], + pid ); + builtin_print_help( parser, argv[0], stderr_buffer ); + j=0; + } + else if( !job_get_flag( j, JOB_CONTROL) ) + { + append_format(stderr_buffer, + _( L"%ls: Can't put job %d, '%ls' to foreground because it is not under job control\n" ), + argv[0], + pid, + j->command_wcstr() ); + builtin_print_help( parser, argv[0], stderr_buffer ); + j=0; + } + } + } - job_continue( j, job_is_stopped(j) ); - } - return j != 0; + if( j ) + { + if( builtin_err_redirect ) + { + append_format(stderr_buffer, + FG_MSG, + j->job_id, + j->command_wcstr() ); + } + else + { + /* + If we aren't redirecting, send output to real stderr, + since stuff in sb_err won't get printed until the + command finishes. + */ + fwprintf( stderr, + FG_MSG, + j->job_id, + j->command_wcstr() ); + } + + wchar_t *ft = tok_first( j->command_wcstr() ); + if( ft != 0 ) + env_set( L"_", ft, ENV_EXPORT ); + free(ft); + reader_write_title(); + + make_first( j ); + job_set_flag( j, JOB_FOREGROUND, 1 ); + + job_continue( j, job_is_stopped(j) ); + } + return j != 0; } /** @@ -3206,36 +3206,36 @@ static int builtin_fg( parser_t &parser, wchar_t **argv ) */ static int send_to_bg( parser_t &parser, job_t *j, const wchar_t *name ) { - if( j == 0 ) - { - append_format(stderr_buffer, - _( L"%ls: Unknown job '%ls'\n" ), - L"bg", - name ); - builtin_print_help( parser, L"bg", stderr_buffer ); - return STATUS_BUILTIN_ERROR; - } - else if( !job_get_flag( j, JOB_CONTROL ) ) - { - append_format(stderr_buffer, - _( L"%ls: Can't put job %d, '%ls' to background because it is not under job control\n" ), - L"bg", - j->job_id, - j->command_wcstr() ); - builtin_print_help( parser, L"bg", stderr_buffer ); - return STATUS_BUILTIN_ERROR; - } - else - { - append_format(stderr_buffer, - _(L"Send job %d '%ls' to background\n"), - j->job_id, - j->command_wcstr() ); - } - make_first( j ); - job_set_flag( j, JOB_FOREGROUND, 0 ); - job_continue( j, job_is_stopped(j) ); - return STATUS_BUILTIN_OK; + if( j == 0 ) + { + append_format(stderr_buffer, + _( L"%ls: Unknown job '%ls'\n" ), + L"bg", + name ); + builtin_print_help( parser, L"bg", stderr_buffer ); + return STATUS_BUILTIN_ERROR; + } + else if( !job_get_flag( j, JOB_CONTROL ) ) + { + append_format(stderr_buffer, + _( L"%ls: Can't put job %d, '%ls' to background because it is not under job control\n" ), + L"bg", + j->job_id, + j->command_wcstr() ); + builtin_print_help( parser, L"bg", stderr_buffer ); + return STATUS_BUILTIN_ERROR; + } + else + { + append_format(stderr_buffer, + _(L"Send job %d '%ls' to background\n"), + j->job_id, + j->command_wcstr() ); + } + make_first( j ); + job_set_flag( j, JOB_FOREGROUND, 0 ); + job_continue( j, job_is_stopped(j) ); + return STATUS_BUILTIN_OK; } @@ -3244,65 +3244,65 @@ static int send_to_bg( parser_t &parser, job_t *j, const wchar_t *name ) */ static int builtin_bg( parser_t &parser, wchar_t **argv ) { - int res = STATUS_BUILTIN_OK; + int res = STATUS_BUILTIN_OK; - if( argv[1] == 0 ) - { - job_t *j; + if( argv[1] == 0 ) + { + job_t *j; job_iterator_t jobs; while ((j = jobs.next())) { - if( job_is_stopped(j) && job_get_flag( j, JOB_CONTROL ) && (!job_is_completed(j)) ) - { - break; - } - } - - if( !j ) - { - append_format(stderr_buffer, - _( L"%ls: There are no suitable jobs\n" ), - argv[0] ); - res = 1; - } - else - { - res = send_to_bg( parser, j, _(L"(default)" ) ); - } - } - else - { - wchar_t *end; - int i; - int pid; - int err = 0; - - for( i=1; argv[i]; i++ ) - { - errno=0; - pid = fish_wcstoi( argv[i], &end, 10 ); - if( errno || pid < 0 || *end || !job_get_from_pid( pid ) ) - { - append_format(stderr_buffer, - _( L"%ls: '%ls' is not a job\n" ), - argv[0], - argv[i] ); - err = 1; - break; - } - } + if( job_is_stopped(j) && job_get_flag( j, JOB_CONTROL ) && (!job_is_completed(j)) ) + { + break; + } + } - if( !err ) - { - for( i=1; !res && argv[i]; i++ ) - { - pid = fish_wcstoi( argv[i], 0, 10 ); - res |= send_to_bg( parser, job_get_from_pid( pid ), *argv); - } - } - } - - return res; + if( !j ) + { + append_format(stderr_buffer, + _( L"%ls: There are no suitable jobs\n" ), + argv[0] ); + res = 1; + } + else + { + res = send_to_bg( parser, j, _(L"(default)" ) ); + } + } + else + { + wchar_t *end; + int i; + int pid; + int err = 0; + + for( i=1; argv[i]; i++ ) + { + errno=0; + pid = fish_wcstoi( argv[i], &end, 10 ); + if( errno || pid < 0 || *end || !job_get_from_pid( pid ) ) + { + append_format(stderr_buffer, + _( L"%ls: '%ls' is not a job\n" ), + argv[0], + argv[i] ); + err = 1; + break; + } + } + + if( !err ) + { + for( i=1; !res && argv[i]; i++ ) + { + pid = fish_wcstoi( argv[i], 0, 10 ); + res |= send_to_bg( parser, job_get_from_pid( pid ), *argv); + } + } + } + + return res; } @@ -3311,65 +3311,65 @@ static int builtin_bg( parser_t &parser, wchar_t **argv ) */ static int builtin_for( parser_t &parser, wchar_t **argv ) { - int argc = builtin_count_args( argv ); - int res=STATUS_BUILTIN_ERROR; + int argc = builtin_count_args( argv ); + int res=STATUS_BUILTIN_ERROR; - if( argc < 3) - { - append_format(stderr_buffer, - BUILTIN_FOR_ERR_COUNT, - argv[0] , - argc ); - builtin_print_help( parser, argv[0], stderr_buffer ); - } - else if ( wcsvarname(argv[1]) ) - { - append_format(stderr_buffer, - BUILTIN_FOR_ERR_NAME, - argv[0], - argv[1] ); - builtin_print_help( parser, argv[0], stderr_buffer ); - } - else if (wcscmp( argv[2], L"in") != 0 ) - { - append_format(stderr_buffer, - BUILTIN_FOR_ERR_IN, - argv[0] ); - builtin_print_help( parser, argv[0], stderr_buffer ); - } - else - { - res=0; - } + if( argc < 3) + { + append_format(stderr_buffer, + BUILTIN_FOR_ERR_COUNT, + argv[0] , + argc ); + builtin_print_help( parser, argv[0], stderr_buffer ); + } + else if ( wcsvarname(argv[1]) ) + { + append_format(stderr_buffer, + BUILTIN_FOR_ERR_NAME, + argv[0], + argv[1] ); + builtin_print_help( parser, argv[0], stderr_buffer ); + } + else if (wcscmp( argv[2], L"in") != 0 ) + { + append_format(stderr_buffer, + BUILTIN_FOR_ERR_IN, + argv[0] ); + builtin_print_help( parser, argv[0], stderr_buffer ); + } + else + { + res=0; + } - if( res ) - { - parser.push_block( new fake_block_t() ); - } - else - { + if( res ) + { + parser.push_block( new fake_block_t() ); + } + else + { const wchar_t *for_variable = argv[1]; for_block_t *fb = new for_block_t(for_variable); - parser.push_block( fb ); - fb->tok_pos = parser.get_pos(); + parser.push_block( fb ); + fb->tok_pos = parser.get_pos(); /* Note that we store the sequence of values in opposite order */ wcstring_list_t &for_vars = fb->sequence; - for( int i=argc-1; i>3; i-- ) + for( int i=argc-1; i>3; i-- ) for_vars.push_back(argv[i]); - if( argc > 3 ) - { - env_set( for_variable, argv[3], ENV_LOCAL ); - } - else - { - parser.current_block->skip=1; - } - } - return res; + if( argc > 3 ) + { + env_set( for_variable, argv[3], ENV_LOCAL ); + } + else + { + parser.current_block->skip=1; + } + } + return res; } /** @@ -3377,9 +3377,9 @@ static int builtin_for( parser_t &parser, wchar_t **argv ) */ static int builtin_begin( parser_t &parser, wchar_t **argv ) { - parser.push_block( new scope_block_t(BEGIN) ); - parser.current_block->tok_pos = parser.get_pos(); - return proc_get_last_status(); + parser.push_block( new scope_block_t(BEGIN) ); + parser.current_block->tok_pos = parser.get_pos(); + return proc_get_last_status(); } @@ -3390,93 +3390,93 @@ static int builtin_begin( parser_t &parser, wchar_t **argv ) */ static int builtin_end( parser_t &parser, wchar_t **argv ) { - if( !parser.current_block->outer ) - { - append_format(stderr_buffer, - _( L"%ls: Not inside of block\n" ), - argv[0] ); + if( !parser.current_block->outer ) + { + append_format(stderr_buffer, + _( L"%ls: Not inside of block\n" ), + argv[0] ); - builtin_print_help( parser, argv[0], stderr_buffer ); - return STATUS_BUILTIN_ERROR; - } - else - { - /** - By default, 'end' kills the current block scope. But if we - are rewinding a loop, this should be set to false, so that - variables in the current loop scope won't die between laps. - */ - int kill_block = 1; + builtin_print_help( parser, argv[0], stderr_buffer ); + return STATUS_BUILTIN_ERROR; + } + else + { + /** + By default, 'end' kills the current block scope. But if we + are rewinding a loop, this should be set to false, so that + variables in the current loop scope won't die between laps. + */ + int kill_block = 1; - switch( parser.current_block->type() ) - { - case WHILE: - { - /* - If this is a while loop, we rewind the loop unless - it's the last lap, in which case we continue. - */ - if( !( parser.current_block->skip && (parser.current_block->loop_status != LOOP_CONTINUE ))) - { - parser.current_block->loop_status = LOOP_NORMAL; - parser.current_block->skip = 0; - kill_block = 0; - parser.set_pos( parser.current_block->tok_pos); + switch( parser.current_block->type() ) + { + case WHILE: + { + /* + If this is a while loop, we rewind the loop unless + it's the last lap, in which case we continue. + */ + if( !( parser.current_block->skip && (parser.current_block->loop_status != LOOP_CONTINUE ))) + { + parser.current_block->loop_status = LOOP_NORMAL; + parser.current_block->skip = 0; + kill_block = 0; + parser.set_pos( parser.current_block->tok_pos); while_block_t *blk = static_cast(parser.current_block); blk->status = WHILE_TEST_AGAIN; - } + } - break; - } + break; + } - case IF: - case SUBST: - case BEGIN: + case IF: + case SUBST: + case BEGIN: case SWITCH: case FAKE: - /* - Nothing special happens at the end of these commands. The scope just ends. - */ + /* + Nothing special happens at the end of these commands. The scope just ends. + */ - break; + break; - case FOR: - { - /* - set loop variable to next element, and rewind to the beginning of the block. - */ + case FOR: + { + /* + set loop variable to next element, and rewind to the beginning of the block. + */ for_block_t *fb = static_cast(parser.current_block); wcstring_list_t &for_vars = fb->sequence; - if( parser.current_block->loop_status == LOOP_BREAK ) - { + if( parser.current_block->loop_status == LOOP_BREAK ) + { for_vars.clear(); - } + } - if( ! for_vars.empty() ) - { + if( ! for_vars.empty() ) + { const wcstring val = for_vars.back(); for_vars.pop_back(); const wcstring &for_variable = fb->variable; - env_set( for_variable.c_str(), val.c_str(), ENV_LOCAL); - parser.current_block->loop_status = LOOP_NORMAL; - parser.current_block->skip = 0; - - kill_block = 0; - parser.set_pos( parser.current_block->tok_pos ); - } - break; - } + env_set( for_variable.c_str(), val.c_str(), ENV_LOCAL); + parser.current_block->loop_status = LOOP_NORMAL; + parser.current_block->skip = 0; + + kill_block = 0; + parser.set_pos( parser.current_block->tok_pos ); + } + break; + } + + case FUNCTION_DEF: + { + function_def_block_t *fdb = static_cast(parser.current_block); + function_data_t &d = fdb->function_data; - case FUNCTION_DEF: - { - function_def_block_t *fdb = static_cast(parser.current_block); - function_data_t &d = fdb->function_data; - if (d.name.empty()) { /* Disallow empty function names */ append_format(stderr_buffer, _( L"%ls: No function name given\n" ), argv[0] ); - + /* Return an error via a crummy way. Don't just return here, since we need to pop the block. */ proc_set_last_status(STATUS_BUILTIN_ERROR); } @@ -3491,28 +3491,28 @@ static int builtin_end( parser_t &parser, wchar_t **argv ) wchar_t *def = wcsndup( parser.get_buffer()+parser.current_block->tok_pos, parser.get_job_pos()-parser.current_block->tok_pos ); d.definition = def; - - function_add( d, parser ); + + function_add( d, parser ); free( def ); } - } - break; - + } + break; + default: assert(false); //should never get here break; - } - if( kill_block ) - { - parser.pop_block(); - } + } + if( kill_block ) + { + parser.pop_block(); + } - /* - If everything goes ok, return status of last command to execute. - */ - return proc_get_last_status(); - } + /* + If everything goes ok, return status of last command to execute. + */ + return proc_get_last_status(); + } } /** @@ -3531,29 +3531,29 @@ static int builtin_else( parser_t &parser, wchar_t **argv ) block_ok = true; } } - - if( ! block_ok ) - { - append_format(stderr_buffer, - _( L"%ls: Not inside of 'if' block\n" ), - argv[0] ); - builtin_print_help( parser, argv[0], stderr_buffer ); - return STATUS_BUILTIN_ERROR; - } - else - { + + if( ! block_ok ) + { + append_format(stderr_buffer, + _( L"%ls: Not inside of 'if' block\n" ), + argv[0] ); + builtin_print_help( parser, argv[0], stderr_buffer ); + return STATUS_BUILTIN_ERROR; + } + else + { /* Run the else block if the IF expression was false and so were all the ELSEIF expressions (if any) */ bool run_else = ! if_block->any_branch_taken; - if_block->skip = ! run_else; + if_block->skip = ! run_else; if_block->else_evaluated = true; - env_pop(); - env_push(0); - } + env_pop(); + env_push(0); + } - /* - If everything goes ok, return status of last command to execute. - */ - return proc_get_last_status(); + /* + If everything goes ok, return status of last command to execute. + */ + return proc_get_last_status(); } /** @@ -3562,49 +3562,49 @@ static int builtin_else( parser_t &parser, wchar_t **argv ) */ static int builtin_break_continue( parser_t &parser, wchar_t **argv ) { - int is_break = (wcscmp(argv[0],L"break")==0); - int argc = builtin_count_args( argv ); + int is_break = (wcscmp(argv[0],L"break")==0); + int argc = builtin_count_args( argv ); - block_t *b = parser.current_block; + block_t *b = parser.current_block; - if( argc != 1 ) - { - append_format(stderr_buffer, - BUILTIN_ERR_UNKNOWN, - argv[0], - argv[1] ); + if( argc != 1 ) + { + append_format(stderr_buffer, + BUILTIN_ERR_UNKNOWN, + argv[0], + argv[1] ); - builtin_print_help( parser, argv[0], stderr_buffer ); - return STATUS_BUILTIN_ERROR; - } + builtin_print_help( parser, argv[0], stderr_buffer ); + return STATUS_BUILTIN_ERROR; + } - while( (b != 0) && - ( b->type() != WHILE) && - (b->type() != FOR ) ) - { - b = b->outer; - } + while( (b != 0) && + ( b->type() != WHILE) && + (b->type() != FOR ) ) + { + b = b->outer; + } - if( b == 0 ) - { - append_format(stderr_buffer, - _( L"%ls: Not inside of loop\n" ), - argv[0] ); - builtin_print_help( parser, argv[0], stderr_buffer ); - return STATUS_BUILTIN_ERROR; - } + if( b == 0 ) + { + append_format(stderr_buffer, + _( L"%ls: Not inside of loop\n" ), + argv[0] ); + builtin_print_help( parser, argv[0], stderr_buffer ); + return STATUS_BUILTIN_ERROR; + } - b = parser.current_block; - while( ( b->type() != WHILE) && - (b->type() != FOR ) ) - { - b->skip=1; - b = b->outer; - } - b->skip=1; - b->loop_status = is_break?LOOP_BREAK:LOOP_CONTINUE; - return STATUS_BUILTIN_OK; + b = parser.current_block; + while( ( b->type() != WHILE) && + (b->type() != FOR ) ) + { + b->skip=1; + b = b->outer; + } + b->skip=1; + b->loop_status = is_break?LOOP_BREAK:LOOP_CONTINUE; + return STATUS_BUILTIN_OK; } /** @@ -3614,13 +3614,13 @@ static int builtin_break_continue( parser_t &parser, wchar_t **argv ) static int builtin_breakpoint( parser_t &parser, wchar_t **argv ) { - parser.push_block( new breakpoint_block_t() ); - - reader_read( STDIN_FILENO, real_io ? *real_io : io_chain_t() ); - - parser.pop_block(); - - return proc_get_last_status(); + parser.push_block( new breakpoint_block_t() ); + + reader_read( STDIN_FILENO, real_io ? *real_io : io_chain_t() ); + + parser.pop_block(); + + return proc_get_last_status(); } @@ -3629,67 +3629,67 @@ static int builtin_breakpoint( parser_t &parser, wchar_t **argv ) */ static int builtin_return( parser_t &parser, wchar_t **argv ) { - int argc = builtin_count_args( argv ); - int status = proc_get_last_status(); + int argc = builtin_count_args( argv ); + int status = proc_get_last_status(); - block_t *b = parser.current_block; + block_t *b = parser.current_block; - switch( argc ) - { - case 1: - break; - case 2: - { - wchar_t *end; - errno = 0; - status = fish_wcstoi(argv[1],&end,10); - if( errno || *end != 0) - { - append_format(stderr_buffer, - _( L"%ls: Argument '%ls' must be an integer\n" ), - argv[0], - argv[1] ); - builtin_print_help( parser, argv[0], stderr_buffer ); - return STATUS_BUILTIN_ERROR; - } - break; - } - default: - append_format(stderr_buffer, - _( L"%ls: Too many arguments\n" ), - argv[0] ); - builtin_print_help( parser, argv[0], stderr_buffer ); - return STATUS_BUILTIN_ERROR; - } + switch( argc ) + { + case 1: + break; + case 2: + { + wchar_t *end; + errno = 0; + status = fish_wcstoi(argv[1],&end,10); + if( errno || *end != 0) + { + append_format(stderr_buffer, + _( L"%ls: Argument '%ls' must be an integer\n" ), + argv[0], + argv[1] ); + builtin_print_help( parser, argv[0], stderr_buffer ); + return STATUS_BUILTIN_ERROR; + } + break; + } + default: + append_format(stderr_buffer, + _( L"%ls: Too many arguments\n" ), + argv[0] ); + builtin_print_help( parser, argv[0], stderr_buffer ); + return STATUS_BUILTIN_ERROR; + } - while( (b != 0) && - ( b->type() != FUNCTION_CALL && - b->type() != FUNCTION_CALL_NO_SHADOW) ) - { - b = b->outer; - } + while( (b != 0) && + ( b->type() != FUNCTION_CALL && + b->type() != FUNCTION_CALL_NO_SHADOW) ) + { + b = b->outer; + } - if( b == 0 ) - { - append_format(stderr_buffer, - _( L"%ls: Not inside of function\n" ), - argv[0] ); - builtin_print_help( parser, argv[0], stderr_buffer ); - return STATUS_BUILTIN_ERROR; - } + if( b == 0 ) + { + append_format(stderr_buffer, + _( L"%ls: Not inside of function\n" ), + argv[0] ); + builtin_print_help( parser, argv[0], stderr_buffer ); + return STATUS_BUILTIN_ERROR; + } - b = parser.current_block; - while( ( b->type() != FUNCTION_CALL && - b->type() != FUNCTION_CALL_NO_SHADOW ) ) - { + b = parser.current_block; + while( ( b->type() != FUNCTION_CALL && + b->type() != FUNCTION_CALL_NO_SHADOW ) ) + { b->mark_as_fake(); - b->skip=1; - b = b->outer; - } - b->skip=1; + b->skip=1; + b = b->outer; + } + b->skip=1; - return status; + return status; } /** @@ -3698,28 +3698,28 @@ static int builtin_return( parser_t &parser, wchar_t **argv ) */ static int builtin_switch( parser_t &parser, wchar_t **argv ) { - int res=STATUS_BUILTIN_OK; - int argc = builtin_count_args( argv ); + int res=STATUS_BUILTIN_OK; + int argc = builtin_count_args( argv ); - if( argc != 2 ) - { - append_format(stderr_buffer, - _( L"%ls: Expected exactly one argument, got %d\n" ), - argv[0], - argc-1 ); + if( argc != 2 ) + { + append_format(stderr_buffer, + _( L"%ls: Expected exactly one argument, got %d\n" ), + argv[0], + argc-1 ); - builtin_print_help( parser, argv[0], stderr_buffer ); - res=1; - parser.push_block( new fake_block_t() ); - } - else - { - parser.push_block( new switch_block_t(argv[1]) ); - parser.current_block->skip=1; + builtin_print_help( parser, argv[0], stderr_buffer ); + res=1; + parser.push_block( new fake_block_t() ); + } + else + { + parser.push_block( new switch_block_t(argv[1]) ); + parser.current_block->skip=1; res = proc_get_last_status(); - } - - return res; + } + + return res; } /** @@ -3728,44 +3728,44 @@ static int builtin_switch( parser_t &parser, wchar_t **argv ) */ static int builtin_case( parser_t &parser, wchar_t **argv ) { - int argc = builtin_count_args( argv ); - int i; - wchar_t *unescaped=0; - - if( parser.current_block->type() != SWITCH ) - { - append_format(stderr_buffer, - _( L"%ls: 'case' command while not in switch block\n" ), - argv[0] ); - builtin_print_help( parser, argv[0], stderr_buffer ); - return STATUS_BUILTIN_ERROR; - } - - parser.current_block->skip = 1; - switch_block_t *sb = static_cast(parser.current_block); - if( sb->switch_taken ) - { - return proc_get_last_status(); - } - + int argc = builtin_count_args( argv ); + int i; + wchar_t *unescaped=0; + + if( parser.current_block->type() != SWITCH ) + { + append_format(stderr_buffer, + _( L"%ls: 'case' command while not in switch block\n" ), + argv[0] ); + builtin_print_help( parser, argv[0], stderr_buffer ); + return STATUS_BUILTIN_ERROR; + } + + parser.current_block->skip = 1; + switch_block_t *sb = static_cast(parser.current_block); + if( sb->switch_taken ) + { + return proc_get_last_status(); + } + const wcstring &switch_value = sb->switch_value; - for( i=1; iskip = 0; - sb->switch_taken = true; - break; - } - } - - return proc_get_last_status(); + for( i=1; iskip = 0; + sb->switch_taken = true; + break; + } + } + + return proc_get_last_status(); } @@ -3776,7 +3776,7 @@ static int builtin_history( parser_t &parser, wchar_t **argv ) { int argc = builtin_count_args(argv); - bool search_history = false; + bool search_history = false; bool delete_item = false; bool search_prefix = false; bool save_history = false; @@ -3798,11 +3798,11 @@ static int builtin_history( parser_t &parser, wchar_t **argv ) int opt_index = 0; woptind = 0; history_t *history = reader_get_history(); - + /* Use the default history if we have none (which happens if invoked non-interactively, e.g. from webconfig.py */ if (! history) history = &history_t::history_with_name(L"fish"); - + while((opt = wgetopt_long_only( argc, argv, L"pdscvl", long_options, &opt_index )) != -1) { switch(opt) @@ -3823,7 +3823,7 @@ static int builtin_history( parser_t &parser, wchar_t **argv ) break; case 'l': clear_history = true; - break; + break; case 'h': builtin_print_help( parser, argv[0], stdout_buffer ); return STATUS_BUILTIN_OK; @@ -3839,7 +3839,7 @@ static int builtin_history( parser_t &parser, wchar_t **argv ) append_format(stderr_buffer, BUILTIN_ERR_UNKNOWN, argv[0], argv[woptind-1]); return STATUS_BUILTIN_ERROR; } - } + } /* Everything after is an argument */ const wcstring_list_t args(argv + woptind, argv + argc); @@ -3869,7 +3869,7 @@ static int builtin_history( parser_t &parser, wchar_t **argv ) while (searcher.go_backwards()) { stdout_buffer.append(searcher.current_string()); - stdout_buffer.append(L"\n"); + stdout_buffer.append(L"\n"); res = STATUS_BUILTIN_OK; } } @@ -3883,7 +3883,7 @@ static int builtin_history( parser_t &parser, wchar_t **argv ) wcstring delete_string = *iter; if (delete_string[0] == '"' && delete_string[delete_string.length() - 1] == '"') delete_string = delete_string.substr(1, delete_string.length() - 2); - + history->remove(delete_string); } return STATUS_BUILTIN_OK; @@ -3919,48 +3919,48 @@ static int builtin_history( parser_t &parser, wchar_t **argv ) */ static const builtin_data_t builtin_datas[]= { - { L".", &builtin_source, N_( L"Evaluate contents of file" ) }, - { L"and", &builtin_generic, N_( L"Execute command if previous command suceeded" ) }, - { L"begin", &builtin_begin, N_( L"Create a block of code" ) }, - { L"bg", &builtin_bg, N_( L"Send job to background" ) }, - { L"bind", &builtin_bind, N_( L"Handle fish key bindings" ) }, - { L"block", &builtin_block, N_( L"Temporarily block delivery of events" ) }, - { L"break", &builtin_break_continue, N_( L"Stop the innermost loop" ) }, - { L"breakpoint", &builtin_breakpoint, N_( L"Temporarily halt execution of a script and launch an interactive debug prompt" ) }, - { L"builtin", &builtin_builtin, N_( L"Run a builtin command instead of a function" ) }, - { L"case", &builtin_case, N_( L"Conditionally execute a block of commands" ) }, - { L"cd", &builtin_cd, N_( L"Change working directory" ) }, - { L"command", &builtin_generic, N_( L"Run a program instead of a function or builtin" ) }, - { L"commandline", &builtin_commandline, N_( L"Set or get the commandline" ) }, - { L"complete", &builtin_complete, N_( L"Edit command specific completions" ) }, - { L"contains", &builtin_contains, N_( L"Search for a specified string in a list" ) }, - { L"continue", &builtin_break_continue, N_( L"Skip the rest of the current lap of the innermost loop" ) }, - { L"count", &builtin_count, N_( L"Count the number of arguments" ) }, - { L"echo", &builtin_echo, N_( L"Print arguments" ) }, - { L"else", &builtin_else, N_( L"Evaluate block if condition is false" ) }, - { L"emit", &builtin_emit, N_( L"Emit an event" ) }, - { L"end", &builtin_end, N_( L"End a block of commands" ) }, - { L"exec", &builtin_generic, N_( L"Run command in current process" ) }, - { L"exit", &builtin_exit, N_( L"Exit the shell" ) }, - { L"fg", &builtin_fg, N_( L"Send job to foreground" ) }, - { L"for", &builtin_for, N_( L"Perform a set of commands multiple times" ) }, - { L"function", &builtin_function, N_( L"Define a new function" ) }, - { L"functions", &builtin_functions, N_( L"List or remove functions" ) }, - { L"history", &builtin_history, N_( L"History of commands executed by user" ) }, - { L"if", &builtin_generic, N_( L"Evaluate block if condition is true" ) }, - { L"jobs", &builtin_jobs, N_( L"Print currently running jobs" ) }, - { L"not", &builtin_generic, N_( L"Negate exit status of job" ) }, - { L"or", &builtin_generic, N_( L"Execute command if previous command failed" ) }, - { L"pwd", &builtin_pwd, N_( L"Print the working directory" ) }, - { L"random", &builtin_random, N_( L"Generate random number" ) }, - { L"read", &builtin_read, N_( L"Read a line of input into variables" ) }, - { L"return", &builtin_return, N_( L"Stop the currently evaluated function" ) }, - { L"set", &builtin_set, N_( L"Handle environment variables" ) }, - { L"status", &builtin_status, N_( L"Return status information about fish" ) }, - { L"switch", &builtin_switch, N_( L"Conditionally execute a block of commands" ) }, - { L"test", &builtin_test, N_( L"Test a condition" ) }, - { L"ulimit", &builtin_ulimit, N_( L"Set or get the shells resource usage limits" ) }, - { L"while", &builtin_generic, N_( L"Perform a command multiple times" ) } + { L".", &builtin_source, N_( L"Evaluate contents of file" ) }, + { L"and", &builtin_generic, N_( L"Execute command if previous command suceeded" ) }, + { L"begin", &builtin_begin, N_( L"Create a block of code" ) }, + { L"bg", &builtin_bg, N_( L"Send job to background" ) }, + { L"bind", &builtin_bind, N_( L"Handle fish key bindings" ) }, + { L"block", &builtin_block, N_( L"Temporarily block delivery of events" ) }, + { L"break", &builtin_break_continue, N_( L"Stop the innermost loop" ) }, + { L"breakpoint", &builtin_breakpoint, N_( L"Temporarily halt execution of a script and launch an interactive debug prompt" ) }, + { L"builtin", &builtin_builtin, N_( L"Run a builtin command instead of a function" ) }, + { L"case", &builtin_case, N_( L"Conditionally execute a block of commands" ) }, + { L"cd", &builtin_cd, N_( L"Change working directory" ) }, + { L"command", &builtin_generic, N_( L"Run a program instead of a function or builtin" ) }, + { L"commandline", &builtin_commandline, N_( L"Set or get the commandline" ) }, + { L"complete", &builtin_complete, N_( L"Edit command specific completions" ) }, + { L"contains", &builtin_contains, N_( L"Search for a specified string in a list" ) }, + { L"continue", &builtin_break_continue, N_( L"Skip the rest of the current lap of the innermost loop" ) }, + { L"count", &builtin_count, N_( L"Count the number of arguments" ) }, + { L"echo", &builtin_echo, N_( L"Print arguments" ) }, + { L"else", &builtin_else, N_( L"Evaluate block if condition is false" ) }, + { L"emit", &builtin_emit, N_( L"Emit an event" ) }, + { L"end", &builtin_end, N_( L"End a block of commands" ) }, + { L"exec", &builtin_generic, N_( L"Run command in current process" ) }, + { L"exit", &builtin_exit, N_( L"Exit the shell" ) }, + { L"fg", &builtin_fg, N_( L"Send job to foreground" ) }, + { L"for", &builtin_for, N_( L"Perform a set of commands multiple times" ) }, + { L"function", &builtin_function, N_( L"Define a new function" ) }, + { L"functions", &builtin_functions, N_( L"List or remove functions" ) }, + { L"history", &builtin_history, N_( L"History of commands executed by user" ) }, + { L"if", &builtin_generic, N_( L"Evaluate block if condition is true" ) }, + { L"jobs", &builtin_jobs, N_( L"Print currently running jobs" ) }, + { L"not", &builtin_generic, N_( L"Negate exit status of job" ) }, + { L"or", &builtin_generic, N_( L"Execute command if previous command failed" ) }, + { L"pwd", &builtin_pwd, N_( L"Print the working directory" ) }, + { L"random", &builtin_random, N_( L"Generate random number" ) }, + { L"read", &builtin_read, N_( L"Read a line of input into variables" ) }, + { L"return", &builtin_return, N_( L"Stop the currently evaluated function" ) }, + { L"set", &builtin_set, N_( L"Handle environment variables" ) }, + { L"status", &builtin_status, N_( L"Return status information about fish" ) }, + { L"switch", &builtin_switch, N_( L"Conditionally execute a block of commands" ) }, + { L"test", &builtin_test, N_( L"Test a condition" ) }, + { L"ulimit", &builtin_ulimit, N_( L"Set or get the shells resource usage limits" ) }, + { L"while", &builtin_generic, N_( L"Perform a command multiple times" ) } }; #define BUILTIN_COUNT (sizeof builtin_datas / sizeof *builtin_datas) @@ -3977,12 +3977,12 @@ static const builtin_data_t *builtin_lookup(const wcstring &name) { void builtin_init() { - - wopterr = 0; - for( size_t i=0; i < BUILTIN_COUNT; i++ ) - { - intern_static( builtin_datas[i].name ); - } + + wopterr = 0; + for( size_t i=0; i < BUILTIN_COUNT; i++ ) + { + intern_static( builtin_datas[i].name ); + } } void builtin_destroy() @@ -3991,7 +3991,7 @@ void builtin_destroy() int builtin_exists( const wcstring &cmd ) { - return !!builtin_lookup(cmd); + return !!builtin_lookup(cmd); } /** @@ -4000,45 +4000,45 @@ int builtin_exists( const wcstring &cmd ) */ static int internal_help( const wchar_t *cmd ) { - CHECK( cmd, 0 ); - return contains( cmd, L"for", L"while", L"function", - L"if", L"end", L"switch", L"case", L"count" ); + CHECK( cmd, 0 ); + return contains( cmd, L"for", L"while", L"function", + L"if", L"end", L"switch", L"case", L"count" ); } int builtin_run( parser_t &parser, const wchar_t * const *argv, const io_chain_t &io ) { - int (*cmd)(parser_t &parser, const wchar_t * const *argv)=0; - real_io = &io; - - CHECK( argv, STATUS_BUILTIN_ERROR ); - CHECK( argv[0], STATUS_BUILTIN_ERROR ); - + int (*cmd)(parser_t &parser, const wchar_t * const *argv)=0; + real_io = &io; + + CHECK( argv, STATUS_BUILTIN_ERROR ); + CHECK( argv[0], STATUS_BUILTIN_ERROR ); + const builtin_data_t *data = builtin_lookup(argv[0]); - cmd = (int (*)(parser_t &parser, const wchar_t * const*))(data ? data->func : NULL); - - if( argv[1] != 0 && !internal_help(argv[0]) ) - { - if( argv[2] == 0 && (parser.is_help( argv[1], 0 ) ) ) - { - builtin_print_help( parser, argv[0], stdout_buffer ); - return STATUS_BUILTIN_OK; - } - } + cmd = (int (*)(parser_t &parser, const wchar_t * const*))(data ? data->func : NULL); - if( data != NULL ) - { - int status; + if( argv[1] != 0 && !internal_help(argv[0]) ) + { + if( argv[2] == 0 && (parser.is_help( argv[1], 0 ) ) ) + { + builtin_print_help( parser, argv[0], stdout_buffer ); + return STATUS_BUILTIN_OK; + } + } - status = cmd(parser, argv); - return status; + if( data != NULL ) + { + int status; - } - else - { - debug( 0, _( L"Unknown builtin '%ls'" ), argv[0] ); - } - return STATUS_BUILTIN_ERROR; + status = cmd(parser, argv); + return status; + + } + else + { + debug( 0, _( L"Unknown builtin '%ls'" ), argv[0] ); + } + return STATUS_BUILTIN_ERROR; } @@ -4053,15 +4053,15 @@ wcstring_list_t builtin_get_names(void) } void builtin_get_names(std::vector &list) { - for (size_t i=0; i < BUILTIN_COUNT; i++) { - list.push_back(completion_t(builtin_datas[i].name)); - } + for (size_t i=0; i < BUILTIN_COUNT; i++) { + list.push_back(completion_t(builtin_datas[i].name)); + } } wcstring builtin_get_desc( const wcstring &name ) { wcstring result; - const builtin_data_t *builtin = builtin_lookup(name); + const builtin_data_t *builtin = builtin_lookup(name); if (builtin) { result = _(builtin->desc); } @@ -4071,12 +4071,12 @@ wcstring builtin_get_desc( const wcstring &name ) void builtin_push_io( parser_t &parser, int in ) { ASSERT_IS_MAIN_THREAD(); - if( builtin_stdin != -1 ) - { + if( builtin_stdin != -1 ) + { struct io_stack_elem_t elem = {builtin_stdin, stdout_buffer, stderr_buffer}; io_stack.push(elem); - } - builtin_stdin = in; + } + builtin_stdin = in; stdout_buffer.clear(); stderr_buffer.clear(); } @@ -4084,20 +4084,20 @@ void builtin_push_io( parser_t &parser, int in ) void builtin_pop_io(parser_t &parser) { ASSERT_IS_MAIN_THREAD(); - builtin_stdin = 0; - if( ! io_stack.empty() ) - { + builtin_stdin = 0; + if( ! io_stack.empty() ) + { struct io_stack_elem_t &elem = io_stack.top(); - stderr_buffer = elem.err; - stdout_buffer = elem.out; - builtin_stdin = elem.in; + stderr_buffer = elem.err; + stdout_buffer = elem.out; + builtin_stdin = elem.in; io_stack.pop(); - } - else - { + } + else + { stdout_buffer.clear(); stderr_buffer.clear(); - builtin_stdin = 0; - } + builtin_stdin = 0; + } } diff --git a/builtin.h b/builtin.h index e4683f001..cb6483382 100644 --- a/builtin.h +++ b/builtin.h @@ -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. diff --git a/builtin_commandline.cpp b/builtin_commandline.cpp index 3a0547d2f..212893848 100644 --- a/builtin_commandline.cpp +++ b/builtin_commandline.cpp @@ -1,6 +1,6 @@ /** \file builtin_commandline.c Functions defining the commandline builtin -Functions used for implementing the commandline builtin. +Functions used for implementing the commandline builtin. */ #include "config.h" @@ -34,23 +34,23 @@ Functions used for implementing the commandline builtin. */ enum { - STRING_MODE=1, /**< Operate on entire buffer */ - JOB_MODE, /**< Operate on job under cursor */ - PROCESS_MODE, /**< Operate on process under cursor */ - TOKEN_MODE /**< Operate on token under cursor */ + STRING_MODE=1, /**< Operate on entire buffer */ + JOB_MODE, /**< Operate on job under cursor */ + PROCESS_MODE, /**< Operate on process under cursor */ + TOKEN_MODE /**< Operate on token under cursor */ } - ; + ; /** For text insertion, how should it be done */ enum { - REPLACE_MODE=1, /**< Replace current text */ - INSERT_MODE, /**< Insert at cursor position */ - APPEND_MODE /**< Insert at end of current token/command/buffer */ + REPLACE_MODE=1, /**< Replace current text */ + INSERT_MODE, /**< Insert at cursor position */ + APPEND_MODE /**< Insert at end of current token/command/buffer */ } - ; + ; /** Pointer to what the commandline builtin considers to be the current @@ -68,7 +68,7 @@ static size_t current_cursor_pos = (size_t)(-1); */ static const wchar_t *get_buffer() { - return current_buffer; + return current_buffer; } /** @@ -76,7 +76,7 @@ static const wchar_t *get_buffer() */ static size_t get_cursor_pos() { - return current_cursor_pos; + return current_cursor_pos; } @@ -89,47 +89,47 @@ static size_t get_cursor_pos() \param append_mode can be one of REPLACE_MODE, INSERT_MODE or APPEND_MODE, affects the way the test update is performed */ static void replace_part( const wchar_t *begin, - const wchar_t *end, - const wchar_t *insert, - int append_mode ) + const wchar_t *end, + const wchar_t *insert, + int append_mode ) { - const wchar_t *buff = get_buffer(); - size_t out_pos = get_cursor_pos(); - + const wchar_t *buff = get_buffer(); + size_t out_pos = get_cursor_pos(); + wcstring out; out.append(buff, begin - buff); - - switch( append_mode) - { - case REPLACE_MODE: - { - - out.append(insert); - out_pos = wcslen( insert ) + (begin-buff); - break; - - } - case APPEND_MODE: - { - out.append( begin, end-begin ); - out.append( insert ); - break; - } - case INSERT_MODE: - { - long cursor = get_cursor_pos() -(begin-buff); - out.append( begin, cursor ); - out.append( insert ); - out.append( begin+cursor, end-begin-cursor ); - out_pos += wcslen( insert ); - break; - } - } - out.append( end ); - reader_set_buffer( out, out_pos ); + + switch( append_mode) + { + case REPLACE_MODE: + { + + out.append(insert); + out_pos = wcslen( insert ) + (begin-buff); + break; + + } + case APPEND_MODE: + { + out.append( begin, end-begin ); + out.append( insert ); + break; + } + case INSERT_MODE: + { + long cursor = get_cursor_pos() -(begin-buff); + out.append( begin, cursor ); + out.append( insert ); + out.append( begin+cursor, end-begin-cursor ); + out_pos += wcslen( insert ); + break; + } + } + out.append( end ); + reader_set_buffer( out, out_pos ); } - + /** Output the specified selection. @@ -138,62 +138,62 @@ static void replace_part( const wchar_t *begin, \param cut_at_cursor whether printing should stop at the surrent cursor position \param tokenize whether the string should be tokenized, printing one string token on every line and skipping non-string tokens */ -static void write_part( const wchar_t *begin, - const wchar_t *end, - int cut_at_cursor, - int tokenize ) -{ - tokenizer tok; - wcstring out; - wchar_t *buff; - size_t pos; +static void write_part( const wchar_t *begin, + const wchar_t *end, + int cut_at_cursor, + int tokenize ) +{ + tokenizer tok; + wcstring out; + wchar_t *buff; + size_t pos; - pos = get_cursor_pos()-(begin-get_buffer()); + pos = get_cursor_pos()-(begin-get_buffer()); - if( tokenize ) - { - buff = wcsndup( begin, end-begin ); -// fwprintf( stderr, L"Subshell: %ls, end char %lc\n", buff, *end ); - out.clear(); - - for( tok_init( &tok, buff, TOK_ACCEPT_UNFINISHED ); - tok_has_next( &tok ); - tok_next( &tok ) ) - { - if( (cut_at_cursor) && - (tok_get_pos( &tok)+wcslen(tok_last( &tok)) >= pos) ) - break; - - switch( tok_last_type( &tok ) ) - { - case TOK_STRING: - { + if( tokenize ) + { + buff = wcsndup( begin, end-begin ); +// fwprintf( stderr, L"Subshell: %ls, end char %lc\n", buff, *end ); + out.clear(); + + for( tok_init( &tok, buff, TOK_ACCEPT_UNFINISHED ); + tok_has_next( &tok ); + tok_next( &tok ) ) + { + if( (cut_at_cursor) && + (tok_get_pos( &tok)+wcslen(tok_last( &tok)) >= pos) ) + break; + + switch( tok_last_type( &tok ) ) + { + case TOK_STRING: + { out.append(escape_string(tok_last( &tok ), UNESCAPE_INCOMPLETE)); out.push_back(L'\n'); - break; - } - - } - } + break; + } + + } + } stdout_buffer.append(out); - - free( buff ); - tok_destroy( &tok ); - } - else - { - if( cut_at_cursor ) - { - end = begin+pos; - } -// debug( 0, L"woot2 %ls -> %ls", buff, esc ); + free( buff ); + tok_destroy( &tok ); + } + else + { + if( cut_at_cursor ) + { + end = begin+pos; + } + +// debug( 0, L"woot2 %ls -> %ls", buff, esc ); stdout_buffer.append(begin, end - begin); stdout_buffer.append(L"\n"); - - } + + } } @@ -204,443 +204,443 @@ static void write_part( const wchar_t *begin, static int builtin_commandline( parser_t &parser, wchar_t **argv ) { - int buffer_part=0; - int cut_at_cursor=0; - - int argc = builtin_count_args( argv ); - int append_mode=0; + int buffer_part=0; + int cut_at_cursor=0; - int function_mode = 0; - - int tokenize = 0; - - int cursor_mode = 0; - int line_mode = 0; - int search_mode = 0; - const wchar_t *begin, *end; + int argc = builtin_count_args( argv ); + int append_mode=0; - current_buffer = (wchar_t *)builtin_complete_get_temporary_buffer(); - if( current_buffer ) - { - current_cursor_pos = wcslen( current_buffer ); - } - else - { - current_buffer = reader_get_buffer(); - current_cursor_pos = reader_get_cursor_pos(); - } + int function_mode = 0; + + int tokenize = 0; + + int cursor_mode = 0; + int line_mode = 0; + int search_mode = 0; + const wchar_t *begin, *end; + + current_buffer = (wchar_t *)builtin_complete_get_temporary_buffer(); + if( current_buffer ) + { + current_cursor_pos = wcslen( current_buffer ); + } + else + { + current_buffer = reader_get_buffer(); + current_cursor_pos = reader_get_cursor_pos(); + } + + if( !get_buffer() ) + { + if (is_interactive_session) + { + /* + Prompt change requested while we don't have + a prompt, most probably while reading the + init files. Just ignore it. + */ + return 1; + } - if( !get_buffer() ) - { - if (is_interactive_session) - { - /* - Prompt change requested while we don't have - a prompt, most probably while reading the - init files. Just ignore it. - */ - return 1; - } - stderr_buffer.append(argv[0]); stderr_buffer.append(L": Can not set commandline in non-interactive mode\n"); - builtin_print_help( parser, argv[0], stderr_buffer ); - return 1; - } + builtin_print_help( parser, argv[0], stderr_buffer ); + return 1; + } - woptind=0; + woptind=0; - while( 1 ) - { - static const struct woption - long_options[] = - { - { - L"append", no_argument, 0, 'a' - } - , - { - L"insert", no_argument, 0, 'i' - } - , - { - L"replace", no_argument, 0, 'r' - } - , - { - L"current-job", no_argument, 0, 'j' - } - , - { - L"current-process", no_argument, 0, 'p' - } - , - { - L"current-token", no_argument, 0, 't' - } - , - { - L"current-buffer", no_argument, 0, 'b' - } - , - { - L"cut-at-cursor", no_argument, 0, 'c' - } - , - { - L"function", no_argument, 0, 'f' - } - , - { - L"tokenize", no_argument, 0, 'o' - } - , - { - L"help", no_argument, 0, 'h' - } - , - { - L"input", required_argument, 0, 'I' - } - , - { - L"cursor", no_argument, 0, 'C' - } - , - { - L"line", no_argument, 0, 'L' - } - , - { - L"search-mode", no_argument, 0, 'S' - } - , - { - 0, 0, 0, 0 - } - } - ; - - int opt_index = 0; - - int opt = wgetopt_long( argc, - argv, - L"abijpctwforhI:CLS", - long_options, - &opt_index ); - if( opt == -1 ) - break; - - switch( opt ) - { - case 0: - if(long_options[opt_index].flag != 0) - break; + while( 1 ) + { + static const struct woption + long_options[] = + { + { + L"append", no_argument, 0, 'a' + } + , + { + L"insert", no_argument, 0, 'i' + } + , + { + L"replace", no_argument, 0, 'r' + } + , + { + L"current-job", no_argument, 0, 'j' + } + , + { + L"current-process", no_argument, 0, 'p' + } + , + { + L"current-token", no_argument, 0, 't' + } + , + { + L"current-buffer", no_argument, 0, 'b' + } + , + { + L"cut-at-cursor", no_argument, 0, 'c' + } + , + { + L"function", no_argument, 0, 'f' + } + , + { + L"tokenize", no_argument, 0, 'o' + } + , + { + L"help", no_argument, 0, 'h' + } + , + { + L"input", required_argument, 0, 'I' + } + , + { + L"cursor", no_argument, 0, 'C' + } + , + { + L"line", no_argument, 0, 'L' + } + , + { + L"search-mode", no_argument, 0, 'S' + } + , + { + 0, 0, 0, 0 + } + } + ; + + int opt_index = 0; + + int opt = wgetopt_long( argc, + argv, + L"abijpctwforhI:CLS", + 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': - append_mode = APPEND_MODE; - break; + return 1; - case L'b': - buffer_part = STRING_MODE; - break; - - - case L'i': - append_mode = INSERT_MODE; - break; + case L'a': + append_mode = APPEND_MODE; + break; - case L'r': - append_mode = REPLACE_MODE; - break; - - case 'c': - cut_at_cursor=1; - break; - - case 't': - buffer_part = TOKEN_MODE; - break; + case L'b': + buffer_part = STRING_MODE; + break; - case 'j': - buffer_part = JOB_MODE; - break; - case 'p': - buffer_part = PROCESS_MODE; - break; + case L'i': + append_mode = INSERT_MODE; + break; - case 'f': - function_mode=1; - break; + case L'r': + append_mode = REPLACE_MODE; + break; - case 'o': - tokenize=1; - break; + case 'c': + cut_at_cursor=1; + break; - case 'I': - current_buffer = woptarg; - current_cursor_pos = wcslen( woptarg ); - break; - - case 'C': - cursor_mode = 1; - break; - - case 'L': - line_mode = 1; - break; - - case 'S': - search_mode = 1; - break; - - case 'h': - builtin_print_help( parser, argv[0], stdout_buffer ); - return 0; + case 't': + buffer_part = TOKEN_MODE; + break; - case L'?': - builtin_unknown_option( parser, argv[0], argv[woptind-1] ); - return 1; - } - } + case 'j': + buffer_part = JOB_MODE; + break; - if( function_mode ) - { - int i; - - /* - Check for invalid switch combinations - */ - if( buffer_part || cut_at_cursor || append_mode || tokenize || cursor_mode || line_mode || search_mode ) - { - append_format(stderr_buffer, - BUILTIN_ERR_COMBO, - argv[0] ); - - builtin_print_help( parser, argv[0], stderr_buffer ); - return 1; - } - + case 'p': + buffer_part = PROCESS_MODE; + break; - if( argc == woptind ) - { - append_format(stderr_buffer, - BUILTIN_ERR_MISSING, - argv[0] ); - - builtin_print_help( parser, argv[0], stderr_buffer ); - return 1; - } - for( i=woptind; i 1) ) - { - - append_format(stderr_buffer, - argv[0], - L": Too many arguments\n", - NULL ); - builtin_print_help( parser, argv[0], stderr_buffer ); - return 1; - } + case 'f': + function_mode=1; + break; - if( (buffer_part || tokenize || cut_at_cursor) && (cursor_mode || line_mode || search_mode) ) - { - append_format(stderr_buffer, - BUILTIN_ERR_COMBO, - argv[0] ); + case 'o': + tokenize=1; + break; - builtin_print_help( parser, argv[0], stderr_buffer ); - return 1; - } - + case 'I': + current_buffer = woptarg; + current_cursor_pos = wcslen( woptarg ); + break; - if( (tokenize || cut_at_cursor) && (argc-woptind) ) - { - append_format(stderr_buffer, - BUILTIN_ERR_COMBO2, - argv[0], - L"--cut-at-cursor and --tokenize can not be used when setting the commandline" ); - + case 'C': + cursor_mode = 1; + break; - builtin_print_help( parser, argv[0], stderr_buffer ); - return 1; - } + case 'L': + line_mode = 1; + break; - if( append_mode && !(argc-woptind) ) - { - append_format(stderr_buffer, + case 'S': + search_mode = 1; + break; + + case '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( function_mode ) + { + int i; + + /* + Check for invalid switch combinations + */ + if( buffer_part || cut_at_cursor || append_mode || tokenize || cursor_mode || line_mode || search_mode ) + { + append_format(stderr_buffer, + BUILTIN_ERR_COMBO, + argv[0] ); + + builtin_print_help( parser, argv[0], stderr_buffer ); + return 1; + } + + + if( argc == woptind ) + { + append_format(stderr_buffer, + BUILTIN_ERR_MISSING, + argv[0] ); + + builtin_print_help( parser, argv[0], stderr_buffer ); + return 1; + } + for( i=woptind; i 1) ) + { + + append_format(stderr_buffer, + argv[0], + L": Too many arguments\n", + NULL ); + builtin_print_help( parser, argv[0], stderr_buffer ); + return 1; + } + + if( (buffer_part || tokenize || cut_at_cursor) && (cursor_mode || line_mode || search_mode) ) + { + append_format(stderr_buffer, + BUILTIN_ERR_COMBO, + argv[0] ); + + builtin_print_help( parser, argv[0], stderr_buffer ); + return 1; + } + + + if( (tokenize || cut_at_cursor) && (argc-woptind) ) + { + append_format(stderr_buffer, + BUILTIN_ERR_COMBO2, + argv[0], + L"--cut-at-cursor and --tokenize can not be used when setting the commandline" ); + + + builtin_print_help( parser, argv[0], stderr_buffer ); + return 1; + } + + if( append_mode && !(argc-woptind) ) + { + append_format(stderr_buffer, BUILTIN_ERR_COMBO2, argv[0], - L"insertion mode switches can not be used when not in insertion mode" ); + L"insertion mode switches can not be used when not in insertion mode" ); - builtin_print_help( parser, argv[0], stderr_buffer ); + builtin_print_help( parser, argv[0], stderr_buffer ); return 1; - } + } - /* - Set default modes - */ - if( !append_mode ) - { - append_mode = REPLACE_MODE; - } - - if( !buffer_part ) - { - buffer_part = STRING_MODE; - } + /* + Set default modes + */ + if( !append_mode ) + { + append_mode = REPLACE_MODE; + } - if( cursor_mode ) - { - if( argc-woptind ) - { - wchar_t *endptr; - long new_pos; - errno = 0; - - new_pos = wcstol( argv[woptind], &endptr, 10 ); - if( *endptr || errno ) - { - append_format(stderr_buffer, - BUILTIN_ERR_NOT_NUMBER, - argv[0], - argv[woptind] ); - builtin_print_help( parser, argv[0], stderr_buffer ); - } - - current_buffer = reader_get_buffer(); - new_pos = maxi( 0L, mini( new_pos, (long)wcslen( current_buffer ) ) ); - reader_set_buffer( current_buffer, (size_t)new_pos ); - return 0; - } - else - { - append_format(stdout_buffer, L"%lu\n", (unsigned long)reader_get_cursor_pos() ); - return 0; - } - - } - - if( line_mode ) - { - size_t pos = reader_get_cursor_pos(); - const wchar_t *buff = reader_get_buffer(); - append_format(stdout_buffer, L"%lu\n", (unsigned long)parse_util_lineno( buff, pos ) ); - return 0; - - } - - if( search_mode ) - { - return !reader_search_mode(); - } - - - switch( buffer_part ) - { - case STRING_MODE: - { - begin = get_buffer(); - end = begin+wcslen(begin); - break; - } + if( !buffer_part ) + { + buffer_part = STRING_MODE; + } - case PROCESS_MODE: - { - parse_util_process_extent( get_buffer(), - get_cursor_pos(), - &begin, - &end ); - break; - } - - case JOB_MODE: - { - parse_util_job_extent( get_buffer(), - get_cursor_pos(), - &begin, - &end ); - break; - } - - case TOKEN_MODE: - { - parse_util_token_extent( get_buffer(), - get_cursor_pos(), - &begin, - &end, - 0, 0 ); - break; - } - - } + if( cursor_mode ) + { + if( argc-woptind ) + { + wchar_t *endptr; + long new_pos; + errno = 0; - switch(argc-woptind) - { - case 0: - { - write_part( begin, end, cut_at_cursor, tokenize ); - break; - } - - case 1: - { - replace_part( begin, end, argv[woptind], append_mode ); - break; - } + new_pos = wcstol( argv[woptind], &endptr, 10 ); + if( *endptr || errno ) + { + append_format(stderr_buffer, + BUILTIN_ERR_NOT_NUMBER, + argv[0], + argv[woptind] ); + builtin_print_help( parser, argv[0], stderr_buffer ); + } - default: - { - wcstring sb = argv[woptind]; - int i; - - for( i=woptind+1; i comp; - complete( do_complete_param, comp, COMPLETE_DEFAULT ); - - for( size_t i=0; i< comp.size() ; i++ ) - { - const completion_t &next = comp.at( i ); + complete( do_complete_param, comp, COMPLETE_DEFAULT ); - const wchar_t *prepend; - - if( next.flags & COMPLETE_NO_CASE ) - { - prepend = L""; - } - else - { - prepend = token; - } - + for( size_t i=0; i< comp.size() ; i++ ) + { + const completion_t &next = comp.at( i ); - if( !(next.description).empty() ) - { - append_format(stdout_buffer, L"%ls%ls\t%ls\n", prepend, next.completion.c_str(), next.description.c_str() ); - } - else - { - append_format(stdout_buffer, L"%ls%ls\n", prepend, next.completion.c_str() ); - } - } - - recursion_level--; - } - - temporary_buffer = prev_temporary_buffer; - - } - else if( woptind != argc ) - { - append_format(stderr_buffer, - _( L"%ls: Too many arguments\n" ), - argv[0] ); - builtin_print_help( parser, argv[0], stderr_buffer ); + const wchar_t *prepend; - res = true; - } - else if( cmd.empty() && path.empty() ) - { - /* No arguments specified, meaning we print the definitions of - * all specified completions to stdout.*/ - complete_print( stdout_buffer ); - } - else - { - if( remove ) - { - builtin_complete_remove( cmd, - path, - short_opt.c_str(), - gnu_opt, - old_opt ); - } - else - { - builtin_complete_add( cmd, - path, - short_opt.c_str(), - gnu_opt, - old_opt, - result_mode, - authoritative, - condition, - comp, - desc, - flags ); - } + if( next.flags & COMPLETE_NO_CASE ) + { + prepend = L""; + } + else + { + prepend = token; + } - } - } - - return res ? 1 : 0; + + if( !(next.description).empty() ) + { + append_format(stdout_buffer, L"%ls%ls\t%ls\n", prepend, next.completion.c_str(), next.description.c_str() ); + } + else + { + append_format(stdout_buffer, L"%ls%ls\n", prepend, next.completion.c_str() ); + } + } + + recursion_level--; + } + + temporary_buffer = prev_temporary_buffer; + + } + else if( woptind != argc ) + { + append_format(stderr_buffer, + _( L"%ls: Too many arguments\n" ), + argv[0] ); + builtin_print_help( parser, argv[0], stderr_buffer ); + + res = true; + } + else if( cmd.empty() && path.empty() ) + { + /* No arguments specified, meaning we print the definitions of + * all specified completions to stdout.*/ + complete_print( stdout_buffer ); + } + else + { + if( remove ) + { + builtin_complete_remove( cmd, + path, + short_opt.c_str(), + gnu_opt, + old_opt ); + } + else + { + builtin_complete_add( cmd, + path, + short_opt.c_str(), + gnu_opt, + old_opt, + result_mode, + authoritative, + condition, + comp, + desc, + flags ); + } + + } + } + + return res ? 1 : 0; } diff --git a/builtin_jobs.cpp b/builtin_jobs.cpp index 1f01514cf..4725f5dd4 100644 --- a/builtin_jobs.cpp +++ b/builtin_jobs.cpp @@ -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; iflags & 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; } diff --git a/builtin_set.cpp b/builtin_set.cpp index a4a2d53e7..5b34227ec 100644 --- a/builtin_set.cpp +++ b/builtin_set.cpp @@ -1,6 +1,6 @@ /** \file builtin_set.c Functions defining the set builtin -Functions used for implementing the set builtin. +Functions used for implementing the set builtin. */ #include "config.h" @@ -49,7 +49,7 @@ extern wcstring stdout_buffer, stderr_buffer; */ static int is_path_variable( const wchar_t *env ) { - return contains(env, L"PATH", L"CDPATH" ); + return contains(env, L"PATH", L"CDPATH" ); } /** @@ -61,32 +61,32 @@ static int my_env_set( const wchar_t *key, const wcstring_list_t &val, int scope size_t i; int retcode = 0; const wchar_t *val_str=NULL; - + if( is_path_variable( key ) ) { /* Fix for https://github.com/fish-shell/fish-shell/issues/199 . Return success if any path setting succeeds. */ bool any_success = false, any_error = false; - + for( i=0; i< val.size() ; i++ ) { bool show_perror = false; int show_hint = 0; bool error = false; - + struct stat buff; const wchar_t *dir = val[i].c_str(); - + if( wstat( dir, &buff ) ) { error = true; show_perror = true; } - + if( !( S_ISDIR(buff.st_mode) ) ) { error = true; } - + if( !error ) { any_success = true; @@ -97,34 +97,34 @@ static int my_env_set( const wchar_t *key, const wcstring_list_t &val, int scope const wchar_t *colon; append_format(stderr_buffer, _(BUILTIN_SET_PATH_ERROR), L"set", dir, key); colon = wcschr( dir, L':' ); - - if( colon && *(colon+1) ) + + if( colon && *(colon+1) ) { show_hint = 1; } - + } - + if( show_perror ) { builtin_wperror( L"set" ); } - + if( show_hint ) { append_format(stderr_buffer, _(BUILTIN_SET_PATH_HINT), L"set", key, key, wcschr( dir, L':' )+1); } - + } - + /* Fail at setting the path if we tried to set it to something non-empty, but it wound up empty. */ if( ! val.empty() && ! any_success ) { return 1; } - + } - + wcstring sb; if( val.size() ) { @@ -138,7 +138,7 @@ static int my_env_set( const wchar_t *key, const wcstring_list_t &val, int scope } val_str = sb.c_str(); } - + switch( env_set( key, val_str, scope | ENV_USER ) ) { case ENV_PERM: @@ -147,7 +147,7 @@ static int my_env_set( const wchar_t *key, const wcstring_list_t &val, int scope retcode=1; break; } - + case ENV_INVALID: { append_format(stderr_buffer, _(L"%ls: Unknown error"), L"set" ); @@ -155,160 +155,160 @@ static int my_env_set( const wchar_t *key, const wcstring_list_t &val, int scope break; } } - + return retcode; } -/** - Extract indexes from a destination argument of the form name[index1 index2...] +/** + Extract indexes from a destination argument of the form name[index1 index2...] - \param indexes the list to insert the new indexes into - \param src the source string to parse - \param name the name of the element. Return null if the name in \c src does not match this name - \param var_count the number of elements in the array to parse. + \param indexes the list to insert the new indexes into + \param src the source string to parse + \param name the name of the element. Return null if the name in \c src does not match this name + \param var_count the number of elements in the array to parse. - \return the total number of indexes parsed, or -1 on error + \return the total number of indexes parsed, or -1 on error */ static int parse_index( std::vector &indexes, - const wchar_t *src, - const wchar_t *name, - size_t var_count ) + const wchar_t *src, + const wchar_t *name, + size_t var_count ) { - size_t len; - - int count = 0; - const wchar_t *src_orig = src; - - if (src == 0) - { - return 0; - } - - while (*src != L'\0' && (iswalnum(*src) || *src == L'_')) - { - src++; - } - - if (*src != L'[') - { - append_format(stderr_buffer, _(BUILTIN_SET_ARG_COUNT), L"set" ); - return 0; - } - - len = src-src_orig; - - if( (wcsncmp( src_orig, name, len )!=0) || (wcslen(name) != (len)) ) - { - append_format(stderr_buffer, - _(L"%ls: Multiple variable names specified in single call (%ls and %.*ls)\n"), - L"set", - name, - len, - src_orig); - return 0; - } + size_t len; - src++; + int count = 0; + const wchar_t *src_orig = src; - while (iswspace(*src)) - { - src++; - } - - while (*src != L']') - { - wchar_t *end; - - long l_ind; + if (src == 0) + { + return 0; + } - errno = 0; - - l_ind = wcstol(src, &end, 10); - - if( end==src || errno ) - { - append_format(stderr_buffer, _(L"%ls: Invalid index starting at '%ls'\n"), L"set", src); - return 0; - } + while (*src != L'\0' && (iswalnum(*src) || *src == L'_')) + { + src++; + } - if( l_ind < 0 ) - { - l_ind = var_count+l_ind+1; - } - - src = end; - if ( *src==L'.' && *(src+1)==L'.' ){ - src+=2; - long l_ind2 = wcstol( src, &end, 10 ); - if( end==src || errno ) - { - return 1; - } - src = end; + if (*src != L'[') + { + append_format(stderr_buffer, _(BUILTIN_SET_ARG_COUNT), L"set" ); + return 0; + } - if( l_ind2 < 0 ) - { - l_ind2 = var_count+l_ind2+1; - } - int direction = l_ind2 &indexes, - wcstring_list_t &values ) +static int update_values( wcstring_list_t &list, + std::vector &indexes, + wcstring_list_t &values ) { - size_t i; + size_t i; - /* Replace values where needed */ - for( i = 0; i < indexes.size(); i++ ) - { - /* - The '- 1' below is because the indices in fish are - one-based, but the vector uses zero-based indices - */ - long ind = indexes[i] - 1; - const wcstring newv = values[ i ]; - if( ind < 0 ) - { - return 1; - } + /* Replace values where needed */ + for( i = 0; i < indexes.size(); i++ ) + { + /* + The '- 1' below is because the indices in fish are + one-based, but the vector uses zero-based indices + */ + long ind = indexes[i] - 1; + const wcstring newv = values[ i ]; + if( ind < 0 ) + { + return 1; + } if ( (size_t)ind >= list.size() ) { list.resize( ind+1 ); } - -// free((void *) al_get(list, ind)); - list[ ind ] = newv; - } - - return 0; + +// free((void *) al_get(list, ind)); + list[ ind ] = newv; + } + + return 0; } /** - Erase from a list of wcstring values at specified indexes + Erase from a list of wcstring values at specified indexes */ -static void erase_values(wcstring_list_t &list, const std::vector &indexes) +static void erase_values(wcstring_list_t &list, const std::vector &indexes) { // Make a set of indexes. // This both sorts them into ascending order and removes duplicates. const std::set indexes_set(indexes.begin(), indexes.end()); - + // Now walk the set backwards, so we encounter larger indexes first, and remove elements at the given (1-based) indexes. std::set::const_reverse_iterator iter; for (iter = indexes_set.rbegin(); iter != indexes_set.rend(); iter++) { @@ -325,46 +325,46 @@ static void erase_values(wcstring_list_t &list, const std::vector &indexes Print the names of all environment variables in the scope, with or without shortening, with or without values, with or without escaping */ -static void print_variables(int include_values, int esc, bool shorten_ok, int scope) +static void print_variables(int include_values, int esc, bool shorten_ok, int scope) { wcstring_list_t names = env_get_names(scope); sort(names.begin(), names.end()); - - for( size_t i = 0; i < names.size(); i++ ) - { - const wcstring key = names.at(i); + + for( size_t i = 0; i < names.size(); i++ ) + { + const wcstring key = names.at(i); const wcstring e_key = escape_string(key, 0); - stdout_buffer.append(e_key); - - if( include_values ) - { - env_var_t value = env_get_string(key); - if( !value.missing() ) - { - int shorten = 0; - - if( shorten_ok && value.length() > 64 ) - { - shorten = 1; - value.resize(60); - } - - wcstring e_value = esc ? expand_escape_variable(value) : value; - + stdout_buffer.append(e_key); + + if( include_values ) + { + env_var_t value = env_get_string(key); + if( !value.missing() ) + { + int shorten = 0; + + if( shorten_ok && value.length() > 64 ) + { + shorten = 1; + value.resize(60); + } + + wcstring e_value = esc ? expand_escape_variable(value) : value; + stdout_buffer.append(L" "); stdout_buffer.append(e_value); - - if( shorten ) - { - stdout_buffer.append(L"\u2026"); - } - } - } - - stdout_buffer.append(L"\n"); - } + if( shorten ) + { + stdout_buffer.append(L"\u2026"); + } + + } + } + + stdout_buffer.append(L"\n"); + } } @@ -373,464 +373,464 @@ static void print_variables(int include_values, int esc, bool shorten_ok, int sc The set builtin. Creates, updates and erases environment variables and environemnt variable arrays. */ -static int builtin_set( parser_t &parser, wchar_t **argv ) +static int builtin_set( parser_t &parser, wchar_t **argv ) { - - /** - Variables used for parsing the argument list - */ - static const struct woption - long_options[] = - { - { - L"export", no_argument, 0, 'x' - } - , - { - L"global", no_argument, 0, 'g' - } - , - { - L"local", no_argument, 0, 'l' - } - , - { - L"erase", no_argument, 0, 'e' - } - , - { - L"names", no_argument, 0, 'n' - } - , - { - L"unexport", no_argument, 0, 'u' - } - , - { - L"universal", no_argument, 0, 'U' - } + + /** + Variables used for parsing the argument list + */ + static const struct woption + long_options[] = + { + { + L"export", no_argument, 0, 'x' + } + , + { + L"global", no_argument, 0, 'g' + } + , + { + L"local", no_argument, 0, 'l' + } + , + { + L"erase", no_argument, 0, 'e' + } + , + { + L"names", no_argument, 0, 'n' + } + , + { + L"unexport", no_argument, 0, 'u' + } + , + { + L"universal", no_argument, 0, 'U' + } , - { - L"long", no_argument, 0, 'L' - } - , - { - L"query", no_argument, 0, 'q' - } - , - { - L"help", no_argument, 0, 'h' - } - , - { - 0, 0, 0, 0 - } - } - ; - - const wchar_t *short_options = L"+xglenuULqh"; + { + L"long", no_argument, 0, 'L' + } + , + { + L"query", no_argument, 0, 'q' + } + , + { + L"help", no_argument, 0, 'h' + } + , + { + 0, 0, 0, 0 + } + } + ; - int argc = builtin_count_args(argv); + const wchar_t *short_options = L"+xglenuULqh"; - /* - Flags to set the work mode - */ - int local = 0, global = 0, exportv = 0; - int erase = 0, list = 0, unexport=0; - int universal = 0, query=0; - bool shorten_ok = true; + int argc = builtin_count_args(argv); - /* - Variables used for performing the actual work - */ - wchar_t *dest = 0; - int retcode=0; - int scope; - int slice=0; - int i; - - wchar_t *bad_char; - - - /* Parse options to obtain the requested operation and the modifiers */ - woptind = 0; - while (1) - { - int c = wgetopt_long(argc, argv, short_options, long_options, 0); + /* + Flags to set the work mode + */ + int local = 0, global = 0, exportv = 0; + int erase = 0, list = 0, unexport=0; + int universal = 0, query=0; + bool shorten_ok = true; - if (c == -1) - { - break; - } - - switch(c) - { - case 0: - break; + /* + Variables used for performing the actual work + */ + wchar_t *dest = 0; + int retcode=0; + int scope; + int slice=0; + int i; - case 'e': - erase = 1; - break; + wchar_t *bad_char; - case 'n': - list = 1; - break; - case 'x': - exportv = 1; - break; + /* Parse options to obtain the requested operation and the modifiers */ + woptind = 0; + while (1) + { + int c = wgetopt_long(argc, argv, short_options, long_options, 0); - case 'l': - local = 1; - break; + if (c == -1) + { + break; + } - case 'g': - global = 1; - break; + switch(c) + { + case 0: + break; - case 'u': - unexport = 1; - break; + case 'e': + erase = 1; + break; + + case 'n': + list = 1; + break; + + case 'x': + exportv = 1; + break; + + case 'l': + local = 1; + break; + + case 'g': + global = 1; + break; + + case 'u': + unexport = 1; + break; + + case 'U': + universal = 1; + break; - case 'U': - universal = 1; - break; - case 'L': shorten_ok = false; break; - case 'q': - query = 1; - break; + case 'q': + query = 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; - default: - break; - } - } + default: + break; + } + } - /* - Ok, all arguments have been parsed, let's validate them - */ + /* + Ok, all arguments have been parsed, let's validate them + */ - /* - If we are checking the existance of a variable (-q) we can not - also specify scope - */ + /* + If we are checking the existance of a variable (-q) we can not + also specify scope + */ - if( query && (erase || list) ) - { - append_format(stderr_buffer, - BUILTIN_ERR_COMBO, - argv[0] ); - - builtin_print_help( parser, argv[0], stderr_buffer ); - return 1; - } - + if( query && (erase || list) ) + { + append_format(stderr_buffer, + BUILTIN_ERR_COMBO, + argv[0] ); - /* We can't both list and erase varaibles */ - if( erase && list ) - { - append_format(stderr_buffer, - BUILTIN_ERR_COMBO, - argv[0] ); + builtin_print_help( parser, argv[0], stderr_buffer ); + return 1; + } - builtin_print_help( parser, argv[0], stderr_buffer ); - return 1; - } - /* - Variables can only have one scope - */ - if( local + global + universal > 1 ) - { - append_format(stderr_buffer, - BUILTIN_ERR_GLOCAL, - argv[0] ); - builtin_print_help( parser, argv[0], stderr_buffer ); - return 1; - } + /* We can't both list and erase varaibles */ + if( erase && list ) + { + append_format(stderr_buffer, + BUILTIN_ERR_COMBO, + argv[0] ); - /* - Variables can only have one export status - */ - if( exportv && unexport ) - { - append_format(stderr_buffer, - BUILTIN_ERR_EXPUNEXP, - argv[0] ); - builtin_print_help( parser, argv[0], stderr_buffer ); - return 1; - } + builtin_print_help( parser, argv[0], stderr_buffer ); + return 1; + } - /* - Calculate the scope value for variable assignement - */ - scope = (local ? ENV_LOCAL : 0) | (global ? ENV_GLOBAL : 0) | (exportv ? ENV_EXPORT : 0) | (unexport ? ENV_UNEXPORT : 0) | (universal ? ENV_UNIVERSAL:0) | ENV_USER; + /* + Variables can only have one scope + */ + if( local + global + universal > 1 ) + { + append_format(stderr_buffer, + BUILTIN_ERR_GLOCAL, + argv[0] ); + builtin_print_help( parser, argv[0], stderr_buffer ); + return 1; + } - if( query ) - { - /* - Query mode. Return the number of variables that do not exist - out of the specified variables. - */ - int i; - for( i=woptind; i indexes; + wcstring_list_t result; + size_t j; - if( wcschr( dest, L'[' ) ) - { - slice = 1; - *wcschr( dest, L'[' )=0; - } - - if( slice ) - { - std::vector indexes; - wcstring_list_t result; - size_t j; - env_var_t dest_str = env_get_string(dest); if (! dest_str.missing()) tokenize_variable_array( dest_str, result ); - - if( !parse_index( indexes, arg, dest, result.size() ) ) - { - builtin_print_help( parser, argv[0], stderr_buffer ); - retcode = 1; - break; - } - for( j=0; j < indexes.size() ; j++ ) - { - long idx = indexes[j]; - if( idx < 1 || (size_t)idx > result.size() ) - { - retcode++; - } - } - } - else - { - if( !env_exist( arg, scope ) ) - { - retcode++; - } - } - - free( dest ); - - } - return retcode; - } - - if( list ) - { - /* Maybe we should issue an error if there are any other arguments? */ - print_variables(0, 0, shorten_ok, scope); - return 0; - } - - if( woptind == argc ) - { - /* - Print values of variables - */ - if( erase ) - { - append_format(stderr_buffer, - _(L"%ls: Erase needs a variable name\n"), - argv[0] ); - - builtin_print_help( parser, argv[0], stderr_buffer ); - retcode = 1; - } - else - { - print_variables( 1, 1, shorten_ok, scope ); - } - - return retcode; - } + if( !parse_index( indexes, arg, dest, result.size() ) ) + { + builtin_print_help( parser, argv[0], stderr_buffer ); + retcode = 1; + break; + } + for( j=0; j < indexes.size() ; j++ ) + { + long idx = indexes[j]; + if( idx < 1 || (size_t)idx > result.size() ) + { + retcode++; + } + } + } + else + { + if( !env_exist( arg, scope ) ) + { + retcode++; + } + } - if( !(dest = wcsdup(argv[woptind]))) - { - DIE_MEM(); - } + free( dest ); - if( wcschr( dest, L'[' ) ) - { - slice = 1; - *wcschr( dest, L'[' )=0; - } - - if( !wcslen( dest ) ) - { - free( dest ); - append_format(stderr_buffer, BUILTIN_ERR_VARNAME_ZERO, argv[0] ); - builtin_print_help( parser, argv[0], stderr_buffer ); - return 1; - } - - if( (bad_char = wcsvarname( dest ) ) ) - { - append_format(stderr_buffer, BUILTIN_ERR_VARCHAR, argv[0], *bad_char ); - builtin_print_help( parser, argv[0], stderr_buffer ); - free( dest ); - return 1; - } - - if( slice && erase && (scope != ENV_USER) ) - { - free( dest ); - append_format(stderr_buffer, _(L"%ls: Can not specify scope when erasing array slice\n"), argv[0] ); - builtin_print_help( parser, argv[0], stderr_buffer ); - return 1; - } - - /* - set assignment can work in two modes, either using slices or - using the whole array. We detect which mode is used here. - */ - - if( slice ) - { + } + return retcode; + } + + if( list ) + { + /* Maybe we should issue an error if there are any other arguments? */ + print_variables(0, 0, shorten_ok, scope); + return 0; + } + + if( woptind == argc ) + { + /* + Print values of variables + */ + + if( erase ) + { + append_format(stderr_buffer, + _(L"%ls: Erase needs a variable name\n"), + argv[0] ); + + builtin_print_help( parser, argv[0], stderr_buffer ); + retcode = 1; + } + else + { + print_variables( 1, 1, shorten_ok, scope ); + } + + return retcode; + } + + if( !(dest = wcsdup(argv[woptind]))) + { + DIE_MEM(); + } + + if( wcschr( dest, L'[' ) ) + { + slice = 1; + *wcschr( dest, L'[' )=0; + } + + if( !wcslen( dest ) ) + { + free( dest ); + append_format(stderr_buffer, BUILTIN_ERR_VARNAME_ZERO, argv[0] ); + builtin_print_help( parser, argv[0], stderr_buffer ); + return 1; + } + + if( (bad_char = wcsvarname( dest ) ) ) + { + append_format(stderr_buffer, BUILTIN_ERR_VARCHAR, argv[0], *bad_char ); + builtin_print_help( parser, argv[0], stderr_buffer ); + free( dest ); + return 1; + } + + if( slice && erase && (scope != ENV_USER) ) + { + free( dest ); + append_format(stderr_buffer, _(L"%ls: Can not specify scope when erasing array slice\n"), argv[0] ); + builtin_print_help( parser, argv[0], stderr_buffer ); + return 1; + } + + /* + set assignment can work in two modes, either using slices or + using the whole array. We detect which mode is used here. + */ + + if( slice ) + { + + /* + Slice mode + */ + size_t idx_count, val_count; + wcstring_list_t values; + std::vector indexes; + wcstring_list_t result; - /* - Slice mode - */ - size_t idx_count, val_count; - wcstring_list_t values; - std::vector indexes; - wcstring_list_t result; - const env_var_t dest_str = env_get_string(dest); if (! dest_str.missing()) tokenize_variable_array( dest_str, result ); - - for( ; woptind = - + = and/or | - + = bang | - + = arg | arg arg | '(' ')' - + */ - + 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 subjects; const std::vector combiners; - + combining_expression(token_t tok, range_t where, const std::vector &exprs, const std::vector &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 subjects; std::vector 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++; diff --git a/builtin_ulimit.cpp b/builtin_ulimit.cpp index 93e37cc54..d20abeed9 100644 --- a/builtin_ulimit.cpp +++ b/builtin_ulimit.cpp @@ -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 process’s 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 process’s 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; } diff --git a/color.cpp b/color.cpp index dce115aa2..f590bcd89 100644 --- a/color.cpp +++ b/color.cpp @@ -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); diff --git a/color.h b/color.h index 23e5ba776..bcfbfcd0a 100644 --- a/color.h +++ b/color.h @@ -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); diff --git a/common.cpp b/common.cpp index de1c06124..3d9a7e475 100644 --- a/common.cpp +++ b/common.cpp @@ -1,5 +1,5 @@ /** \file common.c - + Various functions, mostly string utilities, that are used by most parts of fish. */ @@ -38,7 +38,7 @@ parts of fish. #include #include #include -#include +#include #include #include #include @@ -82,7 +82,7 @@ parts of fish. -struct termios shell_modes; +struct termios shell_modes; // Note we foolishly assume that pthread_t is just a primitive. But it might be a struct. static pthread_t main_thread_id = 0; @@ -102,78 +102,78 @@ int debug_level=1; static struct winsize termsize; -void show_stackframe() +void show_stackframe() { /* Hack to avoid showing backtraces in the tester */ if (program_name && ! wcscmp(program_name, L"(ignore)")) return; - - void *trace[32]; - char **messages = (char **)NULL; - int i, trace_size = 0; - trace_size = backtrace(trace, 32); - messages = backtrace_symbols(trace, trace_size); + void *trace[32]; + char **messages = (char **)NULL; + int i, trace_size = 0; - if( messages ) - { - debug( 0, L"Backtrace:" ); - for( i=0; ipush_back((wchar_t)c); - break; - } - } + s->push_back((wchar_t)c); + break; + } + } } wchar_t *str2wcs( const char *in ) { - wchar_t *out; - size_t len = strlen(in); - - out = (wchar_t *)malloc( sizeof(wchar_t)*(len+1) ); + wchar_t *out; + size_t len = strlen(in); - if( !out ) - { - DIE_MEM(); - } + out = (wchar_t *)malloc( sizeof(wchar_t)*(len+1) ); - return str2wcs_internal( in, out ); + if( !out ) + { + DIE_MEM(); + } + + return str2wcs_internal( in, out ); } wcstring str2wcstring( const char *in ) @@ -194,71 +194,71 @@ wcstring str2wcstring( const std::string &in ) wchar_t *str2wcs_internal( const char *in, wchar_t *out ) { - size_t res=0; - size_t in_pos=0; - size_t out_pos = 0; - mbstate_t state; - size_t len; + size_t res=0; + size_t in_pos=0; + size_t out_pos = 0; + mbstate_t state; + size_t len; - CHECK( in, 0 ); - CHECK( out, 0 ); - - len = strlen(in); + CHECK( in, 0 ); + CHECK( out, 0 ); - memset( &state, 0, sizeof(state) ); + len = strlen(in); - while( in[in_pos] ) - { - res = mbrtowc( &out[out_pos], &in[in_pos], len-in_pos, &state ); + memset( &state, 0, sizeof(state) ); - if( ( ( out[out_pos] >= ENCODE_DIRECT_BASE) && - ( out[out_pos] < ENCODE_DIRECT_BASE+256)) || - ( out[out_pos] == INTERNAL_SEPARATOR ) ) - { - out[out_pos] = ENCODE_DIRECT_BASE + (unsigned char)in[in_pos]; - in_pos++; - memset( &state, 0, sizeof(state) ); - out_pos++; - } - else - { - - switch( res ) - { - case (size_t)(-2): - case (size_t)(-1): - { - out[out_pos] = ENCODE_DIRECT_BASE + (unsigned char)in[in_pos]; - in_pos++; - memset( &state, 0, sizeof(state) ); - break; - } - - case 0: - { - return out; - } - - default: - { - in_pos += res; - break; - } - } - out_pos++; - } - - } - out[out_pos] = 0; - - return out; + while( in[in_pos] ) + { + res = mbrtowc( &out[out_pos], &in[in_pos], len-in_pos, &state ); + + if( ( ( out[out_pos] >= ENCODE_DIRECT_BASE) && + ( out[out_pos] < ENCODE_DIRECT_BASE+256)) || + ( out[out_pos] == INTERNAL_SEPARATOR ) ) + { + out[out_pos] = ENCODE_DIRECT_BASE + (unsigned char)in[in_pos]; + in_pos++; + memset( &state, 0, sizeof(state) ); + out_pos++; + } + else + { + + switch( res ) + { + case (size_t)(-2): + case (size_t)(-1): + { + out[out_pos] = ENCODE_DIRECT_BASE + (unsigned char)in[in_pos]; + in_pos++; + memset( &state, 0, sizeof(state) ); + break; + } + + case 0: + { + return out; + } + + default: + { + in_pos += res; + break; + } + } + out_pos++; + } + + } + out[out_pos] = 0; + + return out; } char *wcs2str( const wchar_t *in ) { if (! in) return NULL; - char *out; + char *out; size_t desired_size = MAX_UTF8_BYTES*wcslen(in)+1; char local_buff[512]; if (desired_size <= sizeof local_buff / sizeof *local_buff) { @@ -272,7 +272,7 @@ char *wcs2str( const wchar_t *in ) } } return result; - + } else { // here we fall into the bad case of allocating a buffer probably much larger than necessary out = (char *)malloc( MAX_UTF8_BYTES*wcslen(in)+1 ); @@ -282,7 +282,7 @@ char *wcs2str( const wchar_t *in ) return wcs2str_internal( in, out ); } - return wcs2str_internal( in, out ); + return wcs2str_internal( in, out ); } std::string wcs2string(const wcstring &input) @@ -295,85 +295,85 @@ std::string wcs2string(const wcstring &input) char *wcs2str_internal( const wchar_t *in, char *out ) { - size_t res=0; - size_t in_pos=0; - size_t out_pos = 0; - mbstate_t state; + size_t res=0; + size_t in_pos=0; + size_t out_pos = 0; + mbstate_t state; - CHECK( in, 0 ); - CHECK( out, 0 ); - - memset( &state, 0, sizeof(state) ); - - while( in[in_pos] ) - { - if( in[in_pos] == INTERNAL_SEPARATOR ) - { - } - else if( ( in[in_pos] >= ENCODE_DIRECT_BASE) && - ( in[in_pos] < ENCODE_DIRECT_BASE+256) ) - { - out[out_pos++] = in[in_pos]- ENCODE_DIRECT_BASE; - } - else - { - res = wcrtomb( &out[out_pos], in[in_pos], &state ); - - if( res == (size_t)(-1) ) - { - debug( 1, L"Wide character %d has no narrow representation", in[in_pos] ); - memset( &state, 0, sizeof(state) ); - } - else - { - out_pos += res; - } - } - in_pos++; - } - out[out_pos] = 0; - - return out; + CHECK( in, 0 ); + CHECK( out, 0 ); + + memset( &state, 0, sizeof(state) ); + + while( in[in_pos] ) + { + if( in[in_pos] == INTERNAL_SEPARATOR ) + { + } + else if( ( in[in_pos] >= ENCODE_DIRECT_BASE) && + ( in[in_pos] < ENCODE_DIRECT_BASE+256) ) + { + out[out_pos++] = in[in_pos]- ENCODE_DIRECT_BASE; + } + else + { + res = wcrtomb( &out[out_pos], in[in_pos], &state ); + + if( res == (size_t)(-1) ) + { + debug( 1, L"Wide character %d has no narrow representation", in[in_pos] ); + memset( &state, 0, sizeof(state) ); + } + else + { + out_pos += res; + } + } + in_pos++; + } + out[out_pos] = 0; + + return out; } char **wcsv2strv( const wchar_t * const *in ) { - size_t i, count = 0; + size_t i, count = 0; - while( in[count] != 0 ) - count++; - char **res = (char **)malloc( sizeof( char *)*(count+1)); - if( res == 0 ) - { - DIE_MEM(); - } + while( in[count] != 0 ) + count++; + char **res = (char **)malloc( sizeof( char *)*(count+1)); + if( res == 0 ) + { + DIE_MEM(); + } - for( i=0; i= count ) - { - break; - } - } - return (ssize_t)out_cum; + ssize_t out=0; + size_t out_cum=0; + while( 1 ) + { + out = write( fd, + &buff[out_cum], + count - out_cum ); + if (out < 0) + { + if(errno != EAGAIN && errno != EINTR) + { + return -1; + } + } + else + { + out_cum += (size_t)out; + } + if( out_cum >= count ) + { + break; + } + } + return (ssize_t)out_cum; } ssize_t read_loop(int fd, void *buff, size_t count) @@ -611,22 +611,22 @@ ssize_t read_loop(int fd, void *buff, size_t count) static bool should_debug(int level) { - if( level > debug_level ) - return false; + if( level > debug_level ) + return false; /* Hack to not print error messages in the tests */ if ( program_name && ! wcscmp(program_name, L"(ignore)") ) return false; - + return true; } static void debug_shared( const wcstring &msg ) { const wcstring sb = wcstring(program_name) + L": " + msg; - wcstring sb2; - write_screen( sb, sb2 ); - fwprintf( stderr, L"%ls", sb2.c_str() ); + wcstring sb2; + write_screen( sb, sb2 ); + fwprintf( stderr, L"%ls", sb2.c_str() ); } void debug( int level, const wchar_t *msg, ... ) @@ -635,9 +635,9 @@ void debug( int level, const wchar_t *msg, ... ) return; int errno_old = errno; va_list va; - va_start(va, msg); + va_start(va, msg); wcstring local_msg = vformat_string(msg, va); - va_end(va); + va_end(va); debug_shared(local_msg); errno = errno_old; } @@ -649,9 +649,9 @@ void debug( int level, const char *msg, ... ) int errno_old = errno; char local_msg[512]; va_list va; - va_start(va, msg); + va_start(va, msg); vsnprintf(local_msg, sizeof local_msg, msg, va); - va_end(va); + va_end(va); debug_shared(str2wcstring(local_msg)); errno = errno_old; } @@ -662,26 +662,26 @@ void debug_safe(int level, const char *msg, const char *param1, const char *para const char * const params[] = {param1, param2, param3, param4, param5, param6, param7, param8, param9, param10, param11, param12}; if (! msg) return; - + /* Can't call printf, that may allocate memory Just call write() over and over. */ if (level > debug_level) return; int errno_old = errno; - + size_t param_idx = 0; const char *cursor = msg; while (*cursor != '\0') { const char *end = strchr(cursor, '%'); if (end == NULL) end = cursor + strlen(cursor); - + write(STDERR_FILENO, cursor, end - cursor); if (end[0] == '%' && end[1] == 's') { /* Handle a format string */ assert(param_idx < sizeof params / sizeof *params); const char *format = params[param_idx++]; - if (! format) + if (! format) format = "(null)"; write(STDERR_FILENO, format, strlen(format)); cursor = end + 2; @@ -693,10 +693,10 @@ void debug_safe(int level, const char *msg, const char *param1, const char *para cursor = end + 1; } } - + // We always append a newline write(STDERR_FILENO, "\n", 1); - + errno = errno_old; } @@ -707,7 +707,7 @@ void format_long_safe(char buff[128], long val) { /* Generate the string in reverse */ size_t idx = 0; bool negative = (val < 0); - + /* Note that we can't just negate val if it's negative, because it may be the most negative value. We do rely on round-towards-zero division though. */ while (val != 0) { @@ -718,7 +718,7 @@ void format_long_safe(char buff[128], long val) { if (negative) buff[idx++] = '-'; buff[idx] = 0; - + size_t left = 0, right = idx - 1; while (left < right) { char tmp = buff[left]; @@ -745,7 +745,7 @@ void format_long_safe(wchar_t buff[128], long val) { if (negative) buff[idx++] = L'-'; buff[idx] = 0; - + size_t left = 0, right = idx - 1; while (left < right) { wchar_t tmp = buff[left]; @@ -757,90 +757,90 @@ void format_long_safe(wchar_t buff[128], long val) { void write_screen( const wcstring &msg, wcstring &buff ) { - const wchar_t *start, *pos; - int line_width = 0; - int tok_width = 0; - int screen_width = common_get_width(); - - if( screen_width ) - { - start = pos = msg.c_str(); - while( 1 ) - { - int overflow = 0; - - tok_width=0; + const wchar_t *start, *pos; + int line_width = 0; + int tok_width = 0; + int screen_width = common_get_width(); - /* - Tokenize on whitespace, and also calculate the width of the token - */ - while( *pos && ( !wcschr( L" \n\r\t", *pos ) ) ) - { - - /* - Check is token is wider than one line. - If so we mark it as an overflow and break the token. - */ - if((tok_width + fish_wcwidth(*pos)) > (screen_width-1)) - { - overflow = 1; - break; - } - - tok_width += fish_wcwidth( *pos ); - pos++; - } + if( screen_width ) + { + start = pos = msg.c_str(); + while( 1 ) + { + int overflow = 0; - /* - If token is zero character long, we don't do anything - */ - if( pos == start ) - { - start = pos = pos+1; - } - else if( overflow ) - { - /* - In case of overflow, we print a newline, except if we already are at position 0 - */ - wchar_t *token = wcsndup( start, pos-start ); - if( line_width != 0 ) + tok_width=0; + + /* + Tokenize on whitespace, and also calculate the width of the token + */ + while( *pos && ( !wcschr( L" \n\r\t", *pos ) ) ) + { + + /* + Check is token is wider than one line. + If so we mark it as an overflow and break the token. + */ + if((tok_width + fish_wcwidth(*pos)) > (screen_width-1)) + { + overflow = 1; + break; + } + + tok_width += fish_wcwidth( *pos ); + pos++; + } + + /* + If token is zero character long, we don't do anything + */ + if( pos == start ) + { + start = pos = pos+1; + } + else if( overflow ) + { + /* + In case of overflow, we print a newline, except if we already are at position 0 + */ + wchar_t *token = wcsndup( start, pos-start ); + if( line_width != 0 ) buff.push_back(L'\n'); buff.append(format_string(L"%ls-\n", token)); - free( token ); - line_width=0; - } - else - { - /* - Print the token - */ - wchar_t *token = wcsndup( start, pos-start ); - if( (line_width + (line_width!=0?1:0) + tok_width) > screen_width ) - { + free( token ); + line_width=0; + } + else + { + /* + Print the token + */ + wchar_t *token = wcsndup( start, pos-start ); + if( (line_width + (line_width!=0?1:0) + tok_width) > screen_width ) + { buff.push_back(L'\n'); - line_width=0; - } + line_width=0; + } buff.append(format_string(L"%ls%ls", line_width?L" ":L"", token )); - free( token ); - line_width += (line_width!=0?1:0) + tok_width; - } - - /* - Break on end of string - */ - if( !*pos ) - { - break; - } - - start=pos; - } - } - else - { + free( token ); + line_width += (line_width!=0?1:0) + tok_width; + } + + /* + Break on end of string + */ + if( !*pos ) + { + break; + } + + start=pos; + } + } + else + { buff.append(msg); - } + } buff.push_back(L'\n'); } @@ -851,198 +851,198 @@ void write_screen( const wcstring &msg, wcstring &buff ) */ static wchar_t *escape_simple( const wchar_t *in ) { - wchar_t *out; - size_t len = wcslen(in); - out = (wchar_t *)malloc( sizeof(wchar_t)*(len+3)); - if( !out ) - DIE_MEM(); - - out[0] = L'\''; - wcscpy(&out[1], in ); - out[len+1]=L'\''; - out[len+2]=0; - return out; + wchar_t *out; + size_t len = wcslen(in); + out = (wchar_t *)malloc( sizeof(wchar_t)*(len+3)); + if( !out ) + DIE_MEM(); + + out[0] = L'\''; + wcscpy(&out[1], in ); + out[len+1]=L'\''; + out[len+2]=0; + return out; } wchar_t *escape( const wchar_t *in_orig, escape_flags_t flags ) { - const wchar_t *in = in_orig; - - bool escape_all = !! (flags & ESCAPE_ALL); - bool no_quoted = !! (flags & ESCAPE_NO_QUOTED); + const wchar_t *in = in_orig; + + bool escape_all = !! (flags & ESCAPE_ALL); + bool no_quoted = !! (flags & ESCAPE_NO_QUOTED); bool no_tilde = !! (flags & ESCAPE_NO_TILDE); - - wchar_t *out; - wchar_t *pos; - int need_escape=0; - int need_complex_escape=0; + wchar_t *out; + wchar_t *pos; - if( !in ) - { - debug( 0, L"%s called with null input", __func__ ); - FATAL_EXIT(); - } + int need_escape=0; + int need_complex_escape=0; - if( !no_quoted && (wcslen( in ) == 0) ) - { - out = wcsdup(L"''"); - if( !out ) - DIE_MEM(); - return out; - } - - - out = (wchar_t *)malloc( sizeof(wchar_t)*(wcslen(in)*4 + 1)); - pos = out; - - if( !out ) - DIE_MEM(); - - while( *in != 0 ) - { + if( !in ) + { + debug( 0, L"%s called with null input", __func__ ); + FATAL_EXIT(); + } - if( ( *in >= ENCODE_DIRECT_BASE) && - ( *in < ENCODE_DIRECT_BASE+256) ) - { - int val = *in - ENCODE_DIRECT_BASE; - int tmp; - - *(pos++) = L'\\'; - *(pos++) = L'X'; - - tmp = val/16; - *pos++ = tmp > 9? L'a'+(tmp-10):L'0'+tmp; - - tmp = val%16; - *pos++ = tmp > 9? L'a'+(tmp-10):L'0'+tmp; - need_escape=need_complex_escape=1; - - } - else - { + if( !no_quoted && (wcslen( in ) == 0) ) + { + out = wcsdup(L"''"); + if( !out ) + DIE_MEM(); + return out; + } + + + out = (wchar_t *)malloc( sizeof(wchar_t)*(wcslen(in)*4 + 1)); + pos = out; + + if( !out ) + DIE_MEM(); + + while( *in != 0 ) + { + + if( ( *in >= ENCODE_DIRECT_BASE) && + ( *in < ENCODE_DIRECT_BASE+256) ) + { + int val = *in - ENCODE_DIRECT_BASE; + int tmp; + + *(pos++) = L'\\'; + *(pos++) = L'X'; + + tmp = val/16; + *pos++ = tmp > 9? L'a'+(tmp-10):L'0'+tmp; + + tmp = val%16; + *pos++ = tmp > 9? L'a'+(tmp-10):L'0'+tmp; + need_escape=need_complex_escape=1; + + } + else + { wchar_t c = *in; - switch( c ) - { - case L'\t': - *(pos++) = L'\\'; - *(pos++) = L't'; - need_escape=need_complex_escape=1; - break; - - case L'\n': - *(pos++) = L'\\'; - *(pos++) = L'n'; - need_escape=need_complex_escape=1; - break; - - case L'\b': - *(pos++) = L'\\'; - *(pos++) = L'b'; - need_escape=need_complex_escape=1; - break; - - case L'\r': - *(pos++) = L'\\'; - *(pos++) = L'r'; - need_escape=need_complex_escape=1; - break; - - case L'\x1b': - *(pos++) = L'\\'; - *(pos++) = L'e'; - need_escape=need_complex_escape=1; - break; - + switch( c ) + { + case L'\t': + *(pos++) = L'\\'; + *(pos++) = L't'; + need_escape=need_complex_escape=1; + break; - case L'\\': - case L'\'': - { - need_escape=need_complex_escape=1; - if( escape_all ) - *pos++ = L'\\'; - *pos++ = *in; - break; - } + case L'\n': + *(pos++) = L'\\'; + *(pos++) = L'n'; + need_escape=need_complex_escape=1; + break; - case L'&': - case L'$': - case L' ': - case L'#': - case L'^': - case L'<': - case L'>': - case L'(': - case L')': - case L'[': - case L']': - case L'{': - case L'}': - case L'?': - case L'*': - case L'|': - case L';': - case L'"': - case L'%': - case L'~': - { + case L'\b': + *(pos++) = L'\\'; + *(pos++) = L'b'; + need_escape=need_complex_escape=1; + break; + + case L'\r': + *(pos++) = L'\\'; + *(pos++) = L'r'; + need_escape=need_complex_escape=1; + break; + + case L'\x1b': + *(pos++) = L'\\'; + *(pos++) = L'e'; + need_escape=need_complex_escape=1; + break; + + + case L'\\': + case L'\'': + { + need_escape=need_complex_escape=1; + if( escape_all ) + *pos++ = L'\\'; + *pos++ = *in; + break; + } + + case L'&': + case L'$': + case L' ': + case L'#': + case L'^': + case L'<': + case L'>': + case L'(': + case L')': + case L'[': + case L']': + case L'{': + case L'}': + case L'?': + case L'*': + case L'|': + case L';': + case L'"': + case L'%': + case L'~': + { if (! no_tilde || c != L'~') { need_escape=1; if( escape_all ) *pos++ = L'\\'; } - *pos++ = *in; - break; - } - - default: - { - if( *in < 32 ) - { - if( *in <27 && *in > 0 ) - { - *(pos++) = L'\\'; - *(pos++) = L'c'; - *(pos++) = L'a' + *in -1; - - need_escape=need_complex_escape=1; - break; - - } - + *pos++ = *in; + break; + } - int tmp = (*in)%16; - *pos++ = L'\\'; - *pos++ = L'x'; - *pos++ = ((*in>15)? L'1' : L'0'); - *pos++ = tmp > 9? L'a'+(tmp-10):L'0'+tmp; - need_escape=need_complex_escape=1; - } - else - { - *pos++ = *in; - } - break; - } - } - } - - in++; - } - *pos = 0; + default: + { + if( *in < 32 ) + { + if( *in <27 && *in > 0 ) + { + *(pos++) = L'\\'; + *(pos++) = L'c'; + *(pos++) = L'a' + *in -1; - /* - Use quoted escaping if possible, since most people find it - easier to read. - */ - if( !no_quoted && need_escape && !need_complex_escape && escape_all ) - { - free( out ); - out = escape_simple( in_orig ); - } - - return out; + need_escape=need_complex_escape=1; + break; + + } + + + int tmp = (*in)%16; + *pos++ = L'\\'; + *pos++ = L'x'; + *pos++ = ((*in>15)? L'1' : L'0'); + *pos++ = tmp > 9? L'a'+(tmp-10):L'0'+tmp; + need_escape=need_complex_escape=1; + } + else + { + *pos++ = *in; + } + break; + } + } + } + + in++; + } + *pos = 0; + + /* + Use quoted escaping if possible, since most people find it + easier to read. + */ + if( !no_quoted && need_escape && !need_complex_escape && escape_all ) + { + free( out ); + out = escape_simple( in_orig ); + } + + return out; } wcstring escape_string( const wcstring &in, escape_flags_t flags ) { @@ -1054,542 +1054,542 @@ wcstring escape_string( const wcstring &in, escape_flags_t flags ) { wchar_t *unescape( const wchar_t * orig, int flags ) { - - int mode = 0; + + int mode = 0; int out_pos; - size_t in_pos; + size_t in_pos; size_t len; - int c; - int bracket_count=0; - wchar_t prev=0; - wchar_t *in; - int unescape_special = flags & UNESCAPE_SPECIAL; - int allow_incomplete = flags & UNESCAPE_INCOMPLETE; - - CHECK( orig, 0 ); - - len = wcslen( orig ); - in = wcsdup( orig ); + int c; + int bracket_count=0; + wchar_t prev=0; + wchar_t *in; + int unescape_special = flags & UNESCAPE_SPECIAL; + int allow_incomplete = flags & UNESCAPE_INCOMPLETE; - if( !in ) - DIE_MEM(); - - for( in_pos=0, out_pos=0; - in_pos=0)?in[out_pos]:0), out_pos++, in_pos++ ) - { - c = in[in_pos]; - switch( mode ) - { + CHECK( orig, 0 ); - /* - Mode 0 means unquoted string - */ - case 0: - { - if( c == L'\\' ) - { - switch( in[++in_pos] ) - { - - /* - A null character after a backslash is an - error, return null - */ - case L'\0': - { - if( !allow_incomplete ) - { - free(in); - return 0; - } - } - - /* - Numeric escape sequences. No prefix means - octal escape, otherwise hexadecimal. - */ - - case L'0': - case L'1': - case L'2': - case L'3': - case L'4': - case L'5': - case L'6': - case L'7': - case L'u': - case L'U': - case L'x': - case L'X': - { - int i; - long long res=0; - int chars=2; - int base=16; - - int byte = 0; - wchar_t max_val = ASCII_MAX; - - switch( in[in_pos] ) - { - case L'u': - { - chars=4; - max_val = UCS2_MAX; - break; - } - - case L'U': - { - chars=8; - max_val = WCHAR_MAX; - break; - } - - case L'x': - { - break; - } - - case L'X': - { - byte=1; - max_val = BYTE_MAX; - break; - } - - default: - { - base=8; - chars=3; + len = wcslen( orig ); + in = wcsdup( orig ); + + if( !in ) + DIE_MEM(); + + for( in_pos=0, out_pos=0; + in_pos=0)?in[out_pos]:0), out_pos++, in_pos++ ) + { + c = in[in_pos]; + switch( mode ) + { + + /* + Mode 0 means unquoted string + */ + case 0: + { + if( c == L'\\' ) + { + switch( in[++in_pos] ) + { + + /* + A null character after a backslash is an + error, return null + */ + case L'\0': + { + if( !allow_incomplete ) + { + free(in); + return 0; + } + } + + /* + Numeric escape sequences. No prefix means + octal escape, otherwise hexadecimal. + */ + + case L'0': + case L'1': + case L'2': + case L'3': + case L'4': + case L'5': + case L'6': + case L'7': + case L'u': + case L'U': + case L'x': + case L'X': + { + int i; + long long res=0; + int chars=2; + int base=16; + + int byte = 0; + wchar_t max_val = ASCII_MAX; + + switch( in[in_pos] ) + { + case L'u': + { + chars=4; + max_val = UCS2_MAX; + break; + } + + case L'U': + { + chars=8; + max_val = WCHAR_MAX; + break; + } + + case L'x': + { + break; + } + + case L'X': + { + byte=1; + max_val = BYTE_MAX; + break; + } + + default: + { + base=8; + chars=3; // note in_pod must be larger than 0 since we incremented it above assert(in_pos > 0); - in_pos--; - break; - } - } - - for( i=0; i= L'a' && - in[in_pos] <= (L'a'+32) ) - { - in[out_pos]=in[in_pos]-L'a'+1; - } - else if( in[in_pos] >= L'A' && - in[in_pos] <= (L'A'+32) ) - { - in[out_pos]=in[in_pos]-L'A'+1; - } - else - { - free(in); - return 0; - } - break; - - } - - /* - \x1b means escape - */ - case L'e': - { - in[out_pos]=L'\x1b'; - break; - } - - /* - \f means form feed - */ - case L'f': - { - in[out_pos]=L'\f'; - break; - } + if( d < 0 ) + { + in_pos--; + break; + } - /* - \n means newline - */ - case L'n': - { - in[out_pos]=L'\n'; - break; - } - - /* - \r means carriage return - */ - case L'r': - { - in[out_pos]=L'\r'; - break; - } - - /* - \t means tab - */ - case L't': - { - in[out_pos]=L'\t'; - break; - } + res=(res*base)|d; + } - /* - \v means vertical tab - */ - case L'v': - { - in[out_pos]=L'\v'; - break; - } - - default: - { - if( unescape_special ) - in[out_pos++] = INTERNAL_SEPARATOR; - in[out_pos]=in[in_pos]; - break; - } - } - } - else - { - switch( in[in_pos]) - { - case L'~': - { - if( unescape_special && (in_pos == 0) ) - { - in[out_pos]=HOME_DIRECTORY; - } - else - { - in[out_pos] = L'~'; - } - break; - } + if( (res <= max_val) ) + { + in[out_pos] = (wchar_t)((byte?ENCODE_DIRECT_BASE:0)+res); + } + else + { + free(in); + return 0; + } - case L'%': - { - if( unescape_special && (in_pos == 0) ) - { - in[out_pos]=PROCESS_EXPAND; - } - else - { - in[out_pos]=in[in_pos]; - } - break; - } + break; + } - case L'*': - { - if( unescape_special ) - { - if( out_pos > 0 && in[out_pos-1]==ANY_STRING ) - { - out_pos--; - in[out_pos] = ANY_STRING_RECURSIVE; - } - else - in[out_pos]=ANY_STRING; - } - else - { - in[out_pos]=in[in_pos]; - } - break; - } + /* + \a means bell (alert) + */ + case L'a': + { + in[out_pos]=L'\a'; + break; + } - case L'?': - { - if( unescape_special ) - { - in[out_pos]=ANY_CHAR; - } - else - { - in[out_pos]=in[in_pos]; - } - break; - } + /* + \b means backspace + */ + case L'b': + { + in[out_pos]=L'\b'; + break; + } - case L'$': - { - if( unescape_special ) - { - in[out_pos]=VARIABLE_EXPAND; - } - else - { - in[out_pos]=in[in_pos]; - } - break; - } + /* + \cX means control sequence X + */ + case L'c': + { + in_pos++; + if( in[in_pos] >= L'a' && + in[in_pos] <= (L'a'+32) ) + { + in[out_pos]=in[in_pos]-L'a'+1; + } + else if( in[in_pos] >= L'A' && + in[in_pos] <= (L'A'+32) ) + { + in[out_pos]=in[in_pos]-L'A'+1; + } + else + { + free(in); + return 0; + } + break; - case L'{': - { - if( unescape_special ) - { - bracket_count++; - in[out_pos]=BRACKET_BEGIN; - } - else - { - in[out_pos]=in[in_pos]; - } - break; - } - - case L'}': - { - if( unescape_special ) - { - bracket_count--; - in[out_pos]=BRACKET_END; - } - else - { - in[out_pos]=in[in_pos]; - } - break; - } - - case L',': - { - if( unescape_special && bracket_count && prev!=BRACKET_SEP) - { - in[out_pos]=BRACKET_SEP; - } - else - { - in[out_pos]=in[in_pos]; - } - break; - } - - case L'\'': - { - mode = 1; - if( unescape_special ) - in[out_pos] = INTERNAL_SEPARATOR; - else - out_pos--; - break; - } - - case L'\"': - { - mode = 2; - if( unescape_special ) - in[out_pos] = INTERNAL_SEPARATOR; - else - out_pos--; - break; - } + } - default: - { - in[out_pos] = in[in_pos]; - break; - } - } - } - break; - } + /* + \x1b means escape + */ + case L'e': + { + in[out_pos]=L'\x1b'; + break; + } - /* - Mode 1 means single quoted string, i.e 'foo' - */ - case 1: - { - if( c == L'\\' ) - { - switch( in[++in_pos] ) - { - case '\\': - case L'\'': - case L'\n': - { - in[out_pos]=in[in_pos]; - break; - } - - case 0: - { - if( !allow_incomplete ) - { - free(in); - return 0; - } - else - { - //We may ever escape a NULL character, but still appending a \ in case I am wrong. - in[out_pos] = L'\\'; - } - } - break; - default: - { - in[out_pos++] = L'\\'; - in[out_pos]= in[in_pos]; - } - } - - } - if( c == L'\'' ) - { - if( unescape_special ) - in[out_pos] = INTERNAL_SEPARATOR; - else - out_pos--; - mode = 0; - } - else - { - in[out_pos] = in[in_pos]; - } - - break; - } + /* + \f means form feed + */ + case L'f': + { + in[out_pos]=L'\f'; + break; + } - /* - Mode 2 means double quoted string, i.e. "foo" - */ - case 2: - { - switch( c ) - { - case '"': - { - mode = 0; - if( unescape_special ) - in[out_pos] = INTERNAL_SEPARATOR; - else - out_pos--; - break; - } - - case '\\': - { - switch( in[++in_pos] ) - { - case L'\0': - { - if( !allow_incomplete ) - { - free(in); - return 0; - } - else - { - //We probably don't need it since NULL character is always appended before ending this function. - in[out_pos]=in[in_pos]; - } - } - break; - case '\\': - case L'$': - case '"': - case '\n': - { - in[out_pos]=in[in_pos]; - break; - } + /* + \n means newline + */ + case L'n': + { + in[out_pos]=L'\n'; + break; + } - default: - { - in[out_pos++] = L'\\'; - in[out_pos] = in[in_pos]; - break; - } - } - break; - } - - case '$': - { - if( unescape_special ) - { - in[out_pos]=VARIABLE_EXPAND_SINGLE; - } - else - { - in[out_pos]=in[in_pos]; - } - break; - } - - default: - { - in[out_pos] = in[in_pos]; - break; - } - - } - break; - } - } - } + /* + \r means carriage return + */ + case L'r': + { + in[out_pos]=L'\r'; + break; + } - if( !allow_incomplete && mode ) - { - free( in ); - return 0; - } + /* + \t means tab + */ + case L't': + { + in[out_pos]=L'\t'; + break; + } - in[out_pos]=L'\0'; - return in; + /* + \v means vertical tab + */ + case L'v': + { + in[out_pos]=L'\v'; + break; + } + + default: + { + if( unescape_special ) + in[out_pos++] = INTERNAL_SEPARATOR; + in[out_pos]=in[in_pos]; + break; + } + } + } + else + { + switch( in[in_pos]) + { + case L'~': + { + if( unescape_special && (in_pos == 0) ) + { + in[out_pos]=HOME_DIRECTORY; + } + else + { + in[out_pos] = L'~'; + } + break; + } + + case L'%': + { + if( unescape_special && (in_pos == 0) ) + { + in[out_pos]=PROCESS_EXPAND; + } + else + { + in[out_pos]=in[in_pos]; + } + break; + } + + case L'*': + { + if( unescape_special ) + { + if( out_pos > 0 && in[out_pos-1]==ANY_STRING ) + { + out_pos--; + in[out_pos] = ANY_STRING_RECURSIVE; + } + else + in[out_pos]=ANY_STRING; + } + else + { + in[out_pos]=in[in_pos]; + } + break; + } + + case L'?': + { + if( unescape_special ) + { + in[out_pos]=ANY_CHAR; + } + else + { + in[out_pos]=in[in_pos]; + } + break; + } + + case L'$': + { + if( unescape_special ) + { + in[out_pos]=VARIABLE_EXPAND; + } + else + { + in[out_pos]=in[in_pos]; + } + break; + } + + case L'{': + { + if( unescape_special ) + { + bracket_count++; + in[out_pos]=BRACKET_BEGIN; + } + else + { + in[out_pos]=in[in_pos]; + } + break; + } + + case L'}': + { + if( unescape_special ) + { + bracket_count--; + in[out_pos]=BRACKET_END; + } + else + { + in[out_pos]=in[in_pos]; + } + break; + } + + case L',': + { + if( unescape_special && bracket_count && prev!=BRACKET_SEP) + { + in[out_pos]=BRACKET_SEP; + } + else + { + in[out_pos]=in[in_pos]; + } + break; + } + + case L'\'': + { + mode = 1; + if( unescape_special ) + in[out_pos] = INTERNAL_SEPARATOR; + else + out_pos--; + break; + } + + case L'\"': + { + mode = 2; + if( unescape_special ) + in[out_pos] = INTERNAL_SEPARATOR; + else + out_pos--; + break; + } + + default: + { + in[out_pos] = in[in_pos]; + break; + } + } + } + break; + } + + /* + Mode 1 means single quoted string, i.e 'foo' + */ + case 1: + { + if( c == L'\\' ) + { + switch( in[++in_pos] ) + { + case '\\': + case L'\'': + case L'\n': + { + in[out_pos]=in[in_pos]; + break; + } + + case 0: + { + if( !allow_incomplete ) + { + free(in); + return 0; + } + else + { + //We may ever escape a NULL character, but still appending a \ in case I am wrong. + in[out_pos] = L'\\'; + } + } + break; + default: + { + in[out_pos++] = L'\\'; + in[out_pos]= in[in_pos]; + } + } + + } + if( c == L'\'' ) + { + if( unescape_special ) + in[out_pos] = INTERNAL_SEPARATOR; + else + out_pos--; + mode = 0; + } + else + { + in[out_pos] = in[in_pos]; + } + + break; + } + + /* + Mode 2 means double quoted string, i.e. "foo" + */ + case 2: + { + switch( c ) + { + case '"': + { + mode = 0; + if( unescape_special ) + in[out_pos] = INTERNAL_SEPARATOR; + else + out_pos--; + break; + } + + case '\\': + { + switch( in[++in_pos] ) + { + case L'\0': + { + if( !allow_incomplete ) + { + free(in); + return 0; + } + else + { + //We probably don't need it since NULL character is always appended before ending this function. + in[out_pos]=in[in_pos]; + } + } + break; + case '\\': + case L'$': + case '"': + case '\n': + { + in[out_pos]=in[in_pos]; + break; + } + + default: + { + in[out_pos++] = L'\\'; + in[out_pos] = in[in_pos]; + break; + } + } + break; + } + + case '$': + { + if( unescape_special ) + { + in[out_pos]=VARIABLE_EXPAND_SINGLE; + } + else + { + in[out_pos]=in[in_pos]; + } + break; + } + + default: + { + in[out_pos] = in[in_pos]; + break; + } + + } + break; + } + } + } + + if( !allow_incomplete && mode ) + { + free( in ); + return 0; + } + + in[out_pos]=L'\0'; + return in; } bool unescape_string(wcstring &str, int escape_special) @@ -1609,25 +1609,25 @@ bool unescape_string(wcstring &str, int escape_special) void common_handle_winch( int signal ) { #ifdef HAVE_WINSIZE - if (ioctl(1,TIOCGWINSZ,&termsize)!=0) - { - return; - } + if (ioctl(1,TIOCGWINSZ,&termsize)!=0) + { + return; + } #else - termsize.ws_col = 80; - termsize.ws_row = 24; + termsize.ws_col = 80; + termsize.ws_row = 24; #endif } int common_get_width() { - return termsize.ws_col; + return termsize.ws_col; } int common_get_height() { - return termsize.ws_row; + return termsize.ws_row; } void tokenize_variable_array( const wcstring &val, std::vector &out) @@ -1677,88 +1677,88 @@ bool list_contains_string(const wcstring_list_t &list, const wcstring &str) int create_directory( const wcstring &d ) { - int ok = 0; - struct stat buf; - int stat_res = 0; - - while( (stat_res = wstat(d, &buf ) ) != 0 ) - { - if( errno != EAGAIN ) - break; - } - - if( stat_res == 0 ) - { - if( S_ISDIR( buf.st_mode ) ) - { - ok = 1; - } - } - else - { - if( errno == ENOENT ) - { + int ok = 0; + struct stat buf; + int stat_res = 0; + + while( (stat_res = wstat(d, &buf ) ) != 0 ) + { + if( errno != EAGAIN ) + break; + } + + if( stat_res == 0 ) + { + if( S_ISDIR( buf.st_mode ) ) + { + ok = 1; + } + } + else + { + if( errno == ENOENT ) + { wcstring dir = wdirname(d); - if( !create_directory( dir ) ) - { - if( !wmkdir( d, 0700 ) ) - { - ok = 1; - } - } - } - } - - return ok?0:-1; + if( !create_directory( dir ) ) + { + if( !wmkdir( d, 0700 ) ) + { + ok = 1; + } + } + } + } + + return ok?0:-1; } __attribute__((noinline)) void bugreport() { - debug( 1, - _( L"This is a bug. Break on bugreport to debug." - L"If you can reproduce it, please send a bug report to %s." ), - PACKAGE_BUGREPORT ); + debug( 1, + _( L"This is a bug. Break on bugreport to debug." + L"If you can reproduce it, please send a bug report to %s." ), + PACKAGE_BUGREPORT ); } wcstring format_size(long long sz) { wcstring result; - const wchar_t *sz_name[]= { - L"kB", L"MB", L"GB", L"TB", L"PB", L"EB", L"ZB", L"YB", 0 + const wchar_t *sz_name[]= { + L"kB", L"MB", L"GB", L"TB", L"PB", L"EB", L"ZB", L"YB", 0 }; - if( sz < 0 ) - { - result.append( L"unknown" ); - } - else if( sz < 1 ) - { - result.append( _( L"empty" ) ); - } - else if( sz < 1024 ) - { - result.append(format_string( L"%lldB", sz )); - } - else - { - int i; - - for( i=0; sz_name[i]; i++ ) - { - if( sz < (1024*1024) || !sz_name[i+1] ) - { - long isz = ((long)sz)/1024; - if( isz > 9 ) - result.append( format_string( L"%d%ls", isz, sz_name[i] )); - else - result.append( format_string( L"%.1f%ls", (double)sz/1024, sz_name[i] )); - break; - } - sz /= 1024; - - } - } + if( sz < 0 ) + { + result.append( L"unknown" ); + } + else if( sz < 1 ) + { + result.append( _( L"empty" ) ); + } + else if( sz < 1024 ) + { + result.append(format_string( L"%lldB", sz )); + } + else + { + int i; + + for( i=0; sz_name[i]; i++ ) + { + if( sz < (1024*1024) || !sz_name[i+1] ) + { + long isz = ((long)sz)/1024; + if( isz > 9 ) + result.append( format_string( L"%d%ls", isz, sz_name[i] )); + else + result.append( format_string( L"%.1f%ls", (double)sz/1024, sz_name[i] )); + break; + } + sz /= 1024; + + } + } return result; } @@ -1793,10 +1793,10 @@ void format_size_safe(char buff[128], unsigned long long sz) { const size_t max_len = buff_size - 1; //need to leave room for a null terminator bzero(buff, buff_size); size_t idx = 0; - const char * const sz_name[]= { + const char * const sz_name[]= { "kB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB", NULL }; - if (sz < 1) + if (sz < 1) { strncpy(buff, "empty", buff_size); } @@ -1807,16 +1807,16 @@ void format_size_safe(char buff[128], unsigned long long sz) { } else { - for( size_t i=0; sz_name[i]; i++ ) - { - if( sz < (1024*1024) || !sz_name[i+1] ) - { - unsigned long long isz = sz/1024; - if( isz > 9 ) + for( size_t i=0; sz_name[i]; i++ ) + { + if( sz < (1024*1024) || !sz_name[i+1] ) + { + unsigned long long isz = sz/1024; + if( isz > 9 ) { append_ull(buff, isz, &idx, max_len); } - else + else { if (isz == 0) { @@ -1826,7 +1826,7 @@ void format_size_safe(char buff[128], unsigned long long sz) { { append_ull(buff, isz, &idx, max_len); } - + // Maybe append a single fraction digit unsigned long long remainder = sz % 1024; if (remainder > 0) @@ -1836,33 +1836,33 @@ void format_size_safe(char buff[128], unsigned long long sz) { } } append_str(buff, sz_name[i], &idx, max_len); - break; - } - sz /= 1024; - } + break; + } + sz /= 1024; + } } } double timef() { - int time_res; - struct timeval tv; - - time_res = gettimeofday(&tv, 0); - - if( time_res ) - { - /* - Fixme: What on earth is the correct parameter value for NaN? - The man pages and the standard helpfully state that this - parameter is implementation defined. Gcc gives a warning if - a null pointer is used. But not even all mighty Google gives - a hint to what value should actually be returned. - */ - return nan(""); - } - - return (double)tv.tv_sec + 0.000001*tv.tv_usec; + int time_res; + struct timeval tv; + + time_res = gettimeofday(&tv, 0); + + if( time_res ) + { + /* + Fixme: What on earth is the correct parameter value for NaN? + The man pages and the standard helpfully state that this + parameter is implementation defined. Gcc gives a warning if + a null pointer is used. But not even all mighty Google gives + a hint to what value should actually be returned. + */ + return nan(""); + } + + return (double)tv.tv_sec + 0.000001*tv.tv_usec; } void exit_without_destructors(int code) { @@ -1874,7 +1874,7 @@ null_terminated_array_t convert_wide_array_to_narrow(const null_terminated const wchar_t *const *arr = wide_arr.get(); if (! arr) return null_terminated_array_t(); - + std::vector list; for (size_t i=0; arr[i]; i++) { list.push_back(wcs2string(arr[i])); @@ -1905,9 +1905,9 @@ extern "C" { __attribute__((noinline)) void debug_thread_error(void) { while (1) sleep(9999999); } } - + void set_main_thread() { - main_thread_id = pthread_self(); + main_thread_id = pthread_self(); } void configure_thread_assertions_for_testing(void) { @@ -1920,7 +1920,7 @@ static pid_t initial_pid = 0; bool is_forked_child(void) { /* Just bail if nobody's called setup_fork_guards - e.g. fishd */ if (! initial_pid) return false; - + bool is_child_of_fork = (getpid() != initial_pid); if (is_child_of_fork) { printf("Uh-oh: %d\n", getpid()); @@ -1935,8 +1935,8 @@ void setup_fork_guards(void) { } bool is_main_thread() { - assert (main_thread_id != 0); - return main_thread_id == pthread_self(); + assert (main_thread_id != 0); + return main_thread_id == pthread_self(); } void assert_is_main_thread(const char *who) @@ -2012,7 +2012,7 @@ bool wcstokenizer::next(wcstring &result) { if (tmp) result = tmp; return tmp != NULL; } - + wcstokenizer::~wcstokenizer() { free(buffer); } diff --git a/common.h b/common.h index b0bf0593c..96e95d32c 100644 --- a/common.h +++ b/common.h @@ -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_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 class null_terminated_array_t { CharType_t **array; - + typedef std::basic_string string_t; typedef std::vector 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 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(); diff --git a/complete.cpp b/complete.cpp index 491e2168c..50fcb51fb 100644 --- a/complete.cpp +++ b/complete.cpp @@ -1,6 +1,6 @@ /** \file complete.c 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. */ #include "config.h" @@ -123,29 +123,29 @@ If either short_opt or long_opt are non-zero, they specify a switch for the command. If \c comp is also not empty, it contains a list of non-switch arguments that may only follow directly after the - specified switch. + specified switch. */ typedef struct complete_entry_opt { - /** Short style option */ - wchar_t short_opt; - /** Long style option */ - wcstring long_opt; - /** Arguments to the option */ - wcstring comp; - /** Description of the completion */ - wcstring desc; - /** Condition under which to use the option */ - wcstring condition; - /** Must be one of the values SHARED, NO_FILES, NO_COMMON, - EXCLUSIVE, and determines how completions should be performed - on the argument after the switch. */ - int result_mode; - /** True if old style long options are used */ - int old_mode; - /** Completion flags */ - complete_flags_t flags; - + /** Short style option */ + wchar_t short_opt; + /** Long style option */ + wcstring long_opt; + /** Arguments to the option */ + wcstring comp; + /** Description of the completion */ + wcstring desc; + /** Condition under which to use the option */ + wcstring condition; + /** Must be one of the values SHARED, NO_FILES, NO_COMMON, + EXCLUSIVE, and determines how completions should be performed + on the argument after the switch. */ + int result_mode; + /** True if old style long options are used */ + int old_mode; + /** Completion flags */ + complete_flags_t flags; + const wchar_t *localized_desc() const { const wchar_t *tmp = desc.c_str(); @@ -161,39 +161,39 @@ static unsigned int kCompleteOrder = 0; */ typedef std::list option_list_t; class completion_entry_t -{ +{ public: - /** List of all options */ - option_list_t options; - - /** String containing all short option characters */ - wcstring short_opt_str; - + /** List of all options */ + option_list_t options; + + /** String containing all short option characters */ + wcstring short_opt_str; + public: - - /** Command string */ - const wcstring cmd; - - /** True if command is a path */ - const bool cmd_is_path; - - /** True if no other options than the ones supplied are possible */ - bool authoritative; - + + /** Command string */ + const wcstring cmd; + + /** True if command is a path */ + const bool cmd_is_path; + + /** True if no other options than the ones supplied are possible */ + bool authoritative; + /** Order for when this completion was created. This aids in outputting completions sorted by time. */ const unsigned int order; - + /** Getters for option list. */ const option_list_t &get_options() const; - + /** Adds or removes an option. */ void add_option(const complete_entry_opt_t &opt); bool remove_option(wchar_t short_opt, const wchar_t *long_opt); - + /** Getter for short_opt_str. */ - wcstring &get_short_opt_str(); + wcstring &get_short_opt_str(); const wcstring &get_short_opt_str() const; - + completion_entry_t(const wcstring &c, bool type, const wcstring &options, bool author) : short_opt_str(options), cmd(c), @@ -248,7 +248,7 @@ wcstring &completion_entry_t::get_short_opt_str() { const wcstring &completion_entry_t::get_short_opt_str() const { ASSERT_IS_LOCKED(completion_entry_lock); - return short_opt_str; + return short_opt_str; } /* completion_t functions */ @@ -315,21 +315,21 @@ class completer_t { const wcstring initial_cmd; std::vector completions; wcstring_list_t commands_to_load; - + /** Table of completions conditions that have already been tested and the corresponding test results */ typedef std::map condition_cache_t; condition_cache_t condition_cache; - + public: completer_t(const wcstring &c, complete_type_t t) : type(t), initial_cmd(c) { } - + bool empty() const { return completions.empty(); } const std::vector &get_completions(void) { return completions; } - + bool try_complete_variable( const wcstring &str ); bool try_complete_user( const wcstring &str ); @@ -337,33 +337,33 @@ class completer_t { const wcstring &popt, const wcstring &str, bool use_switches); - + void complete_param_expand(const wcstring &str, bool do_file); - + void debug_print_completions(); - + void complete_cmd( const wcstring &str, bool use_function, bool use_builtin, bool use_command); - + void complete_from_args( const wcstring &str, const wcstring &args, const wcstring &desc, complete_flags_t flags ); - + void complete_cmd_desc( const wcstring &str ); - + bool complete_variable(const wcstring &str, size_t start_offset); - + bool condition_test( const wcstring &condition ); - + void complete_strings( const wcstring &wc_escaped, const wchar_t *desc, wcstring (*desc_func)(const wcstring &), std::vector &possible_comp, complete_flags_t flags ); - + expand_flags_t expand_flags() const { /* Never do command substitution in autosuggestions. Sadly, we also can't yet do job expansion because it's not thread safe. */ expand_flags_t result = 0; @@ -371,7 +371,7 @@ class completer_t { result |= EXPAND_SKIP_CMDSUBST | EXPAND_SKIP_JOBS; return result; } - + void get_commands_to_load(wcstring_list_t *lst) { if (lst) lst->insert(lst->end(), commands_to_load.begin(), commands_to_load.end()); @@ -417,13 +417,13 @@ void append_completion(std::vector &completions, const wcstring &c completion run to make sure that there are no stale completions. */ bool completer_t::condition_test( const wcstring &condition ) -{ - if( condition.empty() ) - { -// fwprintf( stderr, L"No condition specified\n" ); - return 1; - } - +{ + if( condition.empty() ) + { +// fwprintf( stderr, L"No condition specified\n" ); + return 1; + } + if (this->type == COMPLETE_AUTOSUGGEST) { /* Autosuggestion can't support conditions */ @@ -431,7 +431,7 @@ bool completer_t::condition_test( const wcstring &condition ) } ASSERT_IS_MAIN_THREAD(); - + bool test_res; condition_cache_t::iterator cached_entry = condition_cache.find(condition); if (cached_entry == condition_cache.end()) { @@ -456,83 +456,83 @@ static completion_entry_t *complete_find_exact_entry( const wcstring &cmd, const if (iter != completion_set.end()) { result = *iter; } - return result; + return result; } /** Locate the specified entry. Create it if it doesn't exist. Must be called while locked. */ static completion_entry_t *complete_get_exact_entry( const wcstring &cmd, bool cmd_is_path ) { ASSERT_IS_LOCKED(completion_lock); - completion_entry_t *c; + completion_entry_t *c; - c = complete_find_exact_entry( cmd, cmd_is_path ); + c = complete_find_exact_entry( cmd, cmd_is_path ); - if( c == NULL ) - { + if( c == NULL ) + { c = new completion_entry_t(cmd, cmd_is_path, L"", false); completion_set.insert(c); - } + } - return c; + return c; } void complete_set_authoritative( const wchar_t *cmd, bool cmd_is_path, bool authoritative ) { - completion_entry_t *c; + completion_entry_t *c; - CHECK( cmd, ); + CHECK( cmd, ); scoped_lock lock(completion_lock); - c = complete_get_exact_entry( cmd, cmd_is_path ); - c->authoritative = authoritative; + c = complete_get_exact_entry( cmd, cmd_is_path ); + c->authoritative = authoritative; } void complete_add( const wchar_t *cmd, - bool cmd_is_path, - wchar_t short_opt, - const wchar_t *long_opt, - int old_mode, - int result_mode, - const wchar_t *condition, - const wchar_t *comp, - const wchar_t *desc, - complete_flags_t flags ) + bool cmd_is_path, + wchar_t short_opt, + const wchar_t *long_opt, + int old_mode, + int result_mode, + const wchar_t *condition, + const wchar_t *comp, + const wchar_t *desc, + complete_flags_t flags ) { - CHECK( cmd, ); - + CHECK( cmd, ); + /* Lock the lock that allows us to edit the completion entry list */ scoped_lock lock(completion_lock); - + /* Lock the lock that allows us to edit individual completion entries */ scoped_lock lock2(completion_entry_lock); - - completion_entry_t *c; - c = complete_get_exact_entry( cmd, cmd_is_path ); - + + completion_entry_t *c; + c = complete_get_exact_entry( cmd, cmd_is_path ); + /* Create our new option */ complete_entry_opt_t opt; - if( short_opt != L'\0' ) - { - int len = 1 + ((result_mode & NO_COMMON) != 0); - + if( short_opt != L'\0' ) + { + int len = 1 + ((result_mode & NO_COMMON) != 0); + c->get_short_opt_str().push_back(short_opt); - if( len == 2 ) - { + if( len == 2 ) + { c->get_short_opt_str().push_back(L':'); - } - } - - opt.short_opt = short_opt; - opt.result_mode = result_mode; - opt.old_mode=old_mode; + } + } + + opt.short_opt = short_opt; + opt.result_mode = result_mode; + opt.old_mode=old_mode; if (comp) opt.comp = comp; if (condition) opt.condition = condition; if (long_opt) opt.long_opt = long_opt; if (desc) opt.desc = desc; - opt.flags = flags; - + opt.flags = flags; + c->add_option(opt); } @@ -545,24 +545,24 @@ bool completion_entry_t::remove_option( wchar_t short_opt, const wchar_t *long_o { ASSERT_IS_LOCKED(completion_lock); ASSERT_IS_LOCKED(completion_entry_lock); - if(( short_opt == 0 ) && (long_opt == 0 ) ) - { + if(( short_opt == 0 ) && (long_opt == 0 ) ) + { this->options.clear(); - } - else - { + } + else + { for (option_list_t::iterator iter = this->options.begin(); iter != this->options.end(); ) - { + { complete_entry_opt_t &o = *iter; - if(short_opt==o.short_opt || long_opt == o.long_opt) - { - /* fwprintf( stderr, - L"remove option -%lc --%ls\n", - o->short_opt?o->short_opt:L' ', - o->long_opt ); - */ - if( o.short_opt ) - { + if(short_opt==o.short_opt || long_opt == o.long_opt) + { + /* fwprintf( stderr, + L"remove option -%lc --%ls\n", + o->short_opt?o->short_opt:L' ', + o->long_opt ); + */ + if( o.short_opt ) + { wcstring &short_opt_str = this->get_short_opt_str(); size_t idx = short_opt_str.find(o.short_opt); if (idx != wcstring::npos) @@ -573,31 +573,31 @@ bool completion_entry_t::remove_option( wchar_t short_opt, const wchar_t *long_o first_non_colon++; short_opt_str.erase(idx, first_non_colon - idx); } - } - + } + /* Destroy this option and go to the next one */ - iter = this->options.erase(iter); - } - else - { + iter = this->options.erase(iter); + } + else + { /* Just go to the next one */ - ++iter; - } - } - } + ++iter; + } + } + } return this->options.empty(); } void complete_remove( const wchar_t *cmd, - bool cmd_is_path, - wchar_t short_opt, - const wchar_t *long_opt ) + bool cmd_is_path, + wchar_t short_opt, + const wchar_t *long_opt ) { - CHECK( cmd, ); + CHECK( cmd, ); scoped_lock lock(completion_lock); scoped_lock lock2(completion_entry_lock); - + completion_entry_t tmp_entry(cmd, cmd_is_path, L"", false); completion_entry_set_t::iterator iter = completion_set.find(&tmp_entry); if (iter != completion_set.end()) { @@ -625,10 +625,10 @@ static wcstring format_error(const wchar_t *prefix, const wcstring &str) { */ static void parse_cmd_string(const wcstring &str, wcstring &path, wcstring &cmd) { if (! path_get_path(str, &path)) { - /** Use the empty string as the 'path' for commands that can not be found. */ + /** Use the empty string as the 'path' for commands that can not be found. */ path = L""; } - + /* Make sure the path is not included in the command */ size_t last_slash = str.find_last_of(L'/'); if (last_slash != wcstring::npos) { @@ -639,221 +639,221 @@ static void parse_cmd_string(const wcstring &str, wcstring &path, wcstring &cmd) } int complete_is_valid_option( const wcstring &str, - const wcstring &opt, - wcstring_list_t *errors, - bool allow_autoload ) + const wcstring &opt, + wcstring_list_t *errors, + bool allow_autoload ) { wcstring cmd, path; - bool found_match = false; - bool authoritative = true; - int opt_found=0; - std::set gnu_match_set; - bool is_gnu_opt=false; - bool is_old_opt=false; - bool is_short_opt=false; - bool is_gnu_exact=false; - size_t gnu_opt_len=0; - + bool found_match = false; + bool authoritative = true; + int opt_found=0; + std::set gnu_match_set; + bool is_gnu_opt=false; + bool is_old_opt=false; + bool is_short_opt=false; + bool is_gnu_exact=false; + size_t gnu_opt_len=0; + if (opt.empty()) return false; - - std::vector short_validated; - /* - Check some generic things like -- and - options. - */ - switch( opt.size() ) - { - case 0: - case 1: - { - return true; - } - - case 2: - { - if( opt == L"--" ) - { - return true; - } - break; - } - } - - if( opt.at(0) != L'-' ) - { - if( errors ) + std::vector short_validated; + /* + Check some generic things like -- and - options. + */ + switch( opt.size() ) + { + + case 0: + case 1: + { + return true; + } + + case 2: + { + if( opt == L"--" ) + { + return true; + } + break; + } + } + + if( opt.at(0) != L'-' ) + { + if( errors ) errors->push_back(L"Option does not begin with a '-'"); - return false; - } + return false; + } short_validated.resize(opt.size(), 0); - - is_gnu_opt = opt.at(1) == L'-'; - if( is_gnu_opt ) - { - size_t opt_end = opt.find(L'='); - if( opt_end != wcstring::npos ) - { - gnu_opt_len = opt_end-2; - } - else - { - gnu_opt_len = opt.size() - 2; - } - } - - parse_cmd_string( str, path, cmd ); - /* - Make sure completions are loaded for the specified command - */ - if (allow_autoload) { + is_gnu_opt = opt.at(1) == L'-'; + if( is_gnu_opt ) + { + size_t opt_end = opt.find(L'='); + if( opt_end != wcstring::npos ) + { + gnu_opt_len = opt_end-2; + } + else + { + gnu_opt_len = opt.size() - 2; + } + } + + parse_cmd_string( str, path, cmd ); + + /* + Make sure completions are loaded for the specified command + */ + if (allow_autoload) { complete_load( cmd, false ); } - scoped_lock lock(completion_lock); + scoped_lock lock(completion_lock); scoped_lock lock2(completion_entry_lock); for (completion_entry_set_t::const_iterator iter = completion_set.begin(); iter != completion_set.end(); ++iter) - { + { const completion_entry_t *i = *iter; - const wcstring &match = i->cmd_is_path ? path : cmd; + const wcstring &match = i->cmd_is_path ? path : cmd; - if( !wildcard_match( match, i->cmd ) ) - { - continue; - } - - found_match = true; + if( !wildcard_match( match, i->cmd ) ) + { + continue; + } - if (! i->authoritative) - { - authoritative = false; - break; - } + found_match = true; + + if (! i->authoritative) + { + authoritative = false; + break; + } const option_list_t &options = i->get_options(); - if( is_gnu_opt ) - { + if( is_gnu_opt ) + { for (option_list_t::const_iterator iter = options.begin(); iter != options.end(); ++iter) { const complete_entry_opt_t &o = *iter; - if (o.old_mode ) - { - continue; - } - + if (o.old_mode ) + { + continue; + } + if (opt.compare(2, gnu_opt_len, o.long_opt) == 0) - { + { gnu_match_set.insert(o.long_opt); - if (opt.compare(2, o.long_opt.size(), o.long_opt)) - { - is_gnu_exact = true; - } - } - } - } - else - { - /* Check for old style options */ + if (opt.compare(2, o.long_opt.size(), o.long_opt)) + { + is_gnu_exact = true; + } + } + } + } + else + { + /* Check for old style options */ for (option_list_t::const_iterator iter = options.begin(); iter != options.end(); ++iter) - { + { const complete_entry_opt_t &o = *iter; - - if( !o.old_mode ) - continue; + + if( !o.old_mode ) + continue; - if( opt.compare(1, wcstring::npos, o.long_opt )==0) - { - opt_found = true; - is_old_opt = true; - break; - } + if( opt.compare(1, wcstring::npos, o.long_opt )==0) + { + opt_found = true; + is_old_opt = true; + break; + } - } + } - if( is_old_opt ) - break; + if( is_old_opt ) + break; for (size_t opt_idx = 1; opt_idx < opt.size(); opt_idx++) - { + { const wcstring &short_opt_str = i->get_short_opt_str(); size_t str_idx = short_opt_str.find(opt.at(opt_idx)); - if (str_idx != wcstring::npos ) - { - if (str_idx + 1 < short_opt_str.size() && short_opt_str.at(str_idx + 1) == L':' ) - { - /* - This is a short option with an embedded argument, - call complete_is_valid_argument on the argument. - */ + if (str_idx != wcstring::npos ) + { + if (str_idx + 1 < short_opt_str.size() && short_opt_str.at(str_idx + 1) == L':' ) + { + /* + This is a short option with an embedded argument, + call complete_is_valid_argument on the argument. + */ const wcstring nopt = L"-" + opt.substr(1, 1); - short_validated.at(opt_idx) = - complete_is_valid_argument( str, nopt, opt.substr(2)); - } - else - { - short_validated.at(opt_idx) = true; - } - } - } - } - } + short_validated.at(opt_idx) = + complete_is_valid_argument( str, nopt, opt.substr(2)); + } + else + { + short_validated.at(opt_idx) = true; + } + } + } + } + } - if( authoritative ) - { + if( authoritative ) + { - if( !is_gnu_opt && !is_old_opt ) - is_short_opt = 1; + if( !is_gnu_opt && !is_old_opt ) + is_short_opt = 1; - if( is_short_opt ) - { - opt_found=1; - for( size_t j=1; jpush_back(format_error(_(L"Unknown option: "), str.c_str())); - } + } - opt_found = 0; - break; - } + opt_found = 0; + break; + } - } - } + } + } - if( is_gnu_opt ) - { - opt_found = is_gnu_exact || (gnu_match_set.size() == 1); - if( errors && !opt_found ) - { + if( is_gnu_opt ) + { + opt_found = is_gnu_exact || (gnu_match_set.size() == 1); + if( errors && !opt_found ) + { const wchar_t *prefix; if( gnu_match_set.empty()) - { + { prefix = _(L"Unknown option: "); - } - else - { + } + else + { prefix = _(L"Multiple matches for option: "); - } + } errors->push_back(format_error(prefix, opt)); - } - } - } + } + } + } return (authoritative && found_match)?opt_found:true; } bool complete_is_valid_argument( const wcstring &str, const wcstring &opt, const wcstring &arg ) { - return true; + return true; } @@ -884,21 +884,21 @@ void completer_t::complete_strings( const wcstring &wc_escaped, wcstring tmp = wc_escaped; if (! expand_one(tmp, EXPAND_SKIP_CMDSUBST | EXPAND_SKIP_WILDCARDS | this->expand_flags())) return; - - const wchar_t *wc = parse_util_unescape_wildcards( tmp.c_str() ); - - for( size_t i=0; i< possible_comp.size(); i++ ) - { - wcstring temp = possible_comp.at( i ).completion; - const wchar_t *next_str = temp.empty()?NULL:temp.c_str(); - if( next_str ) - { - wildcard_complete( next_str, wc, desc, desc_func, this->completions, flags ); - } - } + const wchar_t *wc = parse_util_unescape_wildcards( tmp.c_str() ); - free( (void *)wc ); + for( size_t i=0; i< possible_comp.size(); i++ ) + { + wcstring temp = possible_comp.at( i ).completion; + const wchar_t *next_str = temp.empty()?NULL:temp.c_str(); + + if( next_str ) + { + wildcard_complete( next_str, wc, desc, desc_func, this->completions, flags ); + } + } + + free( (void *)wc ); } /** @@ -908,118 +908,118 @@ void completer_t::complete_strings( const wcstring &wc_escaped, void completer_t::complete_cmd_desc( const wcstring &str ) { ASSERT_IS_MAIN_THREAD(); - - const wchar_t *cmd_start; - int skip; - + + const wchar_t *cmd_start; + int skip; + const wchar_t * const cmd = str.c_str(); - cmd_start=wcsrchr(cmd, L'/'); + cmd_start=wcsrchr(cmd, L'/'); - if( cmd_start ) - cmd_start++; - else - cmd_start = cmd; + if( cmd_start ) + cmd_start++; + else + cmd_start = cmd; - /* - Using apropos with a single-character search term produces far - to many results - require at least two characters if we don't - know the location of the whatis-database. - */ - if (wcslen(cmd_start) < 2) - return; + /* + Using apropos with a single-character search term produces far + to many results - require at least two characters if we don't + know the location of the whatis-database. + */ + if (wcslen(cmd_start) < 2) + return; - if( wildcard_has( cmd_start, 0 ) ) - { - return; - } + if( wildcard_has( cmd_start, 0 ) ) + { + return; + } + + skip = 1; + + for( size_t i=0; i< this->completions.size(); i++ ) + { + const completion_t &c = this->completions.at ( i ); + + if( c.completion.empty() || (c.completion[c.completion.size()-1] != L'/' )) + { + skip = 0; + break; + } + + } + + if( skip ) + { + return; + } - skip = 1; - - for( size_t i=0; i< this->completions.size(); i++ ) - { - const completion_t &c = this->completions.at ( i ); - - if( c.completion.empty() || (c.completion[c.completion.size()-1] != L'/' )) - { - skip = 0; - break; - } - - } - - if( skip ) - { - return; - } - wcstring lookup_cmd(L"__fish_describe_command "); lookup_cmd.append(escape_string(cmd_start, 1)); std::map lookup; - /* - First locate a list of possible descriptions using a single - call to apropos or a direct search if we know the location - of the whatis database. This can take some time on slower - systems with a large set of manuals, but it should be ok - since apropos is only called once. - */ + /* + First locate a list of possible descriptions using a single + call to apropos or a direct search if we know the location + of the whatis database. This can take some time on slower + systems with a large set of manuals, but it should be ok + since apropos is only called once. + */ wcstring_list_t list; - if( exec_subshell( lookup_cmd, list ) != -1 ) - { - - /* - Then discard anything that is not a possible completion and put - the result into a hashtable with the completion as key and the - description as value. + if( exec_subshell( lookup_cmd, list ) != -1 ) + { - Should be reasonably fast, since no memory allocations are needed. - */ - for( size_t i=0; i < list.size(); i++ ) - { + /* + Then discard anything that is not a possible completion and put + the result into a hashtable with the completion as key and the + description as value. + + Should be reasonably fast, since no memory allocations are needed. + */ + for( size_t i=0; i < list.size(); i++ ) + { const wcstring &elstr = list.at(i); - + const wcstring fullkey(elstr, wcslen(cmd_start)); - + size_t tab_idx = fullkey.find(L'\t'); - if( tab_idx == wcstring::npos ) - continue; - + if( tab_idx == wcstring::npos ) + continue; + const wcstring key(fullkey, 0, tab_idx); wcstring val(fullkey, tab_idx + 1); - /* - And once again I make sure the first character is uppercased - because I like it that way, and I get to decide these - things. - */ + /* + And once again I make sure the first character is uppercased + because I like it that way, and I get to decide these + things. + */ if (! val.empty()) val[0]=towupper(val[0]); - + lookup[key] = val; - } + } - /* - Then do a lookup on every completion and if a match is found, - change to the new description. + /* + Then do a lookup on every completion and if a match is found, + change to the new description. - This needs to do a reallocation for every description added, but - there shouldn't be that many completions, so it should be ok. - */ - for( size_t i=0; icompletions.size(); i++ ) - { + This needs to do a reallocation for every description added, but + there shouldn't be that many completions, so it should be ok. + */ + for( size_t i=0; icompletions.size(); i++ ) + { completion_t &completion = this->completions.at(i); const wcstring &el = completion.completion; if (el.empty()) continue; - + std::map::iterator new_desc_iter = lookup.find(el); if (new_desc_iter != lookup.end()) completion.description = new_desc_iter->second; - } - } - + } + } + } /** @@ -1032,7 +1032,7 @@ static wcstring complete_function_desc( const wcstring &fn ) if (! has_description) { function_get_definition(fn, &result); } - return result; + return result; } @@ -1050,44 +1050,44 @@ void completer_t::complete_cmd( const wcstring &str_cmd, bool use_function, bool /* Paranoia */ if (str_cmd.empty()) return; - - std::vector possible_comp; + + std::vector possible_comp; env_var_t cdpath = env_get_string(L"CDPATH"); if (cdpath.missing_or_empty()) cdpath = L"."; - - const bool wants_description = (type == COMPLETE_DEFAULT); - - if (str_cmd.find(L'/') != wcstring::npos || str_cmd.at(0) == L'~') - { - if( use_command ) - { - - if( expand_string(str_cmd, this->completions, ACCEPT_INCOMPLETE | EXECUTABLES_ONLY | this->expand_flags() ) != EXPAND_ERROR ) - { + const bool wants_description = (type == COMPLETE_DEFAULT); + + if (str_cmd.find(L'/') != wcstring::npos || str_cmd.at(0) == L'~') + { + + if( use_command ) + { + + if( expand_string(str_cmd, this->completions, ACCEPT_INCOMPLETE | EXECUTABLES_ONLY | this->expand_flags() ) != EXPAND_ERROR ) + { if (wants_description) { this->complete_cmd_desc( str_cmd ); } - } - } - } - else - { - if( use_command ) - { - - const env_var_t path = env_get_string(L"PATH"); - if( !path.missing() ) - { + } + } + } + else + { + if( use_command ) + { + + const env_var_t path = env_get_string(L"PATH"); + if( !path.missing() ) + { wcstring base_path; wcstokenizer tokenizer(path, ARRAY_SEP_STR); - while (tokenizer.next(base_path)) - { + while (tokenizer.next(base_path)) + { if (base_path.empty()) continue; - + /* Make sure the base path ends with a slash */ if (base_path.at(base_path.size() - 1) != L'/') base_path.push_back(L'/'); @@ -1096,51 +1096,51 @@ void completer_t::complete_cmd( const wcstring &str_cmd, bool use_function, bool nxt_completion.append(str_cmd); size_t prev_count = this->completions.size(); - if( expand_string( nxt_completion, - this->completions, - ACCEPT_INCOMPLETE | EXECUTABLES_ONLY | this->expand_flags() ) != EXPAND_ERROR ) - { + if( expand_string( nxt_completion, + this->completions, + ACCEPT_INCOMPLETE | EXECUTABLES_ONLY | this->expand_flags() ) != EXPAND_ERROR ) + { /* For all new completions, if COMPLETE_NO_CASE is set, then use only the last path component */ - for( size_t i=prev_count; i< this->completions.size(); i++ ) - { - completion_t &c = this->completions.at( i ); - if(c.flags & COMPLETE_NO_CASE ) - { - - c.completion.erase(0, base_path.size()); - } - } - } - } - if (wants_description) - this->complete_cmd_desc( str_cmd ); - } - } - - /* - These return the original strings - don't free them - */ + for( size_t i=prev_count; i< this->completions.size(); i++ ) + { + completion_t &c = this->completions.at( i ); + if(c.flags & COMPLETE_NO_CASE ) + { - if( use_function ) - { - //function_get_names( &possible_comp, cmd[0] == L'_' ); + c.completion.erase(0, base_path.size()); + } + } + } + } + if (wants_description) + this->complete_cmd_desc( str_cmd ); + } + } + + /* + These return the original strings - don't free them + */ + + if( use_function ) + { + //function_get_names( &possible_comp, cmd[0] == L'_' ); wcstring_list_t names = function_get_names(str_cmd.at(0) == L'_' ); for (size_t i=0; i < names.size(); i++) { possible_comp.push_back(completion_t(names.at(i))); } - - this->complete_strings( str_cmd, 0, &complete_function_desc, possible_comp, 0 ); - } - possible_comp.clear(); - - if( use_builtin ) - { - builtin_get_names( possible_comp ); - this->complete_strings( str_cmd, 0, &builtin_get_desc, possible_comp, 0 ); - } + this->complete_strings( str_cmd, 0, &complete_function_desc, possible_comp, 0 ); + } - } + possible_comp.clear(); + + if( use_builtin ) + { + builtin_get_names( possible_comp ); + this->complete_strings( str_cmd, 0, &builtin_get_desc, possible_comp, 0 ); + } + + } } @@ -1161,73 +1161,73 @@ void completer_t::complete_from_args( const wcstring &str, const wcstring &desc, complete_flags_t flags ) { - - std::vector possible_comp; + + std::vector possible_comp; bool is_autosuggest = (this->type == COMPLETE_AUTOSUGGEST); parser_t parser(is_autosuggest ? PARSER_TYPE_COMPLETIONS_ONLY : PARSER_TYPE_GENERAL, false); - /* If type is COMPLETE_AUTOSUGGEST, it means we're on a background thread, so don't call proc_push_interactive */ + /* If type is COMPLETE_AUTOSUGGEST, it means we're on a background thread, so don't call proc_push_interactive */ if (! is_autosuggest) proc_push_interactive(0); - parser.eval_args( args.c_str(), possible_comp ); + parser.eval_args( args.c_str(), possible_comp ); if (! is_autosuggest) proc_pop_interactive(); - - this->complete_strings( str, desc.c_str(), 0, possible_comp, flags ); + + this->complete_strings( str, desc.c_str(), 0, possible_comp, flags ); } /** Match against an old style long option */ static int param_match_old( const complete_entry_opt_t *e, - const wchar_t *optstr ) + const wchar_t *optstr ) { - return (optstr[0] == L'-') && (e->long_opt == &optstr[1]); + return (optstr[0] == L'-') && (e->long_opt == &optstr[1]); } /** Match a parameter */ static int param_match( const complete_entry_opt_t *e, - const wchar_t *optstr ) + const wchar_t *optstr ) { - if( e->short_opt != L'\0' && - e->short_opt == optstr[1] ) - return 1; + if( e->short_opt != L'\0' && + e->short_opt == optstr[1] ) + return 1; - if( !e->old_mode && (wcsncmp( L"--", optstr, 2 ) == 0 )) - { - if( e->long_opt == &optstr[2]) - { - return 1; - } - } + if( !e->old_mode && (wcsncmp( L"--", optstr, 2 ) == 0 )) + { + if( e->long_opt == &optstr[2]) + { + return 1; + } + } - return 0; + return 0; } /** Test if a string is an option with an argument, like --color=auto or -I/usr/include */ static wchar_t *param_match2( const complete_entry_opt_t *e, - const wchar_t *optstr ) + const wchar_t *optstr ) { - if( e->short_opt != L'\0' && e->short_opt == optstr[1] ) - return (wchar_t *)&optstr[2]; - if( !e->old_mode && (wcsncmp( L"--", optstr, 2 ) == 0) ) - { - size_t len = e->long_opt.size(); + if( e->short_opt != L'\0' && e->short_opt == optstr[1] ) + return (wchar_t *)&optstr[2]; + if( !e->old_mode && (wcsncmp( L"--", optstr, 2 ) == 0) ) + { + size_t len = e->long_opt.size(); - if( wcsncmp( e->long_opt.c_str(), &optstr[2],len ) == 0 ) - { - if( optstr[len+2] == L'=' ) - return (wchar_t *)&optstr[len+3]; - } - } - return 0; + if( wcsncmp( e->long_opt.c_str(), &optstr[2],len ) == 0 ) + { + if( optstr[len+2] == L'=' ) + return (wchar_t *)&optstr[len+3]; + } + } + return 0; } /** @@ -1237,41 +1237,41 @@ static int short_ok( const wcstring &arg_str, wchar_t nextopt, const wcstring &a { const wchar_t *arg = arg_str.c_str(); const wchar_t *allopt = allopt_str.c_str(); - const wchar_t *ptr; + const wchar_t *ptr; - if( arg[0] != L'-') - return arg[0] == L'\0'; - if( arg[1] == L'-' ) - return 0; + if( arg[0] != L'-') + return arg[0] == L'\0'; + if( arg[1] == L'-' ) + return 0; - if( wcschr( arg, nextopt ) != 0 ) - return 0; + if( wcschr( arg, nextopt ) != 0 ) + return 0; - for( ptr = arg+1; *ptr; ptr++ ) - { - const wchar_t *tmp = wcschr( allopt, *ptr ); - /* Unknown option */ - if( tmp == 0 ) - { - /*fwprintf( stderr, L"Unknown option %lc", *ptr );*/ + for( ptr = arg+1; *ptr; ptr++ ) + { + const wchar_t *tmp = wcschr( allopt, *ptr ); + /* Unknown option */ + if( tmp == 0 ) + { + /*fwprintf( stderr, L"Unknown option %lc", *ptr );*/ - return 0; - } + return 0; + } - if( *(tmp+1) == L':' ) - { -/* fwprintf( stderr, L"Woot %ls", allopt );*/ - return 0; - } + if( *(tmp+1) == L':' ) + { +/* fwprintf( stderr, L"Woot %ls", allopt );*/ + return 0; + } - } + } - return 1; + return 1; } void complete_load( const wcstring &name, bool reload ) { - completion_autoloader.load( name, reload ); + completion_autoloader.load( name, reload ); } /** @@ -1288,7 +1288,7 @@ bool completer_t::complete_param( const wcstring &scmd_orig, const wcstring &spo const wchar_t * const cmd_orig = scmd_orig.c_str(), * const popt = spopt.c_str(), * const str = sstr.c_str(); - bool use_common=1, use_files=1; + bool use_common=1, use_files=1; wcstring cmd, path; parse_cmd_string(cmd_orig, path, cmd); @@ -1305,7 +1305,7 @@ bool completer_t::complete_param( const wcstring &scmd_orig, const wcstring &spo this->commands_to_load.push_back(cmd); } } - + /* Make a list of lists of all options that we care about */ std::vector all_options; { @@ -1319,200 +1319,200 @@ bool completer_t::complete_param( const wcstring &scmd_orig, const wcstring &spo { continue; } - + /* Copy all of their options into our list */ all_options.push_back(local_options_t()); all_options.back().short_opt_str = i->get_short_opt_str(); all_options.back().options = i->get_options(); //Oof, this is a lot of copying } } - + /* Now release the lock and test each option that we captured above. We have to do this outside the lock because callouts (like the condition) may add or remove completions. See https://github.com/ridiculousfish/fishfish/issues/2 */ for (std::vector::const_iterator iter = all_options.begin(); iter != all_options.end(); iter++) { const option_list_t &options = iter->options; - use_common=1; - if( use_switches ) - { - - if( str[0] == L'-' ) - { - /* Check if we are entering a combined option and argument - (like --color=auto or -I/usr/include) */ + use_common=1; + if( use_switches ) + { + + if( str[0] == L'-' ) + { + /* Check if we are entering a combined option and argument + (like --color=auto or -I/usr/include) */ for (option_list_t::const_iterator oiter = options.begin(); oiter != options.end(); ++oiter) - { - const complete_entry_opt_t *o = &*oiter; - wchar_t *arg; - if( (arg=param_match2( o, str ))!=0 && this->condition_test( o->condition )) - { + { + const complete_entry_opt_t *o = &*oiter; + wchar_t *arg; + if( (arg=param_match2( o, str ))!=0 && this->condition_test( o->condition )) + { if (o->result_mode & NO_COMMON) use_common = false; if (o->result_mode & NO_FILES) use_files = false; - complete_from_args( arg, o->comp, o->localized_desc(), o->flags ); - } + complete_from_args( arg, o->comp, o->localized_desc(), o->flags ); + } - } - } - else if( popt[0] == L'-' ) - { - /* Set to true if we found a matching old-style switch */ - int old_style_match = 0; - - /* - If we are using old style long options, check for them - first - */ + } + } + else if( popt[0] == L'-' ) + { + /* Set to true if we found a matching old-style switch */ + int old_style_match = 0; + + /* + If we are using old style long options, check for them + first + */ for (option_list_t::const_iterator oiter = options.begin(); oiter != options.end(); ++oiter) - { + { const complete_entry_opt_t *o = &*oiter; - if( o->old_mode ) - { - if( param_match_old( o, popt ) && this->condition_test( o->condition )) - { - old_style_match = 1; + if( o->old_mode ) + { + if( param_match_old( o, popt ) && this->condition_test( o->condition )) + { + old_style_match = 1; if (o->result_mode & NO_COMMON) use_common = false; if (o->result_mode & NO_FILES) use_files = false; - complete_from_args( str, o->comp, o->localized_desc(), o->flags ); - } - } - } - - /* - No old style option matched, or we are not using old - style options. We check if any short (or gnu style - options do. - */ - if( !old_style_match ) - { + complete_from_args( str, o->comp, o->localized_desc(), o->flags ); + } + } + } + + /* + No old style option matched, or we are not using old + style options. We check if any short (or gnu style + options do. + */ + if( !old_style_match ) + { for (option_list_t::const_iterator oiter = options.begin(); oiter != options.end(); ++oiter) { const complete_entry_opt_t *o = &*oiter; - /* - Gnu-style options with _optional_ arguments must - be specified as a single token, so that it can - be differed from a regular argument. - */ - if( !o->old_mode && ! o->long_opt.empty() && !(o->result_mode & NO_COMMON) ) - continue; + /* + Gnu-style options with _optional_ arguments must + be specified as a single token, so that it can + be differed from a regular argument. + */ + if( !o->old_mode && ! o->long_opt.empty() && !(o->result_mode & NO_COMMON) ) + continue; - if( param_match( o, popt ) && this->condition_test( o->condition )) - { + if( param_match( o, popt ) && this->condition_test( o->condition )) + { if (o->result_mode & NO_COMMON) use_common = false; if (o->result_mode & NO_FILES) use_files = false; - complete_from_args( str, o->comp.c_str(), o->localized_desc(), o->flags ); + complete_from_args( str, o->comp.c_str(), o->localized_desc(), o->flags ); - } - } - } - } - } - - if( use_common ) - { + } + } + } + } + } + + if( use_common ) + { for (option_list_t::const_iterator oiter = options.begin(); oiter != options.end(); ++oiter) { const complete_entry_opt_t *o = &*oiter; - /* - If this entry is for the base command, - check if any of the arguments match - */ + /* + If this entry is for the base command, + check if any of the arguments match + */ - if( !this->condition_test( o->condition )) - continue; + if( !this->condition_test( o->condition )) + continue; - if( (o->short_opt == L'\0' ) && (o->long_opt[0]==L'\0')) - { - use_files &= ((o->result_mode & NO_FILES )==0); - complete_from_args( str, o->comp, o->localized_desc(), o->flags ); - } - - if( wcslen(str) > 0 && use_switches ) - { - /* - Check if the short style option matches - */ - if( o->short_opt != L'\0' && - short_ok(str, o->short_opt, iter->short_opt_str)) - { - const wchar_t *desc = o->localized_desc(); - wchar_t completion[2]; - completion[0] = o->short_opt; - completion[1] = 0; - - append_completion( this->completions, completion, desc, 0 ); + if( (o->short_opt == L'\0' ) && (o->long_opt[0]==L'\0')) + { + use_files &= ((o->result_mode & NO_FILES )==0); + complete_from_args( str, o->comp, o->localized_desc(), o->flags ); + } - } + if( wcslen(str) > 0 && use_switches ) + { + /* + Check if the short style option matches + */ + if( o->short_opt != L'\0' && + short_ok(str, o->short_opt, iter->short_opt_str)) + { + const wchar_t *desc = o->localized_desc(); + wchar_t completion[2]; + completion[0] = o->short_opt; + completion[1] = 0; + + append_completion( this->completions, completion, desc, 0 ); + + } + + /* + Check if the long style option matches + */ + if( o->long_opt[0] != L'\0' ) + { + int match=0, match_no_case=0; - /* - Check if the long style option matches - */ - if( o->long_opt[0] != L'\0' ) - { - int match=0, match_no_case=0; - wcstring whole_opt; whole_opt.append(o->old_mode?L"-":L"--"); whole_opt.append(o->long_opt); match = string_prefixes_string(str, whole_opt); - if( !match ) - { - match_no_case = wcsncasecmp( str, whole_opt.c_str(), wcslen(str) )==0; - } - - if( match || match_no_case ) - { - int has_arg=0; /* Does this switch have any known arguments */ - int req_arg=0; /* Does this switch _require_ an argument */ + if( !match ) + { + match_no_case = wcsncasecmp( str, whole_opt.c_str(), wcslen(str) )==0; + } - size_t offset = 0; - complete_flags_t flags = 0; + if( match || match_no_case ) + { + int has_arg=0; /* Does this switch have any known arguments */ + int req_arg=0; /* Does this switch _require_ an argument */ - - if( match ) - offset = wcslen( str ); - else - flags = COMPLETE_NO_CASE; + size_t offset = 0; + complete_flags_t flags = 0; - has_arg = ! o->comp.empty(); - req_arg = (o->result_mode & NO_COMMON ); - if( !o->old_mode && ( has_arg && !req_arg ) ) - { - - /* - Optional arguments to a switch can - only be handled using the '=', so we - add it as a completion. By default - we avoid using '=' and instead rely - on '--switch switch-arg', since it - is more commonly supported by - homebrew getopt-like functions. - */ - wcstring completion = format_string(L"%ls=", whole_opt.c_str()+offset); - append_completion( this->completions, - completion, - C_(o->desc.c_str()), - flags ); - - } - - append_completion( this->completions, - whole_opt.c_str() + offset, - C_(o->desc.c_str()), - flags ); - } - } - } - } - } - } - - return use_files; + if( match ) + offset = wcslen( str ); + else + flags = COMPLETE_NO_CASE; + + has_arg = ! o->comp.empty(); + req_arg = (o->result_mode & NO_COMMON ); + + if( !o->old_mode && ( has_arg && !req_arg ) ) + { + + /* + Optional arguments to a switch can + only be handled using the '=', so we + add it as a completion. By default + we avoid using '=' and instead rely + on '--switch switch-arg', since it + is more commonly supported by + homebrew getopt-like functions. + */ + wcstring completion = format_string(L"%ls=", whole_opt.c_str()+offset); + append_completion( this->completions, + completion, + C_(o->desc.c_str()), + flags ); + + } + + append_completion( this->completions, + whole_opt.c_str() + offset, + C_(o->desc.c_str()), + flags ); + } + } + } + } + } + } + + return use_files; } /** @@ -1521,32 +1521,32 @@ bool completer_t::complete_param( const wcstring &scmd_orig, const wcstring &spo void completer_t::complete_param_expand( const wcstring &sstr, bool do_file) { const wchar_t * const str = sstr.c_str(); - const wchar_t *comp_str; - - if (string_prefixes_string( L"--", sstr) && (comp_str = wcschr(str, L'=' ) ) ) - { - comp_str++; - } - else - { - comp_str = str; - } - + const wchar_t *comp_str; + + if (string_prefixes_string( L"--", sstr) && (comp_str = wcschr(str, L'=' ) ) ) + { + comp_str++; + } + else + { + comp_str = str; + } + expand_flags_t flags = EXPAND_SKIP_CMDSUBST | ACCEPT_INCOMPLETE; - + if (! do_file) flags |= EXPAND_SKIP_WILDCARDS; - + /* Squelch file descriptions per issue 254 */ if (type == COMPLETE_AUTOSUGGEST || do_file) flags |= EXPAND_NO_DESCRIPTIONS; - - if( expand_string( comp_str, - this->completions, - flags | this->expand_flags() ) == EXPAND_ERROR ) - { - debug( 3, L"Error while expanding string '%ls'", comp_str ); - } + + if( expand_string( comp_str, + this->completions, + flags | this->expand_flags() ) == EXPAND_ERROR ) + { + debug( 3, L"Error while expanding string '%ls'", comp_str ); + } } void completer_t::debug_print_completions() @@ -1562,33 +1562,33 @@ void completer_t::debug_print_completions() bool completer_t::complete_variable(const wcstring &str, size_t start_offset) { const wchar_t * const whole_var = str.c_str(); - const wchar_t *var = &whole_var[start_offset]; - size_t varlen = wcslen( var ); - int res = 0; + const wchar_t *var = &whole_var[start_offset]; + size_t varlen = wcslen( var ); + int res = 0; bool wants_description = (type != COMPLETE_AUTOSUGGEST); - + const wcstring_list_t names = env_get_names(0); - for( size_t i=0; i namelen ) - continue; + if( varlen > namelen ) + continue; - match = string_prefixes_string(var, env_name); - - if( !match ) - { - match_no_case = ( wcsncasecmp( var, env_name.c_str(), varlen) == 0 ); - } + match = string_prefixes_string(var, env_name); - if( match || match_no_case ) - { + if( !match ) + { + match_no_case = ( wcsncasecmp( var, env_name.c_str(), varlen) == 0 ); + } + + if( match || match_no_case ) + { wcstring comp; int flags = 0; - + if( match ) { comp.append(env_name.c_str() + varlen); @@ -1599,51 +1599,51 @@ bool completer_t::complete_variable(const wcstring &str, size_t start_offset) comp.append(env_name); flags = COMPLETE_NO_CASE | COMPLETE_DONT_ESCAPE; } - + wcstring desc; if (wants_description) { env_var_t value_unescaped = env_get_string( env_name ); if (value_unescaped.missing()) continue; - + wcstring value = expand_escape_variable( value_unescaped ); if (type != COMPLETE_AUTOSUGGEST) desc = format_string(COMPLETE_VAR_DESC_VAL, value.c_str()); } - + append_completion( this->completions, comp.c_str(), desc.c_str(), flags ); res =1; - - } - } - return res; + } + } + + return res; } /** Search the specified string for the \$ sign. If found, try to - complete as an environment variable. + complete as an environment variable. \return 0 if unable to complete, 1 otherwise */ bool completer_t::try_complete_variable( const wcstring &str ) { - size_t i = str.size(); - while (i--) - { + size_t i = str.size(); + while (i--) + { wchar_t c = str.at(i); - if( c == L'$' ) - { -/* wprintf( L"Var prefix \'%ls\'\n", &cmd[i+1] );*/ - return this->complete_variable( str, i+1 ); - } - if( !isalnum(c) && c != L'_' ) - { - return false; - } - } - return false; + if( c == L'$' ) + { +/* wprintf( L"Var prefix \'%ls\'\n", &cmd[i+1] );*/ + return this->complete_variable( str, i+1 ); + } + if( !isalnum(c) && c != L'_' ) + { + return false; + } + } + return false; } /** @@ -1655,300 +1655,300 @@ bool completer_t::try_complete_variable( const wcstring &str ) bool completer_t::try_complete_user( const wcstring &str ) { const wchar_t *cmd = str.c_str(); - const wchar_t *first_char=cmd; - int res=0; - double start_time = timef(); - - if( *first_char ==L'~' && !wcschr(first_char, L'/')) - { - const wchar_t *user_name = first_char+1; - const wchar_t *name_end = wcschr( user_name, L'~' ); - if( name_end == 0 ) - { - struct passwd *pw; - size_t name_len = wcslen( user_name ); - - setpwent(); - - while((pw=getpwent()) != 0) - { - double current_time = timef(); - wchar_t *pw_name; + const wchar_t *first_char=cmd; + int res=0; + double start_time = timef(); - if( current_time - start_time > 0.2 ) - { - return 1; - } - - pw_name = str2wcs( pw->pw_name ); + if( *first_char ==L'~' && !wcschr(first_char, L'/')) + { + const wchar_t *user_name = first_char+1; + const wchar_t *name_end = wcschr( user_name, L'~' ); + if( name_end == 0 ) + { + struct passwd *pw; + size_t name_len = wcslen( user_name ); - if( pw_name ) - { - if( wcsncmp( user_name, pw_name, name_len )==0 ) - { - wcstring desc = format_string(COMPLETE_USER_DESC, pw_name); - append_completion( this->completions, - &pw_name[name_len], - desc, - COMPLETE_NO_SPACE ); - - res=1; - } - else if( wcsncasecmp( user_name, pw_name, name_len )==0 ) - { - wcstring name = format_string(L"~%ls", pw_name); + setpwent(); + + while((pw=getpwent()) != 0) + { + double current_time = timef(); + wchar_t *pw_name; + + if( current_time - start_time > 0.2 ) + { + return 1; + } + + pw_name = str2wcs( pw->pw_name ); + + if( pw_name ) + { + if( wcsncmp( user_name, pw_name, name_len )==0 ) + { wcstring desc = format_string(COMPLETE_USER_DESC, pw_name); - - append_completion( this->completions, - name, - desc, - COMPLETE_NO_CASE | COMPLETE_DONT_ESCAPE | COMPLETE_NO_SPACE ); - res=1; - } - free( pw_name ); - } - } - endpwent(); - } - } + append_completion( this->completions, + &pw_name[name_len], + desc, + COMPLETE_NO_SPACE ); - return res; + res=1; + } + else if( wcsncasecmp( user_name, pw_name, name_len )==0 ) + { + wcstring name = format_string(L"~%ls", pw_name); + wcstring desc = format_string(COMPLETE_USER_DESC, pw_name); + + append_completion( this->completions, + name, + desc, + COMPLETE_NO_CASE | COMPLETE_DONT_ESCAPE | COMPLETE_NO_SPACE ); + res=1; + } + free( pw_name ); + } + } + endpwent(); + } + } + + return res; } void complete( const wcstring &cmd, std::vector &comps, complete_type_t type, wcstring_list_t *commands_to_load ) { /* Make our completer */ completer_t completer(cmd, type); - - const wchar_t *tok_begin, *tok_end, *cmdsubst_begin, *cmdsubst_end, *prev_begin, *prev_end; - tokenizer tok; - const wchar_t *current_token=0, *prev_token=0; - wcstring current_command; - int on_command=0; - size_t pos; - bool done=false; - int use_command = 1; - int use_function = 1; - int use_builtin = 1; - int had_ddash = 0; -// debug( 1, L"Complete '%ls'", cmd ); + const wchar_t *tok_begin, *tok_end, *cmdsubst_begin, *cmdsubst_end, *prev_begin, *prev_end; + tokenizer tok; + const wchar_t *current_token=0, *prev_token=0; + wcstring current_command; + int on_command=0; + size_t pos; + bool done=false; + int use_command = 1; + int use_function = 1; + int use_builtin = 1; + int had_ddash = 0; + +// debug( 1, L"Complete '%ls'", cmd ); + + size_t cursor_pos = cmd.size(); - size_t cursor_pos = cmd.size(); - const wchar_t *cmd_cstr = cmd.c_str(); - parse_util_cmdsubst_extent( cmd_cstr, cursor_pos, &cmdsubst_begin, &cmdsubst_end ); - parse_util_token_extent( cmd_cstr, cursor_pos, &tok_begin, &tok_end, &prev_begin, &prev_end ); + parse_util_cmdsubst_extent( cmd_cstr, cursor_pos, &cmdsubst_begin, &cmdsubst_end ); + parse_util_token_extent( cmd_cstr, cursor_pos, &tok_begin, &tok_end, &prev_begin, &prev_end ); - if( !cmdsubst_begin ) - done=1; - + if( !cmdsubst_begin ) + done=1; - /** - If we are completing a variable name or a tilde expansion user - name, we do that and return. No need for any other competions. - */ - if( !done ) - { + /** + If we are completing a variable name or a tilde expansion user + name, we do that and return. No need for any other competions. + */ + + if( !done ) + { wcstring tmp = tok_begin; done = completer.try_complete_variable( tmp ) || completer.try_complete_user( tmp ); - } + } - if( !done ) - { - pos = cursor_pos-(cmdsubst_begin-cmd_cstr); - - wcstring buff = wcstring( cmdsubst_begin, cmdsubst_end-cmdsubst_begin ); + if( !done ) + { + pos = cursor_pos-(cmdsubst_begin-cmd_cstr); - int had_cmd=0; - int end_loop=0; + wcstring buff = wcstring( cmdsubst_begin, cmdsubst_end-cmdsubst_begin ); - tok_init( &tok, buff.c_str(), TOK_ACCEPT_UNFINISHED | TOK_SQUASH_ERRORS ); + int had_cmd=0; + int end_loop=0; - while( tok_has_next( &tok) && !end_loop ) - { + tok_init( &tok, buff.c_str(), TOK_ACCEPT_UNFINISHED | TOK_SQUASH_ERRORS ); - switch( tok_last_type( &tok ) ) - { + while( tok_has_next( &tok) && !end_loop ) + { - case TOK_STRING: - { + switch( tok_last_type( &tok ) ) + { - const wcstring ncmd = tok_last( &tok ); - int is_ddash = (ncmd == L"--") && ( (tok_get_pos( &tok )+2) < (long)pos ); - - if( !had_cmd ) - { + case TOK_STRING: + { - if( parser_keywords_is_subcommand( ncmd ) ) - { - if (ncmd == L"builtin" ) - { - use_function = 0; - use_command = 0; - use_builtin = 1; - } - else if (ncmd == L"command") - { - use_command = 1; - use_function = 0; - use_builtin = 0; - } - break; - } + const wcstring ncmd = tok_last( &tok ); + int is_ddash = (ncmd == L"--") && ( (tok_get_pos( &tok )+2) < (long)pos ); - - if( !is_ddash || - ( (use_command && use_function && use_builtin ) ) ) - { - current_command = ncmd; - - size_t token_end = tok_get_pos( &tok ) + ncmd.size(); - - on_command = (pos <= token_end ); - had_cmd=1; - } + if( !had_cmd ) + { - } - else - { - if( is_ddash ) - { - had_ddash = 1; - } - } - - break; - } - - case TOK_END: - case TOK_PIPE: - case TOK_BACKGROUND: - { - had_cmd=0; - had_ddash = 0; - use_command = 1; - use_function = 1; - use_builtin = 1; - break; - } - - case TOK_ERROR: - { - end_loop=1; - break; - } - - } + if( parser_keywords_is_subcommand( ncmd ) ) + { + if (ncmd == L"builtin" ) + { + use_function = 0; + use_command = 0; + use_builtin = 1; + } + else if (ncmd == L"command") + { + use_command = 1; + use_function = 0; + use_builtin = 0; + } + break; + } - if( tok_get_pos( &tok ) >= (long)pos ) - { - end_loop=1; - } - - tok_next( &tok ); - } + if( !is_ddash || + ( (use_command && use_function && use_builtin ) ) ) + { + current_command = ncmd; - tok_destroy( &tok ); + size_t token_end = tok_get_pos( &tok ) + ncmd.size(); - /* - Get the string to complete - */ + on_command = (pos <= token_end ); + had_cmd=1; + } - current_token = wcsndup( tok_begin, cursor_pos-(tok_begin-cmd_cstr) ); + } + else + { + if( is_ddash ) + { + had_ddash = 1; + } + } - prev_token = prev_begin ? wcsndup( prev_begin, prev_end - prev_begin ): wcsdup(L""); - -// debug( 0, L"on_command: %d, %ls %ls\n", on_command, current_command, current_token ); + break; + } - /* - Check if we are using the 'command' or 'builtin' builtins - _and_ we are writing a switch instead of a command. In that - case, complete using the builtins completions, not using a - subcommand. - */ - - if( (on_command || (wcscmp( current_token, L"--" ) == 0 ) ) && - (current_token[0] == L'-') && - !(use_command && use_function && use_builtin ) ) - { - if( use_command == 0 ) - current_command = L"builtin"; - else - current_command = L"command"; - - had_cmd = 1; - on_command = 0; - } - - /* - Use command completions if in between commands - */ - if( !had_cmd ) - { - on_command=1; - } - - /* - We don't want these to be null - */ + case TOK_END: + case TOK_PIPE: + case TOK_BACKGROUND: + { + had_cmd=0; + had_ddash = 0; + use_command = 1; + use_function = 1; + use_builtin = 1; + break; + } - if( !current_token ) - { - current_token = wcsdup(L""); - } - - if( !prev_token ) - { - prev_token = wcsdup(L""); - } + case TOK_ERROR: + { + end_loop=1; + break; + } + + } + + if( tok_get_pos( &tok ) >= (long)pos ) + { + end_loop=1; + } + + tok_next( &tok ); + + } + + tok_destroy( &tok ); + + /* + Get the string to complete + */ + + current_token = wcsndup( tok_begin, cursor_pos-(tok_begin-cmd_cstr) ); + + prev_token = prev_begin ? wcsndup( prev_begin, prev_end - prev_begin ): wcsdup(L""); + +// debug( 0, L"on_command: %d, %ls %ls\n", on_command, current_command, current_token ); + + /* + Check if we are using the 'command' or 'builtin' builtins + _and_ we are writing a switch instead of a command. In that + case, complete using the builtins completions, not using a + subcommand. + */ + + if( (on_command || (wcscmp( current_token, L"--" ) == 0 ) ) && + (current_token[0] == L'-') && + !(use_command && use_function && use_builtin ) ) + { + if( use_command == 0 ) + current_command = L"builtin"; + else + current_command = L"command"; + + had_cmd = 1; + on_command = 0; + } + + /* + Use command completions if in between commands + */ + if( !had_cmd ) + { + on_command=1; + } + + /* + We don't want these to be null + */ + + if( !current_token ) + { + current_token = wcsdup(L""); + } + + if( !prev_token ) + { + prev_token = wcsdup(L""); + } + + if( current_token && prev_token ) + { + if( on_command ) + { + /* Complete command filename */ + completer.complete_cmd( current_token, use_function, use_builtin, use_command ); + } + else + { + int do_file=0; - if( current_token && prev_token ) - { - if( on_command ) - { - /* Complete command filename */ - completer.complete_cmd( current_token, use_function, use_builtin, use_command ); - } - else - { - int do_file=0; - wcstring current_command_unescape = current_command; wcstring prev_token_unescape = prev_token; wcstring current_token_unescape = current_token; - - if( unescape_string( current_command_unescape, 0 ) && + + if( unescape_string( current_command_unescape, 0 ) && unescape_string( prev_token_unescape, 0 ) && unescape_string( current_token_unescape, UNESCAPE_INCOMPLETE)) - { - do_file = completer.complete_param( current_command_unescape, - prev_token_unescape, - current_token_unescape, + { + do_file = completer.complete_param( current_command_unescape, + prev_token_unescape, + current_token_unescape, !had_ddash ); - } - - /* - If we have found no command specific completions at - all, fall back to using file completions. - */ - if( completer.empty() ) - do_file = 1; - - /* - This function wants the unescaped string - */ - completer.complete_param_expand( current_token, do_file ); - } - } - } - - free( (void *)current_token ); - free( (void *)prev_token ); + } - comps = completer.get_completions(); + /* + If we have found no command specific completions at + all, fall back to using file completions. + */ + if( completer.empty() ) + do_file = 1; + + /* + This function wants the unescaped string + */ + completer.complete_param_expand( current_token, do_file ); + } + } + } + + free( (void *)current_token ); + free( (void *)prev_token ); + + comps = completer.get_completions(); completer.get_commands_to_load(commands_to_load); } @@ -1960,25 +1960,25 @@ void complete( const wcstring &cmd, std::vector &comps, complete_t non-null and longer than 0 characters. */ static void append_switch( wcstring &out, - const wcstring &opt, - const wcstring &argument ) + const wcstring &opt, + const wcstring &argument ) { - if( argument.empty() ) - return; + if( argument.empty() ) + return; wcstring esc = escape_string( argument, 1 ); - append_format( out, L" --%ls %ls", opt.c_str(), esc.c_str() ); + append_format( out, L" --%ls %ls", opt.c_str(), esc.c_str() ); } void complete_print( wcstring &out ) { scoped_lock locker(completion_lock); scoped_lock locker2(completion_entry_lock); - + // Get a list of all completions in a vector, then sort it by order std::vector all_completions(completion_set.begin(), completion_set.end()); sort(all_completions.begin(), all_completions.end(), compare_completions_by_order); - + for (std::vector::const_iterator iter = all_completions.begin(); iter != all_completions.end(); ++iter) { const completion_entry_t *e = *iter; @@ -1986,49 +1986,49 @@ void complete_print( wcstring &out ) for (option_list_t::const_iterator oiter = options.begin(); oiter != options.end(); ++oiter) { const complete_entry_opt_t *o = &*oiter; - const wchar_t *modestr[] = - { - L"", - L" --no-files", - L" --require-parameter", - L" --exclusive" - } - ; + const wchar_t *modestr[] = + { + L"", + L" --no-files", + L" --require-parameter", + L" --exclusive" + } + ; - append_format( out, - L"complete%ls", - modestr[o->result_mode] ); + append_format( out, + L"complete%ls", + modestr[o->result_mode] ); - append_switch( out, - e->cmd_is_path ? L"path" : L"command", - e->cmd ); + append_switch( out, + e->cmd_is_path ? L"path" : L"command", + e->cmd ); - if( o->short_opt != 0 ) - { - append_format( out, - L" --short-option '%lc'", - o->short_opt ); - } + if( o->short_opt != 0 ) + { + append_format( out, + L" --short-option '%lc'", + o->short_opt ); + } - append_switch( out, - o->old_mode?L"old-option":L"long-option", - o->long_opt ); + append_switch( out, + o->old_mode?L"old-option":L"long-option", + o->long_opt ); - append_switch( out, - L"description", - C_(o->desc.c_str()) ); + append_switch( out, + L"description", + C_(o->desc.c_str()) ); - append_switch( out, - L"arguments", - o->comp ); + append_switch( out, + L"arguments", + o->comp ); - append_switch( out, - L"condition", - o->condition ); + append_switch( out, + L"condition", + o->condition ); - out.append( L"\n" ); - } - } + out.append( L"\n" ); + } + } } diff --git a/complete.h b/complete.h index d915a75b0..8b72f2fce 100644 --- a/complete.h +++ b/complete.h @@ -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 &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 &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 &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 &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 ); /** diff --git a/configure.ac b/configure.ac index ddb8a9158..e413be096 100644 --- a/configure.ac +++ b/configure.ac @@ -51,25 +51,25 @@ AC_SUBST(XSEL_MAN_PATH) AC_MSG_CHECKING([if autoconf needs to be run]) if test configure -ot configure.ac; then - AC_MSG_RESULT([yes]) - if which autoconf >/dev/null; then - # No need to provide any error messages if autoconf fails, the - # shell and autconf should take care of that themselves - AC_MSG_NOTICE([running autoconf]) - if autoconf; then - ./configure "$@" - exit - fi - exit 1 - else - AC_MSG_ERROR( - [cannot find the autoconf program in your path. + AC_MSG_RESULT([yes]) + if which autoconf >/dev/null; then + # No need to provide any error messages if autoconf fails, the + # shell and autconf should take care of that themselves + AC_MSG_NOTICE([running autoconf]) + if autoconf; then + ./configure "$@" + exit + fi + exit 1 + else + AC_MSG_ERROR( + [cannot find the autoconf program in your path. This program needs to be run whenever the configure.ac file is modified. Please install it and try again.] - ) - fi + ) + fi else - AC_MSG_RESULT([no]) + AC_MSG_RESULT([no]) fi @@ -82,19 +82,19 @@ fi AC_MSG_CHECKING([if autoheader needs to be run]) if test ! -f ./config.h.in -o config.h.in -ot configure.ac; then - AC_MSG_RESULT([yes]) - if which autoheader >/dev/null; then - AC_MSG_NOTICE([running autoheader]) - autoheader || exit 1 - else - AC_MSG_ERROR( - [cannot find the autoheader program in your path. + AC_MSG_RESULT([yes]) + if which autoheader >/dev/null; then + AC_MSG_NOTICE([running autoheader]) + autoheader || exit 1 + else + AC_MSG_ERROR( + [cannot find the autoheader program in your path. This program needs to be run whenever the configure.ac file is modified. Please install it and try again.] - ) - fi + ) + fi else - AC_MSG_RESULT([no]) + AC_MSG_RESULT([no]) fi @@ -110,30 +110,30 @@ fi for i in /usr/pkg /sw /opt /opt/local /usr/local; do - AC_MSG_CHECKING([for $i/include include directory]) - if test -d $i/include; then - AC_MSG_RESULT(yes) - CXXFLAGS="$CXXFLAGS -I$i/include/" - CFLAGS="$CFLAGS -I$i/include/" - else - AC_MSG_RESULT(no) - fi + AC_MSG_CHECKING([for $i/include include directory]) + if test -d $i/include; then + AC_MSG_RESULT(yes) + CXXFLAGS="$CXXFLAGS -I$i/include/" + CFLAGS="$CFLAGS -I$i/include/" + else + AC_MSG_RESULT(no) + fi - AC_MSG_CHECKING([for $i/lib library directory]) - if test -d $i/lib; then - AC_MSG_RESULT(yes) - LDFLAGS="$LDFLAGS -L$i/lib/ -Wl,-rpath,$i/lib/" - else - AC_MSG_RESULT(no) - fi + AC_MSG_CHECKING([for $i/lib library directory]) + if test -d $i/lib; then + AC_MSG_RESULT(yes) + LDFLAGS="$LDFLAGS -L$i/lib/ -Wl,-rpath,$i/lib/" + else + AC_MSG_RESULT(no) + fi - AC_MSG_CHECKING([for $i/bin command directory]) - if test -d $i/bin; then - AC_MSG_RESULT(yes) - optbindirs="$optbindirs $i/bin" - else - AC_MSG_RESULT(no) - fi + AC_MSG_CHECKING([for $i/bin command directory]) + if test -d $i/bin; then + AC_MSG_RESULT(yes) + optbindirs="$optbindirs $i/bin" + else + AC_MSG_RESULT(no) + fi done @@ -176,24 +176,24 @@ AC_CHECK_PROG( SEQ_FALLBACK, seq, [ ], [seq]) if test "$SEQ_FALLBACK"; then - # - # We already have seq. Check if the seq version is installed by an - # earlier fish version. If it is, we'll replace it. - # + # + # We already have seq. Check if the seq version is installed by an + # earlier fish version. If it is, we'll replace it. + # - file=`which seq` - if test -f "$file"; then + file=`which seq` + if test -f "$file"; then - AC_MSG_CHECKING([if seq comes from a previous fish version]) - shebang=`grep "\(^#!/.*/fish\|^#!/usr/bin/env fish\)" $file` + AC_MSG_CHECKING([if seq comes from a previous fish version]) + shebang=`grep "\(^#!/.*/fish\|^#!/usr/bin/env fish\)" $file` - if test "$shebang"; then - SEQ_FALLBACK=seq - AC_MSG_RESULT(yes, replace it) - else - AC_MSG_RESULT(no, keep it) - fi - fi + if test "$shebang"; then + SEQ_FALLBACK=seq + AC_MSG_RESULT(yes, replace it) + else + AC_MSG_RESULT(no, keep it) + fi + fi fi @@ -202,21 +202,21 @@ fi # AC_ARG_WITH( - xsel, - AC_HELP_STRING( - [--without-xsel], - [do not build the xsel program needed for X clipboard integration. - If build xsel, it will be configured with the same options as fish.] - ), - [xsel=$withval], - [xsel=with_xsel] + xsel, + AC_HELP_STRING( + [--without-xsel], + [do not build the xsel program needed for X clipboard integration. + If build xsel, it will be configured with the same options as fish.] + ), + [xsel=$withval], + [xsel=with_xsel] ) if [[ "$xsel" = "with_xsel" ]]; then - XSEL=xsel-1.2.0 - XSEL_BIN=$XSEL/xsel - XSEL_MAN=xsel.1x - XSEL_MAN_PATH=$XSEL/xsel.1x + XSEL=xsel-1.2.0 + XSEL_BIN=$XSEL/xsel + XSEL_MAN=xsel.1x + XSEL_MAN_PATH=$XSEL/xsel.1x fi @@ -225,17 +225,17 @@ fi # AC_ARG_WITH( - gettext, - AC_HELP_STRING( - [--without-gettext], - [do not translate messages, even if gettext is available] - ), - [local_gettext=$withval], - [local_gettext=yes] + gettext, + AC_HELP_STRING( + [--without-gettext], + [do not translate messages, even if gettext is available] + ), + [local_gettext=$withval], + [local_gettext=yes] ) if test x$local_gettext != xno; then - AC_DEFINE([USE_GETTEXT],[1],[Perform string translations with gettext]) + AC_DEFINE([USE_GETTEXT],[1],[Perform string translations with gettext]) fi @@ -254,31 +254,31 @@ CXXFLAGS="$CXXFLAGS -D_LARGEFILE_SOURCE=1 -D_FILE_OFFSET_BITS=64" if test "$GCC" = yes; then - # - # -fno-optimize-sibling-calls seems to work around a bug where - # sending a SIGWINCH to fish on NetBSD 3.0 causes fish to exit when - # compiled with GCC 3.3.3. This is probably either a compiler bug - # or a libc bug, but adding this flag seems to fix things for - # now. Long term, the real problem should be tracked down and - # truly fixed, at which point we can remove this silly flag. This - # bug has been verified to not exist on Linux using GCC 3.3.3. - # + # + # -fno-optimize-sibling-calls seems to work around a bug where + # sending a SIGWINCH to fish on NetBSD 3.0 causes fish to exit when + # compiled with GCC 3.3.3. This is probably either a compiler bug + # or a libc bug, but adding this flag seems to fix things for + # now. Long term, the real problem should be tracked down and + # truly fixed, at which point we can remove this silly flag. This + # bug has been verified to not exist on Linux using GCC 3.3.3. + # - CXXFLAGS="$CXXFLAGS -fno-optimize-sibling-calls" + CXXFLAGS="$CXXFLAGS -fno-optimize-sibling-calls" - # - # -Wall is there to keep me on my toes - # + # + # -Wall is there to keep me on my toes + # - # Some day... - CXXFLAGS="$CXXFLAGS -Wall" + # Some day... + CXXFLAGS="$CXXFLAGS -Wall" - # - # This is needed in order to get the really cool backtraces - # + # + # This is needed in order to get the really cool backtraces + # - LDFLAGS_FISH="$LDFLAGS_FISH -rdynamic" + LDFLAGS_FISH="$LDFLAGS_FISH -rdynamic" fi @@ -291,38 +291,38 @@ fi AC_MSG_CHECKING([if we are compiling against glibc]) AC_RUN_IFELSE( - [ - AC_LANG_PROGRAM( - [ - #include - #ifdef __GLIBC__ - #define STATUS 0 - #else - #define STATUS 1 - #endif - ], - [ - return STATUS; - ] - ) - ], - [glibc=yes], - [glibc=no] + [ + AC_LANG_PROGRAM( + [ + #include + #ifdef __GLIBC__ + #define STATUS 0 + #else + #define STATUS 1 + #endif + ], + [ + return STATUS; + ] + ) + ], + [glibc=yes], + [glibc=no] ) if test "$glibc" = yes; then - AC_MSG_RESULT(yes) + AC_MSG_RESULT(yes) - # - # This gives us access to prototypes for gnu extensions and C99 - # functions if we are compiling agains glibc. All GNU extensions - # that are used must have a fallback implementation available in - # fallback.h, in order to keep fish working on non-gnu platforms. - # + # + # This gives us access to prototypes for gnu extensions and C99 + # functions if we are compiling agains glibc. All GNU extensions + # that are used must have a fallback implementation available in + # fallback.h, in order to keep fish working on non-gnu platforms. + # - CFLAGS="$CFLAGS -D_GNU_SOURCE=1 -D_ISO99_SOURCE=1" + CFLAGS="$CFLAGS -D_GNU_SOURCE=1 -D_ISO99_SOURCE=1" else - AC_MSG_RESULT(no) + AC_MSG_RESULT(no) fi @@ -340,7 +340,7 @@ fi AC_CANONICAL_TARGET if test $target_cpu = powerpc; then - AC_DEFINE([TPUTS_KLUDGE],[1],[Evil kludge to get Power based machines to work]) + AC_DEFINE([TPUTS_KLUDGE],[1],[Evil kludge to get Power based machines to work]) fi @@ -350,38 +350,38 @@ fi AC_MSG_CHECKING([if we are under Solaris]) case $target_os in - solaris*) - AC_DEFINE( __EXTENSIONS__, 1, [Macro to enable additional prototypes under Solaris]) - AC_MSG_RESULT(yes) - ;; - *) - AC_MSG_RESULT(no) - ;; + solaris*) + AC_DEFINE( __EXTENSIONS__, 1, [Macro to enable additional prototypes under Solaris]) + AC_MSG_RESULT(yes) + ;; + *) + AC_MSG_RESULT(no) + ;; esac # Check for Solaris curses tputs having fixed length parameter list. AC_MSG_CHECKING([if we are using non varargs tparm.]) AC_COMPILE_IFELSE( - [ - AC_LANG_PROGRAM( - [ - #include - #include - ], - [ - tparm( "" ); - ] - ) - ], - [tparm_solaris_kludge=no], - [tparm_solaris_kludge=yes] + [ + AC_LANG_PROGRAM( + [ + #include + #include + ], + [ + tparm( "" ); + ] + ) + ], + [tparm_solaris_kludge=no], + [tparm_solaris_kludge=yes] ) if test "x$tparm_solaris_kludge" = "xyes"; then - AC_MSG_RESULT(yes) - AC_DEFINE( - [TPARM_SOLARIS_KLUDGE], - [1], - [Define to 1 if tparm accepts a fixed amount of paramters.] + AC_MSG_RESULT(yes) + AC_DEFINE( + [TPARM_SOLARIS_KLUDGE], + [1], + [Define to 1 if tparm accepts a fixed amount of paramters.] ) else AC_MSG_RESULT(no) @@ -394,14 +394,14 @@ fi AC_MSG_CHECKING([if we are under BSD]) case $target_os in - *bsd*) - AC_DEFINE( __BSD_VISIBLE, 1, [Macro to enable additional prototypes under BSD]) - AC_DEFINE( _NETBSD_SOURCE, 1, [Macro to enable additional prototypes under BSD]) - AC_MSG_RESULT(yes) - ;; - *) - AC_MSG_RESULT(no) - ;; + *bsd*) + AC_DEFINE( __BSD_VISIBLE, 1, [Macro to enable additional prototypes under BSD]) + AC_DEFINE( _NETBSD_SOURCE, 1, [Macro to enable additional prototypes under BSD]) + AC_MSG_RESULT(yes) + ;; + *) + AC_MSG_RESULT(no) + ;; esac @@ -413,7 +413,7 @@ esac # if [[ "$prefix" = NONE ]]; then - prefix=/usr/local + prefix=/usr/local fi @@ -425,9 +425,9 @@ fi AC_ARG_VAR( [docdir], [Documentation direcotry] ) if test -z $docdir; then - docdir=$datadir/doc/fish + docdir=$datadir/doc/fish else - docdir=$docdir + docdir=$docdir fi @@ -453,9 +453,9 @@ AC_CHECK_FILES([/proc/self/stat]) # AC_DEFINE( - [HAVE_TRANSLATE_H], - [1], - [Define to 1 if the wgettext function should be used for translating strings.] + [HAVE_TRANSLATE_H], + [1], + [Define to 1 if the wgettext function should be used for translating strings.] ) @@ -484,7 +484,7 @@ LIBS=$LIBS_COMMON LIBS_COMMON=$LIBS LIBS="$LIBS_SHARED" if test x$local_gettext != xno; then - AC_SEARCH_LIBS( gettext, intl,,) + AC_SEARCH_LIBS( gettext, intl,,) fi # Check for libiconv_open if we can't find iconv_open. Silly OS X does @@ -501,7 +501,7 @@ LIBS=$LIBS_COMMON LIBS_COMMON=$LIBS LIBS="$LIBS_SHARED" if test x$local_gettext != xno; then - AC_SEARCH_LIBS( gettext, intl,,) + AC_SEARCH_LIBS( gettext, intl,,) fi LIBS_FISH_INDENT=$LIBS LIBS=$LIBS_COMMON @@ -513,7 +513,7 @@ LIBS=$LIBS_COMMON LIBS_COMMON=$LIBS LIBS="$LIBS_SHARED" if test x$local_gettext != xno; then - AC_SEARCH_LIBS( gettext, intl,,) + AC_SEARCH_LIBS( gettext, intl,,) fi AC_SEARCH_LIBS( iconv_open, iconv, , [AC_SEARCH_LIBS( libiconv_open, iconv, , [AC_MSG_ERROR([Could not find an iconv implementation, needed to build fish])] )] ) LIBS_FISH_PAGER=$LIBS @@ -526,7 +526,7 @@ LIBS=$LIBS_COMMON LIBS_COMMON=$LIBS LIBS="$LIBS_SHARED" if test x$local_gettext != xno; then - AC_SEARCH_LIBS( gettext, intl,,) + AC_SEARCH_LIBS( gettext, intl,,) fi AC_SEARCH_LIBS( iconv_open, iconv, , [AC_SEARCH_LIBS( libiconv_open, iconv, , [AC_MSG_ERROR([Could not find an iconv implementation, needed to build fish])] )] ) LIBS_FISHD=$LIBS @@ -539,7 +539,7 @@ LIBS=$LIBS_COMMON LIBS_COMMON=$LIBS LIBS="$LIBS_SHARED" if test x$local_gettext != xno; then - AC_SEARCH_LIBS( gettext, intl,,) + AC_SEARCH_LIBS( gettext, intl,,) fi LIBS_MIMEDB=$LIBS LIBS=$LIBS_COMMON @@ -552,7 +552,7 @@ LIBS=$LIBS_COMMON LIBS_COMMON=$LIBS LIBS="$LIBS_SHARED" if test x$local_gettext != xno; then - AC_SEARCH_LIBS( gettext, intl,,) + AC_SEARCH_LIBS( gettext, intl,,) fi LIBS_SET_COLOR=$LIBS LIBS=$LIBS_COMMON @@ -564,15 +564,15 @@ LIBS=$LIBS_COMMON AC_CHECK_HEADERS([getopt.h termio.h sys/resource.h term.h ncurses/term.h ncurses.h curses.h stropts.h siginfo.h sys/select.h sys/ioctl.h sys/termios.h libintl.h execinfo.h spawn.h]) AC_CHECK_HEADER( - [regex.h], - [ - AC_DEFINE( - [HAVE_REGEX_H], - [1], - [Define to 1 if you have the header file.] - ) - ], - [AC_MSG_ERROR([Could not find the header regex.h, needed to build fish])] + [regex.h], + [ + AC_DEFINE( + [HAVE_REGEX_H], + [1], + [Define to 1 if you have the header file.] + ) + ], + [AC_MSG_ERROR([Could not find the header regex.h, needed to build fish])] ) @@ -612,78 +612,78 @@ local_found_posix_switch=no for i in "" "-D_POSIX_C_SOURCE=200112L" "-D_XOPEN_SOURCE=600 -D_POSIX_C_SOURCE=200112L"; do - AC_MSG_CHECKING( if switches \"$i\" works) - CFLAGS="$XCFLAGS $i" + AC_MSG_CHECKING( if switches \"$i\" works) + CFLAGS="$XCFLAGS $i" - # - # Try to run this program, which should test various extensions - # and Posix functionality. If this program works, then everything - # should work. Hopefully. - # + # + # Try to run this program, which should test various extensions + # and Posix functionality. If this program works, then everything + # should work. Hopefully. + # - AC_TRY_LINK( - [ - #include - #include - #include + AC_TRY_LINK( + [ + #include + #include + #include - /* POSIX, C89 and C99: POSIX extends this header. - * For: kill(), killpg(), siginfo_t, sigset_t, - * struct sigaction, sigemptyset(), sigaction(), - * SIGIO and SIGWINCH. */ - #include + /* POSIX, C89 and C99: POSIX extends this header. + * For: kill(), killpg(), siginfo_t, sigset_t, + * struct sigaction, sigemptyset(), sigaction(), + * SIGIO and SIGWINCH. */ + #include - #ifdef HAVE_SIGINFO_H - /* Neither POSIX, C89 nor C99: Solaris-specific (others?). - * For: siginfo_t (also defined by signal.h when in - * POSIX/extensions mode). */ - #include - #endif + #ifdef HAVE_SIGINFO_H + /* Neither POSIX, C89 nor C99: Solaris-specific (others?). + * For: siginfo_t (also defined by signal.h when in + * POSIX/extensions mode). */ + #include + #endif - #ifdef HAVE_SYS_TERMIOS_H - /* Neither POSIX, C89 nor C99: a common extension. - * For: TIOCGWINSZ and struct winsize (under at least - * Solaris, NetBSD and (dual-listed) FreeBSD). */ - #include - #endif + #ifdef HAVE_SYS_TERMIOS_H + /* Neither POSIX, C89 nor C99: a common extension. + * For: TIOCGWINSZ and struct winsize (under at least + * Solaris, NetBSD and (dual-listed) FreeBSD). */ + #include + #endif - #ifdef HAVE_SYS_IOCTL_H - /* As above (under at least Linux and FreeBSD). */ - #include - #endif - ], - [ - /* Avert high-level optimisation, by making the program's - * return value depend on all tested identifiers. */ - long ret = 0; - /* POSIX only: might be unhidden by _POSIX_C_SOURCE. */ - struct sigaction sa; - sigset_t ss; - siginfo_t info; - ret += (long)(void *)&info + kill( 0, 0 ) + - sigaction( 0, &sa, 0 ) + sigemptyset( &ss ); - /* Extended-POSIX: might be unhidden by _XOPEN_SOURCE. */ - ret += killpg( 0, 0 ); - /* Non-standard: might be hidden by the macros. */ - { - struct winsize termsize; - ret += (long)(void *)&termsize; - ret += SIGWINCH + TIOCGWINSZ + SIGIO; - } - return ret; + #ifdef HAVE_SYS_IOCTL_H + /* As above (under at least Linux and FreeBSD). */ + #include + #endif + ], + [ + /* Avert high-level optimisation, by making the program's + * return value depend on all tested identifiers. */ + long ret = 0; + /* POSIX only: might be unhidden by _POSIX_C_SOURCE. */ + struct sigaction sa; + sigset_t ss; + siginfo_t info; + ret += (long)(void *)&info + kill( 0, 0 ) + + sigaction( 0, &sa, 0 ) + sigemptyset( &ss ); + /* Extended-POSIX: might be unhidden by _XOPEN_SOURCE. */ + ret += killpg( 0, 0 ); + /* Non-standard: might be hidden by the macros. */ + { + struct winsize termsize; + ret += (long)(void *)&termsize; + ret += SIGWINCH + TIOCGWINSZ + SIGIO; + } + return ret; - ], - local_cv_use__posix_c_source=yes, - local_cv_use__posix_c_source=no, - ) + ], + local_cv_use__posix_c_source=yes, + local_cv_use__posix_c_source=no, + ) - if test x$local_cv_use__posix_c_source = xyes; then - AC_MSG_RESULT( yes ) - local_found_posix_switch=yes - break; - else - AC_MSG_RESULT( no ) - fi + if test x$local_cv_use__posix_c_source = xyes; then + AC_MSG_RESULT( yes ) + local_found_posix_switch=yes + break; + else + AC_MSG_RESULT( no ) + fi done @@ -694,7 +694,7 @@ done # if test ! x$local_found_posix_switch = xyes; then - CFLAGS="$XCFLAGS" + CFLAGS="$XCFLAGS" fi @@ -713,7 +713,7 @@ AC_CHECK_FUNCS( dcgettext backtrace backtrace_symbols sysconf ) # if test x$local_gettext != xno; then - AC_CHECK_FUNC( gettext, HAVE_GETTEXT=1, HAVE_GETTEXT=0 ) + AC_CHECK_FUNC( gettext, HAVE_GETTEXT=1, HAVE_GETTEXT=0 ) fi # @@ -728,35 +728,35 @@ fi AC_MSG_CHECKING([if realpath accepts null for its second argument]) AC_RUN_IFELSE( - [ - AC_LANG_PROGRAM( - [ - #include - #include - #include - ], - [ - int status; - char *res; - res = realpath( "somefile", 0 ); - status = !(res != 0 || errno == ENOENT); - exit( status ); - ] - ) - ], - [have_realpath_null=yes], - [have_realpath_null=no] + [ + AC_LANG_PROGRAM( + [ + #include + #include + #include + ], + [ + int status; + char *res; + res = realpath( "somefile", 0 ); + status = !(res != 0 || errno == ENOENT); + exit( status ); + ] + ) + ], + [have_realpath_null=yes], + [have_realpath_null=no] ) if test "$have_realpath_null" = yes; then - AC_MSG_RESULT(yes) - AC_DEFINE( - [HAVE_REALPATH_NULL], - [1], - [Define to 1 if realpath accepts null for its second argument.] - ) + AC_MSG_RESULT(yes) + AC_DEFINE( + [HAVE_REALPATH_NULL], + [1], + [Define to 1 if realpath accepts null for its second argument.] + ) else - AC_MSG_RESULT(no) + AC_MSG_RESULT(no) fi @@ -766,30 +766,30 @@ fi AC_MSG_CHECKING([if struct winsize and TIOCGWINSZ exist]) AC_LINK_IFELSE( - [ - AC_LANG_PROGRAM( - [ - #ifdef HAVE_SYS_TERMIOS_H - #include - #endif + [ + AC_LANG_PROGRAM( + [ + #ifdef HAVE_SYS_TERMIOS_H + #include + #endif - #ifdef HAVE_SYS_IOCTL_H - #include - #endif - ], - [ - struct winsize termsize = {0}; - TIOCGWINSZ; - ] - ) - ], - [ - AC_MSG_RESULT(yes); - AC_DEFINE([HAVE_WINSIZE], [1], [Define to 1 if the winsize struct and TIOCGWINSZ macro exist]) - ], - [ - AC_MSG_RESULT(no) - ] + #ifdef HAVE_SYS_IOCTL_H + #include + #endif + ], + [ + struct winsize termsize = {0}; + TIOCGWINSZ; + ] + ) + ], + [ + AC_MSG_RESULT(yes); + AC_DEFINE([HAVE_WINSIZE], [1], [Define to 1 if the winsize struct and TIOCGWINSZ macro exist]) + ], + [ + AC_MSG_RESULT(no) + ] ) @@ -800,30 +800,30 @@ AC_LINK_IFELSE( if test "$ac_cv_func_fwprintf" = yes; then - AC_MSG_CHECKING([if fwprintf is broken]) - AC_RUN_IFELSE( - [ - AC_LANG_PROGRAM( - [ - #include - #include - #include - #include - ], - [ - setlocale( LC_ALL, "" ); - fwprintf( stderr, L"%ls%ls", L"", L"fish:" ); - ] - ) - ], - [ - AC_MSG_RESULT(no) - ], - [ - AC_MSG_RESULT([yes]) - AC_DEFINE([HAVE_BROKEN_FWPRINTF], [1], [Define to 1 one if the implemented fwprintf is broken]) - ] - ) + AC_MSG_CHECKING([if fwprintf is broken]) + AC_RUN_IFELSE( + [ + AC_LANG_PROGRAM( + [ + #include + #include + #include + #include + ], + [ + setlocale( LC_ALL, "" ); + fwprintf( stderr, L"%ls%ls", L"", L"fish:" ); + ] + ) + ], + [ + AC_MSG_RESULT(no) + ], + [ + AC_MSG_RESULT([yes]) + AC_DEFINE([HAVE_BROKEN_FWPRINTF], [1], [Define to 1 one if the implemented fwprintf is broken]) + ] + ) fi @@ -831,89 +831,89 @@ fi # Check for _nl_msg_cat_cntr symbol AC_MSG_CHECKING([for _nl_msg_cat_cntr symbol]) AC_TRY_LINK( - [ - #if HAVE_LIBINTL_H - #include - #endif - ], - [ - extern int _nl_msg_cat_cntr; - int tmp = _nl_msg_cat_cntr; - exit(tmp); - ], - have__nl_msg_cat_cntr=yes, - have__nl_msg_cat_cntr=no + [ + #if HAVE_LIBINTL_H + #include + #endif + ], + [ + extern int _nl_msg_cat_cntr; + int tmp = _nl_msg_cat_cntr; + exit(tmp); + ], + have__nl_msg_cat_cntr=yes, + have__nl_msg_cat_cntr=no ) if test "$have__nl_msg_cat_cntr" = yes; then - AC_MSG_RESULT(yes) - AC_DEFINE( - [HAVE__NL_MSG_CAT_CNTR], - [1], - [Define to 1 if the _nl_msg_cat_cntr symbol is exported.] - ) + AC_MSG_RESULT(yes) + AC_DEFINE( + [HAVE__NL_MSG_CAT_CNTR], + [1], + [Define to 1 if the _nl_msg_cat_cntr symbol is exported.] + ) else - AC_MSG_RESULT(no) + AC_MSG_RESULT(no) fi # Check for __environ symbol AC_MSG_CHECKING([for __environ symbol]) AC_TRY_LINK( - [ - #include - ], - [ - extern char **__environ; - char **tmp = __environ; - exit(tmp!=0); - ], - have___environ=yes, - have___environ=no + [ + #include + ], + [ + extern char **__environ; + char **tmp = __environ; + exit(tmp!=0); + ], + have___environ=yes, + have___environ=no ) if test "$have___environ" = yes; then - AC_MSG_RESULT(yes) - AC_DEFINE( - [HAVE___ENVIRON], - [1], - [Define to 1 if the __environ symbol is exported.] - ) + AC_MSG_RESULT(yes) + AC_DEFINE( + [HAVE___ENVIRON], + [1], + [Define to 1 if the __environ symbol is exported.] + ) else - AC_MSG_RESULT(no) + AC_MSG_RESULT(no) fi # Check if getopt_long exists and works AC_MSG_CHECKING([if getopt_long exists and works]) AC_TRY_LINK( - [ - #if HAVE_GETOPT_H - #include - #endif - ], - [ - static struct option - long_options[] = - { - 0, 0, 0, 0 - } - ; - int opt = getopt_long( 0, - 0, - 0, - long_options, - 0 ); + [ + #if HAVE_GETOPT_H + #include + #endif + ], + [ + static struct option + long_options[] = + { + 0, 0, 0, 0 + } + ; + int opt = getopt_long( 0, + 0, + 0, + long_options, + 0 ); - ], - have_working_getopt_long=yes, - have_working_getopt_long=no + ], + have_working_getopt_long=yes, + have_working_getopt_long=no ) if test "$have_working_getopt_long" = yes; then - AC_MSG_RESULT(yes) - AC_DEFINE( - [HAVE_WORKING_GETOPT_LONG], - [1], - [Define to 1 if getopt_long exists and works.] - ) + AC_MSG_RESULT(yes) + AC_DEFINE( + [HAVE_WORKING_GETOPT_LONG], + [1], + [Define to 1 if getopt_long exists and works.] + ) else - AC_MSG_RESULT(no) + AC_MSG_RESULT(no) fi # Check if del_curterm is broken - in that case we redefine @@ -921,17 +921,17 @@ fi AC_MSG_CHECKING([If del_curterm is broken]) case $target_os in - *bsd*) - AC_MSG_RESULT(yes) - AC_DEFINE( - [HAVE_BROKEN_DEL_CURTERM], - [1], - [del_curterm is broken, redefine it to a no-op to avoid a double-free bug] - ) - ;; - *) - AC_MSG_RESULT(no) - ;; + *bsd*) + AC_MSG_RESULT(yes) + AC_DEFINE( + [HAVE_BROKEN_DEL_CURTERM], + [1], + [del_curterm is broken, redefine it to a no-op to avoid a double-free bug] + ) + ;; + *) + AC_MSG_RESULT(no) + ;; esac # Tell the world what we know @@ -939,15 +939,15 @@ AC_CONFIG_FILES([Makefile fish.spec Doxyfile.help seq]) AC_OUTPUT if test ! x$local_found_posix_switch = xyes; then - echo "Can't find a combination of switches to enable common extensions like detecting window size." - echo "Some fish features may be disabled." + echo "Can't find a combination of switches to enable common extensions like detecting window size." + echo "Some fish features may be disabled." fi if [[ "$xsel" = "with_xsel" ]]; then - echo "Now configure xsel with $conf_arg" - rm -rf $XSEL - tar xf $XSEL.tar.gz - cd $XSEL && ./configure $conf_arg + echo "Now configure xsel with $conf_arg" + rm -rf $XSEL + tar xf $XSEL.tar.gz + cd $XSEL && ./configure $conf_arg fi echo "fish is now configured." echo "Use 'make' and 'make install' to build and install fish." diff --git a/env.cpp b/env.cpp index 50cfbc6c4..f6b097ce1 100644 --- a/env.cpp +++ b/env.cpp @@ -1,5 +1,5 @@ /** \file env.c - Functions for setting and getting environment variables. + Functions for setting and getting environment variables. */ #include "config.h" @@ -92,10 +92,10 @@ extern char **__environ; */ struct var_entry_t { - bool exportv; /**< Whether the variable should be exported */ - wcstring val; /**< The value of the variable */ + bool exportv; /**< Whether the variable should be exported */ + wcstring val; /**< The value of the variable */ - var_entry_t() : exportv(false) { } + var_entry_t() : exportv(false) { } }; typedef std::map var_table_t; @@ -109,29 +109,29 @@ bool g_use_posix_spawn = false; //will usually be set to true */ struct env_node_t { - /** - Variable table - */ - var_table_t env; - /** - Does this node imply a new variable scope? If yes, all - non-global variables below this one in the stack are - invisible. If new_scope is set for the global variable node, - the universe will explode. - */ - int new_scope; - /** - Does this node contain any variables which are exported to subshells - */ - int exportv; - - /** - Pointer to next level - */ - struct env_node_t *next; + /** + Variable table + */ + var_table_t env; + /** + Does this node imply a new variable scope? If yes, all + non-global variables below this one in the stack are + invisible. If new_scope is set for the global variable node, + the universe will explode. + */ + int new_scope; + /** + Does this node contain any variables which are exported to subshells + */ + int exportv; + + /** + Pointer to next level + */ + struct env_node_t *next; - env_node_t() : new_scope(0), exportv(0), next(NULL) { } + env_node_t() : new_scope(0), exportv(0), next(NULL) { } }; class variable_entry_t { @@ -216,15 +216,15 @@ static int get_names_show_unexported; */ static const wchar_t * const locale_variable[] = { - L"LANG", - L"LC_ALL", - L"LC_COLLATE", - L"LC_CTYPE", - L"LC_MESSAGES", - L"LC_MONETARY", - L"LC_NUMERIC", - L"LC_TIME", - NULL + L"LANG", + L"LC_ALL", + L"LC_COLLATE", + L"LC_CTYPE", + L"LC_MESSAGES", + L"LC_MONETARY", + L"LC_NUMERIC", + L"LC_TIME", + NULL }; @@ -237,18 +237,18 @@ static const wchar_t * const locale_variable[] = */ static void start_fishd() { - struct passwd *pw = getpwuid(getuid()); - - debug( 3, L"Spawning new copy of fishd" ); - - if( !pw ) - { - debug( 0, _( L"Could not get user information" ) ); - return; - } - + struct passwd *pw = getpwuid(getuid()); + + debug( 3, L"Spawning new copy of fishd" ); + + if( !pw ) + { + debug( 0, _( L"Could not get user information" ) ); + return; + } + wcstring cmd = format_string(FISHD_CMD, pw->pw_name); - + /* Prefer the fishd in __fish_bin_dir, if exists */ const env_var_t bin_dir = env_get_string(L"__fish_bin_dir"); if (! bin_dir.missing_or_empty()) @@ -260,9 +260,9 @@ static void start_fishd() cmd.insert(0, bin_dir + L"/"); } } - + parser_t &parser = parser_t::principal_parser(); - parser.eval( cmd, io_chain_t(), TOP ); + parser.eval( cmd, io_chain_t(), TOP ); } /** @@ -270,20 +270,20 @@ static void start_fishd() */ static mode_t get_umask() { - mode_t res; - res = umask( 0 ); - umask( res ); - return res; + mode_t res; + res = umask( 0 ); + umask( res ); + return res; } /** Checks if the specified variable is a locale variable */ static bool var_is_locale(const wcstring &key) { - for (size_t i=0; locale_variable[i]; i++) { - if (key == locale_variable[i]) { - return true; - } - } - return false; + for (size_t i=0; locale_variable[i]; i++) { + if (key == locale_variable[i]) { + return true; + } + } + return false; } /** @@ -291,71 +291,71 @@ static bool var_is_locale(const wcstring &key) { */ static void handle_locale() { - const env_var_t lc_all = env_get_string( L"LC_ALL" ); - int i; + const env_var_t lc_all = env_get_string( L"LC_ALL" ); + int i; const wcstring old_locale = wsetlocale( LC_MESSAGES, NULL ); - /* - Array of locale constants corresponding to the local variable names defined in locale_variable - */ - static const int cat[] = - { - 0, - LC_ALL, - LC_COLLATE, - LC_CTYPE, - LC_MESSAGES, - LC_MONETARY, - LC_NUMERIC, - LC_TIME - } - ; - - if( !lc_all.missing() ) - { - wsetlocale( LC_ALL, lc_all.c_str() ); - } - else - { - const env_var_t lang = env_get_string( L"LANG" ); - if( !lang.missing() ) - { - wsetlocale( LC_ALL, lang.c_str() ); - } - - for( i=2; locale_variable[i]; i++ ) - { - const env_var_t val = env_get_string( locale_variable[i] ); + /* + Array of locale constants corresponding to the local variable names defined in locale_variable + */ + static const int cat[] = + { + 0, + LC_ALL, + LC_COLLATE, + LC_CTYPE, + LC_MESSAGES, + LC_MONETARY, + LC_NUMERIC, + LC_TIME + } + ; + + if( !lc_all.missing() ) + { + wsetlocale( LC_ALL, lc_all.c_str() ); + } + else + { + const env_var_t lang = env_get_string( L"LANG" ); + if( !lang.missing() ) + { + wsetlocale( LC_ALL, lang.c_str() ); + } + + for( i=2; locale_variable[i]; i++ ) + { + const env_var_t val = env_get_string( locale_variable[i] ); + + if( !val.missing() ) + { + wsetlocale( cat[i], val.c_str() ); + } + } + } - if( !val.missing() ) - { - wsetlocale( cat[i], val.c_str() ); - } - } - } - const wcstring new_locale = wsetlocale(LC_MESSAGES, NULL); - if( old_locale != new_locale ) - { + if( old_locale != new_locale ) + { - /* - Try to make change known to gettext. Both changing - _nl_msg_cat_cntr and calling dcgettext might potentially - tell some gettext implementation that the translation - strings should be reloaded. We do both and hope for the - best. - */ + /* + Try to make change known to gettext. Both changing + _nl_msg_cat_cntr and calling dcgettext might potentially + tell some gettext implementation that the translation + strings should be reloaded. We do both and hope for the + best. + */ - extern int _nl_msg_cat_cntr; - _nl_msg_cat_cntr++; + extern int _nl_msg_cat_cntr; + _nl_msg_cat_cntr++; - dcgettext( "fish", "Changing language to English", LC_MESSAGES ); - - if( get_is_interactive() ) - { - debug( 0, _(L"Changing language to English") ); - } - } + dcgettext( "fish", "Changing language to English", LC_MESSAGES ); + + if( get_is_interactive() ) + { + debug( 0, _(L"Changing language to English") ); + } + } } @@ -376,43 +376,43 @@ static void react_to_variable_change(const wcstring &key) { proper events are triggered when an event occurs. */ static void universal_callback( fish_message_type_t type, - const wchar_t *name, - const wchar_t *val ) + const wchar_t *name, + const wchar_t *val ) { - const wchar_t *str=0; - - switch( type ) - { - case SET: - case SET_EXPORT: - { - str=L"SET"; - break; - } - - case ERASE: - { - str=L"ERASE"; - break; - } - - default: - break; - } - - if( str ) - { - mark_changed_exported(); - + const wchar_t *str=0; + + switch( type ) + { + case SET: + case SET_EXPORT: + { + str=L"SET"; + break; + } + + case ERASE: + { + str=L"ERASE"; + break; + } + + default: + break; + } + + if( str ) + { + mark_changed_exported(); + event_t ev = event_t::variable_event(name); ev.arguments.reset(new wcstring_list_t()); ev.arguments->push_back(L"VARIABLE"); ev.arguments->push_back(str); ev.arguments->push_back(name); - event_fire( &ev ); + event_fire( &ev ); ev.arguments.reset(NULL); - } - + } + if (name) react_to_variable_change(name); } @@ -421,82 +421,82 @@ static void universal_callback( fish_message_type_t type, Make sure the PATH variable contains the essential directories */ static void setup_path() -{ +{ size_t i; - int j; - wcstring_list_t lst; - - const wchar_t *path_el[] = + int j; + wcstring_list_t lst; + + const wchar_t *path_el[] = { L"/bin", L"/usr/bin", PREFIX L"/bin", 0 } - ; - - env_var_t path = env_get_string( L"PATH" ); - - if( !path.missing() ) - { - tokenize_variable_array( path, lst ); - } - - for( j=0; path_el[j]; j++ ) - { - - int has_el=0; - - for( i=0; i 0) && (el[len-1]==L'/') ) - { - len--; - } - - if( (wcslen( path_el[j] ) == len) && + ; + + env_var_t path = env_get_string( L"PATH" ); + + if( !path.missing() ) + { + tokenize_variable_array( path, lst ); + } + + for( j=0; path_el[j]; j++ ) + { + + int has_el=0; + + for( i=0; i 0) && (el[len-1]==L'/') ) + { + len--; + } + + if( (wcslen( path_el[j] ) == len) && (wcsncmp( el.c_str(), path_el[j], len)==0) ) - { - has_el = 1; - } - } - - if( !has_el ) - { - wcstring buffer; - - debug( 3, L"directory %ls was missing", path_el[j] ); - - if( !path.missing() ) - { + { + has_el = 1; + } + } + + if( !has_el ) + { + wcstring buffer; + + debug( 3, L"directory %ls was missing", path_el[j] ); + + if( !path.missing() ) + { buffer += path; - } - + } + buffer += ARRAY_SEP_STR; buffer += path_el[j]; - - env_set( L"PATH", buffer.empty()?NULL:buffer.c_str(), ENV_GLOBAL | ENV_EXPORT ); - - path = env_get_string( L"PATH" ); + + env_set( L"PATH", buffer.empty()?NULL:buffer.c_str(), ENV_GLOBAL | ENV_EXPORT ); + + path = env_get_string( L"PATH" ); lst.resize(0); - tokenize_variable_array( path, lst ); - } - } + tokenize_variable_array( path, lst ); + } + } } int env_set_pwd() { - wchar_t dir_path[4096]; - wchar_t *res = wgetcwd( dir_path, 4096 ); - if( !res ) - { - return 0; - } - env_set( L"PWD", dir_path, ENV_EXPORT | ENV_GLOBAL ); - return 1; + wchar_t dir_path[4096]; + wchar_t *res = wgetcwd( dir_path, 4096 ); + if( !res ) + { + return 0; + } + env_set( L"PWD", dir_path, ENV_EXPORT | ENV_GLOBAL ); + return 1; } /** @@ -505,27 +505,27 @@ int env_set_pwd() static void env_set_defaults() { - if( env_get_string(L"USER").missing() ) - { - struct passwd *pw = getpwuid( getuid()); - wchar_t *unam = str2wcs( pw->pw_name ); - env_set( L"USER", unam, ENV_GLOBAL ); - free( unam ); - } + if( env_get_string(L"USER").missing() ) + { + struct passwd *pw = getpwuid( getuid()); + wchar_t *unam = str2wcs( pw->pw_name ); + env_set( L"USER", unam, ENV_GLOBAL ); + free( unam ); + } - if( env_get_string(L"HOME").missing() ) - { - const env_var_t unam = env_get_string( L"USER" ); - char *unam_narrow = wcs2str( unam.c_str() ); - struct passwd *pw = getpwnam( unam_narrow ); - wchar_t *dir = str2wcs( pw->pw_dir ); - env_set( L"HOME", dir, ENV_GLOBAL ); - free( dir ); - free( unam_narrow ); - } + if( env_get_string(L"HOME").missing() ) + { + const env_var_t unam = env_get_string( L"USER" ); + char *unam_narrow = wcs2str( unam.c_str() ); + struct passwd *pw = getpwnam( unam_narrow ); + wchar_t *dir = str2wcs( pw->pw_dir ); + env_set( L"HOME", dir, ENV_GLOBAL ); + free( dir ); + free( unam_narrow ); + } + + env_set_pwd(); - env_set_pwd(); - } // Some variables should not be arrays. This used to be handled by a startup script, but we'd like to get down to 0 forks for startup, so handle it here. @@ -539,15 +539,15 @@ static bool variable_can_be_array(const wchar_t *key) { void env_init(const struct config_paths_t *paths /* or NULL */) { - char **p; - struct passwd *pw; - wchar_t *uname; - wchar_t *version; - - /* - env_read_only variables can not be altered directly by the user - */ - + char **p; + struct passwd *pw; + wchar_t *uname; + wchar_t *version; + + /* + env_read_only variables can not be altered directly by the user + */ + const wchar_t * const ro_keys[] = { L"status", L"history", @@ -562,58 +562,58 @@ void env_init(const struct config_paths_t *paths /* or NULL */) for (size_t i=0; i < sizeof ro_keys / sizeof *ro_keys; i++) { env_read_only.insert(ro_keys[i]); } - - /* - HOME and USER should be writeable by root, since this can be a - convenient way to install software. - */ - if( getuid() != 0 ) - { + + /* + HOME and USER should be writeable by root, since this can be a + convenient way to install software. + */ + if( getuid() != 0 ) + { env_read_only.insert(L"HOME"); env_read_only.insert(L"USER"); - } - - /* + } + + /* Names of all dynamically calculated variables */ env_electric.insert(L"history"); env_electric.insert(L"status"); env_electric.insert(L"umask"); - - top = new env_node_t; - global_env = top; - global = &top->env; - - /* - Now the environemnt variable handling is set up, the next step - is to insert valid data - */ - - /* - Import environment variables - */ - for( p=environ?environ:__environ; p && *p; p++ ) - { - wchar_t *key, *val; - - key = str2wcs(*p); - if( !key ) - { - continue; - } - - val = wcschr( key, L'=' ); + top = new env_node_t; + global_env = top; + global = &top->env; + + /* + Now the environemnt variable handling is set up, the next step + is to insert valid data + */ + + /* + Import environment variables + */ + for( p=environ?environ:__environ; p && *p; p++ ) + { + wchar_t *key, *val; + + key = str2wcs(*p); + + if( !key ) + { + continue; + } + + val = wcschr( key, L'=' ); + + if( val == 0 ) + { + env_set( key, L"", ENV_EXPORT ); + } + else + { + *val = L'\0'; + val++; - if( val == 0 ) - { - env_set( key, L"", ENV_EXPORT ); - } - else - { - *val = L'\0'; - val++; - //fwprintf( stderr, L"Set $%ls to %ls\n", key, val ); if (variable_can_be_array(val)) { for (size_t i=0; val[i] != L'\0'; i++) { @@ -623,11 +623,11 @@ void env_init(const struct config_paths_t *paths /* or NULL */) } } - env_set( key, val, ENV_EXPORT | ENV_GLOBAL ); - } - free(key); - } - + env_set( key, val, ENV_EXPORT | ENV_GLOBAL ); + } + free(key); + } + /* Set the given paths in the environment, if we have any */ if (paths != NULL) { @@ -636,63 +636,63 @@ void env_init(const struct config_paths_t *paths /* or NULL */) env_set(FISH_HELPDIR_VAR, paths->doc.c_str(), ENV_GLOBAL | ENV_EXPORT); env_set(FISH_BIN_DIR, paths->bin.c_str(), ENV_GLOBAL | ENV_EXPORT); } - - /* - Set up the PATH variable - */ - setup_path(); - /* - Set up the USER variable - */ - pw = getpwuid( getuid() ); - if( pw ) - { - uname = str2wcs( pw->pw_name ); - env_set( L"USER", uname, ENV_GLOBAL | ENV_EXPORT ); - free( uname ); - } + /* + Set up the PATH variable + */ + setup_path(); - /* - Set up the version variables - */ - version = str2wcs( PACKAGE_VERSION ); - env_set( L"version", version, ENV_GLOBAL ); - env_set( L"FISH_VERSION", version, ENV_GLOBAL ); - free( version ); + /* + Set up the USER variable + */ + pw = getpwuid( getuid() ); + if( pw ) + { + uname = str2wcs( pw->pw_name ); + env_set( L"USER", uname, ENV_GLOBAL | ENV_EXPORT ); + free( uname ); + } - const env_var_t fishd_dir_wstr = env_get_string( L"FISHD_SOCKET_DIR"); - const env_var_t user_dir_wstr = env_get_string( L"USER" ); + /* + Set up the version variables + */ + version = str2wcs( PACKAGE_VERSION ); + env_set( L"version", version, ENV_GLOBAL ); + env_set( L"FISH_VERSION", version, ENV_GLOBAL ); + free( version ); - wchar_t * fishd_dir = fishd_dir_wstr.missing()?NULL:const_cast(fishd_dir_wstr.c_str()); - wchar_t * user_dir = user_dir_wstr.missing()?NULL:const_cast(user_dir_wstr.c_str()); + const env_var_t fishd_dir_wstr = env_get_string( L"FISHD_SOCKET_DIR"); + const env_var_t user_dir_wstr = env_get_string( L"USER" ); - env_universal_init(fishd_dir , user_dir , - &start_fishd, - &universal_callback ); + wchar_t * fishd_dir = fishd_dir_wstr.missing()?NULL:const_cast(fishd_dir_wstr.c_str()); + wchar_t * user_dir = user_dir_wstr.missing()?NULL:const_cast(user_dir_wstr.c_str()); - /* - Set up SHLVL variable - */ - const env_var_t shlvl_str = env_get_string( L"SHLVL" ); + env_universal_init(fishd_dir , user_dir , + &start_fishd, + &universal_callback ); + + /* + Set up SHLVL variable + */ + const env_var_t shlvl_str = env_get_string( L"SHLVL" ); wcstring nshlvl_str = L"1"; - if (! shlvl_str.missing()) - { + if (! shlvl_str.missing()) + { long shlvl_i = wcstol(shlvl_str.c_str(), NULL, 10); if (shlvl_i >= 0) { nshlvl_str = format_string(L"%ld", 1 + shlvl_i); } - } + } env_set(L"SHLVL", nshlvl_str.c_str(), ENV_GLOBAL | ENV_EXPORT ); - /* Set correct defaults for e.g. USER and HOME variables */ - env_set_defaults(); - + /* Set correct defaults for e.g. USER and HOME variables */ + env_set_defaults(); + /* Set g_log_forks */ env_var_t log_forks = env_get_string(L"fish_log_forks"); g_log_forks = ! log_forks.missing_or_empty() && from_string(log_forks); - + /* Set g_use_posix_spawn. Default to true. */ env_var_t use_posix_spawn = env_get_string(L"fish_use_posix_spawn"); g_use_posix_spawn = (use_posix_spawn.missing_or_empty() ? true : from_string(use_posix_spawn)); @@ -700,29 +700,29 @@ void env_init(const struct config_paths_t *paths /* or NULL */) void env_destroy() { - env_universal_destroy(); - - while( &top->env != global ) - { - env_pop(); - } - + env_universal_destroy(); + + while( &top->env != global ) + { + env_pop(); + } + env_read_only.clear(); env_electric.clear(); - - var_table_t::iterator iter; - for (iter = global->begin(); iter != global->end(); ++iter) { - var_entry_t *entry = iter->second; - if( entry->exportv ) - { - mark_changed_exported(); - } - delete entry; - } + var_table_t::iterator iter; + for (iter = global->begin(); iter != global->end(); ++iter) { + var_entry_t *entry = iter->second; + if( entry->exportv ) + { + mark_changed_exported(); + } - delete top; + delete entry; + } + + delete top; } /** @@ -731,246 +731,246 @@ void env_destroy() */ static env_node_t *env_get_node( const wcstring &key ) { - env_node_t *env = top; - while( env != NULL ) - { - if ( env->env.find( key ) != env->env.end() ) - { - break; - } + env_node_t *env = top; + while( env != NULL ) + { + if ( env->env.find( key ) != env->env.end() ) + { + break; + } - if( env->new_scope ) - { - env = global_env; - } - else - { - env = env->next; - } - } - return env; + if( env->new_scope ) + { + env = global_env; + } + else + { + env = env->next; + } + } + return env; } int env_set(const wcstring &key, const wchar_t *val, int var_mode) { ASSERT_IS_MAIN_THREAD(); - env_node_t *node = NULL; - bool has_changed_old = has_changed_exported; - bool has_changed_new = false; - var_entry_t *e=0; - int done=0; - - int is_universal = 0; - - if( val && contains( key, L"PWD", L"HOME" ) ) - { + env_node_t *node = NULL; + bool has_changed_old = has_changed_exported; + bool has_changed_new = false; + var_entry_t *e=0; + int done=0; + + int is_universal = 0; + + if( val && contains( key, L"PWD", L"HOME" ) ) + { /* Canoncalize our path; if it changes, recurse and try again. */ wcstring val_canonical = val; path_make_canonical(val_canonical); if (val != val_canonical) { return env_set( key, val_canonical.c_str(), var_mode ); } - } - - if( (var_mode & ENV_USER ) && is_read_only(key) ) - { - return ENV_PERM; - } - - if (key == L"umask") - { - wchar_t *end; + } - /* + if( (var_mode & ENV_USER ) && is_read_only(key) ) + { + return ENV_PERM; + } + + if (key == L"umask") + { + wchar_t *end; + + /* Set the new umask */ - if( val && wcslen(val) ) - { - errno=0; - long mask = wcstol( val, &end, 8 ); - - if( !errno && (!*end) && (mask <= 0777) && (mask >= 0) ) - { - umask( mask ); - } - } - /* + if( val && wcslen(val) ) + { + errno=0; + long mask = wcstol( val, &end, 8 ); + + if( !errno && (!*end) && (mask <= 0777) && (mask >= 0) ) + { + umask( mask ); + } + } + /* Do not actually create a umask variable, on env_get, it will be calculated dynamically */ - return 0; - } - - /* + return 0; + } + + /* Zero element arrays are internaly not coded as null but as this placeholder string */ - if( !val ) - { - val = ENV_NULL; - } - - if( var_mode & ENV_UNIVERSAL ) - { - int exportv = 0; - - if( !(var_mode & ENV_EXPORT ) && + if( !val ) + { + val = ENV_NULL; + } + + if( var_mode & ENV_UNIVERSAL ) + { + int exportv = 0; + + if( !(var_mode & ENV_EXPORT ) && !(var_mode & ENV_UNEXPORT ) ) - { - env_universal_get_export( key ); - } - else - { - exportv = (var_mode & ENV_EXPORT ); - } - - env_universal_set(key, val, exportv); - is_universal = 1; - - } - else - { - - node = env_get_node( key ); - if( node ) - { - var_table_t::iterator result = node->env.find(key); + { + env_universal_get_export( key ); + } + else + { + exportv = (var_mode & ENV_EXPORT ); + } + + env_universal_set(key, val, exportv); + is_universal = 1; + + } + else + { + + node = env_get_node( key ); + if( node ) + { + var_table_t::iterator result = node->env.find(key); assert(result != node->env.end()); e = result->second; - - if( e->exportv ) - { - has_changed_new = true; - } - } - - if( (var_mode & ENV_LOCAL) || + + if( e->exportv ) + { + has_changed_new = true; + } + } + + if( (var_mode & ENV_LOCAL) || (var_mode & ENV_GLOBAL) ) - { - node = ( var_mode & ENV_GLOBAL )?global_env:top; - } - else - { - if( node ) - { - if( !(var_mode & ENV_EXPORT ) && + { + node = ( var_mode & ENV_GLOBAL )?global_env:top; + } + else + { + if( node ) + { + if( !(var_mode & ENV_EXPORT ) && !(var_mode & ENV_UNEXPORT ) ) - { - var_mode = e->exportv?ENV_EXPORT:0; - } - } - else - { - if( ! get_proc_had_barrier()) - { - set_proc_had_barrier(true); - env_universal_barrier(); - } - - if( env_universal_get( key ) ) - { - int exportv = 0; - - if( !(var_mode & ENV_EXPORT ) && + { + var_mode = e->exportv?ENV_EXPORT:0; + } + } + else + { + if( ! get_proc_had_barrier()) + { + set_proc_had_barrier(true); + env_universal_barrier(); + } + + if( env_universal_get( key ) ) + { + int exportv = 0; + + if( !(var_mode & ENV_EXPORT ) && !(var_mode & ENV_UNEXPORT ) ) - { - env_universal_get_export( key ); - } - else - { - exportv = (var_mode & ENV_EXPORT ); - } - - env_universal_set(key, val, exportv); - is_universal = 1; - - done = 1; - - } - else - { - /* + { + env_universal_get_export( key ); + } + else + { + exportv = (var_mode & ENV_EXPORT ); + } + + env_universal_set(key, val, exportv); + is_universal = 1; + + done = 1; + + } + else + { + /* New variable with unspecified scope. The default scope is the innermost scope that is shadowing, which will be either the current function or the - global scope. + global scope. */ - node = top; - while( node->next && !node->new_scope ) - { - node = node->next; - } - } - } - } - - if( !done ) + node = top; + while( node->next && !node->new_scope ) + { + node = node->next; + } + } + } + } + + if( !done ) { - var_entry_t *old_entry = NULL; - var_table_t::iterator result = node->env.find(key); - if ( result != node->env.end() ) - { - old_entry = result->second; - node->env.erase(result); - } - - var_entry_t *entry = NULL; - if( old_entry ) + var_entry_t *old_entry = NULL; + var_table_t::iterator result = node->env.find(key); + if ( result != node->env.end() ) + { + old_entry = result->second; + node->env.erase(result); + } + + var_entry_t *entry = NULL; + if( old_entry ) { - entry = old_entry; - - if( (var_mode & ENV_EXPORT) || entry->exportv ) + entry = old_entry; + + if( (var_mode & ENV_EXPORT) || entry->exportv ) { entry->exportv = !!(var_mode & ENV_EXPORT); - has_changed_new = true; + has_changed_new = true; } - } - else + } + else { - entry = new var_entry_t; - - if( var_mode & ENV_EXPORT) + entry = new var_entry_t; + + if( var_mode & ENV_EXPORT) { entry->exportv = 1; - has_changed_new = true; + has_changed_new = true; } - else + else { entry->exportv = 0; } - + } - - entry->val = val; - node->env[key] = entry; - - if( entry->exportv ) + + entry->val = val; + node->env[key] = entry; + + if( entry->exportv ) { - node->exportv=1; + node->exportv=1; } - + if (has_changed_old || has_changed_new) mark_changed_exported(); } - + } - + if( !is_universal ) { - event_t ev = event_t::variable_event(key); + event_t ev = event_t::variable_event(key); ev.arguments.reset(new wcstring_list_t); ev.arguments->push_back(L"VARIABLE"); ev.arguments->push_back(L"SET"); ev.arguments->push_back(key); - - // debug( 1, L"env_set: fire events on variable %ls", key ); + + // debug( 1, L"env_set: fire events on variable %ls", key ); event_fire( &ev ); - // debug( 1, L"env_set: return from event firing" ); + // debug( 1, L"env_set: return from event firing" ); ev.arguments.reset(NULL); } - + react_to_variable_change(key); - + return 0; } @@ -982,91 +982,91 @@ int env_set(const wcstring &key, const wchar_t *val, int var_mode) \return zero if the variable was not found, non-zero otherwise */ static int try_remove( env_node_t *n, - const wchar_t *key, - int var_mode ) + const wchar_t *key, + int var_mode ) { - if( n == 0 ) - { - return 0; - } + if( n == 0 ) + { + return 0; + } - var_table_t::iterator result = n->env.find( key ); - if ( result != n->env.end() ) - { - var_entry_t *v = result->second; + var_table_t::iterator result = n->env.find( key ); + if ( result != n->env.end() ) + { + var_entry_t *v = result->second; - if( v->exportv ) - { - mark_changed_exported(); - } - - n->env.erase(result); - delete v; - return 1; - } + if( v->exportv ) + { + mark_changed_exported(); + } - if( var_mode & ENV_LOCAL ) - { - return 0; - } - - if( n->new_scope ) - { - return try_remove( global_env, key, var_mode ); - } - else - { - return try_remove( n->next, key, var_mode ); - } + n->env.erase(result); + delete v; + return 1; + } + + if( var_mode & ENV_LOCAL ) + { + return 0; + } + + if( n->new_scope ) + { + return try_remove( global_env, key, var_mode ); + } + else + { + return try_remove( n->next, key, var_mode ); + } } int env_remove( const wcstring &key, int var_mode ) { ASSERT_IS_MAIN_THREAD(); - env_node_t *first_node; - int erased = 0; - - if( (var_mode & ENV_USER ) && is_read_only(key) ) - { - return 2; - } - - first_node = top; - - if( ! (var_mode & ENV_UNIVERSAL ) ) - { - - if( var_mode & ENV_GLOBAL ) - { - first_node = global_env; - } - - if( try_remove( first_node, key.c_str(), var_mode ) ) - { - event_t ev = event_t::variable_event(key); + env_node_t *first_node; + int erased = 0; + + if( (var_mode & ENV_USER ) && is_read_only(key) ) + { + return 2; + } + + first_node = top; + + if( ! (var_mode & ENV_UNIVERSAL ) ) + { + + if( var_mode & ENV_GLOBAL ) + { + first_node = global_env; + } + + if( try_remove( first_node, key.c_str(), var_mode ) ) + { + event_t ev = event_t::variable_event(key); ev.arguments.reset(new wcstring_list_t); ev.arguments->push_back(L"VARIABLE"); ev.arguments->push_back(L"ERASE"); ev.arguments->push_back(key); - - event_fire( &ev ); - - ev.arguments.reset(NULL); - erased = 1; - } - } - - if( !erased && - !(var_mode & ENV_GLOBAL) && - !(var_mode & ENV_LOCAL) ) - { - erased = ! env_universal_remove( key.c_str() ); - } + + event_fire( &ev ); + + ev.arguments.reset(NULL); + erased = 1; + } + } + + if( !erased && + !(var_mode & ENV_GLOBAL) && + !(var_mode & ENV_LOCAL) ) + { + erased = ! env_universal_remove( key.c_str() ); + } react_to_variable_change(key); - - return !erased; + + return !erased; } env_var_t env_var_t::missing_var(void) { @@ -1081,66 +1081,66 @@ const wchar_t *env_var_t::c_str(void) const { } env_var_t env_get_string( const wcstring &key ) -{ +{ /* Big hack...we only allow getting the history on the main thread. Note that history_t may ask for an environment variable, so don't take the lock here (we don't need it) */ const bool is_main = is_main_thread(); - if( key == L"history" && is_main) - { + if( key == L"history" && is_main) + { env_var_t result; - + history_t *history = reader_get_history(); if (! history) { history = &history_t::history_with_name(L"fish"); } if (history) - history->get_string_representation(result, ARRAY_SEP_STR); - return result; - } - else if( key == L"COLUMNS" ) - { + history->get_string_representation(result, ARRAY_SEP_STR); + return result; + } + else if( key == L"COLUMNS" ) + { return to_string(common_get_width()); - } - else if( key == L"LINES" ) - { + } + else if( key == L"LINES" ) + { return to_string(common_get_width()); - } - else if( key == L"status" ) - { + } + else if( key == L"status" ) + { return to_string(proc_get_last_status()); - } - else if( key == L"umask" ) - { + } + else if( key == L"umask" ) + { return format_string(L"0%0.3o", get_umask() ); - } - else + } + else { { /* Lock around a local region */ scoped_lock lock(env_lock); - + var_entry_t *res = NULL; env_node_t *env = top; wcstring result; - + while( env != NULL ) { var_table_t::iterator result = env->env.find(key); - if ( result != env->env.end() ) - res = result->second; - - + if ( result != env->env.end() ) + res = result->second; + + if( res != NULL ) { - if( res->val == ENV_NULL ) + if( res->val == ENV_NULL ) { return env_var_t::missing_var(); } else { - return res->val; + return res->val; } } - + if( env->new_scope ) { env = global_env; @@ -1151,7 +1151,7 @@ env_var_t env_get_string( const wcstring &key ) } } } - + /* Another big hack - only do a universal barrier on the main thread (since it can change variable values) Make sure we do this outside the env_lock because it may itself call env_get_string */ if(is_main && ! get_proc_had_barrier()) @@ -1159,9 +1159,9 @@ env_var_t env_get_string( const wcstring &key ) set_proc_had_barrier(true); env_universal_barrier(); } - + wchar_t *item = env_universal_get( key ); - + if( !item || (wcscmp( item, ENV_NULL )==0)) { return env_var_t::missing_var(); @@ -1175,20 +1175,20 @@ env_var_t env_get_string( const wcstring &key ) int env_exist( const wchar_t *key, int mode ) { - var_entry_t *res; - env_node_t *env; - wchar_t *item=0; + var_entry_t *res; + env_node_t *env; + wchar_t *item=0; - CHECK( key, 0 ); - - /* - Read only variables all exist, and they are all global. A local - version can not exist. - */ - if( ! (mode & ENV_LOCAL) && ! (mode & ENV_UNIVERSAL) ) - { - if( is_read_only(key) || is_electric(key) ) - { + CHECK( key, 0 ); + + /* + Read only variables all exist, and they are all global. A local + version can not exist. + */ + if( ! (mode & ENV_LOCAL) && ! (mode & ENV_UNIVERSAL) ) + { + if( is_read_only(key) || is_electric(key) ) + { //Such variables are never exported if (mode & ENV_EXPORT) { @@ -1198,21 +1198,21 @@ int env_exist( const wchar_t *key, int mode ) { return 1; } - return 1; - } - } + return 1; + } + } if( !(mode & ENV_UNIVERSAL) ) - { - env = (mode & ENV_GLOBAL)?global_env:top; - - while( env != 0 ) - { - var_table_t::iterator result = env->env.find( key ); + { + env = (mode & ENV_GLOBAL)?global_env:top; - if ( result != env->env.end() ) - { - res = result->second; + while( env != 0 ) + { + var_table_t::iterator result = env->env.find( key ); + + if ( result != env->env.end() ) + { + res = result->second; if (mode & ENV_EXPORT) { @@ -1222,50 +1222,50 @@ int env_exist( const wchar_t *key, int mode ) { return res->exportv == 0; } - - return 1; - } - - if ( mode & ENV_LOCAL ) - break; - if( env->new_scope ) - { - env = global_env; - } - else - { - env = env->next; - } - } - } + return 1; + } + + if ( mode & ENV_LOCAL ) + break; + + if( env->new_scope ) + { + env = global_env; + } + else + { + env = env->next; + } + } + } if( !(mode & ENV_LOCAL) && !(mode & ENV_GLOBAL) ) - { - if( ! get_proc_had_barrier()) - { - set_proc_had_barrier(true); - env_universal_barrier(); - } - - item = env_universal_get( key ); + { + if( ! get_proc_had_barrier()) + { + set_proc_had_barrier(true); + env_universal_barrier(); + } + + item = env_universal_get( key ); if (item != NULL) { if (mode & ENV_EXPORT) { - return env_universal_get_export(key) == 1; + return env_universal_get_export(key) == 1; } else if (mode & ENV_UNEXPORT) { - return env_universal_get_export(key) == 0; + return env_universal_get_export(key) == 0; } return 1; } - } + } - return 0; + return 0; } /** @@ -1273,85 +1273,85 @@ int env_exist( const wchar_t *key, int mode ) */ static int local_scope_exports( env_node_t *n ) { - - if( n==global_env ) - return 0; - - if( n->exportv ) - return 1; - - if( n->new_scope ) - return 0; - - return local_scope_exports( n->next ); + + if( n==global_env ) + return 0; + + if( n->exportv ) + return 1; + + if( n->new_scope ) + return 0; + + return local_scope_exports( n->next ); } void env_push( int new_scope ) { - env_node_t *node = new env_node_t; - node->next = top; - node->new_scope=new_scope; - - if( new_scope ) - { + env_node_t *node = new env_node_t; + node->next = top; + node->new_scope=new_scope; + + if( new_scope ) + { if (local_scope_exports(top)) mark_changed_exported(); - } - top = node; + } + top = node; } void env_pop() { - if( &top->env != global ) - { - int i; - int locale_changed = 0; - - env_node_t *killme = top; + if( &top->env != global ) + { + int i; + int locale_changed = 0; - for( i=0; locale_variable[i]; i++ ) - { - var_table_t::iterator result = killme->env.find( locale_variable[i] ); - if ( result != killme->env.end() ) - { - locale_changed = 1; - break; - } - } + env_node_t *killme = top; - if( killme->new_scope ) - { + for( i=0; locale_variable[i]; i++ ) + { + var_table_t::iterator result = killme->env.find( locale_variable[i] ); + if ( result != killme->env.end() ) + { + locale_changed = 1; + break; + } + } + + if( killme->new_scope ) + { if (killme->exportv || local_scope_exports( killme->next )) mark_changed_exported(); - } - - top = top->next; + } - var_table_t::iterator iter; - for (iter = killme->env.begin(); iter != killme->env.end(); ++iter) - { - var_entry_t *entry = iter->second; - if( entry->exportv ) - { - mark_changed_exported(); - } - delete entry; - } + top = top->next; - delete killme; + var_table_t::iterator iter; + for (iter = killme->env.begin(); iter != killme->env.end(); ++iter) + { + var_entry_t *entry = iter->second; + if( entry->exportv ) + { + mark_changed_exported(); + } + delete entry; + } - if( locale_changed ) - handle_locale(); - - } - else - { - debug( 0, - _( L"Tried to pop empty environment stack." ) ); - sanity_lose(); - } + delete killme; + + if( locale_changed ) + handle_locale(); + + } + else + { + debug( 0, + _( L"Tried to pop empty environment stack." ) ); + sanity_lose(); + } } /** @@ -1359,181 +1359,181 @@ void env_pop() */ static void add_key_to_string_set(const var_table_t &envs, std::set &strSet) { - var_table_t::const_iterator iter; - for (iter = envs.begin(); iter != envs.end(); ++iter) - { - var_entry_t *e = iter->second; + var_table_t::const_iterator iter; + for (iter = envs.begin(); iter != envs.end(); ++iter) + { + var_entry_t *e = iter->second; - if( ( e->exportv && get_names_show_exported) || - ( !e->exportv && get_names_show_unexported) ) - { - /*Insert Key*/ - strSet.insert(iter->first); - } + if( ( e->exportv && get_names_show_exported) || + ( !e->exportv && get_names_show_unexported) ) + { + /*Insert Key*/ + strSet.insert(iter->first); + } - } + } } wcstring_list_t env_get_names( int flags ) { scoped_lock lock(env_lock); - + wcstring_list_t result; std::set names; int show_local = flags & ENV_LOCAL; - int show_global = flags & ENV_GLOBAL; - int show_universal = flags & ENV_UNIVERSAL; + int show_global = flags & ENV_GLOBAL; + int show_universal = flags & ENV_UNIVERSAL; - env_node_t *n=top; + env_node_t *n=top; - - get_names_show_exported = - (flags & ENV_EXPORT) || !(flags & ENV_UNEXPORT); - get_names_show_unexported = - (flags & ENV_UNEXPORT) || !(flags & ENV_EXPORT); - if( !show_local && !show_global && !show_universal ) - { - show_local =show_universal = show_global=1; - } - - if( show_local ) - { - while( n ) - { - if( n == global_env ) - break; - - add_key_to_string_set(n->env, names); - if( n->new_scope ) - break; - else - n = n->next; + get_names_show_exported = + (flags & ENV_EXPORT) || !(flags & ENV_UNEXPORT); + get_names_show_unexported = + (flags & ENV_UNEXPORT) || !(flags & ENV_EXPORT); - } - } - - if( show_global ) - { - add_key_to_string_set(global_env->env, names); - if( get_names_show_unexported ) { + if( !show_local && !show_global && !show_universal ) + { + show_local =show_universal = show_global=1; + } + + if( show_local ) + { + while( n ) + { + if( n == global_env ) + break; + + add_key_to_string_set(n->env, names); + if( n->new_scope ) + break; + else + n = n->next; + + } + } + + if( show_global ) + { + add_key_to_string_set(global_env->env, names); + if( get_names_show_unexported ) { result.insert(result.end(), env_electric.begin(), env_electric.end()); } - - if( get_names_show_exported ) - { + + if( get_names_show_exported ) + { result.push_back(L"COLUMNS"); result.push_back(L"LINES"); - } - - } - - if( show_universal ) - { - + } + + } + + if( show_universal ) + { + wcstring_list_t uni_list; env_universal_get_names2(uni_list, get_names_show_exported, - get_names_show_unexported); + get_names_show_unexported); names.insert(uni_list.begin(), uni_list.end()); - } - + } + result.insert(result.end(), names.begin(), names.end()); return result; } /** - Get list of all exported variables + Get list of all exported variables */ static void get_exported( const env_node_t *n, std::map &h ) { - if( !n ) - return; - - if( n->new_scope ) - get_exported( global_env, h ); - else - get_exported( n->next, h ); + if( !n ) + return; - var_table_t::const_iterator iter; - for (iter = n->env.begin(); iter != n->env.end(); ++iter) - { - const wcstring &key = iter->first; - var_entry_t *val_entry = iter->second; - if( val_entry->exportv && (val_entry->val != ENV_NULL ) ) - { + if( n->new_scope ) + get_exported( global_env, h ); + else + get_exported( n->next, h ); + + var_table_t::const_iterator iter; + for (iter = n->env.begin(); iter != n->env.end(); ++iter) + { + const wcstring &key = iter->first; + var_entry_t *val_entry = iter->second; + if( val_entry->exportv && (val_entry->val != ENV_NULL ) ) + { // Don't use std::map::insert here, since we need to overwrite existing values from previous scopes h[key] = val_entry->val; - } - } + } + } } static void export_func(const std::map &envs, std::vector &out) { - std::map::const_iterator iter; - for (iter = envs.begin(); iter != envs.end(); ++iter) - { - char* ks = wcs2str(iter->first.c_str()); - char* vs = wcs2str(iter->second.c_str()); - char *pos = vs; - while( *pos ) - { - if( *pos == ARRAY_SEP ) - *pos = ':'; - pos++; - } - + std::map::const_iterator iter; + for (iter = envs.begin(); iter != envs.end(); ++iter) + { + char* ks = wcs2str(iter->first.c_str()); + char* vs = wcs2str(iter->second.c_str()); + char *pos = vs; + while( *pos ) + { + if( *pos == ARRAY_SEP ) + *pos = ':'; + pos++; + } + /* Put a string on the vector */ out.push_back(std::string()); std::string &str = out.back(); - + /* Append our environment variable data to it */ str.append(ks); str.append("="); str.append(vs); - free(ks); - free(vs); - } + free(ks); + free(vs); + } } static void update_export_array_if_necessary(bool recalc) { ASSERT_IS_MAIN_THREAD(); - if( recalc && ! get_proc_had_barrier()) - { - set_proc_had_barrier(true); - env_universal_barrier(); - } - - if( has_changed_exported ) - { - std::map vals; - size_t i; + if( recalc && ! get_proc_had_barrier()) + { + set_proc_had_barrier(true); + env_universal_barrier(); + } + + if( has_changed_exported ) + { + std::map vals; + size_t i; + + debug( 4, L"env_export_arr() recalc" ); + + get_exported( top, vals ); - debug( 4, L"env_export_arr() recalc" ); - - get_exported( top, vals ); - wcstring_list_t uni; - env_universal_get_names2( uni, 1, 0 ); - for( i=0; i(key, val)); + } + } - if (wcscmp( val, ENV_NULL)) { - // Note that std::map::insert does NOT overwrite a value already in the map, - // which we depend on here - vals.insert(std::pair(key, val)); - } - } - std::vector local_export_buffer; - export_func(vals, local_export_buffer ); + export_func(vals, local_export_buffer ); export_array.set(local_export_buffer); - has_changed_exported=false; - } + has_changed_exported=false; + } } diff --git a/env.h b/env.h index 2c32b6a08..9a00488ff 100644 --- a/env.h +++ b/env.h @@ -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 ¤t(); - + // vars necessary for highlighting static const wchar_t * const highlighting_keys[]; }; diff --git a/env_universal.cpp b/env_universal.cpp index 7a1564ed5..c1729be13 100644 --- a/env_universal.cpp +++ b/env_universal.cpp @@ -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= 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 ); } diff --git a/env_universal.h b/env_universal.h index bfc456350..8c1b3925f 100644 --- a/env_universal.h +++ b/env_universal.h @@ -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 diff --git a/env_universal_common.cpp b/env_universal_common.cpp index 5ed554be6..47df13056 100644 --- a/env_universal_common.cpp +++ b/env_universal_common.cpp @@ -93,15 +93,15 @@ */ typedef struct var_uni_entry { - int exportv; /**< Whether the variable should be exported */ - wcstring val; /**< The value of the variable */ - var_uni_entry():exportv(0), val() { } + int exportv; /**< Whether the variable should be exported */ + wcstring val; /**< The value of the variable */ + var_uni_entry():exportv(0), val() { } } var_uni_entry_t; static void parse_message( wchar_t *msg, - connection_t *src ); + connection_t *src ); /** The table of all universal variables @@ -113,8 +113,8 @@ env_var_table_t env_universal_var; Callback function, should be called on all events */ static void (*callback)( fish_message_type_t type, - const wchar_t *key, - const wchar_t *val ); + const wchar_t *key, + const wchar_t *val ); /** List of names for the UTF-8 character set. @@ -132,8 +132,8 @@ static const char *iconv_utf8_names[]= */ static const char *iconv_wide_names_unknown[]= { - "wchar_t", "WCHAR_T", - "wchar", "WCHAR", + "wchar_t", "WCHAR_T", + "wchar", "WCHAR", 0 } ; @@ -143,12 +143,12 @@ static const char *iconv_wide_names_unknown[]= */ static const char *iconv_wide_names_4[]= { - "wchar_t", "WCHAR_T", - "wchar", "WCHAR", - "ucs-4", "UCS-4", - "ucs4", "UCS4", - "utf-32", "UTF-32", - "utf32", "UTF32", + "wchar_t", "WCHAR_T", + "wchar", "WCHAR", + "ucs-4", "UCS-4", + "ucs4", "UCS4", + "utf-32", "UTF-32", + "utf32", "UTF32", 0 } ; @@ -158,18 +158,18 @@ static const char *iconv_wide_names_4[]= */ static const char *iconv_wide_names_2[]= { - "wchar_t", "WCHAR_T", - "wchar", "WCHAR", - "ucs-2", "UCS-2", - "ucs2", "UCS2", - "utf-16", "UTF-16", - "utf16", "UTF16", + "wchar_t", "WCHAR_T", + "wchar", "WCHAR", + "ucs-2", "UCS-2", + "ucs2", "UCS2", + "utf-16", "UTF-16", + "utf16", "UTF16", 0 } ; template -class sloppy {}; +class sloppy {}; static size_t hack_iconv(iconv_t cd, const char * const* inbuf, size_t *inbytesleft, char **outbuf, size_t *outbytesleft) { @@ -184,7 +184,7 @@ static size_t hack_iconv(iconv_t cd, const char * const* inbuf, size_t *inbytesl operator char** () const { return (char **)t; } operator const char** () const { return (const char**)t; } } slop_inbuf = {inbuf}; - + return iconv( cd, slop_inbuf, inbytesleft, outbuf, outbytesleft ); } @@ -193,117 +193,117 @@ static size_t hack_iconv(iconv_t cd, const char * const* inbuf, size_t *inbytesl */ static wchar_t *utf2wcs( const char *in ) { - iconv_t cd=(iconv_t) -1; - int i,j; + iconv_t cd=(iconv_t) -1; + int i,j; - wchar_t *out; + wchar_t *out; - /* - Try to convert to wchar_t. If that is not a valid character set, - try various names for ucs-4. We can't be sure that ucs-4 is - really the character set used by wchar_t, but it is the best - assumption we can make. - */ - const char **to_name=0; + /* + Try to convert to wchar_t. If that is not a valid character set, + try various names for ucs-4. We can't be sure that ucs-4 is + really the character set used by wchar_t, but it is the best + assumption we can make. + */ + const char **to_name=0; - switch (sizeof (wchar_t)) - { - - case 2: - to_name = iconv_wide_names_2; - break; + switch (sizeof (wchar_t)) + { - case 4: - to_name = iconv_wide_names_4; - break; - - default: - to_name = iconv_wide_names_unknown; - break; - } - + case 2: + to_name = iconv_wide_names_2; + break; - /* - The line protocol fish uses is always utf-8. - */ - const char **from_name = iconv_utf8_names; + case 4: + to_name = iconv_wide_names_4; + break; - size_t in_len = strlen( in ); - size_t out_len = sizeof( wchar_t )*(in_len+2); - size_t nconv; - char *nout; - - out = (wchar_t *)malloc( out_len ); - nout = (char *)out; + default: + to_name = iconv_wide_names_unknown; + break; + } - if( !out ) - return 0; - - for( i=0; to_name[i]; i++ ) - { - for( j=0; from_name[j]; j++ ) - { - cd = iconv_open ( to_name[i], from_name[j] ); - if( cd != (iconv_t) -1) - { - goto start_conversion; - - } - } - } + /* + The line protocol fish uses is always utf-8. + */ + const char **from_name = iconv_utf8_names; + + size_t in_len = strlen( in ); + size_t out_len = sizeof( wchar_t )*(in_len+2); + size_t nconv; + char *nout; + + out = (wchar_t *)malloc( out_len ); + nout = (char *)out; + + if( !out ) + return 0; + + for( i=0; to_name[i]; i++ ) + { + for( j=0; from_name[j]; j++ ) + { + cd = iconv_open ( to_name[i], from_name[j] ); + + if( cd != (iconv_t) -1) + { + goto start_conversion; + + } + } + } start_conversion: - if (cd == (iconv_t) -1) - { - /* Something went wrong. */ - debug( 0, L"Could not perform utf-8 conversion" ); - if(errno != EINVAL) - wperror( L"iconv_open" ); - - /* Terminate the output string. */ - free(out); - return 0; - } - + if (cd == (iconv_t) -1) + { + /* Something went wrong. */ + debug( 0, L"Could not perform utf-8 conversion" ); + if(errno != EINVAL) + wperror( L"iconv_open" ); + + /* Terminate the output string. */ + free(out); + return 0; + } + /* FreeBSD has this prototype: size_t iconv (iconv_t, const char **...) OS X and Linux this one: size_t iconv (iconv_t, char **...) AFAIK there's no single type that can be passed as both char ** and const char **. So we cast the function pointer instead (!) */ nconv = hack_iconv( cd, &in, &in_len, &nout, &out_len ); - - if (nconv == (size_t) -1) - { - debug( 0, L"Error while converting from utf string" ); - return 0; - } - - *((wchar_t *) nout) = L'\0'; - /* - Check for silly iconv behaviour inserting an bytemark in the output - string. - */ - if (*out == L'\xfeff' || *out == L'\xffef' || *out == L'\xefbbbf') - { - wchar_t *out_old = out; - out = wcsdup(out+1); - if (! out ) - { - debug(0, L"FNORD!!!!"); - free( out_old ); - return 0; - } - free( out_old ); - } - - - if (iconv_close (cd) != 0) - wperror (L"iconv_close"); - - return out; + if (nconv == (size_t) -1) + { + debug( 0, L"Error while converting from utf string" ); + return 0; + } + + *((wchar_t *) nout) = L'\0'; + + /* + Check for silly iconv behaviour inserting an bytemark in the output + string. + */ + if (*out == L'\xfeff' || *out == L'\xffef' || *out == L'\xefbbbf') + { + wchar_t *out_old = out; + out = wcsdup(out+1); + if (! out ) + { + debug(0, L"FNORD!!!!"); + free( out_old ); + return 0; + } + free( out_old ); + } + + + if (iconv_close (cd) != 0) + wperror (L"iconv_close"); + + return out; } @@ -313,113 +313,113 @@ static wchar_t *utf2wcs( const char *in ) */ static char *wcs2utf( const wchar_t *in ) { - iconv_t cd=(iconv_t) -1; - int i,j; - - char *char_in = (char *)in; - char *out; + iconv_t cd=(iconv_t) -1; + int i,j; - /* - Try to convert to wchar_t. If that is not a valid character set, - try various names for ucs-4. We can't be sure that ucs-4 is - really the character set used by wchar_t, but it is the best - assumption we can make. - */ - const char **from_name=0; + char *char_in = (char *)in; + char *out; - switch (sizeof (wchar_t)) - { - - case 2: - from_name = iconv_wide_names_2; - break; + /* + Try to convert to wchar_t. If that is not a valid character set, + try various names for ucs-4. We can't be sure that ucs-4 is + really the character set used by wchar_t, but it is the best + assumption we can make. + */ + const char **from_name=0; - case 4: - from_name = iconv_wide_names_4; - break; - - default: - from_name = iconv_wide_names_unknown; - break; - } + switch (sizeof (wchar_t)) + { - const char **to_name = iconv_utf8_names; + case 2: + from_name = iconv_wide_names_2; + break; - size_t in_len = wcslen( in ); - size_t out_len = sizeof( char )*( (MAX_UTF8_BYTES*in_len)+1); - size_t nconv; - char *nout; - - out = (char *)malloc( out_len ); - nout = (char *)out; - in_len *= sizeof( wchar_t ); + case 4: + from_name = iconv_wide_names_4; + break; - if( !out ) - return 0; - - for( i=0; to_name[i]; i++ ) - { - for( j=0; from_name[j]; j++ ) - { - cd = iconv_open ( to_name[i], from_name[j] ); - - if( cd != (iconv_t) -1) - { - goto start_conversion; - - } - } - } + default: + from_name = iconv_wide_names_unknown; + break; + } + + const char **to_name = iconv_utf8_names; + + size_t in_len = wcslen( in ); + size_t out_len = sizeof( char )*( (MAX_UTF8_BYTES*in_len)+1); + size_t nconv; + char *nout; + + out = (char *)malloc( out_len ); + nout = (char *)out; + in_len *= sizeof( wchar_t ); + + if( !out ) + return 0; + + for( i=0; to_name[i]; i++ ) + { + for( j=0; from_name[j]; j++ ) + { + cd = iconv_open ( to_name[i], from_name[j] ); + + if( cd != (iconv_t) -1) + { + goto start_conversion; + + } + } + } start_conversion: - if (cd == (iconv_t) -1) - { - /* Something went wrong. */ - debug( 0, L"Could not perform utf-8 conversion" ); - if(errno != EINVAL) - wperror( L"iconv_open" ); - - /* Terminate the output string. */ - free(out); - return 0; - } - - nconv = hack_iconv( cd, &char_in, &in_len, &nout, &out_len ); - + if (cd == (iconv_t) -1) + { + /* Something went wrong. */ + debug( 0, L"Could not perform utf-8 conversion" ); + if(errno != EINVAL) + wperror( L"iconv_open" ); - if (nconv == (size_t) -1) - { - debug( 0, L"%d %d", in_len, out_len ); - debug( 0, L"Error while converting from to string" ); - return 0; - } - - *nout = '\0'; - - if (iconv_close (cd) != 0) - wperror (L"iconv_close"); - - return out; + /* Terminate the output string. */ + free(out); + return 0; + } + + nconv = hack_iconv( cd, &char_in, &in_len, &nout, &out_len ); + + + if (nconv == (size_t) -1) + { + debug( 0, L"%d %d", in_len, out_len ); + debug( 0, L"Error while converting from to string" ); + return 0; + } + + *nout = '\0'; + + if (iconv_close (cd) != 0) + wperror (L"iconv_close"); + + return out; } void env_universal_common_init( void (*cb)(fish_message_type_t type, const wchar_t *key, const wchar_t *val ) ) { - callback = cb; + callback = cb; } void env_universal_common_destroy() { - env_var_table_t::iterator iter; - - for(iter = env_universal_var.begin(); iter != env_universal_var.end(); ++iter) - { - var_uni_entry_t* value = iter->second; - delete value; - } + env_var_table_t::iterator iter; + + for(iter = env_universal_var.begin(); iter != env_universal_var.end(); ++iter) + { + var_uni_entry_t* value = iter->second; + delete value; + } } /** @@ -428,115 +428,115 @@ void env_universal_common_destroy() static int read_byte( connection_t *src ) { - if( src->buffer_consumed >= src->buffer_used ) - { + if( src->buffer_consumed >= src->buffer_used ) + { - ssize_t res = read( src->fd, src->buffer, ENV_UNIVERSAL_BUFFER_SIZE ); - -// debug(4, L"Read chunk '%.*s'", res, src->buffer ); - - if( res < 0 ) - { + ssize_t res = read( src->fd, src->buffer, ENV_UNIVERSAL_BUFFER_SIZE ); - if( errno == EAGAIN || - errno == EINTR ) - { - return ENV_UNIVERSAL_AGAIN; - } - - return ENV_UNIVERSAL_ERROR; +// debug(4, L"Read chunk '%.*s'", res, src->buffer ); - } - - if( res == 0 ) - { - return ENV_UNIVERSAL_EOF; - } - - src->buffer_consumed = 0; - src->buffer_used = res; - } - - return src->buffer[src->buffer_consumed++]; + if( res < 0 ) + { + + if( errno == EAGAIN || + errno == EINTR ) + { + return ENV_UNIVERSAL_AGAIN; + } + + return ENV_UNIVERSAL_ERROR; + + } + + if( res == 0 ) + { + return ENV_UNIVERSAL_EOF; + } + + src->buffer_consumed = 0; + src->buffer_used = res; + } + + return src->buffer[src->buffer_consumed++]; } void read_message( connection_t *src ) { - while( 1 ) - { - - int ib = read_byte( src ); - char b; - - switch( ib ) - { - case ENV_UNIVERSAL_AGAIN: - { - return; - } + while( 1 ) + { - case ENV_UNIVERSAL_ERROR: - { - debug( 2, L"Read error on fd %d, set killme flag", src->fd ); - if( debug_level > 2 ) - wperror( L"read" ); - src->killme = 1; - return; - } + int ib = read_byte( src ); + char b; - case ENV_UNIVERSAL_EOF: - { - src->killme = 1; - debug( 3, L"Fd %d has reached eof, set killme flag", src->fd ); - if( src->input.size() > 0 ) - { - char c = 0; - src->input.push_back(c); - debug( 1, - L"Universal variable connection closed while reading command. Partial command recieved: '%s'", - &src->input.at(0)); - } - return; - } - } - - b = (char)ib; - - if( b == '\n' ) - { - wchar_t *msg; - - b = 0; + switch( ib ) + { + case ENV_UNIVERSAL_AGAIN: + { + return; + } + + case ENV_UNIVERSAL_ERROR: + { + debug( 2, L"Read error on fd %d, set killme flag", src->fd ); + if( debug_level > 2 ) + wperror( L"read" ); + src->killme = 1; + return; + } + + case ENV_UNIVERSAL_EOF: + { + src->killme = 1; + debug( 3, L"Fd %d has reached eof, set killme flag", src->fd ); + if( src->input.size() > 0 ) + { + char c = 0; + src->input.push_back(c); + debug( 1, + L"Universal variable connection closed while reading command. Partial command recieved: '%s'", + &src->input.at(0)); + } + return; + } + } + + b = (char)ib; + + if( b == '\n' ) + { + wchar_t *msg; + + b = 0; src->input.push_back(b); - - msg = utf2wcs( &src->input.at(0) ); - - /* - Before calling parse_message, we must empty reset - everything, since the callback function could - potentially call read_message. - */ - src->input.clear(); - - if( msg ) - { - parse_message( msg, src ); - } - else - { - debug( 0, _(L"Could not convert message '%s' to wide character string"), &src->input.at(0) ); - } - - free( msg ); - - } - else - { + + msg = utf2wcs( &src->input.at(0) ); + + /* + Before calling parse_message, we must empty reset + everything, since the callback function could + potentially call read_message. + */ + src->input.clear(); + + if( msg ) + { + parse_message( msg, src ); + } + else + { + debug( 0, _(L"Could not convert message '%s' to wide character string"), &src->input.at(0) ); + } + + free( msg ); + + } + else + { src->input.push_back(b); - } - } + } + } } /** @@ -544,13 +544,13 @@ void read_message( connection_t *src ) */ void env_universal_common_remove( const wcstring &name ) { - env_var_table_t::iterator result = env_universal_var.find(name); - if (result != env_universal_var.end()) - { - var_uni_entry_t* v = result->second; - env_universal_var.erase(result); - delete v; - } + env_var_table_t::iterator result = env_universal_var.find(name); + if (result != env_universal_var.end()) + { + var_uni_entry_t* v = result->second; + env_universal_var.erase(result); + delete v; + } } /** @@ -558,34 +558,34 @@ void env_universal_common_remove( const wcstring &name ) */ static int match( const wchar_t *msg, const wchar_t *cmd ) { - size_t len = wcslen( cmd ); - if( wcsncasecmp( msg, cmd, len ) != 0 ) - return 0; + size_t len = wcslen( cmd ); + if( wcsncasecmp( msg, cmd, len ) != 0 ) + return 0; - if( msg[len] && msg[len]!= L' ' && msg[len] != L'\t' ) - return 0; - - return 1; + if( msg[len] && msg[len]!= L' ' && msg[len] != L'\t' ) + return 0; + + return 1; } void env_universal_common_set( const wchar_t *key, const wchar_t *val, int exportv ) { - var_uni_entry_t *entry; + var_uni_entry_t *entry; - CHECK( key, ); - CHECK( val, ); - - entry = new var_uni_entry_t; + CHECK( key, ); + CHECK( val, ); + + entry = new var_uni_entry_t; + + entry->exportv=exportv; + entry->val = val; + env_universal_common_remove( key ); - entry->exportv=exportv; - entry->val = val; - env_universal_common_remove( key ); - env_universal_var[key] = entry; - if( callback ) - { - callback( exportv?SET_EXPORT:SET, key, val ); - } + if( callback ) + { + callback( exportv?SET_EXPORT:SET, key, val ); + } } @@ -593,90 +593,90 @@ void env_universal_common_set( const wchar_t *key, const wchar_t *val, int expor Parse message msg */ static void parse_message( wchar_t *msg, - connection_t *src ) + connection_t *src ) { -// debug( 3, L"parse_message( %ls );", msg ); - - if( msg[0] == L'#' ) - return; - - if( match( msg, SET_STR ) || match( msg, SET_EXPORT_STR )) - { - wchar_t *name, *tmp; - int exportv = match( msg, SET_EXPORT_STR ); - - name = msg+(exportv?wcslen(SET_EXPORT_STR):wcslen(SET_STR)); - while( wcschr( L"\t ", *name ) ) - name++; - - tmp = wcschr( name, L':' ); - if( tmp ) - { - wchar_t *key; - wchar_t *val; - - key = (wchar_t *)malloc( sizeof( wchar_t)*(tmp-name+1)); - memcpy( key, name, sizeof( wchar_t)*(tmp-name)); - key[tmp-name]=0; - - val = tmp+1; - val = unescape( val, 0 ); - - if (key && val) - env_universal_common_set( key, val, exportv ); - - free( val ); - free( key ); - } - else - { - debug( 1, PARSE_ERR, msg ); - } - } - else if( match( msg, ERASE_STR ) ) - { - wchar_t *name, *tmp; - - name = msg+wcslen(ERASE_STR); - while( wcschr( L"\t ", *name ) ) - name++; - - tmp = name; - while( iswalnum( *tmp ) || *tmp == L'_') - tmp++; - - *tmp = 0; - - if( !wcslen( name ) ) - { - debug( 1, PARSE_ERR, msg ); - } +// debug( 3, L"parse_message( %ls );", msg ); - env_universal_common_remove( name ); - - if( callback ) - { - callback( ERASE, name, 0 ); - } - } - else if( match( msg, BARRIER_STR) ) - { - message_t *msg = create_message( BARRIER_REPLY, 0, 0 ); - msg->count = 1; + if( msg[0] == L'#' ) + return; + + if( match( msg, SET_STR ) || match( msg, SET_EXPORT_STR )) + { + wchar_t *name, *tmp; + int exportv = match( msg, SET_EXPORT_STR ); + + name = msg+(exportv?wcslen(SET_EXPORT_STR):wcslen(SET_STR)); + while( wcschr( L"\t ", *name ) ) + name++; + + tmp = wcschr( name, L':' ); + if( tmp ) + { + wchar_t *key; + wchar_t *val; + + key = (wchar_t *)malloc( sizeof( wchar_t)*(tmp-name+1)); + memcpy( key, name, sizeof( wchar_t)*(tmp-name)); + key[tmp-name]=0; + + val = tmp+1; + val = unescape( val, 0 ); + + if (key && val) + env_universal_common_set( key, val, exportv ); + + free( val ); + free( key ); + } + else + { + debug( 1, PARSE_ERR, msg ); + } + } + else if( match( msg, ERASE_STR ) ) + { + wchar_t *name, *tmp; + + name = msg+wcslen(ERASE_STR); + while( wcschr( L"\t ", *name ) ) + name++; + + tmp = name; + while( iswalnum( *tmp ) || *tmp == L'_') + tmp++; + + *tmp = 0; + + if( !wcslen( name ) ) + { + debug( 1, PARSE_ERR, msg ); + } + + env_universal_common_remove( name ); + + if( callback ) + { + callback( ERASE, name, 0 ); + } + } + else if( match( msg, BARRIER_STR) ) + { + message_t *msg = create_message( BARRIER_REPLY, 0, 0 ); + msg->count = 1; src->unsent->push(msg); - try_send_all( src ); - } - else if( match( msg, BARRIER_REPLY_STR ) ) - { - if( callback ) - { - callback( BARRIER_REPLY, 0, 0 ); - } - } - else - { - debug( 1, PARSE_ERR, msg ); - } + try_send_all( src ); + } + else if( match( msg, BARRIER_REPLY_STR ) ) + { + if( callback ) + { + callback( BARRIER_REPLY, 0, 0 ); + } + } + else + { + debug( 1, PARSE_ERR, msg ); + } } /** @@ -685,72 +685,72 @@ static void parse_message( wchar_t *msg, \return 1 on sucess, 0 if the message could not be sent without blocking and -1 on error */ static int try_send( message_t *msg, - int fd ) + int fd ) { - debug( 3, - L"before write of %d chars to fd %d", msg->body.size(), fd ); + debug( 3, + L"before write of %d chars to fd %d", msg->body.size(), fd ); - ssize_t res = write( fd, msg->body.c_str(), msg->body.size() ); + ssize_t res = write( fd, msg->body.c_str(), msg->body.size() ); - if( res != -1 ) - { - debug( 4, L"Wrote message '%s'", msg->body.c_str() ); - } - else - { - debug( 4, L"Failed to write message '%s'", msg->body.c_str() ); - } - - if( res == -1 ) - { - switch( errno ) - { - case EAGAIN: - return 0; - - default: - debug( 2, - L"Error while sending universal variable message to fd %d. Closing connection", - fd ); - if( debug_level > 2 ) - wperror( L"write" ); - - return -1; - } - } - msg->count--; - - if( !msg->count ) - { - delete msg; - } - return 1; + if( res != -1 ) + { + debug( 4, L"Wrote message '%s'", msg->body.c_str() ); + } + else + { + debug( 4, L"Failed to write message '%s'", msg->body.c_str() ); + } + + if( res == -1 ) + { + switch( errno ) + { + case EAGAIN: + return 0; + + default: + debug( 2, + L"Error while sending universal variable message to fd %d. Closing connection", + fd ); + if( debug_level > 2 ) + wperror( L"write" ); + + return -1; + } + } + msg->count--; + + if( !msg->count ) + { + delete msg; + } + return 1; } void try_send_all( connection_t *c ) { -/* debug( 3, - L"Send all updates to connection on fd %d", - c->fd );*/ - while( !c->unsent->empty() ) - { - switch( try_send( c->unsent->front(), c->fd ) ) - { - case 1: - c->unsent->pop(); - break; - - case 0: - debug( 4, - L"Socket full, send rest later" ); - return; - - case -1: - c->killme = 1; - return; - } - } +/* debug( 3, + L"Send all updates to connection on fd %d", + c->fd );*/ + while( !c->unsent->empty() ) + { + switch( try_send( c->unsent->front(), c->fd ) ) + { + case 1: + c->unsent->pop(); + break; + + case 0: + debug( 4, + L"Socket full, send rest later" ); + return; + + case -1: + c->killme = 1; + return; + } + } } /* The universal variable format has some funny escaping requirements; here we try to be safe */ @@ -758,7 +758,7 @@ static bool is_universal_safe_to_encode_directly(wchar_t c) { if (c < 32 || c > 128) return false; - + return iswalnum(c) || wcschr(L"/", c); } @@ -767,28 +767,28 @@ static bool is_universal_safe_to_encode_directly(wchar_t c) */ static wcstring full_escape( const wchar_t *in ) { - wcstring out; - for( ; *in; in++ ) - { + wcstring out; + for( ; *in; in++ ) + { wchar_t c = *in; if (is_universal_safe_to_encode_directly(c)) { out.push_back(c); } - else if (c < 256) - { - append_format(out, L"\\x%.2x", c); - } - else if (c < 65536) - { - append_format(out, L"\\u%.4x", c); - } - else - { - append_format(out, L"\\U%.8x", c); - } - } - return out; + else if (c < 256) + { + append_format(out, L"\\x%.2x", c); + } + else if (c < 65536) + { + append_format(out, L"\\u%.4x", c); + } + else + { + append_format(out, L"\\U%.8x", c); + } + } + return out; } /* Sets the body of a message to the null-terminated list of null terminated const char *. */ @@ -798,19 +798,19 @@ void set_body(message_t *msg, ...) size_t body_len = 0; const char *arg; va_list arg_list; - va_start(arg_list, msg); - while ((arg = va_arg(arg_list, const char *)) != NULL) - body_len += strlen(arg); - va_end(arg_list); - + va_start(arg_list, msg); + while ((arg = va_arg(arg_list, const char *)) != NULL) + body_len += strlen(arg); + va_end(arg_list); + /* Reserve that length in the string */ msg->body.reserve(body_len + 1); //+1 for trailing NULL? Do I need that? - + /* Set the string contents */ - va_start(arg_list, msg); - while ((arg = va_arg(arg_list, const char *)) != NULL) - msg->body.append(arg); - va_end(arg_list); + va_start(arg_list, msg); + while ((arg = va_arg(arg_list, const char *)) != NULL) + msg->body.append(arg); + va_end(arg_list); } /* Returns an instance of message_t allocated via new */ @@ -818,169 +818,169 @@ message_t *create_message( fish_message_type_t type, const wchar_t *key_in, const wchar_t *val_in ) { - message_t *msg = new message_t; + message_t *msg = new message_t; msg->count = 0; - - char *key=0; - - // debug( 4, L"Crete message of type %d", type ); - - if( key_in ) - { - if( wcsvarname( key_in ) ) - { - debug( 0, L"Illegal variable name: '%ls'", key_in ); - return 0; - } - - key = wcs2utf(key_in); - if( !key ) - { - debug( 0, + + char *key=0; + + // debug( 4, L"Crete message of type %d", type ); + + if( key_in ) + { + if( wcsvarname( key_in ) ) + { + debug( 0, L"Illegal variable name: '%ls'", key_in ); + return 0; + } + + key = wcs2utf(key_in); + if( !key ) + { + debug( 0, L"Could not convert %ls to narrow character string", key_in ); - return 0; - } - } - - - switch( type ) - { - case SET: - case SET_EXPORT: - { - if( !val_in ) - { - val_in=L""; - } - - wcstring esc = full_escape( val_in ); - char *val = wcs2utf(esc.c_str()); + return 0; + } + } + + + switch( type ) + { + case SET: + case SET_EXPORT: + { + if( !val_in ) + { + val_in=L""; + } + + wcstring esc = full_escape( val_in ); + char *val = wcs2utf(esc.c_str()); set_body(msg, (type==SET?SET_MBS:SET_EXPORT_MBS), " ", key, ":", val, "\n", NULL); - free( val ); - - break; - } - - case ERASE: - { + free( val ); + + break; + } + + case ERASE: + { set_body(msg, ERASE_MBS, " ", key, "\n", NULL); break; - } - - case BARRIER: - { + } + + case BARRIER: + { set_body(msg, BARRIER_MBS, "\n", NULL); - break; - } - - case BARRIER_REPLY: - { + break; + } + + case BARRIER_REPLY: + { set_body(msg, BARRIER_REPLY_MBS, "\n", NULL); - break; - } - - default: - { - debug( 0, L"create_message: Unknown message type" ); - } - } - - free( key ); - - // debug( 4, L"Message body is '%s'", msg->body ); - - return msg; + break; + } + + default: + { + debug( 0, L"create_message: Unknown message type" ); + } + } + + free( key ); + + // debug( 4, L"Message body is '%s'", msg->body ); + + return msg; } /** - Put exported or unexported variables in a string list -*/ + Put exported or unexported variables in a string list +*/ void env_universal_common_get_names( wcstring_list_t &lst, - int show_exported, - int show_unexported ) + int show_exported, + int show_unexported ) { - env_var_table_t::const_iterator iter; - for (iter = env_universal_var.begin(); iter != env_universal_var.end(); ++iter) - { - const wcstring& key = iter->first; - const var_uni_entry_t *e = iter->second; - if( ( e->exportv && show_exported) || - ( !e->exportv && show_unexported) ) - { - lst.push_back(key); - } - - } + env_var_table_t::const_iterator iter; + for (iter = env_universal_var.begin(); iter != env_universal_var.end(); ++iter) + { + const wcstring& key = iter->first; + const var_uni_entry_t *e = iter->second; + if( ( e->exportv && show_exported) || + ( !e->exportv && show_unexported) ) + { + lst.push_back(key); + } + + } } wchar_t *env_universal_common_get( const wcstring &name ) { - env_var_table_t::const_iterator result = env_universal_var.find(name); + env_var_table_t::const_iterator result = env_universal_var.find(name); - if (result != env_universal_var.end() ) - { - const var_uni_entry_t *e = result->second; - if( e ) - return const_cast(e->val.c_str()); - } + if (result != env_universal_var.end() ) + { + const var_uni_entry_t *e = result->second; + if( e ) + return const_cast(e->val.c_str()); + } - return 0; + return 0; } int env_universal_common_get_export( const wcstring &name ) { - env_var_table_t::const_iterator result = env_universal_var.find(name); - if (result != env_universal_var.end() ) - { - const var_uni_entry_t *e = result->second; - if (e != NULL) - return e->exportv; - } - return 0; + env_var_table_t::const_iterator result = env_universal_var.find(name); + if (result != env_universal_var.end() ) + { + const var_uni_entry_t *e = result->second; + if (e != NULL) + return e->exportv; + } + return 0; } void enqueue_all( connection_t *c ) { - env_var_table_t::const_iterator iter; + env_var_table_t::const_iterator iter; - for (iter = env_universal_var.begin(); iter != env_universal_var.end(); ++iter) - { - const wcstring &key = iter->first; - const var_uni_entry_t *val = iter->second; - - message_t *msg = create_message( val->exportv?SET_EXPORT:SET, key.c_str(), val->val.c_str() ); - msg->count=1; - c->unsent->push(msg); - } + for (iter = env_universal_var.begin(); iter != env_universal_var.end(); ++iter) + { + const wcstring &key = iter->first; + const var_uni_entry_t *val = iter->second; - try_send_all( c ); + message_t *msg = create_message( val->exportv?SET_EXPORT:SET, key.c_str(), val->val.c_str() ); + msg->count=1; + c->unsent->push(msg); + } + + try_send_all( c ); } void connection_init( connection_t *c, int fd ) { - memset (c, 0, sizeof (connection_t)); - c->fd = fd; + memset (c, 0, sizeof (connection_t)); + c->fd = fd; c->unsent = new std::queue; - c->buffer_consumed = c->buffer_used = 0; + c->buffer_consumed = c->buffer_used = 0; } void connection_destroy( connection_t *c) { if (c->unsent) delete c->unsent; - /* - A connection need not always be open - we only try to close it - if it is open. - */ - if( c->fd >= 0 ) - { - if( close( c->fd ) ) - { - wperror( L"close" ); - } - } + /* + A connection need not always be open - we only try to close it + if it is open. + */ + if( c->fd >= 0 ) + { + if( close( c->fd ) ) + { + wperror( L"close" ); + } + } } diff --git a/env_universal_common.h b/env_universal_common.h index 7da8cd63a..d783da549 100644 --- a/env_universal_common.h +++ b/env_universal_common.h @@ -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_queue_t; @@ -78,46 +78,46 @@ typedef std::queue 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 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 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. diff --git a/event.cpp b/event.cpp index fc04ab13d..371a3a349 100644 --- a/event.cpp +++ b/event.cpp @@ -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_list_t; +typedef std::vector 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; itype == 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 *out ) { - size_t i; - int found = 0; - - if( events.empty() ) - return 0; + size_t i; + int found = 0; - CHECK( criterion, 0 ); - - for( i=0; ipush_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; ifunction_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 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); } diff --git a/event.h b/event.h index 1ba6d01de..7ed5efacf 100644 --- a/event.h +++ b/event.h @@ -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 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 *out ); diff --git a/exec.cpp b/exec.cpp index a3400a988..92ce69cf2 100644 --- a/exec.cpp +++ b/exec.cpp @@ -1,8 +1,8 @@ /** \file exec.c - Functions for executing a program. + Functions for executing a program. - Some of the code in this file is based on code from the Glibc - manual, though I the changes performed have been massive. + Some of the code in this file is based on code from the Glibc + manual, though I the changes performed have been massive. */ #include "config.h" @@ -76,7 +76,7 @@ List of all pipes used by internal pipes. These must be closed in many situations in order to make sure that stray fds aren't lying around. - + Note this is used after fork, so we must not do anything that may allocate memory. Hopefully methods like open_fds.at() don't. */ static std::vector open_fds; @@ -84,37 +84,37 @@ static std::vector open_fds; // Called in a forked child static void exec_write_and_exit( int fd, const char *buff, size_t count, int status ) { - if( write_loop(fd, buff, count) == -1 ) - { - debug( 0, WRITE_ERROR); - wperror( L"write" ); - exit_without_destructors(status); - } - exit_without_destructors( status ); + if( write_loop(fd, buff, count) == -1 ) + { + debug( 0, WRITE_ERROR); + wperror( L"write" ); + exit_without_destructors(status); + } + exit_without_destructors( status ); } void exec_close( int fd ) { ASSERT_IS_MAIN_THREAD(); - + /* This may be called in a child of fork(), so don't allocate memory */ - if( fd < 0 ) - { - debug( 0, L"Called close on invalid file descriptor " ); - return; - } - - while( close(fd) == -1 ) - { - if( errno != EINTR ) - { - debug( 1, FD_ERROR, fd ); - wperror( L"close" ); - break; - } - } - + if( fd < 0 ) + { + debug( 0, L"Called close on invalid file descriptor " ); + return; + } + + while( close(fd) == -1 ) + { + if( errno != EINTR ) + { + debug( 1, FD_ERROR, fd ); + wperror( L"close" ); + break; + } + } + /* Maybe remove this from our set of open fds */ if ((size_t)fd < open_fds.size()) { open_fds[fd] = false; @@ -123,27 +123,27 @@ void exec_close( int fd ) int exec_pipe( int fd[2]) { - int res; - - while ((res=pipe(fd))) - { - if( errno != EINTR ) - { - wperror(L"pipe"); - return res; - } - } - - debug( 4, L"Created pipe using fds %d and %d", fd[0], fd[1]); - + int res; + + while ((res=pipe(fd))) + { + if( errno != EINTR ) + { + wperror(L"pipe"); + return res; + } + } + + debug( 4, L"Created pipe using fds %d and %d", fd[0], fd[1]); + int max_fd = std::max(fd[0], fd[1]); if (max_fd >= 0 && open_fds.size() <= (size_t)max_fd) { open_fds.resize(max_fd + 1, false); } open_fds.at(fd[0]) = true; open_fds.at(fd[1]) = true; - - return res; + + return res; } /** @@ -159,7 +159,7 @@ static bool use_fd_in_pipe(int fd, const io_chain_t &io_chain ) for (size_t idx = 0; idx < io_chain.size(); idx++) { const io_data_t *io = io_chain.at(idx); - if( ( io->io_mode == IO_BUFFER ) || + if( ( io->io_mode == IO_BUFFER ) || ( io->io_mode == IO_PIPE ) ) { if( io->param1.pipe_fd[0] == fd || @@ -175,7 +175,7 @@ static bool use_fd_in_pipe(int fd, const io_chain_t &io_chain ) Close all fds in open_fds, except for those that are mentioned in the redirection list io. This should make sure that there are no stray opened file descriptors in the child. - + \param io the list of io redirections for this job. Pipes mentioned here should not be closed. */ @@ -215,24 +215,24 @@ void get_unused_internal_pipes(std::vector &fds, const io_chain_t &io) char *get_interpreter( const char *command, char *interpreter, size_t buff_size ) { // OK to not use CLO_EXEC here because this is only called after fork - int fd = open( command, O_RDONLY ); - if( fd >= 0 ) - { + int fd = open( command, O_RDONLY ); + if( fd >= 0 ) + { size_t idx = 0; - while( idx + 1 < buff_size ) - { + while( idx + 1 < buff_size ) + { char ch; ssize_t amt = read(fd, &ch, sizeof ch); - if( amt <= 0 ) - break; - if( ch == '\n' ) - break; + if( amt <= 0 ) + break; + if( ch == '\n' ) + break; interpreter[idx++] = ch; - } + } interpreter[idx++] = '\0'; close(fd); - } - if (strncmp(interpreter, "#! /", 4) == 0) { + } + if (strncmp(interpreter, "#! /", 4) == 0) { return interpreter + 3; } else if (strncmp(interpreter, "#!/", 3) == 0) { return interpreter + 2; @@ -250,30 +250,30 @@ char *get_interpreter( const char *command, char *interpreter, size_t buff_size /* Called in a forked child! Do not allocate memory, etc. */ static void safe_launch_process( process_t *p, const char *actual_cmd, char **argv, char **envv ) { - int err; - -// debug( 1, L"exec '%ls'", p->argv[0] ); + int err; + +// debug( 1, L"exec '%ls'", p->argv[0] ); // Wow, this wcs2str call totally allocates memory - execve ( actual_cmd, argv, envv ); - - err = errno; - - /* - Something went wrong with execve, check for a ":", and run - /bin/sh if encountered. This is a weird predecessor to the shebang - that is still sometimes used since it is supported on Windows. - */ + execve ( actual_cmd, argv, envv ); + + err = errno; + + /* + Something went wrong with execve, check for a ":", and run + /bin/sh if encountered. This is a weird predecessor to the shebang + that is still sometimes used since it is supported on Windows. + */ /* OK to not use CLO_EXEC here because this is called after fork and the file is immediately closed */ - int fd = open(actual_cmd, O_RDONLY); - if (fd >= 0) - { - char begin[1] = {0}; - ssize_t amt_read = read(fd, begin, 1); - close(fd); - - if( (amt_read==1) && (begin[0] == ':') ) - { + int fd = open(actual_cmd, O_RDONLY); + if (fd >= 0) + { + char begin[1] = {0}; + ssize_t amt_read = read(fd, begin, 1); + close(fd); + + if( (amt_read==1) && (begin[0] == ':') ) + { // Relaunch it with /bin/sh. Don't allocate memory, so if you have more args than this, update your silly script! Maybe this should be changed to be based on ARG_MAX somehow. char sh_command[] = "/bin/sh"; char *argv2[128]; @@ -283,14 +283,14 @@ static void safe_launch_process( process_t *p, const char *actual_cmd, char **ar if (argv2[i] == NULL) break; } - - execve(sh_command, argv2, envv); - } - } - - errno = err; + + execve(sh_command, argv2, envv); + } + } + + errno = err; safe_report_exec_error(errno, actual_cmd, argv, envv); - exit_without_destructors(STATUS_EXEC_FAIL); + exit_without_destructors(STATUS_EXEC_FAIL); } /** @@ -300,11 +300,11 @@ static void launch_process_nofork( process_t *p ) { ASSERT_IS_MAIN_THREAD(); ASSERT_IS_NOT_FORKED_CHILD(); - - char **argv = wcsv2strv(p->get_argv()); - char **envv = env_export_arr( false ); + + char **argv = wcsv2strv(p->get_argv()); + char **envv = env_export_arr( false ); char *actual_cmd = wcs2str(p->actual_cmd.c_str()); - + /* Bounce to launch_process. This never returns. */ safe_launch_process(p, actual_cmd, argv, envv); } @@ -316,7 +316,7 @@ static void launch_process_nofork( process_t *p ) */ static int has_fd( const io_chain_t &d, int fd ) { - return io_chain_get( d, fd ) != NULL; + return io_chain_get( d, fd ) != NULL; } /** @@ -329,7 +329,7 @@ static void io_cleanup_chains(io_chain_t &chains, const std::vector &opened for (size_t idx = 0; idx < opened_fds.size(); idx++) { close(opened_fds.at(idx)); } - + /* Then delete all of the redirections we made */ chains.destroy(); } @@ -346,25 +346,25 @@ static void io_cleanup_chains(io_chain_t &chains, const std::vector &opened static bool io_transmogrify(const io_chain_t &in_chain, io_chain_t &out_chain, std::vector &out_opened_fds) { ASSERT_IS_MAIN_THREAD(); assert(out_chain.empty()); - + /* Just to be clear what we do for an empty chain */ if (in_chain.empty()) { return true; } - + bool success = true; - + /* Make our chain of redirections */ io_chain_t result_chain; - + /* In the event we can't finish transmorgrifying, we'll have to close all the files we opened. */ std::vector opened_fds; - + for (size_t idx = 0; idx < in_chain.size(); idx++) { io_data_t *in = in_chain.at(idx); io_data_t *out = NULL; //gets allocated via new - + switch( in->io_mode ) { default: @@ -372,7 +372,7 @@ static bool io_transmogrify(const io_chain_t &in_chain, io_chain_t &out_chain, s fprintf(stderr, "Unknown io_mode %ld\n", (long)in->io_mode); abort(); break; - + /* These redirections don't need transmogrification. They can be passed through. */ @@ -384,13 +384,13 @@ static bool io_transmogrify(const io_chain_t &in_chain, io_chain_t &out_chain, s out = new io_data_t(*in); break; } - + /* Transmogrify file redirections */ case IO_FILE: { - out = new io_data_t(); + out = new io_data_t(); out->fd = in->fd; out->io_mode = IO_FD; out->param2.close_old = 1; @@ -398,31 +398,31 @@ static bool io_transmogrify(const io_chain_t &in_chain, io_chain_t &out_chain, s int fd; if ((fd=open(in->filename_cstr, in->param2.flags, OPEN_MASK))==-1) { - debug( 1, + debug( 1, FILE_ERROR, in->filename_cstr ); - + wperror( L"open" ); success = false; break; - } + } opened_fds.push_back(fd); out->param1.old_fd = fd; - + break; } } - + /* Record this IO redirection even if we failed (so we can free it) */ result_chain.push_back(out); - + /* But don't go any further if we failed */ if (! success) { break; } } - + /* Now either return success, or clean up */ if (success) { /* Yay */ @@ -446,68 +446,68 @@ static bool io_transmogrify(const io_chain_t &in_chain, io_chain_t &out_chain, s */ static void internal_exec_helper( parser_t &parser, - const wchar_t *def, - enum block_type_t block_type, - io_chain_t &ios ) + const wchar_t *def, + enum block_type_t block_type, + io_chain_t &ios ) { io_chain_t morphed_chain; std::vector opened_fds; bool transmorgrified = io_transmogrify(ios, morphed_chain, opened_fds); - - int is_block_old=is_block; - is_block=1; - - /* - Did the transmogrification fail - if so, set error status and return - */ - if( ! transmorgrified ) - { - proc_set_last_status( STATUS_EXEC_FAIL ); - return; - } - - signal_unblock(); - - parser.eval( def, morphed_chain, block_type ); - - signal_block(); - - io_cleanup_chains(morphed_chain, opened_fds); - job_reap( 0 ); - is_block=is_block_old; + + int is_block_old=is_block; + is_block=1; + + /* + Did the transmogrification fail - if so, set error status and return + */ + if( ! transmorgrified ) + { + proc_set_last_status( STATUS_EXEC_FAIL ); + return; + } + + signal_unblock(); + + parser.eval( def, morphed_chain, block_type ); + + signal_block(); + + io_cleanup_chains(morphed_chain, opened_fds); + job_reap( 0 ); + is_block=is_block_old; } /** Perform output from builtins. Called from a forked child, so don't do anything that may allocate memory, etc.. */ static void do_builtin_io( const char *out, const char *err ) { size_t len; - if (out && (len = strlen(out))) - { + if (out && (len = strlen(out))) + { - if (write_loop(STDOUT_FILENO, out, len) == -1) - { - debug( 0, L"Error while writing to stdout" ); - wperror( L"write_loop" ); - show_stackframe(); - } - } - - if (err && (len = strlen(err))) - { - if (write_loop(STDERR_FILENO, err, len) == -1) - { - /* - Can't really show any error message here, since stderr is - dead. - */ - } - } + if (write_loop(STDOUT_FILENO, out, len) == -1) + { + debug( 0, L"Error while writing to stdout" ); + wperror( L"write_loop" ); + show_stackframe(); + } + } + + if (err && (len = strlen(err))) + { + if (write_loop(STDERR_FILENO, err, len) == -1) + { + /* + Can't really show any error message here, since stderr is + dead. + */ + } + } } /* Returns whether we can use posix spawn for a given process in a given job. Per https://github.com/fish-shell/fish-shell/issues/364 , error handling for file redirections is too difficult with posix_spawn So in that case we use fork/exec - + Furthermore, to avoid the race between the caller calling tcsetpgrp() and the client checking the foreground process group, we don't use posix_spawn if we're going to foreground the process. (If we use fork(), we can call tcsetpgrp after the fork, before the exec, and avoid the racse). */ static bool can_use_posix_spawn_for_job(const job_t *job, const process_t *process) @@ -543,621 +543,621 @@ static bool can_use_posix_spawn_for_job(const job_t *job, const process_t *proce void exec( parser_t &parser, job_t *j ) { - process_t *p; - pid_t pid = 0; - int mypipe[2]; - sigset_t chldset; - - io_data_t pipe_read, pipe_write; + process_t *p; + pid_t pid = 0; + int mypipe[2]; + sigset_t chldset; - io_data_t *io_buffer =0; + io_data_t pipe_read, pipe_write; - /* - Set to true if something goes wrong while exec:ing the job, in - which case the cleanup code will kick in. - */ - bool exec_error = false; + io_data_t *io_buffer =0; - bool needs_keepalive = false; - process_t keepalive; - + /* + Set to true if something goes wrong while exec:ing the job, in + which case the cleanup code will kick in. + */ + bool exec_error = false; - CHECK( j, ); - CHECK_BLOCK(); - - if( no_exec ) - return; - - sigemptyset( &chldset ); - sigaddset( &chldset, SIGCHLD ); - - debug( 4, L"Exec job '%ls' with id %d", j->command_wcstr(), j->job_id ); - - if( ! parser.block_io.empty() ) - { + bool needs_keepalive = false; + process_t keepalive; + + + CHECK( j, ); + CHECK_BLOCK(); + + if( no_exec ) + return; + + sigemptyset( &chldset ); + sigaddset( &chldset, SIGCHLD ); + + debug( 4, L"Exec job '%ls' with id %d", j->command_wcstr(), j->job_id ); + + if( ! parser.block_io.empty() ) + { io_duplicate_prepend(parser.block_io, j->io); - } + } - const io_data_t *input_redirect = NULL; + const io_data_t *input_redirect = NULL; for (size_t idx = 0; idx < j->io.size(); idx++) - { + { input_redirect = j->io.at(idx); - - if( (input_redirect->io_mode == IO_BUFFER) && - input_redirect->is_input ) - { - /* - Input redirection - create a new gobetween process to take - care of buffering - */ - process_t *fake = new process_t(); + + if( (input_redirect->io_mode == IO_BUFFER) && + input_redirect->is_input ) + { + /* + Input redirection - create a new gobetween process to take + care of buffering + */ + process_t *fake = new process_t(); fake->type = INTERNAL_BUFFER; - fake->pipe_write_fd = 1; - j->first_process->pipe_read_fd = input_redirect->fd; - fake->next = j->first_process; - j->first_process = fake; - break; - } - } - - if( j->first_process->type==INTERNAL_EXEC ) - { - /* - Do a regular launch - but without forking first... - */ - signal_block(); + fake->pipe_write_fd = 1; + j->first_process->pipe_read_fd = input_redirect->fd; + fake->next = j->first_process; + j->first_process = fake; + break; + } + } - /* - setup_child_process makes sure signals are properly set - up. It will also call signal_unblock - */ - if( !setup_child_process( j, 0 ) ) - { - /* - launch_process _never_ returns - */ - launch_process_nofork( j->first_process ); - } - else - { - job_set_flag( j, JOB_CONSTRUCTED, 1 ); - j->first_process->completed=1; - return; - } + if( j->first_process->type==INTERNAL_EXEC ) + { + /* + Do a regular launch - but without forking first... + */ + signal_block(); - } + /* + setup_child_process makes sure signals are properly set + up. It will also call signal_unblock + */ + if( !setup_child_process( j, 0 ) ) + { + /* + launch_process _never_ returns + */ + launch_process_nofork( j->first_process ); + } + else + { + job_set_flag( j, JOB_CONSTRUCTED, 1 ); + j->first_process->completed=1; + return; + } - pipe_read.fd=0; - pipe_write.fd=1; - pipe_read.io_mode=IO_PIPE; - pipe_read.param1.pipe_fd[0] = -1; - pipe_read.param1.pipe_fd[1] = -1; - pipe_read.is_input = 1; + } + + pipe_read.fd=0; + pipe_write.fd=1; + pipe_read.io_mode=IO_PIPE; + pipe_read.param1.pipe_fd[0] = -1; + pipe_read.param1.pipe_fd[1] = -1; + pipe_read.is_input = 1; + + pipe_write.io_mode=IO_PIPE; + pipe_write.is_input = 0; + pipe_write.param1.pipe_fd[0]=pipe_write.param1.pipe_fd[1]=-1; - pipe_write.io_mode=IO_PIPE; - pipe_write.is_input = 0; - pipe_write.param1.pipe_fd[0]=pipe_write.param1.pipe_fd[1]=-1; - j->io.push_back(&pipe_write); - - signal_block(); - /* - See if we need to create a group keepalive process. This is - a process that we create to make sure that the process group - doesn't die accidentally, and is often needed when a - builtin/block/function is inside a pipeline, since that - usually means we have to wait for one program to exit before - continuing in the pipeline, causing the group leader to - exit. - */ - - if( job_get_flag( j, JOB_CONTROL ) ) - { - for( p=j->first_process; p; p = p->next ) - { - if( p->type != EXTERNAL ) - { - if( p->next ) - { - needs_keepalive = true; - break; - } - if( p != j->first_process ) - { - needs_keepalive = true; - break; - } - - } - - } - } - - if( needs_keepalive ) - { + signal_block(); + + /* + See if we need to create a group keepalive process. This is + a process that we create to make sure that the process group + doesn't die accidentally, and is often needed when a + builtin/block/function is inside a pipeline, since that + usually means we have to wait for one program to exit before + continuing in the pipeline, causing the group leader to + exit. + */ + + if( job_get_flag( j, JOB_CONTROL ) ) + { + for( p=j->first_process; p; p = p->next ) + { + if( p->type != EXTERNAL ) + { + if( p->next ) + { + needs_keepalive = true; + break; + } + if( p != j->first_process ) + { + needs_keepalive = true; + break; + } + + } + + } + } + + if( needs_keepalive ) + { /* Call fork. No need to wait for threads since our use is confined and simple. */ if (g_log_forks) { printf("fork #%d: Executing keepalive fork for '%ls'\n", g_fork_count, j->command_wcstr()); } - keepalive.pid = execute_fork(false); - if( keepalive.pid == 0 ) - { + keepalive.pid = execute_fork(false); + if( keepalive.pid == 0 ) + { /* Child */ - keepalive.pid = getpid(); - set_child_group( j, &keepalive, 1 ); - pause(); - exit_without_destructors(0); - } - else - { + keepalive.pid = getpid(); + set_child_group( j, &keepalive, 1 ); + pause(); + exit_without_destructors(0); + } + else + { /* Parent */ - set_child_group( j, &keepalive, 0 ); - } - } - - /* - This loop loops over every process_t in the job, starting it as - appropriate. This turns out to be rather complex, since a - process_t can be one of many rather different things. + set_child_group( j, &keepalive, 0 ); + } + } - The loop also has to handle pipelining between the jobs. - */ + /* + This loop loops over every process_t in the job, starting it as + appropriate. This turns out to be rather complex, since a + process_t can be one of many rather different things. - for( p=j->first_process; p; p = p->next ) - { - const bool p_wants_pipe = (p->next != NULL); - mypipe[1]=-1; - - pipe_write.fd = p->pipe_write_fd; - pipe_read.fd = p->pipe_read_fd; -// debug( 0, L"Pipe created from fd %d to fd %d", pipe_write.fd, pipe_read.fd ); - + The loop also has to handle pipelining between the jobs. + */ - /* - This call is used so the global environment variable array - is regenerated, if needed, before the fork. That way, we - avoid a lot of duplicate work where EVERY child would need - to generate it, since that result would not get written - back to the parent. This call could be safely removed, but - it would result in slightly lower performance - at least on - uniprocessor systems. - */ - if( p->type == EXTERNAL ) - env_export_arr( true ); - - - /* - Set up fd:s that will be used in the pipe - */ - - if( p == j->first_process->next ) - { + for( p=j->first_process; p; p = p->next ) + { + const bool p_wants_pipe = (p->next != NULL); + mypipe[1]=-1; + + pipe_write.fd = p->pipe_write_fd; + pipe_read.fd = p->pipe_read_fd; +// debug( 0, L"Pipe created from fd %d to fd %d", pipe_write.fd, pipe_read.fd ); + + + /* + This call is used so the global environment variable array + is regenerated, if needed, before the fork. That way, we + avoid a lot of duplicate work where EVERY child would need + to generate it, since that result would not get written + back to the parent. This call could be safely removed, but + it would result in slightly lower performance - at least on + uniprocessor systems. + */ + if( p->type == EXTERNAL ) + env_export_arr( true ); + + + /* + Set up fd:s that will be used in the pipe + */ + + if( p == j->first_process->next ) + { j->io.push_back(&pipe_read); - } - - if( p_wants_pipe ) - { -// debug( 1, L"%ls|%ls" , p->argv[0], p->next->argv[0]); - - if( exec_pipe( mypipe ) == -1 ) - { - debug( 1, PIPE_ERROR ); - wperror (L"pipe"); - exec_error = true; - break; - } + } - memcpy( pipe_write.param1.pipe_fd, mypipe, sizeof(int)*2); - } - else - { - /* - This is the last element of the pipeline. - Remove the io redirection for pipe output. - */ + if( p_wants_pipe ) + { +// debug( 1, L"%ls|%ls" , p->argv[0], p->next->argv[0]); + + if( exec_pipe( mypipe ) == -1 ) + { + debug( 1, PIPE_ERROR ); + wperror (L"pipe"); + exec_error = true; + break; + } + + memcpy( pipe_write.param1.pipe_fd, mypipe, sizeof(int)*2); + } + else + { + /* + This is the last element of the pipeline. + Remove the io redirection for pipe output. + */ io_chain_t::iterator where = std::find(j->io.begin(), j->io.end(), &pipe_write); if (where != j->io.end()) j->io.erase(where); - } + } - switch( p->type ) - { - case INTERNAL_FUNCTION: - { - wchar_t * def=0; - int shadows; - + switch( p->type ) + { + case INTERNAL_FUNCTION: + { + wchar_t * def=0; + int shadows; - /* - Calls to function_get_definition might need to - source a file as a part of autoloading, hence there - must be no blocks. - */ - signal_unblock(); + /* + Calls to function_get_definition might need to + source a file as a part of autoloading, hence there + must be no blocks. + */ + + signal_unblock(); wcstring orig_def; function_get_definition( p->argv0(), &orig_def ); - + // function_get_named_arguments may trigger autoload, which deallocates the orig_def. // We should make function_get_definition return a wcstring (but how to handle NULL...) if (! orig_def.empty()) def = wcsdup(orig_def.c_str()); - - wcstring_list_t named_arguments = function_get_named_arguments( p->argv0() ); - shadows = function_get_shadows( p->argv0() ); - signal_block(); - - if( def == NULL ) - { - debug( 0, _( L"Unknown function '%ls'" ), p->argv0() ); - break; - } + wcstring_list_t named_arguments = function_get_named_arguments( p->argv0() ); + shadows = function_get_shadows( p->argv0() ); + + signal_block(); + + if( def == NULL ) + { + debug( 0, _( L"Unknown function '%ls'" ), p->argv0() ); + break; + } function_block_t *newv = new function_block_t(p, p->argv0(), shadows); - parser.push_block( newv ); - + parser.push_block( newv ); - /* - set_argv might trigger an event - handler, hence we need to unblock - signals. - */ - signal_unblock(); - parse_util_set_argv( p->get_argv()+1, named_arguments ); - signal_block(); - - parser.forbid_function( p->argv0() ); - if( p->next ) - { - io_buffer = io_buffer_create( 0 ); + /* + set_argv might trigger an event + handler, hence we need to unblock + signals. + */ + signal_unblock(); + parse_util_set_argv( p->get_argv()+1, named_arguments ); + signal_block(); + + parser.forbid_function( p->argv0() ); + + if( p->next ) + { + io_buffer = io_buffer_create( 0 ); j->io.push_back(io_buffer); - } - - internal_exec_helper( parser, def, TOP, j->io ); - - parser.allow_function(); - parser.pop_block(); + } + + internal_exec_helper( parser, def, TOP, j->io ); + + parser.allow_function(); + parser.pop_block(); free(def); - - break; - } - - case INTERNAL_BLOCK: - { - if( p->next ) - { - io_buffer = io_buffer_create( 0 ); + + break; + } + + case INTERNAL_BLOCK: + { + if( p->next ) + { + io_buffer = io_buffer_create( 0 ); j->io.push_back(io_buffer); - } - - internal_exec_helper( parser, p->argv0(), TOP, j->io ); - break; - - } + } - case INTERNAL_BUILTIN: - { - int builtin_stdin=0; - int fg; - int close_stdin=0; + internal_exec_helper( parser, p->argv0(), TOP, j->io ); + break; - /* - If this is the first process, check the io - redirections and see where we should be reading - from. - */ - if( p == j->first_process ) - { - const io_data_t *in = io_chain_get( j->io, 0 ); - - if( in ) - { - switch( in->io_mode ) - { - - case IO_FD: - { - builtin_stdin = in->param1.old_fd; - break; - } - case IO_PIPE: - { - builtin_stdin = in->param1.pipe_fd[0]; - break; - } - - case IO_FILE: - { + } + + case INTERNAL_BUILTIN: + { + int builtin_stdin=0; + int fg; + int close_stdin=0; + + /* + If this is the first process, check the io + redirections and see where we should be reading + from. + */ + if( p == j->first_process ) + { + const io_data_t *in = io_chain_get( j->io, 0 ); + + if( in ) + { + switch( in->io_mode ) + { + + case IO_FD: + { + builtin_stdin = in->param1.old_fd; + break; + } + case IO_PIPE: + { + builtin_stdin = in->param1.pipe_fd[0]; + break; + } + + case IO_FILE: + { /* Do not set CLO_EXEC because child needs access */ - builtin_stdin=open( in->filename_cstr, + builtin_stdin=open( in->filename_cstr, in->param2.flags, OPEN_MASK ); - if( builtin_stdin == -1 ) - { - debug( 1, - FILE_ERROR, - in->filename_cstr ); - wperror( L"open" ); - } - else - { - close_stdin = 1; - } - - break; - } - - case IO_CLOSE: - { - /* - FIXME: + if( builtin_stdin == -1 ) + { + debug( 1, + FILE_ERROR, + in->filename_cstr ); + wperror( L"open" ); + } + else + { + close_stdin = 1; + } - When - requesting - that - stdin - be - closed, - we - really - don't - do - anything. How - should - this - be - handled? - */ - builtin_stdin = -1; - - break; - } - - default: - { - builtin_stdin=-1; - debug( 1, - _( L"Unknown input redirection type %d" ), - in->io_mode); - break; - } - - } - } - } - else - { - builtin_stdin = pipe_read.param1.pipe_fd[0]; - } + break; + } - if( builtin_stdin == -1 ) - { - exec_error = true; - break; - } - else - { - int old_out = builtin_out_redirect; - int old_err = builtin_err_redirect; + case IO_CLOSE: + { + /* + FIXME: - /* - Since this may be the foreground job, and since - a builtin may execute another foreground job, - we need to pretend to suspend this job while - running the builtin, in order to avoid a - situation where two jobs are running at once. + When + requesting + that + stdin + be + closed, + we + really + don't + do + anything. How + should + this + be + handled? + */ + builtin_stdin = -1; - The reason this is done here, and not by the - relevant builtins, is that this way, the - builtin does not need to know what job it is - part of. It could probably figure that out by - walking the job list, but it seems more robust - to make exec handle things. - */ - - builtin_push_io( parser, builtin_stdin ); - - builtin_out_redirect = has_fd( j->io, 1 ); - builtin_err_redirect = has_fd( j->io, 2 ); + break; + } - fg = job_get_flag( j, JOB_FOREGROUND ); - job_set_flag( j, JOB_FOREGROUND, 0 ); - - signal_unblock(); - - p->status = builtin_run( parser, p->get_argv(), j->io ); - - builtin_out_redirect=old_out; - builtin_err_redirect=old_err; - - signal_block(); - - /* - Restore the fg flag, which is temporarily set to - false during builtin execution so as not to confuse - some job-handling builtins. - */ - job_set_flag( j, JOB_FOREGROUND, fg ); - } - - /* - If stdin has been redirected, close the redirection - stream. - */ - if( close_stdin ) - { - exec_close( builtin_stdin ); - } - break; - } - } - - if( exec_error ) - { - break; - } - - switch( p->type ) - { + default: + { + builtin_stdin=-1; + debug( 1, + _( L"Unknown input redirection type %d" ), + in->io_mode); + break; + } - case INTERNAL_BLOCK: - case INTERNAL_FUNCTION: - { - int status = proc_get_last_status(); - - /* - Handle output from a block or function. This usually - means do nothing, but in the case of pipes, we have - to buffer such io, since otherwise the internal pipe - buffer might overflow. - */ - if( !io_buffer ) - { - /* - No buffer, so we exit directly. This means we - have to manually set the exit status. - */ - if( p->next == 0 ) - { - proc_set_last_status( job_get_flag( j, JOB_NEGATE )?(!status):status); - } - p->completed = 1; - break; - } + } + } + } + else + { + builtin_stdin = pipe_read.param1.pipe_fd[0]; + } + + if( builtin_stdin == -1 ) + { + exec_error = true; + break; + } + else + { + int old_out = builtin_out_redirect; + int old_err = builtin_err_redirect; + + /* + Since this may be the foreground job, and since + a builtin may execute another foreground job, + we need to pretend to suspend this job while + running the builtin, in order to avoid a + situation where two jobs are running at once. + + The reason this is done here, and not by the + relevant builtins, is that this way, the + builtin does not need to know what job it is + part of. It could probably figure that out by + walking the job list, but it seems more robust + to make exec handle things. + */ + + builtin_push_io( parser, builtin_stdin ); + + builtin_out_redirect = has_fd( j->io, 1 ); + builtin_err_redirect = has_fd( j->io, 2 ); + + fg = job_get_flag( j, JOB_FOREGROUND ); + job_set_flag( j, JOB_FOREGROUND, 0 ); + + signal_unblock(); + + p->status = builtin_run( parser, p->get_argv(), j->io ); + + builtin_out_redirect=old_out; + builtin_err_redirect=old_err; + + signal_block(); + + /* + Restore the fg flag, which is temporarily set to + false during builtin execution so as not to confuse + some job-handling builtins. + */ + job_set_flag( j, JOB_FOREGROUND, fg ); + } + + /* + If stdin has been redirected, close the redirection + stream. + */ + if( close_stdin ) + { + exec_close( builtin_stdin ); + } + break; + } + } + + if( exec_error ) + { + break; + } + + switch( p->type ) + { + + case INTERNAL_BLOCK: + case INTERNAL_FUNCTION: + { + int status = proc_get_last_status(); + + /* + Handle output from a block or function. This usually + means do nothing, but in the case of pipes, we have + to buffer such io, since otherwise the internal pipe + buffer might overflow. + */ + if( !io_buffer ) + { + /* + No buffer, so we exit directly. This means we + have to manually set the exit status. + */ + if( p->next == 0 ) + { + proc_set_last_status( job_get_flag( j, JOB_NEGATE )?(!status):status); + } + p->completed = 1; + break; + } + + io_remove( j->io, io_buffer ); + + io_buffer_read( io_buffer ); - io_remove( j->io, io_buffer ); - - io_buffer_read( io_buffer ); - const char *buffer = io_buffer->out_buffer_ptr(); size_t count = io_buffer->out_buffer_size(); - - if( io_buffer->out_buffer_size() > 0 ) - { + + if( io_buffer->out_buffer_size() > 0 ) + { /* We don't have to drain threads here because our child process is simple */ if (g_log_forks) { printf("Executing fork for internal block or function for '%ls'\n", p->argv0()); } - pid = execute_fork(false); - if( pid == 0 ) - { - - /* - This is the child process. Write out the contents of the pipeline. - */ - p->pid = getpid(); - setup_child_process( j, p ); + pid = execute_fork(false); + if( pid == 0 ) + { - exec_write_and_exit(io_buffer->fd, buffer, count, status); - } - else - { - /* - This is the parent process. Store away - information on the child, and possibly give - it control over the terminal. - */ - p->pid = pid; - set_child_group( j, p, 0 ); - - } - - } - else - { - if( p->next == 0 ) - { - proc_set_last_status( job_get_flag( j, JOB_NEGATE )?(!status):status); - } - p->completed = 1; - } - - io_buffer_destroy( io_buffer ); - - io_buffer=0; - break; - - } + /* + This is the child process. Write out the contents of the pipeline. + */ + p->pid = getpid(); + setup_child_process( j, p ); + + exec_write_and_exit(io_buffer->fd, buffer, count, status); + } + else + { + /* + This is the parent process. Store away + information on the child, and possibly give + it control over the terminal. + */ + p->pid = pid; + set_child_group( j, p, 0 ); + + } + + } + else + { + if( p->next == 0 ) + { + proc_set_last_status( job_get_flag( j, JOB_NEGATE )?(!status):status); + } + p->completed = 1; + } + + io_buffer_destroy( io_buffer ); + + io_buffer=0; + break; + + } - case INTERNAL_BUFFER: - { - - const char *buffer = input_redirect->out_buffer_ptr(); - size_t count = input_redirect->out_buffer_size(); - + case INTERNAL_BUFFER: + { + + const char *buffer = input_redirect->out_buffer_ptr(); + size_t count = input_redirect->out_buffer_size(); + /* We don't have to drain threads here because our child process is simple */ if (g_log_forks) { printf("fork #%d: Executing fork for internal buffer for '%ls'\n", g_fork_count, p->argv0() ? p->argv0() : L"(null)"); } - pid = execute_fork(false); - if( pid == 0 ) - { - /* - This is the child process. Write out the - contents of the pipeline. - */ - p->pid = getpid(); - setup_child_process( j, p ); - - exec_write_and_exit( 1, buffer, count, 0); - } - else - { - /* - This is the parent process. Store away - information on the child, and possibly give - it control over the terminal. - */ - p->pid = pid; - set_child_group( j, p, 0 ); - } + pid = execute_fork(false); + if( pid == 0 ) + { + /* + This is the child process. Write out the + contents of the pipeline. + */ + p->pid = getpid(); + setup_child_process( j, p ); - break; - } - - case INTERNAL_BUILTIN: - { - int skip_fork; - - /* - Handle output from builtin commands. In the general - case, this means forking of a worker process, that - will write out the contents of the stdout and stderr - buffers to the correct file descriptor. Since - forking is expensive, fish tries to avoid it wehn - possible. - */ + exec_write_and_exit( 1, buffer, count, 0); + } + else + { + /* + This is the parent process. Store away + information on the child, and possibly give + it control over the terminal. + */ + p->pid = pid; + set_child_group( j, p, 0 ); + } - /* - If a builtin didn't produce any output, and it is - not inside a pipeline, there is no need to fork - */ - skip_fork = + break; + } + + case INTERNAL_BUILTIN: + { + int skip_fork; + + /* + Handle output from builtin commands. In the general + case, this means forking of a worker process, that + will write out the contents of the stdout and stderr + buffers to the correct file descriptor. Since + forking is expensive, fish tries to avoid it wehn + possible. + */ + + /* + If a builtin didn't produce any output, and it is + not inside a pipeline, there is no need to fork + */ + skip_fork = get_stdout_buffer().empty() && get_stderr_buffer().empty() && - !p->next; - - /* - If the output of a builtin is to be sent to an internal - buffer, there is no need to fork. This helps out the - performance quite a bit in complex completion code. - */ + !p->next; + + /* + If the output of a builtin is to be sent to an internal + buffer, there is no need to fork. This helps out the + performance quite a bit in complex completion code. + */ + + io_data_t *io = io_chain_get( j->io, 1 ); + bool buffer_stdout = io && io->io_mode == IO_BUFFER; + + if( ( get_stderr_buffer().empty() ) && + ( !p->next ) && + ( ! get_stdout_buffer().empty() ) && + ( buffer_stdout ) ) + { + const std::string res = wcs2string( get_stdout_buffer() ); + io->out_buffer_append( res.c_str(), res.size() ); + skip_fork = 1; + } - io_data_t *io = io_chain_get( j->io, 1 ); - bool buffer_stdout = io && io->io_mode == IO_BUFFER; - - if( ( get_stderr_buffer().empty() ) && - ( !p->next ) && - ( ! get_stdout_buffer().empty() ) && - ( buffer_stdout ) ) - { - const std::string res = wcs2string( get_stdout_buffer() ); - io->out_buffer_append( res.c_str(), res.size() ); - skip_fork = 1; - } - if (! skip_fork && j->io.empty()) { /* PCA for some reason, fish forks a lot, even for basic builtins like echo just to write out their buffers. I'm certain a lot of this is unnecessary, but I am not sure exactly when. If j->io is NULL, then it means there's no pipes or anything, so we can certainly just write out our data. Beyond that, we may be able to do the same if io_get returns 0 for STDOUT_FILENO and STDERR_FILENO. */ if (g_log_forks) { @@ -1172,100 +1172,100 @@ void exec( parser_t &parser, job_t *j ) } for( io_chain_t::iterator iter = j->io.begin(); iter != j->io.end(); iter++ ) - { + { io_data_t *tmp_io = *iter; - if( tmp_io->io_mode == IO_FILE && strcmp(tmp_io->filename_cstr, "/dev/null") != 0) - { - skip_fork = 0; + if( tmp_io->io_mode == IO_FILE && strcmp(tmp_io->filename_cstr, "/dev/null") != 0) + { + skip_fork = 0; break; - } - } - - - if( skip_fork ) - { - p->completed=1; - if( p->next == 0 ) - { - debug( 3, L"Set status of %ls to %d using short circut", j->command_wcstr(), p->status ); - - int status = p->status; - proc_set_last_status( job_get_flag( j, JOB_NEGATE )?(!status):status ); - } - break; - } + } + } - /* Ok, unfortunatly, we have to do a real fork. Bummer. We work hard to make sure we don't have to wait for all our threads to exit, by arranging things so that we don't have to allocate memory or do anything except system calls in the child. */ - + if( skip_fork ) + { + p->completed=1; + if( p->next == 0 ) + { + debug( 3, L"Set status of %ls to %d using short circut", j->command_wcstr(), p->status ); + + int status = p->status; + proc_set_last_status( job_get_flag( j, JOB_NEGATE )?(!status):status ); + } + break; + } + + + /* Ok, unfortunatly, we have to do a real fork. Bummer. We work hard to make sure we don't have to wait for all our threads to exit, by arranging things so that we don't have to allocate memory or do anything except system calls in the child. */ + /* Get the strings we'll write before we fork (since they call malloc) */ const wcstring &out = get_stdout_buffer(), &err = get_stderr_buffer(); char *outbuff = wcs2str(out.c_str()), *errbuff = wcs2str(err.c_str()); - + fflush(stdout); fflush(stderr); if (g_log_forks) { printf("fork #%d: Executing fork for internal builtin for '%ls'\n", g_fork_count, p->argv0()); io_print(io_chain_t(io)); } - pid = execute_fork(false); - if( pid == 0 ) - { - /* - This is the child process. Setup redirections, - print correct output to stdout and stderr, and - then exit. - */ - p->pid = getpid(); - setup_child_process( j, p ); - do_builtin_io(outbuff, errbuff); - exit_without_destructors( p->status ); - - } - else - { + pid = execute_fork(false); + if( pid == 0 ) + { + /* + This is the child process. Setup redirections, + print correct output to stdout and stderr, and + then exit. + */ + p->pid = getpid(); + setup_child_process( j, p ); + do_builtin_io(outbuff, errbuff); + exit_without_destructors( p->status ); + + } + else + { /* Free the strings in the parent */ free(outbuff); free(errbuff); - - /* - This is the parent process. Store away - information on the child, and possibly give - it control over the terminal. - */ - p->pid = pid; - - set_child_group( j, p, 0 ); - - } - - break; - } - - case EXTERNAL: - { + + /* + This is the parent process. Store away + information on the child, and possibly give + it control over the terminal. + */ + p->pid = pid; + + set_child_group( j, p, 0 ); + + } + + break; + } + + case EXTERNAL: + { /* Get argv and envv before we fork */ null_terminated_array_t argv_array = convert_wide_array_to_narrow(p->get_argv_array()); - + null_terminated_array_t envv_array; env_export_arr(false, envv_array); - + char **envv = envv_array.get(); char **argv = argv_array.get(); - + std::string actual_cmd_str = wcs2string(p->actual_cmd); const char *actual_cmd = actual_cmd_str.c_str(); - + const wchar_t *reader_current_filename(); if (g_log_forks) { const wchar_t *file = reader_current_filename(); const wchar_t *func = parser_t::principal_parser().is_function(); printf("fork #%d: forking for '%s' in '%ls:%ls'\n", g_fork_count, actual_cmd, file ? file : L"", func ? func : L"?"); - + fprintf(stderr, "IO chain for %s:\n", actual_cmd); io_print(j->io); } - + #if FISH_USE_POSIX_SPAWN /* Prefer to use posix_spawn, since it's faster on some systems like OS X */ bool use_posix_spawn = g_use_posix_spawn && can_use_posix_spawn_for_job(j, p); @@ -1279,22 +1279,22 @@ void exec( parser_t &parser, job_t *j ) { /* We successfully made the attributes and actions; actually call posix_spawn */ int spawn_ret = posix_spawn(&pid, actual_cmd, &actions, &attr, argv, envv); - + /* This usleep can be used to test for various race conditions (https://github.com/fish-shell/fish-shell/issues/360) */ //usleep(10000); - + if (spawn_ret != 0) { safe_report_exec_error(spawn_ret, actual_cmd, argv, envv); /* Make sure our pid isn't set */ pid = 0; } - + /* Clean up our actions */ posix_spawn_file_actions_destroy(&actions); posix_spawnattr_destroy(&attr); } - + /* A 0 pid means we failed to posix_spawn. Since we have no pid, we'll never get told when it's exited, so we have to mark the process as failed. */ if (pid == 0) { @@ -1312,7 +1312,7 @@ void exec( parser_t &parser, job_t *j ) p->pid = getpid(); setup_child_process( j, p ); safe_launch_process( p, actual_cmd, argv, envv ); - + /* safe_launch_process _never_ returns... */ @@ -1320,7 +1320,7 @@ void exec( parser_t &parser, job_t *j ) } - /* + /* This is the parent process. Store away information on the child, and possibly fice it control over the terminal. @@ -1329,174 +1329,174 @@ void exec( parser_t &parser, job_t *j ) set_child_group( j, p, 0 ); - break; - } - - } + break; + } - if( p->type == INTERNAL_BUILTIN ) - builtin_pop_io(parser); - - /* - Close the pipe the current process uses to read from the - previous process_t - */ - if( pipe_read.param1.pipe_fd[0] >= 0 ) - exec_close( pipe_read.param1.pipe_fd[0] ); - /* - Set up the pipe the next process uses to read from the - current process_t - */ - if( p_wants_pipe ) - pipe_read.param1.pipe_fd[0] = mypipe[0]; - - /* - If there is a next process in the pipeline, close the - output end of the current pipe (the surrent child - subprocess already has a copy of the pipe - this makes sure - we don't leak file descriptors either in the shell or in - the children). - */ - if( p->next ) - { - exec_close(mypipe[1]); - } - } + } - /* - The keepalive process is no longer needed, so we terminate it - with extreme prejudice - */ - if( needs_keepalive ) - { - kill( keepalive.pid, SIGKILL ); - } - - signal_unblock(); + if( p->type == INTERNAL_BUILTIN ) + builtin_pop_io(parser); - debug( 3, L"Job is constructed" ); + /* + Close the pipe the current process uses to read from the + previous process_t + */ + if( pipe_read.param1.pipe_fd[0] >= 0 ) + exec_close( pipe_read.param1.pipe_fd[0] ); + /* + Set up the pipe the next process uses to read from the + current process_t + */ + if( p_wants_pipe ) + pipe_read.param1.pipe_fd[0] = mypipe[0]; + + /* + If there is a next process in the pipeline, close the + output end of the current pipe (the surrent child + subprocess already has a copy of the pipe - this makes sure + we don't leak file descriptors either in the shell or in + the children). + */ + if( p->next ) + { + exec_close(mypipe[1]); + } + } + + /* + The keepalive process is no longer needed, so we terminate it + with extreme prejudice + */ + if( needs_keepalive ) + { + kill( keepalive.pid, SIGKILL ); + } + + signal_unblock(); + + debug( 3, L"Job is constructed" ); + + io_remove( j->io, &pipe_read ); - io_remove( j->io, &pipe_read ); - for (io_chain_t::const_iterator iter = parser.block_io.begin(); iter != parser.block_io.end(); iter++) { io_remove( j->io, *iter ); } - - job_set_flag( j, JOB_CONSTRUCTED, 1 ); - if( !job_get_flag( j, JOB_FOREGROUND ) ) - { - proc_last_bg_pid = j->pgid; - } + job_set_flag( j, JOB_CONSTRUCTED, 1 ); + + if( !job_get_flag( j, JOB_FOREGROUND ) ) + { + proc_last_bg_pid = j->pgid; + } + + if( !exec_error ) + { + job_continue (j, 0); + } - if( !exec_error ) - { - job_continue (j, 0); - } - } static int exec_subshell_internal( const wcstring &cmd, wcstring_list_t *lst ) { ASSERT_IS_MAIN_THREAD(); - char *begin, *end; - char z=0; - int prev_subshell = is_subshell; - int status, prev_status; - io_data_t *io_buffer; - char sep=0; - - const env_var_t ifs = env_get_string(L"IFS"); + char *begin, *end; + char z=0; + int prev_subshell = is_subshell; + int status, prev_status; + io_data_t *io_buffer; + char sep=0; + + const env_var_t ifs = env_get_string(L"IFS"); + + if( ! ifs.missing_or_empty() ) + { + if( ifs.at(0) < 128 ) + { + sep = '\n';//ifs[0]; + } + else + { + sep = 0; + debug( 0, L"Warning - invalid command substitution separator '%lc'. Please change the firsta character of IFS", ifs[0] ); + } + + } + + is_subshell=1; + io_buffer= io_buffer_create( 0 ); + + prev_status = proc_get_last_status(); - if( ! ifs.missing_or_empty() ) - { - if( ifs.at(0) < 128 ) - { - sep = '\n';//ifs[0]; - } - else - { - sep = 0; - debug( 0, L"Warning - invalid command substitution separator '%lc'. Please change the firsta character of IFS", ifs[0] ); - } - - } - - is_subshell=1; - io_buffer= io_buffer_create( 0 ); - - prev_status = proc_get_last_status(); - parser_t &parser = parser_t::principal_parser(); - if( parser.eval( cmd, io_chain_t(io_buffer), SUBST ) ) - { - status = -1; - } - else - { - status = proc_get_last_status(); - } - - io_buffer_read( io_buffer ); - - proc_set_last_status( prev_status ); - - is_subshell = prev_subshell; - - io_buffer->out_buffer_append( &z, 1 ); - - begin=end=io_buffer->out_buffer_ptr(); + if( parser.eval( cmd, io_chain_t(io_buffer), SUBST ) ) + { + status = -1; + } + else + { + status = proc_get_last_status(); + } - if( lst ) - { - while( 1 ) - { - if( *end == 0 ) - { - if( begin != end ) - { - wchar_t *el = str2wcs( begin ); - if( el ) - { - lst->push_back(el); + io_buffer_read( io_buffer ); - free(el); - } - else - { - debug( 2, L"Got null string on line %d of file %s", __LINE__, __FILE__ ); - } - } - io_buffer_destroy( io_buffer ); - - return status; - } - else if( *end == sep ) - { - wchar_t *el; - *end=0; - el = str2wcs( begin ); - if( el ) - { + proc_set_last_status( prev_status ); + + is_subshell = prev_subshell; + + io_buffer->out_buffer_append( &z, 1 ); + + begin=end=io_buffer->out_buffer_ptr(); + + if( lst ) + { + while( 1 ) + { + if( *end == 0 ) + { + if( begin != end ) + { + wchar_t *el = str2wcs( begin ); + if( el ) + { + lst->push_back(el); + + free(el); + } + else + { + debug( 2, L"Got null string on line %d of file %s", __LINE__, __FILE__ ); + } + } + io_buffer_destroy( io_buffer ); + + return status; + } + else if( *end == sep ) + { + wchar_t *el; + *end=0; + el = str2wcs( begin ); + if( el ) + { lst->push_back(el); - free(el); - } - else - { - debug( 2, L"Got null string on line %d of file %s", __LINE__, __FILE__ ); - } - begin = end+1; - } - end++; - } - } - - io_buffer_destroy( io_buffer ); + free(el); + } + else + { + debug( 2, L"Got null string on line %d of file %s", __LINE__, __FILE__ ); + } + begin = end+1; + } + end++; + } + } - return status; + io_buffer_destroy( io_buffer ); + + return status; } int exec_subshell( const wcstring &cmd, std::vector &outputs ) diff --git a/exec.h b/exec.h index 97342f114..9ca30839f 100644 --- a/exec.h +++ b/exec.h @@ -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 diff --git a/expand.cpp b/expand.cpp index 98dfc460a..b6806c81b 100644 --- a/expand.cpp +++ b/expand.cpp @@ -1,7 +1,7 @@ /**\file expand.c String expansion functions. These functions perform several kinds of -parameter expansion. +parameter expansion. */ @@ -128,27 +128,27 @@ static void remove_internal_separator( wcstring &s, bool conv ); int expand_is_clean( const wchar_t *in ) { - const wchar_t * str = in; + const wchar_t * str = in; - CHECK( in, 1 ); + CHECK( in, 1 ); - /* - Test characters that have a special meaning in the first character position - */ - if( wcschr( UNCLEAN_FIRST, *str ) ) - return 0; + /* + Test characters that have a special meaning in the first character position + */ + if( wcschr( UNCLEAN_FIRST, *str ) ) + return 0; - /* - Test characters that have a special meaning in any character position - */ - while( *str ) - { - if( wcschr( UNCLEAN, *str ) ) - return 0; - str++; - } + /* + Test characters that have a special meaning in any character position + */ + while( *str ) + { + if( wcschr( UNCLEAN, *str ) ) + return 0; + str++; + } - return 1; + return 1; } /** @@ -156,9 +156,9 @@ int expand_is_clean( const wchar_t *in ) */ static env_var_t expand_var(const wchar_t *in) { - if( !in ) - return env_var_t::missing_var(); - return env_get_string( in ); + if( !in ) + return env_var_t::missing_var(); + return env_get_string( in ); } /** @@ -167,22 +167,22 @@ static env_var_t expand_var(const wchar_t *in) */ static int is_quotable( const wchar_t *str ) { - switch( *str ) - { - case 0: - return 1; + switch( *str ) + { + case 0: + return 1; - case L'\n': - case L'\t': - case L'\r': - case L'\b': - case L'\x1b': - return 0; + case L'\n': + case L'\t': + case L'\r': + case L'\b': + case L'\x1b': + return 0; - default: - return is_quotable(str+1); - } - return 0; + default: + return is_quotable(str+1); + } + return 0; } @@ -193,55 +193,55 @@ static int is_quotable(const wcstring &str) { wcstring expand_escape_variable( const wcstring &in ) { - wcstring_list_t lst; - wcstring buff; + wcstring_list_t lst; + wcstring buff; - tokenize_variable_array( in, lst ); + tokenize_variable_array( in, lst ); - switch( lst.size() ) - { - case 0: - buff.append(L"''"); - break; - - case 1: - { + switch( lst.size() ) + { + case 0: + buff.append(L"''"); + break; + + case 1: + { const wcstring &el = lst.at(0); - if( el.find(L' ') != wcstring::npos && is_quotable( el ) ) - { + if( el.find(L' ') != wcstring::npos && is_quotable( el ) ) + { buff.append(L"'"); buff.append(el); buff.append(L"'"); - } - else - { + } + else + { buff.append(escape_string(el, 1)); - } - break; - } - default: - { - for( size_t j=0; j L'9' ) - { - return 0; - } - } - return 1; + for( ; *n; n++ ) + { + if( *n < L'0' || *n > L'9' ) + { + return 0; + } + } + return 1; } /** @@ -264,21 +264,21 @@ static int iswnumeric( const wchar_t *n ) cmd */ static bool match_pid( const wcstring &cmd, - const wchar_t *proc, - int flags, - size_t *offset) + const wchar_t *proc, + int flags, + size_t *offset) { - /* Test for a direct match. If the proc string is empty (e.g. the user tries to complete against %), then return an offset pointing at the base command. That ensures that you don't see a bunch of dumb paths when completing against all processes. */ - if( proc[0] != L'\0' && wcsncmp( cmd.c_str(), proc, wcslen( proc ) ) == 0 ) - { - if( offset ) - *offset = 0; - return true; - } - + /* Test for a direct match. If the proc string is empty (e.g. the user tries to complete against %), then return an offset pointing at the base command. That ensures that you don't see a bunch of dumb paths when completing against all processes. */ + if( proc[0] != L'\0' && wcsncmp( cmd.c_str(), proc, wcslen( proc ) ) == 0 ) + { + if( offset ) + *offset = 0; + return true; + } + /* Get the command to match against. We're only interested in the last path component. */ const wcstring base_cmd = wbasename(cmd); - + bool result = string_prefixes_string(proc, base_cmd); if (result) { @@ -297,9 +297,9 @@ static bool match_pid( const wcstring &cmd, class process_iterator_t { std::vector pids; size_t idx; - + wcstring name_for_pid(pid_t pid); - + public: process_iterator_t(); bool next_process(wcstring *str, pid_t *pid); @@ -311,30 +311,30 @@ wcstring process_iterator_t::name_for_pid(pid_t pid) int mib[4], maxarg = 0, numArgs = 0; size_t size = 0; char *args = NULL, *stringPtr = NULL; - + mib[0] = CTL_KERN; mib[1] = KERN_ARGMAX; - + size = sizeof(maxarg); if (sysctl(mib, 2, &maxarg, &size, NULL, 0) == -1) { return result; } - + args = (char *)malloc( maxarg ); if ( args == NULL ) { return result; } - + mib[0] = CTL_KERN; mib[1] = KERN_PROCARGS2; mib[2] = pid; - + size = (size_t)maxarg; if ( sysctl(mib, 3, args, &size, NULL, 0) == -1 ) { free( args ); return result;; } - + memcpy( &numArgs, args, sizeof(numArgs) ); stringPtr = args + sizeof(numArgs); result = str2wcstring(stringPtr); @@ -374,8 +374,8 @@ process_iterator_t::process_iterator_t() : idx(0) // Declaring name as const requires us to cast it when passing it to // sysctl because the prototype doesn't include the const modifier. size_t length; - - + + // We start by calling sysctl with result == NULL and length == 0. // That will succeed, and set length to the appropriate length. // We then allocate a buffer of that size and call sysctl again @@ -385,14 +385,14 @@ process_iterator_t::process_iterator_t() : idx(0) // is necessary because the ENOMEM failure case sets length to // the amount of data returned, not the amount of data that // could have been returned. - + result = NULL; done = false; do { assert(result == NULL); - + // Call sysctl with a NULL buffer. - + length = 0; err = sysctl( (int *) name, (sizeof(name) / sizeof(*name)) - 1, NULL, &length, @@ -400,20 +400,20 @@ process_iterator_t::process_iterator_t() : idx(0) if (err == -1) { err = errno; } - + // Allocate an appropriately sized buffer based on the results // from the previous call. - + if (err == 0) { result = (struct kinfo_proc *)malloc(length); if (result == NULL) { err = ENOMEM; } } - + // Call sysctl again with the new buffer. If we get an ENOMEM // error, toss away our buffer and start again. - + if (err == 0) { err = sysctl( (int *) name, (sizeof(name) / sizeof(*name)) - 1, result, &length, @@ -431,16 +431,16 @@ process_iterator_t::process_iterator_t() : idx(0) } } } while (err == 0 && ! done); - + // Clean up and establish post conditions. if (err == 0 && result != NULL) { for (size_t idx = 0; idx < length / sizeof(struct kinfo_proc); idx++) pids.push_back(result[idx].kp_proc.p_pid); } - + if (result) - free(result); + free(result); } #else @@ -448,11 +448,11 @@ process_iterator_t::process_iterator_t() : idx(0) /* /proc style process completions */ class process_iterator_t { DIR *dir; - + public: process_iterator_t(); ~process_iterator_t(); - + bool next_process(wcstring *out_str, pid_t *out_pid); }; @@ -476,30 +476,30 @@ bool process_iterator_t::next_process(wcstring *out_str, pid_t *out_pid) wcstring name; if (! dir || ! wreaddir(dir, name)) break; - - if (!iswnumeric(name.c_str())) - continue; - + + if (!iswnumeric(name.c_str())) + continue; + wcstring path = wcstring(L"/proc/") + name; struct stat buf; - if (wstat(path, &buf)) - continue; + if (wstat(path, &buf)) + continue; + + if( buf.st_uid != getuid() ) + continue; - if( buf.st_uid != getuid() ) - continue; - /* remember the pid */ pid = fish_wcstoi(name.c_str(), NULL, 10); - + /* the 'cmdline' file exists, it should contain the commandline */ FILE *cmdfile; if ((cmdfile=wfopen(path + L"/cmdline", "r"))) { wcstring full_command_line; - signal_block(); - fgetws2(&full_command_line, cmdfile); - signal_unblock(); - + signal_block(); + fgetws2(&full_command_line, cmdfile); + signal_unblock(); + /* The command line needs to be escaped */ wchar_t *first_arg = tok_first( full_command_line.c_str() ); if (first_arg) @@ -522,7 +522,7 @@ bool process_iterator_t::next_process(wcstring *out_str, pid_t *out_pid) if (cmdfile) fclose(cmdfile); } - + bool result = ! cmd.empty(); if (result) { @@ -562,11 +562,11 @@ std::vector expand_get_all_process_names(void) */ static int find_process( const wchar_t *proc, - expand_flags_t flags, - std::vector &out ) + expand_flags_t flags, + std::vector &out ) { - int found = 0; - + int found = 0; + if (! (flags & EXPAND_SKIP_JOBS)) { ASSERT_IS_MAIN_THREAD(); @@ -592,7 +592,7 @@ static int find_process( const wchar_t *proc, if( wcsncmp( proc, jid, wcslen(proc ) )==0 ) { wcstring desc_buff = format_string(COMPLETE_JOB_DESC_VAL, j->command_wcstr()); - append_completion( out, + append_completion( out, jid+wcslen(proc), desc_buff, 0 ); @@ -605,7 +605,7 @@ static int find_process( const wchar_t *proc, int jid; wchar_t *end; - + errno = 0; jid = fish_wcstoi( proc, &end, 10 ); if( jid > 0 && !errno && !*end ) @@ -627,16 +627,16 @@ static int find_process( const wchar_t *proc, job_iterator_t jobs; while ((j = jobs.next())) { - + if( j->command_is_empty() ) continue; - + size_t offset; if( match_pid( j->command(), proc, flags, &offset ) ) { if( flags & ACCEPT_INCOMPLETE ) { - append_completion( out, + append_completion( out, j->command_wcstr() + offset + wcslen(proc), COMPLETE_JOB_DESC, 0 ); @@ -664,13 +664,13 @@ static int find_process( const wchar_t *proc, { if( p->actual_cmd.empty() ) continue; - + size_t offset; if( match_pid( p->actual_cmd, proc, flags, &offset ) ) { if( flags & ACCEPT_INCOMPLETE ) { - append_completion( out, + append_completion( out, wcstring(p->actual_cmd, offset + wcslen(proc)), COMPLETE_CHILD_PROCESS_DESC, 0 ); @@ -704,7 +704,7 @@ static int find_process( const wchar_t *proc, { if( flags & ACCEPT_INCOMPLETE ) { - append_completion( out, + append_completion( out, process_name.c_str() + offset + wcslen(proc), COMPLETE_PROCESS_DESC, 0 ); @@ -716,162 +716,162 @@ static int find_process( const wchar_t *proc, } } - return 1; + return 1; } /** Process id expansion */ static int expand_pid( const wcstring &instr_with_sep, - expand_flags_t flags, - std::vector &out ) + expand_flags_t flags, + std::vector &out ) { - + /* expand_string calls us with internal separators in instr...sigh */ wcstring instr = instr_with_sep; remove_internal_separator(instr, false); - - if( instr.empty() || instr.at(0) != PROCESS_EXPAND ) - { + + if( instr.empty() || instr.at(0) != PROCESS_EXPAND ) + { append_completion(out, instr); - return 1; - } - + return 1; + } + const wchar_t * const in = instr.c_str(); - if( flags & ACCEPT_INCOMPLETE ) - { - if( wcsncmp( in+1, SELF_STR, wcslen(in+1) )==0 ) - { - append_completion( out, - SELF_STR+wcslen(in+1), - COMPLETE_SELF_DESC, - 0 ); - } - else if( wcsncmp( in+1, LAST_STR, wcslen(in+1) )==0 ) - { - append_completion( out, - LAST_STR+wcslen(in+1), - COMPLETE_LAST_DESC, - 0 ); - } - } - else - { - if( wcscmp( (in+1), SELF_STR )==0 ) - { + if( flags & ACCEPT_INCOMPLETE ) + { + if( wcsncmp( in+1, SELF_STR, wcslen(in+1) )==0 ) + { + append_completion( out, + SELF_STR+wcslen(in+1), + COMPLETE_SELF_DESC, + 0 ); + } + else if( wcsncmp( in+1, LAST_STR, wcslen(in+1) )==0 ) + { + append_completion( out, + LAST_STR+wcslen(in+1), + COMPLETE_LAST_DESC, + 0 ); + } + } + else + { + if( wcscmp( (in+1), SELF_STR )==0 ) + { append_completion(out, to_string(getpid())); - return 1; - } - if( wcscmp( (in+1), LAST_STR )==0 ) - { - if( proc_last_bg_pid > 0 ) - { + return 1; + } + if( wcscmp( (in+1), LAST_STR )==0 ) + { + if( proc_last_bg_pid > 0 ) + { append_completion(out, to_string(proc_last_bg_pid)); - } + } - return 1; - } - } + return 1; + } + } - size_t prev = out.size(); - if( !find_process( in+1, flags, out ) ) - return 0; + size_t prev = out.size(); + if( !find_process( in+1, flags, out ) ) + return 0; - if( prev == out.size() ) - { - if( ! (flags & ACCEPT_INCOMPLETE) ) - { - return 0; - } - } + if( prev == out.size() ) + { + if( ! (flags & ACCEPT_INCOMPLETE) ) + { + return 0; + } + } - return 1; + return 1; } void expand_variable_error( parser_t &parser, const wchar_t *token, size_t token_pos, int error_pos ) { - size_t stop_pos = token_pos+1; - - switch( token[stop_pos] ) - { - case BRACKET_BEGIN: - { - wchar_t *cpy = wcsdup( token ); - *(cpy+token_pos)=0; - wchar_t *name = &cpy[stop_pos+1]; - wchar_t *end = wcschr( name, BRACKET_END ); - wchar_t *post; - int is_var=0; - if( end ) - { - post = end+1; - *end = 0; - - if( !wcsvarname( name ) ) - { - is_var = 1; - } - } - - if( is_var ) - { - parser.error( SYNTAX_ERROR, - error_pos, - COMPLETE_VAR_BRACKET_DESC, - cpy, - name, - post ); - } - else - { - parser.error( SYNTAX_ERROR, - error_pos, - COMPLETE_VAR_BRACKET_DESC, - L"", - L"VARIABLE", - L"" ); - } - free( cpy ); - - break; - } - - case INTERNAL_SEPARATOR: - { - parser.error( SYNTAX_ERROR, - error_pos, - COMPLETE_VAR_PARAN_DESC ); - break; - } - - case 0: - { - parser.error( SYNTAX_ERROR, - error_pos, - COMPLETE_VAR_NULL_DESC ); - break; - } - - default: - { + size_t stop_pos = token_pos+1; + + switch( token[stop_pos] ) + { + case BRACKET_BEGIN: + { + wchar_t *cpy = wcsdup( token ); + *(cpy+token_pos)=0; + wchar_t *name = &cpy[stop_pos+1]; + wchar_t *end = wcschr( name, BRACKET_END ); + wchar_t *post; + int is_var=0; + if( end ) + { + post = end+1; + *end = 0; + + if( !wcsvarname( name ) ) + { + is_var = 1; + } + } + + if( is_var ) + { + parser.error( SYNTAX_ERROR, + error_pos, + COMPLETE_VAR_BRACKET_DESC, + cpy, + name, + post ); + } + else + { + parser.error( SYNTAX_ERROR, + error_pos, + COMPLETE_VAR_BRACKET_DESC, + L"", + L"VARIABLE", + L"" ); + } + free( cpy ); + + break; + } + + case INTERNAL_SEPARATOR: + { + parser.error( SYNTAX_ERROR, + error_pos, + COMPLETE_VAR_PARAN_DESC ); + break; + } + + case 0: + { + parser.error( SYNTAX_ERROR, + error_pos, + COMPLETE_VAR_NULL_DESC ); + break; + } + + default: + { wchar_t token_stop_char = token[stop_pos]; // Unescape (see http://github.com/fish-shell/fish-shell/issues/50) if (token_stop_char == ANY_CHAR) token_stop_char = L'?'; else if (token_stop_char == ANY_STRING || token_stop_char == ANY_STRING_RECURSIVE) token_stop_char = L'*'; - - parser.error( SYNTAX_ERROR, - error_pos, - (token_stop_char == L'?' ? COMPLETE_YOU_WANT_STATUS : COMPLETE_VAR_DESC), - token_stop_char ); - break; - } - } + + parser.error( SYNTAX_ERROR, + error_pos, + (token_stop_char == L'?' ? COMPLETE_YOU_WANT_STATUS : COMPLETE_VAR_DESC), + token_stop_char ); + break; + } + } } /** @@ -879,73 +879,73 @@ void expand_variable_error( parser_t &parser, const wchar_t *token, size_t token */ static int parse_slice( const wchar_t *in, wchar_t **end_ptr, std::vector &idx, size_t array_size ) { - wchar_t *end; - - const long size = (long)array_size; - size_t pos = 1; //skip past the opening square bracket - - // debug( 0, L"parse_slice on '%ls'", in ); - - while( 1 ) - { - long tmp; - - while( iswspace(in[pos]) || (in[pos]==INTERNAL_SEPARATOR)) - pos++; - - if( in[pos] == L']' ) - { - pos++; - break; - } - - errno=0; - tmp = wcstol( &in[pos], &end, 10 ); - if( ( errno ) || ( end == &in[pos] ) ) - { - return 1; - } - // debug( 0, L"Push idx %d", tmp ); - - long i1 = tmp>-1 ? tmp : (long)array_size+tmp+1; - pos = end-in; - while( in[pos]==INTERNAL_SEPARATOR ) - pos++; - if ( in[pos]==L'.' && in[pos+1]==L'.' ){ - pos+=2; - while( in[pos]==INTERNAL_SEPARATOR ) - pos++; - long tmp1 = wcstol( &in[pos], &end, 10 ); - if( ( errno ) || ( end == &in[pos] ) ) - { - return 1; - } - pos = end-in; + wchar_t *end; - // debug( 0, L"Push range %d %d", tmp, tmp1 ); - long i2 = tmp1>-1 ? tmp1 : size+tmp1+1; - // debug( 0, L"Push range idx %d %d", i1, i2 ); - short direction = i2-1 ? tmp : (long)array_size+tmp+1; + pos = end-in; + while( in[pos]==INTERNAL_SEPARATOR ) + pos++; + if ( in[pos]==L'.' && in[pos+1]==L'.' ){ + pos+=2; + while( in[pos]==INTERNAL_SEPARATOR ) + pos++; + long tmp1 = wcstol( &in[pos], &end, 10 ); + if( ( errno ) || ( end == &in[pos] ) ) + { + return 1; + } + pos = end-in; + + // debug( 0, L"Push range %d %d", tmp, tmp1 ); + long i2 = tmp1>-1 ? tmp1 : size+tmp1+1; + // debug( 0, L"Push range idx %d %d", i1, i2 ); + short direction = i2 &out, long last_idx ) { - int is_ok= 1; - int empty=0; - + int is_ok= 1; + int empty=0; + wcstring var_tmp; std::vector var_idx_list; - - // CHECK( out, 0 ); - - for( long i=last_idx; (i>=0) && is_ok && !empty; i-- ) - { - const wchar_t c = in[i]; - if( ( c == VARIABLE_EXPAND ) || (c == VARIABLE_EXPAND_SINGLE ) ) - { - long start_pos = i+1; - long stop_pos; - long var_len; - int is_single = (c==VARIABLE_EXPAND_SINGLE); - - stop_pos = start_pos; - - while( 1 ) - { - if( !(in[stop_pos ]) ) - break; - if( !( iswalnum( in[stop_pos] ) || - (wcschr(L"_", in[stop_pos])!= 0) ) ) - break; - - stop_pos++; - } - /* printf( "Stop for '%c'\n", in[stop_pos]);*/ - - var_len = stop_pos - start_pos; - - if( var_len == 0 ) - { - expand_variable_error( parser, in, stop_pos-1, -1 ); - - is_ok = 0; - break; - } - + // CHECK( out, 0 ); + + for( long i=last_idx; (i>=0) && is_ok && !empty; i-- ) + { + const wchar_t c = in[i]; + if( ( c == VARIABLE_EXPAND ) || (c == VARIABLE_EXPAND_SINGLE ) ) + { + long start_pos = i+1; + long stop_pos; + long var_len; + int is_single = (c==VARIABLE_EXPAND_SINGLE); + + stop_pos = start_pos; + + while( 1 ) + { + if( !(in[stop_pos ]) ) + break; + if( !( iswalnum( in[stop_pos] ) || + (wcschr(L"_", in[stop_pos])!= 0) ) ) + break; + + stop_pos++; + } + + /* printf( "Stop for '%c'\n", in[stop_pos]);*/ + + var_len = stop_pos - start_pos; + + if( var_len == 0 ) + { + expand_variable_error( parser, in, stop_pos-1, -1 ); + + is_ok = 0; + break; + } + var_tmp.append(in + start_pos, var_len); - env_var_t var_val = expand_var(var_tmp.c_str() ); - - if( ! var_val.missing() ) - { - int all_vars=1; + env_var_t var_val = expand_var(var_tmp.c_str() ); + + if( ! var_val.missing() ) + { + int all_vars=1; wcstring_list_t var_item_list; - - if( is_ok ) - { - tokenize_variable_array( var_val.c_str(), var_item_list ); - - if( in[stop_pos] == L'[' ) - { - wchar_t *slice_end; - all_vars=0; - - if( parse_slice( in + stop_pos, &slice_end, var_idx_list, var_item_list.size() ) ) - { - parser.error( SYNTAX_ERROR, + + if( is_ok ) + { + tokenize_variable_array( var_val.c_str(), var_item_list ); + + if( in[stop_pos] == L'[' ) + { + wchar_t *slice_end; + all_vars=0; + + if( parse_slice( in + stop_pos, &slice_end, var_idx_list, var_item_list.size() ) ) + { + parser.error( SYNTAX_ERROR, -1, - L"Invalid index value" ); - is_ok = 0; - break; - } - stop_pos = (slice_end-in); - } - - if( !all_vars ) - { + L"Invalid index value" ); + is_ok = 0; + break; + } + stop_pos = (slice_end-in); + } + + if( !all_vars ) + { wcstring_list_t string_values(var_idx_list.size()); - - for( size_t j=0; j var_item_list.size() ) - { - parser.error( SYNTAX_ERROR, + if( tmp < 1 || (size_t)tmp > var_item_list.size() ) + { + parser.error( SYNTAX_ERROR, -1, ARRAY_BOUNDS_ERR ); - is_ok=0; - var_idx_list.resize(j); - break; - } - else - { - /* Replace each index in var_idx_list inplace with the string value at the specified index */ - //al_set( var_idx_list, j, wcsdup((const wchar_t *)al_get( &var_item_list, tmp-1 ) ) ); + is_ok=0; + var_idx_list.resize(j); + break; + } + else + { + /* Replace each index in var_idx_list inplace with the string value at the specified index */ + //al_set( var_idx_list, j, wcsdup((const wchar_t *)al_get( &var_item_list, tmp-1 ) ) ); string_values.at(j) = var_item_list.at(tmp-1); - } - } - + } + } + // string_values is the new var_item_list var_item_list.swap(string_values); - } - } - - if( is_ok ) - { - - if( is_single ) - { + } + } + + if( is_ok ) + { + + if( is_single ) + { in[i]=0; wcstring res = in; res.push_back(INTERNAL_SEPARATOR); - - for( size_t j=0; j 0) new_in.append(in, start_pos - 1); - + // at this point new_in.size() is start_pos - 1 if(start_pos>1 && new_in[start_pos-2]!=VARIABLE_EXPAND) { new_in.push_back(INTERNAL_SEPARATOR); - } + } new_in.append(next); - new_in.append(in + stop_pos); + new_in.append(in + stop_pos); is_ok &= expand_variables2( parser, new_in, out, i ); - } - } - - } - } - } - - return is_ok; - } - else - { - /* + } + } + + } + } + } + + return is_ok; + } + else + { + /* Expand a non-existing variable */ - if( c == VARIABLE_EXPAND ) - { - /* + if( c == VARIABLE_EXPAND ) + { + /* Regular expansion, i.e. expand this argument to nothing */ - empty = 1; - } - else - { - /* + empty = 1; + } + else + { + /* Expansion to single argument. */ - wcstring res; + wcstring res; in[i] = 0; res.append(in); res.append(in + stop_pos); - - is_ok &= expand_variables2( parser, res, out, i ); - return is_ok; - } - } - - - } - } - - if( !empty ) - { + + is_ok &= expand_variables2( parser, res, out, i ); + return is_ok; + } + } + + + } + } + + if( !empty ) + { append_completion(out, in); - } - - return is_ok; + } + + return is_ok; } /** @@ -1182,130 +1182,130 @@ static int expand_variables_internal( parser_t &parser, wchar_t * const in, std: */ static int expand_brackets(parser_t &parser, const wcstring &instr, int flags, std::vector &out ) { - const wchar_t *pos; - bool syntax_error = false; - int bracket_count=0; + const wchar_t *pos; + bool syntax_error = false; + int bracket_count=0; - const wchar_t *bracket_begin=0, *bracket_end=0; - const wchar_t *last_sep=0; + const wchar_t *bracket_begin=0, *bracket_end=0; + const wchar_t *last_sep=0; + + const wchar_t *item_begin; + size_t len1, len2, tot_len; - const wchar_t *item_begin; - size_t len1, len2, tot_len; - const wchar_t * const in = instr.c_str(); - - for( pos=in; - (*pos) && !syntax_error; - pos++ ) - { - switch( *pos ) - { - case BRACKET_BEGIN: - { - bracket_begin = pos; - bracket_count++; - break; + for( pos=in; + (*pos) && !syntax_error; + pos++ ) + { + switch( *pos ) + { + case BRACKET_BEGIN: + { + bracket_begin = pos; - } - case BRACKET_END: - { - bracket_count--; - if( bracket_end < bracket_begin ) - { - bracket_end = pos; - } - - if( bracket_count < 0 ) - { - syntax_error = true; - } - break; - } - case BRACKET_SEP: - { - if( bracket_count == 1 ) - last_sep = pos; - } - } - } + bracket_count++; + break; - if( bracket_count > 0 ) - { - if( !(flags & ACCEPT_INCOMPLETE) ) - { - syntax_error = true; - } - else - { + } + case BRACKET_END: + { + bracket_count--; + if( bracket_end < bracket_begin ) + { + bracket_end = pos; + } + + if( bracket_count < 0 ) + { + syntax_error = true; + } + break; + } + case BRACKET_SEP: + { + if( bracket_count == 1 ) + last_sep = pos; + } + } + } + + if( bracket_count > 0 ) + { + if( !(flags & ACCEPT_INCOMPLETE) ) + { + syntax_error = true; + } + else + { wcstring mod; - if( last_sep ) - { - mod.append( in, bracket_begin-in+1 ); - mod.append( last_sep+1 ); - mod.push_back( BRACKET_END ); - } - else - { + if( last_sep ) + { + mod.append( in, bracket_begin-in+1 ); + mod.append( last_sep+1 ); + mod.push_back( BRACKET_END ); + } + else + { mod.append(in); mod.push_back(BRACKET_END); - } + } - return expand_brackets( parser, mod, 1, out ); - } - } + return expand_brackets( parser, mod, 1, out ); + } + } - if( syntax_error ) - { - parser.error( SYNTAX_ERROR, - -1, - _(L"Mismatched brackets") ); - return 0; - } + if( syntax_error ) + { + parser.error( SYNTAX_ERROR, + -1, + _(L"Mismatched brackets") ); + return 0; + } - if( bracket_begin == 0 ) - { - append_completion(out, in); - return 1; - } + if( bracket_begin == 0 ) + { + append_completion(out, in); + return 1; + } - len1 = (bracket_begin-in); - len2 = wcslen(bracket_end)-1; - tot_len = len1+len2; - item_begin = bracket_begin+1; - for( pos=(bracket_begin+1); 1; pos++ ) - { - if( bracket_count == 0 ) - { - if( (*pos == BRACKET_SEP) || (pos==bracket_end) ) - { + len1 = (bracket_begin-in); + len2 = wcslen(bracket_end)-1; + tot_len = len1+len2; + item_begin = bracket_begin+1; + for( pos=(bracket_begin+1); 1; pos++ ) + { + if( bracket_count == 0 ) + { + if( (*pos == BRACKET_SEP) || (pos==bracket_end) ) + { assert(pos >= item_begin); - size_t item_len = pos-item_begin; - + size_t item_len = pos-item_begin; + wcstring whole_item; whole_item.reserve(tot_len + item_len + 2); whole_item.append(in, len1); whole_item.append(item_begin, item_len); whole_item.append(bracket_end + 1); expand_brackets( parser, whole_item, flags, out ); - - item_begin = pos+1; - if( pos == bracket_end ) - break; - } - } - if( *pos == BRACKET_BEGIN ) - { - bracket_count++; - } + item_begin = pos+1; + if( pos == bracket_end ) + break; + } + } - if( *pos == BRACKET_END ) - { - bracket_count--; - } - } - return 1; + if( *pos == BRACKET_BEGIN ) + { + bracket_count++; + } + + if( *pos == BRACKET_END ) + { + bracket_count--; + } + } + return 1; } /** @@ -1313,85 +1313,85 @@ static int expand_brackets(parser_t &parser, const wcstring &instr, int flags, s */ static int expand_cmdsubst( parser_t &parser, const wcstring &input, std::vector &outList ) { - wchar_t *paran_begin=0, *paran_end=0; + wchar_t *paran_begin=0, *paran_end=0; std::vector sub_res; - size_t i, j; - wchar_t *tail_begin = 0; - + size_t i, j; + wchar_t *tail_begin = 0; + const wchar_t * const in = input.c_str(); - - int parse_ret; - switch( parse_ret = parse_util_locate_cmdsubst(in, + + int parse_ret; + switch( parse_ret = parse_util_locate_cmdsubst(in, ¶n_begin, ¶n_end, 0 ) ) - { - case -1: - parser.error( SYNTAX_ERROR, + { + case -1: + parser.error( SYNTAX_ERROR, -1, L"Mismatched parenthesis" ); - return 0; - case 0: + return 0; + case 0: outList.push_back(completion_t(input)); - return 1; - case 1: - - break; - } - + return 1; + case 1: + + break; + } + const wcstring subcmd(paran_begin + 1, paran_end-paran_begin - 1); - - if( exec_subshell( subcmd, sub_res) == -1 ) - { - parser.error( CMDSUBST_ERROR, -1, L"Unknown error while evaulating command substitution" ); - return 0; - } - - tail_begin = paran_end + 1; - if( *tail_begin == L'[' ) - { + + if( exec_subshell( subcmd, sub_res) == -1 ) + { + parser.error( CMDSUBST_ERROR, -1, L"Unknown error while evaulating command substitution" ); + return 0; + } + + tail_begin = paran_end + 1; + if( *tail_begin == L'[' ) + { std::vector slice_idx; - wchar_t *slice_end; - - if( parse_slice( tail_begin, &slice_end, slice_idx, sub_res.size() ) ) - { - parser.error( SYNTAX_ERROR, -1, L"Invalid index value" ); - return 0; - } - else - { + wchar_t *slice_end; + + if( parse_slice( tail_begin, &slice_end, slice_idx, sub_res.size() ) ) + { + parser.error( SYNTAX_ERROR, -1, L"Invalid index value" ); + return 0; + } + else + { std::vector sub_res2; - tail_begin = slice_end; - for( i=0; i < slice_idx.size(); i++ ) - { - long idx = slice_idx.at(i); - if( idx < 1 || (size_t)idx > sub_res.size() ) - { - parser.error( SYNTAX_ERROR, - -1, - ARRAY_BOUNDS_ERR ); - return 0; - } - idx = idx-1; - + tail_begin = slice_end; + for( i=0; i < slice_idx.size(); i++ ) + { + long idx = slice_idx.at(i); + if( idx < 1 || (size_t)idx > sub_res.size() ) + { + parser.error( SYNTAX_ERROR, + -1, + ARRAY_BOUNDS_ERR ); + return 0; + } + idx = idx-1; + sub_res2.push_back(sub_res.at(idx)); - // debug( 0, L"Pushing item '%ls' with index %d onto sliced result", al_get( sub_res, idx ), idx ); - //sub_res[idx] = 0; // ?? - } - sub_res = sub_res2; - } - } - - - /* + // debug( 0, L"Pushing item '%ls' with index %d onto sliced result", al_get( sub_res, idx ), idx ); + //sub_res[idx] = 0; // ?? + } + sub_res = sub_res2; + } + } + + + /* Recursively call ourselves to expand any remaining command substitutions. The result of this recursive call using the tail of the string is inserted into the tail_expand array list */ std::vector tail_expand; - expand_cmdsubst( parser, tail_begin, tail_expand ); - - /* + expand_cmdsubst( parser, tail_begin, tail_expand ); + + /* Combine the result of the current command substitution with the result of the recursive tail expansion */ @@ -1399,35 +1399,35 @@ static int expand_cmdsubst( parser_t &parser, const wcstring &input, std::vector { wcstring sub_item = sub_res.at(i); wcstring sub_item2 = escape_string(sub_item, 1); - + for( j=0; j < tail_expand.size(); j++ ) { - + wcstring whole_item; - + wcstring tail_item = tail_expand.at(j).completion; - + //sb_append_substring( &whole_item, in, len1 ); whole_item.append(in, paran_begin-in); - + //sb_append_char( &whole_item, INTERNAL_SEPARATOR ); whole_item.push_back(INTERNAL_SEPARATOR); - + //sb_append_substring( &whole_item, sub_item2, item_len ); - whole_item.append(sub_item2); - + whole_item.append(sub_item2); + //sb_append_char( &whole_item, INTERNAL_SEPARATOR ); whole_item.push_back(INTERNAL_SEPARATOR); - + //sb_append( &whole_item, tail_item ); whole_item.append(tail_item); - - //al_push( out, whole_item.buff ); + + //al_push( out, whole_item.buff ); outList.push_back(completion_t(whole_item)); } } - - return 1; + + return 1; } /** @@ -1436,10 +1436,10 @@ static int expand_cmdsubst( parser_t &parser, const wcstring &input, std::vector __attribute__((unused)) static wchar_t *expand_unescape( parser_t &parser, const wchar_t * in, int escape_special ) { - wchar_t *res = unescape( in, escape_special ); - if( !res ) - parser.error( SYNTAX_ERROR, -1, L"Unexpected end of string" ); - return res; + wchar_t *res = unescape( in, escape_special ); + if( !res ) + parser.error( SYNTAX_ERROR, -1, L"Unexpected end of string" ); + return res; } static wcstring expand_unescape_string( const wcstring &in, int escape_special ) @@ -1447,7 +1447,7 @@ static wcstring expand_unescape_string( const wcstring &in, int escape_special ) wcstring tmp = in; unescape_string(tmp, escape_special); /* Need to detect error here */ - return tmp; + return tmp; } /** @@ -1455,24 +1455,24 @@ static wcstring expand_unescape_string( const wcstring &in, int escape_special ) */ static void expand_home_directory( wcstring &input ) { - const wchar_t * const in = input.c_str(); - if( in[0] == HOME_DIRECTORY ) - { - int tilde_error = 0; + const wchar_t * const in = input.c_str(); + if( in[0] == HOME_DIRECTORY ) + { + int tilde_error = 0; size_t tail_idx; wcstring home; - if( in[1] == '/' || in[1] == '\0' ) - { - /* Current users home directory */ + if( in[1] == '/' || in[1] == '\0' ) + { + /* Current users home directory */ - home = env_get_string( L"HOME" ); + home = env_get_string( L"HOME" ); tail_idx = 1; - } - else - { - /* Some other users home directory */ - const wchar_t *name_end = wcschr( in, L'/' ); + } + else + { + /* Some other users home directory */ + const wchar_t *name_end = wcschr( in, L'/' ); if (name_end) { tail_idx = name_end - in; @@ -1483,33 +1483,33 @@ static void expand_home_directory( wcstring &input ) } wcstring name_str = input.substr(1, tail_idx - 1); std::string name_cstr = wcs2string(name_str); - struct passwd *userinfo = getpwnam( name_cstr.c_str() ); + struct passwd *userinfo = getpwnam( name_cstr.c_str() ); - if( userinfo == NULL ) - { - tilde_error = 1; + if( userinfo == NULL ) + { + tilde_error = 1; input[0] = L'~'; - } - else - { - home = str2wcstring(userinfo->pw_dir); - } - } + } + else + { + home = str2wcstring(userinfo->pw_dir); + } + } if (! tilde_error) { input.replace(input.begin(), input.begin() + tail_idx, home); } - } + } } void expand_tilde(wcstring &input) { - if( ! input.empty() && input.at(0) == L'~' ) - { - input.at(0) = HOME_DIRECTORY; - expand_home_directory( input ); - } + if( ! input.empty() && input.at(0) == L'~' ) + { + input.at(0) = HOME_DIRECTORY; + expand_home_directory( input ); + } } /** @@ -1520,7 +1520,7 @@ static void remove_internal_separator( wcstring &str, bool conv ) { /* Remove all instances of INTERNAL_SEPARATOR */ str.erase(std::remove(str.begin(), str.end(), (wchar_t)INTERNAL_SEPARATOR), str.end()); - + /* If conv is true, replace all instances of ANY_CHAR with '?', ANY_STRING with '*', ANY_STRING_RECURSIVE with '*' */ if (conv) { @@ -1543,43 +1543,43 @@ static void remove_internal_separator( wcstring &str, bool conv ) int expand_string( const wcstring &input, std::vector &output, expand_flags_t flags ) { - parser_t parser(PARSER_TYPE_ERRORS_ONLY, true /* show errors */); - std::vector list1, list2; - std::vector *in, *out; - - size_t i; - int res = EXPAND_OK; - - if( (!(flags & ACCEPT_INCOMPLETE)) && expand_is_clean( input.c_str() ) ) - { - output.push_back(completion_t(input)); - return EXPAND_OK; - } - - if( EXPAND_SKIP_CMDSUBST & flags ) - { - wchar_t *begin, *end; - - if( parse_util_locate_cmdsubst( input.c_str(), + parser_t parser(PARSER_TYPE_ERRORS_ONLY, true /* show errors */); + std::vector list1, list2; + std::vector *in, *out; + + size_t i; + int res = EXPAND_OK; + + if( (!(flags & ACCEPT_INCOMPLETE)) && expand_is_clean( input.c_str() ) ) + { + output.push_back(completion_t(input)); + return EXPAND_OK; + } + + if( EXPAND_SKIP_CMDSUBST & flags ) + { + wchar_t *begin, *end; + + if( parse_util_locate_cmdsubst( input.c_str(), &begin, &end, 1 ) != 0 ) - { - parser.error( CMDSUBST_ERROR, -1, L"Command substitutions not allowed" ); - return EXPAND_ERROR; - } - list1.push_back(completion_t(input)); - } - else - { + { + parser.error( CMDSUBST_ERROR, -1, L"Command substitutions not allowed" ); + return EXPAND_ERROR; + } + list1.push_back(completion_t(input)); + } + else + { int cmdsubst_ok = expand_cmdsubst(parser, input, list1); if (! cmdsubst_ok) return EXPAND_ERROR; - } + } in = &list1; out = &list2; - + for( i=0; i < in->size(); i++ ) { /* @@ -1587,9 +1587,9 @@ int expand_string( const wcstring &input, std::vector &output, exp expand_string to expand incomplete strings from the commandline. */ - int unescape_flags = UNESCAPE_SPECIAL | UNESCAPE_INCOMPLETE; + int unescape_flags = UNESCAPE_SPECIAL | UNESCAPE_INCOMPLETE; wcstring next = expand_unescape_string( in->at(i).completion, unescape_flags ); - + if( EXPAND_SKIP_VARIABLES & flags ) { for (size_t i=0; i < next.size(); i++) { @@ -1607,33 +1607,33 @@ int expand_string( const wcstring &input, std::vector &output, exp } } } - + in->clear(); - + in = &list2; out = &list1; - + for( i=0; i < in->size(); i++ ) { wcstring next = in->at(i).completion; - + if( !expand_brackets( parser, next, flags, *out )) { return EXPAND_ERROR; } } in->clear(); - + in = &list1; out = &list2; - + for( i=0; i < in->size(); i++ ) { wcstring next = in->at(i).completion; - + expand_home_directory(next); - - + + if( flags & ACCEPT_INCOMPLETE ) { if( next[0] == PROCESS_EXPAND ) @@ -1660,26 +1660,26 @@ int expand_string( const wcstring &input, std::vector &output, exp } } } - + in->clear(); - + in = &list2; out = &list1; - + for( i=0; i < in->size(); i++ ) { wcstring next_str = in->at(i).completion; int wc_res; - - remove_internal_separator( next_str, (EXPAND_SKIP_WILDCARDS & flags) ? true : false ); + + remove_internal_separator( next_str, (EXPAND_SKIP_WILDCARDS & flags) ? true : false ); const wchar_t *next = next_str.c_str(); - + if( ((flags & ACCEPT_INCOMPLETE) && (!(flags & EXPAND_SKIP_WILDCARDS))) || wildcard_has( next, 1 ) ) { const wchar_t *start, *rest; std::vector *list = out; - + if( next[0] == '/' ) { start = L"/"; @@ -1690,17 +1690,17 @@ int expand_string( const wcstring &input, std::vector &output, exp start = L""; rest = next; } - + if( flags & ACCEPT_INCOMPLETE ) { list = &output; } - + wc_res = wildcard_expand_string(rest, start, flags, *list); - + if( !(flags & ACCEPT_INCOMPLETE) ) { - + switch( wc_res ) { case 0: @@ -1712,13 +1712,13 @@ int expand_string( const wcstring &input, std::vector &output, exp break; } } - + case 1: { size_t j; res = EXPAND_WILDCARD_MATCH; sort_completions( *out ); - + for( j=0; j< out->size(); j++ ) { output.push_back( out->at(j) ); @@ -1726,15 +1726,15 @@ int expand_string( const wcstring &input, std::vector &output, exp out->clear(); break; } - + case -1: { return EXPAND_ERROR; } - + } } - + } else { @@ -1746,26 +1746,26 @@ int expand_string( const wcstring &input, std::vector &output, exp output.push_back(completion_t(next)); } } - + } - - return res; + + return res; } bool expand_one(wcstring &string, expand_flags_t flags) { - std::vector completions; - bool result = false; - - if( (!(flags & ACCEPT_INCOMPLETE)) && expand_is_clean( string.c_str() ) ) - { + std::vector completions; + bool result = false; + + if( (!(flags & ACCEPT_INCOMPLETE)) && expand_is_clean( string.c_str() ) ) + { return true; - } - + } + if (expand_string(string, completions, flags)) { if (completions.size() == 1) { string = completions.at(0).completion; result = true; } } - return result; + return result; } diff --git a/expand.h b/expand.h index 1f8c08e81..799425751 100644 --- a/expand.h +++ b/expand.h @@ -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= L'0') && (*filter <= L'9')) - { - width=10*width+(*filter++ - L'0'); - } - } + const wchar_t *filter_org=filter; + int count=0; - while( loop ) - { - - switch(*filter) - { - case L'l': - /* Long variable */ - is_long++; - filter++; - break; + for( ;*filter; filter++) + { + if(*filter == L'%') + { + int is_long=0; + int width = -1; + filter++; + int loop=1; + int precision=-1; + int pad_left = 1; - case L'*': - /* Set minimum field width */ - width = va_arg( va, int ); - filter++; - break; + if( iswdigit( *filter ) ) + { + width=0; + while( (*filter >= L'0') && (*filter <= L'9')) + { + width=10*width+(*filter++ - L'0'); + } + } - case L'-': - filter++; - pad_left=0; - break; - - case L'.': - /* - Set precision. - */ - filter++; - if( *filter == L'*' ) - { - precision = va_arg( va, int ); - } - else - { - precision=0; - while( (*filter >= L'0') && (*filter <= L'9')) - { - precision=10*precision+(*filter++ - L'0'); - } - } - break; - - default: - loop=0; - break; - } - } + while( loop ) + { - switch( *filter ) - { - case L'c': - { - wchar_t c; + switch(*filter) + { + case L'l': + /* Long variable */ + is_long++; + filter++; + break; - if( (width >= 0) && pad_left ) - { - pad( writer, width-1 ); - count += maxi( width-1, 0 ); - } + case L'*': + /* Set minimum field width */ + width = va_arg( va, int ); + filter++; + break; - c = is_long?va_arg(va, wint_t):btowc(va_arg(va, int)); - if( precision != 0 ) - writer( c ); + case L'-': + filter++; + pad_left=0; + break; + + case L'.': + /* + Set precision. + */ + filter++; + if( *filter == L'*' ) + { + precision = va_arg( va, int ); + } + else + { + precision=0; + while( (*filter >= L'0') && (*filter <= L'9')) + { + precision=10*precision+(*filter++ - L'0'); + } + } + break; + + default: + loop=0; + break; + } + } + + switch( *filter ) + { + case L'c': + { + wchar_t c; + + if( (width >= 0) && pad_left ) + { + pad( writer, width-1 ); + count += maxi( width-1, 0 ); + } + + c = is_long?va_arg(va, wint_t):btowc(va_arg(va, int)); + if( precision != 0 ) + writer( c ); - if( (width >= 0) && !pad_left ) - { - pad( writer, width-1 ); - count += maxi( width-1, 0 ); - } - - count++; - - break; - } - case L's': - { - - wchar_t *ss=0; - if( is_long ) - { - ss = va_arg(va, wchar_t *); - } - else - { - char *ns = va_arg(va, char*); + if( (width >= 0) && !pad_left ) + { + pad( writer, width-1 ); + count += maxi( width-1, 0 ); + } - if( ns ) - { - ss = str2wcs( ns ); - } - } - - if( !ss ) - { - return -1; - } - - if( (width >= 0) && pad_left ) - { - pad( writer, width-wcslen(ss) ); - count += maxi(width-wcslen(ss), 0); - } - - wchar_t *s=ss; - int precount = count; - - while( *s ) - { - if( (precision > 0) && (precision <= (count-precount) ) ) - break; - - writer( *(s++) ); - count++; - } - - if( (width >= 0) && !pad_left ) - { - pad( writer, width-wcslen(ss) ); - count += maxi( width-wcslen(ss), 0 ); - } - - if( !is_long ) - free( ss ); - - break; - } + count++; - case L'd': - case L'i': - case L'o': - case L'u': - case L'x': - case L'X': - { - char str[33]; - char *pos; - char format[16]; - int len; - - format[0]=0; - strcat( format, "%"); - if( precision >= 0 ) - strcat( format, ".*" ); - switch( is_long ) - { - case 2: - strcat( format, "ll" ); - break; - case 1: - strcat( format, "l" ); - break; - } - - len = strlen(format); - format[len++]=(char)*filter; - format[len]=0; + break; + } + case L's': + { - switch( *filter ) - { - case L'd': - case L'i': - { - - switch( is_long ) - { - case 0: - { - int d = va_arg( va, int ); - if( precision >= 0 ) - snprintf( str, 32, format, precision, d ); - else - snprintf( str, 32, format, d ); - - break; - } - - case 1: - { - long d = va_arg( va, long ); - if( precision >= 0 ) - snprintf( str, 32, format, precision, d ); - else - snprintf( str, 32, format, d ); - break; - } - - case 2: - { - long long d = va_arg( va, long long ); - if( precision >= 0 ) - snprintf( str, 32, format, precision, d ); - else - snprintf( str, 32, format, d ); - break; - } - - default: - debug( 0, L"Invalid length modifier in string %ls\n", filter_org ); - return -1; - } - break; - - } - - case L'u': - case L'o': - case L'x': - case L'X': - { - - switch( is_long ) - { - case 0: - { - unsigned d = va_arg( va, unsigned ); - if( precision >= 0 ) - snprintf( str, 32, format, precision, d ); - else - snprintf( str, 32, format, d ); - break; - } - - case 1: - { - unsigned long d = va_arg( va, unsigned long ); - if( precision >= 0 ) - snprintf( str, 32, format, precision, d ); - else - snprintf( str, 32, format, d ); - break; - } - - case 2: - { - unsigned long long d = va_arg( va, unsigned long long ); - if( precision >= 0 ) - snprintf( str, 32, format, precision, d ); - else - snprintf( str, 32, format, d ); - break; - } - - default: - debug( 0, L"Invalid length modifier in string %ls\n", filter_org ); - return -1; - } - break; - - } - - default: - debug( 0, L"Invalid filter %ls in string %ls\n", *filter, filter_org ); - return -1; - - } - - if( (width >= 0) && pad_left ) - { - int l = maxi(width-strlen(str), 0 ); - pad( writer, l ); - count += l; - } - - pos = str; - - while( *pos ) - { - writer( *(pos++) ); - count++; - } - - if( (width >= 0) && !pad_left ) - { - int l = maxi(width-strlen(str), 0 ); - pad( writer, l ); - count += l; - } - - break; - } + wchar_t *ss=0; + if( is_long ) + { + ss = va_arg(va, wchar_t *); + } + else + { + char *ns = va_arg(va, char*); - case L'f': - { - char str[32]; - char *pos; - double val = va_arg( va, double ); - - if( precision>= 0 ) - { - if( width>= 0 ) - { - snprintf( str, 32, "%*.*f", width, precision, val ); - } - else - { - snprintf( str, 32, "%.*f", precision, val ); - } - } - else - { - if( width>= 0 ) - { - snprintf( str, 32, "%*f", width, val ); - } - else - { - snprintf( str, 32, "%f", val ); - } - } + if( ns ) + { + ss = str2wcs( ns ); + } + } - pos = str; - - while( *pos ) - { - writer( *(pos++) ); - count++; - } - - break; - } + if( !ss ) + { + return -1; + } - case L'n': - { - int *n = va_arg( va, int *); - - *n = count; - break; - } - case L'%': - { - writer('%'); - count++; - break; - } - default: - debug( 0, L"Unknown switch %lc in string %ls\n", *filter, filter_org ); - return -1; - } - } - else - { - writer( *filter ); - count++; - } - } + if( (width >= 0) && pad_left ) + { + pad( writer, width-wcslen(ss) ); + count += maxi(width-wcslen(ss), 0); + } - return count; + wchar_t *s=ss; + int precount = count; + + while( *s ) + { + if( (precision > 0) && (precision <= (count-precount) ) ) + break; + + writer( *(s++) ); + count++; + } + + if( (width >= 0) && !pad_left ) + { + pad( writer, width-wcslen(ss) ); + count += maxi( width-wcslen(ss), 0 ); + } + + if( !is_long ) + free( ss ); + + break; + } + + case L'd': + case L'i': + case L'o': + case L'u': + case L'x': + case L'X': + { + char str[33]; + char *pos; + char format[16]; + int len; + + format[0]=0; + strcat( format, "%"); + if( precision >= 0 ) + strcat( format, ".*" ); + switch( is_long ) + { + case 2: + strcat( format, "ll" ); + break; + case 1: + strcat( format, "l" ); + break; + } + + len = strlen(format); + format[len++]=(char)*filter; + format[len]=0; + + switch( *filter ) + { + case L'd': + case L'i': + { + + switch( is_long ) + { + case 0: + { + int d = va_arg( va, int ); + if( precision >= 0 ) + snprintf( str, 32, format, precision, d ); + else + snprintf( str, 32, format, d ); + + break; + } + + case 1: + { + long d = va_arg( va, long ); + if( precision >= 0 ) + snprintf( str, 32, format, precision, d ); + else + snprintf( str, 32, format, d ); + break; + } + + case 2: + { + long long d = va_arg( va, long long ); + if( precision >= 0 ) + snprintf( str, 32, format, precision, d ); + else + snprintf( str, 32, format, d ); + break; + } + + default: + debug( 0, L"Invalid length modifier in string %ls\n", filter_org ); + return -1; + } + break; + + } + + case L'u': + case L'o': + case L'x': + case L'X': + { + + switch( is_long ) + { + case 0: + { + unsigned d = va_arg( va, unsigned ); + if( precision >= 0 ) + snprintf( str, 32, format, precision, d ); + else + snprintf( str, 32, format, d ); + break; + } + + case 1: + { + unsigned long d = va_arg( va, unsigned long ); + if( precision >= 0 ) + snprintf( str, 32, format, precision, d ); + else + snprintf( str, 32, format, d ); + break; + } + + case 2: + { + unsigned long long d = va_arg( va, unsigned long long ); + if( precision >= 0 ) + snprintf( str, 32, format, precision, d ); + else + snprintf( str, 32, format, d ); + break; + } + + default: + debug( 0, L"Invalid length modifier in string %ls\n", filter_org ); + return -1; + } + break; + + } + + default: + debug( 0, L"Invalid filter %ls in string %ls\n", *filter, filter_org ); + return -1; + + } + + if( (width >= 0) && pad_left ) + { + int l = maxi(width-strlen(str), 0 ); + pad( writer, l ); + count += l; + } + + pos = str; + + while( *pos ) + { + writer( *(pos++) ); + count++; + } + + if( (width >= 0) && !pad_left ) + { + int l = maxi(width-strlen(str), 0 ); + pad( writer, l ); + count += l; + } + + break; + } + + case L'f': + { + char str[32]; + char *pos; + double val = va_arg( va, double ); + + if( precision>= 0 ) + { + if( width>= 0 ) + { + snprintf( str, 32, "%*.*f", width, precision, val ); + } + else + { + snprintf( str, 32, "%.*f", precision, val ); + } + } + else + { + if( width>= 0 ) + { + snprintf( str, 32, "%*f", width, val ); + } + else + { + snprintf( str, 32, "%f", val ); + } + } + + pos = str; + + while( *pos ) + { + writer( *(pos++) ); + count++; + } + + break; + } + + case L'n': + { + int *n = va_arg( va, int *); + + *n = count; + break; + } + case L'%': + { + writer('%'); + count++; + break; + } + default: + debug( 0, L"Unknown switch %lc in string %ls\n", *filter, filter_org ); + return -1; + } + } + else + { + writer( *filter ); + count++; + } + } + + return count; } /** @@ -552,9 +552,9 @@ static int vgwprintf( void (*writer)(wchar_t), */ static struct { - int count; - int max; - wchar_t *pos; + int count; + int max; + wchar_t *pos; } sw_data; @@ -563,42 +563,42 @@ sw_data; */ static void sw_writer( wchar_t c ) { - if( sw_data.count < sw_data.max ) - *(sw_data.pos++)=c; - sw_data.count++; + if( sw_data.count < sw_data.max ) + *(sw_data.pos++)=c; + sw_data.count++; } int vswprintf( wchar_t *out, size_t n, const wchar_t *filter, va_list va ) { - int written; - - sw_data.pos=out; - sw_data.max=n; - sw_data.count=0; - written=vgwprintf( &sw_writer, - filter, - va ); - if( written < n ) - { - *sw_data.pos = 0; - } - else - { - written=-1; - } - - return written; + int written; + + sw_data.pos=out; + sw_data.max=n; + sw_data.count=0; + written=vgwprintf( &sw_writer, + filter, + va ); + if( written < n ) + { + *sw_data.pos = 0; + } + else + { + written=-1; + } + + return written; } int swprintf( wchar_t *out, size_t n, const wchar_t *filter, ... ) { - va_list va; - int written; - - va_start( va, filter ); - written = vswprintf( out, n, filter, va ); - va_end( va ); - return written; + va_list va; + int written; + + va_start( va, filter ); + written = vswprintf( out, n, filter, va ); + va_end( va ); + return written; } /** @@ -608,7 +608,7 @@ static FILE *fw_data; static void fw_writer( wchar_t c ) { - putwc( c, fw_data ); + putwc( c, fw_data ); } /* @@ -616,35 +616,35 @@ static void fw_writer( wchar_t c ) */ int vfwprintf( FILE *f, const wchar_t *filter, va_list va ) { - fw_data = f; - return vgwprintf( &fw_writer, filter, va ); + fw_data = f; + return vgwprintf( &fw_writer, filter, va ); } int fwprintf( FILE *f, const wchar_t *filter, ... ) { - va_list va; - int written; - - va_start( va, filter ); - written = vfwprintf( f, filter, va ); - va_end( va ); - return written; + va_list va; + int written; + + va_start( va, filter ); + written = vfwprintf( f, filter, va ); + va_end( va ); + return written; } int vwprintf( const wchar_t *filter, va_list va ) { - return vfwprintf( stdout, filter, va ); + return vfwprintf( stdout, filter, va ); } int wprintf( const wchar_t *filter, ... ) { - va_list va; - int written; - - va_start( va, filter ); - written=vwprintf( filter, va ); - va_end( va ); - return written; + va_list va; + int written; + + va_start( va, filter ); + written=vwprintf( filter, va ); + va_end( va ); + return written; } #endif @@ -653,45 +653,45 @@ int wprintf( const wchar_t *filter, ... ) wint_t fgetwc(FILE *stream) { - wchar_t res=0; - mbstate_t state; - memset (&state, '\0', sizeof (state)); + wchar_t res=0; + mbstate_t state; + memset (&state, '\0', sizeof (state)); - while(1) - { - int b = fgetc( stream ); - char bb; - - int sz; - - if( b == EOF ) - return WEOF; + while(1) + { + int b = fgetc( stream ); + char bb; - bb=b; - - sz = mbrtowc( &res, &bb, 1, &state ); - - switch( sz ) - { - case -1: - memset (&state, '\0', sizeof (state)); - return WEOF; + int sz; - case -2: - break; - case 0: - return 0; - default: - return res; - } - } + if( b == EOF ) + return WEOF; + + bb=b; + + sz = mbrtowc( &res, &bb, 1, &state ); + + switch( sz ) + { + case -1: + memset (&state, '\0', sizeof (state)); + return WEOF; + + case -2: + break; + case 0: + return 0; + default: + return res; + } + } } wint_t getwc(FILE *stream) { - return fgetwc( stream ); + return fgetwc( stream ); } @@ -701,17 +701,17 @@ wint_t getwc(FILE *stream) wint_t fputwc(wchar_t wc, FILE *stream) { - int res; - char s[MB_CUR_MAX+1]; - memset( s, 0, MB_CUR_MAX+1 ); - wctomb( s, wc ); - res = fputs( s, stream ); - return res==EOF?WEOF:wc; + int res; + char s[MB_CUR_MAX+1]; + memset( s, 0, MB_CUR_MAX+1 ); + wctomb( s, wc ); + res = fputs( s, stream ); + return res==EOF?WEOF:wc; } wint_t putwc(wchar_t wc, FILE *stream) { - return fputwc( wc, stream ); + return fputwc( wc, stream ); } #endif @@ -722,24 +722,24 @@ wint_t putwc(wchar_t wc, FILE *stream) Used by fallback wcstok. Borrowed from glibc */ static size_t fish_wcsspn (const wchar_t *wcs, - const wchar_t *accept ) + const wchar_t *accept ) { - register const wchar_t *p; - register const wchar_t *a; - register size_t count = 0; + register const wchar_t *p; + register const wchar_t *a; + register size_t count = 0; - for (p = wcs; *p != L'\0'; ++p) + for (p = wcs; *p != L'\0'; ++p) { - for (a = accept; *a != L'\0'; ++a) - if (*p == *a) - break; - - if (*a == L'\0') - return count; - else - ++count; + for (a = accept; *a != L'\0'; ++a) + if (*p == *a) + break; + + if (*a == L'\0') + return count; + else + ++count; } - return count; + return count; } /* @@ -747,12 +747,12 @@ static size_t fish_wcsspn (const wchar_t *wcs, */ static wchar_t *fish_wcspbrk (const wchar_t *wcs, const wchar_t *accept) { - while (*wcs != L'\0') - if (wcschr (accept, *wcs) == NULL) - ++wcs; - else - return (wchar_t *) wcs; - return NULL; + while (*wcs != L'\0') + if (wcschr (accept, *wcs) == NULL) + ++wcs; + else + return (wchar_t *) wcs; + return NULL; } /* @@ -760,45 +760,45 @@ static wchar_t *fish_wcspbrk (const wchar_t *wcs, const wchar_t *accept) */ wchar_t *wcstok(wchar_t *wcs, const wchar_t *delim, wchar_t **save_ptr) { - wchar_t *result; + wchar_t *result; - if (wcs == NULL) + if (wcs == NULL) { - if (*save_ptr == NULL) + if (*save_ptr == NULL) { - errno = EINVAL; - return NULL; + errno = EINVAL; + return NULL; } - else - wcs = *save_ptr; + else + wcs = *save_ptr; } - /* Scan leading delimiters. */ - wcs += fish_wcsspn (wcs, delim); - - if (*wcs == L'\0') + /* Scan leading delimiters. */ + wcs += fish_wcsspn (wcs, delim); + + if (*wcs == L'\0') { - *save_ptr = NULL; - return NULL; + *save_ptr = NULL; + return NULL; } - /* Find the end of the token. */ - result = wcs; - - wcs = fish_wcspbrk (result, delim); - - if (wcs == NULL) - { - /* This token finishes the string. */ - *save_ptr = NULL; - } - else + /* Find the end of the token. */ + result = wcs; + + wcs = fish_wcspbrk (result, delim); + + if (wcs == NULL) + { + /* This token finishes the string. */ + *save_ptr = NULL; + } + else { - /* Terminate the token and make *SAVE_PTR point past it. */ - *wcs = L'\0'; - *save_ptr = wcs + 1; + /* Terminate the token and make *SAVE_PTR point past it. */ + *wcs = L'\0'; + *save_ptr = wcs + 1; } - return result; + return result; } #endif @@ -807,32 +807,32 @@ wchar_t *wcstok(wchar_t *wcs, const wchar_t *delim, wchar_t **save_ptr) */ static wchar_t *wcsdup_fallback(const wchar_t *in) { - size_t len=wcslen(in); - wchar_t *out = (wchar_t *)malloc( sizeof( wchar_t)*(len+1)); - if( out == 0 ) - { - return 0; - } + size_t len=wcslen(in); + wchar_t *out = (wchar_t *)malloc( sizeof( wchar_t)*(len+1)); + if( out == 0 ) + { + return 0; + } - memcpy( out, in, sizeof( wchar_t)*(len+1)); - return out; + memcpy( out, in, sizeof( wchar_t)*(len+1)); + return out; } int wcscasecmp_fallback( const wchar_t *a, const wchar_t *b ) { - if( *a == 0 ) - { - return (*b==0)?0:-1; - } - else if( *b == 0 ) - { - return 1; - } - int diff = towlower(*a)-towlower(*b); - if( diff != 0 ) - return diff; - else - return wcscasecmp_fallback( a+1,b+1); + if( *a == 0 ) + { + return (*b==0)?0:-1; + } + else if( *b == 0 ) + { + return 1; + } + int diff = towlower(*a)-towlower(*b); + if( diff != 0 ) + return diff; + else + return wcscasecmp_fallback( a+1,b+1); } @@ -851,14 +851,14 @@ int wcscasecmp_use_weak(const wchar_t *a, const wchar_t *b) return (wcscasecmp)(a, b); return wcscasecmp_fallback(a, b); } - + #else //__APPLE__ #ifndef HAVE_WCSDUP wchar_t *wcsdup( const wchar_t *in ) { - return wcsdup_fallback(in); - + return wcsdup_fallback(in); + } #endif @@ -866,7 +866,7 @@ wchar_t *wcsdup( const wchar_t *in ) #ifndef HAVE_WCSCASECMP int wcscasecmp( const wchar_t *a, const wchar_t *b ) { - return wcscasecmp_fallback(a, b); + return wcscasecmp_fallback(a, b); } #endif #endif //__APPLE__ @@ -874,122 +874,122 @@ int wcscasecmp( const wchar_t *a, const wchar_t *b ) #ifndef HAVE_WCSLEN size_t wcslen(const wchar_t *in) { - const wchar_t *end=in; - while( *end ) - end++; - return end-in; + const wchar_t *end=in; + while( *end ) + end++; + return end-in; } #endif #ifndef HAVE_WCSNCASECMP int wcsncasecmp( const wchar_t *a, const wchar_t *b, int count ) { - if( count == 0 ) - return 0; - - if( *a == 0 ) - { - return (*b==0)?0:-1; - } - else if( *b == 0 ) - { - return 1; - } - int diff = towlower(*a)-towlower(*b); - if( diff != 0 ) - return diff; - else - return wcsncasecmp( a+1,b+1, count-1); + if( count == 0 ) + return 0; + + if( *a == 0 ) + { + return (*b==0)?0:-1; + } + else if( *b == 0 ) + { + return 1; + } + int diff = towlower(*a)-towlower(*b); + if( diff != 0 ) + return diff; + else + return wcsncasecmp( a+1,b+1, count-1); } #endif #ifndef HAVE_WCWIDTH int wcwidth( wchar_t c ) { - if( c < 32 ) - return 0; - if ( c == 127 ) - return 0; - return 1; + if( c < 32 ) + return 0; + if ( c == 127 ) + return 0; + return 1; } #endif #ifndef HAVE_WCSNDUP wchar_t *wcsndup( const wchar_t *in, size_t c ) { - wchar_t *res = (wchar_t *)malloc( sizeof(wchar_t)*(c+1) ); - if( res == 0 ) - { - return 0; - } - wcslcpy( res, in, c+1 ); - return res; + wchar_t *res = (wchar_t *)malloc( sizeof(wchar_t)*(c+1) ); + if( res == 0 ) + { + return 0; + } + wcslcpy( res, in, c+1 ); + return res; } #endif long convert_digit( wchar_t d, int base ) { - long res=-1; - if( (d <= L'9') && (d >= L'0') ) - { - res = d - L'0'; - } - else if( (d <= L'z') && (d >= L'a') ) - { - res = d + 10 - L'a'; - } - else if( (d <= L'Z') && (d >= L'A') ) - { - res = d + 10 - L'A'; - } - if( res >= base ) - { - res = -1; - } - - return res; + long res=-1; + if( (d <= L'9') && (d >= L'0') ) + { + res = d - L'0'; + } + else if( (d <= L'z') && (d >= L'a') ) + { + res = d + 10 - L'a'; + } + else if( (d <= L'Z') && (d >= L'A') ) + { + res = d + 10 - L'A'; + } + if( res >= base ) + { + res = -1; + } + + return res; } #ifndef HAVE_WCSTOL -long wcstol(const wchar_t *nptr, - wchar_t **endptr, - int base) +long wcstol(const wchar_t *nptr, + wchar_t **endptr, + int base) { - long long res=0; - int is_set=0; - if( base > 36 ) - { - errno = EINVAL; - return 0; - } + long long res=0; + int is_set=0; + if( base > 36 ) + { + errno = EINVAL; + return 0; + } - while( 1 ) - { - long nxt = convert_digit( *nptr, base ); - if( endptr != 0 ) - *endptr = (wchar_t *)nptr; - if( nxt < 0 ) - { - if( !is_set ) - { - errno = EINVAL; - } - return res; - } - res = (res*base)+nxt; - is_set = 1; - if( res > LONG_MAX ) - { - errno = ERANGE; - return LONG_MAX; - } - if( res < LONG_MIN ) - { - errno = ERANGE; - return LONG_MIN; - } - nptr++; - } + while( 1 ) + { + long nxt = convert_digit( *nptr, base ); + if( endptr != 0 ) + *endptr = (wchar_t *)nptr; + if( nxt < 0 ) + { + if( !is_set ) + { + errno = EINVAL; + } + return res; + } + res = (res*base)+nxt; + is_set = 1; + if( res > LONG_MAX ) + { + errno = ERANGE; + return LONG_MAX; + } + if( res < LONG_MIN ) + { + errno = ERANGE; + return LONG_MIN; + } + nptr++; + } } #endif @@ -1016,35 +1016,35 @@ long wcstol(const wchar_t *nptr, size_t wcslcat(wchar_t *dst, const wchar_t *src, size_t siz) { - - register wchar_t *d = dst; - register const wchar_t *s = src; - register size_t n = siz; - size_t dlen; - /* Find the end of dst and adjust bytes left but don't go past end */ - while (n-- != 0 && *d != '\0') - d++; - - dlen = d - dst; - n = siz - dlen; + register wchar_t *d = dst; + register const wchar_t *s = src; + register size_t n = siz; + size_t dlen; - if (n == 0) - return(dlen + wcslen(s)); + /* Find the end of dst and adjust bytes left but don't go past end */ + while (n-- != 0 && *d != '\0') + d++; - while (*s != '\0') - { - if (n != 1) - { - *d++ = *s; - n--; - } - s++; - } - *d = '\0'; + dlen = d - dst; + n = siz - dlen; - return(dlen + (s - src)); - /* count does not include NUL */ + if (n == 0) + return(dlen + wcslen(s)); + + while (*s != '\0') + { + if (n != 1) + { + *d++ = *s; + n--; + } + s++; + } + *d = '\0'; + + return(dlen + (s - src)); + /* count does not include NUL */ } #endif @@ -1071,32 +1071,32 @@ wcslcat(wchar_t *dst, const wchar_t *src, size_t siz) size_t wcslcpy(wchar_t *dst, const wchar_t *src, size_t siz) { - register wchar_t *d = dst; - register const wchar_t *s = src; - register size_t n = siz; - - /* Copy as many bytes as will fit */ - if (n != 0 && --n != 0) - { - do - { - if ((*d++ = *s++) == 0) - break; - } - while (--n != 0); - } + register wchar_t *d = dst; + register const wchar_t *s = src; + register size_t n = siz; - /* Not enough room in dst, add NUL and traverse rest of src */ - if (n == 0) - { - if (siz != 0) - *d = '\0'; - /* NUL-terminate dst */ - while (*s++) - ; - } - return(s - src - 1); - /* count does not include NUL */ + /* Copy as many bytes as will fit */ + if (n != 0 && --n != 0) + { + do + { + if ((*d++ = *s++) == 0) + break; + } + while (--n != 0); + } + + /* Not enough room in dst, add NUL and traverse rest of src */ + if (n == 0) + { + if (siz != 0) + *d = '\0'; + /* NUL-terminate dst */ + while (*s++) + ; + } + return(s - src - 1); + /* count does not include NUL */ } #endif @@ -1105,14 +1105,14 @@ wcslcpy(wchar_t *dst, const wchar_t *src, size_t siz) int lrand48_r(struct drand48_data *buffer, long int *result) { - *result = rand_r( &buffer->seed ); - return 0; + *result = rand_r( &buffer->seed ); + return 0; } int srand48_r(long int seedval, struct drand48_data *buffer) { - buffer->seed = (unsigned int)seedval; - return 0; + buffer->seed = (unsigned int)seedval; + return 0; } #endif @@ -1121,8 +1121,8 @@ int srand48_r(long int seedval, struct drand48_data *buffer) int futimes(int fd, const struct timeval *times) { - errno = ENOSYS; - return -1; + errno = ENOSYS; + return -1; } #endif @@ -1131,17 +1131,17 @@ int futimes(int fd, const struct timeval *times) char * gettext (const char * msgid) { - return (char *)msgid; + return (char *)msgid; } char * bindtextdomain (const char * domainname, const char * dirname) { - return 0; + return 0; } char * textdomain (const char * domainname) { - return 0; + return 0; } #endif @@ -1149,10 +1149,10 @@ char * textdomain (const char * domainname) #ifndef HAVE_DCGETTEXT char * dcgettext ( const char * domainname, - const char * msgid, - int category) + const char * msgid, + int category) { - return (char *)msgid; + return (char *)msgid; } @@ -1167,20 +1167,20 @@ int _nl_msg_cat_cntr=0; #ifndef HAVE_KILLPG int killpg( int pgr, int sig ) { - assert( pgr > 1 ); - return kill( -pgr, sig ); + assert( pgr > 1 ); + return kill( -pgr, sig ); } #endif #ifndef HAVE_WORKING_GETOPT_LONG -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 ) { - return getopt( argc, argv, optstring ); + return getopt( argc, argv, optstring ); } @@ -1189,14 +1189,14 @@ int getopt_long( int argc, #ifndef HAVE_BACKTRACE int backtrace (void **buffer, int size) { - return 0; + return 0; } #endif #ifndef HAVE_BACKTRACE_SYMBOLS char ** backtrace_symbols (void *const *buffer, int size) { - return 0; + return 0; } #endif @@ -1204,22 +1204,22 @@ char ** backtrace_symbols (void *const *buffer, int size) long sysconf(int name) { - if( name == _SC_ARG_MAX ) - { + if( name == _SC_ARG_MAX ) + { #ifdef ARG_MAX - return ARG_MAX; + return ARG_MAX; #endif - } + } + + return -1; - return -1; - } #endif #ifndef HAVE_NAN double nan(char *tagp) { - return 0.0/0.0; + return 0.0/0.0; } #endif @@ -1443,12 +1443,12 @@ static int mk_wcwidth(wchar_t ucs) /* binary search in table of non-spacing characters */ if (bisearch(ucs, combining, - sizeof(combining) / sizeof(struct interval) - 1)) + sizeof(combining) / sizeof(struct interval) - 1)) return 0; /* if we arrive here, ucs is not a combining or C0/C1 control character */ - return 1 + + return 1 + (ucs >= 0x1100 && (ucs <= 0x115f || /* Hangul Jamo init. consonants */ ucs == 0x2329 || ucs == 0x232a || diff --git a/fallback.h b/fallback.h index 2c091be01..3b852b30f 100644 --- a/fallback.h +++ b/fallback.h @@ -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 diff --git a/fish.cpp b/fish.cpp index d1699d584..78723bd08 100644 --- a/fish.cpp +++ b/fish.cpp @@ -17,7 +17,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. /** \file fish.c - The main loop of fish. + The main loop of fish. */ #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(); } diff --git a/fish_indent.cpp b/fish_indent.cpp index 298d52e81..c04e6ded0 100644 --- a/fish_indent.cpp +++ b/fish_indent.cpp @@ -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; } diff --git a/fish_pager.cpp b/fish_pager.cpp index 4fc0a44cc..88ef8d3b3 100644 --- a/fish_pager.cpp +++ b/fish_pager.cpp @@ -62,42 +62,42 @@ #include "env_universal.h" #include "print_help.h" -enum +enum { - LINE_UP = R_NULL+1, - LINE_DOWN, - PAGE_UP, - PAGE_DOWN + LINE_UP = R_NULL+1, + LINE_DOWN, + PAGE_UP, + PAGE_DOWN } - ; + ; enum { - HIGHLIGHT_PAGER_PREFIX, - HIGHLIGHT_PAGER_COMPLETION, - HIGHLIGHT_PAGER_DESCRIPTION, - HIGHLIGHT_PAGER_PROGRESS, - HIGHLIGHT_PAGER_SECONDARY + HIGHLIGHT_PAGER_PREFIX, + HIGHLIGHT_PAGER_COMPLETION, + HIGHLIGHT_PAGER_DESCRIPTION, + HIGHLIGHT_PAGER_PROGRESS, + HIGHLIGHT_PAGER_SECONDARY } - ; + ; enum { - /* - Returnd by the pager if no more displaying is needed - */ - PAGER_DONE, - /* - Returned by the pager if the completions would not fit in the specified number of columns - */ - PAGER_RETRY, - /* - Returned by the pager if the terminal changes size - */ - PAGER_RESIZE + /* + Returnd by the pager if no more displaying is needed + */ + PAGER_DONE, + /* + Returned by the pager if the completions would not fit in the specified number of columns + */ + PAGER_RETRY, + /* + Returned by the pager if the terminal changes size + */ + PAGER_RESIZE } - ; + ; /** The minimum width (in characters) the terminal may have for fish_pager to not refuse showing the completions @@ -148,15 +148,15 @@ static std::vector pager_buffer; The environment variables used to specify the color of different tokens. */ -static const wchar_t *hightlight_var[] = +static const wchar_t *hightlight_var[] = { - L"fish_pager_color_prefix", - L"fish_pager_color_completion", - L"fish_pager_color_description", - L"fish_pager_color_progress", - L"fish_pager_color_secondary" + L"fish_pager_color_prefix", + L"fish_pager_color_completion", + L"fish_pager_color_description", + L"fish_pager_color_progress", + L"fish_pager_color_secondary" } - ; + ; /** This string contains the text that should be sent back to the calling program @@ -172,30 +172,30 @@ static FILE *out_file; */ struct comp_t { - /** - The list of all completin strings this entry applies to - */ - wcstring_list_t comp; - /** - The description - */ - wcstring desc; - /** - On-screen width of the completion string - */ - int comp_width; - /** - On-screen width of the description information - */ - int desc_width; - /** - Preffered total width - */ - int pref_width; - /** - Minimum acceptable width - */ - int min_width; + /** + The list of all completin strings this entry applies to + */ + wcstring_list_t comp; + /** + The description + */ + wcstring desc; + /** + On-screen width of the completion string + */ + int comp_width; + /** + On-screen width of the description information + */ + int desc_width; + /** + Preffered total width + */ + int pref_width; + /** + Minimum acceptable width + */ + int min_width; }; /** @@ -204,26 +204,26 @@ struct comp_t */ static rgb_color_t get_color( int highlight ) { - const wchar_t *val; + const wchar_t *val; - if( highlight < 0 ) - return rgb_color_t::normal(); - if( highlight >= (5) ) - return rgb_color_t::normal(); - - val = wgetenv( hightlight_var[highlight]); + if( highlight < 0 ) + return rgb_color_t::normal(); + if( highlight >= (5) ) + return rgb_color_t::normal(); - if( !val ) - { - val = env_universal_get( hightlight_var[highlight]); - } - - if( !val ) - { - return rgb_color_t::normal(); - } - - return parse_color( val, false ); + val = wgetenv( hightlight_var[highlight]); + + if( !val ) + { + val = env_universal_get( hightlight_var[highlight]); + } + + if( !val ) + { + return rgb_color_t::normal(); + } + + return parse_color( val, false ); } /** @@ -234,14 +234,14 @@ static rgb_color_t get_color( int highlight ) */ static void recalc_width( std::vector &lst, const wchar_t *prefix ) { - for( size_t i=0; imin_width = mini( c->desc_width, maxi(0,termsize.ws_col/3 - 2)) + - mini( c->desc_width, maxi(0,termsize.ws_col/5 - 4)) +4; - } - + for( size_t i=0; imin_width = mini( c->desc_width, maxi(0,termsize.ws_col/3 - 2)) + + mini( c->desc_width, maxi(0,termsize.ws_col/5 - 4)) +4; + } + } /** @@ -250,25 +250,25 @@ static void recalc_width( std::vector &lst, const wchar_t *prefix ) */ static int try_sequence( const char *seq ) { - int j, k; - wint_t c=0; - - for( j=0; - seq[j] != '\0' && seq[j] == (c=input_common_readch( j>0 )); - j++ ) - ; + int j, k; + wint_t c=0; - if( seq[j] == '\0' ) - { - return 1; - } - else - { - input_common_unreadch(c); - for(k=j-1; k>=0; k--) - input_common_unreadch(seq[k]); - } - return 0; + for( j=0; + seq[j] != '\0' && seq[j] == (c=input_common_readch( j>0 )); + j++ ) + ; + + if( seq[j] == '\0' ) + { + return 1; + } + else + { + input_common_unreadch(c); + for(k=j-1; k>=0; k--) + input_common_unreadch(seq[k]); + } + return 0; } /** @@ -276,66 +276,66 @@ static int try_sequence( const char *seq ) */ static wint_t readch() { - struct mapping - { - const char *seq; - wint_t bnd; - } - ; - - struct mapping m[]= - { - { - "\x1b[A", LINE_UP - } - , - { - key_up, LINE_UP - } - , - { - "\x1b[B", LINE_DOWN - } - , - { - key_down, LINE_DOWN - } - , - { - key_ppage, PAGE_UP - } - , - { - key_npage, PAGE_DOWN - } - , - { - " ", PAGE_DOWN - } - , - { - "\t", PAGE_DOWN - } - , - { - 0, 0 - } - - } - ; - int i; - - for( i=0; m[i].bnd; i++ ) - { - if( !m[i].seq ) - { - continue; - } - - if( try_sequence(m[i].seq ) ) - return m[i].bnd; - } - return input_common_readch(0); + struct mapping + { + const char *seq; + wint_t bnd; + } + ; + + struct mapping m[]= + { + { + "\x1b[A", LINE_UP + } + , + { + key_up, LINE_UP + } + , + { + "\x1b[B", LINE_DOWN + } + , + { + key_down, LINE_DOWN + } + , + { + key_ppage, PAGE_UP + } + , + { + key_npage, PAGE_DOWN + } + , + { + " ", PAGE_DOWN + } + , + { + "\t", PAGE_DOWN + } + , + { + 0, 0 + } + + } + ; + int i; + + for( i=0; m[i].bnd; i++ ) + { + if( !m[i].seq ) + { + continue; + } + + if( try_sequence(m[i].seq ) ) + return m[i].bnd; + } + return input_common_readch(0); } /** @@ -343,8 +343,8 @@ static wint_t readch() */ static int pager_buffered_writer( char c) { - pager_buffer.push_back(c); - return 0; + pager_buffer.push_back(c); + return 0; } /** @@ -368,24 +368,24 @@ static void pager_flush() */ static int print_max( const wchar_t *str, int max, int has_more ) { - int i; - int written = 0; - for( i=0; str[i]; i++ ) - { - - if( written + wcwidth(str[i]) > max ) - break; - if( ( written + wcwidth(str[i]) == max) && (has_more || str[i+1]) ) - { - writech( ellipsis_char ); - written += wcwidth(ellipsis_char ); - break; - } - - writech( str[i] ); - written+= wcwidth( str[i] ); - } - return written; + int i; + int written = 0; + for( i=0; str[i]; i++ ) + { + + if( written + wcwidth(str[i]) > max ) + break; + if( ( written + wcwidth(str[i]) == max) && (has_more || str[i+1]) ) + { + writech( ellipsis_char ); + written += wcwidth(ellipsis_char ); + break; + } + + writech( str[i] ); + written+= wcwidth( str[i] ); + } + return written; } /** @@ -393,72 +393,72 @@ static int print_max( const wchar_t *str, int max, int has_more ) */ static void completion_print_item( const wchar_t *prefix, comp_t *c, int width, bool secondary ) { - int comp_width=0, desc_width=0; - int written=0; - - if( c->pref_width <= width ) - { - /* - The entry fits, we give it as much space as it wants - */ - comp_width = c->comp_width; - desc_width = c->desc_width; - } - else - { - /* - The completion and description won't fit on the - allocated space. Give a maximum of 2/3 of the - space to the completion, and whatever is left to - the description. - */ - int desc_all = c->desc_width?c->desc_width+4:0; - - comp_width = maxi( mini( c->comp_width, - 2*(width-4)/3 ), - width - desc_all ); - if( c->desc_width ) - desc_width = width-comp_width-4; - else - c->desc_width=0; - - } - + int comp_width=0, desc_width=0; + int written=0; + + if( c->pref_width <= width ) + { + /* + The entry fits, we give it as much space as it wants + */ + comp_width = c->comp_width; + desc_width = c->desc_width; + } + else + { + /* + The completion and description won't fit on the + allocated space. Give a maximum of 2/3 of the + space to the completion, and whatever is left to + the description. + */ + int desc_all = c->desc_width?c->desc_width+4:0; + + comp_width = maxi( mini( c->comp_width, + 2*(width-4)/3 ), + width - desc_all ); + if( c->desc_width ) + desc_width = width-comp_width-4; + else + c->desc_width=0; + + } + rgb_color_t bg = secondary ? get_color(HIGHLIGHT_PAGER_SECONDARY) : rgb_color_t::normal(); - for( size_t i=0; icomp.size(); i++ ) - { + for( size_t i=0; icomp.size(); i++ ) + { const wcstring &comp = c->comp.at(i); - if( i != 0 ) - written += print_max( L" ", comp_width - written, 2 ); - set_color( get_color(HIGHLIGHT_PAGER_PREFIX), bg ); - written += print_max( prefix, comp_width - written, comp.empty()?0:1 ); - set_color( get_color(HIGHLIGHT_PAGER_COMPLETION), bg); - written += print_max( comp.c_str(), comp_width - written, i!=(c->comp.size()-1) ); - } + if( i != 0 ) + written += print_max( L" ", comp_width - written, 2 ); + set_color( get_color(HIGHLIGHT_PAGER_PREFIX), bg ); + written += print_max( prefix, comp_width - written, comp.empty()?0:1 ); + set_color( get_color(HIGHLIGHT_PAGER_COMPLETION), bg); + written += print_max( comp.c_str(), comp_width - written, i!=(c->comp.size()-1) ); + } - if( desc_width ) - { - while( written < (width-desc_width-2)) - { - written++; - writech( L' '); - } - set_color( get_color( HIGHLIGHT_PAGER_DESCRIPTION ), bg); - written += print_max( L"(", 1, 0 ); - written += print_max( c->desc.c_str(), desc_width, 0 ); - written += print_max( L")", 1, 0 ); - } - else - { - while( written < width ) - { - written++; - writech( L' '); - } - } - if ( secondary ) - set_color( rgb_color_t::normal(), rgb_color_t::normal() ); + if( desc_width ) + { + while( written < (width-desc_width-2)) + { + written++; + writech( L' '); + } + set_color( get_color( HIGHLIGHT_PAGER_DESCRIPTION ), bg); + written += print_max( L"(", 1, 0 ); + written += print_max( c->desc.c_str(), desc_width, 0 ); + written += print_max( L")", 1, 0 ); + } + else + { + while( written < width ) + { + written++; + writech( L' '); + } + } + if ( secondary ) + set_color( rgb_color_t::normal(), rgb_color_t::normal() ); } /** @@ -475,37 +475,37 @@ static void completion_print_item( const wchar_t *prefix, comp_t *c, int width, */ static void completion_print( int cols, - int *width, - int row_start, - int row_stop, - wchar_t *prefix, - int is_quoted, - const std::vector &lst ) + int *width, + int row_start, + int row_stop, + wchar_t *prefix, + int is_quoted, + const std::vector &lst ) { - size_t rows = (lst.size()-1)/cols+1; - size_t i, j; + size_t rows = (lst.size()-1)/cols+1; + size_t i, j; - for( i = row_start; i &lst ) + wchar_t *prefix, + int is_quoted, + std::vector &lst ) { - /* - The calculated preferred width of each column - */ - int pref_width[PAGER_MAX_COLS]; - /* - The calculated minimum width of each column - */ - int min_width[PAGER_MAX_COLS]; - /* - If the list can be printed with this width, width will contain the width of each column - */ - int *width=pref_width; - /* - Set to one if the list should be printed at this width - */ - int print=0; - - long i, j; - - int rows = (int)((lst.size()-1)/cols+1); - - int pref_tot_width=0; - int min_tot_width = 0; - int res=PAGER_RETRY; - /* - Skip completions on tiny terminals - */ - - if( termsize.ws_col < PAGER_MIN_WIDTH ) - return PAGER_DONE; - - memset( pref_width, 0, sizeof(pref_width) ); - memset( min_width, 0, sizeof(min_width) ); - - /* Calculate how wide the list would be */ - for( j = 0; j < cols; j++ ) - { - for( i = 0; ipref_width; - min = c->min_width; - - if( j != cols-1 ) - { - pref += 2; - min += 2; - } - min_width[j] = maxi( min_width[j], - min ); - pref_width[j] = maxi( pref_width[j], - pref ); - } - min_tot_width += min_width[j]; - pref_tot_width += pref_width[j]; - } - /* - Force fit if one column - */ - if( cols == 1) - { - if( pref_tot_width > termsize.ws_col ) - { - pref_width[0] = termsize.ws_col; - } - width = pref_width; - print=1; - } - else if( pref_tot_width <= termsize.ws_col ) - { - /* Terminal is wide enough. Print the list! */ - width = pref_width; - print=1; - } - else - { - long next_rows = (lst.size()-1)/(cols-1)+1; -/* fwprintf( stderr, + long i, j; + + int rows = (int)((lst.size()-1)/cols+1); + + int pref_tot_width=0; + int min_tot_width = 0; + int res=PAGER_RETRY; + /* + Skip completions on tiny terminals + */ + + if( termsize.ws_col < PAGER_MIN_WIDTH ) + return PAGER_DONE; + + memset( pref_width, 0, sizeof(pref_width) ); + memset( min_width, 0, sizeof(min_width) ); + + /* Calculate how wide the list would be */ + for( j = 0; j < cols; j++ ) + { + for( i = 0; ipref_width; + min = c->min_width; + + if( j != cols-1 ) + { + pref += 2; + min += 2; + } + min_width[j] = maxi( min_width[j], + min ); + pref_width[j] = maxi( pref_width[j], + pref ); + } + min_tot_width += min_width[j]; + pref_tot_width += pref_width[j]; + } + /* + Force fit if one column + */ + if( cols == 1) + { + if( pref_tot_width > termsize.ws_col ) + { + pref_width[0] = termsize.ws_col; + } + width = pref_width; + print=1; + } + else if( pref_tot_width <= termsize.ws_col ) + { + /* Terminal is wide enough. Print the list! */ + width = pref_width; + print=1; + } + else + { + long next_rows = (lst.size()-1)/(cols-1)+1; +/* fwprintf( stderr, L"cols %d, min_tot %d, term %d, rows=%d, nextrows %d, termrows %d, diff %d\n", cols, min_tot_width, termsize.ws_col, rows, next_rows, termsize.ws_row, pref_tot_width-termsize.ws_col ); */ - if( min_tot_width < termsize.ws_col && - ( ( (rows < termsize.ws_row) && (next_rows >= termsize.ws_row ) ) || - ( pref_tot_width-termsize.ws_col< 4 && cols < 3 ) ) ) - { - /* - Terminal almost wide enough, or squeezing makes the - whole list fit on-screen. + if( min_tot_width < termsize.ws_col && + ( ( (rows < termsize.ws_row) && (next_rows >= termsize.ws_row ) ) || + ( pref_tot_width-termsize.ws_col< 4 && cols < 3 ) ) ) + { + /* + Terminal almost wide enough, or squeezing makes the + whole list fit on-screen. - This part of the code is really important. People hate - having to scroll through the completion list. In cases - where there are a huge number of completions, it can't - be helped, but it is not uncommon for the completions to - _almost_ fit on one screen. In those cases, it is almost - always desirable to 'squeeze' the completions into a - single page. + This part of the code is really important. People hate + having to scroll through the completion list. In cases + where there are a huge number of completions, it can't + be helped, but it is not uncommon for the completions to + _almost_ fit on one screen. In those cases, it is almost + always desirable to 'squeeze' the completions into a + single page. - If we are using N columns and can get everything to - fit using squeezing, but everything would also fit - using N-1 columns, don't try. - */ + If we are using N columns and can get everything to + fit using squeezing, but everything would also fit + using N-1 columns, don't try. + */ - int tot_width = min_tot_width; - width = min_width; + int tot_width = min_tot_width; + width = min_width; - while( tot_width < termsize.ws_col ) - { - for( i=0; (i 0 ) - { - pos--; - writembs(tparm( cursor_address, 0, 0)); - writembs(scroll_reverse); - completion_print( cols, - width, - pos, - pos+1, - prefix, - is_quoted, - lst ); - writembs( tparm( cursor_address, - termsize.ws_row-1, 0) ); - writembs(clr_eol ); + writestr(msg.c_str()); + set_color( rgb_color_t::normal(), rgb_color_t::normal() ); + pager_flush(); + int c = readch(); - } + switch( c ) + { + case LINE_UP: + { + if( pos > 0 ) + { + pos--; + writembs(tparm( cursor_address, 0, 0)); + writembs(scroll_reverse); + completion_print( cols, + width, + pos, + pos+1, + prefix, + is_quoted, + lst ); + writembs( tparm( cursor_address, + termsize.ws_row-1, 0) ); + writembs(clr_eol ); - break; - } + } - case LINE_DOWN: - { - if( pos <= (rows - termsize.ws_row ) ) - { - pos++; - completion_print( cols, - width, - pos+termsize.ws_row-2, - pos+termsize.ws_row-1, - prefix, - is_quoted, - lst ); - } - break; - } + break; + } - case PAGE_DOWN: - { + case LINE_DOWN: + { + if( pos <= (rows - termsize.ws_row ) ) + { + pos++; + completion_print( cols, + width, + pos+termsize.ws_row-2, + pos+termsize.ws_row-1, + prefix, + is_quoted, + lst ); + } + break; + } - npos = mini( (int)(rows - termsize.ws_row+1), (int)(pos + termsize.ws_row-1) ); - if( npos != pos ) - { - pos = npos; - completion_print( cols, - width, - pos, - pos+termsize.ws_row-1, - prefix, - is_quoted, - lst ); - } - else - { - if( flash_screen ) - writembs( flash_screen ); - } + case PAGE_DOWN: + { - break; - } + npos = mini( (int)(rows - termsize.ws_row+1), (int)(pos + termsize.ws_row-1) ); + if( npos != pos ) + { + pos = npos; + completion_print( cols, + width, + pos, + pos+termsize.ws_row-1, + prefix, + is_quoted, + lst ); + } + else + { + if( flash_screen ) + writembs( flash_screen ); + } - case PAGE_UP: - { - npos = maxi( 0, - pos - termsize.ws_row+1 ); + break; + } - if( npos != pos ) - { - pos = npos; - completion_print( cols, - width, - pos, - pos+termsize.ws_row-1, - prefix, - is_quoted, - lst ); - } - else - { - if( flash_screen ) - writembs( flash_screen ); - } - break; - } + case PAGE_UP: + { + npos = maxi( 0, + pos - termsize.ws_row+1 ); - case R_NULL: - { - do_loop=0; - res=PAGER_RESIZE; - break; - - } - - default: - { - out_buff.push_back( c ); - do_loop = 0; - break; - } - } - } - writembs(clr_eol); - } - } - return res; + if( npos != pos ) + { + pos = npos; + completion_print( cols, + width, + pos, + pos+termsize.ws_row-1, + prefix, + is_quoted, + lst ); + } + else + { + if( flash_screen ) + writembs( flash_screen ); + } + break; + } + + case R_NULL: + { + do_loop=0; + res=PAGER_RESIZE; + break; + + } + + default: + { + out_buff.push_back( c ); + do_loop = 0; + break; + } + } + } + writembs(clr_eol); + } + } + return res; } /** @@ -832,39 +832,39 @@ static int completion_try_print( int cols, */ static void mangle_descriptions( wcstring_list_t &lst ) { - int skip; - for( size_t i=0; i desc_table; - for( size_t i=0; i mangle_completions( wcstring_list_t &lst, const wchar_t *prefix ) { std::vector result; - for( size_t i=0; icomp_width += my_wcswidth( str.c_str() ); - comp->comp.push_back(str); - start = end+1; - } + comp->comp_width += my_wcswidth( str.c_str() ); + comp->comp.push_back(str); + start = end+1; + } - if( c == COMPLETE_SEP ) - { - comp->desc = next.c_str() + start; - break; - } - - if( !c ) - break; - - } + if( c == COMPLETE_SEP ) + { + comp->desc = next.c_str() + start; + break; + } + + if( !c ) + break; + + } + + comp->comp_width += (int)(my_wcswidth(prefix)*comp->comp.size() + 2*(comp->comp.size()-1)); + comp->desc_width = comp->desc.empty()?0:my_wcswidth( comp->desc.c_str() ); + + comp->pref_width = comp->comp_width + comp->desc_width + (comp->desc_width?4:0); - comp->comp_width += (int)(my_wcswidth(prefix)*comp->comp.size() + 2*(comp->comp.size()-1)); - comp->desc_width = comp->desc.empty()?0:my_wcswidth( comp->desc.c_str() ); - - comp->pref_width = comp->comp_width + comp->desc_width + (comp->desc_width?4:0); - result.push_back(comp); - } - - recalc_width( result, prefix ); + } + + recalc_width( result, prefix ); return result; } @@ -970,10 +970,10 @@ static std::vector mangle_completions( wcstring_list_t &lst, const wch */ static void handle_winch( int sig ) { - if (ioctl(1,TIOCGWINSZ,&termsize)!=0) - { - return; - } + if (ioctl(1,TIOCGWINSZ,&termsize)!=0) + { + return; + } } /** @@ -983,7 +983,7 @@ static void handle_winch( int sig ) */ static int interrupt_handler() { - return R_NULL; + return R_NULL; } /** @@ -993,103 +993,103 @@ static int interrupt_handler() */ static void init( int mangle_descriptors, int out ) { - struct sigaction act; + struct sigaction act; - static struct termios pager_modes; - char *term; - - if( mangle_descriptors ) - { - - /* - Make fd 1 output to screen, and use some other fd for writing - the resulting output back to the caller - */ - int in; - out = dup( 1 ); - close(1); - close(0); + static struct termios pager_modes; + char *term; + + if( mangle_descriptors ) + { + + /* + Make fd 1 output to screen, and use some other fd for writing + the resulting output back to the caller + */ + int in; + out = dup( 1 ); + close(1); + close(0); /* OK to not use CLO_EXEC here because fish_pager is single threaded */ - if( (in = open( ttyname(2), O_RDWR )) != -1 ) - { - if( dup2( 2, 1 ) == -1 ) - { - debug( 0, _(L"Could not set up output file descriptors for pager") ); - exit( 1 ); - } - - if( dup2( in, 0 ) == -1 ) - { - debug( 0, _(L"Could not set up input file descriptors for pager") ); - exit( 1 ); - } - } - else - { - debug( 0, _(L"Could not open tty for pager") ); - exit( 1 ); - } - } - - if( !(out_file = fdopen( out, "w" )) ) - { - debug( 0, _(L"Could not initialize result pipe" ) ); - exit( 1 ); - } - + if( (in = open( ttyname(2), O_RDWR )) != -1 ) + { + if( dup2( 2, 1 ) == -1 ) + { + debug( 0, _(L"Could not set up output file descriptors for pager") ); + exit( 1 ); + } - env_universal_init( 0, 0, 0, 0); - input_common_init( &interrupt_handler ); - output_set_writer( &pager_buffered_writer ); + if( dup2( in, 0 ) == -1 ) + { + debug( 0, _(L"Could not set up input file descriptors for pager") ); + exit( 1 ); + } + } + else + { + debug( 0, _(L"Could not open tty for pager") ); + exit( 1 ); + } + } - sigemptyset( & act.sa_mask ); - act.sa_flags=0; - act.sa_handler=SIG_DFL; - act.sa_flags = 0; - act.sa_handler= &handle_winch; - if( sigaction( SIGWINCH, &act, 0 ) ) - { - wperror( L"sigaction" ); - exit(1); - } - - handle_winch( 0 ); /* Set handler for window change events */ + if( !(out_file = fdopen( out, "w" )) ) + { + debug( 0, _(L"Could not initialize result pipe" ) ); + exit( 1 ); + } - tcgetattr(0,&pager_modes); /* get the current terminal modes */ - memcpy( &saved_modes, - &pager_modes, - sizeof(saved_modes)); /* save a copy so we can reset the terminal later */ - pager_modes.c_lflag &= ~ICANON; /* turn off canonical mode */ - pager_modes.c_lflag &= ~ECHO; /* turn off echo mode */ - pager_modes.c_cc[VMIN]=1; - pager_modes.c_cc[VTIME]=0; + env_universal_init( 0, 0, 0, 0); + input_common_init( &interrupt_handler ); + output_set_writer( &pager_buffered_writer ); - /* - - */ - if( tcsetattr(0,TCSANOW,&pager_modes)) /* set the new modes */ - { - wperror(L"tcsetattr"); - exit(1); - } - + sigemptyset( & act.sa_mask ); + act.sa_flags=0; + act.sa_handler=SIG_DFL; + act.sa_flags = 0; + act.sa_handler= &handle_winch; + if( sigaction( SIGWINCH, &act, 0 ) ) + { + wperror( L"sigaction" ); + exit(1); + } - if( setupterm( 0, STDOUT_FILENO, 0) == ERR ) - { - debug( 0, _(L"Could not set up terminal") ); - exit(1); - } + handle_winch( 0 ); /* Set handler for window change events */ + + tcgetattr(0,&pager_modes); /* get the current terminal modes */ + memcpy( &saved_modes, + &pager_modes, + sizeof(saved_modes)); /* save a copy so we can reset the terminal later */ + + pager_modes.c_lflag &= ~ICANON; /* turn off canonical mode */ + pager_modes.c_lflag &= ~ECHO; /* turn off echo mode */ + pager_modes.c_cc[VMIN]=1; + pager_modes.c_cc[VTIME]=0; + + /* + + */ + if( tcsetattr(0,TCSANOW,&pager_modes)) /* set the new modes */ + { + wperror(L"tcsetattr"); + exit(1); + } + + + if( setupterm( 0, STDOUT_FILENO, 0) == ERR ) + { + debug( 0, _(L"Could not set up terminal") ); + exit(1); + } + + term = getenv("TERM"); + if( term ) + { + wchar_t *wterm = str2wcs(term); + output_set_term( wterm ); + free( wterm ); + } - term = getenv("TERM"); - if( term ) - { - wchar_t *wterm = str2wcs(term); - output_set_term( wterm ); - free( wterm ); - } - /* Infer term256 support */ char *fish_term256 = getenv("fish_term256"); bool support_term256; @@ -1098,7 +1098,7 @@ static void init( int mangle_descriptors, int out ) } else { support_term256 = term && strstr(term, "256color"); } - output_set_supports_term256(support_term256); + output_set_supports_term256(support_term256); } /** @@ -1106,15 +1106,15 @@ static void init( int mangle_descriptors, int out ) */ static void destroy() { - env_universal_destroy(); - input_common_destroy(); - wutil_destroy(); - if( del_curterm( cur_term ) == ERR ) - { - debug( 0, _(L"Error while closing terminfo") ); - } - - fclose( out_file ); + env_universal_destroy(); + input_common_destroy(); + wutil_destroy(); + if( del_curterm( cur_term ) == ERR ) + { + debug( 0, _(L"Error while closing terminfo") ); + } + + fclose( out_file ); } /** @@ -1123,319 +1123,319 @@ static void destroy() */ static void read_array( FILE* file, wcstring_list_t &comp ) { - std::vector buffer; - int c; - wchar_t *wcs; + std::vector buffer; + int c; + wchar_t *wcs; - while( !feof( file ) ) - { - buffer.clear(); + while( !feof( file ) ) + { + buffer.clear(); - while( 1 ) - { - c = getc( file ); - if( c == EOF ) - { - break; - } + while( 1 ) + { + c = getc( file ); + if( c == EOF ) + { + break; + } - if( c == '\n' ) - { - break; - } + if( c == '\n' ) + { + break; + } buffer.push_back(static_cast(c)); - } + } - if( ! buffer.empty() ) - { + if( ! buffer.empty() ) + { buffer.push_back(0); - - wcs = str2wcs( &buffer.at(0) ); - if( wcs ) - { + + wcs = str2wcs( &buffer.at(0) ); + if( wcs ) + { wcstring tmp = wcs; if (unescape_string(tmp, 0)) { comp.push_back(tmp); - } - free( wcs ); - } - } - } + } + free( wcs ); + } + } + } } static int get_fd( const char *str ) { - char *end; - long fd; - - errno = 0; - fd = strtol( str, &end, 10 ); - if( fd < 0 || *end || errno ) - { - debug( 0, ERR_NOT_FD, program_name, optarg ); - exit( 1 ); - } - return (int)fd; + char *end; + long fd; + + errno = 0; + fd = strtol( str, &end, 10 ); + if( fd < 0 || *end || errno ) + { + debug( 0, ERR_NOT_FD, program_name, optarg ); + exit( 1 ); + } + return (int)fd; } int main( int argc, char **argv ) { - int i; - int is_quoted=0; - wcstring_list_t comp; - wchar_t *prefix = 0; + int i; + int is_quoted=0; + wcstring_list_t comp; + wchar_t *prefix = 0; - int mangle_descriptors = 0; - int result_fd = -1; - set_main_thread(); + int mangle_descriptors = 0; + int result_fd = -1; + set_main_thread(); setup_fork_guards(); - - /* - This initialization is made early, so that the other init code - can use global_context for memory managment - */ - program_name = L"fish_pager"; + + /* + This initialization is made early, so that the other init code + can use global_context for memory managment + */ + program_name = L"fish_pager"; - wsetlocale( LC_ALL, L"" ); + wsetlocale( LC_ALL, L"" ); - /* - The call signature for fish_pager is a mess. Because we want - to be able to upgrade fish without breaking running - instances, we need to support all previous - modes. Unfortunatly, the two previous ones are a mess. The - third one is designed to be extensible, so hopefully it will - be the last. - */ + /* + The call signature for fish_pager is a mess. Because we want + to be able to upgrade fish without breaking running + instances, we need to support all previous + modes. Unfortunatly, the two previous ones are a mess. The + third one is designed to be extensible, so hopefully it will + be the last. + */ - if( argc > 1 && argv[1][0] == '-' ) - { - /* - Third mode - */ - - int completion_fd = -1; - FILE *completion_file; - - while( 1 ) - { - static struct option - long_options[] = - { - { - "result-fd", required_argument, 0, 'r' - } - , - { - "completion-fd", required_argument, 0, 'c' - } - , - { - "prefix", required_argument, 0, 'p' - } - , - { - "is-quoted", no_argument, 0, 'q' - } - , - { - "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 'r': - { - result_fd = get_fd( optarg ); - break; - } - - case 'c': - { - completion_fd = get_fd( optarg ); - break; - } + if( argc > 1 && argv[1][0] == '-' ) + { + /* + Third mode + */ - case 'p': - { - prefix = str2wcs(optarg); - break; - } + int completion_fd = -1; + FILE *completion_file; - case 'h': - { - print_help( argv[0], 1 ); - exit(0); - } + while( 1 ) + { + static struct option + long_options[] = + { + { + "result-fd", required_argument, 0, 'r' + } + , + { + "completion-fd", required_argument, 0, 'c' + } + , + { + "prefix", required_argument, 0, 'p' + } + , + { + "is-quoted", no_argument, 0, 'q' + } + , + { + "help", no_argument, 0, 'h' + } + , + { + "version", no_argument, 0, 'v' + } + , + { + 0, 0, 0, 0 + } + } + ; - case 'v': - { - debug( 0, L"%ls, version %s\n", program_name, PACKAGE_VERSION ); - exit( 0 ); - } - - case 'q': - { - is_quoted = 1; - } - - } - } + int opt_index = 0; - if( completion_fd == -1 || result_fd == -1 ) - { - debug( 0, _(L"Unspecified file descriptors") ); - exit( 1 ); - } - + int opt = getopt_long( argc, + argv, + GETOPT_STRING, + long_options, + &opt_index ); - if( (completion_file = fdopen( completion_fd, "r" ) ) ) - { - read_array( completion_file, comp ); - fclose( completion_file ); - } - else - { - debug( 0, _(L"Could not read completions") ); - wperror( L"fdopen" ); - exit( 1 ); - } + if( opt == -1 ) + break; - if( !prefix ) - { - prefix = wcsdup( L"" ); - } - - - } - else - { - /* - Second or first mode. These suck, but we need to support - them for backwards compatibility. At least for some - time. + switch( opt ) + { + case 0: + { + break; + } - Third mode was implemented in January 2007, and previous - modes should be considered deprecated from that point - forward. A reasonable time frame for removal of the code - below has yet to be determined. - */ - - if( argc < 3 ) - { - print_help( argv[0], 1 ); - exit( 0 ); - } - else - { - mangle_descriptors = 1; - - prefix = str2wcs( argv[2] ); - is_quoted = strcmp( "1", argv[1] )==0; - - if( argc > 3 ) - { - /* - First mode - */ - for( i=3; i 3 ) + { + /* + First mode + */ + for( i=3; i completions = mangle_completions( comp, prefix ); + init( mangle_descriptors, result_fd ); - /** - Try to print the completions. Start by trying to print the - list in PAGER_MAX_COLS columns, if the completions won't - fit, reduce the number of columns by one. Printing a single - column never fails. - */ - for( i = PAGER_MAX_COLS; i>0; i-- ) - { - switch( completion_try_print( i, prefix, is_quoted, completions ) ) - { + mangle_descriptions( comp ); - case PAGER_RETRY: - break; + if( wcscmp( prefix, L"-" ) == 0 ) + join_completions( comp ); - case PAGER_DONE: - i=0; - break; + std::vector completions = mangle_completions( comp, prefix ); - case PAGER_RESIZE: - /* - This means we got a resize event, so we start - over from the beginning. Since it the screen got - bigger, we might be able to fit all completions - on-screen. - */ - i=PAGER_MAX_COLS+1; - break; + /** + Try to print the completions. Start by trying to print the + list in PAGER_MAX_COLS columns, if the completions won't + fit, reduce the number of columns by one. Printing a single + column never fails. + */ + for( i = PAGER_MAX_COLS; i>0; i-- ) + { + switch( completion_try_print( i, prefix, is_quoted, completions ) ) + { - } - } - - free(prefix ); + case PAGER_RETRY: + break; - fwprintf( out_file, L"%ls", out_buff.c_str() ); - if( is_ca_mode ) - { - writembs(exit_ca_mode); - pager_flush(); - } - destroy(); + case PAGER_DONE: + i=0; + break; + + case PAGER_RESIZE: + /* + This means we got a resize event, so we start + over from the beginning. Since it the screen got + bigger, we might be able to fit all completions + on-screen. + */ + i=PAGER_MAX_COLS+1; + break; + + } + } + + free(prefix ); + + fwprintf( out_file, L"%ls", out_buff.c_str() ); + if( is_ca_mode ) + { + writembs(exit_ca_mode); + pager_flush(); + } + destroy(); } diff --git a/fish_tests.cpp b/fish_tests.cpp index 07e51f217..42f220bf3 100644 --- a/fish_tests.cpp +++ b/fish_tests.cpp @@ -1,5 +1,5 @@ /** \file fish_tests.c - Various bug and feature tests. Compiled and run by make test. + Various bug and feature tests. Compiled and run by make test. */ #include "config.h" @@ -95,11 +95,11 @@ static int err_count=0; */ static void say( const wchar_t *blah, ... ) { - va_list va; - va_start( va, blah ); - vwprintf( blah, va ); - va_end( va ); - wprintf( L"\n" ); + va_list va; + va_start( va, blah ); + vwprintf( blah, va ); + va_end( va ); + wprintf( L"\n" ); } /** @@ -107,14 +107,14 @@ static void say( const wchar_t *blah, ... ) */ static void err( const wchar_t *blah, ... ) { - va_list va; - va_start( va, blah ); - err_count++; - - wprintf( L"Error: " ); - vwprintf( blah, va ); - va_end( va ); - wprintf( L"\n" ); + va_list va; + va_start( va, blah ); + err_count++; + + wprintf( L"Error: " ); + vwprintf( blah, va ); + va_end( va ); + wprintf( L"\n" ); } /** @@ -123,40 +123,40 @@ static void err( const wchar_t *blah, ... ) */ static void test_escape() { - int i; - wcstring sb; - - say( L"Testing escaping and unescaping" ); - - for( i=0; i sb; - - say( L"Testing wide/narrow string conversion" ); - - for( i=0; i sb; + + say( L"Testing wide/narrow string conversion" ); + + for( i=0; i&1 'nested \"quoted\" '(string containing subshells ){and,brackets}$as[$well (as variable arrays)] not_a_redirect^ ^ ^^is_a_redirect"; - const int types[] = - { - TOK_STRING, TOK_REDIRECT_IN, TOK_STRING, TOK_REDIRECT_FD, TOK_STRING, TOK_STRING, TOK_STRING, TOK_REDIRECT_OUT, TOK_REDIRECT_APPEND, TOK_STRING, TOK_END - } - ; - size_t i; - - say( L"Test correct tokenization" ); + if( tok_last_type( &t ) != TOK_ERROR ) + { + err(L"Invalid input to tokenizer was undetected" ); + } - for( i=0, tok_init( &t, str, 0 ); i<(sizeof(types)/sizeof(int)); i++,tok_next( &t ) ) - { - if( types[i] != tok_last_type( &t ) ) - { - err( L"Tokenization error:"); - wprintf( L"Token number %d of string \n'%ls'\n, expected token type %ls, got token '%ls' of type %ls\n", - i+1, - str, - tok_get_desc(types[i]), - tok_last(&t), - tok_get_desc(tok_last_type( &t )) ); - } - } - } + say( L"Testing use of broken tokenizer" ); + if( !tok_has_next( &t ) ) + { + err( L"tok_has_next() should return 1 once on broken tokenizer" ); + } + + tok_next( &t ); + if( tok_last_type( &t ) != TOK_ERROR ) + { + err(L"Invalid input to tokenizer was undetected" ); + } + + /* + This should crash if there is a bug. No reliable way to detect otherwise. + */ + say( L"Test destruction of broken tokenizer" ); + tok_destroy( &t ); + + { + + const wchar_t *str = L"string &1 'nested \"quoted\" '(string containing subshells ){and,brackets}$as[$well (as variable arrays)] not_a_redirect^ ^ ^^is_a_redirect"; + const int types[] = + { + TOK_STRING, TOK_REDIRECT_IN, TOK_STRING, TOK_REDIRECT_FD, TOK_STRING, TOK_STRING, TOK_STRING, TOK_REDIRECT_OUT, TOK_REDIRECT_APPEND, TOK_STRING, TOK_END + } + ; + size_t i; + + say( L"Test correct tokenization" ); + + for( i=0, tok_init( &t, str, 0 ); i<(sizeof(types)/sizeof(int)); i++,tok_next( &t ) ) + { + if( types[i] != tok_last_type( &t ) ) + { + err( L"Tokenization error:"); + wprintf( L"Token number %d of string \n'%ls'\n, expected token type %ls, got token '%ls' of type %ls\n", + i+1, + str, + tok_get_desc(types[i]), + tok_last(&t), + tok_get_desc(tok_last_type( &t )) ); + } + } + } } static int test_fork_helper(void *unused) { @@ -375,7 +375,7 @@ static void test_fork(void) { exit_without_destructors(0); } else { perror("fork"); - } + } } for (size_t i=0; i < FORK_COUNT; i++) { int status = 0; @@ -397,73 +397,73 @@ static void test_fork(void) { */ static void test_parser() { - say( L"Testing parser" ); - - parser_t parser(PARSER_TYPE_GENERAL, true); - - say( L"Testing null input to parser" ); - if( !parser.test( 0, 0, 0, 0 ) ) - { - err( L"Null input to parser.test undetected" ); - } + say( L"Testing parser" ); - say( L"Testing block nesting" ); - if( !parser.test( L"if; end", 0, 0, 0 ) ) - { - err( L"Incomplete if statement undetected" ); - } - if( !parser.test( L"if test; echo", 0, 0, 0 ) ) - { - err( L"Missing end undetected" ); - } - if( !parser.test( L"if test; end; end", 0, 0, 0 ) ) - { - err( L"Unbalanced end undetected" ); - } + parser_t parser(PARSER_TYPE_GENERAL, true); - say( L"Testing detection of invalid use of builtin commands" ); - if( !parser.test( L"case foo", 0, 0, 0 ) ) - { - err( L"'case' command outside of block context undetected" ); - } - if( !parser.test( L"switch ggg; if true; case foo;end;end", 0, 0, 0 ) ) - { - err( L"'case' command outside of switch block context undetected" ); - } - if( !parser.test( L"else", 0, 0, 0 ) ) - { - err( L"'else' command outside of conditional block context undetected" ); - } - if( !parser.test( L"else if", 0, 0, 0 ) ) - { - err( L"'else if' command outside of conditional block context undetected" ); - } - if( !parser.test( L"if false; else if; end", 0, 0, 0 ) ) - { - err( L"'else if' missing command undetected" ); - } + say( L"Testing null input to parser" ); + if( !parser.test( 0, 0, 0, 0 ) ) + { + err( L"Null input to parser.test undetected" ); + } - if( !parser.test( L"break", 0, 0, 0 ) ) - { - err( L"'break' command outside of loop block context undetected" ); - } - if( !parser.test( L"exec ls|less", 0, 0, 0 ) || !parser.test( L"echo|return", 0, 0, 0 )) - { - err( L"Invalid pipe command undetected" ); - } + say( L"Testing block nesting" ); + if( !parser.test( L"if; end", 0, 0, 0 ) ) + { + err( L"Incomplete if statement undetected" ); + } + if( !parser.test( L"if test; echo", 0, 0, 0 ) ) + { + err( L"Missing end undetected" ); + } + if( !parser.test( L"if test; end; end", 0, 0, 0 ) ) + { + err( L"Unbalanced end undetected" ); + } - say( L"Testing basic evaluation" ); + say( L"Testing detection of invalid use of builtin commands" ); + if( !parser.test( L"case foo", 0, 0, 0 ) ) + { + err( L"'case' command outside of block context undetected" ); + } + if( !parser.test( L"switch ggg; if true; case foo;end;end", 0, 0, 0 ) ) + { + err( L"'case' command outside of switch block context undetected" ); + } + if( !parser.test( L"else", 0, 0, 0 ) ) + { + err( L"'else' command outside of conditional block context undetected" ); + } + if( !parser.test( L"else if", 0, 0, 0 ) ) + { + err( L"'else if' command outside of conditional block context undetected" ); + } + if( !parser.test( L"if false; else if; end", 0, 0, 0 ) ) + { + err( L"'else if' missing command undetected" ); + } + + if( !parser.test( L"break", 0, 0, 0 ) ) + { + err( L"'break' command outside of loop block context undetected" ); + } + if( !parser.test( L"exec ls|less", 0, 0, 0 ) || !parser.test( L"echo|return", 0, 0, 0 )) + { + err( L"Invalid pipe command undetected" ); + } + + say( L"Testing basic evaluation" ); #if 0 /* This fails now since the parser takes a wcstring&, and NULL converts to wchar_t * converts to wcstring which crashes (thanks C++) */ - if( !parser.eval( 0, 0, TOP ) ) - { - err( L"Null input when evaluating undetected" ); - } + if( !parser.eval( 0, 0, TOP ) ) + { + err( L"Null input when evaluating undetected" ); + } #endif - if( !parser.eval( L"ls", io_chain_t(), WHILE ) ) - { - err( L"Invalid block mode when evaluating undetected" ); - } + if( !parser.eval( L"ls", io_chain_t(), WHILE ) ) + { + err( L"Invalid block mode when evaluating undetected" ); + } } class lru_node_test_t : public lru_node_t { @@ -474,9 +474,9 @@ class lru_node_test_t : public lru_node_t { class test_lru_t : public lru_cache_t { public: test_lru_t() : lru_cache_t(16) { } - + std::vector evicted_nodes; - + virtual void node_was_evicted(lru_node_test_t *node) { assert(find(evicted_nodes.begin(), evicted_nodes.end(), node) == evicted_nodes.end()); evicted_nodes.push_back(node); @@ -485,7 +485,7 @@ class test_lru_t : public lru_cache_t { static void test_lru(void) { say( L"Testing LRU cache" ); - + test_lru_t cache; std::vector expected_evicted; size_t total_nodes = 20; @@ -506,7 +506,7 @@ static void test_lru(void) { delete node; } } - + /** Perform parameter expansion and test if the output equals the zero-terminated parameter list supplied. @@ -516,46 +516,46 @@ static void test_lru(void) { static int expand_test( const wchar_t *in, int flags, ... ) { - std::vector output; - va_list va; - size_t i=0; - int res=1; - wchar_t *arg; - - if( expand_string( in, output, flags) ) - { - - } - + std::vector output; + va_list va; + size_t i=0; + int res=1; + wchar_t *arg; + + if( expand_string( in, output, flags) ) + { + + } + #if 0 for (size_t idx=0; idx < output.size(); idx++) { printf("%ls\n", output.at(idx).completion.c_str()); } #endif - - va_start( va, flags ); - while( (arg=va_arg(va, wchar_t *) )!= 0 ) - { - if( output.size() == i ) - { - res=0; - break; - } - + va_start( va, flags ); + + while( (arg=va_arg(va, wchar_t *) )!= 0 ) + { + if( output.size() == i ) + { + res=0; + break; + } + if (output.at(i).completion != arg) - { - res=0; - break; - } - - i++; - } - va_end( va ); - - return res; - + { + res=0; + break; + } + + i++; + } + va_end( va ); + + return res; + } /** @@ -563,36 +563,36 @@ static int expand_test( const wchar_t *in, int flags, ... ) */ static void test_expand() { - say( L"Testing parameter expansion" ); - - if( !expand_test( L"foo", 0, L"foo", 0 )) - { - err( L"Strings do not expand to themselves" ); - } + say( L"Testing parameter expansion" ); - if( !expand_test( L"a{b,c,d}e", 0, L"abe", L"ace", L"ade", 0 ) ) - { - err( L"Bracket expansion is broken" ); - } + if( !expand_test( L"foo", 0, L"foo", 0 )) + { + err( L"Strings do not expand to themselves" ); + } + + if( !expand_test( L"a{b,c,d}e", 0, L"abe", L"ace", L"ade", 0 ) ) + { + err( L"Bracket expansion is broken" ); + } + + if( !expand_test( L"a*", EXPAND_SKIP_WILDCARDS, L"a*", 0 ) ) + { + err( L"Cannot skip wildcard expansion" ); + } - if( !expand_test( L"a*", EXPAND_SKIP_WILDCARDS, L"a*", 0 ) ) - { - err( L"Cannot skip wildcard expansion" ); - } - if (system("mkdir -p /tmp/fish_expand_test/")) err(L"mkdir failed"); if (system("touch /tmp/fish_expand_test/.foo")) err(L"touch failed"); if (system("touch /tmp/fish_expand_test/bar")) err(L"touch failed"); - + // This is checking that .* does NOT match . and .. (https://github.com/fish-shell/fish-shell/issues/270). But it does have to match literal components (e.g. "./*" has to match the same as "*" - if (! expand_test( L"/tmp/fish_expand_test/.*", 0, L"/tmp/fish_expand_test/.foo", 0 )) - { - err( L"Expansion not correctly handling dotfiles" ); - } - if (! expand_test( L"/tmp/fish_expand_test/./.*", 0, L"/tmp/fish_expand_test/./.foo", 0 )) - { - err( L"Expansion not correctly handling literal path components in dotfiles" ); - } + if (! expand_test( L"/tmp/fish_expand_test/.*", 0, L"/tmp/fish_expand_test/.foo", 0 )) + { + err( L"Expansion not correctly handling dotfiles" ); + } + if (! expand_test( L"/tmp/fish_expand_test/./.*", 0, L"/tmp/fish_expand_test/./.foo", 0 )) + { + err( L"Expansion not correctly handling literal path components in dotfiles" ); + } //system("rm -Rf /tmp/fish_expand_test"); } @@ -600,21 +600,21 @@ static void test_expand() /** Test path functions */ static void test_path() { - say( L"Testing path functions" ); + say( L"Testing path functions" ); wcstring path = L"//foo//////bar/"; wcstring canon = path; path_make_canonical(canon); - if( canon != L"/foo/bar" ) - { - err( L"Bug in canonical PATH code" ); - } - + if( canon != L"/foo/bar" ) + { + err( L"Bug in canonical PATH code" ); + } + path = L"/"; path_make_canonical(path); if (path != L"/") { - err( L"Bug in canonical PATH code" ); + err( L"Bug in canonical PATH code" ); } } @@ -625,36 +625,36 @@ static void test_is_potential_path() if (system("rm -Rf /tmp/is_potential_path_test/")) { err(L"Failed to remove /tmp/is_potential_path_test/"); } - + /* Directories */ if (system("mkdir -p /tmp/is_potential_path_test/alpha/")) err(L"mkdir failed"); if (system("mkdir -p /tmp/is_potential_path_test/beta/")) err(L"mkdir failed"); - + /* Files */ if (system("touch /tmp/is_potential_path_test/aardvark")) err(L"touch failed"); if (system("touch /tmp/is_potential_path_test/gamma")) err(L"touch failed"); - + const wcstring wd = L"/tmp/is_potential_path_test/"; const wcstring_list_t wds(1, wd); - + wcstring tmp; assert(is_potential_path(L"al", wds, PATH_REQUIRE_DIR, &tmp) && tmp == L"alpha/"); assert(is_potential_path(L"alpha/", wds, PATH_REQUIRE_DIR, &tmp) && tmp == L"alpha/"); assert(is_potential_path(L"aard", wds, 0, &tmp) && tmp == L"aardvark"); - + assert(! is_potential_path(L"balpha/", wds, PATH_REQUIRE_DIR, &tmp)); assert(! is_potential_path(L"aard", wds, PATH_REQUIRE_DIR, &tmp)); assert(! is_potential_path(L"aarde", wds, PATH_REQUIRE_DIR, &tmp)); assert(! is_potential_path(L"aarde", wds, 0, &tmp)); - + assert(is_potential_path(L"/tmp/is_potential_path_test/aardvark", wds, 0, &tmp) && tmp == L"/tmp/is_potential_path_test/aardvark"); assert(is_potential_path(L"/tmp/is_potential_path_test/al", wds, PATH_REQUIRE_DIR, &tmp) && tmp == L"/tmp/is_potential_path_test/alpha/"); assert(is_potential_path(L"/tmp/is_potential_path_test/aardv", wds, 0, &tmp) && tmp == L"/tmp/is_potential_path_test/aardvark"); - + assert(! is_potential_path(L"/tmp/is_potential_path_test/aardvark", wds, PATH_REQUIRE_DIR, &tmp)); assert(! is_potential_path(L"/tmp/is_potential_path_test/al/", wds, 0, &tmp)); assert(! is_potential_path(L"/tmp/is_potential_path_test/ar", wds, 0, &tmp)); - + assert(is_potential_path(L"/usr", wds, PATH_REQUIRE_DIR, &tmp) && tmp == L"/usr/"); } @@ -711,7 +711,7 @@ static void test_test() { assert(run_test_test(0, L"0 -ne 1 -a -n 5")); assert(run_test_test(0, L"-n 5 -a 10 -gt 5")); assert(run_test_test(0, L"-n 3 -a -n 5")); - + /* test precedence: '0 == 0 || 0 == 1 && 0 == 2' should be evaluated as: @@ -722,8 +722,8 @@ static void test_test() { assert(run_test_test(0, L"0 = 0 -o 0 = 1 -a 0 = 2")); assert(run_test_test(0, L"-n 5 -o 0 = 1 -a 0 = 2")); assert(run_test_test(1, L"( 0 = 0 -o 0 = 1 ) -a 0 = 2")); - assert(run_test_test(0, L"0 = 0 -o ( 0 = 1 -a 0 = 2 )")); - + assert(run_test_test(0, L"0 = 0 -o ( 0 = 1 -a 0 = 2 )")); + /* A few lame tests for permissions; these need to be a lot more complete. */ assert(run_test_test(0, L"-e /bin/ls")); assert(run_test_test(1, L"-e /bin/ls_not_a_path")); @@ -731,12 +731,12 @@ static void test_test() { assert(run_test_test(1, L"-x /bin/ls_not_a_path")); assert(run_test_test(0, L"-d /bin/")); assert(run_test_test(1, L"-d /bin/ls")); - + /* This failed at one point */ assert(run_test_test(1, L"-d /bin -a 5 -eq 3")); assert(run_test_test(0, L"-d /bin -o 5 -eq 3")); assert(run_test_test(0, L"-d /bin -a ! 5 -eq 3")); - + /* We didn't properly handle multiple "just strings" either */ assert(run_test_test(0, L"foo")); assert(run_test_test(0, L"foo -a bar")); @@ -773,7 +773,7 @@ static void perform_one_autosuggestion_test(const wcstring &command, const wcstr printf("line %ld: autosuggest_suggest_special() returned the wrong expected string for command %ls\n", line, command.c_str()); printf(" actual: %ls\n", suggestion.c_str()); printf("expected: %ls\n", expected.c_str()); - assert(suggestion == expected); + assert(suggestion == expected); } } @@ -786,7 +786,7 @@ static void test_autosuggest_suggest_special() { if (system("mkdir -p /tmp/autosuggest_test/4foo\\'bar")) err(L"mkdir failed"); //a path with a single quote if (system("mkdir -p /tmp/autosuggest_test/5foo\\\"bar")) err(L"mkdir failed"); //a path with a double quote if (system("mkdir -p ~/test_autosuggest_suggest_special/")) err(L"mkdir failed"); //make sure tilde is handled - + const wcstring wd = L"/tmp/autosuggest_test/"; perform_one_autosuggestion_test(L"cd /tmp/autosuggest_test/0", wd, L"cd /tmp/autosuggest_test/0foobar/", __LINE__); perform_one_autosuggestion_test(L"cd \"/tmp/autosuggest_test/0", wd, L"cd \"/tmp/autosuggest_test/0foobar/\"", __LINE__); @@ -794,47 +794,47 @@ static void test_autosuggest_suggest_special() { perform_one_autosuggestion_test(L"cd 0", wd, L"cd 0foobar/", __LINE__); perform_one_autosuggestion_test(L"cd \"0", wd, L"cd \"0foobar/\"", __LINE__); perform_one_autosuggestion_test(L"cd '0", wd, L"cd '0foobar/'", __LINE__); - + perform_one_autosuggestion_test(L"cd /tmp/autosuggest_test/1", wd, L"cd /tmp/autosuggest_test/1foo\\ bar/", __LINE__); perform_one_autosuggestion_test(L"cd \"/tmp/autosuggest_test/1", wd, L"cd \"/tmp/autosuggest_test/1foo bar/\"", __LINE__); perform_one_autosuggestion_test(L"cd '/tmp/autosuggest_test/1", wd, L"cd '/tmp/autosuggest_test/1foo bar/'", __LINE__); perform_one_autosuggestion_test(L"cd 1", wd, L"cd 1foo\\ bar/", __LINE__); perform_one_autosuggestion_test(L"cd \"1", wd, L"cd \"1foo bar/\"", __LINE__); perform_one_autosuggestion_test(L"cd '1", wd, L"cd '1foo bar/'", __LINE__); - + perform_one_autosuggestion_test(L"cd /tmp/autosuggest_test/2", wd, L"cd /tmp/autosuggest_test/2foo\\ \\ bar/", __LINE__); perform_one_autosuggestion_test(L"cd \"/tmp/autosuggest_test/2", wd, L"cd \"/tmp/autosuggest_test/2foo bar/\"", __LINE__); perform_one_autosuggestion_test(L"cd '/tmp/autosuggest_test/2", wd, L"cd '/tmp/autosuggest_test/2foo bar/'", __LINE__); perform_one_autosuggestion_test(L"cd 2", wd, L"cd 2foo\\ \\ bar/", __LINE__); perform_one_autosuggestion_test(L"cd \"2", wd, L"cd \"2foo bar/\"", __LINE__); perform_one_autosuggestion_test(L"cd '2", wd, L"cd '2foo bar/'", __LINE__); - + perform_one_autosuggestion_test(L"cd /tmp/autosuggest_test/3", wd, L"cd /tmp/autosuggest_test/3foo\\\\bar/", __LINE__); perform_one_autosuggestion_test(L"cd \"/tmp/autosuggest_test/3", wd, L"cd \"/tmp/autosuggest_test/3foo\\bar/\"", __LINE__); perform_one_autosuggestion_test(L"cd '/tmp/autosuggest_test/3", wd, L"cd '/tmp/autosuggest_test/3foo\\bar/'", __LINE__); perform_one_autosuggestion_test(L"cd 3", wd, L"cd 3foo\\\\bar/", __LINE__); perform_one_autosuggestion_test(L"cd \"3", wd, L"cd \"3foo\\bar/\"", __LINE__); perform_one_autosuggestion_test(L"cd '3", wd, L"cd '3foo\\bar/'", __LINE__); - + perform_one_autosuggestion_test(L"cd /tmp/autosuggest_test/4", wd, L"cd /tmp/autosuggest_test/4foo\\'bar/", __LINE__); perform_one_autosuggestion_test(L"cd \"/tmp/autosuggest_test/4", wd, L"cd \"/tmp/autosuggest_test/4foo'bar/\"", __LINE__); perform_one_autosuggestion_test(L"cd '/tmp/autosuggest_test/4", wd, L"cd '/tmp/autosuggest_test/4foo\\'bar/'", __LINE__); perform_one_autosuggestion_test(L"cd 4", wd, L"cd 4foo\\'bar/", __LINE__); perform_one_autosuggestion_test(L"cd \"4", wd, L"cd \"4foo'bar/\"", __LINE__); perform_one_autosuggestion_test(L"cd '4", wd, L"cd '4foo\\'bar/'", __LINE__); - + perform_one_autosuggestion_test(L"cd /tmp/autosuggest_test/5", wd, L"cd /tmp/autosuggest_test/5foo\\\"bar/", __LINE__); perform_one_autosuggestion_test(L"cd \"/tmp/autosuggest_test/5", wd, L"cd \"/tmp/autosuggest_test/5foo\\\"bar/\"", __LINE__); perform_one_autosuggestion_test(L"cd '/tmp/autosuggest_test/5", wd, L"cd '/tmp/autosuggest_test/5foo\"bar/'", __LINE__); perform_one_autosuggestion_test(L"cd 5", wd, L"cd 5foo\\\"bar/", __LINE__); perform_one_autosuggestion_test(L"cd \"5", wd, L"cd \"5foo\\\"bar/\"", __LINE__); perform_one_autosuggestion_test(L"cd '5", wd, L"cd '5foo\"bar/'", __LINE__); - + perform_one_autosuggestion_test(L"cd ~/test_autosuggest_suggest_specia", wd, L"cd ~/test_autosuggest_suggest_special/", __LINE__); - + // A single quote should defeat tilde expansion perform_one_autosuggestion_test(L"cd '~/test_autosuggest_suggest_specia'", wd, L"", __LINE__); - + system("rm -Rf '/tmp/autosuggest_test/'"); system("rm -Rf ~/test_autosuggest_suggest_special/"); } @@ -845,65 +845,65 @@ static void test_autosuggest_suggest_special() { */ void perf_complete() { - wchar_t c; - std::vector out; - long long t1, t2; - int matches=0; - double t; - wchar_t str[3]= - { - 0, 0, 0 - } - ; - int i; - - - say( L"Testing completion performance" ); - - reader_push(L""); - say( L"Here we go" ); - - t1 = get_time(); - - - for( c=L'a'; c<=L'z'; c++ ) - { - str[0]=c; - reader_set_buffer( str, 0 ); - - complete( str, out, COMPLETE_DEFAULT, NULL ); - - matches += out.size(); + wchar_t c; + std::vector out; + long long t1, t2; + int matches=0; + double t; + wchar_t str[3]= + { + 0, 0, 0 + } + ; + int i; + + + say( L"Testing completion performance" ); + + reader_push(L""); + say( L"Here we go" ); + + t1 = get_time(); + + + for( c=L'a'; c<=L'z'; c++ ) + { + str[0]=c; + reader_set_buffer( str, 0 ); + + complete( str, out, COMPLETE_DEFAULT, NULL ); + + matches += out.size(); out.clear(); - } - t2=get_time(); - - t = (double)(t2-t1)/(1000000*26); - - say( L"One letter command completion took %f seconds per completion, %f microseconds/match", t, (double)(t2-t1)/matches ); - - matches=0; - t1 = get_time(); - for( i=0; iitem_at_index(i); if (item.empty()) break; - + if (item.str() == txt) { result = true; break; @@ -955,18 +955,18 @@ static wcstring random_string(void) { void history_tests_t::test_history(void) { say( L"Testing history"); - + history_t &history = history_t::history_with_name(L"test_history"); history.clear(); history.add(L"Gamma"); history.add(L"Beta"); history.add(L"Alpha"); - + /* All three items match "a" */ history_search_t search1(history, L"a"); test_history_matches(search1, 3); assert(search1.current_string() == L"Alpha"); - + /* One item matches "et" */ history_search_t search2(history, L"et"); test_history_matches(search2, 1); @@ -976,34 +976,34 @@ void history_tests_t::test_history(void) { history.remove(L"Alpha"); history_search_t search3(history, L"Alpha"); test_history_matches(search3, 0); - + /* Test history escaping and unescaping, yaml, etc. */ std::vector before, after; history.clear(); size_t i, max = 100; for (i=1; i <= max; i++) { - + /* Generate a value */ wcstring value = wcstring(L"test item ") + to_string(i); - + /* Maybe add some backslashes */ if (i % 3 == 0) value.append(L"(slashies \\\\\\ slashies)"); /* Generate some paths */ - path_list_t paths; + path_list_t paths; size_t count = rand() % 6; while (count--) { paths.push_back(random_string()); } - + /* Record this item */ history_item_t item(value, time(NULL), paths); before.push_back(item); history.add(item); } history.save(); - + /* Read items back in reverse order and ensure they're the same */ for (i=100; i >= 1; i--) { history_item_t item = history.item_at_index(i); @@ -1017,7 +1017,7 @@ void history_tests_t::test_history(void) { assert(bef.creation_timestamp == aft.creation_timestamp); assert(bef.required_paths == aft.required_paths); } - + /* Clean up after our tests */ history.clear(); } @@ -1040,25 +1040,25 @@ void history_tests_t::test_history_merge(void) { const wcstring name = L"merge_test"; history_t *hists[count] = {new history_t(name), new history_t(name), new history_t(name)}; wcstring texts[count] = {L"History 1", L"History 2", L"History 3"}; - + /* Make sure history is clear */ for (size_t i=0; i < count; i++) { hists[i]->clear(); } - + /* Make sure we don't add an item in the same second as we created the history */ time_barrier(); - + /* Add a different item to each */ for (size_t i=0; i < count; i++) { hists[i]->add(texts[i]); } - + /* Save them */ for (size_t i=0; i < count; i++) { hists[i]->save(); } - + /* Make sure each history contains what it ought to, but they have not leaked into each other */ for (size_t i = 0; i < count; i++) { for (size_t j=0; j < count; j++) { @@ -1067,7 +1067,7 @@ void history_tests_t::test_history_merge(void) { assert(should_contain == does_contain); } } - + /* Make a new history. It should contain everything. The time_barrier() is so that the timestamp is newer, since we only pick up items whose timestamp is before the birth stamp. */ time_barrier(); history_t *everything = new history_t(name); @@ -1085,7 +1085,7 @@ void history_tests_t::test_history_merge(void) { static bool install_sample_history(const wchar_t *name) { char command[512]; - snprintf(command, sizeof command, "cp tests/%ls ~/.config/fish/%ls_history", name, name); + snprintf(command, sizeof command, "cp tests/%ls ~/.config/fish/%ls_history", name, name); if (system(command)) { err(L"Failed to copy sample history"); return false; @@ -1100,7 +1100,7 @@ static bool history_equals(history_t &hist, const wchar_t * const *strings) { while (strings[expected_count]) { expected_count++; } - + /* Ensure the contents are the same */ size_t history_idx = 1; size_t array_idx = 0; @@ -1120,13 +1120,13 @@ static bool history_equals(history_t &hist, const wchar_t * const *strings) { history_idx++; array_idx++; } - + return true; } void history_tests_t::test_history_formats(void) { const wchar_t *name; - + // Test inferring and reading legacy and bash history formats name = L"history_sample_fish_1_x"; say(L"Testing %ls", name); @@ -1138,25 +1138,25 @@ void history_tests_t::test_history_formats(void) { L"#def", L"echo #abc", - + L"function yay\n" "echo hi\n" "end", - + L"cd foobar", - + L"ls /", - + NULL }; - + history_t &test_history = history_t::history_with_name(name); if (! history_equals(test_history, expected)) { err(L"test_history_formats failed for %ls\n", name); } test_history.clear(); } - + name = L"history_sample_fish_2_0"; say(L"Testing %ls", name); if (! install_sample_history(name)) { @@ -1164,23 +1164,23 @@ void history_tests_t::test_history_formats(void) { } else { const wchar_t * const expected[] = { L"echo this has\\\nbackslashes", - + L"function foo\n" "echo bar\n" "end", - + L"echo alpha", - + NULL }; - + history_t &test_history = history_t::history_with_name(name); if (! history_equals(test_history, expected)) { err(L"test_history_formats failed for %ls\n", name); } test_history.clear(); } - + say(L"Testing bash import"); FILE *f = fopen("tests/history_sample_bash", "r"); if (! f) { @@ -1189,11 +1189,11 @@ void history_tests_t::test_history_formats(void) { // It should skip over the export command since that's a bash-ism const wchar_t *expected[] = { L"echo supsup", - + L"history --help", - + L"echo foo", - + NULL }; history_t &test_history = history_t::history_with_name(L"bash_import"); @@ -1208,57 +1208,57 @@ void history_tests_t::test_history_formats(void) { /** - Main test + Main test */ int main( int argc, char **argv ) { - setlocale( LC_ALL, "" ); - srand( time( 0 ) ); + setlocale( LC_ALL, "" ); + srand( time( 0 ) ); configure_thread_assertions_for_testing(); - program_name=L"(ignore)"; - - say( L"Testing low-level functionality"); - say( L"Lines beginning with '(ignore):' are not errors, they are warning messages\ngenerated by the fish parser library when given broken input, and can be\nignored. All actual errors begin with 'Error:'." ); - set_main_thread(); + program_name=L"(ignore)"; + + say( L"Testing low-level functionality"); + say( L"Lines beginning with '(ignore):' are not errors, they are warning messages\ngenerated by the fish parser library when given broken input, and can be\nignored. All actual errors begin with 'Error:'." ); + set_main_thread(); setup_fork_guards(); - proc_init(); - event_init(); - function_init(); - builtin_init(); - reader_init(); - env_init(); - + proc_init(); + event_init(); + function_init(); + builtin_init(); + reader_init(); + env_init(); + test_format(); - test_escape(); - test_convert(); - test_tok(); + test_escape(); + test_convert(); + test_tok(); test_fork(); - test_parser(); - test_lru(); - test_expand(); + test_parser(); + test_lru(); + test_expand(); test_test(); - test_path(); + test_path(); test_is_potential_path(); test_colors(); test_autosuggest_suggest_special(); history_tests_t::test_history(); history_tests_t::test_history_merge(); history_tests_t::test_history_formats(); - - say( L"Encountered %d errors in low-level tests", err_count ); - /* - Skip performance tests for now, since they seem to hang when running from inside make (?) - */ -// say( L"Testing performance" ); -// perf_complete(); - - env_destroy(); - reader_destroy(); - builtin_destroy(); - wutil_destroy(); - event_destroy(); - proc_destroy(); - + say( L"Encountered %d errors in low-level tests", err_count ); + + /* + Skip performance tests for now, since they seem to hang when running from inside make (?) + */ +// say( L"Testing performance" ); +// perf_complete(); + + env_destroy(); + reader_destroy(); + builtin_destroy(); + wutil_destroy(); + event_destroy(); + proc_destroy(); + } diff --git a/fishd.cpp b/fishd.cpp index 86c17373d..acfecae1c 100644 --- a/fishd.cpp +++ b/fishd.cpp @@ -85,7 +85,7 @@ time the original barrier request was sent have been received. /** Maximum length of socket filename */ -#ifndef UNIX_PATH_MAX +#ifndef UNIX_PATH_MAX #define UNIX_PATH_MAX 100 #endif @@ -153,47 +153,47 @@ static int quit=0; */ static char *get_socket_filename() { - char *name; - const char *dir = getenv( "FISHD_SOCKET_DIR" ); - char *uname = getenv( "USER" ); + char *name; + const char *dir = getenv( "FISHD_SOCKET_DIR" ); + char *uname = getenv( "USER" ); - if( dir == NULL ) - { - dir = "/tmp"; - } + if( dir == NULL ) + { + dir = "/tmp"; + } - if( uname == NULL ) - { - struct passwd *pw; - pw = getpwuid( getuid() ); - uname = strdup( pw->pw_name ); - } + if( uname == NULL ) + { + struct passwd *pw; + pw = getpwuid( getuid() ); + uname = strdup( pw->pw_name ); + } - name = (char *)malloc( strlen(dir)+ strlen(uname)+ strlen(SOCK_FILENAME) + 2 ); - if( name == NULL ) - { - wperror( L"get_socket_filename" ); - exit( EXIT_FAILURE ); - } - strcpy( name, dir ); - strcat( name, "/" ); - strcat( name, SOCK_FILENAME ); - strcat( name, uname ); + name = (char *)malloc( strlen(dir)+ strlen(uname)+ strlen(SOCK_FILENAME) + 2 ); + if( name == NULL ) + { + wperror( L"get_socket_filename" ); + exit( EXIT_FAILURE ); + } + strcpy( name, dir ); + strcat( name, "/" ); + strcat( name, SOCK_FILENAME ); + strcat( name, uname ); - if( strlen( name ) >= UNIX_PATH_MAX ) - { - debug( 1, L"Filename too long: '%s'", name ); - exit( EXIT_FAILURE ); - } - return name; + if( strlen( name ) >= UNIX_PATH_MAX ) + { + debug( 1, L"Filename too long: '%s'", name ); + exit( EXIT_FAILURE ); + } + return name; } /** - Signal handler for the term signal. + Signal handler for the term signal. */ static void handle_term( int signal ) { - quit=1; + quit=1; } @@ -214,24 +214,24 @@ static void handle_term( int signal ) */ static void sprint_rand_digits( char *str, int maxlen ) { - int i, max; - struct timeval tv; - - /* + int i, max; + struct timeval tv; + + /* Seed the pseudo-random generator based on time - this assumes that consecutive calls to gettimeofday will return different values and ignores errors returned by gettimeofday. Cast to unsigned so that wrapping occurs on overflow as per ANSI C. */ - (void)gettimeofday( &tv, NULL ); + (void)gettimeofday( &tv, NULL ); unsigned long long seed = tv.tv_sec + tv.tv_usec * 1000000ULL; - srand( (unsigned int)seed ); - max = (int)(1 + (maxlen - 1) * (rand() / (RAND_MAX + 1.0))); - for( i = 0; i < max; i++ ) - { - str[i] = '0' + 10 * (rand() / (RAND_MAX + 1.0)); - } - str[i] = 0; + srand( (unsigned int)seed ); + max = (int)(1 + (maxlen - 1) * (rand() / (RAND_MAX + 1.0))); + for( i = 0; i < max; i++ ) + { + str[i] = '0' + 10 * (rand() / (RAND_MAX + 1.0)); + } + str[i] = 0; } @@ -239,174 +239,174 @@ static void sprint_rand_digits( char *str, int maxlen ) Generate a filename unique in an NFS namespace by creating a copy of str and appending .{hostname}.{pid} to it. If gethostname() fails then a pseudo- random string is substituted for {hostname} - the randomness of the string - should be strong enough across different machines. The main assumption + should be strong enough across different machines. The main assumption though is that gethostname will not fail and this is just a "safe enough" fallback. The memory returned should be freed using free(). */ static std::string gen_unique_nfs_filename( const char *filename ) { - char hostname[HOST_NAME_MAX + 1]; + char hostname[HOST_NAME_MAX + 1]; char pid_str[256]; snprintf(pid_str, sizeof pid_str, "%ld", (long)getpid()); - - if ( gethostname( hostname, sizeof hostname ) != 0 ) + + if ( gethostname( hostname, sizeof hostname ) != 0 ) { - sprint_rand_digits( hostname, HOST_NAME_MAX ); - } - + sprint_rand_digits( hostname, HOST_NAME_MAX ); + } + std::string newname(filename); - newname.push_back('.'); + newname.push_back('.'); newname.append(hostname); newname.push_back('.'); - newname.append(pid_str); - return newname; + newname.append(pid_str); + return newname; } /** - The number of milliseconds to wait between polls when attempting to acquire + The number of milliseconds to wait between polls when attempting to acquire a lockfile */ #define LOCKPOLLINTERVAL 10 /** - Attempt to acquire a lock based on a lockfile, waiting LOCKPOLLINTERVAL - milliseconds between polls and timing out after timeout seconds, + Attempt to acquire a lock based on a lockfile, waiting LOCKPOLLINTERVAL + milliseconds between polls and timing out after timeout seconds, thereafter forcibly attempting to obtain the lock if force is non-zero. Returns 1 on success, 0 on failure. To release the lock the lockfile must be unlinked. - A unique temporary file named by appending characters to the lockfile name + A unique temporary file named by appending characters to the lockfile name is used; any pre-existing file of the same name is subject to deletion. */ static int acquire_lock_file( const char *lockfile, const int timeout, int force ) { - int fd, timed_out = 0; - int ret = 0; /* early exit returns failure */ - struct timespec pollint; - struct timeval start, end; - double elapsed; - struct stat statbuf; - - /* + int fd, timed_out = 0; + int ret = 0; /* early exit returns failure */ + struct timespec pollint; + struct timeval start, end; + double elapsed; + struct stat statbuf; + + /* (Re)create a unique file and check that it has one only link. */ - const std::string linkfile_str = gen_unique_nfs_filename( lockfile ); + const std::string linkfile_str = gen_unique_nfs_filename( lockfile ); const char * const linkfile = linkfile_str.c_str(); - (void)unlink( linkfile ); + (void)unlink( linkfile ); /* OK to not use CLO_EXEC here because fishd is single threaded */ - if( ( fd = open( linkfile, O_CREAT|O_RDONLY, 0600 ) ) == -1 ) - { - debug( 1, L"acquire_lock_file: open: %s", strerror( errno ) ); - goto done; - } - /* + if( ( fd = open( linkfile, O_CREAT|O_RDONLY, 0600 ) ) == -1 ) + { + debug( 1, L"acquire_lock_file: open: %s", strerror( errno ) ); + goto done; + } + /* Don't need to check exit status of close on read-only file descriptors */ - close( fd ); - if( stat( linkfile, &statbuf ) != 0 ) - { - debug( 1, L"acquire_lock_file: stat: %s", strerror( errno ) ); - goto done; - } - if ( statbuf.st_nlink != 1 ) - { - debug( 1, L"acquire_lock_file: number of hardlinks on unique " + close( fd ); + if( stat( linkfile, &statbuf ) != 0 ) + { + debug( 1, L"acquire_lock_file: stat: %s", strerror( errno ) ); + goto done; + } + if ( statbuf.st_nlink != 1 ) + { + debug( 1, L"acquire_lock_file: number of hardlinks on unique " L"tmpfile is %d instead of 1.", (int)statbuf.st_nlink ); - goto done; - } - if( gettimeofday( &start, NULL ) != 0 ) - { - debug( 1, L"acquire_lock_file: gettimeofday: %s", strerror( errno ) ); - goto done; - } - end = start; - pollint.tv_sec = 0; - pollint.tv_nsec = LOCKPOLLINTERVAL * 1000000; - do - { - /* - Try to create a hard link to the unique file from the - lockfile. This will only succeed if the lockfile does not - already exist. It is guaranteed to provide race-free - semantics over NFS which the alternative of calling - open(O_EXCL|O_CREAT) on the lockfile is not. The lock - succeeds if the call to link returns 0 or the link count on + goto done; + } + if( gettimeofday( &start, NULL ) != 0 ) + { + debug( 1, L"acquire_lock_file: gettimeofday: %s", strerror( errno ) ); + goto done; + } + end = start; + pollint.tv_sec = 0; + pollint.tv_nsec = LOCKPOLLINTERVAL * 1000000; + do + { + /* + Try to create a hard link to the unique file from the + lockfile. This will only succeed if the lockfile does not + already exist. It is guaranteed to provide race-free + semantics over NFS which the alternative of calling + open(O_EXCL|O_CREAT) on the lockfile is not. The lock + succeeds if the call to link returns 0 or the link count on the unique file increases to 2. */ - if( link( linkfile, lockfile ) == 0 || - ( stat( linkfile, &statbuf ) == 0 && + if( link( linkfile, lockfile ) == 0 || + ( stat( linkfile, &statbuf ) == 0 && statbuf.st_nlink == 2 ) ) - { - /* Successful lock */ - ret = 1; - break; - } - elapsed = end.tv_sec + end.tv_usec/1000000.0 - + { + /* Successful lock */ + ret = 1; + break; + } + elapsed = end.tv_sec + end.tv_usec/1000000.0 - ( start.tv_sec + start.tv_usec/1000000.0 ); - /* - The check for elapsed < 0 is to deal with the unlikely event + /* + The check for elapsed < 0 is to deal with the unlikely event that after the loop is entered the system time is set forward - past the loop's end time. This would otherwise result in a + past the loop's end time. This would otherwise result in a (practically) infinite loop. */ - if( timed_out || elapsed >= timeout || elapsed < 0 ) - { - if ( timed_out == 0 && force ) - { - /* + if( timed_out || elapsed >= timeout || elapsed < 0 ) + { + if ( timed_out == 0 && force ) + { + /* Timed out and force was specified - attempt to remove stale lock and try a final time */ - (void)unlink( lockfile ); - timed_out = 1; - continue; - } - else - { - /* - Timed out and final try was unsuccessful or + (void)unlink( lockfile ); + timed_out = 1; + continue; + } + else + { + /* + Timed out and final try was unsuccessful or force was not specified */ - debug( 1, L"acquire_lock_file: timed out " + debug( 1, L"acquire_lock_file: timed out " L"trying to obtain lockfile %s using " L"linkfile %s", lockfile, linkfile ); - break; - } - } - nanosleep( &pollint, NULL ); - } while( gettimeofday( &end, NULL ) == 0 ); + break; + } + } + nanosleep( &pollint, NULL ); + } while( gettimeofday( &end, NULL ) == 0 ); done: - /* The linkfile is not needed once the lockfile has been created */ - (void)unlink( linkfile ); - return ret; + /* The linkfile is not needed once the lockfile has been created */ + (void)unlink( linkfile ); + return ret; } /** Acquire the lock for the socket - Returns the name of the lock file if successful or + Returns the name of the lock file if successful or NULL if unable to obtain lock. - The returned string must be free()d after unlink()ing the file to release + The returned string must be free()d after unlink()ing the file to release the lock */ static char *acquire_socket_lock( const char *sock_name ) { - size_t len = strlen( sock_name ); - char *lockfile = (char *)malloc( len + strlen( LOCKPOSTFIX ) + 1 ); - - if( lockfile == NULL ) - { - wperror( L"acquire_socket_lock" ); - exit( EXIT_FAILURE ); - } - strcpy( lockfile, sock_name ); - strcpy( lockfile + len, LOCKPOSTFIX ); - if ( !acquire_lock_file( lockfile, LOCKTIMEOUT, 1 ) ) - { - free( lockfile ); - lockfile = NULL; - } - return lockfile; + size_t len = strlen( sock_name ); + char *lockfile = (char *)malloc( len + strlen( LOCKPOSTFIX ) + 1 ); + + if( lockfile == NULL ) + { + wperror( L"acquire_socket_lock" ); + exit( EXIT_FAILURE ); + } + strcpy( lockfile, sock_name ); + strcpy( lockfile + len, LOCKPOSTFIX ); + if ( !acquire_lock_file( lockfile, LOCKTIMEOUT, 1 ) ) + { + free( lockfile ); + lockfile = NULL; + } + return lockfile; } /** @@ -414,83 +414,83 @@ static char *acquire_socket_lock( const char *sock_name ) */ static int get_socket() { - int s, len, doexit = 0; - int exitcode = EXIT_FAILURE; - struct sockaddr_un local; - char *sock_name = get_socket_filename(); + int s, len, doexit = 0; + int exitcode = EXIT_FAILURE; + struct sockaddr_un local; + char *sock_name = get_socket_filename(); - /* - Start critical section protected by lock - */ - char *lockfile = acquire_socket_lock( sock_name ); - if( lockfile == NULL ) - { - debug( 0, L"Unable to obtain lock on socket, exiting" ); - exit( EXIT_FAILURE ); - } - debug( 4, L"Acquired lockfile: %s", lockfile ); - - local.sun_family = AF_UNIX; - strcpy( local.sun_path, sock_name ); - len = sizeof(local); - - debug(1, L"Connect to socket at %s", sock_name); - - if( ( s = socket( AF_UNIX, SOCK_STREAM, 0 ) ) == -1 ) - { - wperror( L"socket" ); - doexit = 1; - goto unlock; - } + /* + Start critical section protected by lock + */ + char *lockfile = acquire_socket_lock( sock_name ); + if( lockfile == NULL ) + { + debug( 0, L"Unable to obtain lock on socket, exiting" ); + exit( EXIT_FAILURE ); + } + debug( 4, L"Acquired lockfile: %s", lockfile ); - /* - First check whether the socket has been opened by another fishd; - if so, exit with success status - */ - if( connect( s, (struct sockaddr *)&local, len ) == 0 ) - { - debug( 1, L"Socket already exists, exiting" ); - doexit = 1; - exitcode = 0; - goto unlock; - } - - unlink( local.sun_path ); - if( bind( s, (struct sockaddr *)&local, len ) == -1 ) - { - wperror( L"bind" ); - doexit = 1; - goto unlock; - } + local.sun_family = AF_UNIX; + strcpy( local.sun_path, sock_name ); + len = sizeof(local); - if( fcntl( s, F_SETFL, O_NONBLOCK ) != 0 ) - { - wperror( L"fcntl" ); - close( s ); - doexit = 1; - } else if( listen( s, 64 ) == -1 ) - { - wperror( L"listen" ); - doexit = 1; - } + debug(1, L"Connect to socket at %s", sock_name); + + if( ( s = socket( AF_UNIX, SOCK_STREAM, 0 ) ) == -1 ) + { + wperror( L"socket" ); + doexit = 1; + goto unlock; + } + + /* + First check whether the socket has been opened by another fishd; + if so, exit with success status + */ + if( connect( s, (struct sockaddr *)&local, len ) == 0 ) + { + debug( 1, L"Socket already exists, exiting" ); + doexit = 1; + exitcode = 0; + goto unlock; + } + + unlink( local.sun_path ); + if( bind( s, (struct sockaddr *)&local, len ) == -1 ) + { + wperror( L"bind" ); + doexit = 1; + goto unlock; + } + + if( fcntl( s, F_SETFL, O_NONBLOCK ) != 0 ) + { + wperror( L"fcntl" ); + close( s ); + doexit = 1; + } else if( listen( s, 64 ) == -1 ) + { + wperror( L"listen" ); + doexit = 1; + } unlock: - (void)unlink( lockfile ); - debug( 4, L"Released lockfile: %s", lockfile ); - /* - End critical section protected by lock - */ - - free( lockfile ); - - free( sock_name ); + (void)unlink( lockfile ); + debug( 4, L"Released lockfile: %s", lockfile ); + /* + End critical section protected by lock + */ - if( doexit ) - { - exit( exitcode ); - } + free( lockfile ); - return s; + free( sock_name ); + + if( doexit ) + { + exit( exitcode ); + } + + return s; } /** @@ -498,29 +498,29 @@ unlock: */ static void broadcast( fish_message_type_t type, const wchar_t *key, const wchar_t *val ) { - connection_t *c; - message_t *msg; + connection_t *c; + message_t *msg; - if( !conn ) - return; - - msg = create_message( type, key, val ); - - /* - Don't merge these loops, or try_send_all can free the message - prematurely - */ - - for( c = conn; c; c=c->next ) - { - msg->count++; + if( !conn ) + return; + + msg = create_message( type, key, val ); + + /* + Don't merge these loops, or try_send_all can free the message + prematurely + */ + + for( c = conn; c; c=c->next ) + { + msg->count++; c->unsent->push(msg); - } - - for( c = conn; c; c=c->next ) - { - try_send_all( c ); - } + } + + for( c = conn; c; c=c->next ) + { + try_send_all( c ); + } } /** @@ -528,58 +528,58 @@ static void broadcast( fish_message_type_t type, const wchar_t *key, const wchar */ static void daemonize() { - /* - Fork, and let parent exit - */ - switch( fork() ) - { - case -1: - debug( 0, L"Could not put fishd in background. Quitting" ); - wperror( L"fork" ); - exit(1); - - case 0: - { - /* Ordinarily there's very limited things we will do after fork, due to multithreading. But fishd is safe because it's single threaded. So don't die in is_forked_child. */ - setup_fork_guards(); - - /* - Make fishd ignore the HUP signal. - */ - struct sigaction act; - sigemptyset( & act.sa_mask ); - act.sa_flags=0; - act.sa_handler=SIG_IGN; - sigaction( SIGHUP, &act, 0); + /* + Fork, and let parent exit + */ + switch( fork() ) + { + case -1: + debug( 0, L"Could not put fishd in background. Quitting" ); + wperror( L"fork" ); + exit(1); - /* - Make fishd save and exit on the TERM signal. - */ - sigfillset( & act.sa_mask ); - act.sa_flags=0; - act.sa_handler=&handle_term; - sigaction( SIGTERM, &act, 0); - break; - - } - - default: - { - debug( 0, L"Parent process exiting (This is normal)" ); - exit(0); - } - } - - /* - Put ourself in out own processing group - */ - setsid(); - - /* - Close stdin and stdout. We only use stderr, anyway. - */ - close( 0 ); - close( 1 ); + case 0: + { + /* Ordinarily there's very limited things we will do after fork, due to multithreading. But fishd is safe because it's single threaded. So don't die in is_forked_child. */ + setup_fork_guards(); + + /* + Make fishd ignore the HUP signal. + */ + struct sigaction act; + sigemptyset( & act.sa_mask ); + act.sa_flags=0; + act.sa_handler=SIG_IGN; + sigaction( SIGHUP, &act, 0); + + /* + Make fishd save and exit on the TERM signal. + */ + sigfillset( & act.sa_mask ); + act.sa_flags=0; + act.sa_handler=&handle_term; + sigaction( SIGTERM, &act, 0); + break; + + } + + default: + { + debug( 0, L"Parent process exiting (This is normal)" ); + exit(0); + } + } + + /* + Put ourself in out own processing group + */ + setsid(); + + /* + Close stdin and stdout. We only use stderr, anyway. + */ + close( 0 ); + close( 1 ); } @@ -588,23 +588,23 @@ static void daemonize() */ static wchar_t *fishd_env_get( const wchar_t *key ) { - char *nres, *nkey; - wchar_t *res; - - nkey = wcs2str( key ); - nres = getenv( nkey ); - free( nkey ); - if( nres ) - { - return str2wcs( nres ); - } - else - { - res = env_universal_common_get( key ); - if( res ) - res = wcsdup( res ); - return res; - } + char *nres, *nkey; + wchar_t *res; + + nkey = wcs2str( key ); + nres = getenv( nkey ); + free( nkey ); + if( nres ) + { + return str2wcs( nres ); + } + else + { + res = env_universal_common_get( key ); + if( res ) + res = wcsdup( res ); + return res; + } } /** @@ -615,42 +615,42 @@ static wchar_t *fishd_env_get( const wchar_t *key ) */ static wcstring fishd_get_config() { - wchar_t *xdg_dir, *home; - bool done = false; - wcstring result; - - xdg_dir = fishd_env_get( L"XDG_CONFIG_HOME" ); - if (xdg_dir) - { + wchar_t *xdg_dir, *home; + bool done = false; + wcstring result; + + xdg_dir = fishd_env_get( L"XDG_CONFIG_HOME" ); + if (xdg_dir) + { result = xdg_dir; append_path_component(result, L"/fish"); - if (!create_directory(result)) - { - done = true; - } - free(xdg_dir); - } - else - { - home = fishd_env_get( L"HOME" ); - if( home ) - { + if (!create_directory(result)) + { + done = true; + } + free(xdg_dir); + } + else + { + home = fishd_env_get( L"HOME" ); + if( home ) + { result = home; append_path_component(result, L"/.config/fish"); - if (!create_directory(result)) - { - done = 1; - } - free( home ); - } - } - + if (!create_directory(result)) + { + done = 1; + } + free( home ); + } + } + if (! done) { /* Bad juju */ - 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." )); - result.clear(); + 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." )); + result.clear(); } - + return result; } @@ -659,51 +659,51 @@ static wcstring fishd_get_config() */ static void load_or_save( int save) { - const wcstring wdir = fishd_get_config(); - char hostname[HOSTNAME_LEN]; - connection_t c; - int fd; - - if (wdir.empty()) - return; - - std::string dir = wcs2string( wdir ); - - gethostname( hostname, HOSTNAME_LEN ); - + const wcstring wdir = fishd_get_config(); + char hostname[HOSTNAME_LEN]; + connection_t c; + int fd; + + if (wdir.empty()) + return; + + std::string dir = wcs2string( wdir ); + + gethostname( hostname, HOSTNAME_LEN ); + std::string name; name.append(dir); name.append("/"); name.append(FILE); name.append(hostname); - - debug( 4, L"Open file for %s: '%s'", - save?"saving":"loading", - name.c_str() ); - + + debug( 4, L"Open file for %s: '%s'", + save?"saving":"loading", + name.c_str() ); + /* OK to not use CLO_EXEC here because fishd is single threaded */ - fd = open(name.c_str(), save?(O_CREAT | O_TRUNC | O_WRONLY):O_RDONLY, 0600); - - if( fd == -1 ) - { - debug( 1, L"Could not open load/save file. No previous saves?" ); - wperror( L"open" ); - return; - } - debug( 4, L"File open on fd %d", c.fd ); + fd = open(name.c_str(), save?(O_CREAT | O_TRUNC | O_WRONLY):O_RDONLY, 0600); - connection_init( &c, fd ); + if( fd == -1 ) + { + debug( 1, L"Could not open load/save file. No previous saves?" ); + wperror( L"open" ); + return; + } + debug( 4, L"File open on fd %d", c.fd ); - if( save ) - { - - write_loop( c.fd, SAVE_MSG, strlen(SAVE_MSG) ); - enqueue_all( &c ); - } - else - read_message( &c ); + connection_init( &c, fd ); - connection_destroy( &c ); + if( save ) + { + + write_loop( c.fd, SAVE_MSG, strlen(SAVE_MSG) ); + enqueue_all( &c ); + } + else + read_message( &c ); + + connection_destroy( &c ); } /** @@ -711,7 +711,7 @@ static void load_or_save( int save) */ static void load() { - load_or_save(0); + load_or_save(0); } /** @@ -719,7 +719,7 @@ static void load() */ static void save() { - load_or_save(1); + load_or_save(1); } /** @@ -728,11 +728,11 @@ static void save() static void init() { - sock = get_socket(); - daemonize(); - env_universal_common_init( &broadcast ); - - load(); + sock = get_socket(); + daemonize(); + env_universal_common_init( &broadcast ); + + load(); } /** @@ -740,220 +740,220 @@ static void init() */ int main( int argc, char ** argv ) { - int child_socket; - struct sockaddr_un remote; - socklen_t t; - int max_fd; - int update_count=0; - - fd_set read_fd, write_fd; + int child_socket; + struct sockaddr_un remote; + socklen_t t; + int max_fd; + int update_count=0; - set_main_thread(); + fd_set read_fd, write_fd; + + set_main_thread(); setup_fork_guards(); - - program_name=L"fishd"; - wsetlocale( LC_ALL, L"" ); - /* - Parse options - */ - while( 1 ) - { - static struct option - long_options[] = - { - { - "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; + program_name=L"fishd"; + wsetlocale( LC_ALL, L"" ); - case 'h': - print_help( argv[0], 1 ); - exit(0); - - case 'v': - debug( 0, L"%ls, version %s\n", program_name, PACKAGE_VERSION ); - exit( 0 ); - - case '?': - return 1; - - } - } - - init(); - while(1) - { - connection_t *c; - int res; + /* + Parse options + */ + while( 1 ) + { + static struct option + long_options[] = + { + { + "help", no_argument, 0, 'h' + } + , + { + "version", no_argument, 0, 'v' + } + , + { + 0, 0, 0, 0 + } + } + ; - t = sizeof( remote ); - - FD_ZERO( &read_fd ); - FD_ZERO( &write_fd ); - FD_SET( sock, &read_fd ); - max_fd = sock+1; - for( c=conn; c; c=c->next ) - { - FD_SET( c->fd, &read_fd ); - max_fd = maxi( max_fd, c->fd+1); - - if( ! c->unsent->empty() ) - { - FD_SET( c->fd, &write_fd ); - } - } + int opt_index = 0; - while( 1 ) - { - res=select( max_fd, &read_fd, &write_fd, 0, 0 ); + int opt = getopt_long( argc, + argv, + GETOPT_STRING, + long_options, + &opt_index ); - if( quit ) - { - save(); - exit(0); - } - - if( res != -1 ) - break; - - if( errno != EINTR ) - { - wperror( L"select" ); - exit(1); - } - } - - if( FD_ISSET( sock, &read_fd ) ) - { - if( (child_socket = - accept( sock, - (struct sockaddr *)&remote, - &t) ) == -1) { - wperror( L"accept" ); - exit(1); - } - else - { - debug( 4, L"Connected with new child on fd %d", child_socket ); + if( opt == -1 ) + break; - if( fcntl( child_socket, F_SETFL, O_NONBLOCK ) != 0 ) - { - wperror( L"fcntl" ); - close( child_socket ); - } - else - { - connection_t *newc = (connection_t *)malloc( sizeof(connection_t)); - connection_init( newc, child_socket ); - newc->next = conn; - send( newc->fd, GREETING, strlen(GREETING), MSG_DONTWAIT ); - enqueue_all( newc ); - conn=newc; - } - } - } - - for( c=conn; c; c=c->next ) - { - if( FD_ISSET( c->fd, &write_fd ) ) - { - try_send_all( c ); - } - } - - for( c=conn; c; c=c->next ) - { - if( FD_ISSET( c->fd, &read_fd ) ) - { - read_message( c ); + switch( opt ) + { + case 0: + break; - /* - Occasionally we save during normal use, so that we - won't lose everything on a system crash - */ - update_count++; - if( update_count >= 64 ) - { - save(); - update_count = 0; - } - } - } - - connection_t *prev=0; - c=conn; - - while( c ) - { - if( c->killme ) - { - debug( 4, L"Close connection %d", c->fd ); + case 'h': + print_help( argv[0], 1 ); + exit(0); - while( ! c->unsent->empty() ) - { - message_t *msg = c->unsent->front(); + case 'v': + debug( 0, L"%ls, version %s\n", program_name, PACKAGE_VERSION ); + exit( 0 ); + + case '?': + return 1; + + } + } + + init(); + while(1) + { + connection_t *c; + int res; + + t = sizeof( remote ); + + FD_ZERO( &read_fd ); + FD_ZERO( &write_fd ); + FD_SET( sock, &read_fd ); + max_fd = sock+1; + for( c=conn; c; c=c->next ) + { + FD_SET( c->fd, &read_fd ); + max_fd = maxi( max_fd, c->fd+1); + + if( ! c->unsent->empty() ) + { + FD_SET( c->fd, &write_fd ); + } + } + + while( 1 ) + { + res=select( max_fd, &read_fd, &write_fd, 0, 0 ); + + if( quit ) + { + save(); + exit(0); + } + + if( res != -1 ) + break; + + if( errno != EINTR ) + { + wperror( L"select" ); + exit(1); + } + } + + if( FD_ISSET( sock, &read_fd ) ) + { + if( (child_socket = + accept( sock, + (struct sockaddr *)&remote, + &t) ) == -1) { + wperror( L"accept" ); + exit(1); + } + else + { + debug( 4, L"Connected with new child on fd %d", child_socket ); + + if( fcntl( child_socket, F_SETFL, O_NONBLOCK ) != 0 ) + { + wperror( L"fcntl" ); + close( child_socket ); + } + else + { + connection_t *newc = (connection_t *)malloc( sizeof(connection_t)); + connection_init( newc, child_socket ); + newc->next = conn; + send( newc->fd, GREETING, strlen(GREETING), MSG_DONTWAIT ); + enqueue_all( newc ); + conn=newc; + } + } + } + + for( c=conn; c; c=c->next ) + { + if( FD_ISSET( c->fd, &write_fd ) ) + { + try_send_all( c ); + } + } + + for( c=conn; c; c=c->next ) + { + if( FD_ISSET( c->fd, &read_fd ) ) + { + read_message( c ); + + /* + Occasionally we save during normal use, so that we + won't lose everything on a system crash + */ + update_count++; + if( update_count >= 64 ) + { + save(); + update_count = 0; + } + } + } + + connection_t *prev=0; + c=conn; + + while( c ) + { + if( c->killme ) + { + debug( 4, L"Close connection %d", c->fd ); + + while( ! c->unsent->empty() ) + { + message_t *msg = c->unsent->front(); c->unsent->pop(); - msg->count--; - if( !msg->count ) - free( msg ); - } - - connection_destroy( c ); - if( prev ) - { - prev->next=c->next; - } - else - { - conn=c->next; - } - - free(c); - - c=(prev?prev->next:conn); - - } - else - { - prev=c; - c=c->next; - } - } + msg->count--; + if( !msg->count ) + free( msg ); + } - if( !conn ) - { - debug( 0, L"No more clients. Quitting" ); - save(); - env_universal_common_destroy(); - break; - } + connection_destroy( c ); + if( prev ) + { + prev->next=c->next; + } + else + { + conn=c->next; + } - } + free(c); + + c=(prev?prev->next:conn); + + } + else + { + prev=c; + c=c->next; + } + } + + if( !conn ) + { + debug( 0, L"No more clients. Quitting" ); + save(); + env_universal_common_destroy(); + break; + } + + } } diff --git a/function.cpp b/function.cpp index 551959d53..b1cdcdb2b 100644 --- a/function.cpp +++ b/function.cpp @@ -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 &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; itok_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::const_iterator iter = data.events.begin(); iter != data.events.end(); ++iter ) - { - event_add_handler( &*iter ); - } + for( std::vector::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 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; diff --git a/function.h b/function.h index 42747912c..cfd39de25 100644 --- a/function.h +++ b/function.h @@ -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 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 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 ); diff --git a/highlight.cpp b/highlight.cpp index 7f73909bc..0cc765029 100644 --- a/highlight.cpp +++ b/highlight.cpp @@ -1,5 +1,5 @@ /** \file highlight.c - Functions for syntax highlighting + Functions for syntax highlighting */ #include "config.h" @@ -45,21 +45,21 @@ static void highlight_universal_internal( const wcstring &buff, std::vector /** The environment variables used to specify the color of different tokens. */ -static const wchar_t * const highlight_var[] = +static const wchar_t * const highlight_var[] = { - L"fish_color_normal", - L"fish_color_error", - L"fish_color_command", - L"fish_color_end", - L"fish_color_param", - L"fish_color_comment", - L"fish_color_match", - L"fish_color_search_match", - L"fish_color_operator", - L"fish_color_escape", - L"fish_color_quote", - L"fish_color_redirection", - L"fish_color_valid_path", + L"fish_color_normal", + L"fish_color_error", + L"fish_color_command", + L"fish_color_end", + L"fish_color_param", + L"fish_color_comment", + L"fish_color_match", + L"fish_color_search_match", + L"fish_color_operator", + L"fish_color_escape", + L"fish_color_quote", + L"fish_color_redirection", + L"fish_color_valid_path", L"fish_color_autosuggestion" }; @@ -67,7 +67,7 @@ static const wchar_t * const highlight_var[] = static wcstring apply_working_directory(const wcstring &path, const wcstring &working_directory) { if (path.empty() || working_directory.empty()) return path; - + /* We're going to make sure that if we want to prepend the wd, that the string has no leading / */ bool prepend_wd; switch (path.at(0)) { @@ -79,7 +79,7 @@ static wcstring apply_working_directory(const wcstring &path, const wcstring &wo prepend_wd = true; break; } - + if (! prepend_wd) { /* No need to prepend the wd, so just return the path we were given */ return path; @@ -89,12 +89,12 @@ static wcstring apply_working_directory(const wcstring &path, const wcstring &wo if (string_prefixes_string(L"./", path_component)) { path_component.erase(0, 2); } - + /* Removing leading /s */ while (string_prefixes_string(L"/", path_component)) { path_component.erase(0, 1); } - + /* Construct and return a new path */ wcstring new_path = working_directory; append_path_component(new_path, path_component); @@ -131,18 +131,18 @@ bool fs_is_case_insensitive(const wcstring &path, int fd, case_sensitivity_cache bool is_potential_path(const wcstring &const_path, const wcstring_list_t &directories, path_flags_t flags, wcstring *out_path) { ASSERT_IS_BACKGROUND_THREAD(); - + const bool require_dir = !! (flags & PATH_REQUIRE_DIR); wcstring clean_path; - int has_magic = 0; - bool result = false; - + int has_magic = 0; + bool result = false; + wcstring path(const_path); if (flags & PATH_EXPAND_TILDE) expand_tilde(path); - - // debug( 1, L"%ls -> %ls ->%ls", path, tilde, unescaped ); - + + // debug( 1, L"%ls -> %ls ->%ls", path, tilde, unescaped ); + for( size_t i=0; i < path.size(); i++) { wchar_t c = path.at(i); @@ -159,45 +159,45 @@ bool is_potential_path(const wcstring &const_path, const wcstring_list_t &direct case ANY_STRING_RECURSIVE: { has_magic = 1; - break; + break; } - + case INTERNAL_SEPARATOR: { break; } - + default: { clean_path.push_back(c); break; } - + } - + } - + if( ! has_magic && ! clean_path.empty() ) { /* Don't test the same path multiple times, which can happen if the path is absolute and the CDPATH contains multiple entries */ std::set checked_paths; - + /* Keep a cache of which paths / filesystems are case sensitive */ case_sensitivity_cache_t case_sensitivity_cache; - + for (size_t wd_idx = 0; wd_idx < directories.size() && ! result; wd_idx++) { const wcstring &wd = directories.at(wd_idx); - + const wcstring abs_path = apply_working_directory(clean_path, wd); - + /* Skip this if it's empty or we've already checked it */ if (abs_path.empty() || checked_paths.count(abs_path)) continue; checked_paths.insert(abs_path); - + /* If we end with a slash, then it must be a directory */ bool must_be_full_dir = abs_path.at(abs_path.size()-1) == L'/'; - if (must_be_full_dir) + if (must_be_full_dir) { struct stat buf; if (0 == wstat(abs_path, &buf) && S_ISDIR(buf.st_mode)) { @@ -210,7 +210,7 @@ bool is_potential_path(const wcstring &const_path, const wcstring_list_t &direct else { DIR *dir = NULL; - + /* We do not end with a slash; it does not have to be a directory */ const wcstring dir_name = wdirname(abs_path); const wcstring base_name = wbasename(abs_path); @@ -223,14 +223,14 @@ bool is_potential_path(const wcstring &const_path, const wcstring_list_t &direct else if ((dir = wopendir(dir_name))) { // We opened the dir_name; look for a string where the base name prefixes it wcstring ent; - + // Check if we're case insensitive bool case_insensitive = fs_is_case_insensitive(dir_name, dirfd(dir), case_sensitivity_cache); - + // Don't ask for the is_dir value unless we care, because it can cause extra filesystem acces */ bool is_dir = false; while (wreaddir_resolving(dir, dir_name, ent, require_dir ? &is_dir : NULL)) - { + { /* Determine which function to call to check for prefixes */ bool (*prefix_func)(const wcstring &, const wcstring &); @@ -245,11 +245,11 @@ bool is_potential_path(const wcstring &const_path, const wcstring_list_t &direct result = true; if (out_path) { /* We want to return the path in the same "form" as it was given. Take the given path, get its basename. Append that to the output if the basename actually prefixes the path (which it won't if the given path contains no slashes), and isn't a slash (so we don't duplicate slashes). Then append the directory entry. */ - + out_path->clear(); const wcstring path_base = wdirname(const_path); - - + + if (prefix_func(path_base, const_path)) { out_path->append(path_base); if (! string_suffixes_string(L"/", *out_path)) @@ -276,7 +276,7 @@ bool is_potential_path(const wcstring &const_path, const wcstring_list_t &direct static bool is_potential_cd_path(const wcstring &path, const wcstring &working_directory, path_flags_t flags, wcstring *out_path) { wcstring_list_t directories; - + if (string_prefixes_string(L"./", path)) { /* Ignore the CDPATH in this case; just use the working directory */ directories.push_back(working_directory); @@ -285,7 +285,7 @@ static bool is_potential_cd_path(const wcstring &path, const wcstring &working_d env_var_t cdpath = env_get_string(L"CDPATH"); if (cdpath.missing_or_empty()) cdpath = L"."; - + /* Tokenize it into directories */ wcstokenizer tokenizer(cdpath, ARRAY_SEP_STR); wcstring next_path; @@ -295,7 +295,7 @@ static bool is_potential_cd_path(const wcstring &path, const wcstring &working_d directories.push_back(apply_working_directory(next_path, working_directory)); } } - + /* Call is_potential_path with all of these directories */ bool result = is_potential_path(path, directories, flags | PATH_REQUIRE_DIR, out_path); #if 0 @@ -308,49 +308,49 @@ static bool is_potential_cd_path(const wcstring &path, const wcstring &working_d rgb_color_t highlight_get_color( int highlight, bool is_background ) { - size_t idx=0; - rgb_color_t result; + size_t idx=0; + rgb_color_t result; - if( highlight < 0 ) - return rgb_color_t::normal(); - if( highlight > (1< (1< %d -> %ls", highlight, idx, val ); - - if (val_wstr.missing()) - val_wstr = env_get_string( highlight_var[0]); - - if( ! val_wstr.missing() ) - result = parse_color( val_wstr, is_background ); - - if( highlight & HIGHLIGHT_VALID_PATH ) - { - env_var_t val2_wstr = env_get_string( L"fish_color_valid_path" ); - const wcstring val2 = val2_wstr.missing() ? L"" : val2_wstr.c_str(); + env_var_t val_wstr = env_get_string( highlight_var[idx]); - rgb_color_t result2 = parse_color( val2, is_background ); - if( result.is_normal() ) - result = result2; - else - { - if( result2.is_bold() ) - result.set_bold(true); - if( result2.is_underline() ) - result.set_underline(true); - } - } - return result; +// debug( 1, L"%d -> %d -> %ls", highlight, idx, val ); + + if (val_wstr.missing()) + val_wstr = env_get_string( highlight_var[0]); + + if( ! val_wstr.missing() ) + result = parse_color( val_wstr, is_background ); + + if( highlight & HIGHLIGHT_VALID_PATH ) + { + env_var_t val2_wstr = env_get_string( L"fish_color_valid_path" ); + const wcstring val2 = val2_wstr.missing() ? L"" : val2_wstr.c_str(); + + rgb_color_t result2 = parse_color( val2, is_background ); + if( result.is_normal() ) + result = result2; + else + { + if( result2.is_bold() ) + result.set_bold(true); + if( result2.is_underline() ) + result.set_underline(true); + } + } + return result; } @@ -360,301 +360,301 @@ rgb_color_t highlight_get_color( int highlight, bool is_background ) static void highlight_param( const wcstring &buffstr, std::vector &colors, wcstring_list_t *error ) { const wchar_t * const buff = buffstr.c_str(); - enum {e_unquoted, e_single_quoted, e_double_quoted} mode = e_unquoted; - size_t in_pos, len = buffstr.size(); - int bracket_count=0; - int normal_status = colors.at(0); - - for (in_pos=0; in_pos^ \\#;|&", buff[in_pos] ) ) - { - colors.at(start_pos)=HIGHLIGHT_ESCAPE; - colors.at(in_pos+1)=normal_status; - } - else if( wcschr( L"c", buff[in_pos] ) ) + case e_unquoted: + { + if( c == L'\\' ) + { + size_t start_pos = in_pos; + in_pos++; + + if( wcschr( L"~%", buff[in_pos] ) ) + { + if( in_pos == 1 ) + { + colors.at(start_pos) = HIGHLIGHT_ESCAPE; + colors.at(in_pos+1) = normal_status; + } + } + else if( buff[in_pos]==L',' ) + { + if( bracket_count ) + { + colors.at(start_pos) = HIGHLIGHT_ESCAPE; + colors.at(in_pos+1) = normal_status; + } + } + else if( wcschr( L"abefnrtv*?$(){}[]'\"<>^ \\#;|&", buff[in_pos] ) ) + { + colors.at(start_pos)=HIGHLIGHT_ESCAPE; + colors.at(in_pos+1)=normal_status; + } + else if( wcschr( L"c", buff[in_pos] ) ) { - colors.at(start_pos)=HIGHLIGHT_ESCAPE; + colors.at(start_pos)=HIGHLIGHT_ESCAPE; if (in_pos+2 < colors.size()) colors.at(in_pos+2)=normal_status; - } - else if( wcschr( L"uUxX01234567", buff[in_pos] ) ) - { - int i; - long long res=0; - int chars=2; - int base=16; - - wchar_t max_val = ASCII_MAX; - - switch( buff[in_pos] ) - { - case L'u': - { - chars=4; - max_val = UCS2_MAX; - break; - } - - case L'U': - { - chars=8; - max_val = WCHAR_MAX; - break; - } - - case L'x': - { - break; - } - - case L'X': - { - max_val = BYTE_MAX; - break; - } - - default: - { - base=8; - chars=3; - in_pos--; - break; - } - } - - for( i=0; i= EXPAND_RESERVED && - *str <= EXPAND_RESERVED_END ) - { - return 1; - } - str++; - } - return 0; + while( *str ) + { + if( *str >= EXPAND_RESERVED && + *str <= EXPAND_RESERVED_END ) + { + return 1; + } + str++; + } + return 0; } /* Parse a command line. Return by reference the last command, its arguments, and the offset in the string of the beginning of the last argument. This is used by autosuggestions */ @@ -662,17 +662,17 @@ static bool autosuggest_parse_command(const wcstring &str, wcstring *out_command { if (str.empty()) return false; - + wcstring cmd; wcstring_list_t args; int arg_pos = -1; - + bool had_cmd = false; tokenizer tok; for (tok_init( &tok, str.c_str(), TOK_ACCEPT_UNFINISHED | TOK_SQUASH_ERRORS); tok_has_next(&tok); tok_next(&tok)) { int last_type = tok_last_type(&tok); - + switch( last_type ) { case TOK_STRING: @@ -684,7 +684,7 @@ static bool autosuggest_parse_command(const wcstring &str, wcstring *out_command arg_pos = tok_get_pos(&tok); } else - { + { /* Command. First check that the command actually exists. */ wcstring local_cmd = tok_last( &tok ); bool expanded = expand_one(cmd, EXPAND_SKIP_CMDSUBST | EXPAND_SKIP_VARIABLES); @@ -696,12 +696,12 @@ static bool autosuggest_parse_command(const wcstring &str, wcstring *out_command { bool is_subcommand = false; int mark = tok_get_pos(&tok); - + if (parser_keywords_is_subcommand(cmd)) { int sw; tok_next( &tok ); - + sw = parser_keywords_is_switch( tok_last( &tok ) ); if( !parser_keywords_is_block( cmd ) && sw == ARG_SWITCH ) @@ -711,12 +711,12 @@ static bool autosuggest_parse_command(const wcstring &str, wcstring *out_command else { if( sw == ARG_SKIP ) - mark = tok_get_pos( &tok ); + mark = tok_get_pos( &tok ); is_subcommand = true; } tok_set_pos( &tok, mark ); } - + if (!is_subcommand) { /* It's really a command */ @@ -724,11 +724,11 @@ static bool autosuggest_parse_command(const wcstring &str, wcstring *out_command cmd = local_cmd; } } - + } break; } - + case TOK_REDIRECT_NOCLOB: case TOK_REDIRECT_OUT: case TOK_REDIRECT_IN: @@ -739,10 +739,10 @@ static bool autosuggest_parse_command(const wcstring &str, wcstring *out_command { break; } - tok_next( &tok ); + tok_next( &tok ); break; } - + case TOK_PIPE: case TOK_BACKGROUND: case TOK_END: @@ -753,17 +753,17 @@ static bool autosuggest_parse_command(const wcstring &str, wcstring *out_command arg_pos = -1; break; } - - case TOK_COMMENT: + + case TOK_COMMENT: case TOK_ERROR: default: { - break; - } + break; + } } } tok_destroy( &tok ); - + /* Remember our command if we have one */ if (had_cmd) { if (out_command) out_command->swap(cmd); @@ -778,39 +778,39 @@ static bool autosuggest_parse_command(const wcstring &str, wcstring *out_command bool autosuggest_suggest_special(const wcstring &str, const wcstring &working_directory, wcstring &outSuggestion) { if (str.empty()) return false; - + ASSERT_IS_BACKGROUND_THREAD(); - + /* Parse the string */ wcstring parsed_command; wcstring_list_t parsed_arguments; int parsed_last_arg_pos = -1; if (! autosuggest_parse_command(str, &parsed_command, &parsed_arguments, &parsed_last_arg_pos)) return false; - + bool result = false; - if (parsed_command == L"cd" && ! parsed_arguments.empty()) { + if (parsed_command == L"cd" && ! parsed_arguments.empty()) { /* We can possibly handle this specially */ const wcstring escaped_dir = parsed_arguments.back(); wcstring suggested_path; - + /* We always return true because we recognized the command. This prevents us from falling back to dumber algorithms; for example we won't suggest a non-directory for the cd command. */ result = true; outSuggestion.clear(); - + /* Unescape the parameter */ wcstring unescaped_dir = escaped_dir; bool unescaped = unescape_string(unescaped_dir, UNESCAPE_INCOMPLETE); - + /* Determine the quote type we got from the input directory. */ wchar_t quote = L'\0'; parse_util_get_parameter_info(escaped_dir, 0, "e, NULL, NULL); - + /* Big hack to avoid expanding a tilde inside quotes */ path_flags_t path_flags = (quote == L'\0') ? PATH_EXPAND_TILDE : 0; if (unescaped && is_potential_cd_path(unescaped_dir, working_directory, path_flags, &suggested_path)) { - + /* Note: this looks really wrong for strings that have an "unescapable" character in them, e.g. a \t, because parse_util_escape_string_with_quote will insert that character */ wcstring escaped_suggested_path = parse_util_escape_string_with_quote(suggested_path, quote); @@ -832,14 +832,14 @@ bool autosuggest_validate_from_history(const history_item_t &item, file_detectio bool handled = false, suggestionOK = false; - /* Parse the string */ + /* Parse the string */ wcstring parsed_command; wcstring_list_t parsed_arguments; int parsed_last_arg_pos = -1; if (! autosuggest_parse_command(item.str(), &parsed_command, &parsed_arguments, &parsed_last_arg_pos)) return false; - if (parsed_command == L"cd" && ! parsed_arguments.empty()) { + if (parsed_command == L"cd" && ! parsed_arguments.empty()) { /* We can possibly handle this specially */ wcstring dir = parsed_arguments.back(); if (expand_one(dir, EXPAND_SKIP_CMDSUBST)) @@ -860,9 +860,9 @@ bool autosuggest_validate_from_history(const history_item_t &item, file_detectio suggestionOK = true; } } - } - } - + } + } + /* If not handled specially, handle it here */ if (! handled) { bool cmd_ok = false; @@ -894,369 +894,369 @@ bool autosuggest_validate_from_history(const history_item_t &item, file_detectio // This function does I/O static void tokenize( const wchar_t * const buff, std::vector &color, const size_t pos, wcstring_list_t *error, const wcstring &working_directory, const env_vars_snapshot_t &vars) { ASSERT_IS_BACKGROUND_THREAD(); - - wcstring cmd; - int had_cmd=0; - wcstring last_cmd; - int accept_switches = 1; - - int use_function = 1; - int use_command = 1; - int use_builtin = 1; - - CHECK( buff, ); + wcstring cmd; + int had_cmd=0; + wcstring last_cmd; + + int accept_switches = 1; + + int use_function = 1; + int use_command = 1; + int use_builtin = 1; + + CHECK( buff, ); if (buff[0] == L'\0') return; - std::fill(color.begin(), color.end(), -1); + std::fill(color.begin(), color.end(), -1); tokenizer tok; - for( tok_init( &tok, buff, TOK_SHOW_COMMENTS | TOK_SQUASH_ERRORS ); - tok_has_next( &tok ); - tok_next( &tok ) ) - { - int last_type = tok_last_type( &tok ); - - switch( last_type ) - { - case TOK_STRING: - { - if( had_cmd ) - { - - /*Parameter */ - wchar_t *param = tok_last( &tok ); - if( param[0] == L'-' ) - { - if (wcscmp( param, L"--" ) == 0 ) - { - accept_switches = 0; - color.at(tok_get_pos( &tok )) = HIGHLIGHT_PARAM; - } - else if( accept_switches ) - { - if( complete_is_valid_option( last_cmd, param, error, false /* no autoload */ ) ) - color.at(tok_get_pos( &tok )) = HIGHLIGHT_PARAM; - else - color.at(tok_get_pos( &tok )) = HIGHLIGHT_ERROR; - } - else - { - color.at(tok_get_pos( &tok )) = HIGHLIGHT_PARAM; - } - } - else - { - color.at(tok_get_pos( &tok )) = HIGHLIGHT_PARAM; - } + for( tok_init( &tok, buff, TOK_SHOW_COMMENTS | TOK_SQUASH_ERRORS ); + tok_has_next( &tok ); + tok_next( &tok ) ) + { + int last_type = tok_last_type( &tok ); - if( cmd == L"cd" ) - { + switch( last_type ) + { + case TOK_STRING: + { + if( had_cmd ) + { + + /*Parameter */ + wchar_t *param = tok_last( &tok ); + if( param[0] == L'-' ) + { + if (wcscmp( param, L"--" ) == 0 ) + { + accept_switches = 0; + color.at(tok_get_pos( &tok )) = HIGHLIGHT_PARAM; + } + else if( accept_switches ) + { + if( complete_is_valid_option( last_cmd, param, error, false /* no autoload */ ) ) + color.at(tok_get_pos( &tok )) = HIGHLIGHT_PARAM; + else + color.at(tok_get_pos( &tok )) = HIGHLIGHT_ERROR; + } + else + { + color.at(tok_get_pos( &tok )) = HIGHLIGHT_PARAM; + } + } + else + { + color.at(tok_get_pos( &tok )) = HIGHLIGHT_PARAM; + } + + if( cmd == L"cd" ) + { wcstring dir = tok_last( &tok ); if (expand_one(dir, EXPAND_SKIP_CMDSUBST)) - { - int is_help = string_prefixes_string(dir, L"--help") || string_prefixes_string(dir, L"-h"); - if( !is_help && ! is_potential_cd_path(dir, working_directory, PATH_EXPAND_TILDE, NULL)) - { - color.at(tok_get_pos( &tok )) = HIGHLIGHT_ERROR; - } - } - } - + { + int is_help = string_prefixes_string(dir, L"--help") || string_prefixes_string(dir, L"-h"); + if( !is_help && ! is_potential_cd_path(dir, working_directory, PATH_EXPAND_TILDE, NULL)) + { + color.at(tok_get_pos( &tok )) = HIGHLIGHT_ERROR; + } + } + } + /* Highlight the parameter. highlight_param wants to write one more color than we have characters (hysterical raisins) so allocate one more in the vector. But don't copy it back. */ const wcstring param_str = param; size_t tok_pos = tok_get_pos(&tok); - + std::vector::const_iterator where = color.begin() + tok_pos; std::vector subcolors(where, where + param_str.size()); subcolors.push_back(-1); highlight_param(param_str, subcolors, error); - + /* Copy the subcolors back into our colors array */ std::copy(subcolors.begin(), subcolors.begin() + param_str.size(), color.begin() + tok_pos); - } - else - { - /* - Command. First check that the command actually exists. - */ + } + else + { + /* + Command. First check that the command actually exists. + */ cmd = tok_last( &tok ); bool expanded = expand_one(cmd, EXPAND_SKIP_CMDSUBST | EXPAND_SKIP_VARIABLES | EXPAND_SKIP_JOBS); - if (! expanded || has_expand_reserved(cmd.c_str())) - { - color.at(tok_get_pos( &tok )) = HIGHLIGHT_ERROR; - } - else - { - bool is_cmd = false; - int is_subcommand = 0; - int mark = tok_get_pos( &tok ); - color.at(tok_get_pos( &tok )) = HIGHLIGHT_COMMAND; - - if( parser_keywords_is_subcommand( cmd ) ) - { - - int sw; - - if( cmd == L"builtin") - { - use_function = 0; - use_command = 0; - use_builtin = 1; - } - else if( cmd == L"command") - { - use_command = 1; - use_function = 0; - use_builtin = 0; - } - - tok_next( &tok ); - - sw = parser_keywords_is_switch( tok_last( &tok ) ); - - if( !parser_keywords_is_block( cmd ) && - sw == ARG_SWITCH ) - { - /* - The 'builtin' and 'command' builtins - are normally followed by another - command, but if they are invoked - with a switch, they aren't. - - */ - use_command = 1; - use_function = 1; - use_builtin = 2; - } - else - { - if( sw == ARG_SKIP ) - { - color.at(tok_get_pos( &tok )) = HIGHLIGHT_PARAM; - mark = tok_get_pos( &tok ); - } - - is_subcommand = 1; - } - tok_set_pos( &tok, mark ); - } - - if( !is_subcommand ) - { - /* - OK, this is a command, it has been - successfully expanded and everything - looks ok. Lets check if the command - exists. - */ - - /* - First check if it is a builtin or - function, since we don't have to stat - any files for that - */ - if (! is_cmd && use_builtin ) - is_cmd = builtin_exists( cmd ); - - if (! is_cmd && use_function ) - is_cmd = function_exists_no_autoload( cmd, vars ); - - /* - Moving on to expensive tests - */ - - /* - Check if this is a regular command - */ - if (! is_cmd && use_command ) + if (! expanded || has_expand_reserved(cmd.c_str())) + { + color.at(tok_get_pos( &tok )) = HIGHLIGHT_ERROR; + } + else + { + bool is_cmd = false; + int is_subcommand = 0; + int mark = tok_get_pos( &tok ); + color.at(tok_get_pos( &tok )) = HIGHLIGHT_COMMAND; + + if( parser_keywords_is_subcommand( cmd ) ) + { + + int sw; + + if( cmd == L"builtin") + { + use_function = 0; + use_command = 0; + use_builtin = 1; + } + else if( cmd == L"command") + { + use_command = 1; + use_function = 0; + use_builtin = 0; + } + + tok_next( &tok ); + + sw = parser_keywords_is_switch( tok_last( &tok ) ); + + if( !parser_keywords_is_block( cmd ) && + sw == ARG_SWITCH ) + { + /* + The 'builtin' and 'command' builtins + are normally followed by another + command, but if they are invoked + with a switch, they aren't. + + */ + use_command = 1; + use_function = 1; + use_builtin = 2; + } + else + { + if( sw == ARG_SKIP ) + { + color.at(tok_get_pos( &tok )) = HIGHLIGHT_PARAM; + mark = tok_get_pos( &tok ); + } + + is_subcommand = 1; + } + tok_set_pos( &tok, mark ); + } + + if( !is_subcommand ) + { + /* + OK, this is a command, it has been + successfully expanded and everything + looks ok. Lets check if the command + exists. + */ + + /* + First check if it is a builtin or + function, since we don't have to stat + any files for that + */ + if (! is_cmd && use_builtin ) + is_cmd = builtin_exists( cmd ); + + if (! is_cmd && use_function ) + is_cmd = function_exists_no_autoload( cmd, vars ); + + /* + Moving on to expensive tests + */ + + /* + Check if this is a regular command + */ + if (! is_cmd && use_command ) { - is_cmd = path_get_path( cmd, NULL, vars ); + is_cmd = path_get_path( cmd, NULL, vars ); } - + /* Maybe it is a path for a implicit cd command. */ if (! is_cmd) { if (use_builtin || (use_function && function_exists_no_autoload( L"cd", vars))) is_cmd = path_can_be_implicit_cd(cmd, NULL, working_directory.c_str(), vars); } - - if( is_cmd ) - { - color.at(tok_get_pos( &tok )) = HIGHLIGHT_COMMAND; - } - else - { - if( error ) { + + if( is_cmd ) + { + color.at(tok_get_pos( &tok )) = HIGHLIGHT_COMMAND; + } + else + { + if( error ) { error->push_back(format_string(L"Unknown command \'%ls\'", cmd.c_str())); } - color.at(tok_get_pos( &tok )) = (HIGHLIGHT_ERROR); - } - had_cmd = 1; - } - - if( had_cmd ) - { - last_cmd = tok_last( &tok ); - } - } - - } - break; - } - - case TOK_REDIRECT_NOCLOB: - case TOK_REDIRECT_OUT: - case TOK_REDIRECT_IN: - case TOK_REDIRECT_APPEND: - case TOK_REDIRECT_FD: - { - if( !had_cmd ) - { - color.at(tok_get_pos( &tok )) = HIGHLIGHT_ERROR; - if( error ) + color.at(tok_get_pos( &tok )) = (HIGHLIGHT_ERROR); + } + had_cmd = 1; + } + + if( had_cmd ) + { + last_cmd = tok_last( &tok ); + } + } + + } + break; + } + + case TOK_REDIRECT_NOCLOB: + case TOK_REDIRECT_OUT: + case TOK_REDIRECT_IN: + case TOK_REDIRECT_APPEND: + case TOK_REDIRECT_FD: + { + if( !had_cmd ) + { + color.at(tok_get_pos( &tok )) = HIGHLIGHT_ERROR; + if( error ) error->push_back(L"Redirection without a command"); - break; - } - + break; + } + wcstring target_str; - const wchar_t *target=NULL; - - color.at(tok_get_pos( &tok )) = HIGHLIGHT_REDIRECTION; - tok_next( &tok ); - - /* - Check that we are redirecting into a file - */ - - switch( tok_last_type( &tok ) ) - { - case TOK_STRING: - { + const wchar_t *target=NULL; + + color.at(tok_get_pos( &tok )) = HIGHLIGHT_REDIRECTION; + tok_next( &tok ); + + /* + Check that we are redirecting into a file + */ + + switch( tok_last_type( &tok ) ) + { + case TOK_STRING: + { target_str = tok_last( &tok ); if (expand_one(target_str, EXPAND_SKIP_CMDSUBST)) { target = target_str.c_str(); } - /* - Redirect filename may contain a cmdsubst. - If so, it will be ignored/not flagged. - */ - } - break; - default: - { + /* + Redirect filename may contain a cmdsubst. + If so, it will be ignored/not flagged. + */ + } + break; + default: + { size_t pos = tok_get_pos(&tok); if (pos < color.size()) { color.at(pos) = HIGHLIGHT_ERROR; } - if( error ) + if( error ) error->push_back(L"Invalid redirection"); - } - - } - - if( target != 0 ) - { + } + + } + + if( target != 0 ) + { wcstring dir = target; size_t slash_idx = dir.find_last_of(L'/'); - struct stat buff; - /* - If file is in directory other than '.', check - that the directory exists. - */ - if( slash_idx != wcstring::npos ) - { - dir.resize(slash_idx); - if( wstat( dir, &buff ) == -1 ) - { - color.at(tok_get_pos( &tok )) = HIGHLIGHT_ERROR; - if( error ) + struct stat buff; + /* + If file is in directory other than '.', check + that the directory exists. + */ + if( slash_idx != wcstring::npos ) + { + dir.resize(slash_idx); + if( wstat( dir, &buff ) == -1 ) + { + color.at(tok_get_pos( &tok )) = HIGHLIGHT_ERROR; + if( error ) error->push_back(format_string(L"Directory \'%ls\' does not exist", dir.c_str())); - - } - } - - /* - If the file is read from or appended to, check - if it exists. - */ - if( last_type == TOK_REDIRECT_IN || - last_type == TOK_REDIRECT_APPEND ) - { - if( wstat( target, &buff ) == -1 ) - { - color.at(tok_get_pos( &tok )) = HIGHLIGHT_ERROR; - if( error ) + + } + } + + /* + If the file is read from or appended to, check + if it exists. + */ + if( last_type == TOK_REDIRECT_IN || + last_type == TOK_REDIRECT_APPEND ) + { + if( wstat( target, &buff ) == -1 ) + { + color.at(tok_get_pos( &tok )) = HIGHLIGHT_ERROR; + if( error ) error->push_back(format_string(L"File \'%ls\' does not exist", target)); - } - } - if( last_type == TOK_REDIRECT_NOCLOB ) - { - if( wstat( target, &buff ) != -1 ) - { - color.at(tok_get_pos( &tok )) = HIGHLIGHT_ERROR; - if( error ) + } + } + if( last_type == TOK_REDIRECT_NOCLOB ) + { + if( wstat( target, &buff ) != -1 ) + { + color.at(tok_get_pos( &tok )) = HIGHLIGHT_ERROR; + if( error ) error->push_back(format_string(L"File \'%ls\' exists", target)); - } - } - } - break; - } - - case TOK_PIPE: - case TOK_BACKGROUND: - { - if( had_cmd ) - { - color.at(tok_get_pos( &tok )) = HIGHLIGHT_END; - had_cmd = 0; - use_command = 1; - use_function = 1; - use_builtin = 1; - accept_switches = 1; - } - else - { - color.at(tok_get_pos( &tok )) = HIGHLIGHT_ERROR; - if( error ) + } + } + } + break; + } + + case TOK_PIPE: + case TOK_BACKGROUND: + { + if( had_cmd ) + { + color.at(tok_get_pos( &tok )) = HIGHLIGHT_END; + had_cmd = 0; + use_command = 1; + use_function = 1; + use_builtin = 1; + accept_switches = 1; + } + else + { + color.at(tok_get_pos( &tok )) = HIGHLIGHT_ERROR; + if( error ) error->push_back(L"No job to put in background" ); - } - - break; - } - - case TOK_END: - { - color.at(tok_get_pos( &tok )) = HIGHLIGHT_END; - had_cmd = 0; - use_command = 1; - use_function = 1; - use_builtin = 1; - accept_switches = 1; - break; - } - - case TOK_COMMENT: - { - color.at(tok_get_pos( &tok )) = HIGHLIGHT_COMMENT; - break; - } - - case TOK_ERROR: - default: - { - /* - If the tokenizer reports an error, highlight it as such. - */ - if( error ) + } + + break; + } + + case TOK_END: + { + color.at(tok_get_pos( &tok )) = HIGHLIGHT_END; + had_cmd = 0; + use_command = 1; + use_function = 1; + use_builtin = 1; + accept_switches = 1; + break; + } + + case TOK_COMMENT: + { + color.at(tok_get_pos( &tok )) = HIGHLIGHT_COMMENT; + break; + } + + case TOK_ERROR: + default: + { + /* + If the tokenizer reports an error, highlight it as such. + */ + if( error ) error->push_back(tok_last( &tok)); - color.at(tok_get_pos( &tok )) = HIGHLIGHT_ERROR; - break; - } - } - } + color.at(tok_get_pos( &tok )) = HIGHLIGHT_ERROR; + break; + } + } + } tok_destroy( &tok ); } @@ -1265,14 +1265,14 @@ static void tokenize( const wchar_t * const buff, std::vector &color, const void highlight_shell( const wcstring &buff, std::vector &color, size_t pos, wcstring_list_t *error, const env_vars_snapshot_t &vars ) { ASSERT_IS_BACKGROUND_THREAD(); - + const size_t length = buff.size(); assert(buff.size() == color.size()); - if( length == 0 ) - return; - + if( length == 0 ) + return; + std::fill(color.begin(), color.end(), -1); /* Do something sucky and get the current working directory on this background thread. This should really be passed in. Note that we also need this as a vector (of one directory). */ @@ -1281,78 +1281,78 @@ void highlight_shell( const wcstring &buff, std::vector &color, size_t pos, /* Tokenize the string */ tokenize(buff.c_str(), color, pos, error, working_directory, vars); - /* - Locate and syntax highlight cmdsubsts recursively - */ + /* + Locate and syntax highlight cmdsubsts recursively + */ - wchar_t * const subbuff = wcsdup(buff.c_str()); + wchar_t * const subbuff = wcsdup(buff.c_str()); wchar_t * subpos = subbuff; - int done=0; - - while( 1 ) - { - wchar_t *begin, *end; - - if( parse_util_locate_cmdsubst(subpos, &begin, &end, 1) <= 0) - { - break; - } - - if( !*end ) - done=1; - else - *end=0; - + int done=0; + + while( 1 ) + { + wchar_t *begin, *end; + + if( parse_util_locate_cmdsubst(subpos, &begin, &end, 1) <= 0) + { + break; + } + + if( !*end ) + done=1; + else + *end=0; + //our subcolors start at color + (begin-subbuff)+1 size_t start = begin - subbuff + 1, len = wcslen(begin + 1); std::vector subcolors(len, -1); - - highlight_shell( begin+1, subcolors, -1, error, vars ); - + + highlight_shell( begin+1, subcolors, -1, error, vars ); + // insert subcolors std::copy(subcolors.begin(), subcolors.end(), color.begin() + start); - + // highlight the end of the subcommand assert(end >= subbuff); if ((size_t)(end - subbuff) < length) { color.at(end-subbuff)=HIGHLIGHT_OPERATOR; } - - if( done ) - break; - - subpos = end+1; - } + + if( done ) + break; + + subpos = end+1; + } free(subbuff); - /* - The highlighting code only changes the first element when the - color changes. This fills in the rest. - */ - int last_val=0; - for( size_t i=0; i < buff.size(); i++ ) - { - if( color.at(i) >= 0 ) - last_val = color.at(i); - else - color.at(i) = last_val; - } - - /* - Color potentially valid paths in a special path color if they - are the current token. + /* + The highlighting code only changes the first element when the + color changes. This fills in the rest. + */ + int last_val=0; + for( size_t i=0; i < buff.size(); i++ ) + { + if( color.at(i) >= 0 ) + last_val = color.at(i); + else + color.at(i) = last_val; + } + + /* + Color potentially valid paths in a special path color if they + are the current token. For reasons that I don't yet understand, it's required that pos be allowed to be length (e.g. when backspacing). - */ - if( pos <= length ) - { - + */ + if( pos <= length ) + { + const wchar_t *cbuff = buff.c_str(); - const wchar_t *tok_begin, *tok_end; - parse_util_token_extent( cbuff, pos, &tok_begin, &tok_end, 0, 0 ); - if( tok_begin && tok_end ) - { - wcstring token(tok_begin, tok_end-tok_begin); - const wcstring_list_t working_directory_list(1, working_directory); + const wchar_t *tok_begin, *tok_end; + parse_util_token_extent( cbuff, pos, &tok_begin, &tok_end, 0, 0 ); + if( tok_begin && tok_end ) + { + wcstring token(tok_begin, tok_end-tok_begin); + const wcstring_list_t working_directory_list(1, working_directory); if (unescape_string(token, 1)) { /* Big hack: is_potential_path expects a tilde, but unescape_string gives us HOME_DIRECTORY. Put it back. */ @@ -1369,22 +1369,22 @@ void highlight_shell( const wcstring &buff, std::vector &color, size_t pos, } } } - } - } - + } + } - highlight_universal_internal( buff, color, pos ); - /* - Spaces should not be highlighted at all, since it makes cursor look funky in some terminals - */ - for( size_t i=0; i < buff.size(); i++ ) - { - if( iswspace(buff.at(i)) ) - { - color.at(i)=0; - } - } + highlight_universal_internal( buff, color, pos ); + + /* + Spaces should not be highlighted at all, since it makes cursor look funky in some terminals + */ + for( size_t i=0; i < buff.size(); i++ ) + { + if( iswspace(buff.at(i)) ) + { + color.at(i)=0; + } + } } @@ -1393,115 +1393,115 @@ void highlight_shell( const wcstring &buff, std::vector &color, size_t pos, Perform quote and parenthesis highlighting on the specified string. */ static void highlight_universal_internal( const wcstring &buffstr, std::vector &color, size_t pos ) -{ +{ assert(buffstr.size() == color.size()); - if( pos < buffstr.size() ) - { - - /* - Highlight matching quotes - */ - if( (buffstr.at(pos) == L'\'') || (buffstr.at(pos) == L'\"') ) - { - std::vector lst; - - int level=0; - wchar_t prev_q=0; - + if( pos < buffstr.size() ) + { + + /* + Highlight matching quotes + */ + if( (buffstr.at(pos) == L'\'') || (buffstr.at(pos) == L'\"') ) + { + std::vector lst; + + int level=0; + wchar_t prev_q=0; + const wchar_t * const buff = buffstr.c_str(); - const wchar_t *str = buff; + const wchar_t *str = buff; - int match_found=0; - - while(*str) - { - switch( *str ) - { - case L'\\': - str++; - break; - case L'\"': - case L'\'': - if( level == 0 ) - { - level++; + int match_found=0; + + while(*str) + { + switch( *str ) + { + case L'\\': + str++; + break; + case L'\"': + case L'\'': + if( level == 0 ) + { + level++; lst.push_back(str-buff); - prev_q = *str; - } - else - { - if( prev_q == *str ) - { - long pos1, pos2; - - level--; + prev_q = *str; + } + else + { + if( prev_q == *str ) + { + long pos1, pos2; + + level--; pos1 = lst.back(); - pos2 = str-buff; - if( pos1==pos || pos2==pos ) - { - color.at(pos1)|=HIGHLIGHT_MATCH<<16; - color.at(pos2)|=HIGHLIGHT_MATCH<<16; - match_found = 1; - - } - prev_q = *str==L'\"'?L'\'':L'\"'; - } - else - { - level++; + pos2 = str-buff; + if( pos1==pos || pos2==pos ) + { + color.at(pos1)|=HIGHLIGHT_MATCH<<16; + color.at(pos2)|=HIGHLIGHT_MATCH<<16; + match_found = 1; + + } + prev_q = *str==L'\"'?L'\'':L'\"'; + } + else + { + level++; lst.push_back(str-buff); - prev_q = *str; - } - } - - break; - } - if( (*str == L'\0')) - break; + prev_q = *str; + } + } - str++; - } - - if( !match_found ) - color.at(pos) = HIGHLIGHT_ERROR<<16; - } + break; + } + if( (*str == L'\0')) + break; - /* - Highlight matching parenthesis - */ + str++; + } + + if( !match_found ) + color.at(pos) = HIGHLIGHT_ERROR<<16; + } + + /* + Highlight matching parenthesis + */ const wchar_t c = buffstr.at(pos); - if( wcschr( L"()[]{}", c ) ) - { - int step = wcschr(L"({[", c)?1:-1; - wchar_t dec_char = *(wcschr( L"()[]{}", c ) + step); - wchar_t inc_char = c; - int level = 0; - int match_found=0; + if( wcschr( L"()[]{}", c ) ) + { + int step = wcschr(L"({[", c)?1:-1; + wchar_t dec_char = *(wcschr( L"()[]{}", c ) + step); + wchar_t inc_char = c; + int level = 0; + int match_found=0; for (long i=pos; i >= 0 && (size_t)i < buffstr.size(); i+=step) { - const wchar_t test_char = buffstr.at(i); + const wchar_t test_char = buffstr.at(i); if( test_char == inc_char ) - level++; - if( test_char == dec_char ) - level--; - if( level == 0 ) - { - long pos2 = i; - color.at(pos)|=HIGHLIGHT_MATCH<<16; - color.at(pos2)|=HIGHLIGHT_MATCH<<16; - match_found=1; - break; - } - } - - if( !match_found ) - color[pos] = HIGHLIGHT_ERROR<<16; - } - } + level++; + if( test_char == dec_char ) + level--; + if( level == 0 ) + { + long pos2 = i; + color.at(pos)|=HIGHLIGHT_MATCH<<16; + color.at(pos2)|=HIGHLIGHT_MATCH<<16; + match_found=1; + break; + } + } + + if( !match_found ) + color[pos] = HIGHLIGHT_ERROR<<16; + } + } } void highlight_universal( const wcstring &buff, std::vector &color, size_t pos, wcstring_list_t *error, const env_vars_snapshot_t &vars ) { assert(buff.size() == color.size()); - std::fill(color.begin(), color.end(), 0); - highlight_universal_internal( buff, color, pos ); + std::fill(color.begin(), color.end(), 0); + highlight_universal_internal( buff, color, pos ); } diff --git a/highlight.h b/highlight.h index 22121e488..dc8a03059 100644 --- a/highlight.h +++ b/highlight.h @@ -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 &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 &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 }; diff --git a/history.cpp b/history.cpp index fd7b328f1..39595e7e5 100644 --- a/history.cpp +++ b/history.cpp @@ -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 { 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 { public: history_lru_cache_t(size_t max) : lru_cache_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, ×tamp); } - + /* 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::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::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::const_reverse_iterator iter = old_item_offsets.rbegin(); iter != old_item_offsets.rend(); ++iter) { + for (std::vector::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::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); diff --git a/history.h b/history.h index 9bfb2a516..3d083acbd 100644 --- a/history.h +++ b/history.h @@ -18,7 +18,7 @@ typedef std::vector 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 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 deleted_items; + /** New items. */ + std::vector 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 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 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 prev_match_t; std::vector 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); }; diff --git a/input.cpp b/input.cpp index 1db104f7f..ac57b83c0 100644 --- a/input.cpp +++ b/input.cpp @@ -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 mapping_list; @@ -234,7 +234,7 @@ static std::vector 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 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; } diff --git a/input_common.h b/input_common.h index 759bc9cec..9b5e0e630 100644 --- a/input_common.h +++ b/input_common.h @@ -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 diff --git a/intern.cpp b/intern.cpp index 87480ea10..015182cbf 100644 --- a/intern.cpp +++ b/intern.cpp @@ -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); } diff --git a/io.cpp b/io.cpp index a956bbbc0..525f438bd 100644 --- a/io.cpp +++ b/io.cpp @@ -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(); } diff --git a/io.h b/io.h index 540c8141c..249995dd6 100644 --- a/io.h +++ b/io.h @@ -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); } - + /** 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 { 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); - + }; /** diff --git a/iothread.cpp b/iothread.cpp index 3665df302..8e2ece1c6 100644 --- a/iothread.cpp +++ b/iothread.cpp @@ -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 diff --git a/iothread.h b/iothread.h index bca951eac..8ca103596 100644 --- a/iothread.h +++ b/iothread.h @@ -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); diff --git a/key_reader.cpp b/key_reader.cpp index c61e80b7b..d126af7b1 100644 --- a/key_reader.cpp +++ b/key_reader.cpp @@ -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; } diff --git a/kill.cpp b/kill.cpp index f7d7cf0fc..99f36799e 100644 --- a/kill.cpp +++ b/kill.cpp @@ -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 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 ); } diff --git a/kill.h b/kill.h index 8cf4a3ed3..6997341a4 100644 --- a/kill.h +++ b/kill.h @@ -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 diff --git a/lru.h b/lru.h index 2da9ab234..ca49417a1 100644 --- a/lru.h +++ b/lru.h @@ -20,17 +20,17 @@ struct dereference_less_t { class lru_node_t { template 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 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(*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(*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); } }; - + iterator begin() { return iterator(mouth.prev); } iterator end() { return iterator(&mouth); } }; diff --git a/mimedb.cpp b/mimedb.cpp index 919e2033c..d70e682e3 100644 --- a/mimedb.cpp +++ b/mimedb.cpp @@ -1,8 +1,8 @@ -/** \file mimedb.c +/** \file mimedb.c mimedb is a program for checking the mimetype, description and default action associated with a file or mimetype. It uses the -xdgmime library written by the fine folks at freedesktop.org. There does +xdgmime library written by the fine folks at freedesktop.org. There does not seem to be any standard way for the user to change the preferred application yet. @@ -102,7 +102,7 @@ typedef std::vector string_list_t; /** Error message if system call goes wrong. */ -#define ERROR_SYSTEM "%s: Could not execute command \"%s\"\n" +#define ERROR_SYSTEM "%s: Could not execute command \"%s\"\n" /** Exit code if system call goes wrong. @@ -115,12 +115,12 @@ typedef std::vector string_list_t; */ enum { - FILEDATA, - FILENAME, - MIMETYPE, - DESCRIPTION, - ACTION, - LAUNCH + FILEDATA, + FILENAME, + MIMETYPE, + DESCRIPTION, + ACTION, + LAUNCH } ; @@ -166,13 +166,13 @@ static int launch_pos=0; */ void *my_malloc( size_t s ) { - void *res = malloc( s ); - if( !s ) - { - error=1; - fprintf( stderr, _("%s: Out of memory\n"), MIMEDB ); - } - return res; + void *res = malloc( s ); + if( !s ) + { + error=1; + fprintf( stderr, _("%s: Out of memory\n"), MIMEDB ); + } + return res; } /** @@ -180,13 +180,13 @@ void *my_malloc( size_t s ) */ char *my_strdup( const char *s ) { - char *res = strdup( s ); - if( !s ) - { - error=1; - fprintf( stderr, _("%s: Out of memory\n"), MIMEDB ); - } - return res; + char *res = strdup( s ); + if( !s ) + { + error=1; + fprintf( stderr, _("%s: Out of memory\n"), MIMEDB ); + } + return res; } @@ -197,47 +197,47 @@ char *my_strdup( const char *s ) static const char * search_ini( const char *filename, const char *match ) { /* OK to not use CLO_EXEC here because mimedb is single threaded */ - FILE *f = fopen( filename, "r" ); - char buf[4096]; - int len=strlen(match); - int done = 0; - - if(!f ) - { - perror( "fopen" ); - error=1; - return 0; - } - while( !done ) - { - if( !fgets( buf, 4096, f ) ) - { - if( !feof( f ) ) - { - perror( "fgets" ); - error=1; - } - buf[0]=0; - done = 1; - } - else if( strncmp( buf, match, len ) == 0 && buf[len] == '=' ) - { - done=1; - } - } - fclose( f ); - if( buf[0] ) - { - char *res=strdup(buf); - if( res ) - { - if(res[strlen(res)-1]=='\n' ) - res[strlen(res)-1]='\0'; - } - return res; - } - else - return (char *)0; + FILE *f = fopen( filename, "r" ); + char buf[4096]; + int len=strlen(match); + int done = 0; + + if(!f ) + { + perror( "fopen" ); + error=1; + return 0; + } + while( !done ) + { + if( !fgets( buf, 4096, f ) ) + { + if( !feof( f ) ) + { + perror( "fgets" ); + error=1; + } + buf[0]=0; + done = 1; + } + else if( strncmp( buf, match, len ) == 0 && buf[len] == '=' ) + { + done=1; + } + } + fclose( f ); + if( buf[0] ) + { + char *res=strdup(buf); + if( res ) + { + if(res[strlen(res)-1]=='\n' ) + res[strlen(res)-1]='\0'; + } + return res; + } + else + return (char *)0; } /** @@ -246,48 +246,48 @@ static const char * search_ini( const char *filename, const char *match ) */ static char *file_exists( const char *dir, const char *in ) { - int dir_len = strlen( dir ); - int need_sep = dir[dir_len - 1] != '/'; - char *filename = (char *)my_malloc( dir_len + need_sep + strlen( in ) + 1 ); + int dir_len = strlen( dir ); + int need_sep = dir[dir_len - 1] != '/'; + char *filename = (char *)my_malloc( dir_len + need_sep + strlen( in ) + 1 ); char *replaceme; - struct stat buf; + struct stat buf; -// fprintf( stderr, "Check %s%s\n", dir, in ); - - if( !filename ) - { - return 0; - } - strcpy( filename, dir ); - if ( need_sep ) - filename[dir_len++] = '/'; - strcpy( filename + dir_len, in ); - - if( !stat( filename, &buf ) ) - return filename; +// fprintf( stderr, "Check %s%s\n", dir, in ); - free( filename ); - - /* - DOH! File does not exist. But all is not lost. KDE sometimes uses - a slash in the name as a directory separator. We try to replace - a dash with a slash and try again. - */ - replaceme = const_cast(strchr( in, '-' )); - if( replaceme ) - { - char *res; - - *replaceme = '/'; - res = file_exists( dir, in ); - *replaceme = '-'; - return res; - } - /* - OK, no more slashes left. We really are screwed. Nothing to to - but admit defeat and go home. - */ - return 0; + if( !filename ) + { + return 0; + } + strcpy( filename, dir ); + if ( need_sep ) + filename[dir_len++] = '/'; + strcpy( filename + dir_len, in ); + + if( !stat( filename, &buf ) ) + return filename; + + free( filename ); + + /* + DOH! File does not exist. But all is not lost. KDE sometimes uses + a slash in the name as a directory separator. We try to replace + a dash with a slash and try again. + */ + replaceme = const_cast(strchr( in, '-' )); + if( replaceme ) + { + char *res; + + *replaceme = '/'; + res = file_exists( dir, in ); + *replaceme = '-'; + return res; + } + /* + OK, no more slashes left. We really are screwed. Nothing to to + but admit defeat and go home. + */ + return 0; } @@ -303,94 +303,94 @@ static char *file_exists( const char *dir, const char *in ) */ static int append_filenames( string_list_t &list, const char *f, int all ) { - size_t prev_count = list.size(); - char *result; - const char *xdg_data_home; - const char *xdg_data_dirs; - const char *ptr; + size_t prev_count = list.size(); + char *result; + const char *xdg_data_home; + const char *xdg_data_dirs; + const char *ptr; - xdg_data_home = getenv ("XDG_DATA_HOME"); - if (xdg_data_home) + xdg_data_home = getenv ("XDG_DATA_HOME"); + if (xdg_data_home) + { + result = file_exists( xdg_data_home, f ); + if (result) { - result = file_exists( xdg_data_home, f ); - if (result) - { list.push_back(result); - if ( !all ) - return 1; - } + if ( !all ) + return 1; } - else + } + else { - const char *home; + const char *home; - home = getenv ("HOME"); - if (home != NULL) - { - char *guessed_xdg_home; + home = getenv ("HOME"); + if (home != NULL) + { + char *guessed_xdg_home; - guessed_xdg_home = (char *)my_malloc (strlen (home) + strlen ("/.local/share") + 1); - if( !guessed_xdg_home ) - return 0; - - strcpy (guessed_xdg_home, home); - strcat (guessed_xdg_home, "/.local/share"); - result = file_exists( guessed_xdg_home, f ); - free (guessed_xdg_home); + guessed_xdg_home = (char *)my_malloc (strlen (home) + strlen ("/.local/share") + 1); + if( !guessed_xdg_home ) + return 0; - if (result) - { + strcpy (guessed_xdg_home, home); + strcat (guessed_xdg_home, "/.local/share"); + result = file_exists( guessed_xdg_home, f ); + free (guessed_xdg_home); + + if (result) + { list.push_back(result); - if ( !all ) - return 1; - } - } + if ( !all ) + return 1; + } + } } - xdg_data_dirs = getenv ("XDG_DATA_DIRS"); - if (xdg_data_dirs == NULL) - xdg_data_dirs = "/usr/local/share:/usr/share"; + xdg_data_dirs = getenv ("XDG_DATA_DIRS"); + if (xdg_data_dirs == NULL) + xdg_data_dirs = "/usr/local/share:/usr/share"; - ptr = xdg_data_dirs; + ptr = xdg_data_dirs; - while (*ptr != '\000') + while (*ptr != '\000') { - const char *end_ptr; - char *dir; - int len; + const char *end_ptr; + char *dir; + int len; - end_ptr = ptr; - while (*end_ptr != ':' && *end_ptr != '\000') - end_ptr ++; + end_ptr = ptr; + while (*end_ptr != ':' && *end_ptr != '\000') + end_ptr ++; - if (end_ptr == ptr) - { - ptr++; - continue; - } + if (end_ptr == ptr) + { + ptr++; + continue; + } len = end_ptr - ptr; - dir = (char *)my_malloc (len + 1); - if( !dir ) - return 0; - - strncpy (dir, ptr, len); - dir[len] = '\0'; - result = file_exists( dir, f ); - - free (dir); + dir = (char *)my_malloc (len + 1); + if( !dir ) + return 0; - if (result) - { + strncpy (dir, ptr, len); + dir[len] = '\0'; + result = file_exists( dir, f ); + + free (dir); + + if (result) + { list.push_back(result); - if ( !all ) { - return 1; - } - } - - ptr = end_ptr; + if ( !all ) { + return 1; + } } - return list.size() - prev_count; + + ptr = end_ptr; + } + return list.size() - prev_count; } /** @@ -400,7 +400,7 @@ static std::string get_filename( char *f ) { string_list_t list; - append_filenames( list, f, 0 ); + append_filenames( list, f, 0 ); if (list.empty()) { return ""; } else { @@ -415,49 +415,49 @@ static std::string get_filename( char *f ) */ static char *munge( char *in ) { - char *out = (char *)my_malloc( strlen( in )+1 ); - char *p=out; - int had_whitespace = 0; - int printed = 0; - if( !out ) - { - return 0; - } - - while( 1 ) - { -// fprintf( stderr, "%c\n", *in ); - - switch( *in ) - { - case ' ': - case '\n': - case '\t': - case '\r': - { - had_whitespace = 1; - break; - } - case '\0': - *p = '\0'; - return out; - default: - { - if( printed && had_whitespace ) - { - *(p++)=' '; - } - printed=1; - had_whitespace=0; - *(p++)=*in; - break; - } - } - in++; - } - fprintf( stderr, _( "%s: Unknown error in munge()\n"), MIMEDB ); - error=1; - return 0; + char *out = (char *)my_malloc( strlen( in )+1 ); + char *p=out; + int had_whitespace = 0; + int printed = 0; + if( !out ) + { + return 0; + } + + while( 1 ) + { +// fprintf( stderr, "%c\n", *in ); + + switch( *in ) + { + case ' ': + case '\n': + case '\t': + case '\r': + { + had_whitespace = 1; + break; + } + case '\0': + *p = '\0'; + return out; + default: + { + if( printed && had_whitespace ) + { + *(p++)=' '; + } + printed=1; + had_whitespace=0; + *(p++)=*in; + break; + } + } + in++; + } + fprintf( stderr, _( "%s: Unknown error in munge()\n"), MIMEDB ); + error=1; + return 0; } /** @@ -465,325 +465,325 @@ static char *munge( char *in ) */ static char *get_lang_re() { - - static char buff[BUFF_SIZE]; - const char *lang = setlocale( LC_MESSAGES, 0 ); - int close=0; - char *out=buff; - - if( (1+strlen(lang)*4) >= BUFF_SIZE ) - { - fprintf( stderr, _( "%s: Locale string too long\n"), MIMEDB ); - error = 1; - return 0; - } - - for( ; *lang; lang++ ) - { - switch( *lang ) - { - case '@': - case '.': - case '_': - if( close ) - { - *out++ = ')'; - *out++ = '?'; - } - - close=1; - *out++ = '('; - *out++ = *lang; - break; - - default: - *out++ = *lang; - } - } - - if( close ) - { - *out++ = ')'; - *out++ = '?'; - } - *out++=0; - return buff; + static char buff[BUFF_SIZE]; + const char *lang = setlocale( LC_MESSAGES, 0 ); + int close=0; + char *out=buff; + + if( (1+strlen(lang)*4) >= BUFF_SIZE ) + { + fprintf( stderr, _( "%s: Locale string too long\n"), MIMEDB ); + error = 1; + return 0; + } + + for( ; *lang; lang++ ) + { + switch( *lang ) + { + case '@': + case '.': + case '_': + if( close ) + { + *out++ = ')'; + *out++ = '?'; + } + + close=1; + *out++ = '('; + *out++ = *lang; + break; + + default: + *out++ = *lang; + } + } + + if( close ) + { + *out++ = ')'; + *out++ = '?'; + } + *out++=0; + + return buff; } /** - Get description for a specified mimetype. + Get description for a specified mimetype. */ static char *get_description( const char *mimetype ) { - char *fn_part; - - std::string fn; - int fd; - struct stat st; - char *contents; - char *start=0, *stop=0, *best_start=0; + char *fn_part; - if( !start_re ) - { - char *lang; - char buff[BUFF_SIZE]; + std::string fn; + int fd; + struct stat st; + char *contents; + char *start=0, *stop=0, *best_start=0; - lang = get_lang_re(); - if( !lang ) - return 0; - - snprintf( buff, BUFF_SIZE, START_TAG, lang, lang ); + if( !start_re ) + { + char *lang; + char buff[BUFF_SIZE]; + + lang = get_lang_re(); + if( !lang ) + return 0; + + snprintf( buff, BUFF_SIZE, START_TAG, lang, lang ); + + start_re = (regex_t *)my_malloc( sizeof(regex_t)); + stop_re = (regex_t *)my_malloc( sizeof(regex_t)); - start_re = (regex_t *)my_malloc( sizeof(regex_t)); - stop_re = (regex_t *)my_malloc( sizeof(regex_t)); - int reg_status; - if( ( reg_status = regcomp( start_re, buff, REG_EXTENDED ) ) ) + if( ( reg_status = regcomp( start_re, buff, REG_EXTENDED ) ) ) { char regerrbuf[BUFF_SIZE]; regerror(reg_status, start_re, regerrbuf, BUFF_SIZE); - fprintf( stderr, _( "%s: Could not compile regular expressions %s with error %s\n"), MIMEDB, buff, regerrbuf); - error=1; - + fprintf( stderr, _( "%s: Could not compile regular expressions %s with error %s\n"), MIMEDB, buff, regerrbuf); + error=1; + } else if ( ( reg_status = regcomp( stop_re, STOP_TAG, REG_EXTENDED ) ) ) - { + { char regerrbuf[BUFF_SIZE]; regerror(reg_status, stop_re, regerrbuf, BUFF_SIZE); - fprintf( stderr, _( "%s: Could not compile regular expressions %s with error %s\n"), MIMEDB, buff, regerrbuf); - error=1; - + fprintf( stderr, _( "%s: Could not compile regular expressions %s with error %s\n"), MIMEDB, buff, regerrbuf); + error=1; + } if( error ) { - free( start_re ); - free( stop_re ); - start_re = stop_re = 0; + free( start_re ); + free( stop_re ); + start_re = stop_re = 0; - return 0; + return 0; } - } - - fn_part = (char *)my_malloc( strlen(MIME_DIR) + strlen( mimetype) + strlen(MIME_SUFFIX) + 1 ); + } - if( !fn_part ) - { - return 0; - } + fn_part = (char *)my_malloc( strlen(MIME_DIR) + strlen( mimetype) + strlen(MIME_SUFFIX) + 1 ); - strcpy( fn_part, MIME_DIR ); - strcat( fn_part, mimetype ); - strcat( fn_part, MIME_SUFFIX ); + if( !fn_part ) + { + return 0; + } - fn = get_filename(fn_part); //malloc( strlen(MIME_DIR) +strlen( MIME_SUFFIX)+ strlen( mimetype ) + 1 ); - free(fn_part ); - - if( fn.empty() ) - { - return 0; - } + strcpy( fn_part, MIME_DIR ); + strcat( fn_part, mimetype ); + strcat( fn_part, MIME_SUFFIX ); + + fn = get_filename(fn_part); //malloc( strlen(MIME_DIR) +strlen( MIME_SUFFIX)+ strlen( mimetype ) + 1 ); + free(fn_part ); + + if( fn.empty() ) + { + return 0; + } /* OK to not use CLO_EXEC here because mimedb is single threaded */ - fd = open( fn.c_str(), O_RDONLY ); + fd = open( fn.c_str(), O_RDONLY ); -// fprintf( stderr, "%s\n", fn ); - - if( fd == -1 ) - { - perror( "open" ); - error=1; - return 0; - } - - if( stat( fn.c_str(), &st) ) - { - perror( "stat" ); - error=1; - return 0; - } +// fprintf( stderr, "%s\n", fn ); - contents = (char *)my_malloc( st.st_size + 1 ); - if( !contents ) - { - return 0; - } - - if( read( fd, contents, st.st_size ) != st.st_size ) - { - perror( "read" ); - error=1; + if( fd == -1 ) + { + perror( "open" ); + error=1; + return 0; + } + + if( stat( fn.c_str(), &st) ) + { + perror( "stat" ); + error=1; + return 0; + } + + contents = (char *)my_malloc( st.st_size + 1 ); + if( !contents ) + { + return 0; + } + + if( read( fd, contents, st.st_size ) != st.st_size ) + { + perror( "read" ); + error=1; free((void *)contents); - return 0; - } + return 0; + } - /* - Don't need to check exit status of close on read-only file descriptors - */ - close( fd ); + /* + Don't need to check exit status of close on read-only file descriptors + */ + close( fd ); - contents[st.st_size]=0; - regmatch_t match[1]; - int w = -1; - - start=contents; - - /* - On multiple matches, use the longest match, should be a pretty - good heuristic for best match... - */ - while( !regexec(start_re, start, 1, match, 0) ) - { - int new_w = match[0].rm_eo - match[0].rm_so; - start += match[0].rm_eo; - - if( new_w > w ) - { - /* - New match is for a longer match then the previous - match, so we use the new match - */ - w=new_w; - best_start = start; - } - } - - if( w != -1 ) - { - start = best_start; - if( !regexec(stop_re, start, 1, match, 0) ) - { - /* - We've found the beginning and the end of a suitable description - */ - char *res; + contents[st.st_size]=0; + regmatch_t match[1]; + int w = -1; - stop = start + match[0].rm_so; - *stop = '\0'; - res = munge( start ); - free( contents ); - return res; - } - } - free( contents ); - fprintf( stderr, _( "%s: No description for type %s\n"), MIMEDB, mimetype ); - error=1; - return 0; + start=contents; + + /* + On multiple matches, use the longest match, should be a pretty + good heuristic for best match... + */ + while( !regexec(start_re, start, 1, match, 0) ) + { + int new_w = match[0].rm_eo - match[0].rm_so; + start += match[0].rm_eo; + + if( new_w > w ) + { + /* + New match is for a longer match then the previous + match, so we use the new match + */ + w=new_w; + best_start = start; + } + } + + if( w != -1 ) + { + start = best_start; + if( !regexec(stop_re, start, 1, match, 0) ) + { + /* + We've found the beginning and the end of a suitable description + */ + char *res; + + stop = start + match[0].rm_so; + *stop = '\0'; + res = munge( start ); + free( contents ); + return res; + } + } + free( contents ); + fprintf( stderr, _( "%s: No description for type %s\n"), MIMEDB, mimetype ); + error=1; + return 0; } /** - Get default action for a specified mimetype. + Get default action for a specified mimetype. */ static char *get_action( const char *mimetype ) { - char *res=0; - - const char *launcher, *end; - string_list_t mime_filenames; - - const char *launcher_str = NULL; - const char *launcher_command_str, *launcher_command; - char *launcher_full; - - if( !append_filenames( mime_filenames, DESKTOP_DEFAULT, 1 ) ) - { - return 0; - } - - for ( size_t i = 0; i < mime_filenames.size(); i++ ) - { - launcher_str = search_ini( mime_filenames.at(i).c_str(), mimetype ); - if ( launcher_str ) - break; - } + char *res=0; - - if( !launcher_str ) - { - /* - This type does not have a launcher. Try the supertype! - */ -// fprintf( stderr, "mimedb: %s does not have launcher, try supertype\n", mimetype ); - const char ** parents = xdg_mime_get_mime_parents(mimetype); - - const char **p; - if( parents ) - { - for( p=parents; *p; p++ ) - { - char *a = get_action(*p); - if( a != 0 ) - return a; - } - } - /* - Just in case subclassing doesn't work, (It doesn't on Fedora - Core 3) we also test some common subclassings. - */ - - if( strncmp( mimetype, "text/plain", 10) != 0 && strncmp( mimetype, "text/", 5 ) == 0 ) - return get_action( "text/plain" ); + const char *launcher, *end; + string_list_t mime_filenames; - return 0; - } - -// fprintf( stderr, "WOOT %s\n", launcher_str ); - launcher = const_cast(strchr( launcher_str, '=' )); - - if( !launcher ) - { - fprintf( stderr, _("%s: Could not parse launcher string '%s'\n"), MIMEDB, launcher_str ); - error=1; - return 0; - } - - /* Skip the = */ - launcher++; - - /* Make one we can change */ - std::string mut_launcher = launcher; - - /* Only use first launcher */ - end = strchr( launcher, ';' ); - if( end ) - mut_launcher.resize(end - launcher); + const char *launcher_str = NULL; + const char *launcher_command_str, *launcher_command; + char *launcher_full; - launcher_full = (char *)my_malloc( mut_launcher.size() + strlen( APPLICATIONS_DIR)+1 ); - if( !launcher_full ) - { - free( (void *)launcher_str ); - return 0; - } - - strcpy( launcher_full, APPLICATIONS_DIR ); - strcat( launcher_full, mut_launcher.c_str() ); - free( (void *)launcher_str ); - - std::string launcher_filename = get_filename( launcher_full ); - - free( launcher_full ); - - launcher_command_str = search_ini( launcher_filename.c_str(), "Exec" ); - - if( !launcher_command_str ) - { - fprintf( stderr, - _( "%s: Default launcher '%s' does not specify how to start\n"), MIMEDB, - launcher_filename.c_str() ); - return 0; - } - - launcher_command = strchr( launcher_command_str, '=' ); - launcher_command++; - - res = my_strdup( launcher_command ); - - free( (void *)launcher_command_str ); + if( !append_filenames( mime_filenames, DESKTOP_DEFAULT, 1 ) ) + { + return 0; + } - return res; + for ( size_t i = 0; i < mime_filenames.size(); i++ ) + { + launcher_str = search_ini( mime_filenames.at(i).c_str(), mimetype ); + if ( launcher_str ) + break; + } + + + if( !launcher_str ) + { + /* + This type does not have a launcher. Try the supertype! + */ +// fprintf( stderr, "mimedb: %s does not have launcher, try supertype\n", mimetype ); + const char ** parents = xdg_mime_get_mime_parents(mimetype); + + const char **p; + if( parents ) + { + for( p=parents; *p; p++ ) + { + char *a = get_action(*p); + if( a != 0 ) + return a; + } + } + /* + Just in case subclassing doesn't work, (It doesn't on Fedora + Core 3) we also test some common subclassings. + */ + + if( strncmp( mimetype, "text/plain", 10) != 0 && strncmp( mimetype, "text/", 5 ) == 0 ) + return get_action( "text/plain" ); + + return 0; + } + +// fprintf( stderr, "WOOT %s\n", launcher_str ); + launcher = const_cast(strchr( launcher_str, '=' )); + + if( !launcher ) + { + fprintf( stderr, _("%s: Could not parse launcher string '%s'\n"), MIMEDB, launcher_str ); + error=1; + return 0; + } + + /* Skip the = */ + launcher++; + + /* Make one we can change */ + std::string mut_launcher = launcher; + + /* Only use first launcher */ + end = strchr( launcher, ';' ); + if( end ) + mut_launcher.resize(end - launcher); + + launcher_full = (char *)my_malloc( mut_launcher.size() + strlen( APPLICATIONS_DIR)+1 ); + if( !launcher_full ) + { + free( (void *)launcher_str ); + return 0; + } + + strcpy( launcher_full, APPLICATIONS_DIR ); + strcat( launcher_full, mut_launcher.c_str() ); + free( (void *)launcher_str ); + + std::string launcher_filename = get_filename( launcher_full ); + + free( launcher_full ); + + launcher_command_str = search_ini( launcher_filename.c_str(), "Exec" ); + + if( !launcher_command_str ) + { + fprintf( stderr, + _( "%s: Default launcher '%s' does not specify how to start\n"), MIMEDB, + launcher_filename.c_str() ); + return 0; + } + + launcher_command = strchr( launcher_command_str, '=' ); + launcher_command++; + + res = my_strdup( launcher_command ); + + free( (void *)launcher_command_str ); + + return res; } @@ -792,25 +792,25 @@ static char *get_action( const char *mimetype ) */ static void writer( char c ) { - if( launch_len == -1 ) - return; - - if( launch_len <= launch_pos ) - { - int new_len = launch_len?2*launch_len:256; - char *new_buff = (char *)realloc( launch_buff, new_len ); - if( !new_buff ) - { - free( launch_buff ); - launch_len = -1; - error=1; - return; - } - launch_buff = new_buff; - launch_len = new_len; - - } - launch_buff[launch_pos++]=c; + if( launch_len == -1 ) + return; + + if( launch_len <= launch_pos ) + { + int new_len = launch_len?2*launch_len:256; + char *new_buff = (char *)realloc( launch_buff, new_len ); + if( !new_buff ) + { + free( launch_buff ); + launch_len = -1; + error=1; + return; + } + launch_buff = new_buff; + launch_len = new_len; + + } + launch_buff[launch_pos++]=c; } /** @@ -818,12 +818,12 @@ static void writer( char c ) */ static void writer_hex( int num ) { - int a, b; - a = num /16; - b = num %16; + int a, b; + a = num /16; + b = num %16; - writer( a>9?('A'+a-10):('0'+a)); - writer( b>9?('A'+b-10):('0'+b)); + writer( a>9?('A'+a-10):('0'+a)); + writer( b>9?('A'+b-10):('0'+b)); } /** @@ -831,17 +831,17 @@ static void writer_hex( int num ) */ static char *my_getcwd () { - size_t size = 100; - while (1) - { - char *buffer = (char *) malloc (size); - if (getcwd (buffer, size) == buffer) - return buffer; - free (buffer); - if (errno != ERANGE) - return 0; - size *= 2; - } + size_t size = 100; + while (1) + { + char *buffer = (char *) malloc (size); + if (getcwd (buffer, size) == buffer) + return buffer; + free (buffer); + if (errno != ERANGE) + return 0; + size *= 2; + } } /** @@ -849,39 +849,39 @@ static char *my_getcwd () */ static const char *get_fullfile( const char *file ) { - const char *fullfile; - - if( file[0] == '/' ) - { - fullfile = file; - } - else - { - char *cwd = my_getcwd(); - if( !cwd ) - { - error = 1; - perror( "getcwd" ); - return 0; - } - - int l = strlen(cwd); - - char *tmp = (char *)my_malloc( l + strlen(file)+2 ); - if( !tmp ) - { - free(cwd); - return 0; - } - strcpy( tmp, cwd ); - if( cwd[l-1] != '/' ) - strcat(tmp, "/" ); - strcat( tmp, file ); - - free(cwd); + const char *fullfile; + + if( file[0] == '/' ) + { + fullfile = file; + } + else + { + char *cwd = my_getcwd(); + if( !cwd ) + { + error = 1; + perror( "getcwd" ); + return 0; + } + + int l = strlen(cwd); + + char *tmp = (char *)my_malloc( l + strlen(file)+2 ); + if( !tmp ) + { + free(cwd); + return 0; + } + strcpy( tmp, cwd ); + if( cwd[l-1] != '/' ) + strcat(tmp, "/" ); + strcat( tmp, file ); + + free(cwd); fullfile = tmp; - } - return fullfile; + } + return fullfile; } @@ -890,46 +890,46 @@ static const char *get_fullfile( const char *file ) */ static void write_url( const char *file ) { - const char *fullfile = get_fullfile( file ); - const char *str = fullfile; - - if( str == 0 ) - { - launch_len = -1; - return; - } - - writer( 'f'); - writer( 'i'); - writer( 'l'); - writer( 'e'); - writer( ':'); - writer( '/'); - writer( '/'); - while( *str ) - { - if( ((*str >= 'a') && (*str <='z')) || - ((*str >= 'A') && (*str <='Z')) || - ((*str >= '0') && (*str <='9')) || - (strchr( "-_.~/",*str) != 0) ) - { - writer(*str); - } - else if(strchr( "()?&=",*str) != 0) - { - writer('\\'); - writer(*str); - } - else - { - writer( '%' ); - writer_hex( (unsigned char)*str ); - } - str++; - } - if( fullfile != file ) - free( (void *)fullfile ); - + const char *fullfile = get_fullfile( file ); + const char *str = fullfile; + + if( str == 0 ) + { + launch_len = -1; + return; + } + + writer( 'f'); + writer( 'i'); + writer( 'l'); + writer( 'e'); + writer( ':'); + writer( '/'); + writer( '/'); + while( *str ) + { + if( ((*str >= 'a') && (*str <='z')) || + ((*str >= 'A') && (*str <='Z')) || + ((*str >= '0') && (*str <='9')) || + (strchr( "-_.~/",*str) != 0) ) + { + writer(*str); + } + else if(strchr( "()?&=",*str) != 0) + { + writer('\\'); + writer(*str); + } + else + { + writer( '%' ); + writer_hex( (unsigned char)*str ); + } + str++; + } + if( fullfile != file ) + free( (void *)fullfile ); + } /** @@ -937,97 +937,97 @@ static void write_url( const char *file ) */ static void write_file( const char *file, int print_path ) { - const char *fullfile; - const char *str; - if( print_path ) - { - fullfile = get_fullfile( file ); - str = fullfile; - } - else - { - char *tmp = my_strdup( file ); - if( !tmp ) - { - return; - } - str = basename( tmp ); + const char *fullfile; + const char *str; + if( print_path ) + { + fullfile = get_fullfile( file ); + str = fullfile; + } + else + { + char *tmp = my_strdup( file ); + if( !tmp ) + { + return; + } + str = basename( tmp ); fullfile = tmp; - } - - if( !str ) - { - error = 1; - return; - } + } - while( *str ) - { - switch(*str ) - { - case ')': - case '(': - case '-': - case '#': - case '$': - case '}': - case '{': - case ']': - case '[': - case '*': - case '?': - case ' ': - case '|': - case '<': - case '>': - case '^': - case '&': - case '\\': - case '`': - case '\'': - case '\"': - writer('\\'); - writer(*str); - break; + if( !str ) + { + error = 1; + return; + } - case '\n': - writer('\\'); - writer('n'); - break; + while( *str ) + { + switch(*str ) + { + case ')': + case '(': + case '-': + case '#': + case '$': + case '}': + case '{': + case ']': + case '[': + case '*': + case '?': + case ' ': + case '|': + case '<': + case '>': + case '^': + case '&': + case '\\': + case '`': + case '\'': + case '\"': + writer('\\'); + writer(*str); + break; - case '\r': - writer('\\'); - writer('r'); - break; + case '\n': + writer('\\'); + writer('n'); + break; - case '\t': - writer('\\'); - writer('t'); - break; + case '\r': + writer('\\'); + writer('r'); + break; - case '\b': - writer('\\'); - writer('b'); - break; + case '\t': + writer('\\'); + writer('t'); + break; - case '\v': - writer('\\'); - writer('v'); - break; + case '\b': + writer('\\'); + writer('b'); + break; - default: - writer(*str); - break; - } - str++; - } + case '\v': + writer('\\'); + writer('v'); + break; - if( fullfile != file ) - free( (void *)fullfile ); + default: + writer(*str); + break; + } + str++; + } + + if( fullfile != file ) + free( (void *)fullfile ); } /** - Use the specified launch filter to launch all the files in the specified list. + Use the specified launch filter to launch all the files in the specified list. \param filter the action to take \param files the list of files for which to perform the action @@ -1035,169 +1035,169 @@ static void write_file( const char *file, int print_path ) */ static void launch( char *filter, const string_list_t &files, size_t fileno ) { - char *filter_org=filter; - int count=0; - int launch_again=0; - - if( files.size() <= fileno ) - return; - - - launch_pos=0; + char *filter_org=filter; + int count=0; + int launch_again=0; - for( ;*filter && !error; filter++) - { - if(*filter == '%') - { - filter++; - switch( *filter ) - { - case 'u': - { - launch_again = 1; - write_url( files.at(fileno).c_str() ); - break; - } - case 'U': - { - for( size_t i=0; i launch_hash_t; launch_hash_t launch_hash; - locale_init(); - - /* - Parse options - */ - while( 1 ) - { - static struct option - long_options[] = - { - { - "input-file-data", no_argument, 0, 't' - } - , - { - "input-filename", no_argument, 0, 'f' - } - , - { - "input-mime", no_argument, 0, 'i' - } - , - { - "output-mime", no_argument, 0, 'm' - } - , - { - "output-description", no_argument, 0, 'd' - } - , - { - "output-action", no_argument, 0, 'a' - } - , - { - "help", no_argument, 0, 'h' - } - , - { - "version", no_argument, 0, 'v' - } - , - { - "launch", no_argument, 0, 'l' - } - , - { - 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; + locale_init(); - case 't': - input_type=FILEDATA; - break; + /* + Parse options + */ + while( 1 ) + { + static struct option + long_options[] = + { + { + "input-file-data", no_argument, 0, 't' + } + , + { + "input-filename", no_argument, 0, 'f' + } + , + { + "input-mime", no_argument, 0, 'i' + } + , + { + "output-mime", no_argument, 0, 'm' + } + , + { + "output-description", no_argument, 0, 'd' + } + , + { + "output-action", no_argument, 0, 'a' + } + , + { + "help", no_argument, 0, 'h' + } + , + { + "version", no_argument, 0, 'v' + } + , + { + "launch", no_argument, 0, 'l' + } + , + { + 0, 0, 0, 0 + } + } + ; - case 'f': - input_type=FILENAME; - break; + int opt_index = 0; - case 'i': - input_type=MIMETYPE; - break; + int opt = getopt_long( argc, + argv, + GETOPT_STRING, + long_options, + &opt_index ); - case 'm': - output_type=MIMETYPE; - break; + if( opt == -1 ) + break; - case 'd': - output_type=DESCRIPTION; - break; - - case 'a': - output_type=ACTION; - break; - - case 'l': - output_type=LAUNCH; - break; - - case 'h': - print_help( argv[0], 1 ); - exit(0); - - case 'v': - printf( _("%s, version %s\n"), MIMEDB, PACKAGE_VERSION ); - exit( 0 ); - - case '?': - return 1; - - } - } - - if( ( output_type == LAUNCH )&&(input_type==MIMETYPE)) - { - fprintf( stderr, _("%s: Can not launch a mimetype\n"), MIMEDB ); - print_help( argv[0], 2 ); - exit(1); - } - - /* - Loop over all non option arguments and do the specified lookup - */ - - //fprintf( stderr, "Input %d, output %d\n", input_type, output_type ); - - for (i = optind; (i < argc)&&(!error); i++) + switch( opt ) { - /* Convert from filename to mimetype, if needed */ - if( input_type == FILENAME ) - { - mimetype = xdg_mime_get_mime_type_from_file_name(argv[i]); - } - else if( input_type == FILEDATA ) - { - mimetype = xdg_mime_get_mime_type_for_file(argv[i]); - } - else - mimetype = xdg_mime_is_valid_mime_type(argv[i])?argv[i]:0; + case 0: + break; - mimetype = xdg_mime_unalias_mime_type (mimetype); - if( !mimetype ) - { - fprintf( stderr, _( "%s: Could not parse mimetype from argument '%s'\n"), MIMEDB, argv[i] ); - error=1; - return 1; - } - - /* - Convert from mimetype to whatever, if needed - */ - switch( output_type ) - { - case MIMETYPE: - { - output = (char *)mimetype; - break; - - } - case DESCRIPTION: - { - output = get_description( mimetype ); - if( !output ) - output = strdup( _("Unknown") ); - - break; - } - case ACTION: - { - output = get_action( mimetype ); - break; - } - case LAUNCH: - { - /* - There may be more files using the same launcher, we - add them all up in little array_list_ts and launched - them together after all the arguments have been - parsed. - */ - output = 0; - string_list_t &l = launch_hash[mimetype]; - l.push_back(argv[i]); - } - } - - /* - Print the glorious result - */ - if( output ) - { - printf( "%s\n", output ); - if( output != mimetype ) - free( output ); - } - output = 0; + case 't': + input_type=FILEDATA; + break; + + case 'f': + input_type=FILENAME; + break; + + case 'i': + input_type=MIMETYPE; + break; + + case 'm': + output_type=MIMETYPE; + break; + + case 'd': + output_type=DESCRIPTION; + break; + + case 'a': + output_type=ACTION; + break; + + case 'l': + output_type=LAUNCH; + break; + + case 'h': + print_help( argv[0], 1 ); + exit(0); + + case 'v': + printf( _("%s, version %s\n"), MIMEDB, PACKAGE_VERSION ); + exit( 0 ); + + case '?': + return 1; + + } + } + + if( ( output_type == LAUNCH )&&(input_type==MIMETYPE)) + { + fprintf( stderr, _("%s: Can not launch a mimetype\n"), MIMEDB ); + print_help( argv[0], 2 ); + exit(1); + } + + /* + Loop over all non option arguments and do the specified lookup + */ + + //fprintf( stderr, "Input %d, output %d\n", input_type, output_type ); + + for (i = optind; (i < argc)&&(!error); i++) + { + /* Convert from filename to mimetype, if needed */ + if( input_type == FILENAME ) + { + mimetype = xdg_mime_get_mime_type_from_file_name(argv[i]); + } + else if( input_type == FILEDATA ) + { + mimetype = xdg_mime_get_mime_type_for_file(argv[i]); + } + else + mimetype = xdg_mime_is_valid_mime_type(argv[i])?argv[i]:0; + + mimetype = xdg_mime_unalias_mime_type (mimetype); + if( !mimetype ) + { + fprintf( stderr, _( "%s: Could not parse mimetype from argument '%s'\n"), MIMEDB, argv[i] ); + error=1; + return 1; } - /* - Perform the actual launching - */ - if( output_type == LAUNCH && !error ) - { - for( launch_hash_t::iterator iter = launch_hash.begin(); iter != launch_hash.end(); ++iter) - { - const char *mimetype = iter->first.c_str(); - string_list_t &files = iter->second; - - char *launcher = get_action( mimetype ); + /* + Convert from mimetype to whatever, if needed + */ + switch( output_type ) + { + case MIMETYPE: + { + output = (char *)mimetype; + break; - if( launcher ) - { - launch( launcher, files, 0 ); - free( launcher ); - } - } - } - - if( launch_buff ) - free( launch_buff ); + } + case DESCRIPTION: + { + output = get_description( mimetype ); + if( !output ) + output = strdup( _("Unknown") ); - if( start_re ) - { - regfree( start_re ); - regfree( stop_re ); - free( start_re ); - free( stop_re ); - } + break; + } + case ACTION: + { + output = get_action( mimetype ); + break; + } + case LAUNCH: + { + /* + There may be more files using the same launcher, we + add them all up in little array_list_ts and launched + them together after all the arguments have been + parsed. + */ + output = 0; + string_list_t &l = launch_hash[mimetype]; + l.push_back(argv[i]); + } + } - xdg_mime_shutdown(); - - return error; + /* + Print the glorious result + */ + if( output ) + { + printf( "%s\n", output ); + if( output != mimetype ) + free( output ); + } + output = 0; + } + + /* + Perform the actual launching + */ + if( output_type == LAUNCH && !error ) + { + for( launch_hash_t::iterator iter = launch_hash.begin(); iter != launch_hash.end(); ++iter) + { + const char *mimetype = iter->first.c_str(); + string_list_t &files = iter->second; + + char *launcher = get_action( mimetype ); + + if( launcher ) + { + launch( launcher, files, 0 ); + free( launcher ); + } + } + } + + if( launch_buff ) + free( launch_buff ); + + if( start_re ) + { + regfree( start_re ); + regfree( stop_re ); + free( start_re ); + free( stop_re ); + } + + xdg_mime_shutdown(); + + return error; } diff --git a/output.cpp b/output.cpp index b061693e4..49917239b 100644 --- a/output.cpp +++ b/output.cpp @@ -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; imax_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 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() diff --git a/output.h b/output.h index 0bc432b81..b88c7bef7 100644 --- a/output.h +++ b/output.h @@ -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); \ + } \ + } /** diff --git a/parse_util.cpp b/parse_util.cpp index f523e38d7..dc1116574 100644 --- a/parse_util.cpp +++ b/parse_util.cpp @@ -3,9 +3,9 @@ Various mostly unrelated utility functions related to parsing, loading and evaluating fish code. - This library can be seen as a 'toolbox' for functions that are - used in many places in fish and that are somehow related to - parsing the code. + This library can be seen as a 'toolbox' for functions that are + used in many places in fish and that are somehow related to + parsing the code. */ #include "config.h" @@ -53,566 +53,566 @@ int parse_util_lineno( const wchar_t *str, size_t offset ) { - if (! str) - return 0; + if (! str) + return 0; - int res = 1; - for( size_t i=0; str[i] && i= off2-off-1 ) - { - line_offset2 = off2-off-1; - } - - return off + line_offset2; - + size_t off = parse_util_get_offset_from_line( buff, line ); + size_t off2 = parse_util_get_offset_from_line( buff, line+1 ); + long line_offset2 = line_offset; + + if( off == (size_t)(-1) ) + { + return -1; + } + + if( off2 == (size_t)(-1) ) + { + off2 = wcslen( buff )+1; + } + + if( line_offset2 < 0 ) + { + line_offset2 = 0; + } + + if( line_offset2 >= off2-off-1 ) + { + line_offset2 = off2-off-1; + } + + return off + line_offset2; + } -int parse_util_locate_cmdsubst( const wchar_t *in, - wchar_t **begin, - wchar_t **end, - int allow_incomplete ) +int parse_util_locate_cmdsubst( const wchar_t *in, + wchar_t **begin, + wchar_t **end, + int allow_incomplete ) { - wchar_t *pos; - wchar_t prev=0; - int syntax_error=0; - int paran_count=0; + wchar_t *pos; + wchar_t prev=0; + int syntax_error=0; + int paran_count=0; - wchar_t *paran_begin=0, *paran_end=0; + wchar_t *paran_begin=0, *paran_end=0; - CHECK( in, 0 ); - - for( pos = (wchar_t *)in; *pos; pos++ ) - { - if( prev != '\\' ) - { - if( wcschr( L"\'\"", *pos ) ) - { - wchar_t *q_end = quote_end( pos ); - if( q_end && *q_end) - { - pos=q_end; - } - else - { - break; - } - } - else - { - if( *pos == '(' ) - { - if(( paran_count == 0)&&(paran_begin==0)) - { - paran_begin = pos; - } - - paran_count++; - } - else if( *pos == ')' ) - { + CHECK( in, 0 ); - paran_count--; + for( pos = (wchar_t *)in; *pos; pos++ ) + { + if( prev != '\\' ) + { + if( wcschr( L"\'\"", *pos ) ) + { + wchar_t *q_end = quote_end( pos ); + if( q_end && *q_end) + { + pos=q_end; + } + else + { + break; + } + } + else + { + if( *pos == '(' ) + { + if(( paran_count == 0)&&(paran_begin==0)) + { + paran_begin = pos; + } - if( (paran_count == 0) && (paran_end == 0) ) - { - paran_end = pos; - break; - } - - if( paran_count < 0 ) - { - syntax_error = 1; - break; - } - } - } - - } - prev = *pos; - } - - syntax_error |= (paran_count < 0 ); - syntax_error |= ((paran_count>0)&&(!allow_incomplete)); - - if( syntax_error ) - { - return -1; - } - - if( paran_begin == 0 ) - { - return 0; - } + paran_count++; + } + else if( *pos == ')' ) + { - if( begin ) - { - *begin = paran_begin; - } + paran_count--; - if( end ) - { - *end = paran_count?(wchar_t *)in+wcslen(in):paran_end; - } - - return 1; + if( (paran_count == 0) && (paran_end == 0) ) + { + paran_end = pos; + break; + } + + if( paran_count < 0 ) + { + syntax_error = 1; + break; + } + } + } + + } + prev = *pos; + } + + syntax_error |= (paran_count < 0 ); + syntax_error |= ((paran_count>0)&&(!allow_incomplete)); + + if( syntax_error ) + { + return -1; + } + + if( paran_begin == 0 ) + { + return 0; + } + + if( begin ) + { + *begin = paran_begin; + } + + if( end ) + { + *end = paran_count?(wchar_t *)in+wcslen(in):paran_end; + } + + return 1; } 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 ) { - wchar_t *begin, *end; - wchar_t *pos; - const wchar_t *cursor = buff + cursor_pos; - - CHECK( buff, ); + wchar_t *begin, *end; + wchar_t *pos; + const wchar_t *cursor = buff + cursor_pos; - if( a ) - { - *a = (wchar_t *)buff; - } + CHECK( buff, ); - if( b ) - { - *b = (wchar_t *)buff+wcslen(buff); - } - - pos = (wchar_t *)buff; - - while( 1 ) - { - if( parse_util_locate_cmdsubst( pos, - &begin, - &end, - 1 ) <= 0) - { - /* - No subshell found - */ - break; - } + if( a ) + { + *a = (wchar_t *)buff; + } - if( !end ) - { - end = (wchar_t *)buff + wcslen(buff); - } + if( b ) + { + *b = (wchar_t *)buff+wcslen(buff); + } - if(( begin < cursor ) && (end >= cursor) ) - { - begin++; + pos = (wchar_t *)buff; - if( a ) - { - *a = begin; - } + while( 1 ) + { + if( parse_util_locate_cmdsubst( pos, + &begin, + &end, + 1 ) <= 0) + { + /* + No subshell found + */ + break; + } - if( b ) - { - *b = end; - } + if( !end ) + { + end = (wchar_t *)buff + wcslen(buff); + } - break; - } + if(( begin < cursor ) && (end >= cursor) ) + { + begin++; + + if( a ) + { + *a = begin; + } + + if( b ) + { + *b = end; + } + + break; + } + + if( !*end ) + { + break; + } + + pos = end+1; + } - if( !*end ) - { - break; - } - - pos = end+1; - } - } /** Get the beginning and end of the job or process definition under the cursor */ static void job_or_process_extent( const wchar_t *buff, - size_t cursor_pos, - const wchar_t **a, - const wchar_t **b, - int process ) + size_t cursor_pos, + const wchar_t **a, + const wchar_t **b, + int process ) { - const wchar_t *begin, *end; - long pos; - wchar_t *buffcpy; - int finished=0; - - tokenizer tok; + const wchar_t *begin, *end; + long pos; + wchar_t *buffcpy; + int finished=0; - CHECK( buff, ); - - if( a ) - { - *a=0; - } + tokenizer tok; - if( b ) - { - *b = 0; - } - - parse_util_cmdsubst_extent( buff, cursor_pos, &begin, &end ); - if( !end || !begin ) - { - return; - } - - pos = cursor_pos - (begin - buff); + CHECK( buff, ); - if( a ) - { - *a = begin; - } - - if( b ) - { - *b = end; - } + if( a ) + { + *a=0; + } - buffcpy = wcsndup( begin, end-begin ); + if( b ) + { + *b = 0; + } - if( !buffcpy ) - { - DIE_MEM(); - } + parse_util_cmdsubst_extent( buff, cursor_pos, &begin, &end ); + if( !end || !begin ) + { + return; + } - for( tok_init( &tok, buffcpy, TOK_ACCEPT_UNFINISHED ); - tok_has_next( &tok ) && !finished; - tok_next( &tok ) ) - { - int tok_begin = tok_get_pos( &tok ); + pos = cursor_pos - (begin - buff); - switch( tok_last_type( &tok ) ) - { - case TOK_PIPE: - { - if( !process ) - { - break; - } - } - - case TOK_END: - case TOK_BACKGROUND: - { - - if( tok_begin >= pos ) - { - finished=1; - if( b ) - { - *b = (wchar_t *)buff + tok_begin; - } - } - else - { - if( a ) - { - *a = (wchar_t *)buff + tok_begin+1; - } - } + if( a ) + { + *a = begin; + } - break; - } - } - } + if( b ) + { + *b = end; + } - free( buffcpy); - - tok_destroy( &tok ); + buffcpy = wcsndup( begin, end-begin ); + + if( !buffcpy ) + { + DIE_MEM(); + } + + for( tok_init( &tok, buffcpy, TOK_ACCEPT_UNFINISHED ); + tok_has_next( &tok ) && !finished; + tok_next( &tok ) ) + { + int tok_begin = tok_get_pos( &tok ); + + switch( tok_last_type( &tok ) ) + { + case TOK_PIPE: + { + if( !process ) + { + break; + } + } + + case TOK_END: + case TOK_BACKGROUND: + { + + if( tok_begin >= pos ) + { + finished=1; + if( b ) + { + *b = (wchar_t *)buff + tok_begin; + } + } + else + { + if( a ) + { + *a = (wchar_t *)buff + tok_begin+1; + } + } + + break; + } + } + } + + free( buffcpy); + + tok_destroy( &tok ); } void parse_util_process_extent( const wchar_t *buff, - size_t pos, - const wchar_t **a, - const wchar_t **b ) + size_t pos, + const wchar_t **a, + const wchar_t **b ) { - job_or_process_extent( buff, pos, a, b, 1 ); + job_or_process_extent( buff, pos, a, b, 1 ); } void parse_util_job_extent( const wchar_t *buff, - size_t pos, - const wchar_t **a, - const wchar_t **b ) + size_t pos, + const wchar_t **a, + const wchar_t **b ) { - job_or_process_extent( buff,pos,a, b, 0 ); + job_or_process_extent( buff,pos,a, b, 0 ); } 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 ) { - const wchar_t *begin, *end; - long pos; - wchar_t *buffcpy; + const wchar_t *begin, *end; + long pos; + wchar_t *buffcpy; - tokenizer tok; + tokenizer tok; - const wchar_t *a = NULL, *b = NULL, *pa = NULL, *pb = NULL; - - CHECK( buff, ); - - assert( cursor_pos >= 0 ); - - parse_util_cmdsubst_extent( buff, cursor_pos, &begin, &end ); + const wchar_t *a = NULL, *b = NULL, *pa = NULL, *pb = NULL; - if( !end || !begin ) - { - return; - } - - pos = cursor_pos - (begin - buff); - - a = buff + pos; - b = a; - pa = buff + pos; - pb = pa; - - assert( begin >= buff ); - assert( begin <= (buff+wcslen(buff) ) ); - assert( end >= begin ); - assert( end <= (buff+wcslen(buff) ) ); - - buffcpy = wcsndup( begin, end-begin ); - - if( !buffcpy ) - { - DIE_MEM(); - } + CHECK( buff, ); - for( tok_init( &tok, buffcpy, TOK_ACCEPT_UNFINISHED | TOK_SQUASH_ERRORS ); - tok_has_next( &tok ); - tok_next( &tok ) ) - { - size_t tok_begin = tok_get_pos( &tok ); - size_t tok_end = tok_begin; + assert( cursor_pos >= 0 ); - /* - Calculate end of token - */ - if( tok_last_type( &tok ) == TOK_STRING ) - { - tok_end += wcslen(tok_last(&tok)); - } - - /* - Cursor was before beginning of this token, means that the - cursor is between two tokens, so we set it to a zero element - string and break - */ - if( tok_begin > pos ) - { - a = b = (wchar_t *)buff + pos; - break; - } + parse_util_cmdsubst_extent( buff, cursor_pos, &begin, &end ); - /* - If cursor is inside the token, this is the token we are - looking for. If so, set a and b and break - */ - if( (tok_last_type( &tok ) == TOK_STRING) && (tok_end >= pos ) ) - { - a = begin + tok_get_pos( &tok ); - b = a + wcslen(tok_last(&tok)); - break; - } - - /* - Remember previous string token - */ - if( tok_last_type( &tok ) == TOK_STRING ) - { - pa = begin + tok_get_pos( &tok ); - pb = pa + wcslen(tok_last(&tok)); - } - } + if( !end || !begin ) + { + return; + } - free( buffcpy); - - tok_destroy( &tok ); + pos = cursor_pos - (begin - buff); - if( tok_begin ) - { - *tok_begin = a; - } + a = buff + pos; + b = a; + pa = buff + pos; + pb = pa; - if( tok_end ) - { - *tok_end = b; - } + assert( begin >= buff ); + assert( begin <= (buff+wcslen(buff) ) ); + assert( end >= begin ); + assert( end <= (buff+wcslen(buff) ) ); - if( prev_begin ) - { - *prev_begin = pa; - } + buffcpy = wcsndup( begin, end-begin ); - if( prev_end ) - { - *prev_end = pb; - } - - assert( pa >= buff ); - assert( pa <= (buff+wcslen(buff) ) ); - assert( pb >= pa ); - assert( pb <= (buff+wcslen(buff) ) ); + if( !buffcpy ) + { + DIE_MEM(); + } + + for( tok_init( &tok, buffcpy, TOK_ACCEPT_UNFINISHED | TOK_SQUASH_ERRORS ); + tok_has_next( &tok ); + tok_next( &tok ) ) + { + size_t tok_begin = tok_get_pos( &tok ); + size_t tok_end = tok_begin; + + /* + Calculate end of token + */ + if( tok_last_type( &tok ) == TOK_STRING ) + { + tok_end += wcslen(tok_last(&tok)); + } + + /* + Cursor was before beginning of this token, means that the + cursor is between two tokens, so we set it to a zero element + string and break + */ + if( tok_begin > pos ) + { + a = b = (wchar_t *)buff + pos; + break; + } + + /* + If cursor is inside the token, this is the token we are + looking for. If so, set a and b and break + */ + if( (tok_last_type( &tok ) == TOK_STRING) && (tok_end >= pos ) ) + { + a = begin + tok_get_pos( &tok ); + b = a + wcslen(tok_last(&tok)); + break; + } + + /* + Remember previous string token + */ + if( tok_last_type( &tok ) == TOK_STRING ) + { + pa = begin + tok_get_pos( &tok ); + pb = pa + wcslen(tok_last(&tok)); + } + } + + free( buffcpy); + + tok_destroy( &tok ); + + if( tok_begin ) + { + *tok_begin = a; + } + + if( tok_end ) + { + *tok_end = b; + } + + if( prev_begin ) + { + *prev_begin = pa; + } + + if( prev_end ) + { + *prev_end = pb; + } + + assert( pa >= buff ); + assert( pa <= (buff+wcslen(buff) ) ); + assert( pb >= pa ); + assert( pb <= (buff+wcslen(buff) ) ); } void parse_util_set_argv( const wchar_t * const *argv, const wcstring_list_t &named_arguments ) { - if( *argv ) - { - const wchar_t * const *arg; - wcstring sb; - - for( arg=argv; *arg; arg++ ) - { - if( arg != argv ) - { - sb.append(ARRAY_SEP_STR); - } + if( *argv ) + { + const wchar_t * const *arg; + wcstring sb; + + for( arg=argv; *arg; arg++ ) + { + if( arg != argv ) + { + sb.append(ARRAY_SEP_STR); + } sb.append(*arg); - } - - env_set( L"argv", sb.c_str(), ENV_LOCAL ); - } - else - { - env_set( L"argv", 0, ENV_LOCAL ); - } + } - if( named_arguments.size() ) - { - const wchar_t * const *arg; - size_t i; - - for( i=0, arg=argv; i < named_arguments.size(); i++ ) - { - env_set( named_arguments.at(i).c_str(), *arg, ENV_LOCAL ); + env_set( L"argv", sb.c_str(), ENV_LOCAL ); + } + else + { + env_set( L"argv", 0, ENV_LOCAL ); + } + + if( named_arguments.size() ) + { + const wchar_t * const *arg; + size_t i; + + for( i=0, arg=argv; i < named_arguments.size(); i++ ) + { + env_set( named_arguments.at(i).c_str(), *arg, ENV_LOCAL ); + + if( *arg ) + arg++; + } + + + } - if( *arg ) - arg++; - } - - - } - } wchar_t *parse_util_unescape_wildcards( const wchar_t *str ) { - wchar_t *in, *out; - wchar_t *unescaped; + wchar_t *in, *out; + wchar_t *unescaped; - CHECK( str, 0 ); - - unescaped = wcsdup(str); + CHECK( str, 0 ); - if( !unescaped ) - { - DIE_MEM(); - } - - for( in=out=unescaped; *in; in++ ) - { - switch( *in ) - { - case L'\\': - { + unescaped = wcsdup(str); + + if( !unescaped ) + { + DIE_MEM(); + } + + for( in=out=unescaped; *in; in++ ) + { + switch( *in ) + { + case L'\\': + { switch ( *(in + 1) ) { case L'*': @@ -635,30 +635,30 @@ wchar_t *parse_util_unescape_wildcards( const wchar_t *str ) break; } } - break; - } - - case L'*': - { - *(out++)=ANY_STRING; - break; - } - - case L'?': - { - *(out++)=ANY_CHAR; - break; - } - - default: - { - *(out++)=*in; - break; - } - } - } + break; + } + + case L'*': + { + *(out++)=ANY_STRING; + break; + } + + case L'?': + { + *(out++)=ANY_CHAR; + break; + } + + default: + { + *(out++)=*in; + break; + } + } + } *out = *in; - return unescaped; + return unescaped; } @@ -669,105 +669,105 @@ wchar_t *parse_util_unescape_wildcards( const wchar_t *str ) */ static wchar_t get_quote( const wchar_t *cmd, size_t len ) { - size_t i=0; - wchar_t res=0; + size_t i=0; + wchar_t res=0; - while( 1 ) - { - if( !cmd[i] ) - break; + while( 1 ) + { + if( !cmd[i] ) + break; - if( cmd[i] == L'\\' ) - { - i++; - if( !cmd[i] ) - break; - i++; - } - else - { - if( cmd[i] == L'\'' || cmd[i] == L'\"' ) - { - const wchar_t *end = quote_end( &cmd[i] ); - //fwprintf( stderr, L"Jump %d\n", end-cmd ); - if(( end == 0 ) || (!*end) || (end-cmd > len)) - { - res = cmd[i]; - break; - } - i = end-cmd+1; - } - else - i++; - } - } + if( cmd[i] == L'\\' ) + { + i++; + if( !cmd[i] ) + break; + i++; + } + else + { + if( cmd[i] == L'\'' || cmd[i] == L'\"' ) + { + const wchar_t *end = quote_end( &cmd[i] ); + //fwprintf( stderr, L"Jump %d\n", end-cmd ); + if(( end == 0 ) || (!*end) || (end-cmd > len)) + { + res = cmd[i]; + break; + } + i = end-cmd+1; + } + else + i++; + } + } - return res; + return res; } void parse_util_get_parameter_info( const wcstring &cmd, const size_t pos, wchar_t *quote, size_t *offset, int *type ) { - size_t prev_pos=0; - wchar_t last_quote = '\0'; - int unfinished; + size_t prev_pos=0; + wchar_t last_quote = '\0'; + int unfinished; - tokenizer tok; - tok_init( &tok, cmd.c_str(), TOK_ACCEPT_UNFINISHED | TOK_SQUASH_ERRORS ); + tokenizer tok; + tok_init( &tok, cmd.c_str(), TOK_ACCEPT_UNFINISHED | TOK_SQUASH_ERRORS ); - for( ; tok_has_next( &tok ); tok_next( &tok ) ) - { - if( tok_get_pos( &tok ) > pos ) - break; + for( ; tok_has_next( &tok ); tok_next( &tok ) ) + { + if( tok_get_pos( &tok ) > pos ) + break; - if( tok_last_type( &tok ) == TOK_STRING ) - last_quote = get_quote( tok_last( &tok ), - pos - tok_get_pos( &tok ) ); + if( tok_last_type( &tok ) == TOK_STRING ) + last_quote = get_quote( tok_last( &tok ), + pos - tok_get_pos( &tok ) ); - if( type != NULL ) - *type = tok_last_type( &tok ); + if( type != NULL ) + *type = tok_last_type( &tok ); - prev_pos = tok_get_pos( &tok ); - } + prev_pos = tok_get_pos( &tok ); + } + + tok_destroy( &tok ); - tok_destroy( &tok ); - wchar_t *cmd_tmp = wcsdup(cmd.c_str()); - cmd_tmp[pos]=0; - size_t cmdlen = wcslen( cmd_tmp ); - unfinished = (cmdlen==0); - if( !unfinished ) - { - unfinished = (quote != 0); + cmd_tmp[pos]=0; + size_t cmdlen = wcslen( cmd_tmp ); + unfinished = (cmdlen==0); + if( !unfinished ) + { + unfinished = (quote != 0); - if( !unfinished ) - { - if( wcschr( L" \t\n\r", cmd_tmp[cmdlen-1] ) != 0 ) - { - if( ( cmdlen == 1) || (cmd_tmp[cmdlen-2] != L'\\') ) - { - unfinished=1; - } - } - } - } + if( !unfinished ) + { + if( wcschr( L" \t\n\r", cmd_tmp[cmdlen-1] ) != 0 ) + { + if( ( cmdlen == 1) || (cmd_tmp[cmdlen-2] != L'\\') ) + { + unfinished=1; + } + } + } + } - if( quote ) - *quote = last_quote; + if( quote ) + *quote = last_quote; - if( offset != 0 ) - { - if( !unfinished ) - { - while( (cmd_tmp[prev_pos] != 0) && (wcschr( L";|",cmd_tmp[prev_pos])!= 0) ) - prev_pos++; + if( offset != 0 ) + { + if( !unfinished ) + { + while( (cmd_tmp[prev_pos] != 0) && (wcschr( L";|",cmd_tmp[prev_pos])!= 0) ) + prev_pos++; - *offset = prev_pos; - } - else - { - *offset = pos; - } - } + *offset = prev_pos; + } + else + { + *offset = pos; + } + } free(cmd_tmp); } @@ -799,7 +799,7 @@ wcstring parse_util_escape_string_with_quote( const wcstring &cmd, wchar_t quote break; } } - + if (unescapable) { result = escape_string(cmd, ESCAPE_ALL | ESCAPE_NO_QUOTED); diff --git a/parse_util.h b/parse_util.h index 27719851b..4b8699f8e 100644 --- a/parse_util.h +++ b/parse_util.h @@ -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 ); diff --git a/parser.cpp b/parser.cpp index 1824cebd9..48b96fb33 100644 --- a/parser.cpp +++ b/parser.cpp @@ -191,7 +191,7 @@ The fish parser. Contains functions for parsing and evaluating code. /** While block description -*/ +*/ #define WHILE_BLOCK N_( L"'while' block" ) /** @@ -199,10 +199,10 @@ The fish parser. Contains functions for parsing and evaluating code. */ #define FOR_BLOCK N_( L"'for' block" ) -/** - Breakpoint block +/** + Breakpoint block */ -#define BREAKPOINT_BLOCK N_( L"Block created by breakpoint" ) +#define BREAKPOINT_BLOCK N_( L"Block created by breakpoint" ) @@ -282,87 +282,87 @@ The fish parser. Contains functions for parsing and evaluating code. struct block_lookup_entry { - /** - The block type id. The legal values are defined in parser.h. - */ - block_type_t type; + /** + The block type id. The legal values are defined in parser.h. + */ + block_type_t type; - /** - The name of the builtin that creates this type of block, if any. - */ - const wchar_t *name; - - /** - A description of this block type - */ - const wchar_t *desc; + /** + The name of the builtin that creates this type of block, if any. + */ + const wchar_t *name; + + /** + A description of this block type + */ + const wchar_t *desc; } - ; + ; /** List of all legal block types */ static const struct block_lookup_entry block_lookup[]= { - { - WHILE, L"while", WHILE_BLOCK - } - , - { - FOR, L"for", FOR_BLOCK - } - , - { - IF, L"if", IF_BLOCK - } - , - { - FUNCTION_DEF, L"function", FUNCTION_DEF_BLOCK - } - , - { - FUNCTION_CALL, 0, FUNCTION_CALL_BLOCK - } - , - { - FUNCTION_CALL_NO_SHADOW, 0, FUNCTION_CALL_NO_SHADOW_BLOCK - } - , - { - SWITCH, L"switch", SWITCH_BLOCK - } - , - { - FAKE, 0, FAKE_BLOCK - } - , - { - TOP, 0, TOP_BLOCK - } - , - { - SUBST, 0, SUBST_BLOCK - } - , - { - BEGIN, L"begin", BEGIN_BLOCK - } - , - { - SOURCE, L".", SOURCE_BLOCK - } - , - { - EVENT, 0, EVENT_BLOCK - } - , - { - BREAKPOINT, L"breakpoint", BREAKPOINT_BLOCK - } - , - { - (block_type_t)0, 0, 0 - } + { + WHILE, L"while", WHILE_BLOCK + } + , + { + FOR, L"for", FOR_BLOCK + } + , + { + IF, L"if", IF_BLOCK + } + , + { + FUNCTION_DEF, L"function", FUNCTION_DEF_BLOCK + } + , + { + FUNCTION_CALL, 0, FUNCTION_CALL_BLOCK + } + , + { + FUNCTION_CALL_NO_SHADOW, 0, FUNCTION_CALL_NO_SHADOW_BLOCK + } + , + { + SWITCH, L"switch", SWITCH_BLOCK + } + , + { + FAKE, 0, FAKE_BLOCK + } + , + { + TOP, 0, TOP_BLOCK + } + , + { + SUBST, 0, SUBST_BLOCK + } + , + { + BEGIN, L"begin", BEGIN_BLOCK + } + , + { + SOURCE, L".", SOURCE_BLOCK + } + , + { + EVENT, 0, EVENT_BLOCK + } + , + { + BREAKPOINT, L"breakpoint", BREAKPOINT_BLOCK + } + , + { + (block_type_t)0, 0, 0 + } }; static bool job_should_skip_elseif(const job_t *job, const block_t *current_block); @@ -379,7 +379,7 @@ parser_t::parser_t(enum parser_type_t type, bool errors) : current_block(NULL), block_io(NULL) { - + } /* A pointer to the principal parser (which is a static local) */ @@ -420,91 +420,91 @@ void parser_t::skip_all_blocks(void) static int block_count( block_t *b ) { - if( b==0) - return 0; - return( block_count(b->outer)+1); + if( b==0) + return 0; + return( block_count(b->outer)+1); } */ void parser_t::push_block( block_t *newv ) { const enum block_type_t type = newv->type(); - newv->src_lineno = parser_t::get_lineno(); - newv->src_filename = parser_t::current_filename()?intern(parser_t::current_filename()):0; - - newv->outer = current_block; + newv->src_lineno = parser_t::get_lineno(); + newv->src_filename = parser_t::current_filename()?intern(parser_t::current_filename()):0; + + newv->outer = current_block; if (current_block && current_block->skip) newv->mark_as_fake(); - - /* - New blocks should be skipped if the outer block is skipped, - except TOP ans SUBST block, which open up new environments. Fake - blocks should always be skipped. Rather complicated... :-( - */ - newv->skip=current_block?current_block->skip:0; - - /* - Type TOP and SUBST are never skipped - */ - if( type == TOP || type == SUBST ) - { - newv->skip = 0; - } - /* - Fake blocks and function definition blocks are never executed - */ - if( type == FAKE || type == FUNCTION_DEF ) - { - newv->skip = 1; - } - - newv->job = 0; - newv->loop_status=LOOP_NORMAL; + /* + New blocks should be skipped if the outer block is skipped, + except TOP ans SUBST block, which open up new environments. Fake + blocks should always be skipped. Rather complicated... :-( + */ + newv->skip=current_block?current_block->skip:0; - current_block = newv; + /* + Type TOP and SUBST are never skipped + */ + if( type == TOP || type == SUBST ) + { + newv->skip = 0; + } - if( (newv->type() != FUNCTION_DEF) && - (newv->type() != FAKE) && - (newv->type() != TOP) ) - { - env_push( type == FUNCTION_CALL ); + /* + Fake blocks and function definition blocks are never executed + */ + if( type == FAKE || type == FUNCTION_DEF ) + { + newv->skip = 1; + } + + newv->job = 0; + newv->loop_status=LOOP_NORMAL; + + current_block = newv; + + if( (newv->type() != FUNCTION_DEF) && + (newv->type() != FAKE) && + (newv->type() != TOP) ) + { + env_push( type == FUNCTION_CALL ); newv->wants_pop_env = true; - } + } } void parser_t::pop_block() { - block_t *old = current_block; - if( !current_block ) - { - debug( 1, - L"function %s called on empty block stack.", - __func__); - bugreport(); - return; - } - - current_block = current_block->outer; - + block_t *old = current_block; + if( !current_block ) + { + debug( 1, + L"function %s called on empty block stack.", + __func__); + bugreport(); + return; + } + + current_block = current_block->outer; + if (old->wants_pop_env) env_pop(); - + delete old; } const wchar_t *parser_t::get_block_desc( int block ) const { - int i; - - for( i=0; block_lookup[i].desc; i++ ) - { - if( block_lookup[i].type == block ) - { - return _( block_lookup[i].desc ); - } - } - return _(UNKNOWN_BLOCK); + int i; + + for( i=0; block_lookup[i].desc; i++ ) + { + if( block_lookup[i].type == block ) + { + return _( block_lookup[i].desc ); + } + } + return _(UNKNOWN_BLOCK); } /** @@ -512,12 +512,12 @@ const wchar_t *parser_t::get_block_desc( int block ) const */ static int parser_is_pipe_forbidden( const wcstring &word ) { - return contains( word, - L"exec", - L"case", - L"break", - L"return", - L"continue" ); + return contains( word, + L"exec", + L"case", + L"break", + L"return", + L"continue" ); } /** @@ -525,87 +525,87 @@ static int parser_is_pipe_forbidden( const wcstring &word ) */ static const wchar_t *parser_find_end( const wchar_t * buff ) { - tokenizer tok; - int had_cmd=0; - int count = 0; - int error=0; - int mark=0; + tokenizer tok; + int had_cmd=0; + int count = 0; + int error=0; + int mark=0; - CHECK( buff, 0 ); + CHECK( buff, 0 ); - for( tok_init( &tok, buff, 0 ); - tok_has_next( &tok ) && !error; - tok_next( &tok ) ) - { - int last_type = tok_last_type( &tok ); - switch( last_type ) - { - case TOK_STRING: - { - if( !had_cmd ) - { - if( wcscmp(tok_last(&tok), L"end")==0) - { - count--; - } - else if( parser_keywords_is_block( tok_last(&tok) ) ) - { - count++; - } + for( tok_init( &tok, buff, 0 ); + tok_has_next( &tok ) && !error; + tok_next( &tok ) ) + { + int last_type = tok_last_type( &tok ); + switch( last_type ) + { + case TOK_STRING: + { + if( !had_cmd ) + { + if( wcscmp(tok_last(&tok), L"end")==0) + { + count--; + } + else if( parser_keywords_is_block( tok_last(&tok) ) ) + { + count++; + } - if( count < 0 ) - { - error = 1; - } - had_cmd = 1; - } - break; - } + if( count < 0 ) + { + error = 1; + } + had_cmd = 1; + } + break; + } - case TOK_END: - { - had_cmd = 0; - break; - } + case TOK_END: + { + had_cmd = 0; + break; + } - case TOK_PIPE: - case TOK_BACKGROUND: - { - if( had_cmd ) - { - had_cmd = 0; - } - else - { - error = 1; - } - break; + case TOK_PIPE: + case TOK_BACKGROUND: + { + if( had_cmd ) + { + had_cmd = 0; + } + else + { + error = 1; + } + break; - } + } - case TOK_ERROR: - error = 1; - break; + case TOK_ERROR: + error = 1; + break; - default: - break; + default: + break; - } - if(!count) - { - tok_next( &tok ); - mark = tok_get_pos( &tok ); - break; - } + } + if(!count) + { + tok_next( &tok ); + mark = tok_get_pos( &tok ); + break; + } - } + } - tok_destroy( &tok ); - if(!count && !error){ + tok_destroy( &tok ); + if(!count && !error){ - return buff+mark; - } - return 0; + return buff+mark; + } + return 0; } @@ -626,16 +626,16 @@ void parser_t::allow_function() void parser_t::error( int ec, int p, const wchar_t *str, ... ) { - va_list va; + va_list va; - CHECK( str, ); - - error_code = ec; - err_pos = p; + CHECK( str, ); - va_start( va, str ); + error_code = ec; + err_pos = p; + + va_start( va, str ); err_buff = vformat_string(str, va); - va_end( va ); + va_end( va ); } @@ -643,108 +643,108 @@ void parser_t::error( int ec, int p, const wchar_t *str, ... ) Print profiling information to the specified stream */ static void print_profile( const std::vector &items, - size_t pos, - FILE *out ) + size_t pos, + FILE *out ) { - const profile_item_t *me, *prev; - size_t i; - int my_time; + const profile_item_t *me, *prev; + size_t i; + int my_time; - if( pos >= items.size() ) - { - return; - } - - me= &items.at(pos); - if( !me->skipped ) - { - my_time=me->parse+me->exec; + if( pos >= items.size() ) + { + return; + } - for( i=pos+1; iskipped ) - { - continue; - } - - if( prev->level <= me->level ) - { - break; - } + me= &items.at(pos); + if( !me->skipped ) + { + my_time=me->parse+me->exec; - if( prev->level > me->level+1 ) - { - continue; - } + for( i=pos+1; iskipped ) + { + continue; + } - my_time -= prev->parse; - my_time -= prev->exec; - } + if( prev->level <= me->level ) + { + break; + } - if( me->cmd.size() > 0 ) - { - if( fwprintf( out, L"%d\t%d\t", my_time, me->parse+me->exec ) < 0 ) - { - wperror( L"fwprintf" ); - return; - } - - for( i=0; ilevel; i++ ) - { - if( fwprintf( out, L"-" ) < 0 ) - { - wperror( L"fwprintf" ); - return; - } + if( prev->level > me->level+1 ) + { + continue; + } - } - if( fwprintf( out, L"> %ls\n", me->cmd.c_str() ) < 0 ) - { - wperror( L"fwprintf" ); - return; - } + my_time -= prev->parse; + my_time -= prev->exec; + } - } - } - print_profile( items, pos+1, out ); + if( me->cmd.size() > 0 ) + { + if( fwprintf( out, L"%d\t%d\t", my_time, me->parse+me->exec ) < 0 ) + { + wperror( L"fwprintf" ); + return; + } + + for( i=0; ilevel; i++ ) + { + if( fwprintf( out, L"-" ) < 0 ) + { + wperror( L"fwprintf" ); + return; + } + + } + if( fwprintf( out, L"> %ls\n", me->cmd.c_str() ) < 0 ) + { + wperror( L"fwprintf" ); + return; + } + + } + } + print_profile( items, pos+1, out ); } void parser_t::destroy() { - if( profile ) - { - /* Save profiling information. OK to not use CLO_EXEC here because this is called while fish is dying (and hence will not fork) */ - FILE *f = fopen( profile, "w" ); - if( !f ) - { - debug( 1, - _(L"Could not write profiling information to file '%s'"), - profile ); - } - else - { - if( fwprintf( f, - _(L"Time\tSum\tCommand\n"), - profile_items.size() ) < 0 ) - { - wperror( L"fwprintf" ); - } - else - { - print_profile( profile_items, 0, f ); - } - - if( fclose( f ) ) - { - wperror( L"fclose" ); - } - } - } + if( profile ) + { + /* Save profiling information. OK to not use CLO_EXEC here because this is called while fish is dying (and hence will not fork) */ + FILE *f = fopen( profile, "w" ); + if( !f ) + { + debug( 1, + _(L"Could not write profiling information to file '%s'"), + profile ); + } + else + { + if( fwprintf( f, + _(L"Time\tSum\tCommand\n"), + profile_items.size() ) < 0 ) + { + wperror( L"fwprintf" ); + } + else + { + print_profile( profile_items, 0, f ); + } + + if( fclose( f ) ) + { + wperror( L"fclose" ); + } + } + } lineinfo.clear(); - forbidden_function.clear(); + forbidden_function.clear(); } @@ -756,21 +756,21 @@ void parser_t::destroy() */ void parser_t::print_errors( wcstring &target, const wchar_t *prefix ) { - CHECK( prefix, ); - - if( error_code && ! err_buff.empty() ) - { - int tmp; + CHECK( prefix, ); - append_format( target, L"%ls: %ls\n", prefix, err_buff.c_str() ); + if( error_code && ! err_buff.empty() ) + { + int tmp; - tmp = current_tokenizer_pos; - current_tokenizer_pos = err_pos; + append_format( target, L"%ls: %ls\n", prefix, err_buff.c_str() ); - append_format( target, L"%ls", this->current_line() ); + tmp = current_tokenizer_pos; + current_tokenizer_pos = err_pos; - current_tokenizer_pos=tmp; - } + append_format( target, L"%ls", this->current_line() ); + + current_tokenizer_pos=tmp; + } } /** @@ -778,215 +778,215 @@ void parser_t::print_errors( wcstring &target, const wchar_t *prefix ) */ void parser_t::print_errors_stderr() { - if( error_code && ! err_buff.empty() ) - { - debug( 0, L"%ls", err_buff.c_str() ); - int tmp; - - tmp = current_tokenizer_pos; - current_tokenizer_pos = err_pos; - - fwprintf( stderr, L"%ls", this->current_line() ); - - current_tokenizer_pos=tmp; - } - + if( error_code && ! err_buff.empty() ) + { + debug( 0, L"%ls", err_buff.c_str() ); + int tmp; + + tmp = current_tokenizer_pos; + current_tokenizer_pos = err_pos; + + fwprintf( stderr, L"%ls", this->current_line() ); + + current_tokenizer_pos=tmp; + } + } int parser_t::eval_args( const wchar_t *line, std::vector &args ) { - tokenizer tok; - + tokenizer tok; + expand_flags_t eflags = 0; if (! show_errors) eflags |= EXPAND_NO_DESCRIPTIONS; if (this->parser_type != PARSER_TYPE_GENERAL) eflags |= EXPAND_SKIP_CMDSUBST; - - /* - eval_args may be called while evaulating another command, so we - save the previous tokenizer and restore it on exit - */ - tokenizer *previous_tokenizer=current_tokenizer; - int previous_pos=current_tokenizer_pos; - int do_loop=1; - CHECK( line, 1 ); -// CHECK( args, 1 ); - + /* + eval_args may be called while evaulating another command, so we + save the previous tokenizer and restore it on exit + */ + tokenizer *previous_tokenizer=current_tokenizer; + int previous_pos=current_tokenizer_pos; + int do_loop=1; + + CHECK( line, 1 ); +// CHECK( args, 1 ); + // PCA we need to suppress calling proc_push_interactive off of the main thread. I'm not sure exactly what it does. if (this->parser_type == PARSER_TYPE_GENERAL) proc_push_interactive(0); - - current_tokenizer = &tok; - current_tokenizer_pos = 0; - - tok_init( &tok, line, (show_errors ? 0 : TOK_SQUASH_ERRORS) ); - error_code=0; - for(;do_loop && tok_has_next( &tok) ; tok_next( &tok ) ) - { - current_tokenizer_pos = tok_get_pos( &tok ); - switch(tok_last_type( &tok ) ) - { - case TOK_STRING: - { - const wcstring tmp = tok_last(&tok); - if( expand_string(tmp, args, eflags) == EXPAND_ERROR ) - { - err_pos=tok_get_pos( &tok ); - do_loop=0; - } - break; - } - - case TOK_END: - { - break; - } - - case TOK_ERROR: - { - if (show_errors) + current_tokenizer = &tok; + current_tokenizer_pos = 0; + + tok_init( &tok, line, (show_errors ? 0 : TOK_SQUASH_ERRORS) ); + error_code=0; + + for(;do_loop && tok_has_next( &tok) ; tok_next( &tok ) ) + { + current_tokenizer_pos = tok_get_pos( &tok ); + switch(tok_last_type( &tok ) ) + { + case TOK_STRING: + { + const wcstring tmp = tok_last(&tok); + if( expand_string(tmp, args, eflags) == EXPAND_ERROR ) + { + err_pos=tok_get_pos( &tok ); + do_loop=0; + } + break; + } + + case TOK_END: + { + break; + } + + case TOK_ERROR: + { + if (show_errors) error( SYNTAX_ERROR, tok_get_pos( &tok ), TOK_ERR_MSG, tok_last(&tok) ); - do_loop=0; - break; - } + do_loop=0; + break; + } - default: - { + default: + { if (show_errors) error( SYNTAX_ERROR, tok_get_pos( &tok ), UNEXPECTED_TOKEN_ERR_MSG, tok_get_desc( tok_last_type(&tok)) ); - do_loop=0; - break; - } - } - } + do_loop=0; + break; + } + } + } if (show_errors) this->print_errors_stderr(); - - tok_destroy( &tok ); - - current_tokenizer=previous_tokenizer; - current_tokenizer_pos = previous_pos; - + + tok_destroy( &tok ); + + current_tokenizer=previous_tokenizer; + current_tokenizer_pos = previous_pos; + if (this->parser_type == PARSER_TYPE_GENERAL) proc_pop_interactive(); - return 1; + return 1; } void parser_t::stack_trace( block_t *b, wcstring &buff) -{ - /* - Check if we should end the recursion - */ - if( !b ) - return; +{ + /* + Check if we should end the recursion + */ + if( !b ) + return; - if( b->type()==EVENT ) - { - /* - This is an event handler - */ + if( b->type()==EVENT ) + { + /* + This is an event handler + */ const event_block_t *eb = static_cast(b); wcstring description = event_get_desc( eb->event ); - append_format( buff, _(L"in event handler: %ls\n"), description.c_str()); - buff.append( L"\n" ); + append_format( buff, _(L"in event handler: %ls\n"), description.c_str()); + buff.append( L"\n" ); - /* - Stop recursing at event handler. No reason to belive that - any other code is relevant. + /* + Stop recursing at event handler. No reason to belive that + any other code is relevant. - It might make sense in the future to continue printing the - stack trace of the code that invoked the event, if this is a - programmatic event, but we can't currently detect that. - */ - return; - } - - if( b->type() == FUNCTION_CALL || b->type()==SOURCE || b->type()==SUBST) - { - /* - These types of blocks should be printed - */ + It might make sense in the future to continue printing the + stack trace of the code that invoked the event, if this is a + programmatic event, but we can't currently detect that. + */ + return; + } - int i; + if( b->type() == FUNCTION_CALL || b->type()==SOURCE || b->type()==SUBST) + { + /* + These types of blocks should be printed + */ - switch( b->type()) - { - case SOURCE: - { + int i; + + switch( b->type()) + { + case SOURCE: + { const source_block_t *sb = static_cast(b); const wchar_t *source_dest = sb->source_file; - append_format( buff, _(L"in . (source) call of file '%ls',\n"), source_dest ); - break; - } - case FUNCTION_CALL: - { + append_format( buff, _(L"in . (source) call of file '%ls',\n"), source_dest ); + break; + } + case FUNCTION_CALL: + { const function_block_t *fb = static_cast(b); - append_format( buff, _(L"in function '%ls',\n"), fb->name.c_str() ); - break; - } - case SUBST: - { - append_format( buff, _(L"in command substitution\n") ); - break; - } - + append_format( buff, _(L"in function '%ls',\n"), fb->name.c_str() ); + break; + } + case SUBST: + { + append_format( buff, _(L"in command substitution\n") ); + break; + } + default: /* Can't get here */ break; - } + } - const wchar_t *file = b->src_filename; + const wchar_t *file = b->src_filename; - if( file ) - { - append_format( buff, - _(L"\tcalled on line %d of file '%ls',\n"), - b->src_lineno, - file ); - } - else - { - append_format( buff, - _(L"\tcalled on standard input,\n") ); - } - - if( b->type() == FUNCTION_CALL ) + if( file ) + { + append_format( buff, + _(L"\tcalled on line %d of file '%ls',\n"), + b->src_lineno, + file ); + } + else + { + append_format( buff, + _(L"\tcalled on standard input,\n") ); + } + + if( b->type() == FUNCTION_CALL ) { const function_block_t *fb = static_cast(b); const process_t * const process = fb->process; - if( process->argv(1) ) - { - wcstring tmp; - - for( i=1; process->argv(i); i++ ) - { + if( process->argv(1) ) + { + wcstring tmp; + + for( i=1; process->argv(i); i++ ) + { if (i > 1) tmp.push_back(L' '); tmp.append(process->argv(i)); - } - append_format( buff, _(L"\twith parameter list '%ls'\n"), tmp.c_str() ); - } - } - - append_format( buff, L"\n" ); - } + } + append_format( buff, _(L"\twith parameter list '%ls'\n"), tmp.c_str() ); + } + } - /* - Recursively print the next block - */ - parser_t::stack_trace( b->outer, buff ); + append_format( buff, L"\n" ); + } + + /* + Recursively print the next block + */ + parser_t::stack_trace( b->outer, buff ); } /** @@ -1000,47 +1000,47 @@ const wchar_t *parser_t::is_function() const // PCA: Have to make this a string somehow ASSERT_IS_MAIN_THREAD(); wcstring result; - - block_t *b = current_block; - while( 1 ) - { - if( !b ) - { - return NULL; - } - if( b->type() == FUNCTION_CALL ) - { + + block_t *b = current_block; + while( 1 ) + { + if( !b ) + { + return NULL; + } + if( b->type() == FUNCTION_CALL ) + { const function_block_t *fb = static_cast(b); - return fb->name.c_str(); - } - b=b->outer; - } + return fb->name.c_str(); + } + b=b->outer; + } } int parser_t::get_lineno() const { - int lineno; - - if( ! current_tokenizer || ! tok_string( current_tokenizer )) - return -1; - - lineno = current_tokenizer->line_number_of_character_at_offset(current_tokenizer_pos); - - const wchar_t *function_name; - if( (function_name = is_function()) ) - { - lineno += function_get_definition_offset( function_name ); - } + int lineno; - return lineno; + if( ! current_tokenizer || ! tok_string( current_tokenizer )) + return -1; + + lineno = current_tokenizer->line_number_of_character_at_offset(current_tokenizer_pos); + + const wchar_t *function_name; + if( (function_name = is_function()) ) + { + lineno += function_get_definition_offset( function_name ); + } + + return lineno; } int parser_t::line_number_of_character_at_offset(size_t idx) const { if( ! current_tokenizer) return -1; - + int result = current_tokenizer->line_number_of_character_at_offset(idx); //assert(result == parse_util_lineno(tok_string( current_tokenizer ), idx)); return result; @@ -1051,22 +1051,22 @@ const wchar_t *parser_t::current_filename() const /* We query a global array for the current file name, so it only makes sense to ask this on the principal parser. */ ASSERT_IS_MAIN_THREAD(); assert(this == &principal_parser()); - - block_t *b = current_block; - while( 1 ) - { - if( !b ) - { - return reader_current_filename(); - } - if( b->type() == FUNCTION_CALL ) - { + block_t *b = current_block; + + while( 1 ) + { + if( !b ) + { + return reader_current_filename(); + } + if( b->type() == FUNCTION_CALL ) + { const function_block_t *fb = static_cast(b); - return function_get_definition_file(fb->name); - } - b=b->outer; - } + return function_get_definition_file(fb->name); + } + b=b->outer; + } } /** @@ -1077,184 +1077,184 @@ const wchar_t *parser_t::current_filename() const */ static int printed_width( const wchar_t *str, int len ) { - int res=0; - int i; + int res=0; + int i; - CHECK( str, 0 ); + CHECK( str, 0 ); - for( i=0; str[i] && i= min_match && (wcsncmp( L"--help", s, len ) == 0) ); + min_match = maxi( min_match, 3 ); + + return ( wcscmp( L"-h", s ) == 0 ) || + ( len >= min_match && (wcsncmp( L"--help", s, len ) == 0) ); } job_t *parser_t::job_create(void) { job_t *res = new job_t(acquire_job_id()); this->my_job_list.push_front(res); - - job_set_flag( res, - JOB_CONTROL, - (job_control_mode==JOB_CONTROL_ALL) || - ((job_control_mode == JOB_CONTROL_INTERACTIVE) && (get_is_interactive())) ); + + job_set_flag( res, + JOB_CONTROL, + (job_control_mode==JOB_CONTROL_ALL) || + ((job_control_mode == JOB_CONTROL_INTERACTIVE) && (get_is_interactive())) ); return res; } @@ -1265,8 +1265,8 @@ bool parser_t::job_remove( job_t *j ) my_job_list.erase(iter); return true; } else { - debug( 1, _( L"Job inconsistency" ) ); - sanity_lose(); + debug( 1, _( L"Job inconsistency" ) ); + sanity_lose(); return false; } } @@ -1275,7 +1275,7 @@ void parser_t::job_promote(job_t *job) { job_list_t::iterator loc = std::find(my_job_list.begin(), my_job_list.end(), job); assert(loc != my_job_list.end()); - + /* Move the job to the beginning */ my_job_list.splice(my_job_list.begin(), my_job_list, loc); } @@ -1287,8 +1287,8 @@ job_t *parser_t::job_get(job_id_t id) while ((job = jobs.next())) { if( id <= 0 || job->job_id == id) return job; - } - return NULL; + } + return NULL; } job_t *parser_t::job_get_from_pid( int pid ) @@ -1296,10 +1296,10 @@ job_t *parser_t::job_get_from_pid( int pid ) job_iterator_t jobs; job_t *job; while ((job = jobs.next())) { - if( job->pgid == pid ) - return job; - } - return 0; + if( job->pgid == pid ) + return job; + } + return 0; } /** @@ -1312,115 +1312,115 @@ job_t *parser_t::job_get_from_pid( int pid ) \param args unskip whether we should ignore current_block->skip. Big hack because of our dumb handling of if statements. */ void parser_t::parse_job_argument_list( process_t *p, - job_t *j, - tokenizer *tok, - std::vector &args, + job_t *j, + tokenizer *tok, + std::vector &args, bool unskip ) { - int is_finished=0; + int is_finished=0; - int proc_is_count=0; + int proc_is_count=0; - int matched_wildcard = 0, unmatched_wildcard = 0; + int matched_wildcard = 0, unmatched_wildcard = 0; - wcstring unmatched; - int unmatched_pos=0; + wcstring unmatched; + int unmatched_pos=0; - /* - Test if this is the 'count' command. We need to special case - count in the shell, since it should display a help message on - 'count -h', but not on 'set foo -h; count $foo'. This is an ugly - workaround and a huge hack, but as near as I can tell, the - alternatives are worse. - */ - proc_is_count = ( args.at(0).completion == L"count" ); - - while( 1 ) - { + /* + Test if this is the 'count' command. We need to special case + count in the shell, since it should display a help message on + 'count -h', but not on 'set foo -h; count $foo'. This is an ugly + workaround and a huge hack, but as near as I can tell, the + alternatives are worse. + */ + proc_is_count = ( args.at(0).completion == L"count" ); - switch( tok_last_type( tok ) ) - { - case TOK_PIPE: - { - wchar_t *end; - - if (p->type == INTERNAL_EXEC) - { - error( SYNTAX_ERROR, - tok_get_pos( tok ), - EXEC_ERR_MSG ); - return; - } + while( 1 ) + { + + switch( tok_last_type( tok ) ) + { + case TOK_PIPE: + { + wchar_t *end; + + if (p->type == INTERNAL_EXEC) + { + error( SYNTAX_ERROR, + tok_get_pos( tok ), + EXEC_ERR_MSG ); + return; + } + + errno = 0; + p->pipe_write_fd = fish_wcstoi( tok_last( tok ), &end, 10 ); + if( p->pipe_write_fd < 0 || errno || *end ) + { + error( SYNTAX_ERROR, + tok_get_pos( tok ), + ILLEGAL_FD_ERR_MSG, + tok_last( tok ) ); + return; + } - errno = 0; - p->pipe_write_fd = fish_wcstoi( tok_last( tok ), &end, 10 ); - if( p->pipe_write_fd < 0 || errno || *end ) - { - error( SYNTAX_ERROR, - tok_get_pos( tok ), - ILLEGAL_FD_ERR_MSG, - tok_last( tok ) ); - return; - } - p->set_argv(completions_to_wcstring_list(args)); - p->next = new process_t(); + p->next = new process_t(); - tok_next( tok ); - - /* - Don't do anything on failiure. parse_job will notice - the error flag and report any errors for us - */ - parse_job( p->next, j, tok ); + tok_next( tok ); - is_finished = 1; - break; - } - - case TOK_BACKGROUND: - { - job_set_flag( j, JOB_FOREGROUND, 0 ); - } - - case TOK_END: - { - if( !p->get_argv() ) - p->set_argv(completions_to_wcstring_list(args)); - if( tok_has_next(tok)) - tok_next(tok); + /* + Don't do anything on failiure. parse_job will notice + the error flag and report any errors for us + */ + parse_job( p->next, j, tok ); - is_finished = 1; + is_finished = 1; + break; + } - break; - } + case TOK_BACKGROUND: + { + job_set_flag( j, JOB_FOREGROUND, 0 ); + } - case TOK_STRING: - { - int skip=0; + case TOK_END: + { + if( !p->get_argv() ) + p->set_argv(completions_to_wcstring_list(args)); + if( tok_has_next(tok)) + tok_next(tok); - if( job_get_flag( j, JOB_SKIP ) ) - { - skip = 1; - } - else if( current_block->skip && ! unskip ) - { - /* - If this command should be skipped, we do not expand the arguments - */ - skip=1; + is_finished = 1; - /* But if this is in fact a case statement or an elseif statement, then it should be evaluated */ + break; + } + + case TOK_STRING: + { + int skip=0; + + if( job_get_flag( j, JOB_SKIP ) ) + { + skip = 1; + } + else if( current_block->skip && ! unskip ) + { + /* + If this command should be skipped, we do not expand the arguments + */ + skip=1; + + /* But if this is in fact a case statement or an elseif statement, then it should be evaluated */ block_type_t type = current_block->type(); - if( type == SWITCH && args.at(0).completion == L"case" && p->type == INTERNAL_BUILTIN ) - { - skip=0; - } + if( type == SWITCH && args.at(0).completion == L"case" && p->type == INTERNAL_BUILTIN ) + { + skip=0; + } else if (job_get_flag(j, JOB_ELSEIF) && ! job_should_skip_elseif(j, current_block)) { skip=0; } - } + } else { /* If this is an else if, and we should skip it, then don't expand any arguments */ @@ -1430,264 +1430,264 @@ void parser_t::parse_job_argument_list( process_t *p, } } - if( !skip ) - { - if( ( proc_is_count ) && - ( args.size() == 1) && - ( parser_t::is_help( tok_last(tok), 0) ) && - ( p->type == INTERNAL_BUILTIN ) ) - { - /* - Display help for count - */ - p->count_help_magic = 1; - } + if( !skip ) + { + if( ( proc_is_count ) && + ( args.size() == 1) && + ( parser_t::is_help( tok_last(tok), 0) ) && + ( p->type == INTERNAL_BUILTIN ) ) + { + /* + Display help for count + */ + p->count_help_magic = 1; + } - switch( expand_string( tok_last( tok ), args, 0 ) ) - { - case EXPAND_ERROR: - { - err_pos=tok_get_pos( tok ); - if( error_code == 0 ) - { - error( SYNTAX_ERROR, - tok_get_pos( tok ), - _(L"Could not expand string '%ls'"), - tok_last(tok) ); + switch( expand_string( tok_last( tok ), args, 0 ) ) + { + case EXPAND_ERROR: + { + err_pos=tok_get_pos( tok ); + if( error_code == 0 ) + { + error( SYNTAX_ERROR, + tok_get_pos( tok ), + _(L"Could not expand string '%ls'"), + tok_last(tok) ); - } - break; - } + } + break; + } - case EXPAND_WILDCARD_NO_MATCH: - { - unmatched_wildcard = 1; - if( unmatched.empty() ) - { + case EXPAND_WILDCARD_NO_MATCH: + { + unmatched_wildcard = 1; + if( unmatched.empty() ) + { unmatched = tok_last(tok); - unmatched_pos = tok_get_pos( tok ); - } + unmatched_pos = tok_get_pos( tok ); + } - break; - } + break; + } - case EXPAND_WILDCARD_MATCH: - { - matched_wildcard = 1; - break; - } + case EXPAND_WILDCARD_MATCH: + { + matched_wildcard = 1; + break; + } - case EXPAND_OK: - { - break; - } + case EXPAND_OK: + { + break; + } - } + } - } + } - break; - } + break; + } - case TOK_REDIRECT_OUT: - case TOK_REDIRECT_IN: - case TOK_REDIRECT_APPEND: - case TOK_REDIRECT_FD: - case TOK_REDIRECT_NOCLOB: - { - int type = tok_last_type( tok ); + case TOK_REDIRECT_OUT: + case TOK_REDIRECT_IN: + case TOK_REDIRECT_APPEND: + case TOK_REDIRECT_FD: + case TOK_REDIRECT_NOCLOB: + { + int type = tok_last_type( tok ); std::auto_ptr new_io; wcstring target; - bool has_target = false; - wchar_t *end; - - /* - Don't check redirections in skipped part + bool has_target = false; + wchar_t *end; - Otherwise, bogus errors may be the result. (Do check - that token is string, though) - */ - if( current_block->skip && ! unskip ) - { - tok_next( tok ); - if( tok_last_type( tok ) != TOK_STRING ) - { - error( SYNTAX_ERROR, - tok_get_pos( tok ), - REDIRECT_TOKEN_ERR_MSG, - tok_get_desc( tok_last_type(tok)) ); - } + /* + Don't check redirections in skipped part - break; - } + Otherwise, bogus errors may be the result. (Do check + that token is string, though) + */ + if( current_block->skip && ! unskip ) + { + tok_next( tok ); + if( tok_last_type( tok ) != TOK_STRING ) + { + error( SYNTAX_ERROR, + tok_get_pos( tok ), + REDIRECT_TOKEN_ERR_MSG, + tok_get_desc( tok_last_type(tok)) ); + } - new_io.reset(new io_data_t); + break; + } - errno = 0; - new_io->fd = fish_wcstoi( tok_last( tok ), - &end, - 10 ); - if( new_io->fd < 0 || errno || *end ) - { - error( SYNTAX_ERROR, - tok_get_pos( tok ), - ILLEGAL_FD_ERR_MSG, - tok_last( tok ) ); - } - else - { - - tok_next( tok ); + new_io.reset(new io_data_t); - switch( tok_last_type( tok ) ) - { - case TOK_STRING: - { + errno = 0; + new_io->fd = fish_wcstoi( tok_last( tok ), + &end, + 10 ); + if( new_io->fd < 0 || errno || *end ) + { + error( SYNTAX_ERROR, + tok_get_pos( tok ), + ILLEGAL_FD_ERR_MSG, + tok_last( tok ) ); + } + else + { + + tok_next( tok ); + + switch( tok_last_type( tok ) ) + { + case TOK_STRING: + { target = tok_last( tok ); has_target = expand_one(target, no_exec ? EXPAND_SKIP_VARIABLES : 0); - if( ! has_target && error_code == 0 ) - { - error( SYNTAX_ERROR, - tok_get_pos( tok ), - REDIRECT_TOKEN_ERR_MSG, - tok_last( tok ) ); + if( ! has_target && error_code == 0 ) + { + error( SYNTAX_ERROR, + tok_get_pos( tok ), + REDIRECT_TOKEN_ERR_MSG, + tok_last( tok ) ); - } - break; - } + } + break; + } - default: - error( SYNTAX_ERROR, - tok_get_pos( tok ), - REDIRECT_TOKEN_ERR_MSG, - tok_get_desc( tok_last_type(tok)) ); - } + default: + error( SYNTAX_ERROR, + tok_get_pos( tok ), + REDIRECT_TOKEN_ERR_MSG, + tok_get_desc( tok_last_type(tok)) ); + } - if( ! has_target || target.empty() ) - { - if( error_code == 0 ) - error( SYNTAX_ERROR, - tok_get_pos( tok ), - _(L"Invalid IO redirection") ); - tok_next(tok); - } - else - { + if( ! has_target || target.empty() ) + { + if( error_code == 0 ) + error( SYNTAX_ERROR, + tok_get_pos( tok ), + _(L"Invalid IO redirection") ); + tok_next(tok); + } + else + { - switch( type ) - { - case TOK_REDIRECT_APPEND: - new_io->io_mode = IO_FILE; - new_io->param2.flags = O_CREAT | O_APPEND | O_WRONLY; - new_io->set_filename(target); - break; + switch( type ) + { + case TOK_REDIRECT_APPEND: + new_io->io_mode = IO_FILE; + new_io->param2.flags = O_CREAT | O_APPEND | O_WRONLY; + new_io->set_filename(target); + break; - case TOK_REDIRECT_OUT: - new_io->io_mode = IO_FILE; - new_io->param2.flags = O_CREAT | O_WRONLY | O_TRUNC; - new_io->set_filename(target); - break; + case TOK_REDIRECT_OUT: + new_io->io_mode = IO_FILE; + new_io->param2.flags = O_CREAT | O_WRONLY | O_TRUNC; + new_io->set_filename(target); + break; - case TOK_REDIRECT_NOCLOB: - new_io->io_mode = IO_FILE; - new_io->param2.flags = O_CREAT | O_EXCL | O_WRONLY; - new_io->set_filename(target); - break; + case TOK_REDIRECT_NOCLOB: + new_io->io_mode = IO_FILE; + new_io->param2.flags = O_CREAT | O_EXCL | O_WRONLY; + new_io->set_filename(target); + break; - case TOK_REDIRECT_IN: - new_io->io_mode = IO_FILE; - new_io->param2.flags = O_RDONLY; - new_io->set_filename(target); - break; + case TOK_REDIRECT_IN: + new_io->io_mode = IO_FILE; + new_io->param2.flags = O_RDONLY; + new_io->set_filename(target); + break; - case TOK_REDIRECT_FD: - { - if( target == L"-" ) - { - new_io->io_mode = IO_CLOSE; - } - else - { - wchar_t *end; - - new_io->io_mode = IO_FD; - errno = 0; - - new_io->param1.old_fd = fish_wcstoi( target.c_str(), &end, 10 ); - - if( ( new_io->param1.old_fd < 0 ) || - errno || *end ) - { - error( SYNTAX_ERROR, - tok_get_pos( tok ), - _(L"Requested redirection to something that is not a file descriptor %ls"), - target.c_str() ); + case TOK_REDIRECT_FD: + { + if( target == L"-" ) + { + new_io->io_mode = IO_CLOSE; + } + else + { + wchar_t *end; - tok_next(tok); - } - } - break; - } - } - - } - } + new_io->io_mode = IO_FD; + errno = 0; - j->io.push_back(new_io.release()); - - } - break; + new_io->param1.old_fd = fish_wcstoi( target.c_str(), &end, 10 ); - case TOK_ERROR: - { - error( SYNTAX_ERROR, - tok_get_pos( tok ), - TOK_ERR_MSG, - tok_last(tok) ); + if( ( new_io->param1.old_fd < 0 ) || + errno || *end ) + { + error( SYNTAX_ERROR, + tok_get_pos( tok ), + _(L"Requested redirection to something that is not a file descriptor %ls"), + target.c_str() ); - return; - } + tok_next(tok); + } + } + break; + } + } - default: - error( SYNTAX_ERROR, - tok_get_pos( tok ), - UNEXPECTED_TOKEN_ERR_MSG, - tok_get_desc( tok_last_type(tok)) ); + } + } - tok_next(tok); - break; - } + j->io.push_back(new_io.release()); - if( (is_finished) || (error_code != 0) ) - break; + } + break; - tok_next( tok ); - } + case TOK_ERROR: + { + error( SYNTAX_ERROR, + tok_get_pos( tok ), + TOK_ERR_MSG, + tok_last(tok) ); - if( !error_code ) - { - if( unmatched_wildcard && !matched_wildcard ) - { - job_set_flag( j, JOB_WILDCARD_ERROR, 1 ); - proc_set_last_status( STATUS_UNMATCHED_WILDCARD ); - if( get_is_interactive() && !is_block ) - { - int tmp; + return; + } - debug( 1, WILDCARD_ERR_MSG, unmatched.c_str() ); - tmp = current_tokenizer_pos; - current_tokenizer_pos = unmatched_pos; + default: + error( SYNTAX_ERROR, + tok_get_pos( tok ), + UNEXPECTED_TOKEN_ERR_MSG, + tok_get_desc( tok_last_type(tok)) ); - fwprintf( stderr, L"%ls", parser_t::current_line() ); + tok_next(tok); + break; + } - current_tokenizer_pos=tmp; - } + if( (is_finished) || (error_code != 0) ) + break; - } - } + tok_next( tok ); + } - return; + if( !error_code ) + { + if( unmatched_wildcard && !matched_wildcard ) + { + job_set_flag( j, JOB_WILDCARD_ERROR, 1 ); + proc_set_last_status( STATUS_UNMATCHED_WILDCARD ); + if( get_is_interactive() && !is_block ) + { + int tmp; + + debug( 1, WILDCARD_ERR_MSG, unmatched.c_str() ); + tmp = current_tokenizer_pos; + current_tokenizer_pos = unmatched_pos; + + fwprintf( stderr, L"%ls", parser_t::current_line() ); + + current_tokenizer_pos=tmp; + } + + } + } + + return; } /* @@ -1696,11 +1696,11 @@ void parser_t::parse_job_argument_list( process_t *p, if( !b ) return; print_block_stack( b->outer ); - - debug( 0, L"Block type %ls, skip: %d", parser_get_block_desc( b->type ), b->skip ); + + debug( 0, L"Block type %ls, skip: %d", parser_get_block_desc( b->type ), b->skip ); } */ - + /** Fully parse a single job. Does not call exec on it, but any command substitutions in the job will be executed. @@ -1711,213 +1711,213 @@ f \return 1 on success, 0 on error */ int parser_t::parse_job( process_t *p, - job_t *j, - tokenizer *tok ) + job_t *j, + tokenizer *tok ) { std::vector args; // The list that will become the argc array for the program - int use_function = 1; // May functions be considered when checking what action this command represents - int use_builtin = 1; // May builtins be considered when checking what action this command represents - int use_command = 1; // May commands be considered when checking what action this command represents - int is_new_block=0; // Does this command create a new block? - bool unskip = false; // Maybe we are an elseif inside an if block; if so we may want to evaluate this even if the if block is currently set to skip - bool allow_bogus_command = false; // If we are an elseif that will not be executed, or an AND or OR that will have been short circuited, don't complain about non-existent commands + int use_function = 1; // May functions be considered when checking what action this command represents + int use_builtin = 1; // May builtins be considered when checking what action this command represents + int use_command = 1; // May commands be considered when checking what action this command represents + int is_new_block=0; // Does this command create a new block? + bool unskip = false; // Maybe we are an elseif inside an if block; if so we may want to evaluate this even if the if block is currently set to skip + bool allow_bogus_command = false; // If we are an elseif that will not be executed, or an AND or OR that will have been short circuited, don't complain about non-existent commands - block_t *prev_block = current_block; - int prev_tokenizer_pos = current_tokenizer_pos; + block_t *prev_block = current_block; + int prev_tokenizer_pos = current_tokenizer_pos; - current_tokenizer_pos = tok_get_pos( tok ); + current_tokenizer_pos = tok_get_pos( tok ); - while( args.empty() ) - { - wcstring nxt; + while( args.empty() ) + { + wcstring nxt; bool has_nxt = false; - bool consumed = false; // Set to one if the command requires a second command, like e.g. while does - int mark; // Use to save the position of the beginning of the token - - switch( tok_last_type( tok )) - { - case TOK_STRING: - { + bool consumed = false; // Set to one if the command requires a second command, like e.g. while does + int mark; // Use to save the position of the beginning of the token + + switch( tok_last_type( tok )) + { + case TOK_STRING: + { nxt = tok_last( tok ); has_nxt = expand_one(nxt, EXPAND_SKIP_CMDSUBST | EXPAND_SKIP_VARIABLES); - - if( ! has_nxt) - { - error( SYNTAX_ERROR, - tok_get_pos( tok ), - ILLEGAL_CMD_ERR_MSG, - tok_last( tok ) ); - - current_tokenizer_pos = prev_tokenizer_pos; - return 0; - } - break; - } - case TOK_ERROR: - { - error( SYNTAX_ERROR, - tok_get_pos( tok ), - TOK_ERR_MSG, - tok_last(tok) ); + if( ! has_nxt) + { + error( SYNTAX_ERROR, + tok_get_pos( tok ), + ILLEGAL_CMD_ERR_MSG, + tok_last( tok ) ); - current_tokenizer_pos = prev_tokenizer_pos; - return 0; - } + current_tokenizer_pos = prev_tokenizer_pos; + return 0; + } + break; + } - case TOK_PIPE: - { - const wchar_t *str = tok_string( tok ); - if( tok_get_pos(tok)>0 && str[tok_get_pos(tok)-1] == L'|' ) - { - error( SYNTAX_ERROR, - tok_get_pos( tok ), - CMD_OR_ERR_MSG, - tok_get_desc( tok_last_type(tok) ) ); - } - else - { - error( SYNTAX_ERROR, - tok_get_pos( tok ), - CMD_ERR_MSG, - tok_get_desc( tok_last_type(tok) ) ); - } + case TOK_ERROR: + { + error( SYNTAX_ERROR, + tok_get_pos( tok ), + TOK_ERR_MSG, + tok_last(tok) ); - current_tokenizer_pos = prev_tokenizer_pos; - return 0; - } + current_tokenizer_pos = prev_tokenizer_pos; + return 0; + } - default: - { - error( SYNTAX_ERROR, - tok_get_pos( tok ), - CMD_ERR_MSG, - tok_get_desc( tok_last_type(tok) ) ); + case TOK_PIPE: + { + const wchar_t *str = tok_string( tok ); + if( tok_get_pos(tok)>0 && str[tok_get_pos(tok)-1] == L'|' ) + { + error( SYNTAX_ERROR, + tok_get_pos( tok ), + CMD_OR_ERR_MSG, + tok_get_desc( tok_last_type(tok) ) ); + } + else + { + error( SYNTAX_ERROR, + tok_get_pos( tok ), + CMD_ERR_MSG, + tok_get_desc( tok_last_type(tok) ) ); + } - current_tokenizer_pos = prev_tokenizer_pos; - return 0; - } - } - - mark = tok_get_pos( tok ); - - if( contains( nxt, - L"command", - L"builtin", - L"not", - L"and", - L"or", - L"exec" ) ) - { - int sw; - int is_exec = nxt == L"exec"; - - if( is_exec && (p != j->first_process) ) - { - error( SYNTAX_ERROR, - tok_get_pos( tok ), - EXEC_ERR_MSG ); - current_tokenizer_pos = prev_tokenizer_pos; - return 0; - } + current_tokenizer_pos = prev_tokenizer_pos; + return 0; + } - tok_next( tok ); - sw = parser_keywords_is_switch( tok_last( tok ) ); - - if( sw == ARG_SWITCH ) - { - tok_set_pos( tok, mark); - } - else - { - if( sw == ARG_SKIP ) - { - tok_next( tok ); - } - - consumed = true; + default: + { + error( SYNTAX_ERROR, + tok_get_pos( tok ), + CMD_ERR_MSG, + tok_get_desc( tok_last_type(tok) ) ); - if( nxt == L"command" || nxt == L"builtin" ) - { - use_function = 0; - if( nxt == L"command" ) - { - use_builtin = 0; - use_command = 1; - } - else - { - use_builtin = 1; - use_command = 0; - } - } - else if( nxt == L"not" ) - { - job_set_flag( j, JOB_NEGATE, !job_get_flag( j, JOB_NEGATE ) ); - } - else if( nxt == L"and" ) - { + current_tokenizer_pos = prev_tokenizer_pos; + return 0; + } + } + + mark = tok_get_pos( tok ); + + if( contains( nxt, + L"command", + L"builtin", + L"not", + L"and", + L"or", + L"exec" ) ) + { + int sw; + int is_exec = nxt == L"exec"; + + if( is_exec && (p != j->first_process) ) + { + error( SYNTAX_ERROR, + tok_get_pos( tok ), + EXEC_ERR_MSG ); + current_tokenizer_pos = prev_tokenizer_pos; + return 0; + } + + tok_next( tok ); + sw = parser_keywords_is_switch( tok_last( tok ) ); + + if( sw == ARG_SWITCH ) + { + tok_set_pos( tok, mark); + } + else + { + if( sw == ARG_SKIP ) + { + tok_next( tok ); + } + + consumed = true; + + if( nxt == L"command" || nxt == L"builtin" ) + { + use_function = 0; + if( nxt == L"command" ) + { + use_builtin = 0; + use_command = 1; + } + else + { + use_builtin = 1; + use_command = 0; + } + } + else if( nxt == L"not" ) + { + job_set_flag( j, JOB_NEGATE, !job_get_flag( j, JOB_NEGATE ) ); + } + else if( nxt == L"and" ) + { bool skip = (proc_get_last_status() != 0); - job_set_flag( j, JOB_SKIP, skip); + job_set_flag( j, JOB_SKIP, skip); allow_bogus_command = skip; - } - else if( nxt == L"or" ) - { + } + else if( nxt == L"or" ) + { bool skip = (proc_get_last_status() == 0); - job_set_flag( j, JOB_SKIP, skip); + job_set_flag( j, JOB_SKIP, skip); allow_bogus_command = skip; - } - else if( is_exec ) - { - use_function = 0; - use_builtin=0; - p->type=INTERNAL_EXEC; - current_tokenizer_pos = prev_tokenizer_pos; - } - } - } - else if( nxt == L"while" ) - { - bool new_block = false; - tok_next( tok ); + } + else if( is_exec ) + { + use_function = 0; + use_builtin=0; + p->type=INTERNAL_EXEC; + current_tokenizer_pos = prev_tokenizer_pos; + } + } + } + else if( nxt == L"while" ) + { + bool new_block = false; + tok_next( tok ); while_block_t *wb = NULL; - if( ( current_block->type() != WHILE ) ) - { - new_block = true; - } - else if( (wb = static_cast(current_block))->status == WHILE_TEST_AGAIN ) - { - wb->status = WHILE_TEST_FIRST; - } - else - { - new_block = true; - } + if( ( current_block->type() != WHILE ) ) + { + new_block = true; + } + else if( (wb = static_cast(current_block))->status == WHILE_TEST_AGAIN ) + { + wb->status = WHILE_TEST_FIRST; + } + else + { + new_block = true; + } - if( new_block ) - { + if( new_block ) + { while_block_t *wb = new while_block_t(); - wb->status = WHILE_TEST_FIRST; - wb->tok_pos = mark; - this->push_block( wb ); - } + wb->status = WHILE_TEST_FIRST; + wb->tok_pos = mark; + this->push_block( wb ); + } - consumed = true; - is_new_block=1; + consumed = true; + is_new_block=1; - } - else if( nxt == L"if" ) - { - tok_next( tok ); + } + else if( nxt == L"if" ) + { + tok_next( tok ); if_block_t *ib = new if_block_t(); - this->push_block( ib ); - ib->tok_pos = mark; + this->push_block( ib ); + ib->tok_pos = mark; - is_new_block=1; - consumed = true; - } + is_new_block=1; + consumed = true; + } else if (nxt == L"else") { /* Record where the else is for error reporting */ @@ -1927,7 +1927,7 @@ int parser_t::parse_job( process_t *p, if (tok_last_type(tok) == TOK_STRING && current_block->type() == IF) { const if_block_t *ib = static_cast(current_block); - + /* If we've already encountered an else, complain */ if (ib->else_evaluated) { @@ -1939,16 +1939,16 @@ int parser_t::parse_job( process_t *p, } else { - + job_set_flag( j, JOB_ELSEIF, 1 ); consumed = true; - + /* We're at the IF. Go past it. */ tok_next(tok); - + /* We want to execute this ELSEIF if the IF expression was evaluated, it failed, and so has every other ELSEIF (if any) */ unskip = (ib->if_expr_evaluated && ! ib->any_branch_taken); - + /* But if we're not executing it, don't complain about its command if it doesn't exist */ if (! unskip) allow_bogus_command = true; @@ -1956,44 +1956,44 @@ int parser_t::parse_job( process_t *p, } } - /* - Test if we need another command - */ - if( consumed ) - { - /* - Yes we do, around in the loop for another lap, then! - */ - continue; - } - - if( use_function && ( unskip || ! current_block->skip )) - { - bool nxt_forbidden=false; - wcstring forbid; + /* + Test if we need another command + */ + if( consumed ) + { + /* + Yes we do, around in the loop for another lap, then! + */ + continue; + } - int is_function_call=0; + if( use_function && ( unskip || ! current_block->skip )) + { + bool nxt_forbidden=false; + wcstring forbid; - /* - This is a bit fragile. It is a test to see if we are - inside of function call, but not inside a block in that - function call. If, in the future, the rules for what - block scopes are pushed on function invocation changes, - then this check will break. - */ - if( ( current_block->type() == TOP ) && - ( current_block->outer ) && - ( current_block->outer->type() == FUNCTION_CALL ) ) - is_function_call = 1; - - /* - If we are directly in a function, and this is the first - command of the block, then the function we are executing - may not be called, since that would mean an infinite - recursion. - */ - if( is_function_call && !current_block->had_command ) - { + int is_function_call=0; + + /* + This is a bit fragile. It is a test to see if we are + inside of function call, but not inside a block in that + function call. If, in the future, the rules for what + block scopes are pushed on function invocation changes, + then this check will break. + */ + if( ( current_block->type() == TOP ) && + ( current_block->outer ) && + ( current_block->outer->type() == FUNCTION_CALL ) ) + is_function_call = 1; + + /* + If we are directly in a function, and this is the first + command of the block, then the function we are executing + may not be called, since that would mean an infinite + recursion. + */ + if( is_function_call && !current_block->had_command ) + { forbid = forbidden_function.empty() ? wcstring(L"") : forbidden_function.back(); if (forbid == nxt) { @@ -2001,57 +2001,57 @@ int parser_t::parse_job( process_t *p, nxt_forbidden = true; error( SYNTAX_ERROR, tok_get_pos( tok ), INFINITE_RECURSION_ERR_MSG ); } - } - - if( !nxt_forbidden && has_nxt && function_exists( nxt ) ) - { - /* - Check if we have reached the maximum recursion depth - */ - if( forbidden_function.size() > MAX_RECURSION_DEPTH ) - { - error( SYNTAX_ERROR, tok_get_pos( tok ), OVERFLOW_RECURSION_ERR_MSG ); - } - else - { - p->type = INTERNAL_FUNCTION; - } - } - } - args.push_back(completion_t(nxt)); - } + } - if( error_code == 0 ) - { - if( !p->type ) - { - if( use_builtin && - builtin_exists(args.at(0).completion)) - { - p->type = INTERNAL_BUILTIN; - is_new_block |= parser_keywords_is_block( args.at( 0 ).completion ); - } - } - - if( (!p->type || (p->type == INTERNAL_EXEC) ) ) - { - /* - If we are not executing the current block, allow - non-existent commands. - */ + if( !nxt_forbidden && has_nxt && function_exists( nxt ) ) + { + /* + Check if we have reached the maximum recursion depth + */ + if( forbidden_function.size() > MAX_RECURSION_DEPTH ) + { + error( SYNTAX_ERROR, tok_get_pos( tok ), OVERFLOW_RECURSION_ERR_MSG ); + } + else + { + p->type = INTERNAL_FUNCTION; + } + } + } + args.push_back(completion_t(nxt)); + } + + if( error_code == 0 ) + { + if( !p->type ) + { + if( use_builtin && + builtin_exists(args.at(0).completion)) + { + p->type = INTERNAL_BUILTIN; + is_new_block |= parser_keywords_is_block( args.at( 0 ).completion ); + } + } + + if( (!p->type || (p->type == INTERNAL_EXEC) ) ) + { + /* + If we are not executing the current block, allow + non-existent commands. + */ if (current_block->skip && ! unskip) allow_bogus_command = true; //note this may already be true for other reasons - - if (allow_bogus_command) - { - p->actual_cmd.clear(); - } - else - { - int err; + + if (allow_bogus_command) + { + p->actual_cmd.clear(); + } + else + { + int err; bool has_command = path_get_path(args.at(0).completion, &p->actual_cmd); - err = errno; - + err = errno; + bool use_implicit_cd = false; if (! has_command) { @@ -2063,7 +2063,7 @@ int parser_t::parse_job( process_t *p, args.clear(); args.push_back(completion_t(L"cd")); args.push_back(completion_t(implicit_cd_path)); - + /* If we have defined a wrapper around cd, use it, otherwise use the cd builtin */ if (use_function && function_exists(L"cd")) p->type = INTERNAL_FUNCTION; @@ -2071,17 +2071,17 @@ int parser_t::parse_job( process_t *p, p->type = INTERNAL_BUILTIN; } } - - /* Check if the specified command exists */ - if( ! has_command && ! use_implicit_cd ) - { - - int tmp; + + /* Check if the specified command exists */ + if( ! has_command && ! use_implicit_cd ) + { + + int tmp; const wchar_t *cmd = args.at( 0 ).completion.c_str(); - - /* + + /* We couldn't find the specified command. - + What we want to happen now is that the specified job won't get executed, and an error message is printed on-screen, but @@ -2101,20 +2101,20 @@ int parser_t::parse_job( process_t *p, wchar_t *cpy = wcsdup( cmd ); wchar_t *valpart = wcschr( cpy, L'=' ); *valpart++=0; - + debug( 0, COMMAND_ASSIGN_ERR_MSG, cmd, cpy, valpart); free(cpy); - + } else if(cmd[0]==L'$') { - + const env_var_t val_wstr = env_get_string( cmd+1 ); - const wchar_t *val = val_wstr.missing() ? NULL : val_wstr.c_str(); + const wchar_t *val = val_wstr.missing() ? NULL : val_wstr.c_str(); if( val ) { debug( 0, @@ -2128,7 +2128,7 @@ int parser_t::parse_job( process_t *p, debug( 0, _(L"Variables may not be used as commands. Instead, define a function. See the help section for the function command by typing 'help function'." ), cmd ); - } + } } else if(wcschr( cmd, L'$' )) { @@ -2144,155 +2144,155 @@ int parser_t::parse_job( process_t *p, cmd?cmd:L"UNKNOWN" ); } else - { + { debug( 0, _(L"Unknown command '%ls'"), cmd?cmd:L"UNKNOWN" ); } - + tmp = current_tokenizer_pos; current_tokenizer_pos = tok_get_pos(tok); - + fwprintf( stderr, L"%ls", parser_t::current_line() ); - + current_tokenizer_pos=tmp; - + job_set_flag( j, JOB_SKIP, 1 ); event_fire_generic(L"fish_command_not_found", (wchar_t *)( args.at( 0 ).completion.c_str() ) ); proc_set_last_status( err==ENOENT?STATUS_UNKNOWN_COMMAND:STATUS_NOT_EXECUTABLE ); - } - } - } - - if( (p->type == EXTERNAL) && !use_command ) - { - error( SYNTAX_ERROR, - tok_get_pos( tok ), - UNKNOWN_BUILTIN_ERR_MSG, - args.back().completion.c_str() ); - } - } - - - if( is_new_block ) - { - - const wchar_t *end=parser_find_end( tok_string( tok ) + - current_tokenizer_pos ); - tokenizer subtok; - int make_sub_block = j->first_process != p; + } + } + } - if( !end ) - { - error( SYNTAX_ERROR, - tok_get_pos( tok ), - BLOCK_END_ERR_MSG ); - - } - else - { - - if( !make_sub_block ) - { - int done=0; - - for( tok_init( &subtok, end, 0 ); - !done && tok_has_next( &subtok ); - tok_next( &subtok ) ) - { - - switch( tok_last_type( &subtok ) ) - { - case TOK_END: - done = 1; - break; - - case TOK_REDIRECT_OUT: - case TOK_REDIRECT_NOCLOB: - case TOK_REDIRECT_APPEND: - case TOK_REDIRECT_IN: - case TOK_REDIRECT_FD: - case TOK_PIPE: - { - done = 1; - make_sub_block = 1; - break; - } - - case TOK_STRING: - { - break; - } - - default: - { - done = 1; - error( SYNTAX_ERROR, - current_tokenizer_pos, - BLOCK_END_ERR_MSG ); - } - } - } - - tok_destroy( &subtok ); - } - - if( make_sub_block ) - { - - long end_pos = end-tok_string( tok ); + if( (p->type == EXTERNAL) && !use_command ) + { + error( SYNTAX_ERROR, + tok_get_pos( tok ), + UNKNOWN_BUILTIN_ERR_MSG, + args.back().completion.c_str() ); + } + } + + + if( is_new_block ) + { + + const wchar_t *end=parser_find_end( tok_string( tok ) + + current_tokenizer_pos ); + tokenizer subtok; + int make_sub_block = j->first_process != p; + + if( !end ) + { + error( SYNTAX_ERROR, + tok_get_pos( tok ), + BLOCK_END_ERR_MSG ); + + } + else + { + + if( !make_sub_block ) + { + int done=0; + + for( tok_init( &subtok, end, 0 ); + !done && tok_has_next( &subtok ); + tok_next( &subtok ) ) + { + + switch( tok_last_type( &subtok ) ) + { + case TOK_END: + done = 1; + break; + + case TOK_REDIRECT_OUT: + case TOK_REDIRECT_NOCLOB: + case TOK_REDIRECT_APPEND: + case TOK_REDIRECT_IN: + case TOK_REDIRECT_FD: + case TOK_PIPE: + { + done = 1; + make_sub_block = 1; + break; + } + + case TOK_STRING: + { + break; + } + + default: + { + done = 1; + error( SYNTAX_ERROR, + current_tokenizer_pos, + BLOCK_END_ERR_MSG ); + } + } + } + + tok_destroy( &subtok ); + } + + if( make_sub_block ) + { + + long end_pos = end-tok_string( tok ); const wcstring sub_block(tok_string( tok ) + current_tokenizer_pos, end_pos - current_tokenizer_pos); - - p->type = INTERNAL_BLOCK; - args.at( 0 ) = completion_t(sub_block); - - tok_set_pos( tok, (int)end_pos ); - while( prev_block != current_block ) - { - parser_t::pop_block(); - } + p->type = INTERNAL_BLOCK; + args.at( 0 ) = completion_t(sub_block); - } - else tok_next( tok ); - } - - } - else tok_next( tok ); + tok_set_pos( tok, (int)end_pos ); - if( !error_code ) - { - if( p->type == INTERNAL_BUILTIN && parser_keywords_skip_arguments(args.at(0).completion)) - { - if( !p->get_argv() ) + while( prev_block != current_block ) + { + parser_t::pop_block(); + } + + } + else tok_next( tok ); + } + + } + else tok_next( tok ); + + if( !error_code ) + { + if( p->type == INTERNAL_BUILTIN && parser_keywords_skip_arguments(args.at(0).completion)) + { + if( !p->get_argv() ) p->set_argv(completions_to_wcstring_list(args)); - } - else - { - parse_job_argument_list(p, j, tok, args, unskip); - } - } + } + else + { + parse_job_argument_list(p, j, tok, args, unskip); + } + } - if( !error_code ) - { - if( !is_new_block ) - { - current_block->had_command = true; - } - } + if( !error_code ) + { + if( !is_new_block ) + { + current_block->had_command = true; + } + } - if( error_code ) - { - /* - Make sure the block stack is consistent - */ - while( prev_block != current_block ) - { - parser_t::pop_block(); - } - } - current_tokenizer_pos = prev_tokenizer_pos; - return !error_code; + if( error_code ) + { + /* + Make sure the block stack is consistent + */ + while( prev_block != current_block ) + { + parser_t::pop_block(); + } + } + current_tokenizer_pos = prev_tokenizer_pos; + return !error_code; } /** @@ -2305,32 +2305,32 @@ int parser_t::parse_job( process_t *p, */ void parser_t::skipped_exec( job_t * j ) { - process_t *p; + process_t *p; /* Handle other skipped guys */ - for( p = j->first_process; p; p=p->next ) - { - if( p->type == INTERNAL_BUILTIN ) - { - if(( wcscmp( p->argv0(), L"for" )==0) || - ( wcscmp( p->argv0(), L"switch" )==0) || - ( wcscmp( p->argv0(), L"begin" )==0) || - ( wcscmp( p->argv0(), L"function" )==0)) - { - this->push_block( new fake_block_t() ); - } - else if( wcscmp( p->argv0(), L"end" )==0) - { - if(!current_block->outer->skip ) - { - exec( *this, j ); - return; - } - parser_t::pop_block(); - } - else if( wcscmp( p->argv0(), L"else" )==0) - { - if (current_block->type() == IF) + for( p = j->first_process; p; p=p->next ) + { + if( p->type == INTERNAL_BUILTIN ) + { + if(( wcscmp( p->argv0(), L"for" )==0) || + ( wcscmp( p->argv0(), L"switch" )==0) || + ( wcscmp( p->argv0(), L"begin" )==0) || + ( wcscmp( p->argv0(), L"function" )==0)) + { + this->push_block( new fake_block_t() ); + } + else if( wcscmp( p->argv0(), L"end" )==0) + { + if(!current_block->outer->skip ) + { + exec( *this, j ); + return; + } + parser_t::pop_block(); + } + else if( wcscmp( p->argv0(), L"else" )==0) + { + if (current_block->type() == IF) { /* Evaluate this ELSE if the IF expression failed, and so has every ELSEIF (if any) expression thus far */ const if_block_t *ib = static_cast(current_block); @@ -2341,17 +2341,17 @@ void parser_t::skipped_exec( job_t * j ) } } } - else if( wcscmp( p->argv0(), L"case" )==0) - { - if(current_block->type() == SWITCH) - { - exec( *this, j ); - return; - } - } - } - } - job_free( j ); + else if( wcscmp( p->argv0(), L"case" )==0) + { + if(current_block->type() == SWITCH) + { + exec( *this, j ); + return; + } + } + } + } + job_free( j ); } /* Return whether we should skip the current block, if it is an elseif. */ @@ -2366,10 +2366,10 @@ static bool job_should_skip_elseif(const job_t *job, const block_t *current_bloc { /* We are an IF block */ const if_block_t *ib = static_cast(current_block); - + /* Execute this ELSEIF if the IF expression has been evaluated, it evaluated to false, and all ELSEIFs so far have evaluated to false. */ bool execute_elseif = (ib->if_expr_evaluated && ! ib->any_branch_taken); - + /* Invert the sense */ return ! execute_elseif; } @@ -2385,83 +2385,83 @@ static bool job_should_skip_elseif(const job_t *job, const block_t *current_bloc void parser_t::eval_job( tokenizer *tok ) { ASSERT_IS_MAIN_THREAD(); - job_t *j; + job_t *j; + + int start_pos = job_start_pos = tok_get_pos( tok ); + long long t1=0, t2=0, t3=0; + - int start_pos = job_start_pos = tok_get_pos( tok ); - long long t1=0, t2=0, t3=0; - - profile_item_t *profile_item = NULL; - bool skip = false; - int job_begin_pos, prev_tokenizer_pos; - const bool do_profile = profile; - - if( do_profile ) - { + bool skip = false; + int job_begin_pos, prev_tokenizer_pos; + const bool do_profile = profile; + + if( do_profile ) + { profile_items.resize(profile_items.size() + 1); profile_item = &profile_items.back(); profile_item->cmd = L""; - profile_item->skipped = 1; - t1 = get_time(); - } + profile_item->skipped = 1; + t1 = get_time(); + } - switch( tok_last_type( tok ) ) - { - case TOK_STRING: - { - j = this->job_create(); - job_set_flag( j, JOB_FOREGROUND, 1 ); - job_set_flag( j, JOB_TERMINAL, job_get_flag( j, JOB_CONTROL ) ); - job_set_flag( j, JOB_TERMINAL, job_get_flag( j, JOB_CONTROL ) \ - && (!is_subshell && !is_event)); - job_set_flag( j, JOB_SKIP_NOTIFICATION, is_subshell \ - || is_block \ - || is_event \ - || (!get_is_interactive())); - - current_block->job = j; - - if( get_is_interactive() ) - { - if( tcgetattr (0, &j->tmodes) ) - { - tok_next( tok ); - wperror( L"tcgetattr" ); - job_free( j ); - break; - } - } + switch( tok_last_type( tok ) ) + { + case TOK_STRING: + { + j = this->job_create(); + job_set_flag( j, JOB_FOREGROUND, 1 ); + job_set_flag( j, JOB_TERMINAL, job_get_flag( j, JOB_CONTROL ) ); + job_set_flag( j, JOB_TERMINAL, job_get_flag( j, JOB_CONTROL ) \ + && (!is_subshell && !is_event)); + job_set_flag( j, JOB_SKIP_NOTIFICATION, is_subshell \ + || is_block \ + || is_event \ + || (!get_is_interactive())); - j->first_process = new process_t(); - job_begin_pos = tok_get_pos( tok ); - - if( parse_job( j->first_process, j, tok ) && - j->first_process->get_argv() ) - { - if( job_start_pos < tok_get_pos( tok ) ) - { - long stop_pos = tok_get_pos( tok ); - const wchar_t *newline = wcschr(tok_string(tok)+start_pos, L'\n'); - if( newline ) - stop_pos = mini( stop_pos, newline - tok_string(tok) ); + current_block->job = j; - j->set_command(wcstring(tok_string(tok)+start_pos, stop_pos-start_pos)); - } - else - j->set_command(L""); + if( get_is_interactive() ) + { + if( tcgetattr (0, &j->tmodes) ) + { + tok_next( tok ); + wperror( L"tcgetattr" ); + job_free( j ); + break; + } + } + + j->first_process = new process_t(); + job_begin_pos = tok_get_pos( tok ); + + if( parse_job( j->first_process, j, tok ) && + j->first_process->get_argv() ) + { + if( job_start_pos < tok_get_pos( tok ) ) + { + long stop_pos = tok_get_pos( tok ); + const wchar_t *newline = wcschr(tok_string(tok)+start_pos, L'\n'); + if( newline ) + stop_pos = mini( stop_pos, newline - tok_string(tok) ); + + j->set_command(wcstring(tok_string(tok)+start_pos, stop_pos-start_pos)); + } + else + j->set_command(L""); + + if( do_profile ) + { + t2 = get_time(); + profile_item->cmd = wcsdup( j->command_wcstr() ); + profile_item->skipped=current_block->skip; + } - if( do_profile ) - { - t2 = get_time(); - profile_item->cmd = wcsdup( j->command_wcstr() ); - profile_item->skipped=current_block->skip; - } - /* If we're an ELSEIF, then we may want to unskip, if we're skipping because of an IF */ if (job_get_flag(j, JOB_ELSEIF)) { bool skip_elseif = job_should_skip_elseif(j, current_block); - + /* Record that we're entering an elseif */ if (! skip_elseif) { @@ -2469,62 +2469,62 @@ void parser_t::eval_job( tokenizer *tok ) assert(current_block->type() == IF); static_cast(current_block)->is_elseif_entry = true; } - + /* Record that in the block too. This is similar to what builtin_else does. */ current_block->skip = skip_elseif; } - + skip = skip || current_block->skip; - skip = skip || job_get_flag( j, JOB_WILDCARD_ERROR ); - skip = skip || job_get_flag( j, JOB_SKIP ); - - if(!skip ) - { - int was_builtin = 0; - if( j->first_process->type==INTERNAL_BUILTIN && !j->first_process->next) - was_builtin = 1; - prev_tokenizer_pos = current_tokenizer_pos; - current_tokenizer_pos = job_begin_pos; - exec( *this, j ); - current_tokenizer_pos = prev_tokenizer_pos; - - /* Only external commands require a new fishd barrier */ - if( !was_builtin ) - set_proc_had_barrier(false); - } - else - { - this->skipped_exec( j ); - } + skip = skip || job_get_flag( j, JOB_WILDCARD_ERROR ); + skip = skip || job_get_flag( j, JOB_SKIP ); - if( do_profile ) - { - t3 = get_time(); - profile_item->level=eval_level; - profile_item->parse = (int)(t2-t1); - profile_item->exec=(int)(t3-t2); - } + if(!skip ) + { + int was_builtin = 0; + if( j->first_process->type==INTERNAL_BUILTIN && !j->first_process->next) + was_builtin = 1; + prev_tokenizer_pos = current_tokenizer_pos; + current_tokenizer_pos = job_begin_pos; + exec( *this, j ); + current_tokenizer_pos = prev_tokenizer_pos; - if( current_block->type() == WHILE ) - { + /* Only external commands require a new fishd barrier */ + if( !was_builtin ) + set_proc_had_barrier(false); + } + else + { + this->skipped_exec( j ); + } + + if( do_profile ) + { + t3 = get_time(); + profile_item->level=eval_level; + profile_item->parse = (int)(t2-t1); + profile_item->exec=(int)(t3-t2); + } + + if( current_block->type() == WHILE ) + { while_block_t *wb = static_cast(current_block); - switch( wb->status ) - { - case WHILE_TEST_FIRST: - { - // PCA I added the 'wb->skip ||' part because we couldn't reliably - // control-C out of loops like this: while test 1 -eq 1; end - wb->skip = wb->skip || proc_get_last_status()!= 0; - wb->status = WHILE_TESTED; - } - break; - } - } + switch( wb->status ) + { + case WHILE_TEST_FIRST: + { + // PCA I added the 'wb->skip ||' part because we couldn't reliably + // control-C out of loops like this: while test 1 -eq 1; end + wb->skip = wb->skip || proc_get_last_status()!= 0; + wb->status = WHILE_TESTED; + } + break; + } + } + + if( current_block->type() == IF ) + { + if_block_t *ib = static_cast(current_block); - if( current_block->type() == IF ) - { - if_block_t *ib = static_cast(current_block); - if (ib->skip) { /* Nothing */ @@ -2534,10 +2534,10 @@ void parser_t::eval_job( tokenizer *tok ) /* Execute the IF */ bool if_result = (proc_get_last_status() == 0); ib->any_branch_taken = if_result; - + /* Don't execute if the expression failed */ - current_block->skip = ! if_result; - ib->if_expr_evaluated = true; + current_block->skip = ! if_result; + ib->if_expr_evaluated = true; } else if (ib->is_elseif_entry && ! ib->any_branch_taken) { @@ -2547,200 +2547,200 @@ void parser_t::eval_job( tokenizer *tok ) current_block->skip = ! elseif_taken; ib->is_elseif_entry = false; } - } + } - } - else - { - /* - This job could not be properly parsed. We free it - instead, and set the status to 1. This should be - rare, since most errors should be detected by the - ahead of time validator. - */ - job_free( j ); + } + else + { + /* + This job could not be properly parsed. We free it + instead, and set the status to 1. This should be + rare, since most errors should be detected by the + ahead of time validator. + */ + job_free( j ); - proc_set_last_status( 1 ); - } - current_block->job = 0; - break; - } + proc_set_last_status( 1 ); + } + current_block->job = 0; + break; + } - case TOK_END: - { - if( tok_has_next( tok )) - tok_next( tok ); - break; - } + case TOK_END: + { + if( tok_has_next( tok )) + tok_next( tok ); + break; + } - case TOK_BACKGROUND: - { - const wchar_t *str = tok_string( tok ); - if( tok_get_pos(tok)>0 && str[tok_get_pos(tok)-1] == L'&' ) - { - error( SYNTAX_ERROR, - tok_get_pos( tok ), - CMD_AND_ERR_MSG, - tok_get_desc( tok_last_type(tok) ) ); - } - else - { - error( SYNTAX_ERROR, - tok_get_pos( tok ), - CMD_ERR_MSG, - tok_get_desc( tok_last_type(tok) ) ); - } + case TOK_BACKGROUND: + { + const wchar_t *str = tok_string( tok ); + if( tok_get_pos(tok)>0 && str[tok_get_pos(tok)-1] == L'&' ) + { + error( SYNTAX_ERROR, + tok_get_pos( tok ), + CMD_AND_ERR_MSG, + tok_get_desc( tok_last_type(tok) ) ); + } + else + { + error( SYNTAX_ERROR, + tok_get_pos( tok ), + CMD_ERR_MSG, + tok_get_desc( tok_last_type(tok) ) ); + } - return; - } + return; + } - case TOK_ERROR: - { - error( SYNTAX_ERROR, - tok_get_pos( tok ), - TOK_ERR_MSG, - tok_last(tok) ); + case TOK_ERROR: + { + error( SYNTAX_ERROR, + tok_get_pos( tok ), + TOK_ERR_MSG, + tok_last(tok) ); - return; - } + return; + } - default: - { - error( SYNTAX_ERROR, - tok_get_pos( tok ), - CMD_ERR_MSG, - tok_get_desc( tok_last_type(tok)) ); + default: + { + error( SYNTAX_ERROR, + tok_get_pos( tok ), + CMD_ERR_MSG, + tok_get_desc( tok_last_type(tok)) ); - return; - } - } + return; + } + } - job_reap( 0 ); + job_reap( 0 ); } int parser_t::eval( const wcstring &cmdStr, const io_chain_t &io, enum block_type_t block_type ) { const wchar_t * const cmd = cmdStr.c_str(); - size_t forbid_count; - int code; - tokenizer *previous_tokenizer=current_tokenizer; - block_t *start_current_block = current_block; - + size_t forbid_count; + int code; + tokenizer *previous_tokenizer=current_tokenizer; + block_t *start_current_block = current_block; + /* Record the current chain so we can put it back later */ const io_chain_t prev_io = block_io; block_io = io; - + std::vector prev_forbidden = forbidden_function; - if( block_type == SUBST ) - { - forbidden_function.clear(); - } - - CHECK_BLOCK( 1 ); - - forbid_count = forbidden_function.size(); + if( block_type == SUBST ) + { + forbidden_function.clear(); + } - block_io = io; + CHECK_BLOCK( 1 ); - job_reap( 0 ); + forbid_count = forbidden_function.size(); - debug( 4, L"eval: %ls", cmd ); + block_io = io; - if( !cmd ) - { - debug( 1, - EVAL_NULL_ERR_MSG ); - bugreport(); - return 1; - } + job_reap( 0 ); - if( (block_type != TOP) && - (block_type != SUBST)) - { - debug( 1, - INVALID_SCOPE_ERR_MSG, - parser_t::get_block_desc( block_type ) ); - bugreport(); - return 1; - } + debug( 4, L"eval: %ls", cmd ); - eval_level++; + if( !cmd ) + { + debug( 1, + EVAL_NULL_ERR_MSG ); + bugreport(); + return 1; + } - this->push_block( new scope_block_t(block_type) ); + if( (block_type != TOP) && + (block_type != SUBST)) + { + debug( 1, + INVALID_SCOPE_ERR_MSG, + parser_t::get_block_desc( block_type ) ); + bugreport(); + return 1; + } - current_tokenizer = new tokenizer; - tok_init( current_tokenizer, cmd, 0 ); + eval_level++; - error_code = 0; + this->push_block( new scope_block_t(block_type) ); - event_fire( NULL ); + current_tokenizer = new tokenizer; + tok_init( current_tokenizer, cmd, 0 ); - while( tok_has_next( current_tokenizer ) && - !error_code && - !sanity_check() && - !exit_status() ) - { - this->eval_job( current_tokenizer ); - event_fire( NULL ); - } + error_code = 0; - parser_t::pop_block(); + event_fire( NULL ); - while( start_current_block != current_block ) - { - if( current_block == 0 ) - { - debug( 0, - _(L"End of block mismatch. Program terminating.") ); - bugreport(); - FATAL_EXIT(); - break; - } + while( tok_has_next( current_tokenizer ) && + !error_code && + !sanity_check() && + !exit_status() ) + { + this->eval_job( current_tokenizer ); + event_fire( NULL ); + } - if( (!error_code) && (!exit_status()) && (!proc_get_last_status()) ) - { + parser_t::pop_block(); - //debug( 2, L"Status %d\n", proc_get_last_status() ); + while( start_current_block != current_block ) + { + if( current_block == 0 ) + { + debug( 0, + _(L"End of block mismatch. Program terminating.") ); + bugreport(); + FATAL_EXIT(); + break; + } - debug( 1, - L"%ls", parser_t::get_block_desc( current_block->type() ) ); - debug( 1, - BLOCK_END_ERR_MSG ); - fwprintf( stderr, L"%ls", parser_t::current_line() ); + if( (!error_code) && (!exit_status()) && (!proc_get_last_status()) ) + { - const wcstring h = builtin_help_get( *this, L"end" ); - if( h.size() ) - fwprintf( stderr, L"%ls", h.c_str() ); - break; + //debug( 2, L"Status %d\n", proc_get_last_status() ); - } - parser_t::pop_block(); - } + debug( 1, + L"%ls", parser_t::get_block_desc( current_block->type() ) ); + debug( 1, + BLOCK_END_ERR_MSG ); + fwprintf( stderr, L"%ls", parser_t::current_line() ); - this->print_errors_stderr(); + const wcstring h = builtin_help_get( *this, L"end" ); + if( h.size() ) + fwprintf( stderr, L"%ls", h.c_str() ); + break; - tok_destroy( current_tokenizer ); - delete current_tokenizer; + } + parser_t::pop_block(); + } + + this->print_errors_stderr(); + + tok_destroy( current_tokenizer ); + delete current_tokenizer; while (forbidden_function.size() > forbid_count) - parser_t::allow_function(); + parser_t::allow_function(); - /* - Restore previous eval state - */ - forbidden_function = prev_forbidden; - current_tokenizer=previous_tokenizer; - block_io = prev_io; - eval_level--; + /* + Restore previous eval state + */ + forbidden_function = prev_forbidden; + current_tokenizer=previous_tokenizer; + block_io = prev_io; + eval_level--; - code=error_code; - error_code=0; + code=error_code; + error_code=0; - job_reap( 0 ); + job_reap( 0 ); - return code; + return code; } @@ -2749,16 +2749,16 @@ int parser_t::eval( const wcstring &cmdStr, const io_chain_t &io, enum block_typ */ block_type_t parser_get_block_type( const wcstring &cmd ) { - int i; - - for( i=0; block_lookup[i].desc; i++ ) - { - if( block_lookup[i].name && cmd == block_lookup[i].name ) - { - return block_lookup[i].type; - } - } - return (block_type_t)-1; + int i; + + for( i=0; block_lookup[i].desc; i++ ) + { + if( block_lookup[i].name && cmd == block_lookup[i].name ) + { + return block_lookup[i].type; + } + } + return (block_type_t)-1; } /** @@ -2766,16 +2766,16 @@ block_type_t parser_get_block_type( const wcstring &cmd ) */ const wchar_t *parser_get_block_command( int type ) { - int i; - - for( i=0; block_lookup[i].desc; i++ ) - { - if( block_lookup[i].type == type ) - { - return block_lookup[i].name; - } - } - return 0; + int i; + + for( i=0; block_lookup[i].desc; i++ ) + { + if( block_lookup[i].type == type ) + { + return block_lookup[i].name; + } + } + return 0; } /** @@ -2785,349 +2785,349 @@ const wchar_t *parser_get_block_command( int type ) */ int parser_t::parser_test_argument( const wchar_t *arg, wcstring *out, const wchar_t *prefix, int offset ) { - wchar_t *unesc; - wchar_t *pos; - int err=0; - - wchar_t *paran_begin, *paran_end; - wchar_t *arg_cpy; - int do_loop = 1; - - CHECK( arg, 1 ); - - arg_cpy = wcsdup( arg ); - - while( do_loop ) - { - switch( parse_util_locate_cmdsubst(arg_cpy, - ¶n_begin, - ¶n_end, - 0 ) ) - { - case -1: - err=1; - if( out ) - { - error( SYNTAX_ERROR, - offset, - L"Mismatched parenthesis" ); - this->print_errors( *out, prefix); - } - free( arg_cpy ); - return err; - - case 0: - do_loop = 0; - break; + wchar_t *unesc; + wchar_t *pos; + int err=0; - case 1: - { - - wchar_t *subst = wcsndup( paran_begin+1, paran_end-paran_begin-1 ); - wcstring tmp; - - tmp.append(arg_cpy, paran_begin - arg_cpy); - tmp.push_back(INTERNAL_SEPARATOR); + wchar_t *paran_begin, *paran_end; + wchar_t *arg_cpy; + int do_loop = 1; + + CHECK( arg, 1 ); + + arg_cpy = wcsdup( arg ); + + while( do_loop ) + { + switch( parse_util_locate_cmdsubst(arg_cpy, + ¶n_begin, + ¶n_end, + 0 ) ) + { + case -1: + err=1; + if( out ) + { + error( SYNTAX_ERROR, + offset, + L"Mismatched parenthesis" ); + this->print_errors( *out, prefix); + } + free( arg_cpy ); + return err; + + case 0: + do_loop = 0; + break; + + case 1: + { + + wchar_t *subst = wcsndup( paran_begin+1, paran_end-paran_begin-1 ); + wcstring tmp; + + tmp.append(arg_cpy, paran_begin - arg_cpy); + tmp.push_back(INTERNAL_SEPARATOR); tmp.append(paran_end+1); - -// debug( 1, L"%ls -> %ls %ls", arg_cpy, subst, tmp.buff ); - - err |= parser_t::test( subst, 0, out, prefix ); - - free( subst ); - free( arg_cpy ); - arg_cpy = wcsdup(tmp.c_str()); - - /* - Do _not_ call sb_destroy on this stringbuffer - it's - buffer is used as the new 'arg_cpy'. It is free'd at - the end of the loop. - */ - break; - } - } - } - unesc = unescape( arg_cpy, 1 ); - if( !unesc ) - { - if( out ) - { - error( SYNTAX_ERROR, - offset, - L"Invalid token '%ls'", arg_cpy ); - print_errors( *out, prefix); - } - return 1; - } - else - { - /* - Check for invalid variable expansions - */ - for( pos = unesc; *pos; pos++ ) - { - switch( *pos ) - { - case VARIABLE_EXPAND: - case VARIABLE_EXPAND_SINGLE: - { - wchar_t n = *(pos+1); - - if( n != VARIABLE_EXPAND && - n != VARIABLE_EXPAND_SINGLE && - !wcsvarchr(n) ) - { - err=1; - if( out ) - { - expand_variable_error( *this, unesc, pos-unesc, offset ); - print_errors( *out, prefix); - } - } - - break; - } - } - } - } +// debug( 1, L"%ls -> %ls %ls", arg_cpy, subst, tmp.buff ); + + err |= parser_t::test( subst, 0, out, prefix ); + + free( subst ); + free( arg_cpy ); + arg_cpy = wcsdup(tmp.c_str()); + + /* + Do _not_ call sb_destroy on this stringbuffer - it's + buffer is used as the new 'arg_cpy'. It is free'd at + the end of the loop. + */ + break; + } + } + } + + unesc = unescape( arg_cpy, 1 ); + if( !unesc ) + { + if( out ) + { + error( SYNTAX_ERROR, + offset, + L"Invalid token '%ls'", arg_cpy ); + print_errors( *out, prefix); + } + return 1; + } + else + { + /* + Check for invalid variable expansions + */ + for( pos = unesc; *pos; pos++ ) + { + switch( *pos ) + { + case VARIABLE_EXPAND: + case VARIABLE_EXPAND_SINGLE: + { + wchar_t n = *(pos+1); + + if( n != VARIABLE_EXPAND && + n != VARIABLE_EXPAND_SINGLE && + !wcsvarchr(n) ) + { + err=1; + if( out ) + { + expand_variable_error( *this, unesc, pos-unesc, offset ); + print_errors( *out, prefix); + } + } + + break; + } + } + } + } + + free( arg_cpy ); + + free( unesc ); + return err; - free( arg_cpy ); - - free( unesc ); - return err; - } int parser_t::test_args(const wchar_t * buff, wcstring *out, const wchar_t *prefix ) { - tokenizer tok; - tokenizer *previous_tokenizer = current_tokenizer; - int previous_pos = current_tokenizer_pos; - int do_loop = 1; - int err = 0; - - CHECK( buff, 1 ); - - current_tokenizer = &tok; - - for( tok_init( &tok, buff, 0 ); - do_loop && tok_has_next( &tok ); - tok_next( &tok ) ) - { - current_tokenizer_pos = tok_get_pos( &tok ); - switch( tok_last_type( &tok ) ) - { - - case TOK_STRING: - { - err |= parser_test_argument( tok_last( &tok ), out, prefix, tok_get_pos( &tok ) ); - break; - } - - case TOK_END: - { - break; - } - - case TOK_ERROR: - { - if( out ) - { - error( SYNTAX_ERROR, - tok_get_pos( &tok ), - TOK_ERR_MSG, - tok_last(&tok) ); - print_errors( *out, prefix ); - } - err=1; - do_loop=0; - break; - } - - default: - { - if( out ) - { - error( SYNTAX_ERROR, - tok_get_pos( &tok ), - UNEXPECTED_TOKEN_ERR_MSG, - tok_get_desc( tok_last_type(&tok)) ); - print_errors( *out, prefix ); - } - err=1; - do_loop=0; - break; - } - } - } - - tok_destroy( &tok ); - - current_tokenizer=previous_tokenizer; - current_tokenizer_pos = previous_pos; - - error_code=0; - - return err; + tokenizer tok; + tokenizer *previous_tokenizer = current_tokenizer; + int previous_pos = current_tokenizer_pos; + int do_loop = 1; + int err = 0; + + CHECK( buff, 1 ); + + current_tokenizer = &tok; + + for( tok_init( &tok, buff, 0 ); + do_loop && tok_has_next( &tok ); + tok_next( &tok ) ) + { + current_tokenizer_pos = tok_get_pos( &tok ); + switch( tok_last_type( &tok ) ) + { + + case TOK_STRING: + { + err |= parser_test_argument( tok_last( &tok ), out, prefix, tok_get_pos( &tok ) ); + break; + } + + case TOK_END: + { + break; + } + + case TOK_ERROR: + { + if( out ) + { + error( SYNTAX_ERROR, + tok_get_pos( &tok ), + TOK_ERR_MSG, + tok_last(&tok) ); + print_errors( *out, prefix ); + } + err=1; + do_loop=0; + break; + } + + default: + { + if( out ) + { + error( SYNTAX_ERROR, + tok_get_pos( &tok ), + UNEXPECTED_TOKEN_ERR_MSG, + tok_get_desc( tok_last_type(&tok)) ); + print_errors( *out, prefix ); + } + err=1; + do_loop=0; + break; + } + } + } + + tok_destroy( &tok ); + + current_tokenizer=previous_tokenizer; + current_tokenizer_pos = previous_pos; + + error_code=0; + + return err; } int parser_t::test( const wchar_t * buff, - int *block_level, - wcstring *out, - const wchar_t *prefix ) + int *block_level, + wcstring *out, + const wchar_t *prefix ) { ASSERT_IS_MAIN_THREAD(); - - tokenizer tok; - /* - Set to one if a command name has been given for the currently - parsed process specification - */ - int had_cmd=0; - int err=0; - int unfinished = 0; - - tokenizer *previous_tokenizer=current_tokenizer; - int previous_pos=current_tokenizer_pos; - - int block_pos[BLOCK_MAX_COUNT] = {}; - block_type_t block_type[BLOCK_MAX_COUNT] = {}; - int count = 0; - int res = 0; - - /* - Set to 1 if the current command is inside a pipeline - */ - int is_pipeline = 0; - /* - Set to one if the currently specified process can not be used inside a pipeline - */ - int forbid_pipeline = 0; + tokenizer tok; + /* + Set to one if a command name has been given for the currently + parsed process specification + */ + int had_cmd=0; + int err=0; + int unfinished = 0; - /* - Set to one if an additional process specification is needed - */ - bool needs_cmd = false; + tokenizer *previous_tokenizer=current_tokenizer; + int previous_pos=current_tokenizer_pos; - /* - Counter on the number of arguments this function has encountered - so far. Is set to -1 when the count is unknown, i.e. after - encountering an argument that contains substitutions that can - expand to more/less arguemtns then 1. - */ - int arg_count=0; - - /* - The currently validated command. - */ + int block_pos[BLOCK_MAX_COUNT] = {}; + block_type_t block_type[BLOCK_MAX_COUNT] = {}; + int count = 0; + int res = 0; + + /* + Set to 1 if the current command is inside a pipeline + */ + int is_pipeline = 0; + + /* + Set to one if the currently specified process can not be used inside a pipeline + */ + int forbid_pipeline = 0; + + /* + Set to one if an additional process specification is needed + */ + bool needs_cmd = false; + + /* + Counter on the number of arguments this function has encountered + so far. Is set to -1 when the count is unknown, i.e. after + encountering an argument that contains substitutions that can + expand to more/less arguemtns then 1. + */ + int arg_count=0; + + /* + The currently validated command. + */ wcstring command; bool has_command = false; - - CHECK( buff, 1 ); - if( block_level ) - { - size_t len = wcslen(buff); - for( size_t i=0; i= BLOCK_MAX_COUNT ) - { + err=1; + if( out ) + { + error( SYNTAX_ERROR, + tok_get_pos( &tok ), + ILLEGAL_CMD_ERR_MSG, + tok_last( &tok ) ); + + print_errors( *out, prefix ); + } + break; + } + + if( needs_cmd ) + { + /* + end is not a valid command when a followup + command is needed, such as after 'and' or + 'while' + */ + if( contains( command, + L"end" ) ) + { + err=1; + if( out ) + { + error( SYNTAX_ERROR, + tok_get_pos( &tok ), + COND_ERR_MSG ); + + print_errors( *out, prefix ); + } + } + + needs_cmd = false; + } + + /* + Decrement block count on end command + */ + if( command == L"end") + { + tok_next( &tok ); + count--; + tok_set_pos( &tok, mark ); + } + + /* + Store the block level. This needs to be done + _after_ checking for end commands, but _before_ + checking for block opening commands. + */ + bool is_else_or_elseif = (command == L"else"); + if( block_level ) + { + block_level[tok_get_pos( &tok )] = count + (is_else_or_elseif?-1:0); + } + + /* + Handle block commands + */ + if( parser_keywords_is_block( command ) ) + { + if( count >= BLOCK_MAX_COUNT ) + { if (out) { error( SYNTAX_ERROR, tok_get_pos( &tok ), @@ -3135,325 +3135,325 @@ int parser_t::test( const wchar_t * buff, print_errors( *out, prefix ); } - } - else - { - block_type[count] = parser_get_block_type( command ); - block_pos[count] = current_tokenizer_pos; - tok_next( &tok ); - count++; - tok_set_pos( &tok, mark ); - } - } + } + else + { + block_type[count] = parser_get_block_type( command ); + block_pos[count] = current_tokenizer_pos; + tok_next( &tok ); + count++; + tok_set_pos( &tok, mark ); + } + } - /* - If parser_keywords_is_subcommand is true, the command - accepts a second command as it's first - argument. If parser_skip_arguments is true, the - second argument is optional. - */ - if( parser_keywords_is_subcommand( command ) && !parser_keywords_skip_arguments(command ) ) - { - needs_cmd = true; - had_cmd = 0; - } - - if( contains( command, - L"or", - L"and" ) ) - { - /* - 'or' and 'and' can not be used inside pipelines - */ - if( is_pipeline ) - { - err=1; - if( out ) - { - error( SYNTAX_ERROR, - tok_get_pos( &tok ), - EXEC_ERR_MSG ); + /* + If parser_keywords_is_subcommand is true, the command + accepts a second command as it's first + argument. If parser_skip_arguments is true, the + second argument is optional. + */ + if( parser_keywords_is_subcommand( command ) && !parser_keywords_skip_arguments(command ) ) + { + needs_cmd = true; + had_cmd = 0; + } - print_errors( *out, prefix ); + if( contains( command, + L"or", + L"and" ) ) + { + /* + 'or' and 'and' can not be used inside pipelines + */ + if( is_pipeline ) + { + err=1; + if( out ) + { + error( SYNTAX_ERROR, + tok_get_pos( &tok ), + EXEC_ERR_MSG ); - } - } - } - - /* - There are a lot of situations where pipelines - are forbidden, including when using the exec - builtin. - */ - if( parser_is_pipe_forbidden( command ) ) - { - if( is_pipeline ) - { - err=1; - if( out ) - { - error( SYNTAX_ERROR, - tok_get_pos( &tok ), - EXEC_ERR_MSG ); + print_errors( *out, prefix ); - print_errors( *out, prefix ); + } + } + } - } - } - forbid_pipeline = 1; - } + /* + There are a lot of situations where pipelines + are forbidden, including when using the exec + builtin. + */ + if( parser_is_pipe_forbidden( command ) ) + { + if( is_pipeline ) + { + err=1; + if( out ) + { + error( SYNTAX_ERROR, + tok_get_pos( &tok ), + EXEC_ERR_MSG ); - /* - Test that the case builtin is only used directly in a switch block - */ - if( command == L"case" ) - { - if( !count || block_type[count-1]!=SWITCH ) - { - err=1; + print_errors( *out, prefix ); - if( out ) - { - error( SYNTAX_ERROR, - tok_get_pos( &tok ), - INVALID_CASE_ERR_MSG ); + } + } + forbid_pipeline = 1; + } - print_errors( *out, prefix); - const wcstring h = builtin_help_get( *this, L"case" ); - if( h.size() ) - append_format( *out, L"%ls", h.c_str() ); - } - } - } + /* + Test that the case builtin is only used directly in a switch block + */ + if( command == L"case" ) + { + if( !count || block_type[count-1]!=SWITCH ) + { + err=1; - /* - Test that the return bultin is only used within function definitions - */ - if( command == L"return" ) - { - int found_func=0; - int i; - for( i=count-1; i>=0; i-- ) - { - if( block_type[i]==FUNCTION_DEF ) - { - found_func=1; - break; - } - } + if( out ) + { + error( SYNTAX_ERROR, + tok_get_pos( &tok ), + INVALID_CASE_ERR_MSG ); - if( !found_func ) - { - /* - Peek to see if the next argument is - --help, in which case we'll allow it to - show the help. - */ + print_errors( *out, prefix); + const wcstring h = builtin_help_get( *this, L"case" ); + if( h.size() ) + append_format( *out, L"%ls", h.c_str() ); + } + } + } - int old_pos = tok_get_pos( &tok ); - int is_help = 0; - - tok_next( &tok ); - if( tok_last_type( &tok ) == TOK_STRING ) - { + /* + Test that the return bultin is only used within function definitions + */ + if( command == L"return" ) + { + int found_func=0; + int i; + for( i=count-1; i>=0; i-- ) + { + if( block_type[i]==FUNCTION_DEF ) + { + found_func=1; + break; + } + } + + if( !found_func ) + { + /* + Peek to see if the next argument is + --help, in which case we'll allow it to + show the help. + */ + + int old_pos = tok_get_pos( &tok ); + int is_help = 0; + + tok_next( &tok ); + if( tok_last_type( &tok ) == TOK_STRING ) + { wcstring first_arg = tok_last(&tok); if (expand_one(first_arg, EXPAND_SKIP_CMDSUBST) && parser_t::is_help( first_arg.c_str(), 3)) - { - is_help = 1; - } - } - - tok_set_pos( &tok, old_pos ); - - if( !is_help ) - { - err=1; + { + is_help = 1; + } + } - if( out ) - { - error( SYNTAX_ERROR, - tok_get_pos( &tok ), - INVALID_RETURN_ERR_MSG ); - print_errors( *out, prefix ); - } - } - } - } - + tok_set_pos( &tok, old_pos ); - /* - Test that break and continue are only used within loop blocks - */ - if( contains( command, L"break", L"continue" ) ) - { - int found_loop=0; - int i; - for( i=count-1; i>=0; i-- ) - { - if( (block_type[i]==WHILE) || - (block_type[i]==FOR) ) - { - found_loop=1; - break; - } - } + if( !is_help ) + { + err=1; - if( !found_loop ) - { - /* - Peek to see if the next argument is - --help, in which case we'll allow it to - show the help. - */ + if( out ) + { + error( SYNTAX_ERROR, + tok_get_pos( &tok ), + INVALID_RETURN_ERR_MSG ); + print_errors( *out, prefix ); + } + } + } + } - int old_pos = tok_get_pos( &tok ); - int is_help = 0; - - tok_next( &tok ); - if( tok_last_type( &tok ) == TOK_STRING ) - { + + /* + Test that break and continue are only used within loop blocks + */ + if( contains( command, L"break", L"continue" ) ) + { + int found_loop=0; + int i; + for( i=count-1; i>=0; i-- ) + { + if( (block_type[i]==WHILE) || + (block_type[i]==FOR) ) + { + found_loop=1; + break; + } + } + + if( !found_loop ) + { + /* + Peek to see if the next argument is + --help, in which case we'll allow it to + show the help. + */ + + int old_pos = tok_get_pos( &tok ); + int is_help = 0; + + tok_next( &tok ); + if( tok_last_type( &tok ) == TOK_STRING ) + { wcstring first_arg = tok_last( &tok ); - if( expand_one(first_arg, EXPAND_SKIP_CMDSUBST) && parser_t::is_help( first_arg.c_str(), 3 ) ) - { - is_help = 1; - } - } - - tok_set_pos( &tok, old_pos ); - - if( !is_help ) - { - err=1; + if( expand_one(first_arg, EXPAND_SKIP_CMDSUBST) && parser_t::is_help( first_arg.c_str(), 3 ) ) + { + is_help = 1; + } + } - if( out ) - { - error( SYNTAX_ERROR, - tok_get_pos( &tok ), - INVALID_LOOP_ERR_MSG ); - print_errors( *out, prefix ); - } - } - } - } + tok_set_pos( &tok, old_pos ); - /* - Test that else and else-if are only used directly in an if-block - */ - if( command == L"else") - { - if( !count || block_type[count-1]!=IF ) - { - err=1; - if( out ) - { - error( SYNTAX_ERROR, - tok_get_pos( &tok ), - INVALID_ELSE_ERR_MSG, + if( !is_help ) + { + err=1; + + if( out ) + { + error( SYNTAX_ERROR, + tok_get_pos( &tok ), + INVALID_LOOP_ERR_MSG ); + print_errors( *out, prefix ); + } + } + } + } + + /* + Test that else and else-if are only used directly in an if-block + */ + if( command == L"else") + { + if( !count || block_type[count-1]!=IF ) + { + err=1; + if( out ) + { + error( SYNTAX_ERROR, + tok_get_pos( &tok ), + INVALID_ELSE_ERR_MSG, command.c_str()); - print_errors( *out, prefix ); - } - } - } + print_errors( *out, prefix ); + } + } + } - /* - Test that end is not used when not inside any block - */ - if( count < 0 ) - { - err = 1; - if( out ) - { - error( SYNTAX_ERROR, - tok_get_pos( &tok ), - INVALID_END_ERR_MSG ); - print_errors( *out, prefix ); - const wcstring h = builtin_help_get( *this, L"end" ); - if( h.size() ) - append_format( *out, L"%ls", h.c_str() ); - } - } - - } - else - { - err |= parser_test_argument( tok_last( &tok ), out, prefix, tok_get_pos( &tok ) ); - - /* If possible, keep track of number of supplied arguments */ - if( arg_count >= 0 && expand_is_clean( tok_last( &tok ) ) ) - { - arg_count++; - } - else - { - arg_count = -1; - } - - if( has_command ) - { - - /* - Try to make sure the second argument to 'for' is 'in' - */ - if( command == L"for" ) - { - if( arg_count == 1 ) - { - - if( wcsvarname( tok_last( &tok )) ) - { - - err = 1; - - if( out ) - { - error( SYNTAX_ERROR, - tok_get_pos( &tok ), - BUILTIN_FOR_ERR_NAME, - L"for", - tok_last( &tok ) ); - - print_errors( *out, prefix ); - } - } - - } - else if( arg_count == 2 ) - { - if( wcscmp( tok_last( &tok ), L"in" ) != 0 ) - { - err = 1; - - if( out ) - { - error( SYNTAX_ERROR, - tok_get_pos( &tok ), - BUILTIN_FOR_ERR_IN, - L"for" ); - - print_errors( *out, prefix ); - } - } - } - } + /* + Test that end is not used when not inside any block + */ + if( count < 0 ) + { + err = 1; + if( out ) + { + error( SYNTAX_ERROR, + tok_get_pos( &tok ), + INVALID_END_ERR_MSG ); + print_errors( *out, prefix ); + const wcstring h = builtin_help_get( *this, L"end" ); + if( h.size() ) + append_format( *out, L"%ls", h.c_str() ); + } + } + + } + else + { + err |= parser_test_argument( tok_last( &tok ), out, prefix, tok_get_pos( &tok ) ); + + /* If possible, keep track of number of supplied arguments */ + if( arg_count >= 0 && expand_is_clean( tok_last( &tok ) ) ) + { + arg_count++; + } + else + { + arg_count = -1; + } + + if( has_command ) + { + + /* + Try to make sure the second argument to 'for' is 'in' + */ + if( command == L"for" ) + { + if( arg_count == 1 ) + { + + if( wcsvarname( tok_last( &tok )) ) + { + + err = 1; + + if( out ) + { + error( SYNTAX_ERROR, + tok_get_pos( &tok ), + BUILTIN_FOR_ERR_NAME, + L"for", + tok_last( &tok ) ); + + print_errors( *out, prefix ); + } + } + + } + else if( arg_count == 2 ) + { + if( wcscmp( tok_last( &tok ), L"in" ) != 0 ) + { + err = 1; + + if( out ) + { + error( SYNTAX_ERROR, + tok_get_pos( &tok ), + BUILTIN_FOR_ERR_IN, + L"for" ); + + print_errors( *out, prefix ); + } + } + } + } else if (command == L"else") { if (arg_count == 1) { /* Any second argument must be "if" */ - if (wcscmp(tok_last(&tok), L"if") != 0) - { - err = 1; - - if( out ) - { - error( SYNTAX_ERROR, - tok_get_pos( &tok ), - BUILTIN_ELSEIF_ERR_ARGUMENT, - L"else" ); - print_errors( *out, prefix ); - } - } + if (wcscmp(tok_last(&tok), L"if") != 0) + { + err = 1; + + if( out ) + { + error( SYNTAX_ERROR, + tok_get_pos( &tok ), + BUILTIN_ELSEIF_ERR_ARGUMENT, + L"else" ); + print_errors( *out, prefix ); + } + } else { /* Successfully detected "else if". Now we need a new command. */ @@ -3462,185 +3462,185 @@ int parser_t::test( const wchar_t * buff, } } } - } + } - } - - break; - } + } - case TOK_REDIRECT_OUT: - case TOK_REDIRECT_IN: - case TOK_REDIRECT_APPEND: - case TOK_REDIRECT_FD: - case TOK_REDIRECT_NOCLOB: - { - if( !had_cmd ) - { - err = 1; - if( out ) - { - error( SYNTAX_ERROR, - tok_get_pos( &tok ), - INVALID_REDIRECTION_ERR_MSG ); - print_errors( *out, prefix ); - } - } - break; - } + break; + } - case TOK_END: - { - if( needs_cmd && !had_cmd ) - { - err = 1; - if( out ) - { - error( SYNTAX_ERROR, - tok_get_pos( &tok ), - CMD_ERR_MSG, - tok_get_desc( tok_last_type(&tok))); - print_errors( *out, prefix ); - } - } - needs_cmd = false; - had_cmd = 0; - is_pipeline=0; - forbid_pipeline=0; - end_of_cmd = 1; - - break; - } + case TOK_REDIRECT_OUT: + case TOK_REDIRECT_IN: + case TOK_REDIRECT_APPEND: + case TOK_REDIRECT_FD: + case TOK_REDIRECT_NOCLOB: + { + if( !had_cmd ) + { + err = 1; + if( out ) + { + error( SYNTAX_ERROR, + tok_get_pos( &tok ), + INVALID_REDIRECTION_ERR_MSG ); + print_errors( *out, prefix ); + } + } + break; + } - case TOK_PIPE: - { - if( !had_cmd ) - { - err=1; - if( out ) - { - if( tok_get_pos(&tok)>0 && buff[tok_get_pos(&tok)-1] == L'|' ) - { - error( SYNTAX_ERROR, - tok_get_pos( &tok ), - CMD_OR_ERR_MSG, - tok_get_desc( tok_last_type(&tok) ) ); - - } - else - { - error( SYNTAX_ERROR, - tok_get_pos( &tok ), - CMD_ERR_MSG, - tok_get_desc( tok_last_type(&tok))); - } - - print_errors( *out, prefix ); - } - } - else if( forbid_pipeline ) - { - err=1; - if( out ) - { - error( SYNTAX_ERROR, - tok_get_pos( &tok ), - EXEC_ERR_MSG ); - - print_errors( *out, prefix ); - } - } - else - { - needs_cmd = true; - is_pipeline=1; - had_cmd=0; - end_of_cmd = 1; - - } - break; - } - - case TOK_BACKGROUND: - { - if( !had_cmd ) - { - err = 1; - if( out ) - { - if( tok_get_pos(&tok)>0 && buff[tok_get_pos(&tok)-1] == L'&' ) - { - error( SYNTAX_ERROR, - tok_get_pos( &tok ), - CMD_AND_ERR_MSG, - tok_get_desc( tok_last_type(&tok) ) ); - - } - else - { - error( SYNTAX_ERROR, - tok_get_pos( &tok ), - CMD_ERR_MSG, - tok_get_desc( tok_last_type(&tok))); - } - - print_errors( *out, prefix ); - } - } - - had_cmd = 0; - end_of_cmd = 1; - - break; - } + case TOK_END: + { + if( needs_cmd && !had_cmd ) + { + err = 1; + if( out ) + { + error( SYNTAX_ERROR, + tok_get_pos( &tok ), + CMD_ERR_MSG, + tok_get_desc( tok_last_type(&tok))); + print_errors( *out, prefix ); + } + } + needs_cmd = false; + had_cmd = 0; + is_pipeline=0; + forbid_pipeline=0; + end_of_cmd = 1; - case TOK_ERROR: - default: - if( tok_get_error( &tok ) == TOK_UNTERMINATED_QUOTE ) - { - unfinished = 1; - } - else - { - err = 1; - if( out ) - { - error( SYNTAX_ERROR, - tok_get_pos( &tok ), - TOK_ERR_MSG, - tok_last(&tok) ); - - - print_errors( *out, prefix ); - } - } - - break; - } + break; + } - if( end_of_cmd ) - { - if( has_command && command == L"for" ) - { - if( arg_count >= 0 && arg_count < 2 ) - { - /* - Not enough arguments to the for builtin - */ - err = 1; - - if( out ) - { - error( SYNTAX_ERROR, - tok_get_pos( &tok ), - BUILTIN_FOR_ERR_COUNT, - L"for", - arg_count ); - - print_errors( *out, prefix ); - } - } - } + case TOK_PIPE: + { + if( !had_cmd ) + { + err=1; + if( out ) + { + if( tok_get_pos(&tok)>0 && buff[tok_get_pos(&tok)-1] == L'|' ) + { + error( SYNTAX_ERROR, + tok_get_pos( &tok ), + CMD_OR_ERR_MSG, + tok_get_desc( tok_last_type(&tok) ) ); + + } + else + { + error( SYNTAX_ERROR, + tok_get_pos( &tok ), + CMD_ERR_MSG, + tok_get_desc( tok_last_type(&tok))); + } + + print_errors( *out, prefix ); + } + } + else if( forbid_pipeline ) + { + err=1; + if( out ) + { + error( SYNTAX_ERROR, + tok_get_pos( &tok ), + EXEC_ERR_MSG ); + + print_errors( *out, prefix ); + } + } + else + { + needs_cmd = true; + is_pipeline=1; + had_cmd=0; + end_of_cmd = 1; + + } + break; + } + + case TOK_BACKGROUND: + { + if( !had_cmd ) + { + err = 1; + if( out ) + { + if( tok_get_pos(&tok)>0 && buff[tok_get_pos(&tok)-1] == L'&' ) + { + error( SYNTAX_ERROR, + tok_get_pos( &tok ), + CMD_AND_ERR_MSG, + tok_get_desc( tok_last_type(&tok) ) ); + + } + else + { + error( SYNTAX_ERROR, + tok_get_pos( &tok ), + CMD_ERR_MSG, + tok_get_desc( tok_last_type(&tok))); + } + + print_errors( *out, prefix ); + } + } + + had_cmd = 0; + end_of_cmd = 1; + + break; + } + + case TOK_ERROR: + default: + if( tok_get_error( &tok ) == TOK_UNTERMINATED_QUOTE ) + { + unfinished = 1; + } + else + { + err = 1; + if( out ) + { + error( SYNTAX_ERROR, + tok_get_pos( &tok ), + TOK_ERR_MSG, + tok_last(&tok) ); + + + print_errors( *out, prefix ); + } + } + + break; + } + + if( end_of_cmd ) + { + if( has_command && command == L"for" ) + { + if( arg_count >= 0 && arg_count < 2 ) + { + /* + Not enough arguments to the for builtin + */ + err = 1; + + if( out ) + { + error( SYNTAX_ERROR, + tok_get_pos( &tok ), + BUILTIN_FOR_ERR_COUNT, + L"for", + arg_count ); + + print_errors( *out, prefix ); + } + } + } else if (has_command && command == L"else") { if (arg_count == 1) @@ -3649,133 +3649,133 @@ int parser_t::test( const wchar_t * buff, err = true; if (out) { - error( SYNTAX_ERROR, - tok_get_pos( &tok ), - BUILTIN_ELSEIF_ERR_COUNT, - L"else", - arg_count ); - - print_errors( *out, prefix ); + error( SYNTAX_ERROR, + tok_get_pos( &tok ), + BUILTIN_ELSEIF_ERR_COUNT, + L"else", + arg_count ); + + print_errors( *out, prefix ); } } } - - } - - if( !tok_has_next( &tok ) ) - break; - - } - if( needs_cmd ) - { - err=1; - if( out ) - { - error( SYNTAX_ERROR, - tok_get_pos( &tok ), - COND_ERR_MSG ); + } - print_errors( *out, prefix ); - } - } + if( !tok_has_next( &tok ) ) + break; + + } + + if( needs_cmd ) + { + err=1; + if( out ) + { + error( SYNTAX_ERROR, + tok_get_pos( &tok ), + COND_ERR_MSG ); + + print_errors( *out, prefix ); + } + } - if( out && count>0 ) - { - const wchar_t *cmd; - - error( SYNTAX_ERROR, - block_pos[count-1], - BLOCK_END_ERR_MSG ); + if( out && count>0 ) + { + const wchar_t *cmd; - print_errors( *out, prefix ); + error( SYNTAX_ERROR, + block_pos[count-1], + BLOCK_END_ERR_MSG ); - cmd = parser_get_block_command( block_type[count -1] ); - if( cmd ) - { - const wcstring h = builtin_help_get( *this, cmd ); - if( h.size() ) - { - append_format( *out, L"%ls", h.c_str() ); - } - } - - - } + print_errors( *out, prefix ); - /* - Fill in the unset block_level entries. Until now, only places - where the block level _changed_ have been filled out. This fills - in the rest. - */ + cmd = parser_get_block_command( block_type[count -1] ); + if( cmd ) + { + const wcstring h = builtin_help_get( *this, cmd ); + if( h.size() ) + { + append_format( *out, L"%ls", h.c_str() ); + } + } - if( block_level ) - { - int last_level = 0; - size_t i, len = wcslen(buff); - for( i=0; i= 0 ) - { - last_level = block_level[i]; - /* - Make all whitespace before a token have the new - level. This avoid using the wrong indentation level - if a new line starts with whitespace. - */ + + } + + /* + Fill in the unset block_level entries. Until now, only places + where the block level _changed_ have been filled out. This fills + in the rest. + */ + + if( block_level ) + { + int last_level = 0; + size_t i, len = wcslen(buff); + for( i=0; i= 0 ) + { + last_level = block_level[i]; + /* + Make all whitespace before a token have the new + level. This avoid using the wrong indentation level + if a new line starts with whitespace. + */ size_t prev_char_idx = i; while (prev_char_idx--) - { - if( !wcschr( L" \n\t\r", buff[prev_char_idx] ) ) - break; - block_level[prev_char_idx] = last_level; - } - } - block_level[i] = last_level; - } + { + if( !wcschr( L" \n\t\r", buff[prev_char_idx] ) ) + break; + block_level[prev_char_idx] = last_level; + } + } + block_level[i] = last_level; + } - /* - Make all trailing whitespace have the block level that the - validator had at exit. This makes sure a new line is - correctly indented even if it is empty. - */ + /* + Make all trailing whitespace have the block level that the + validator had at exit. This makes sure a new line is + correctly indented even if it is empty. + */ size_t suffix_idx = len; while (suffix_idx--) - { - if( !wcschr( L" \n\t\r", buff[suffix_idx] ) ) - break; - block_level[suffix_idx] = count; - } - } + { + if( !wcschr( L" \n\t\r", buff[suffix_idx] ) ) + break; + block_level[suffix_idx] = count; + } + } - /* - Calculate exit status - */ - if( count!= 0 ) - unfinished = 1; + /* + Calculate exit status + */ + if( count!= 0 ) + unfinished = 1; - if( err ) - res |= PARSER_TEST_ERROR; + if( err ) + res |= PARSER_TEST_ERROR; - if( unfinished ) - res |= PARSER_TEST_INCOMPLETE; + if( unfinished ) + res |= PARSER_TEST_INCOMPLETE; - /* - Cleanup - */ - - tok_destroy( &tok ); + /* + Cleanup + */ - current_tokenizer=previous_tokenizer; - current_tokenizer_pos = previous_pos; - - error_code=0; - + tok_destroy( &tok ); + + current_tokenizer=previous_tokenizer; + current_tokenizer_pos = previous_pos; + + error_code=0; + + + return res; - return res; - } block_t::block_t(block_type_t t) : diff --git a/parser.h b/parser.h index ebcfe1c3d..1c51323a2 100644 --- a/parser.h +++ b/parser.h @@ -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_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; /** 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&, 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_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 &output ); + /** + \param line Line to evaluate + \param output List to insert output to + */ + int eval_args( const wchar_t *line, std::vector &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. diff --git a/parser_keywords.cpp b/parser_keywords.cpp index efc2ff6e6..762bb6f2b 100644 --- a/parser_keywords.cpp +++ b/parser_keywords.cpp @@ -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" ); } diff --git a/parser_keywords.h b/parser_keywords.h index 5bad85aab..d5978c4a6 100644 --- a/parser_keywords.h +++ b/parser_keywords.h @@ -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 }; diff --git a/path.cpp b/path.cpp index 2ba524ef7..df7a95965 100644 --- a/path.cpp +++ b/path.cpp @@ -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; diff --git a/path.h b/path.h index 2d3cf83d3..aa49b266c 100644 --- a/path.h +++ b/path.h @@ -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. diff --git a/postfork.cpp b/postfork.cpp index 4379ca2dd..2609831f6 100644 --- a/postfork.cpp +++ b/postfork.cpp @@ -1,6 +1,6 @@ /** \file postfork.cpp - Functions that we may safely call after fork(). + Functions that we may safely call after fork(). */ #include @@ -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= 0) - { - return pid; - } - - if( errno != EAGAIN ) - { - break; - } - pollint.tv_sec = 0; - pollint.tv_nsec = FORK_SLEEP_TIME; + for( i=0; i= 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 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; - } - } + } + } } diff --git a/postfork.h b/postfork.h index 9acd4c86c..6b0857641 100644 --- a/postfork.h +++ b/postfork.h @@ -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 ); diff --git a/print_help.cpp b/print_help.cpp index 18a0f9653..3bb17835b 100644 --- a/print_help.cpp +++ b/print_help.cpp @@ -1,6 +1,6 @@ /** \file print_help.c - Print help message for the specified command + Print help message for the specified command */ #include @@ -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)); + } + + } + } diff --git a/print_help.h b/print_help.h index 1020ca759..69f6578c6 100644 --- a/print_help.h +++ b/print_help.h @@ -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 ); diff --git a/proc.cpp b/proc.cpp index deeb0ed8d..0ba13c6cd 100644 --- a/proc.cpp +++ b/proc.cpp @@ -7,7 +7,7 @@ will call proc to create representations of the running jobs as needed. Some of the code in this file is based on code from the Glibc manual. - + */ #include "config.h" @@ -75,7 +75,7 @@ Some of the code in this file is based on code from the Glibc manual. #include "output.h" /** - Size of message buffer + Size of message buffer */ #define MESS_SIZE 256 @@ -84,13 +84,13 @@ Some of the code in this file is based on code from the Glibc manual. */ #define BUFFER_SIZE 4096 -/** - Status of last process to exit +/** + Status of last process to exit */ static int last_status=0; /** - Signal flag + Signal flag */ static sig_atomic_t got_signal=0; @@ -156,13 +156,13 @@ static std::vector interactive_stack; void proc_init() { - proc_push_interactive( 0 ); + proc_push_interactive( 0 ); event.arguments.reset(new wcstring_list_t); } /** - Remove job from list of jobs + Remove job from list of jobs */ static int job_remove( job_t *j ) { @@ -183,7 +183,7 @@ void job_promote(job_t *job) */ void job_free( job_t * j ) { - job_remove( j ); + job_remove( j ); delete j; } @@ -191,22 +191,22 @@ void proc_destroy() { event.arguments.reset(NULL); job_list_t &jobs = parser_t::principal_parser().job_list(); - while( ! jobs.empty() ) - { - job_t *job = jobs.front(); - debug( 2, L"freeing leaked job %ls", job->command_wcstr() ); - job_free( job ); - } + while( ! jobs.empty() ) + { + job_t *job = jobs.front(); + debug( 2, L"freeing leaked job %ls", job->command_wcstr() ); + job_free( job ); + } } void proc_set_last_status( int s ) { - last_status = s; + last_status = s; } int proc_get_last_status() { - return last_status; + return last_status; } /* Basic thread safe job IDs. The vector consumed_job_ids has a true value wherever the job ID corresponding to that slot is in use. The job ID corresponding to slot 0 is 1. */ @@ -216,7 +216,7 @@ static std::vector consumed_job_ids; job_id_t acquire_job_id(void) { scoped_lock lock(job_id_lock); - + /* Find the index of the first 0 slot */ std::vector::iterator slot = std::find(consumed_job_ids.begin(), consumed_job_ids.end(), false); if (slot != consumed_job_ids.end()) @@ -238,11 +238,11 @@ void release_job_id(job_id_t jid) assert(jid > 0); scoped_lock lock(job_id_lock); size_t slot = (size_t)(jid - 1), count = consumed_job_ids.size(); - + /* Make sure this slot is within our vector and is currently set to consumed */ assert(slot < count); assert(consumed_job_ids.at(slot) == true); - + /* Clear it and then resize the vector to eliminate unused trailing job IDs */ consumed_job_ids.at(slot) = false; while (count--) { @@ -265,127 +265,127 @@ job_t *job_get_from_pid( int pid ) } -/* - Return true if all processes in the job have stopped or completed. +/* + Return true if all processes in the job have stopped or completed. \param j the job to test */ int job_is_stopped( const job_t *j ) { - process_t *p; + process_t *p; - for (p = j->first_process; p; p = p->next) - { - if (!p->completed && !p->stopped) - { - return 0; - } - } - return 1; + for (p = j->first_process; p; p = p->next) + { + if (!p->completed && !p->stopped) + { + return 0; + } + } + return 1; } -/* - Return true if the last processes in the job has completed. +/* + Return true if the last processes in the job has completed. \param j the job to test */ int job_is_completed( const job_t *j ) { - process_t *p; - assert(j->first_process != NULL); - for (p = j->first_process; p->next; p = p->next) - ; - - return p->completed; - + process_t *p; + assert(j->first_process != NULL); + for (p = j->first_process; p->next; p = p->next) + ; + + return p->completed; + } void job_set_flag( job_t *j, int flag, int set ) { - if( set ) - j->flags |= flag; - else - j->flags = j->flags & ((unsigned int)(-1) ^ flag); + if( set ) + j->flags |= flag; + else + j->flags = j->flags & ((unsigned int)(-1) ^ flag); } int job_get_flag( const job_t *j, int flag ) { - return j->flags&flag?1:0; + return j->flags&flag?1:0; } int job_signal( job_t *j, int signal ) -{ - pid_t my_pid = getpid(); - int res = 0; - - if( j->pgid != my_pid ) - { - res = killpg( j->pgid, SIGHUP ); - } - else - { - process_t *p; +{ + pid_t my_pid = getpid(); + int res = 0; - for( p = j->first_process; p; p=p->next ) - { - if( ! p->completed ) - { - if( p->pid ) - { - if( kill( p->pid, SIGHUP ) ) - { - res = -1; - break; - } - } - } - } + if( j->pgid != my_pid ) + { + res = killpg( j->pgid, SIGHUP ); + } + else + { + process_t *p; - } + for( p = j->first_process; p; p=p->next ) + { + if( ! p->completed ) + { + if( p->pid ) + { + if( kill( p->pid, SIGHUP ) ) + { + res = -1; + break; + } + } + } + } - return res; + } + + return res; } /** Store the status of the process pid that was returned by waitpid. - Return 0 if all went well, nonzero otherwise. + Return 0 if all went well, nonzero otherwise. */ static void mark_process_status( const job_t *j, - process_t *p, - int status ) + process_t *p, + int status ) { -// debug( 0, L"Process %ls %ls", p->argv[0], WIFSTOPPED (status)?L"stopped":(WIFEXITED( status )?L"exited":(WIFSIGNALED( status )?L"signaled to exit":L"BLARGH")) ); - p->status = status; - - if (WIFSTOPPED (status)) - { - p->stopped = 1; - } - else if (WIFSIGNALED(status) || WIFEXITED(status)) - { - p->completed = 1; - } - else - { - ssize_t ignore; - - /* This should never be reached */ - p->completed = 1; +// debug( 0, L"Process %ls %ls", p->argv[0], WIFSTOPPED (status)?L"stopped":(WIFEXITED( status )?L"exited":(WIFSIGNALED( status )?L"signaled to exit":L"BLARGH")) ); + p->status = status; - char mess[MESS_SIZE]; - snprintf( mess, - MESS_SIZE, - "Process %ld exited abnormally\n", - (long) p->pid ); - /* - If write fails, do nothing. We're in a signal handlers error - handler. If things aren't working properly, it's safer to - give up. - */ - ignore = write( 2, mess, strlen(mess) ); - } + if (WIFSTOPPED (status)) + { + p->stopped = 1; + } + else if (WIFSIGNALED(status) || WIFEXITED(status)) + { + p->completed = 1; + } + else + { + ssize_t ignore; + + /* This should never be reached */ + p->completed = 1; + + char mess[MESS_SIZE]; + snprintf( mess, + MESS_SIZE, + "Process %ld exited abnormally\n", + (long) p->pid ); + /* + If write fails, do nothing. We're in a signal handlers error + handler. If things aren't working properly, it's safer to + give up. + */ + ignore = write( 2, mess, strlen(mess) ); + } } void job_mark_process_as_failed( const job_t *job, process_t *p ) @@ -404,97 +404,97 @@ void job_mark_process_as_failed( const job_t *job, process_t *p ) */ static void handle_child_status( pid_t pid, int status ) { - bool found_proc = false; - const job_t *j=0; - process_t *p=0; -// char mess[MESS_SIZE]; - /* - snprintf( mess, - MESS_SIZE, - "Process %d\n", - (int) pid ); - write( 2, mess, strlen(mess )); - */ + bool found_proc = false; + const job_t *j=0; + process_t *p=0; +// char mess[MESS_SIZE]; + /* + snprintf( mess, + MESS_SIZE, + "Process %d\n", + (int) pid ); + write( 2, mess, strlen(mess )); + */ job_iterator_t jobs; - while (! found_proc && (j = jobs.next())) - { - process_t *prev=0; - for( p=j->first_process; p; p=p->next ) - { - if( pid == p->pid ) - { -/* snprintf( mess, + while (! found_proc && (j = jobs.next())) + { + process_t *prev=0; + for( p=j->first_process; p; p=p->next ) + { + if( pid == p->pid ) + { +/* snprintf( mess, MESS_SIZE, "Process %d is %ls from job %ls\n", (int) pid, p->actual_cmd, j->command ); write( 2, mess, strlen(mess )); -*/ - - mark_process_status ( j, p, status); - if( p->completed && prev != 0 ) - { - if( !prev->completed && prev->pid) - { - /* snprintf( mess, - MESS_SIZE, - "Kill previously uncompleted process %ls (%d)\n", - prev->actual_cmd, - prev->pid ); - write( 2, mess, strlen(mess )); - */ - kill(prev->pid,SIGPIPE); - } - } - found_proc = true; - break; - } - prev = p; - } - } +*/ + + mark_process_status ( j, p, status); + if( p->completed && prev != 0 ) + { + if( !prev->completed && prev->pid) + { + /* snprintf( mess, + MESS_SIZE, + "Kill previously uncompleted process %ls (%d)\n", + prev->actual_cmd, + prev->pid ); + write( 2, mess, strlen(mess )); + */ + kill(prev->pid,SIGPIPE); + } + } + found_proc = true; + break; + } + prev = p; + } + } - if( WIFSIGNALED( status ) && - ( WTERMSIG(status)==SIGINT || - WTERMSIG(status)==SIGQUIT ) ) - { - if( !is_interactive_session ) - { - struct sigaction act; - sigemptyset( & act.sa_mask ); - act.sa_flags=0; - act.sa_handler=SIG_DFL; - sigaction( SIGINT, &act, 0 ); - sigaction( SIGQUIT, &act, 0 ); - kill( getpid(), WTERMSIG(status) ); - } - else - { + if( WIFSIGNALED( status ) && + ( WTERMSIG(status)==SIGINT || + WTERMSIG(status)==SIGQUIT ) ) + { + if( !is_interactive_session ) + { + struct sigaction act; + sigemptyset( & act.sa_mask ); + act.sa_flags=0; + act.sa_handler=SIG_DFL; + sigaction( SIGINT, &act, 0 ); + sigaction( SIGQUIT, &act, 0 ); + kill( getpid(), WTERMSIG(status) ); + } + else + { /* In an interactive session, tell the principal parser to skip all blocks we're executing so control-C returns control to the user. */ - if( p && found_proc ) - { + if( p && found_proc ) + { parser_t::skip_all_blocks(); - } - } - } - - if( !found_proc ) - { - /* - A child we lost track of? - - There have been bugs in both subshell handling and in - builtin handling that have caused this previously... - */ -/* snprintf( mess, + } + } + } + + if( !found_proc ) + { + /* + A child we lost track of? + + There have been bugs in both subshell handling and in + builtin handling that have caused this previously... + */ +/* snprintf( mess, MESS_SIZE, "Process %d not found by %d\n", (int) pid, (int)getpid() ); write( 2, mess, strlen(mess )); */ - } - return; + } + return; } process_t::process_t() : @@ -516,7 +516,7 @@ process_t::process_t() : #endif { } - + process_t::~process_t() { if (this->next != NULL) @@ -534,7 +534,7 @@ job_t::job_t(job_id_t jobid) : flags(0) { } - + job_t::~job_t() { if (first_process != NULL) @@ -546,191 +546,191 @@ job_t::~job_t() /* This is called from a signal handler */ void job_handle_signal ( int signal, siginfo_t *info, void *con ) { - - int status; - pid_t pid; - int errno_old = errno; - got_signal = 1; + int status; + pid_t pid; + int errno_old = errno; -// write( 2, "got signal\n", 11 ); + got_signal = 1; - while(1) - { - switch(pid=waitpid( -1,&status,WUNTRACED|WNOHANG )) - { - case 0: - case -1: - { - errno=errno_old; - return; - } - default: +// write( 2, "got signal\n", 11 ); - handle_child_status( pid, status ); - break; - } - } - kill( 0, SIGIO ); - errno=errno_old; + while(1) + { + switch(pid=waitpid( -1,&status,WUNTRACED|WNOHANG )) + { + case 0: + case -1: + { + errno=errno_old; + return; + } + default: + + handle_child_status( pid, status ); + break; + } + } + kill( 0, SIGIO ); + errno=errno_old; } -/** - Format information about job status for the user to look at. +/** + Format information about job status for the user to look at. - \param j the job to test - \param status a string description of the job exit type + \param j the job to test + \param status a string description of the job exit type */ static void format_job_info( const job_t *j, const wchar_t *status ) { - fwprintf (stdout, L"\r" ); - fwprintf (stdout, _( L"Job %d, \'%ls\' has %ls" ), j->job_id, j->command_wcstr(), status); - fflush( stdout ); - tputs(clr_eol,1,&writeb); - fwprintf (stdout, L"\n" ); + fwprintf (stdout, L"\r" ); + fwprintf (stdout, _( L"Job %d, \'%ls\' has %ls" ), j->job_id, j->command_wcstr(), status); + fflush( stdout ); + tputs(clr_eol,1,&writeb); + fwprintf (stdout, L"\n" ); } void proc_fire_event( const wchar_t *msg, int type, pid_t pid, int status ) { - - event.type=type; - event.param1.pid = pid; - + + event.type=type; + event.param1.pid = pid; + event.arguments->push_back(msg); event.arguments->push_back(to_string(pid)); event.arguments->push_back(to_string(status)); - event_fire( &event ); + event_fire( &event ); event.arguments->resize(0); -} +} int job_reap( bool interactive ) { ASSERT_IS_MAIN_THREAD(); - job_t *jnext; - int found=0; - - static int locked = 0; - - locked++; - - /* - job_read may fire an event handler, we do not want to call - ourselves recursively (to avoid infinite recursion). - */ - if( locked>1 ) - return 0; - + job_t *jnext; + int found=0; + + static int locked = 0; + + locked++; + + /* + job_read may fire an event handler, we do not want to call + ourselves recursively (to avoid infinite recursion). + */ + if( locked>1 ) + return 0; + job_iterator_t jobs; jnext = jobs.next(); - while (jnext) + while (jnext) { job_t *j = jnext; jnext = jobs.next(); - process_t *p; - - /* - If we are reaping only jobs who do not need status messages - sent to the console, do not consider reaping jobs that need - status messages - */ - if( (!job_get_flag( j, JOB_SKIP_NOTIFICATION ) ) && (!interactive) && (!job_get_flag( j, JOB_FOREGROUND ))) - { - continue; - } - - for( p=j->first_process; p; p=p->next ) - { - int s; - if( !p->completed ) - continue; - - if( !p->pid ) - continue; - - s = p->status; - - proc_fire_event( L"PROCESS_EXIT", EVENT_EXIT, p->pid, ( WIFSIGNALED(s)?-1:WEXITSTATUS( s )) ); - - if( WIFSIGNALED(s) ) - { - /* - Ignore signal SIGPIPE.We issue it ourselves to the pipe - writer when the pipe reader dies. - */ - if( WTERMSIG(s) != SIGPIPE ) - { - int proc_is_job = ((p==j->first_process) && (p->next == 0)); - if( proc_is_job ) - job_set_flag( j, JOB_NOTIFIED, 1 ); - if( !job_get_flag( j, JOB_SKIP_NOTIFICATION ) ) - { - if( proc_is_job ) - fwprintf( stdout, - _( L"%ls: Job %d, \'%ls\' terminated by signal %ls (%ls)" ), - program_name, - j->job_id, - j->command_wcstr(), - sig2wcs(WTERMSIG(p->status)), - signal_get_desc( WTERMSIG(p->status) ) ); - else - fwprintf( stdout, - _( L"%ls: Process %d, \'%ls\' from job %d, \'%ls\' terminated by signal %ls (%ls)" ), - program_name, - p->pid, - p->argv0(), - j->job_id, - j->command_wcstr(), - sig2wcs(WTERMSIG(p->status)), - signal_get_desc( WTERMSIG(p->status) ) ); - tputs(clr_eol,1,&writeb); - fwprintf (stdout, L"\n" ); - found=1; - } - - /* - Clear status so it is not reported more than once - */ - 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_get_flag( j, JOB_FOREGROUND) && !job_get_flag( j, JOB_NOTIFIED ) && !job_get_flag( j, JOB_SKIP_NOTIFICATION ) ) - { - format_job_info( j, _( L"ended" ) ); - found=1; - } - proc_fire_event( L"JOB_EXIT", EVENT_EXIT, -j->pgid, 0 ); - proc_fire_event( L"JOB_EXIT", EVENT_JOB_ID, j->job_id, 0 ); + process_t *p; - job_free(j); - } - else if( job_is_stopped( j ) && !job_get_flag( j, JOB_NOTIFIED ) ) - { - /* - Notify the user about newly stopped jobs. - */ - if( !job_get_flag( j, JOB_SKIP_NOTIFICATION ) ) - { - format_job_info( j, _( L"stopped" ) ); - found=1; - } - job_set_flag( j, JOB_NOTIFIED, 1 ); - } - } + /* + If we are reaping only jobs who do not need status messages + sent to the console, do not consider reaping jobs that need + status messages + */ + if( (!job_get_flag( j, JOB_SKIP_NOTIFICATION ) ) && (!interactive) && (!job_get_flag( j, JOB_FOREGROUND ))) + { + continue; + } - if( found ) - fflush( stdout ); + for( p=j->first_process; p; p=p->next ) + { + int s; + if( !p->completed ) + continue; - locked = 0; - - return found; + if( !p->pid ) + continue; + + s = p->status; + + proc_fire_event( L"PROCESS_EXIT", EVENT_EXIT, p->pid, ( WIFSIGNALED(s)?-1:WEXITSTATUS( s )) ); + + if( WIFSIGNALED(s) ) + { + /* + Ignore signal SIGPIPE.We issue it ourselves to the pipe + writer when the pipe reader dies. + */ + if( WTERMSIG(s) != SIGPIPE ) + { + int proc_is_job = ((p==j->first_process) && (p->next == 0)); + if( proc_is_job ) + job_set_flag( j, JOB_NOTIFIED, 1 ); + if( !job_get_flag( j, JOB_SKIP_NOTIFICATION ) ) + { + if( proc_is_job ) + fwprintf( stdout, + _( L"%ls: Job %d, \'%ls\' terminated by signal %ls (%ls)" ), + program_name, + j->job_id, + j->command_wcstr(), + sig2wcs(WTERMSIG(p->status)), + signal_get_desc( WTERMSIG(p->status) ) ); + else + fwprintf( stdout, + _( L"%ls: Process %d, \'%ls\' from job %d, \'%ls\' terminated by signal %ls (%ls)" ), + program_name, + p->pid, + p->argv0(), + j->job_id, + j->command_wcstr(), + sig2wcs(WTERMSIG(p->status)), + signal_get_desc( WTERMSIG(p->status) ) ); + tputs(clr_eol,1,&writeb); + fwprintf (stdout, L"\n" ); + found=1; + } + + /* + Clear status so it is not reported more than once + */ + 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_get_flag( j, JOB_FOREGROUND) && !job_get_flag( j, JOB_NOTIFIED ) && !job_get_flag( j, JOB_SKIP_NOTIFICATION ) ) + { + format_job_info( j, _( L"ended" ) ); + found=1; + } + proc_fire_event( L"JOB_EXIT", EVENT_EXIT, -j->pgid, 0 ); + proc_fire_event( L"JOB_EXIT", EVENT_JOB_ID, j->job_id, 0 ); + + job_free(j); + } + else if( job_is_stopped( j ) && !job_get_flag( j, JOB_NOTIFIED ) ) + { + /* + Notify the user about newly stopped jobs. + */ + if( !job_get_flag( j, JOB_SKIP_NOTIFICATION ) ) + { + format_job_info( j, _( L"stopped" ) ); + found=1; + } + job_set_flag( j, JOB_NOTIFIED, 1 ); + } + } + + if( found ) + fflush( stdout ); + + locked = 0; + + return found; } @@ -746,83 +746,83 @@ int job_reap( bool interactive ) */ unsigned long proc_get_jiffies( process_t *p ) { - wchar_t fn[FN_SIZE]; + wchar_t fn[FN_SIZE]; - char state; - int pid, ppid, pgrp, - session, tty_nr, tpgid, - exit_signal, processor; - - long int cutime, cstime, priority, - nice, placeholder, itrealvalue, - rss; - unsigned long int flags, minflt, cminflt, - majflt, cmajflt, utime, - stime, starttime, vsize, - rlim, startcode, endcode, - startstack, kstkesp, kstkeip, - signal, blocked, sigignore, - sigcatch, wchan, nswap, cnswap; - char comm[1024]; - - if( p->pid <= 0 ) - return 0; - - swprintf( fn, FN_SIZE, L"/proc/%d/stat", p->pid ); - - FILE *f = wfopen( fn, "r" ); - if( !f ) - return 0; - - int count = fscanf( f, - "%d %s %c " - "%d %d %d " - "%d %d %lu " - - "%lu %lu %lu " - "%lu %lu %lu " - "%ld %ld %ld " + char state; + int pid, ppid, pgrp, + session, tty_nr, tpgid, + exit_signal, processor; - "%ld %ld %ld " - "%lu %lu %ld " - "%lu %lu %lu " + long int cutime, cstime, priority, + nice, placeholder, itrealvalue, + rss; + unsigned long int flags, minflt, cminflt, + majflt, cmajflt, utime, + stime, starttime, vsize, + rlim, startcode, endcode, + startstack, kstkesp, kstkeip, + signal, blocked, sigignore, + sigcatch, wchan, nswap, cnswap; + char comm[1024]; - "%lu %lu %lu " - "%lu %lu %lu " - "%lu %lu %lu " + if( p->pid <= 0 ) + return 0; - "%lu %d %d ", - - &pid, comm, &state, - &ppid, &pgrp, &session, - &tty_nr, &tpgid, &flags, + swprintf( fn, FN_SIZE, L"/proc/%d/stat", p->pid ); - &minflt, &cminflt, &majflt, - &cmajflt, &utime, &stime, - &cutime, &cstime, &priority, - - &nice, &placeholder, &itrealvalue, - &starttime, &vsize, &rss, - &rlim, &startcode, &endcode, + FILE *f = wfopen( fn, "r" ); + if( !f ) + return 0; - &startstack, &kstkesp, &kstkeip, - &signal, &blocked, &sigignore, - &sigcatch, &wchan, &nswap, + int count = fscanf( f, + "%d %s %c " + "%d %d %d " + "%d %d %lu " - &cnswap, &exit_signal, &processor - ); + "%lu %lu %lu " + "%lu %lu %lu " + "%ld %ld %ld " - if( count < 17 ) - { - return 0; - } + "%ld %ld %ld " + "%lu %lu %ld " + "%lu %lu %lu " + + "%lu %lu %lu " + "%lu %lu %lu " + "%lu %lu %lu " + + "%lu %d %d ", + + &pid, comm, &state, + &ppid, &pgrp, &session, + &tty_nr, &tpgid, &flags, + + &minflt, &cminflt, &majflt, + &cmajflt, &utime, &stime, + &cutime, &cstime, &priority, + + &nice, &placeholder, &itrealvalue, + &starttime, &vsize, &rss, + &rlim, &startcode, &endcode, + + &startstack, &kstkesp, &kstkeip, + &signal, &blocked, &sigignore, + &sigcatch, &wchan, &nswap, + + &cnswap, &exit_signal, &processor + ); + + if( count < 17 ) + { + return 0; + } + + /* + Don't need to check exit status of fclose on read-only streams + */ + fclose( f ); + return utime+stime+cutime+cstime; - /* - Don't need to check exit status of fclose on read-only streams - */ - fclose( f ); - return utime+stime+cutime+cstime; - } /** @@ -830,18 +830,18 @@ unsigned long proc_get_jiffies( process_t *p ) */ void proc_update_jiffies() { - job_t* job; - process_t *p; - job_iterator_t j; + job_t* job; + process_t *p; + job_iterator_t j; - for( job = j.next(); job; job = j.next() ) - { - for( p=job->first_process; p; p=p->next ) - { - gettimeofday( &p->last_time, 0 ); - p->last_jiffies = proc_get_jiffies( p ); - } - } + for( job = j.next(); job; job = j.next() ) + { + for( p=job->first_process; p; p=p->next ) + { + gettimeofday( &p->last_time, 0 ); + p->last_jiffies = proc_get_jiffies( p ); + } + } } @@ -849,103 +849,103 @@ void proc_update_jiffies() /** Check if there are buffers associated with the job, and select on - them for a while if available. - + them for a while if available. + \param j the job to test \return 1 if buffers were avaialble, zero otherwise */ static int select_try( job_t *j ) { - fd_set fds; - int maxfd=-1; + fd_set fds; + int maxfd=-1; + + FD_ZERO(&fds); - FD_ZERO(&fds); - for (size_t idx = 0; idx < j->io.size(); idx++) - { + { const io_data_t *d = j->io.at(idx); - if( d->io_mode == IO_BUFFER ) - { - int fd = d->param1.pipe_fd[0]; -// fwprintf( stderr, L"fd %d on job %ls\n", fd, j->command ); - FD_SET( fd, &fds ); - maxfd = maxi(maxfd, fd); - debug( 3, L"select_try on %d\n", fd ); - } - } - - if( maxfd >= 0 ) - { - int retval; - struct timeval tv; - - tv.tv_sec=0; - tv.tv_usec=10000; - - retval =select( maxfd+1, &fds, 0, 0, &tv ); - return retval > 0; - } + if( d->io_mode == IO_BUFFER ) + { + int fd = d->param1.pipe_fd[0]; +// fwprintf( stderr, L"fd %d on job %ls\n", fd, j->command ); + FD_SET( fd, &fds ); + maxfd = maxi(maxfd, fd); + debug( 3, L"select_try on %d\n", fd ); + } + } - return -1; + if( maxfd >= 0 ) + { + int retval; + struct timeval tv; + + tv.tv_sec=0; + tv.tv_usec=10000; + + retval =select( maxfd+1, &fds, 0, 0, &tv ); + return retval > 0; + } + + return -1; } /** - Read from descriptors until they are empty. + Read from descriptors until they are empty. \param j the job to test */ static void read_try( job_t *j ) { - io_data_t *buff=NULL; + io_data_t *buff=NULL; - /* - Find the last buffer, which is the one we want to read from - */ + /* + Find the last buffer, which is the one we want to read from + */ for (size_t idx = 0; idx < j->io.size(); idx++) - { + { io_data_t *d = j->io.at(idx); - if( d->io_mode == IO_BUFFER ) - { - buff=d; - } - } - - if( buff ) - { - debug( 3, L"proc::read_try('%ls')\n", j->command_wcstr() ); - while(1) - { - char b[BUFFER_SIZE]; - long l; - - l=read_blocked( buff->param1.pipe_fd[0], - b, BUFFER_SIZE ); - if( l==0 ) - { - break; - } - else if( l<0 ) - { - if( errno != EAGAIN ) - { - debug( 1, - _( L"An error occured while reading output from code block" ) ); - wperror( L"read_try" ); - } - break; - } - else - { + if( d->io_mode == IO_BUFFER ) + { + buff=d; + } + } + + if( buff ) + { + debug( 3, L"proc::read_try('%ls')\n", j->command_wcstr() ); + while(1) + { + char b[BUFFER_SIZE]; + long l; + + l=read_blocked( buff->param1.pipe_fd[0], + b, BUFFER_SIZE ); + if( l==0 ) + { + break; + } + else if( l<0 ) + { + if( errno != EAGAIN ) + { + debug( 1, + _( L"An error occured while reading output from code block" ) ); + wperror( L"read_try" ); + } + break; + } + else + { buff->out_buffer_append(b, l); - } - } - } + } + } + } } /** - Give ownership of the terminal to the specified job. + Give ownership of the terminal to the specified job. \param j The job to give the terminal to. @@ -955,363 +955,363 @@ static void read_try( job_t *j ) */ static int terminal_give_to_job( job_t *j, int cont ) { - - if( tcsetpgrp (0, j->pgid) ) - { - debug( 1, - _( L"Could not send job %d ('%ls') to foreground" ), - j->job_id, - j->command_wcstr() ); - wperror( L"tcsetpgrp" ); - return 0; - } - - if( cont ) - { - if( tcsetattr (0, TCSADRAIN, &j->tmodes)) - { - debug( 1, - _( L"Could not send job %d ('%ls') to foreground" ), - j->job_id, - j->command_wcstr() ); - wperror( L"tcsetattr" ); - return 0; - } - } - return 1; + + if( tcsetpgrp (0, j->pgid) ) + { + debug( 1, + _( L"Could not send job %d ('%ls') to foreground" ), + j->job_id, + j->command_wcstr() ); + wperror( L"tcsetpgrp" ); + return 0; + } + + if( cont ) + { + if( tcsetattr (0, TCSADRAIN, &j->tmodes)) + { + debug( 1, + _( L"Could not send job %d ('%ls') to foreground" ), + j->job_id, + j->command_wcstr() ); + wperror( L"tcsetattr" ); + return 0; + } + } + return 1; } /** Returns contol of the terminal to the shell, and saves the terminal attribute state to the job, so that we can restore the terminal - ownership to the job at a later time . + ownership to the job at a later time . */ static int terminal_return_from_job( job_t *j) { - - if( tcsetpgrp (0, getpgrp()) ) - { - debug( 1, _( L"Could not return shell to foreground" ) ); - wperror( L"tcsetpgrp" ); - return 0; - } - - /* - Save jobs terminal modes. - */ - if( tcgetattr (0, &j->tmodes) ) - { - debug( 1, _( L"Could not return shell to foreground" ) ); - wperror( L"tcgetattr" ); - return 0; - } - + + if( tcsetpgrp (0, getpgrp()) ) + { + debug( 1, _( L"Could not return shell to foreground" ) ); + wperror( L"tcsetpgrp" ); + return 0; + } + + /* + Save jobs terminal modes. + */ + if( tcgetattr (0, &j->tmodes) ) + { + debug( 1, _( L"Could not return shell to foreground" ) ); + wperror( L"tcgetattr" ); + return 0; + } + /* Disabling this per https://github.com/adityagodbole/fish-shell/commit/9d229cd18c3e5c25a8bd37e9ddd3b67ddc2d1b72 On Linux, 'cd . ; ftp' prevents you from typing into the ftp prompt See https://github.com/fish-shell/fish-shell/issues/121 */ #if 0 - /* - Restore the shell's terminal modes. - */ - if( tcsetattr (0, TCSADRAIN, &shell_modes)) - { - debug( 1, _( L"Could not return shell to foreground" ) ); - wperror( L"tcsetattr" ); - return 0; - } + /* + Restore the shell's terminal modes. + */ + if( tcsetattr (0, TCSADRAIN, &shell_modes)) + { + debug( 1, _( L"Could not return shell to foreground" ) ); + wperror( L"tcsetattr" ); + return 0; + } #endif - return 1; + return 1; } void job_continue (job_t *j, int cont) { - /* - Put job first in the job list - */ + /* + Put job first in the job list + */ job_promote(j); - job_set_flag( j, JOB_NOTIFIED, 0 ); + job_set_flag( j, JOB_NOTIFIED, 0 ); - CHECK_BLOCK(); - - debug( 4, - L"Continue job %d, gid %d (%ls), %ls, %ls", - j->job_id, - j->pgid, - j->command_wcstr(), - job_is_completed( j )?L"COMPLETED":L"UNCOMPLETED", - is_interactive?L"INTERACTIVE":L"NON-INTERACTIVE" ); - - if( !job_is_completed( j ) ) - { - if( job_get_flag( j, JOB_TERMINAL ) && job_get_flag( j, JOB_FOREGROUND ) ) - { - /* Put the job into the foreground. */ - int ok; - - signal_block(); - - ok = terminal_give_to_job( j, cont ); - - signal_unblock(); + CHECK_BLOCK(); - if( !ok ) - return; - - } - - /* - Send the job a continue signal, if necessary. - */ - if( cont ) - { - process_t *p; + debug( 4, + L"Continue job %d, gid %d (%ls), %ls, %ls", + j->job_id, + j->pgid, + j->command_wcstr(), + job_is_completed( j )?L"COMPLETED":L"UNCOMPLETED", + is_interactive?L"INTERACTIVE":L"NON-INTERACTIVE" ); - for( p=j->first_process; p; p=p->next ) - p->stopped=0; + if( !job_is_completed( j ) ) + { + if( job_get_flag( j, JOB_TERMINAL ) && job_get_flag( j, JOB_FOREGROUND ) ) + { + /* Put the job into the foreground. */ + int ok; - if( job_get_flag( j, JOB_CONTROL ) ) - { - if( killpg( j->pgid, SIGCONT ) ) - { - wperror( L"killpg (SIGCONT)" ); - return; - } - } - else - { - for( p=j->first_process; p; p=p->next ) - { - if (kill ( p->pid, SIGCONT) < 0) - { - wperror (L"kill (SIGCONT)"); - return; - } - } - } - } - - if( job_get_flag( j, JOB_FOREGROUND ) ) - { - int quit = 0; - - /* - Wait for job to report. Looks a bit ugly because it has to - handle the possibility that a signal is dispatched while - running job_is_stopped(). - */ - while( !quit ) - { - do - { - got_signal = 0; - quit = job_is_stopped( j ) || job_is_completed( j ); - } - while (got_signal && !quit); - - if( !quit ) - { - -// debug( 1, L"select_try()" ); - switch( select_try(j) ) - { - case 1: - { - read_try( j ); - break; - } - - case -1: - { - /* - If there is no funky IO magic, we can use - waitpid instead of handling child deaths - through signals. This gives a rather large - speed boost (A factor 3 startup time - improvement on my 300 MHz machine) on - short-lived jobs. - */ - int status; - pid_t pid = waitpid(-1, &status, WUNTRACED ); - if( pid > 0 ) - { - handle_child_status( pid, status ); - } - else - { - /* - This probably means we got a - signal. A signal might mean that the - terminal emulator sent us a hup - signal to tell is to close. If so, - we should exit. - */ - if( reader_exit_forced() ) - { - quit = 1; - } - - } - break; - } - - } - } - } - } - } - - if( job_get_flag( j, JOB_FOREGROUND ) ) - { - - if( job_is_completed( j )) - { - - // It's possible that the job will produce output and exit before we've even read from it. - // We'll eventually read the output, but it may be after we've executed subsequent calls - // This is why my prompt colors kept getting screwed up - the builtin echo calls - // were sometimes having their output combined with the set_color calls in the wrong order! - read_try(j); + signal_block(); - - process_t *p = j->first_process; - while( p->next ) - p = p->next; + ok = terminal_give_to_job( j, cont ); - if( WIFEXITED( p->status ) || WIFSIGNALED(p->status)) - { - /* - Mark process status only if we are in the foreground - and the last process in a pipe, and it is not a short circuted builtin - */ - if( p->pid ) - { - int status = proc_format_status(p->status); - //wprintf(L"setting status %d for %ls\n", job_get_flag( j, JOB_NEGATE )?!status:status, j->command); - proc_set_last_status( job_get_flag( j, JOB_NEGATE )?!status:status); - } - } - } - /* - Put the shell back in the foreground. - */ - if( job_get_flag( j, JOB_TERMINAL ) && job_get_flag( j, JOB_FOREGROUND ) ) - { - int ok; - - signal_block(); + signal_unblock(); + + if( !ok ) + return; + + } + + /* + Send the job a continue signal, if necessary. + */ + if( cont ) + { + process_t *p; + + for( p=j->first_process; p; p=p->next ) + p->stopped=0; + + if( job_get_flag( j, JOB_CONTROL ) ) + { + if( killpg( j->pgid, SIGCONT ) ) + { + wperror( L"killpg (SIGCONT)" ); + return; + } + } + else + { + for( p=j->first_process; p; p=p->next ) + { + if (kill ( p->pid, SIGCONT) < 0) + { + wperror (L"kill (SIGCONT)"); + return; + } + } + } + } + + if( job_get_flag( j, JOB_FOREGROUND ) ) + { + int quit = 0; + + /* + Wait for job to report. Looks a bit ugly because it has to + handle the possibility that a signal is dispatched while + running job_is_stopped(). + */ + while( !quit ) + { + do + { + got_signal = 0; + quit = job_is_stopped( j ) || job_is_completed( j ); + } + while (got_signal && !quit); + + if( !quit ) + { + +// debug( 1, L"select_try()" ); + switch( select_try(j) ) + { + case 1: + { + read_try( j ); + break; + } + + case -1: + { + /* + If there is no funky IO magic, we can use + waitpid instead of handling child deaths + through signals. This gives a rather large + speed boost (A factor 3 startup time + improvement on my 300 MHz machine) on + short-lived jobs. + */ + int status; + pid_t pid = waitpid(-1, &status, WUNTRACED ); + if( pid > 0 ) + { + handle_child_status( pid, status ); + } + else + { + /* + This probably means we got a + signal. A signal might mean that the + terminal emulator sent us a hup + signal to tell is to close. If so, + we should exit. + */ + if( reader_exit_forced() ) + { + quit = 1; + } + + } + break; + } + + } + } + } + } + } + + if( job_get_flag( j, JOB_FOREGROUND ) ) + { + + if( job_is_completed( j )) + { + + // It's possible that the job will produce output and exit before we've even read from it. + // We'll eventually read the output, but it may be after we've executed subsequent calls + // This is why my prompt colors kept getting screwed up - the builtin echo calls + // were sometimes having their output combined with the set_color calls in the wrong order! + read_try(j); + + + process_t *p = j->first_process; + while( p->next ) + p = p->next; + + if( WIFEXITED( p->status ) || WIFSIGNALED(p->status)) + { + /* + Mark process status only if we are in the foreground + and the last process in a pipe, and it is not a short circuted builtin + */ + if( p->pid ) + { + int status = proc_format_status(p->status); + //wprintf(L"setting status %d for %ls\n", job_get_flag( j, JOB_NEGATE )?!status:status, j->command); + proc_set_last_status( job_get_flag( j, JOB_NEGATE )?!status:status); + } + } + } + /* + Put the shell back in the foreground. + */ + if( job_get_flag( j, JOB_TERMINAL ) && job_get_flag( j, JOB_FOREGROUND ) ) + { + int ok; + + signal_block(); + + ok = terminal_return_from_job( j ); + + signal_unblock(); + + if( !ok ) + return; + + } + } - ok = terminal_return_from_job( j ); - - signal_unblock(); - - if( !ok ) - return; - - } - } - } -int proc_format_status(int status) +int proc_format_status(int status) { - if( WIFSIGNALED( status ) ) - { - return 128+WTERMSIG(status); - } - else if( WIFEXITED( status ) ) - { - return WEXITSTATUS(status); - } - return status; - + if( WIFSIGNALED( status ) ) + { + return 128+WTERMSIG(status); + } + else if( WIFEXITED( status ) ) + { + return WEXITSTATUS(status); + } + return status; + } void proc_sanity_check() { - job_t *j; - job_t *fg_job=0; - + job_t *j; + job_t *fg_job=0; + job_iterator_t jobs; while ((j = jobs.next())) - { - process_t *p; + { + process_t *p; - if( !job_get_flag( j, JOB_CONSTRUCTED ) ) - continue; - - - validate_pointer( j->first_process, - _( L"Process list pointer" ), - 0 ); + if( !job_get_flag( j, JOB_CONSTRUCTED ) ) + continue; - /* - More than one foreground job? - */ - if( job_get_flag( j, JOB_FOREGROUND ) && !(job_is_stopped(j) || job_is_completed(j) ) ) - { - if( fg_job != 0 ) - { - debug( 0, - _( L"More than one job in foreground: job 1: '%ls' job 2: '%ls'"), - fg_job->command_wcstr(), - j->command_wcstr() ); - sanity_lose(); - } - fg_job = j; - } - - p = j->first_process; - while( p ) - { - validate_pointer( p->get_argv(), _( L"Process argument list" ), 0 ); - validate_pointer( p->argv0(), _( L"Process name" ), 0 ); - validate_pointer( p->next, _( L"Process list pointer" ), 1 ); - - if ( (p->stopped & (~0x00000001)) != 0 ) - { - debug( 0, - _( L"Job '%ls', process '%ls' has inconsistent state \'stopped\'=%d" ), - j->command_wcstr(), - p->argv0(), - p->stopped ); - sanity_lose(); - } - - if ( (p->completed & (~0x00000001)) != 0 ) - { - debug( 0, - _( L"Job '%ls', process '%ls' has inconsistent state \'completed\'=%d" ), - j->command_wcstr(), - p->argv0(), - p->completed ); - sanity_lose(); - } - - p=p->next; - } - - } + + validate_pointer( j->first_process, + _( L"Process list pointer" ), + 0 ); + + /* + More than one foreground job? + */ + if( job_get_flag( j, JOB_FOREGROUND ) && !(job_is_stopped(j) || job_is_completed(j) ) ) + { + if( fg_job != 0 ) + { + debug( 0, + _( L"More than one job in foreground: job 1: '%ls' job 2: '%ls'"), + fg_job->command_wcstr(), + j->command_wcstr() ); + sanity_lose(); + } + fg_job = j; + } + + p = j->first_process; + while( p ) + { + validate_pointer( p->get_argv(), _( L"Process argument list" ), 0 ); + validate_pointer( p->argv0(), _( L"Process name" ), 0 ); + validate_pointer( p->next, _( L"Process list pointer" ), 1 ); + + if ( (p->stopped & (~0x00000001)) != 0 ) + { + debug( 0, + _( L"Job '%ls', process '%ls' has inconsistent state \'stopped\'=%d" ), + j->command_wcstr(), + p->argv0(), + p->stopped ); + sanity_lose(); + } + + if ( (p->completed & (~0x00000001)) != 0 ) + { + debug( 0, + _( L"Job '%ls', process '%ls' has inconsistent state \'completed\'=%d" ), + j->command_wcstr(), + p->argv0(), + p->completed ); + sanity_lose(); + } + + p=p->next; + } + + } } void proc_push_interactive( int value ) { ASSERT_IS_MAIN_THREAD(); - int old = is_interactive; + int old = is_interactive; interactive_stack.push_back(is_interactive); - is_interactive = value; - if( old != value ) - signal_set_handlers(); + is_interactive = value; + if( old != value ) + signal_set_handlers(); } void proc_pop_interactive() { ASSERT_IS_MAIN_THREAD(); - int old = is_interactive; - is_interactive= interactive_stack.back(); + int old = is_interactive; + is_interactive= interactive_stack.back(); interactive_stack.pop_back(); - if( is_interactive != old ) - signal_set_handlers(); + if( is_interactive != old ) + signal_set_handlers(); } diff --git a/proc.h b/proc.h index 6feb9afe7..2038f9699 100644 --- a/proc.h +++ b/proc.h @@ -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 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 &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 ); diff --git a/reader.cpp b/reader.cpp index 4102aa289..3c19557b8 100644 --- a/reader.cpp +++ b/reader.cpp @@ -194,129 +194,129 @@ typedef int color_t; class reader_data_t { public: - - /** String containing the whole current commandline */ - wcstring command_line; - + + /** String containing the whole current commandline */ + wcstring command_line; + /** String containing the autosuggestion */ wcstring autosuggestion; /** When backspacing, we suppress autosuggestions */ bool suppress_autosuggestion; - /** The representation of the current screen contents */ - screen_t screen; - + /** The representation of the current screen contents */ + screen_t screen; + /** The history */ history_t *history; - /** - String containing the current search item - */ - wcstring search_buff; + /** + String containing the current search item + */ + wcstring search_buff; /* History search */ history_search_t history_search; - /** - Saved position used by token history search - */ - int token_history_pos; + /** + Saved position used by token history search + */ + int token_history_pos; - /** - Saved search string for token history search. Not handled by command_line_changed. - */ - wcstring token_history_buff; + /** + Saved search string for token history search. Not handled by command_line_changed. + */ + wcstring token_history_buff; - /** - List for storing previous search results. Used to avoid duplicates. - */ - wcstring_list_t search_prev; + /** + List for storing previous search results. Used to avoid duplicates. + */ + wcstring_list_t search_prev; + + /** The current position in search_prev */ + size_t search_pos; - /** The current position in search_prev */ - size_t search_pos; - /** Length of the command */ size_t command_length() const { return command_line.size(); } - + /** Do what we need to do whenever our command line changes */ void command_line_changed(void); - /** The current position of the cursor in buff. */ - size_t buff_pos; + /** The current position of the cursor in buff. */ + size_t buff_pos; - /** Name of the current application */ - wcstring app_name; + /** Name of the current application */ + wcstring app_name; - /** The prompt commands */ - wcstring left_prompt; + /** The prompt commands */ + wcstring left_prompt; wcstring right_prompt; - /** The output of the last evaluation of the prompt command */ - wcstring left_prompt_buff; - - /** The output of the last evaluation of the right prompt command */ - wcstring right_prompt_buff; - - /** - Color is the syntax highlighting for buff. The format is that - color[i] is the classification (according to the enum in - highlight.h) of buff[i]. - */ + /** The output of the last evaluation of the prompt command */ + wcstring left_prompt_buff; + + /** The output of the last evaluation of the right prompt command */ + wcstring right_prompt_buff; + + /** + Color is the syntax highlighting for buff. The format is that + color[i] is the classification (according to the enum in + highlight.h) of buff[i]. + */ std::vector colors; - /** An array defining the block level at each character. */ - std::vector indents; + /** An array defining the block level at each character. */ + std::vector indents; - /** - Function for tab completion - */ + /** + Function for tab completion + */ complete_function_t complete_func; - /** - Function for syntax highlighting - */ - highlight_function_t highlight_function; + /** + Function for syntax highlighting + */ + highlight_function_t highlight_function; - /** - Function for testing if the string can be returned - */ - int (*test_func)( const wchar_t * ); + /** + Function for testing if the string can be returned + */ + int (*test_func)( const wchar_t * ); - /** - When this is true, the reader will exit - */ - bool end_loop; + /** + When this is true, the reader will exit + */ + bool end_loop; - /** - If this is true, exit reader even if there are running - jobs. This happens if we press e.g. ^D twice. - */ - bool prev_end_loop; + /** + If this is true, exit reader even if there are running + jobs. This happens if we press e.g. ^D twice. + */ + bool prev_end_loop; - /** The current contents of the top item in the kill ring. */ - wcstring kill_item; + /** The current contents of the top item in the kill ring. */ + wcstring kill_item; - /** - Pointer to previous reader_data - */ - reader_data_t *next; + /** + Pointer to previous reader_data + */ + reader_data_t *next; - /** - This variable keeps state on if we are in search mode, and - if yes, what mode - */ - int search_mode; + /** + This variable keeps state on if we are in search mode, and + if yes, what mode + */ + int search_mode; + + /** + Keep track of whether any internal code has done something + which is known to require a repaint. + */ + bool repaint_needed; - /** - Keep track of whether any internal code has done something - which is known to require a repaint. - */ - bool repaint_needed; - /** Whether the a screen reset is needed after a repaint. */ bool screen_reset_needed; - + /** Constructor */ reader_data_t() : suppress_autosuggestion(0), @@ -392,22 +392,22 @@ static int exit_forced; */ static void term_donate() { - set_color(rgb_color_t::normal(), rgb_color_t::normal()); + set_color(rgb_color_t::normal(), rgb_color_t::normal()); - while( 1 ) - { - if( tcsetattr(0,TCSANOW,&saved_modes) ) - { - if( errno != EINTR ) - { - debug( 1, _( L"Could not set terminal mode for new job" ) ); - wperror( L"tcsetattr" ); - break; - } - } - else - break; - } + while( 1 ) + { + if( tcsetattr(0,TCSANOW,&saved_modes) ) + { + if( errno != EINTR ) + { + debug( 1, _( L"Could not set terminal mode for new job" ) ); + wperror( L"tcsetattr" ); + break; + } + } + else + break; + } } @@ -418,28 +418,28 @@ static void term_donate() static void term_steal() { - while( 1 ) - { - if( tcsetattr(0,TCSANOW,&shell_modes) ) - { - if( errno != EINTR ) - { - debug( 1, _( L"Could not set terminal mode for shell" ) ); - wperror( L"tcsetattr" ); - break; - } - } - else - break; - } + while( 1 ) + { + if( tcsetattr(0,TCSANOW,&shell_modes) ) + { + if( errno != EINTR ) + { + debug( 1, _( L"Could not set terminal mode for shell" ) ); + wperror( L"tcsetattr" ); + break; + } + } + else + break; + } - common_handle_winch(0 ); + common_handle_winch(0 ); } int reader_exit_forced() { - return exit_forced; + return exit_forced; } /** @@ -451,16 +451,16 @@ int reader_exit_forced() static void reader_repaint() { //Update the indentation - parser_t::principal_parser().test( data->command_line.c_str(), &data->indents[0], 0, 0 ); - + parser_t::principal_parser().test( data->command_line.c_str(), &data->indents[0], 0, 0 ); + wcstring full_line = (data->autosuggestion.empty() ? data->command_line : data->autosuggestion); size_t len = full_line.size(); if (len < 1) len = 1; - + std::vector colors = data->colors; colors.resize(len, HIGHLIGHT_AUTOSUGGESTION); - + std::vector indents = data->indents; indents.resize(len); @@ -473,7 +473,7 @@ static void reader_repaint() &indents[0], data->buff_pos); - data->repaint_needed = false; + data->repaint_needed = false; } /** @@ -482,53 +482,53 @@ static void reader_repaint() static void reader_kill( size_t begin_idx, size_t length, int mode, int newv ) { const wchar_t *begin = data->command_line.c_str() + begin_idx; - if( newv ) - { + if( newv ) + { data->kill_item = wcstring(begin, length); - kill_add(data->kill_item); - } - else - { + kill_add(data->kill_item); + } + else + { wcstring old = data->kill_item; - if( mode == KILL_APPEND ) - { + if( mode == KILL_APPEND ) + { data->kill_item.append(begin, length); - } - else - { + } + else + { data->kill_item = wcstring(begin, length); data->kill_item.append(old); - } + } - - kill_replace( old, data->kill_item ); - } - if( data->buff_pos > begin_idx ) { + kill_replace( old, data->kill_item ); + } + + if( data->buff_pos > begin_idx ) { /* Move the buff position back by the number of characters we deleted, but don't go past buff_pos */ size_t backtrack = mini(data->buff_pos - begin_idx, length); data->buff_pos -= backtrack; - } - + } + data->command_line.erase(begin_idx, length); data->command_line_changed(); - - reader_super_highlight_me_plenty( data->buff_pos ); - reader_repaint(); - + + reader_super_highlight_me_plenty( data->buff_pos ); + reader_repaint(); + } /* This is called from a signal handler! */ void reader_handle_int( int sig ) { - if( !is_interactive_read ) - { + if( !is_interactive_read ) + { parser_t::skip_all_blocks(); - } - - interrupted = 1; - + } + + interrupted = 1; + } const wchar_t *reader_current_filename() @@ -548,7 +548,7 @@ void reader_push_current_filename( const wchar_t *fn ) void reader_pop_current_filename() { ASSERT_IS_MAIN_THREAD(); - current_filename.pop(); + current_filename.pop(); } @@ -556,13 +556,13 @@ void reader_pop_current_filename() void reader_data_t::command_line_changed() { ASSERT_IS_MAIN_THREAD(); size_t len = command_length(); - + /* When we grow colors, propagate the last color (if any), under the assumption that usually it will be correct. If it is, it avoids a repaint. */ color_t last_color = colors.empty() ? color_t() : colors.back(); colors.resize(len, last_color); - + indents.resize(len); - + /* Update the gen count */ s_generation_count++; } @@ -571,84 +571,84 @@ void reader_data_t::command_line_changed() { /** Remove any duplicate completions in the list. This relies on the list first beeing sorted. */ static void remove_duplicates(std::vector &l) { - l.erase(std::unique( l.begin(), l.end()), l.end()); + l.erase(std::unique( l.begin(), l.end()), l.end()); } int reader_interrupted() { - int res=interrupted; - if( res ) - interrupted=0; - return res; + int res=interrupted; + if( res ) + interrupted=0; + return res; } void reader_write_title() { - const wchar_t *title; - const env_var_t term_str = env_get_string( L"TERM" ); + const wchar_t *title; + const env_var_t term_str = env_get_string( L"TERM" ); - /* - This is a pretty lame heuristic for detecting terminals that do - not support setting the title. If we recognise the terminal name - as that of a virtual terminal, we assume it supports setting the - title. If we recognise it as that of a console, we assume it - does not support setting the title. Otherwise we check the - ttyname and see if we belive it is a virtual terminal. + /* + This is a pretty lame heuristic for detecting terminals that do + not support setting the title. If we recognise the terminal name + as that of a virtual terminal, we assume it supports setting the + title. If we recognise it as that of a console, we assume it + does not support setting the title. Otherwise we check the + ttyname and see if we belive it is a virtual terminal. - One situation in which this breaks down is with screen, since - screen supports setting the terminal title if the underlying - terminal does so, but will print garbage on terminals that - don't. Since we can't see the underlying terminal below screen - there is no way to fix this. - */ - if ( term_str.missing() ) - return; + One situation in which this breaks down is with screen, since + screen supports setting the terminal title if the underlying + terminal does so, but will print garbage on terminals that + don't. Since we can't see the underlying terminal below screen + there is no way to fix this. + */ + if ( term_str.missing() ) + return; - const wchar_t *term = term_str.c_str(); + const wchar_t *term = term_str.c_str(); bool recognized = false; recognized = recognized || contains( term, L"xterm", L"screen", L"nxterm", L"rxvt" ); recognized = recognized || ! wcsncmp(term, L"xterm-", wcslen(L"xterm-")); recognized = recognized || ! wcsncmp(term, L"screen-", wcslen(L"screen-")); - if( ! recognized ) - { - char *n = ttyname( STDIN_FILENO ); + if( ! recognized ) + { + char *n = ttyname( STDIN_FILENO ); - if( contains( term, L"linux" ) ) - { - return; - } + if( contains( term, L"linux" ) ) + { + return; + } - if( strstr( n, "tty" ) || strstr( n, "/vc/") ) - return; - - - } + if( strstr( n, "tty" ) || strstr( n, "/vc/") ) + return; - title = function_exists( L"fish_title" )?L"fish_title":DEFAULT_TITLE; - if( wcslen( title ) ==0 ) - return; + } + + title = function_exists( L"fish_title" )?L"fish_title":DEFAULT_TITLE; + + if( wcslen( title ) ==0 ) + return; wcstring_list_t lst; - proc_push_interactive(0); - if( exec_subshell( title, lst ) != -1 ) - { - size_t i; - if( lst.size() > 0 ) - { - writestr( L"\x1b]0;" ); - for( i=0; i 0 ) + { + writestr( L"\x1b]0;" ); + for( i=0; ileft_prompt_buff.clear(); data->right_prompt_buff.clear(); - + /* If we have any prompts, they must be run non-interactively */ if (data->left_prompt.size() || data->right_prompt.size()) { proc_push_interactive( 0 ); - + if (data->left_prompt.size()) { wcstring_list_t prompt_list; @@ -677,7 +677,7 @@ static void exec_prompt() } } } - + if (data->right_prompt.size()) { wcstring_list_t prompt_list; @@ -690,34 +690,34 @@ static void exec_prompt() } } } - + proc_pop_interactive(); } - + /* Write the screen title */ - reader_write_title(); + reader_write_title(); } void reader_init() { - tcgetattr(0,&shell_modes); /* get the current terminal modes */ - memcpy( &saved_modes, - &shell_modes, - sizeof(saved_modes)); /* save a copy so we can reset the terminal later */ - - shell_modes.c_lflag &= ~ICANON; /* turn off canonical mode */ - shell_modes.c_lflag &= ~ECHO; /* turn off echo mode */ + tcgetattr(0,&shell_modes); /* get the current terminal modes */ + memcpy( &saved_modes, + &shell_modes, + sizeof(saved_modes)); /* save a copy so we can reset the terminal later */ + + shell_modes.c_lflag &= ~ICANON; /* turn off canonical mode */ + shell_modes.c_lflag &= ~ECHO; /* turn off echo mode */ shell_modes.c_cc[VMIN]=1; shell_modes.c_cc[VTIME]=0; - + // PCA disable VDSUSP (typically control-Y), which is a funny job control // function available only on OS X and BSD systems // This lets us use control-Y for yank instead #ifdef VDSUSP - shell_modes.c_cc[VDSUSP] = _POSIX_VDISABLE; + shell_modes.c_cc[VDSUSP] = _POSIX_VDISABLE; #endif - + /* Repaint if necessary before each byte is read. This lets us react immediately to universal variable color changes. */ input_common_set_poll_callback(reader_repaint_if_needed); } @@ -725,25 +725,25 @@ void reader_init() void reader_destroy() { - tcsetattr(0, TCSANOW, &saved_modes); + tcsetattr(0, TCSANOW, &saved_modes); } void reader_exit( int do_exit, int forced ) { - if( data ) - data->end_loop=do_exit; - end_loop=do_exit; - if( forced ) - exit_forced = 1; - + if( data ) + data->end_loop=do_exit; + end_loop=do_exit; + if( forced ) + exit_forced = 1; + } void reader_repaint_needed() { - if (data) { - data->repaint_needed = true; - } + if (data) { + data->repaint_needed = true; + } } void reader_repaint_if_needed() { @@ -759,10 +759,10 @@ void reader_repaint_if_needed() { } void reader_react_to_color_change() { - if (data) { + if (data) { data->repaint_needed = true; data->screen_reset_needed = true; - } + } } @@ -773,22 +773,22 @@ void reader_react_to_color_change() { static void remove_backward() { - if( data->buff_pos <= 0 ) - return; - + if( data->buff_pos <= 0 ) + return; + /* Fake composed character sequences by continuning to delete until we delete a character of width at least 1. */ int width; do { data->buff_pos -= 1; width = fish_wcwidth(data->command_line.at(data->buff_pos)); - data->command_line.erase(data->buff_pos, 1); + data->command_line.erase(data->buff_pos, 1); } while (width == 0 && data->buff_pos > 0); data->command_line_changed(); data->suppress_autosuggestion = true; - reader_super_highlight_me_plenty( data->buff_pos ); + reader_super_highlight_me_plenty( data->buff_pos ); - reader_repaint(); + reader_repaint(); } @@ -802,18 +802,18 @@ static int insert_string(const wcstring &str) size_t len = str.size(); if (len == 0) return 0; - + data->command_line.insert(data->buff_pos, str); data->buff_pos += len; data->command_line_changed(); data->suppress_autosuggestion = false; - - /* Syntax highlight. Note we must have that buff_pos > 0 because we just added something nonzero to its length */ + + /* Syntax highlight. Note we must have that buff_pos > 0 because we just added something nonzero to its length */ assert(data->buff_pos > 0); - reader_super_highlight_me_plenty( data->buff_pos-1 ); - - reader_repaint(); - return 1; + reader_super_highlight_me_plenty( data->buff_pos-1 ); + + reader_repaint(); + return 1; } @@ -823,7 +823,7 @@ static int insert_string(const wcstring &str) */ static int insert_char( wchar_t c ) { - return insert_string(wcstring(&c, 1)); + return insert_string(wcstring(&c, 1)); } @@ -832,10 +832,10 @@ static int insert_char( wchar_t c ) */ static size_t comp_len( const wchar_t *a, const wchar_t *b ) { - size_t i; - for( i=0; a[i] != L'\0' && b[i] != L'\0' && a[i]==b[i]; i++ ) - ; - return i; + size_t i; + for( i=0; a[i] != L'\0' && b[i] != L'\0' && a[i]==b[i]; i++ ) + ; + return i; } /** @@ -843,10 +843,10 @@ static size_t comp_len( const wchar_t *a, const wchar_t *b ) */ static size_t comp_ilen( const wchar_t *a, const wchar_t *b ) { - size_t i; - for( i=0; a[i] != L'\0' && b[i] != L'\0' && towlower(a[i])==towlower(b[i]); i++ ) - ; - return i; + size_t i; + for( i=0; a[i] != L'\0' && b[i] != L'\0' && towlower(a[i])==towlower(b[i]); i++ ) + ; + return i; } @@ -863,71 +863,71 @@ static size_t comp_ilen( const wchar_t *a, const wchar_t *b ) static wcstring completion_apply_to_command_line(const wcstring &val_str, int flags, const wcstring &command_line, size_t *inout_cursor_pos) { const wchar_t *val = val_str.c_str(); - bool add_space = !(flags & COMPLETE_NO_SPACE); - bool do_replace = !!(flags & COMPLETE_NO_CASE); - bool do_escape = !(flags & COMPLETE_DONT_ESCAPE); + bool add_space = !(flags & COMPLETE_NO_SPACE); + bool do_replace = !!(flags & COMPLETE_NO_CASE); + bool do_escape = !(flags & COMPLETE_DONT_ESCAPE); const size_t cursor_pos = *inout_cursor_pos; - - // debug( 0, L"Insert completion %ls with flags %d", val, flags); - if( do_replace ) - { - - size_t move_cursor; - const wchar_t *begin, *end; - wchar_t *escaped; - + // debug( 0, L"Insert completion %ls with flags %d", val, flags); + + if( do_replace ) + { + + size_t move_cursor; + const wchar_t *begin, *end; + wchar_t *escaped; + const wchar_t *buff = command_line.c_str(); - parse_util_token_extent( buff, cursor_pos, &begin, 0, 0, 0 ); - end = buff + cursor_pos; + parse_util_token_extent( buff, cursor_pos, &begin, 0, 0, 0 ); + end = buff + cursor_pos; - wcstring sb(buff, begin - buff); - - if( do_escape ) - { - escaped = escape( val, ESCAPE_ALL | ESCAPE_NO_QUOTED ); - sb.append( escaped ); - move_cursor = wcslen(escaped); - free( escaped ); - } - else - { - sb.append( val ); - move_cursor = wcslen(val); - } - + wcstring sb(buff, begin - buff); + + if( do_escape ) + { + escaped = escape( val, ESCAPE_ALL | ESCAPE_NO_QUOTED ); + sb.append( escaped ); + move_cursor = wcslen(escaped); + free( escaped ); + } + else + { + sb.append( val ); + move_cursor = wcslen(val); + } + + + if( add_space ) + { + sb.append( L" " ); + move_cursor += 1; + } + sb.append( end ); - if( add_space ) - { - sb.append( L" " ); - move_cursor += 1; - } - sb.append( end ); - size_t new_cursor_pos = (begin - buff) + move_cursor; *inout_cursor_pos = new_cursor_pos; return sb; - } - else - { + } + else + { wchar_t quote = L'\0'; wcstring replaced; - if( do_escape ) - { + if( do_escape ) + { parse_util_get_parameter_info(command_line, cursor_pos, "e, NULL, NULL); replaced = parse_util_escape_string_with_quote(val_str, quote); - } - else - { - replaced = val; - } - + } + else + { + replaced = val; + } + wcstring result = command_line; result.insert(cursor_pos, replaced); size_t new_cursor_pos = cursor_pos + replaced.size(); if (add_space) { - if (quote && (command_line.c_str()[cursor_pos] != quote)) + if (quote && (command_line.c_str()[cursor_pos] != quote)) { /* This is a quoted parameter, first print a quote */ result.insert(new_cursor_pos++, wcstring("e, 1)); @@ -936,7 +936,7 @@ static wcstring completion_apply_to_command_line(const wcstring &val_str, int fl } *inout_cursor_pos = new_cursor_pos; return result; - } + } } /** @@ -953,7 +953,7 @@ static void completion_insert( const wchar_t *val, int flags ) size_t cursor = data->buff_pos; wcstring new_command_line = completion_apply_to_command_line(val, flags, data->command_line, &cursor); reader_set_buffer(new_command_line, cursor); - + /* Since we just inserted a completion, don't immediately do a new autosuggestion */ data->suppress_autosuggestion = true; } @@ -963,7 +963,7 @@ static void completion_insert( const wchar_t *val, int flags ) fish_pager outputs any text, it is inserted into the input backbuffer. - \param prefix the string to display before every completion. + \param prefix the string to display before every completion. \param is_quoted should be set if the argument is quoted. This will change the display style. \param comp the list of completions to display */ @@ -971,74 +971,74 @@ static void completion_insert( const wchar_t *val, int flags ) static void run_pager( const wcstring &prefix, int is_quoted, const std::vector &comp ) { wcstring msg; - wcstring prefix_esc; - char *foo; + wcstring prefix_esc; + char *foo; - wchar_t *escaped_separator; - int has_case_sensitive=0; + wchar_t *escaped_separator; + int has_case_sensitive=0; + + if (prefix.empty()) + { + prefix_esc = L"\"\""; + } + else + { + prefix_esc = escape_string(prefix, 1); + } - if (prefix.empty()) - { - prefix_esc = L"\"\""; - } - else - { - prefix_esc = escape_string(prefix, 1); - } - wcstring cmd = format_string(L"fish_pager -c 3 -r 4 %ls -p %ls", // L"valgrind --track-fds=yes --log-file=pager.txt --leak-check=full ./fish_pager %d %ls", is_quoted?L"-q":L"", prefix_esc.c_str() ); - + io_data_t *in = io_buffer_create(true); - in->fd = 3; + in->fd = 3; - escaped_separator = escape( COMPLETE_SEP_STR, 1); - - for( size_t i=0; i< comp.size(); i++ ) - { - const completion_t &el = comp.at( i ); - has_case_sensitive |= !(el.flags & COMPLETE_NO_CASE ); - } - - for( size_t i=0; i< comp.size(); i++ ) - { + escaped_separator = escape( COMPLETE_SEP_STR, 1); - long base_len=-1; - const completion_t &el = comp.at( i ); + for( size_t i=0; i< comp.size(); i++ ) + { + const completion_t &el = comp.at( i ); + has_case_sensitive |= !(el.flags & COMPLETE_NO_CASE ); + } - wcstring completion_text; - wcstring description_text; + for( size_t i=0; i< comp.size(); i++ ) + { - if( has_case_sensitive && (el.flags & COMPLETE_NO_CASE )) - { - continue; - } - - // Note that an empty completion is perfectly sensible here, e.g. tab-completing 'foo' with a file called 'foo' and another called 'foobar' - if( el.flags & COMPLETE_NO_CASE ) - { - if( base_len == -1 ) + long base_len=-1; + const completion_t &el = comp.at( i ); + + wcstring completion_text; + wcstring description_text; + + if( has_case_sensitive && (el.flags & COMPLETE_NO_CASE )) + { + continue; + } + + // Note that an empty completion is perfectly sensible here, e.g. tab-completing 'foo' with a file called 'foo' and another called 'foobar' + if( el.flags & COMPLETE_NO_CASE ) + { + if( base_len == -1 ) { const wchar_t *begin, *buff = data->command_line.c_str(); - + parse_util_token_extent( buff, data->buff_pos, &begin, 0, 0, 0 ); base_len = data->buff_pos - (begin-buff); } - - completion_text = escape_string( el.completion.c_str() + base_len, ESCAPE_ALL | ESCAPE_NO_QUOTED ); - } - else + + completion_text = escape_string( el.completion.c_str() + base_len, ESCAPE_ALL | ESCAPE_NO_QUOTED ); + } + else { - completion_text = escape_string( el.completion, ESCAPE_ALL | ESCAPE_NO_QUOTED ); + completion_text = escape_string( el.completion, ESCAPE_ALL | ESCAPE_NO_QUOTED ); } - - - if( ! el.description.empty() ) - { - description_text = escape_string( el.description, true ); - } + + + if( ! el.description.empty() ) + { + description_text = escape_string( el.description, true ); + } /* It's possible (even common) to have an empty completion with no description. An example would be completing 'foo' with extant files 'foo' and 'foobar'. But fish_pager ignores blank lines. So if our completion text is empty, always include a description, even if it's empty. */ @@ -1052,44 +1052,44 @@ static void run_pager( const wcstring &prefix, int is_quoted, const std::vector< msg.push_back(L'\n'); } - free( escaped_separator ); - - foo = wcs2str(msg.c_str()); - in->out_buffer_append(foo, strlen(foo) ); - free( foo ); - - term_donate(); - - io_data_t *out = io_buffer_create( false ); - out->fd = 4; - + free( escaped_separator ); + + foo = wcs2str(msg.c_str()); + in->out_buffer_append(foo, strlen(foo) ); + free( foo ); + + term_donate(); + + io_data_t *out = io_buffer_create( false ); + out->fd = 4; + parser_t &parser = parser_t::principal_parser(); io_chain_t io_chain; io_chain.push_back(out); io_chain.push_back(in); - parser.eval( cmd, io_chain, TOP); - term_steal(); + parser.eval( cmd, io_chain, TOP); + term_steal(); - io_buffer_read( out ); + io_buffer_read( out ); - int nil=0; + int nil=0; out->out_buffer_append((char *)&nil, 1); - wchar_t *tmp; - wchar_t *str = str2wcs(out->out_buffer_ptr()); + wchar_t *tmp; + wchar_t *str = str2wcs(out->out_buffer_ptr()); - if( str ) - { - for( tmp = str + wcslen(str)-1; tmp >= str; tmp-- ) - { - input_unreadch( *tmp ); - } - free( str ); - } + if( str ) + { + for( tmp = str + wcslen(str)-1; tmp >= str; tmp-- ) + { + input_unreadch( *tmp ); + } + free( str ); + } - io_buffer_destroy( out); - io_buffer_destroy( in); + io_buffer_destroy( out); + io_buffer_destroy( in); } struct autosuggestion_context_t { @@ -1102,10 +1102,10 @@ struct autosuggestion_context_t { const env_vars_snapshot_t vars; wcstring_list_t commands_to_load; const unsigned int generation_count; - + // don't reload more than once bool has_tried_reloading; - + autosuggestion_context_t(history_t *history, const wcstring &term, size_t pos) : search_string(term), cursor_pos(pos), @@ -1117,28 +1117,28 @@ struct autosuggestion_context_t { has_tried_reloading(false) { } - + /* The function run in the background thread to determine an autosuggestion */ int threaded_autosuggest(void) { ASSERT_IS_BACKGROUND_THREAD(); - + /* If the main thread has moved on, skip all the work */ if (generation_count != s_generation_count) { return 0; } - + /* Let's make sure we aren't using the empty string */ if (search_string.empty()) { return 0; } - + while (searcher.go_backwards()) { history_item_t item = searcher.current_item(); - + /* Skip items with newlines because they make terrible autosuggestions */ if (item.str().find('\n') != wcstring::npos) continue; - + if (autosuggest_validate_from_history(item, detector, working_directory, vars)) { /* The command autosuggestion was handled specially, so we're done */ this->autosuggestion = searcher.current_string(); @@ -1146,14 +1146,14 @@ struct autosuggestion_context_t { } } - + /* Try handling a special command like cd */ wcstring special_suggestion; if (autosuggest_suggest_special(search_string, working_directory, special_suggestion)) { this->autosuggestion = special_suggestion; return 1; } - + // Here we do something a little funny // If the line ends with a space, and the cursor is not at the end, // don't use completion autosuggestions. It ends up being pretty weird seeing stuff get spammed on the right @@ -1176,7 +1176,7 @@ struct autosuggestion_context_t { this->autosuggestion = completion_apply_to_command_line(comp.completion.c_str(), comp.flags, this->search_string, &cursor); return 1; } - + return 0; } }; @@ -1198,7 +1198,7 @@ static void autosuggest_completed(autosuggestion_context_t *ctx, int result) { /* Extract the commands to load */ wcstring_list_t commands_to_load; ctx->commands_to_load.swap(commands_to_load); - + /* If we have autosuggestions to load, load them and try again */ if (! result && ! commands_to_load.empty() && ! ctx->has_tried_reloading) { @@ -1210,7 +1210,7 @@ static void autosuggest_completed(autosuggestion_context_t *ctx, int result) { iothread_perform(threaded_autosuggest, autosuggest_completed, ctx); return; } - + if (result && can_autosuggest() && ctx->search_string == data->command_line && @@ -1263,22 +1263,22 @@ static void accept_autosuggestion(void) { */ static void reader_flash() { - struct timespec pollint; + struct timespec pollint; - for( size_t i=0; ibuff_pos; i++ ) - { - data->colors.at(i) = HIGHLIGHT_SEARCH_MATCH<<16; - } - - reader_repaint(); - - pollint.tv_sec = 0; - pollint.tv_nsec = 100 * 1000000; - nanosleep( &pollint, NULL ); + for( size_t i=0; ibuff_pos; i++ ) + { + data->colors.at(i) = HIGHLIGHT_SEARCH_MATCH<<16; + } - reader_super_highlight_me_plenty( data->buff_pos ); - - reader_repaint(); + reader_repaint(); + + pollint.tv_sec = 0; + pollint.tv_nsec = 100 * 1000000; + nanosleep( &pollint, NULL ); + + reader_super_highlight_me_plenty( data->buff_pos ); + + reader_repaint(); } /** @@ -1299,23 +1299,23 @@ static void reader_flash() static bool reader_can_replace( const wcstring &in, int flags ) { - const wchar_t * str = in.c_str(); + const wchar_t * str = in.c_str(); - if( flags & COMPLETE_DONT_ESCAPE ) - { - return true; - } - /* - Test characters that have a special meaning in any character position - */ - while( *str ) - { - if( wcschr( REPLACE_UNCLEAN, *str ) ) - return false; - str++; - } + if( flags & COMPLETE_DONT_ESCAPE ) + { + return true; + } + /* + Test characters that have a special meaning in any character position + */ + while( *str ) + { + if( wcschr( REPLACE_UNCLEAN, *str ) ) + return false; + str++; + } - return true; + return true; } /* Compare two completions, except make the case insensitive comes larger than everyone (so they come last) */ @@ -1350,14 +1350,14 @@ static const completion_t *cycle_competions(const std::vector &com { /* Bump the index */ idx = (idx + 1) % size; - + /* Bail if we've looped */ if (idx == start_idx) break; - + /* Get the completion */ const completion_t &c = comp.at(idx); - + /* Try this completion */ if (! c.is_case_insensitive() || reader_can_replace(command_line, c.flags)) { @@ -1366,14 +1366,14 @@ static const completion_t *cycle_competions(const std::vector &com break; } } - + *inout_idx = idx; return result; } /** Handle the list of completions. This means the following: - + - If the list is empty, flash the terminal. - If the list contains one element, write the whole element, and if the element does not end on a '/', '@', ':', or a '=', also write a trailing @@ -1382,199 +1382,199 @@ static const completion_t *cycle_competions(const std::vector &com the prefix. - If the list contains multiple elements without. a common prefix, call run_pager to display a list of completions. Depending on terminal size and the length of the list, run_pager may either show less than a screenfull and exit or use an interactive pager to allow the user to scroll through the completions. - + \param comp the list of completion strings - + Return true if we inserted text into the command line, false if we did not. */ static bool handle_completions( const std::vector &comp ) { - wchar_t *base = NULL; - size_t len = 0; - bool done = false; + wchar_t *base = NULL; + size_t len = 0; + bool done = false; bool success = false; - int count = 0; - int flags=0; - const wchar_t *begin, *end, *buff = data->command_line.c_str(); - - parse_util_token_extent( buff, data->buff_pos, &begin, 0, 0, 0 ); - end = buff+data->buff_pos; - + int count = 0; + int flags=0; + const wchar_t *begin, *end, *buff = data->command_line.c_str(); + + parse_util_token_extent( buff, data->buff_pos, &begin, 0, 0, 0 ); + end = buff+data->buff_pos; + const wcstring tok(begin, end - begin); - - /* - Check trivial cases - */ - switch(comp.size()) - { - /* No suitable completions found, flash screen and return */ - case 0: - { - reader_flash(); - done = true; + + /* + Check trivial cases + */ + switch(comp.size()) + { + /* No suitable completions found, flash screen and return */ + case 0: + { + reader_flash(); + done = true; success = false; - break; - } + break; + } - /* Exactly one suitable completion found - insert it */ - case 1: - { - - const completion_t &c = comp.at( 0 ); - - /* - If this is a replacement completion, check - that we know how to replace it, e.g. that - the token doesn't contain evil operators - like {} - */ - if( ! c.is_case_insensitive() || reader_can_replace( tok, c.flags ) ) - { - completion_insert( c.completion.c_str(), c.flags ); - } - done = true; - success = true; - break; - } - } - - - if( !done ) - { - /* Try to find something to insert with the correct case */ - for( size_t i=0; i< comp.size() ; i++ ) - { - const completion_t &c = comp.at( i ); + /* Exactly one suitable completion found - insert it */ + case 1: + { - /* Ignore case insensitive completions for now */ - if( c.is_case_insensitive() ) - continue; - - count++; - - if( base ) - { - size_t new_len = comp_len( base, c.completion.c_str() ); + const completion_t &c = comp.at( 0 ); + + /* + If this is a replacement completion, check + that we know how to replace it, e.g. that + the token doesn't contain evil operators + like {} + */ + if( ! c.is_case_insensitive() || reader_can_replace( tok, c.flags ) ) + { + completion_insert( c.completion.c_str(), c.flags ); + } + done = true; + success = true; + break; + } + } + + + if( !done ) + { + /* Try to find something to insert with the correct case */ + for( size_t i=0; i< comp.size() ; i++ ) + { + const completion_t &c = comp.at( i ); + + /* Ignore case insensitive completions for now */ + if( c.is_case_insensitive() ) + continue; + + count++; + + if( base ) + { + size_t new_len = comp_len( base, c.completion.c_str() ); len = mini(new_len, len); - } - else - { - base = wcsdup( c.completion.c_str() ); - len = wcslen( base ); - flags = c.flags; - } - } + } + else + { + base = wcsdup( c.completion.c_str() ); + len = wcslen( base ); + flags = c.flags; + } + } - /* If we found something to insert, do it. */ - if( len > 0 ) - { - if( count > 1 ) - flags = flags | COMPLETE_NO_SPACE; + /* If we found something to insert, do it. */ + if( len > 0 ) + { + if( count > 1 ) + flags = flags | COMPLETE_NO_SPACE; - base[len]=L'\0'; - completion_insert(base, flags); - done = true; + base[len]=L'\0'; + completion_insert(base, flags); + done = true; success = true; - } - } - - + } + } - if( !done && base == NULL ) - { - /* Try to find something to insert ignoring case */ - if( begin ) - { - size_t offset = tok.size(); - - count = 0; - - for( size_t i=0; i< comp.size(); i++ ) - { - const completion_t &c = comp.at( i ); - if( ! c.is_case_insensitive() ) - continue; - - if( !reader_can_replace( tok, c.flags ) ) - { - len=0; - break; - } - count++; + if( !done && base == NULL ) + { + /* Try to find something to insert ignoring case */ + if( begin ) + { + size_t offset = tok.size(); - if( base ) - { - size_t new_len = offset + comp_ilen( base+offset, c.completion.c_str()+offset ); - len = new_len < len ? new_len: len; - } - else - { - base = wcsdup( c.completion.c_str() ); - len = wcslen( base ); - flags = c.flags; - - } - } + count = 0; - if( len > offset ) - { - if( count > 1 ) - flags = flags | COMPLETE_NO_SPACE; - - base[len]=L'\0'; - completion_insert( base, flags ); - done = 1; - success = true; - } - - } - } - - free( base ); + for( size_t i=0; i< comp.size(); i++ ) + { + const completion_t &c = comp.at( i ); - if( !done ) - { - /* - There is no common prefix in the completions, and show_list - is true, so we print the list - */ - size_t len, prefix_start = 0; - wcstring prefix; - parse_util_get_parameter_info(data->command_line, data->buff_pos, NULL, &prefix_start, NULL); + if( ! c.is_case_insensitive() ) + continue; - assert(data->buff_pos >= prefix_start); - len = data->buff_pos - prefix_start; - - if( len <= PREFIX_MAX_LEN ) + if( !reader_can_replace( tok, c.flags ) ) { - prefix.append(data->command_line, prefix_start, len); - } - else - { - // append just the end of the string - prefix = wcstring(&ellipsis_char, 1); - prefix.append(data->command_line, prefix_start + len - PREFIX_MAX_LEN - 1, wcstring::npos); + len=0; + break; } - { - int is_quoted; + count++; - wchar_t quote; - parse_util_get_parameter_info(data->command_line, data->buff_pos, "e, NULL, NULL); - is_quoted = (quote != L'\0'); - - write_loop(1, "\n", 1 ); - - run_pager( prefix, is_quoted, comp ); - } - s_reset( &data->screen, true); - reader_repaint(); + if( base ) + { + size_t new_len = offset + comp_ilen( base+offset, c.completion.c_str()+offset ); + len = new_len < len ? new_len: len; + } + else + { + base = wcsdup( c.completion.c_str() ); + len = wcslen( base ); + flags = c.flags; + + } + } + + if( len > offset ) + { + if( count > 1 ) + flags = flags | COMPLETE_NO_SPACE; + + base[len]=L'\0'; + completion_insert( base, flags ); + done = 1; + success = true; + } + + } + } + + free( base ); + + if( !done ) + { + /* + There is no common prefix in the completions, and show_list + is true, so we print the list + */ + size_t len, prefix_start = 0; + wcstring prefix; + parse_util_get_parameter_info(data->command_line, data->buff_pos, NULL, &prefix_start, NULL); + + assert(data->buff_pos >= prefix_start); + len = data->buff_pos - prefix_start; + + if( len <= PREFIX_MAX_LEN ) + { + prefix.append(data->command_line, prefix_start, len); + } + else + { + // append just the end of the string + prefix = wcstring(&ellipsis_char, 1); + prefix.append(data->command_line, prefix_start + len - PREFIX_MAX_LEN - 1, wcstring::npos); + } + + { + int is_quoted; + + wchar_t quote; + parse_util_get_parameter_info(data->command_line, data->buff_pos, "e, NULL, NULL); + is_quoted = (quote != L'\0'); + + write_loop(1, "\n", 1 ); + + run_pager( prefix, is_quoted, comp ); + } + s_reset( &data->screen, true); + reader_repaint(); success = false; - } - return success; + } + return success; } @@ -1583,101 +1583,101 @@ static bool handle_completions( const std::vector &comp ) */ static void reader_interactive_init() { - /* See if we are running interactively. */ - pid_t shell_pgid; + /* See if we are running interactively. */ + pid_t shell_pgid; - input_init(); - kill_init(); - shell_pgid = getpgrp (); + input_init(); + kill_init(); + shell_pgid = getpgrp (); - /* - This should enable job control on fish, even if our parent process did - not enable it for us. - */ + /* + This should enable job control on fish, even if our parent process did + not enable it for us. + */ - /* - Check if we are in control of the terminal, so that we don't do - semi-expensive things like reset signal handlers unless we - really have to, which we often don't. - */ - if (tcgetpgrp( 0 ) != shell_pgid) - { - int block_count = 0; - int i; - - /* - Bummer, we are not in control of the terminal. Stop until - parent has given us control of it. Stopping in fish is a bit - of a challange, what with all the signal fidgeting, we need - to reset a bunch of signal state, making this coda a but - unobvious. + /* + Check if we are in control of the terminal, so that we don't do + semi-expensive things like reset signal handlers unless we + really have to, which we often don't. + */ + if (tcgetpgrp( 0 ) != shell_pgid) + { + int block_count = 0; + int i; - In theory, reseting signal handlers could cause us to miss - signal deliveries. In practice, this code should only be run - suring startup, when we're not waiting for any signals. - */ - while (signal_is_blocked()) - { - signal_unblock(); - block_count++; - } - signal_reset_handlers(); - - /* - Ok, signal handlers are taken out of the picture. Stop ourself in a loop - until we are in control of the terminal. - */ - while (tcgetpgrp( 0 ) != shell_pgid) - { - killpg( shell_pgid, SIGTTIN); - } - - signal_set_handlers(); + /* + Bummer, we are not in control of the terminal. Stop until + parent has given us control of it. Stopping in fish is a bit + of a challange, what with all the signal fidgeting, we need + to reset a bunch of signal state, making this coda a but + unobvious. - for( i=0; ibuff_pos <= data->command_length() )) + sanity_lose(); - if(!( data->buff_pos <= data->command_length() )) - sanity_lose(); - if (data->colors.size() != data->command_length()) sanity_lose(); - + if (data->indents.size() != data->command_length()) sanity_lose(); - - } + + } } /** @@ -1727,22 +1727,22 @@ static void set_command_line_and_position( const wcstring &new_str, size_t pos ) void reader_replace_current_token( const wchar_t *new_token ) { - const wchar_t *begin, *end; - size_t new_pos; + const wchar_t *begin, *end; + size_t new_pos; - /* Find current token */ + /* Find current token */ const wchar_t *buff = data->command_line.c_str(); - parse_util_token_extent( (wchar_t *)buff, data->buff_pos, &begin, &end, 0, 0 ); + parse_util_token_extent( (wchar_t *)buff, data->buff_pos, &begin, &end, 0, 0 ); - if( !begin || !end ) - return; + if( !begin || !end ) + return; - /* Make new string */ + /* Make new string */ wcstring new_buff(buff, begin - buff); new_buff.append(new_token); new_buff.append(end); - new_pos = (begin-buff) + wcslen(new_token); - + new_pos = (begin-buff) + wcslen(new_token); + set_command_line_and_position(new_buff, new_pos); } @@ -1752,21 +1752,21 @@ void reader_replace_current_token( const wchar_t *new_token ) */ static void reset_token_history() { - const wchar_t *begin, *end; + const wchar_t *begin, *end; const wchar_t *buff = data->command_line.c_str(); - parse_util_token_extent( (wchar_t *)buff, data->buff_pos, &begin, &end, 0, 0 ); - - data->search_buff.clear(); - if( begin ) - { - data->search_buff.append(begin, end - begin); - } + parse_util_token_extent( (wchar_t *)buff, data->buff_pos, &begin, &end, 0, 0 ); - data->token_history_pos = -1; - data->search_pos=0; + data->search_buff.clear(); + if( begin ) + { + data->search_buff.append(begin, end - begin); + } + + data->token_history_pos = -1; + data->search_pos=0; data->search_prev.clear(); data->search_prev.push_back(data->search_buff); - + data->history_search = history_search_t(*data->history, data->search_buff, HISTORY_SEARCH_TYPE_CONTAINS); } @@ -1782,128 +1782,128 @@ static void handle_token_history( int forward, int reset ) /* Paranoia */ if (! data) return; - - const wchar_t *str=0; - long current_pos; - tokenizer tok; - if( reset ) - { - /* - Start a new token search using the current token - */ - reset_token_history(); + const wchar_t *str=0; + long current_pos; + tokenizer tok; - } + if( reset ) + { + /* + Start a new token search using the current token + */ + reset_token_history(); + + } - current_pos = data->token_history_pos; + current_pos = data->token_history_pos; - if( forward || data->search_pos + 1 < data->search_prev.size() ) - { - if( forward ) - { - if( data->search_pos > 0 ) - { - data->search_pos--; - } + if( forward || data->search_pos + 1 < data->search_prev.size() ) + { + if( forward ) + { + if( data->search_pos > 0 ) + { + data->search_pos--; + } str = data->search_prev.at(data->search_pos).c_str(); - } - else - { - data->search_pos++; + } + else + { + data->search_pos++; str = data->search_prev.at(data->search_pos).c_str(); - } + } - reader_replace_current_token( str ); - reader_super_highlight_me_plenty( data->buff_pos ); - reader_repaint(); - } - else - { - if( current_pos == -1 ) - { + reader_replace_current_token( str ); + reader_super_highlight_me_plenty( data->buff_pos ); + reader_repaint(); + } + else + { + if( current_pos == -1 ) + { data->token_history_buff.clear(); - - /* - Search for previous item that contains this substring - */ + + /* + Search for previous item that contains this substring + */ if (data->history_search.go_backwards()) { wcstring item = data->history_search.current_string(); data->token_history_buff = data->history_search.current_string(); } - current_pos = data->token_history_buff.size(); + current_pos = data->token_history_buff.size(); - } + } - if( data->token_history_buff.empty() ) - { - /* - We have reached the end of the history - check if the - history already contains the search string itself, if so - return, otherwise add it. - */ + if( data->token_history_buff.empty() ) + { + /* + We have reached the end of the history - check if the + history already contains the search string itself, if so + return, otherwise add it. + */ - const wcstring &last = data->search_prev.back(); - if (data->search_buff != last) - { - str = wcsdup( data->search_buff.c_str() ); - } - else - { - return; - } - } - else - { + const wcstring &last = data->search_prev.back(); + if (data->search_buff != last) + { + str = wcsdup( data->search_buff.c_str() ); + } + else + { + return; + } + } + else + { - //debug( 3, L"new '%ls'", data->token_history_buff.c_str() ); + //debug( 3, L"new '%ls'", data->token_history_buff.c_str() ); - for( tok_init( &tok, data->token_history_buff.c_str(), TOK_ACCEPT_UNFINISHED ); - tok_has_next( &tok); - tok_next( &tok )) - { - switch( tok_last_type( &tok ) ) - { - case TOK_STRING: - { - if( wcsstr( tok_last( &tok ), data->search_buff.c_str() ) ) - { - //debug( 3, L"Found token at pos %d\n", tok_get_pos( &tok ) ); - if( tok_get_pos( &tok ) >= current_pos ) - { - break; - } - //debug( 3, L"ok pos" ); + for( tok_init( &tok, data->token_history_buff.c_str(), TOK_ACCEPT_UNFINISHED ); + tok_has_next( &tok); + tok_next( &tok )) + { + switch( tok_last_type( &tok ) ) + { + case TOK_STRING: + { + if( wcsstr( tok_last( &tok ), data->search_buff.c_str() ) ) + { + //debug( 3, L"Found token at pos %d\n", tok_get_pos( &tok ) ); + if( tok_get_pos( &tok ) >= current_pos ) + { + break; + } + //debug( 3, L"ok pos" ); const wcstring last_tok = tok_last( &tok ); if (find(data->search_prev.begin(), data->search_prev.end(), last_tok) == data->search_prev.end()) { - data->token_history_pos = tok_get_pos( &tok ); - str = wcsdup(tok_last( &tok )); - } + data->token_history_pos = tok_get_pos( &tok ); + str = wcsdup(tok_last( &tok )); + } - } - } - } - } + } + } + } + } - tok_destroy( &tok ); - } + tok_destroy( &tok ); + } - if( str ) - { - reader_replace_current_token( str ); - reader_super_highlight_me_plenty( data->buff_pos ); - reader_repaint(); + if( str ) + { + reader_replace_current_token( str ); + reader_super_highlight_me_plenty( data->buff_pos ); + reader_repaint(); data->search_pos = data->search_prev.size(); data->search_prev.push_back(str); - } - else if( ! reader_interrupted() ) - { - data->token_history_pos=-1; - handle_token_history( 0, 0 ); - } - } + } + else if( ! reader_interrupted() ) + { + data->token_history_pos=-1; + handle_token_history( 0, 0 ); + } + } } /* Our state machine that implements "one word" movement or erasure. */ @@ -1915,13 +1915,13 @@ class move_word_state_machine_t s_alphanumeric_or_punctuation_except_slash, s_end } state; - + public: - + move_word_state_machine_t() : state(s_whitespace) { } - + bool consume_char(wchar_t c) { /* Note fall-through in all of these! */ @@ -1931,20 +1931,20 @@ class move_word_state_machine_t if (iswspace(c)) return true; state = s_punctuation; - + case s_punctuation: if (iswpunct(c)) return true; state = s_alphanumeric_or_punctuation_except_slash; - + case s_alphanumeric_or_punctuation_except_slash: if (c != L'/' && (iswalnum(c) || iswpunct(c))) return true; state = s_end; - + case s_end: default: - return false; + return false; } } }; @@ -1957,13 +1957,13 @@ class move_word_state_machine_t \param dir Direction to move/erase. 0 means move left, 1 means move right. \param erase Whether to erase the characters along the way or only move past them. \param new if the new kill item should be appended to the previous kill item or not. - + The regex we implement: - + WHITESPACE* PUNCTUATION* ALPHANUMERIC_OR_PUNCTUATION_EXCEPT_SLASH* - + Interesting test case: /foo/bar/baz/ -> /foo/bar/ -> /foo/ -> / echo --foo --bar -> echo --foo -> echo @@ -1975,26 +1975,26 @@ enum move_word_dir_t { static void move_word( bool move_right, bool erase, bool newv ) { const size_t start_buff_pos = data->buff_pos; - size_t end_buff_pos = data->buff_pos; //this is the last character we delete; we always delete at least one character + size_t end_buff_pos = data->buff_pos; //this is the last character we delete; we always delete at least one character - /* Return if we are already at the edge */ + /* Return if we are already at the edge */ const size_t boundary = move_right ? data->command_length() : 0; if (data->buff_pos == boundary) return; - - /* - If we are beyond the last character and moving left, start by - moving one step, since otherwise we'll start on the \0, which - should be ignored. - */ - if (! move_right && (end_buff_pos == data->command_length()) ) - { - if( !end_buff_pos ) - return; - - end_buff_pos--; - } - + + /* + If we are beyond the last character and moving left, start by + moving one step, since otherwise we'll start on the \0, which + should be ignored. + */ + if (! move_right && (end_buff_pos == data->command_length()) ) + { + if( !end_buff_pos ) + return; + + end_buff_pos--; + } + const wchar_t * const command_line = data->command_line.c_str(); move_word_state_machine_t state; const size_t last_valid_end_buff_pos = (move_right ? data->command_length() - 1 : 0); @@ -2007,132 +2007,132 @@ static void move_word( bool move_right, bool erase, bool newv ) end_buff_pos = next_buff_pos; } - if( erase ) - { + if( erase ) + { size_t start_idx = mini(start_buff_pos, end_buff_pos); //first char to delete size_t end_idx = maxi(start_buff_pos, end_buff_pos); //last char to delete - + // Don't try to delete past the end end_idx = mini(end_idx, data->command_length() - 1); - + // Don't autosuggest after a kill data->suppress_autosuggestion = true; - - reader_kill( start_idx, end_idx - start_idx + 1, move_right?KILL_APPEND:KILL_PREPEND, newv ); - } - else - { - data->buff_pos = move_right ? end_buff_pos + 1 : end_buff_pos; - reader_repaint(); - } + + reader_kill( start_idx, end_idx - start_idx + 1, move_right?KILL_APPEND:KILL_PREPEND, newv ); + } + else + { + data->buff_pos = move_right ? end_buff_pos + 1 : end_buff_pos; + reader_repaint(); + } } const wchar_t *reader_get_buffer(void) { ASSERT_IS_MAIN_THREAD(); - return data?data->command_line.c_str():NULL; + return data?data->command_line.c_str():NULL; } history_t *reader_get_history(void) { ASSERT_IS_MAIN_THREAD(); - return data ? data->history : NULL; + return data ? data->history : NULL; } void reader_set_buffer( const wcstring &b, size_t pos ) { - if( !data ) - return; + if( !data ) + return; /* Callers like to pass us pointers into ourselves, so be careful! I don't know if we can use operator= with a pointer to our interior, so use an intermediate. */ - size_t command_line_len = b.size(); + size_t command_line_len = b.size(); data->command_line = b; data->command_line_changed(); /* Don't set a position past the command line length */ if (pos > command_line_len) pos = command_line_len; - + data->buff_pos = pos; - data->search_mode = NO_SEARCH; - data->search_buff.clear(); - data->history_search.go_to_end(); + data->search_mode = NO_SEARCH; + data->search_buff.clear(); + data->history_search.go_to_end(); - reader_super_highlight_me_plenty( data->buff_pos ); - reader_repaint_needed(); + reader_super_highlight_me_plenty( data->buff_pos ); + reader_repaint_needed(); } size_t reader_get_cursor_pos() { - if( !data ) - return (size_t)(-1); + if( !data ) + return (size_t)(-1); - return data->buff_pos; + return data->buff_pos; } #define ENV_CMD_DURATION L"CMD_DURATION" void set_env_cmd_duration(struct timeval *after, struct timeval *before) { - time_t secs = after->tv_sec - before->tv_sec; - suseconds_t usecs = after->tv_usec - before->tv_usec; - wchar_t buf[16]; + time_t secs = after->tv_sec - before->tv_sec; + suseconds_t usecs = after->tv_usec - before->tv_usec; + wchar_t buf[16]; - if (after->tv_usec < before->tv_usec) { - usecs += 1000000; - secs -= 1; - } + if (after->tv_usec < before->tv_usec) { + usecs += 1000000; + secs -= 1; + } - if (secs < 1) { - env_remove( ENV_CMD_DURATION, 0 ); - } else { - if (secs < 10) { // 10 secs - swprintf(buf, 16, L"%lu.%02us", secs, usecs / 10000); - } else if (secs < 60) { // 1 min - swprintf(buf, 16, L"%lu.%01us", secs, usecs / 100000); - } else if (secs < 600) { // 10 mins - swprintf(buf, 16, L"%lum %lu.%01us", secs / 60, secs % 60, usecs / 100000); - } else if (secs < 5400) { // 1.5 hours - swprintf(buf, 16, L"%lum %lus", secs / 60, secs % 60); - } else { - swprintf(buf, 16, L"%.1fh", secs / 3600.0); - } - env_set( ENV_CMD_DURATION, buf, ENV_EXPORT ); - } + if (secs < 1) { + env_remove( ENV_CMD_DURATION, 0 ); + } else { + if (secs < 10) { // 10 secs + swprintf(buf, 16, L"%lu.%02us", secs, usecs / 10000); + } else if (secs < 60) { // 1 min + swprintf(buf, 16, L"%lu.%01us", secs, usecs / 100000); + } else if (secs < 600) { // 10 mins + swprintf(buf, 16, L"%lum %lu.%01us", secs / 60, secs % 60, usecs / 100000); + } else if (secs < 5400) { // 1.5 hours + swprintf(buf, 16, L"%lum %lus", secs / 60, secs % 60); + } else { + swprintf(buf, 16, L"%.1fh", secs / 3600.0); + } + env_set( ENV_CMD_DURATION, buf, ENV_EXPORT ); + } } void reader_run_command( parser_t &parser, const wchar_t *cmd ) { - wchar_t *ft; - struct timeval time_before, time_after; + wchar_t *ft; + struct timeval time_before, time_after; - ft= tok_first( cmd ); + ft= tok_first( cmd ); - if( ft != 0 ) - env_set( L"_", ft, ENV_GLOBAL ); - free(ft); + if( ft != 0 ) + env_set( L"_", ft, ENV_GLOBAL ); + free(ft); - reader_write_title(); + reader_write_title(); - term_donate(); + term_donate(); - gettimeofday(&time_before, NULL); + gettimeofday(&time_before, NULL); - parser.eval( cmd, io_chain_t(), TOP ); - job_reap( 1 ); + parser.eval( cmd, io_chain_t(), TOP ); + job_reap( 1 ); - gettimeofday(&time_after, NULL); - set_env_cmd_duration(&time_after, &time_before); + gettimeofday(&time_after, NULL); + set_env_cmd_duration(&time_after, &time_before); - term_steal(); + term_steal(); - env_set( L"_", program_name, ENV_GLOBAL ); + env_set( L"_", program_name, ENV_GLOBAL ); #ifdef HAVE__PROC_SELF_STAT - proc_update_jiffies(); + proc_update_jiffies(); #endif @@ -2141,16 +2141,16 @@ void reader_run_command( parser_t &parser, const wchar_t *cmd ) int reader_shell_test( const wchar_t *b ) { - int res = parser_t::principal_parser().test( b, 0, 0, 0 ); - - if( res & PARSER_TEST_ERROR ) - { - wcstring sb; + int res = parser_t::principal_parser().test( b, 0, 0, 0 ); - const int tmp[1] = {0}; - const int tmp2[1] = {0}; + if( res & PARSER_TEST_ERROR ) + { + wcstring sb; + + const int tmp[1] = {0}; + const int tmp2[1] = {0}; const wcstring empty; - + s_write( &data->screen, empty, empty, @@ -2159,12 +2159,12 @@ int reader_shell_test( const wchar_t *b ) tmp, tmp2, 0); - - - parser_t::principal_parser().test( b, 0, &sb, L"fish" ); - fwprintf( stderr, L"%ls", sb.c_str() ); - } - return res; + + + parser_t::principal_parser().test( b, 0, &sb, L"fish" ); + fwprintf( stderr, L"%ls", sb.c_str() ); + } + return res; } /** @@ -2174,58 +2174,58 @@ int reader_shell_test( const wchar_t *b ) */ static int default_test( const wchar_t *b ) { - return 0; + return 0; } void reader_push( const wchar_t *name ) { reader_data_t *n = new reader_data_t(); - + n->history = & history_t::history_with_name(name); - n->app_name = name; - n->next = data; + n->app_name = name; + n->next = data; - data=n; + data=n; - data->command_line_changed(); + data->command_line_changed(); - if( data->next == 0 ) - { - reader_interactive_init(); - } + if( data->next == 0 ) + { + reader_interactive_init(); + } - exec_prompt(); - reader_set_highlight_function( &highlight_universal ); - reader_set_test_function( &default_test ); - reader_set_left_prompt( L"" ); + exec_prompt(); + reader_set_highlight_function( &highlight_universal ); + reader_set_test_function( &default_test ); + reader_set_left_prompt( L"" ); } void reader_pop() { - reader_data_t *n = data; + reader_data_t *n = data; - if( data == 0 ) - { - debug( 0, _( L"Pop null reader block" ) ); - sanity_lose(); - return; - } + if( data == 0 ) + { + debug( 0, _( L"Pop null reader block" ) ); + sanity_lose(); + return; + } + + data=data->next; - data=data->next; - /* Invoke the destructor to balance our new */ delete n; - if( data == 0 ) - { - reader_interactive_destroy(); - } - else - { - end_loop = 0; - //history_set_mode( data->app_name.c_str() ); - s_reset( &data->screen, true); - } + if( data == 0 ) + { + reader_interactive_destroy(); + } + else + { + end_loop = 0; + //history_set_mode( data->app_name.c_str() ); + s_reset( &data->screen, true); + } } void reader_set_left_prompt( const wcstring &new_prompt ) @@ -2240,17 +2240,17 @@ void reader_set_right_prompt(const wcstring &new_prompt) void reader_set_complete_function( complete_function_t f ) { - data->complete_func = f; + data->complete_func = f; } void reader_set_highlight_function( highlight_function_t func ) { - data->highlight_function = func; + data->highlight_function = func; } void reader_set_test_function( int (*f)( const wchar_t * ) ) { - data->test_func = f; + data->test_func = f; } void reader_import_history_if_necessary(void) @@ -2276,26 +2276,26 @@ void reader_import_history_if_necessary(void) class background_highlight_context_t { public: /** The string to highlight */ - const wcstring string_to_highlight; - - /** Color buffer */ - std::vector colors; - - /** The position to use for bracket matching */ - const size_t match_highlight_pos; - - /** Function for syntax highlighting */ - const highlight_function_t highlight_function; - + const wcstring string_to_highlight; + + /** Color buffer */ + std::vector colors; + + /** The position to use for bracket matching */ + const size_t match_highlight_pos; + + /** Function for syntax highlighting */ + const highlight_function_t highlight_function; + /** Environment variables */ const env_vars_snapshot_t vars; /** When the request was made */ const double when; - + /** Gen count at the time the request was made */ const unsigned int generation_count; - + background_highlight_context_t(const wcstring &pbuff, size_t phighlight_pos, highlight_function_t phighlight_func) : string_to_highlight(pbuff), colors(pbuff.size(), 0), @@ -2306,7 +2306,7 @@ public: generation_count(s_generation_count) { } - + int threaded_highlight() { if (generation_count != s_generation_count) { @@ -2323,41 +2323,41 @@ public: /* Called to set the highlight flag for search results */ static void highlight_search(void) { - if( ! data->search_buff.empty() && ! data->history_search.is_at_end()) - { + if( ! data->search_buff.empty() && ! data->history_search.is_at_end()) + { const wchar_t *buff = data->command_line.c_str(); - const wchar_t *match = wcsstr( buff, data->search_buff.c_str() ); - if( match ) - { - size_t start = match-buff; - size_t i, count = data->search_buff.size(); - for( i=0; icolors.at(start+i) |= HIGHLIGHT_SEARCH_MATCH<<16; - } - } - } + const wchar_t *match = wcsstr( buff, data->search_buff.c_str() ); + if( match ) + { + size_t start = match-buff; + size_t i, count = data->search_buff.size(); + for( i=0; icolors.at(start+i) |= HIGHLIGHT_SEARCH_MATCH<<16; + } + } + } } static void highlight_complete(background_highlight_context_t *ctx, int result) { ASSERT_IS_MAIN_THREAD(); - if (ctx->string_to_highlight == data->command_line) { - /* The data hasn't changed, so swap in our colors. The colors may not have changed, so do nothing if they have not. */ + if (ctx->string_to_highlight == data->command_line) { + /* The data hasn't changed, so swap in our colors. The colors may not have changed, so do nothing if they have not. */ assert(ctx->colors.size() == data->command_length()); if (data->colors != ctx->colors) { data->colors.swap(ctx->colors); - + //data->repaint_needed = 1; //s_reset( &data->screen, 1 ); - + sanity_check(); highlight_search(); reader_repaint(); } - } - - /* Free our context */ + } + + /* Free our context */ delete ctx; } @@ -2378,11 +2378,11 @@ static int threaded_highlight(background_highlight_context_t *ctx) { static void reader_super_highlight_me_plenty( size_t match_highlight_pos ) { reader_sanity_check(); - - background_highlight_context_t *ctx = new background_highlight_context_t(data->command_line, match_highlight_pos, data->highlight_function); - iothread_perform(threaded_highlight, highlight_complete, ctx); + + background_highlight_context_t *ctx = new background_highlight_context_t(data->command_line, match_highlight_pos, data->highlight_function); + iothread_perform(threaded_highlight, highlight_complete, ctx); highlight_search(); - + /* Here's a hack. Check to see if our autosuggestion still applies; if so, don't recompute it. Since the autosuggestion computation is asynchronous, this avoids "flashing" as you type into the autosuggestion. */ const wcstring &cmd = data->command_line, &suggest = data->autosuggestion; if (can_autosuggest() && ! suggest.empty() && string_prefixes_string_case_insensitive(cmd, suggest)) { @@ -2395,10 +2395,10 @@ static void reader_super_highlight_me_plenty( size_t match_highlight_pos ) int exit_status() { - if( get_is_interactive() ) - return job_list_is_empty() && data->end_loop; - else - return end_loop; + if( get_is_interactive() ) + return job_list_is_empty() && data->end_loop; + else + return end_loop; } /** @@ -2409,63 +2409,63 @@ int exit_status() static void handle_end_loop() { - job_t *j; - int job_count=0; - int is_breakpoint=0; - block_t *b; - parser_t &parser = parser_t::principal_parser(); - - for( b = parser.current_block; - b; - b = b->outer ) - { - if( b->type() == BREAKPOINT ) - { - is_breakpoint = 1; - break; - } - } - + job_t *j; + int job_count=0; + int is_breakpoint=0; + block_t *b; + parser_t &parser = parser_t::principal_parser(); + + for( b = parser.current_block; + b; + b = b->outer ) + { + if( b->type() == BREAKPOINT ) + { + is_breakpoint = 1; + break; + } + } + job_iterator_t jobs; while ((j = jobs.next())) - { - if( !job_is_completed(j) ) - { - job_count++; - break; - } - } - - if( !reader_exit_forced() && !data->prev_end_loop && job_count && !is_breakpoint ) - { - writestr(_( L"There are stopped jobs. A second attempt to exit will enforce their termination.\n" )); - - reader_exit( 0, 0 ); - data->prev_end_loop=1; - } - else - { + { + if( !job_is_completed(j) ) + { + job_count++; + break; + } + } + + if( !reader_exit_forced() && !data->prev_end_loop && job_count && !is_breakpoint ) + { + writestr(_( L"There are stopped jobs. A second attempt to exit will enforce their termination.\n" )); + + reader_exit( 0, 0 ); + data->prev_end_loop=1; + } + else + { /* PCA: we used to only hangup jobs if stdin was closed. This prevented child processes from exiting. It's unclear to my why it matters if stdin is closed, but it seems to me if we're forcing an exit, we definitely want to hang up our processes. - + See https://github.com/fish-shell/fish-shell/issues/138 */ - if( reader_exit_forced() || !isatty(0) ) - { - /* - We already know that stdin is a tty since we're - in interactive mode. If isatty returns false, it - means stdin must have been closed. - */ - job_iterator_t jobs; - while ((j = jobs.next())) - { - if( ! job_is_completed( j ) ) - { - job_signal( j, SIGHUP ); - } - } - } - } + if( reader_exit_forced() || !isatty(0) ) + { + /* + We already know that stdin is a tty since we're + in interactive mode. If isatty returns false, it + means stdin must have been closed. + */ + job_iterator_t jobs; + while ((j = jobs.next())) + { + if( ! job_is_completed( j ) ) + { + job_signal( j, SIGHUP ); + } + } + } + } } @@ -2476,69 +2476,69 @@ static void handle_end_loop() */ static int read_i() { - reader_push(L"fish"); - reader_set_complete_function( &complete ); - reader_set_highlight_function( &highlight_shell ); - reader_set_test_function( &reader_shell_test ); + reader_push(L"fish"); + reader_set_complete_function( &complete ); + reader_set_highlight_function( &highlight_shell ); + reader_set_test_function( &reader_shell_test ); reader_import_history_if_necessary(); - - parser_t &parser = parser_t::principal_parser(); - - data->prev_end_loop=0; - while( (!data->end_loop) && (!sanity_check()) ) - { - const wchar_t *tmp; + parser_t &parser = parser_t::principal_parser(); - event_fire_generic(L"fish_prompt"); - if( function_exists( LEFT_PROMPT_FUNCTION_NAME ) ) - reader_set_left_prompt( LEFT_PROMPT_FUNCTION_NAME ); - else - reader_set_left_prompt( DEFAULT_PROMPT ); - - if( function_exists( RIGHT_PROMPT_FUNCTION_NAME ) ) - reader_set_right_prompt( RIGHT_PROMPT_FUNCTION_NAME ); - else - reader_set_right_prompt( L"" ); - + data->prev_end_loop=0; - /* - Put buff in temporary string and clear buff, so - that we can handle a call to reader_set_buffer - during evaluation. - */ + while( (!data->end_loop) && (!sanity_check()) ) + { + const wchar_t *tmp; - - tmp = reader_readline(); - + event_fire_generic(L"fish_prompt"); + if( function_exists( LEFT_PROMPT_FUNCTION_NAME ) ) + reader_set_left_prompt( LEFT_PROMPT_FUNCTION_NAME ); + else + reader_set_left_prompt( DEFAULT_PROMPT ); - if( data->end_loop) - { - handle_end_loop(); - } - else if( tmp ) - { - tmp = wcsdup( tmp ); - - data->buff_pos=0; + if( function_exists( RIGHT_PROMPT_FUNCTION_NAME ) ) + reader_set_right_prompt( RIGHT_PROMPT_FUNCTION_NAME ); + else + reader_set_right_prompt( L"" ); + + + /* + Put buff in temporary string and clear buff, so + that we can handle a call to reader_set_buffer + during evaluation. + */ + + + tmp = reader_readline(); + + + if( data->end_loop) + { + handle_end_loop(); + } + else if( tmp ) + { + tmp = wcsdup( tmp ); + + data->buff_pos=0; data->command_line.clear(); data->command_line_changed(); - reader_run_command( parser, tmp ); - free( (void *)tmp ); - if( data->end_loop) - { - handle_end_loop(); - } - else - { - data->prev_end_loop=0; - } - } - + reader_run_command( parser, tmp ); + free( (void *)tmp ); + if( data->end_loop) + { + handle_end_loop(); + } + else + { + data->prev_end_loop=0; + } + } - } - reader_pop(); - return 0; + + } + reader_pop(); + return 0; } /** @@ -2547,10 +2547,10 @@ static int read_i() */ static int can_read( int fd ) { - struct timeval can_read_timeout = { 0, 0 }; - fd_set fds; + struct timeval can_read_timeout = { 0, 0 }; + fd_set fds; - FD_ZERO(&fds); + FD_ZERO(&fds); FD_SET(fd, &fds); return select(fd + 1, &fds, 0, 0, &can_read_timeout) == 1; } @@ -2561,7 +2561,7 @@ static int can_read( int fd ) */ static int wchar_private( wchar_t c ) { - return ( (c >= 0xe000) && (c <= 0xf8ff ) ); + return ( (c >= 0xe000) && (c <= 0xf8ff ) ); } /** @@ -2570,191 +2570,191 @@ static int wchar_private( wchar_t c ) */ static bool is_backslashed( const wchar_t *str, size_t pos ) { - size_t count = 0; - size_t idx = pos; - while (idx--) - { - if( str[idx] != L'\\' ) - break; - - count++; - } + size_t count = 0; + size_t idx = pos; + while (idx--) + { + if( str[idx] != L'\\' ) + break; - return (count % 2) == 1; + count++; + } + + return (count % 2) == 1; } const wchar_t *reader_readline() { - - wint_t c; - int last_char=0; + + wint_t c; + int last_char=0; size_t yank_len=0; - const wchar_t *yank_str; - bool comp_empty = true; - std::vector comp; - int finished=0; - struct termios old_modes; + const wchar_t *yank_str; + bool comp_empty = true; + std::vector comp; + int finished=0; + struct termios old_modes; /* The cycle index in our completion list */ size_t completion_cycle_idx = (size_t)(-1); - + /* The command line before completion */ wcstring cycle_command_line; size_t cycle_cursor_pos; - - data->search_buff.clear(); - data->search_mode = NO_SEARCH; - - - exec_prompt(); - - reader_super_highlight_me_plenty( data->buff_pos ); - s_reset( &data->screen, true); - reader_repaint(); - - /* + + data->search_buff.clear(); + data->search_mode = NO_SEARCH; + + + exec_prompt(); + + reader_super_highlight_me_plenty( data->buff_pos ); + s_reset( &data->screen, true); + reader_repaint(); + + /* get the current terminal modes. These will be restored when the function returns. */ - tcgetattr(0,&old_modes); - /* set the new modes */ - if( tcsetattr(0,TCSANOW,&shell_modes)) - { - wperror(L"tcsetattr"); + tcgetattr(0,&old_modes); + /* set the new modes */ + if( tcsetattr(0,TCSANOW,&shell_modes)) + { + wperror(L"tcsetattr"); } - - while( !finished && !data->end_loop) - { - /* + + while( !finished && !data->end_loop) + { + /* Sometimes strange input sequences seem to generate a zero byte. I believe these simply mean a character was pressed but it should be ignored. (Example: Trying to add a tilde (~) to digit) */ - while( 1 ) - { - int was_interactive_read = is_interactive_read; - is_interactive_read = 1; - c=input_readch(); - is_interactive_read = was_interactive_read; - - if( ( (!wchar_private(c))) && (c>31) && (c != 127) ) - { - if( can_read(0) ) - { - - wchar_t arr[READAHEAD_MAX+1]; - int i; - - memset( arr, 0, sizeof( arr ) ); - arr[0] = c; - - for( i=1; i31) && (c != 127) ) - { - arr[i]=c; - c=0; - } - else - break; - } - - insert_string( arr ); - - } - } - - if( c != 0 ) - break; - } + while( 1 ) + { + int was_interactive_read = is_interactive_read; + is_interactive_read = 1; + c=input_readch(); + is_interactive_read = was_interactive_read; - if( last_char != R_YANK && last_char != R_YANK_POP ) - yank_len=0; + if( ( (!wchar_private(c))) && (c>31) && (c != 127) ) + { + if( can_read(0) ) + { + + wchar_t arr[READAHEAD_MAX+1]; + int i; + + memset( arr, 0, sizeof( arr ) ); + arr[0] = c; + + for( i=1; i31) && (c != 127) ) + { + arr[i]=c; + c=0; + } + else + break; + } + + insert_string( arr ); + + } + } + + if( c != 0 ) + break; + } + + if( last_char != R_YANK && last_char != R_YANK_POP ) + yank_len=0; const wchar_t *buff = data->command_line.c_str(); - switch( c ) - { - + switch( c ) + { + /* go to beginning of line*/ - case R_BEGINNING_OF_LINE: - { - while( ( data->buff_pos>0 ) && + case R_BEGINNING_OF_LINE: + { + while( ( data->buff_pos>0 ) && ( buff[data->buff_pos-1] != L'\n' ) ) - { - data->buff_pos--; - } - - reader_repaint(); - break; - } - - case R_END_OF_LINE: - { - while( buff[data->buff_pos] && + { + data->buff_pos--; + } + + reader_repaint(); + break; + } + + case R_END_OF_LINE: + { + while( buff[data->buff_pos] && buff[data->buff_pos] != L'\n' ) - { - data->buff_pos++; - } - - reader_repaint(); - break; - } - - - case R_BEGINNING_OF_BUFFER: - { - data->buff_pos = 0; - - reader_repaint(); - break; - } - + { + data->buff_pos++; + } + + reader_repaint(); + break; + } + + + case R_BEGINNING_OF_BUFFER: + { + data->buff_pos = 0; + + reader_repaint(); + break; + } + /* go to EOL*/ - case R_END_OF_BUFFER: - { - data->buff_pos = data->command_length(); - - reader_repaint(); - break; - } - - case R_NULL: - { - reader_repaint_if_needed(); - break; - } - - case R_REPAINT: - { - exec_prompt(); - write_loop( 1, "\r", 1 ); - s_reset( &data->screen, false); - reader_repaint(); - break; - } - - case R_EOF: - { - exit_forced = 1; - data->end_loop=1; - break; - } - + case R_END_OF_BUFFER: + { + data->buff_pos = data->command_length(); + + reader_repaint(); + break; + } + + case R_NULL: + { + reader_repaint_if_needed(); + break; + } + + case R_REPAINT: + { + exec_prompt(); + write_loop( 1, "\r", 1 ); + s_reset( &data->screen, false); + reader_repaint(); + break; + } + + case R_EOF: + { + exit_forced = 1; + data->end_loop=1; + break; + } + /* complete */ - case R_COMPLETE: - { - - if( !data->complete_func ) - break; - + case R_COMPLETE: + { + + if( !data->complete_func ) + break; + if (! comp_empty && last_char == R_COMPLETE) { /* The user typed R_COMPLETE more than once in a row. Cycle through our available completions */ @@ -2764,541 +2764,541 @@ const wchar_t *reader_readline() size_t cursor_pos = cycle_cursor_pos; const wcstring new_cmd_line = completion_apply_to_command_line(next_comp->completion, next_comp->flags, cycle_command_line, &cursor_pos); reader_set_buffer(new_cmd_line, cursor_pos); - + /* Since we just inserted a completion, don't immediately do a new autosuggestion */ data->suppress_autosuggestion = true; } } - else - { + else + { /* Either the user hit tab only once, or we had no visible completion list. */ - const wchar_t *begin, *end; - const wchar_t *token_begin, *token_end; + const wchar_t *begin, *end; + const wchar_t *token_begin, *token_end; const wchar_t *buff = data->command_line.c_str(); - long cursor_steps; - + long cursor_steps; + /* Clear the completion list */ comp.clear(); - - parse_util_cmdsubst_extent( buff, data->buff_pos, &begin, &end ); - - parse_util_token_extent( begin, data->buff_pos - (begin-buff), &token_begin, &token_end, 0, 0 ); - - cursor_steps = token_end - buff- data->buff_pos; - data->buff_pos += cursor_steps; - if( is_backslashed( buff, data->buff_pos ) ) - { - remove_backward(); - } - - reader_repaint(); - - size_t len = data->buff_pos - (begin-buff); + + parse_util_cmdsubst_extent( buff, data->buff_pos, &begin, &end ); + + parse_util_token_extent( begin, data->buff_pos - (begin-buff), &token_begin, &token_end, 0, 0 ); + + cursor_steps = token_end - buff- data->buff_pos; + data->buff_pos += cursor_steps; + if( is_backslashed( buff, data->buff_pos ) ) + { + remove_backward(); + } + + reader_repaint(); + + size_t len = data->buff_pos - (begin-buff); const wcstring buffcpy = wcstring(begin, len); - - data->complete_func( buffcpy, comp, COMPLETE_DEFAULT, NULL); - - /* Munge our completions */ - sort(comp.begin(), comp.end()); - remove_duplicates( comp ); - prioritize_completions(comp); - - /* Record our cycle_command_line */ - cycle_command_line = data->command_line; - cycle_cursor_pos = data->buff_pos; - - comp_empty = handle_completions( comp ); - - /* Start the cycle at the beginning */ - completion_cycle_idx = (size_t)(-1); - } - - break; - } - + + data->complete_func( buffcpy, comp, COMPLETE_DEFAULT, NULL); + + /* Munge our completions */ + sort(comp.begin(), comp.end()); + remove_duplicates( comp ); + prioritize_completions(comp); + + /* Record our cycle_command_line */ + cycle_command_line = data->command_line; + cycle_cursor_pos = data->buff_pos; + + comp_empty = handle_completions( comp ); + + /* Start the cycle at the beginning */ + completion_cycle_idx = (size_t)(-1); + } + + break; + } + /* kill */ - case R_KILL_LINE: - { + case R_KILL_LINE: + { const wchar_t *buff = data->command_line.c_str(); - const wchar_t *begin = &buff[data->buff_pos]; - const wchar_t *end = begin; - - while( *end && *end != L'\n' ) - end++; - - if( end==begin && *end ) - end++; - - size_t len = end-begin; - if( len ) - { - reader_kill( begin - buff, len, KILL_APPEND, last_char!=R_KILL_LINE ); - } - - break; - } - - case R_BACKWARD_KILL_LINE: - { - if( data->buff_pos > 0 ) - { + const wchar_t *begin = &buff[data->buff_pos]; + const wchar_t *end = begin; + + while( *end && *end != L'\n' ) + end++; + + if( end==begin && *end ) + end++; + + size_t len = end-begin; + if( len ) + { + reader_kill( begin - buff, len, KILL_APPEND, last_char!=R_KILL_LINE ); + } + + break; + } + + case R_BACKWARD_KILL_LINE: + { + if( data->buff_pos > 0 ) + { const wchar_t *buff = data->command_line.c_str(); - const wchar_t *end = &buff[data->buff_pos]; - const wchar_t *begin = end; - - while( begin > buff && *begin != L'\n' ) - begin--; - - if( *begin == L'\n' ) - begin++; - - size_t len = maxi( end-begin, 1 ); - begin = end - len; - - reader_kill( begin - buff, len, KILL_PREPEND, last_char!=R_BACKWARD_KILL_LINE ); - - } - break; - - } - - case R_KILL_WHOLE_LINE: - { + const wchar_t *end = &buff[data->buff_pos]; + const wchar_t *begin = end; + + while( begin > buff && *begin != L'\n' ) + begin--; + + if( *begin == L'\n' ) + begin++; + + size_t len = maxi( end-begin, 1 ); + begin = end - len; + + reader_kill( begin - buff, len, KILL_PREPEND, last_char!=R_BACKWARD_KILL_LINE ); + + } + break; + + } + + case R_KILL_WHOLE_LINE: + { const wchar_t *buff = data->command_line.c_str(); - const wchar_t *end = &buff[data->buff_pos]; - const wchar_t *begin = end; - size_t len; - - while( begin > buff && *begin != L'\n' ) - begin--; - - if( *begin == L'\n' ) - begin++; - - len = maxi( end-begin, 0 ); - begin = end - len; - - while( *end && *end != L'\n' ) - end++; - - if( begin == end && *end ) - end++; - - len = end-begin; - - if( len ) - { - reader_kill( begin - buff, len, KILL_APPEND, last_char!=R_KILL_WHOLE_LINE ); - } - - break; - } - + const wchar_t *end = &buff[data->buff_pos]; + const wchar_t *begin = end; + size_t len; + + while( begin > buff && *begin != L'\n' ) + begin--; + + if( *begin == L'\n' ) + begin++; + + len = maxi( end-begin, 0 ); + begin = end - len; + + while( *end && *end != L'\n' ) + end++; + + if( begin == end && *end ) + end++; + + len = end-begin; + + if( len ) + { + reader_kill( begin - buff, len, KILL_APPEND, last_char!=R_KILL_WHOLE_LINE ); + } + + break; + } + /* yank*/ - case R_YANK: - { - yank_str = kill_yank(); - insert_string( yank_str ); - yank_len = wcslen( yank_str ); - break; - } - + case R_YANK: + { + yank_str = kill_yank(); + insert_string( yank_str ); + yank_len = wcslen( yank_str ); + break; + } + /* rotate killring*/ - case R_YANK_POP: - { - if( yank_len ) - { - for( size_t i=0; isearch_mode ) - { - data->search_mode= NO_SEARCH; - - if( data->token_history_pos==-1 ) - { - //history_reset(); + case L'\x1b': + { + if( data->search_mode ) + { + data->search_mode= NO_SEARCH; + + if( data->token_history_pos==-1 ) + { + //history_reset(); data->history_search.go_to_end(); - reader_set_buffer( data->search_buff.c_str(), data->search_buff.size() ); - } - else - { - reader_replace_current_token( data->search_buff.c_str() ); - } - data->search_buff.clear(); - reader_super_highlight_me_plenty( data->buff_pos ); - reader_repaint(); - - } - - break; - } - + reader_set_buffer( data->search_buff.c_str(), data->search_buff.size() ); + } + else + { + reader_replace_current_token( data->search_buff.c_str() ); + } + data->search_buff.clear(); + reader_super_highlight_me_plenty( data->buff_pos ); + reader_repaint(); + + } + + break; + } + /* delete backward*/ - case R_BACKWARD_DELETE_CHAR: - { - remove_backward(); - break; - } - + case R_BACKWARD_DELETE_CHAR: + { + remove_backward(); + break; + } + /* delete forward*/ - case R_DELETE_CHAR: - { - /** + case R_DELETE_CHAR: + { + /** Remove the current character in the character buffer and on the screen using syntax highlighting, etc. */ - if( data->buff_pos < data->command_length() ) - { - data->buff_pos++; - remove_backward(); - } - break; - } - + if( data->buff_pos < data->command_length() ) + { + data->buff_pos++; + remove_backward(); + } + break; + } + /* Evaluate. If the current command is unfinished, or if the charater is escaped using a backslash, insert a newline */ - case R_EXECUTE: - { + case R_EXECUTE: + { /* Delete any autosuggestion */ data->autosuggestion.clear(); - - /* + + /* Allow backslash-escaped newlines */ - if( is_backslashed( data->command_line.c_str(), data->buff_pos ) ) - { - insert_char( '\n' ); - break; - } - - switch( data->test_func( data->command_line.c_str() ) ) - { - - case 0: - { - /* + if( is_backslashed( data->command_line.c_str(), data->buff_pos ) ) + { + insert_char( '\n' ); + break; + } + + switch( data->test_func( data->command_line.c_str() ) ) + { + + case 0: + { + /* Finished commend, execute it */ - if( ! data->command_line.empty() ) - { + if( ! data->command_line.empty() ) + { if (data->history) { data->history->add_with_file_detection(data->command_line); } - } - finished=1; - data->buff_pos=data->command_length(); - reader_repaint(); - break; - } - + } + finished=1; + data->buff_pos=data->command_length(); + reader_repaint(); + break; + } + /* We are incomplete, continue editing */ - case PARSER_TEST_INCOMPLETE: - { - insert_char( '\n' ); - break; - } - + case PARSER_TEST_INCOMPLETE: + { + insert_char( '\n' ); + break; + } + /* Result must be some combination including an error. The error message will already be printed, all we need to do is repaint */ - default: - { - s_reset( &data->screen, true); - reader_repaint(); - break; - } - - } - - break; - } - + default: + { + s_reset( &data->screen, true); + reader_repaint(); + break; + } + + } + + break; + } + /* History functions */ - case R_HISTORY_SEARCH_BACKWARD: - case R_HISTORY_TOKEN_SEARCH_BACKWARD: - case R_HISTORY_SEARCH_FORWARD: - case R_HISTORY_TOKEN_SEARCH_FORWARD: - { - int reset = 0; - - if( data->search_mode == NO_SEARCH ) - { - reset = 1; - if( ( c == R_HISTORY_SEARCH_BACKWARD ) || + case R_HISTORY_SEARCH_BACKWARD: + case R_HISTORY_TOKEN_SEARCH_BACKWARD: + case R_HISTORY_SEARCH_FORWARD: + case R_HISTORY_TOKEN_SEARCH_FORWARD: + { + int reset = 0; + + if( data->search_mode == NO_SEARCH ) + { + reset = 1; + if( ( c == R_HISTORY_SEARCH_BACKWARD ) || ( c == R_HISTORY_SEARCH_FORWARD ) ) - { - data->search_mode = LINE_SEARCH; - } - else - { - data->search_mode = TOKEN_SEARCH; - } - + { + data->search_mode = LINE_SEARCH; + } + else + { + data->search_mode = TOKEN_SEARCH; + } + data->search_buff.append(data->command_line); data->history_search = history_search_t(*data->history, data->search_buff, HISTORY_SEARCH_TYPE_CONTAINS); - + /* Skip the autosuggestion as history */ const wcstring &suggest = data->autosuggestion; if (! suggest.empty()) { data->history_search.skip_matches(wcstring_list_t(&suggest, 1 + &suggest)); } - } - - switch( data->search_mode ) - { - - case LINE_SEARCH: - { - if( ( c == R_HISTORY_SEARCH_BACKWARD ) || + } + + switch( data->search_mode ) + { + + case LINE_SEARCH: + { + if( ( c == R_HISTORY_SEARCH_BACKWARD ) || ( c == R_HISTORY_TOKEN_SEARCH_BACKWARD ) ) - { - data->history_search.go_backwards(); - } - else - { - if (! data->history_search.go_forwards()) { + { + data->history_search.go_backwards(); + } + else + { + if (! data->history_search.go_forwards()) { /* If you try to go forwards past the end, we just go to the end */ data->history_search.go_to_end(); } - } - + } + wcstring new_text; if (data->history_search.is_at_end()) { new_text = data->search_buff; } else { new_text = data->history_search.current_string(); } - set_command_line_and_position(new_text, new_text.size()); - - break; - } - - case TOKEN_SEARCH: - { - if( ( c == R_HISTORY_SEARCH_BACKWARD ) || + set_command_line_and_position(new_text, new_text.size()); + + break; + } + + case TOKEN_SEARCH: + { + if( ( c == R_HISTORY_SEARCH_BACKWARD ) || ( c == R_HISTORY_TOKEN_SEARCH_BACKWARD ) ) - { - handle_token_history( SEARCH_BACKWARD, reset ); - } - else - { - handle_token_history( SEARCH_FORWARD, reset ); - } - - break; - } - - } - break; - } - - + { + handle_token_history( SEARCH_BACKWARD, reset ); + } + else + { + handle_token_history( SEARCH_FORWARD, reset ); + } + + break; + } + + } + break; + } + + /* Move left*/ - case R_BACKWARD_CHAR: - { - if( data->buff_pos > 0 ) - { - data->buff_pos--; - reader_repaint(); - } - break; - } - + case R_BACKWARD_CHAR: + { + if( data->buff_pos > 0 ) + { + data->buff_pos--; + reader_repaint(); + } + break; + } + /* Move right*/ - case R_FORWARD_CHAR: - { - if( data->buff_pos < data->command_length() ) - { - data->buff_pos++; - reader_repaint(); - } else { + case R_FORWARD_CHAR: + { + if( data->buff_pos < data->command_length() ) + { + data->buff_pos++; + reader_repaint(); + } else { accept_autosuggestion(); } - break; - } - + break; + } + /* kill one word left */ - case R_BACKWARD_KILL_WORD: - { - move_word(MOVE_DIR_LEFT, true /* erase */, last_char!=R_BACKWARD_KILL_WORD); - break; - } - + case R_BACKWARD_KILL_WORD: + { + move_word(MOVE_DIR_LEFT, true /* erase */, last_char!=R_BACKWARD_KILL_WORD); + break; + } + /* kill one word right */ - case R_KILL_WORD: - { - move_word(MOVE_DIR_RIGHT, true /* erase */, last_char!=R_KILL_WORD); - break; - } - + case R_KILL_WORD: + { + move_word(MOVE_DIR_RIGHT, true /* erase */, last_char!=R_KILL_WORD); + break; + } + /* move one word left*/ - case R_BACKWARD_WORD: - { - move_word(MOVE_DIR_LEFT, false /* do not erase */, false); - break; - } - + case R_BACKWARD_WORD: + { + move_word(MOVE_DIR_LEFT, false /* do not erase */, false); + break; + } + /* move one word right*/ - case R_FORWARD_WORD: - { - move_word(MOVE_DIR_RIGHT, false /* do not erase */, false); - break; - } - - case R_BEGINNING_OF_HISTORY: - { + case R_FORWARD_WORD: + { + move_word(MOVE_DIR_RIGHT, false /* do not erase */, false); + break; + } + + case R_BEGINNING_OF_HISTORY: + { data->history_search = history_search_t(*data->history, data->command_line, HISTORY_SEARCH_TYPE_PREFIX); data->history_search.go_to_beginning(); if (! data->history_search.is_at_end()) { wcstring new_text = data->history_search.current_string(); set_command_line_and_position(new_text, new_text.size()); } - - break; - } - - case R_END_OF_HISTORY: - { - data->history_search.go_to_end(); - break; - } - - case R_UP_LINE: - case R_DOWN_LINE: - { - int line_old = parse_util_get_line_from_offset( data->command_line, data->buff_pos ); - int line_new; - - if( c == R_UP_LINE ) - line_new = line_old-1; - else - line_new = line_old+1; - - int line_count = parse_util_lineno( data->command_line.c_str(), data->command_length() )-1; - - if( line_new >= 0 && line_new <= line_count) - { - size_t base_pos_new; - size_t base_pos_old; - - int indent_old; - int indent_new; - size_t line_offset_old; - size_t total_offset_new; - - base_pos_new = parse_util_get_offset_from_line( data->command_line, line_new ); - - base_pos_old = parse_util_get_offset_from_line( data->command_line, line_old ); - - assert(base_pos_new != (size_t)(-1) && base_pos_old != (size_t)(-1)); - indent_old = data->indents.at(base_pos_old); - indent_new = data->indents.at(base_pos_new); - - line_offset_old = data->buff_pos - parse_util_get_offset_from_line( data->command_line, line_old ); - total_offset_new = parse_util_get_offset( data->command_line, line_new, line_offset_old - 4*(indent_new-indent_old)); - data->buff_pos = total_offset_new; - reader_repaint(); - } - - break; - } - - case R_SUPPRESS_AUTOSUGGESTION: - { - data->suppress_autosuggestion = true; - data->autosuggestion.clear(); - reader_repaint(); - break; - } - - case R_ACCEPT_AUTOSUGGESTION: - { - accept_autosuggestion(); - break; - } - + + break; + } + + case R_END_OF_HISTORY: + { + data->history_search.go_to_end(); + break; + } + + case R_UP_LINE: + case R_DOWN_LINE: + { + int line_old = parse_util_get_line_from_offset( data->command_line, data->buff_pos ); + int line_new; + + if( c == R_UP_LINE ) + line_new = line_old-1; + else + line_new = line_old+1; + + int line_count = parse_util_lineno( data->command_line.c_str(), data->command_length() )-1; + + if( line_new >= 0 && line_new <= line_count) + { + size_t base_pos_new; + size_t base_pos_old; + + int indent_old; + int indent_new; + size_t line_offset_old; + size_t total_offset_new; + + base_pos_new = parse_util_get_offset_from_line( data->command_line, line_new ); + + base_pos_old = parse_util_get_offset_from_line( data->command_line, line_old ); + + assert(base_pos_new != (size_t)(-1) && base_pos_old != (size_t)(-1)); + indent_old = data->indents.at(base_pos_old); + indent_new = data->indents.at(base_pos_new); + + line_offset_old = data->buff_pos - parse_util_get_offset_from_line( data->command_line, line_old ); + total_offset_new = parse_util_get_offset( data->command_line, line_new, line_offset_old - 4*(indent_new-indent_old)); + data->buff_pos = total_offset_new; + reader_repaint(); + } + + break; + } + + case R_SUPPRESS_AUTOSUGGESTION: + { + data->suppress_autosuggestion = true; + data->autosuggestion.clear(); + reader_repaint(); + break; + } + + case R_ACCEPT_AUTOSUGGESTION: + { + accept_autosuggestion(); + break; + } + /* Other, if a normal character, we add it to the command */ - default: - { - - if( (!wchar_private(c)) && (( (c>31) || (c==L'\n'))&& (c != 127)) ) - { + default: + { + + if( (!wchar_private(c)) && (( (c>31) || (c==L'\n'))&& (c != 127)) ) + { /* Regular character */ - insert_char( c ); - } - else - { - /* + insert_char( c ); + } + else + { + /* Low priority debug message. These can happen if the user presses an unefined control sequnece. No reason to report. */ - debug( 2, _( L"Unknown keybinding %d" ), c ); - } - break; - } - - } - - if( (c != R_HISTORY_SEARCH_BACKWARD) && + debug( 2, _( L"Unknown keybinding %d" ), c ); + } + break; + } + + } + + if( (c != R_HISTORY_SEARCH_BACKWARD) && (c != R_HISTORY_SEARCH_FORWARD) && (c != R_HISTORY_TOKEN_SEARCH_BACKWARD) && (c != R_HISTORY_TOKEN_SEARCH_FORWARD) && (c != R_NULL) ) - { - data->search_mode = NO_SEARCH; - data->search_buff.clear(); - data->history_search.go_to_end(); - data->token_history_pos=-1; - } - - last_char = c; - } - - writestr( L"\n" ); + { + data->search_mode = NO_SEARCH; + data->search_buff.clear(); + data->history_search.go_to_end(); + data->token_history_pos=-1; + } + + last_char = c; + } + + writestr( L"\n" ); /* if( comp ) halloc_free( comp ); */ - if( !reader_exit_forced() ) - { - if( tcsetattr(0,TCSANOW,&old_modes)) /* return to previous mode */ - { - wperror(L"tcsetattr"); - } - - set_color( rgb_color_t::reset(), rgb_color_t::reset() ); - } - - return finished ? data->command_line.c_str() : 0; + if( !reader_exit_forced() ) + { + if( tcsetattr(0,TCSANOW,&old_modes)) /* return to previous mode */ + { + wperror(L"tcsetattr"); + } + + set_color( rgb_color_t::reset(), rgb_color_t::reset() ); + } + + return finished ? data->command_line.c_str() : 0; } int reader_search_mode() { - if( !data ) - { - return -1; - } - - return !!data->search_mode; + if( !data ) + { + return -1; + } + + return !!data->search_mode; } @@ -3310,123 +3310,123 @@ int reader_search_mode() static int read_ni( int fd, const io_chain_t &io ) { parser_t &parser = parser_t::principal_parser(); - FILE *in_stream; - wchar_t *buff=0; - std::vector acc; + FILE *in_stream; + wchar_t *buff=0; + std::vector acc; - int des = fd == 0 ? dup(0) : fd; - int res=0; + int des = fd == 0 ? dup(0) : fd; + int res=0; - if (des == -1) - { - wperror( L"dup" ); - return 1; - } + if (des == -1) + { + wperror( L"dup" ); + return 1; + } - in_stream = fdopen( des, "r" ); - if( in_stream != 0 ) - { - wchar_t *str; - size_t acc_used; + in_stream = fdopen( des, "r" ); + if( in_stream != 0 ) + { + wchar_t *str; + size_t acc_used; - while(!feof( in_stream )) - { - char buff[4096]; - size_t c = fread(buff, 1, 4096, in_stream); - - if( ferror( in_stream ) && ( errno != EINTR ) ) - { - debug( 1, - _( L"Error while reading from file descriptor" ) ); - - /* - Reset buffer on error. We won't evaluate incomplete files. - */ - acc.clear(); - break; - - } + while(!feof( in_stream )) + { + char buff[4096]; + size_t c = fread(buff, 1, 4096, in_stream); - acc.insert(acc.end(), buff, buff + c); - } + if( ferror( in_stream ) && ( errno != EINTR ) ) + { + debug( 1, + _( L"Error while reading from file descriptor" ) ); + + /* + Reset buffer on error. We won't evaluate incomplete files. + */ + acc.clear(); + break; + + } + + acc.insert(acc.end(), buff, buff + c); + } acc.push_back(0); - acc_used = acc.size(); - str = str2wcs(&acc.at(0)); + acc_used = acc.size(); + str = str2wcs(&acc.at(0)); acc.clear(); - if( fclose( in_stream )) - { - debug( 1, - _( L"Error while closing input stream" ) ); - wperror( L"fclose" ); - res = 1; - } + if( fclose( in_stream )) + { + debug( 1, + _( L"Error while closing input stream" ) ); + wperror( L"fclose" ); + res = 1; + } - if( str ) - { - wcstring sb; - if( ! parser.test( str, 0, &sb, L"fish" ) ) - { - parser.eval( str, io, TOP ); - } - else - { - fwprintf( stderr, L"%ls", sb.c_str() ); - res = 1; - } - free( str ); - } - else - { - if( acc_used > 1 ) - { - debug( 1, - _( L"Could not convert input. Read %d bytes." ), - acc_used-1 ); - } - else - { - debug( 1, - _( L"Could not read input stream" ) ); - } - res=1; - } + if( str ) + { + wcstring sb; + if( ! parser.test( str, 0, &sb, L"fish" ) ) + { + parser.eval( str, io, TOP ); + } + else + { + fwprintf( stderr, L"%ls", sb.c_str() ); + res = 1; + } + free( str ); + } + else + { + if( acc_used > 1 ) + { + debug( 1, + _( L"Could not convert input. Read %d bytes." ), + acc_used-1 ); + } + else + { + debug( 1, + _( L"Could not read input stream" ) ); + } + res=1; + } - } - else - { - debug( 1, - _( L"Error while opening input stream" ) ); - wperror( L"fdopen" ); - free( buff ); - res=1; - } - return res; + } + else + { + debug( 1, + _( L"Error while opening input stream" ) ); + wperror( L"fdopen" ); + free( buff ); + res=1; + } + return res; } int reader_read( int fd, const io_chain_t &io ) { - int res; + int res; - /* - If reader_read is called recursively through the '.' builtin, we - need to preserve is_interactive. This, and signal handler setup - is handled by proc_push_interactive/proc_pop_interactive. - */ + /* + If reader_read is called recursively through the '.' builtin, we + need to preserve is_interactive. This, and signal handler setup + is handled by proc_push_interactive/proc_pop_interactive. + */ - int inter = ((fd == STDIN_FILENO) && isatty(STDIN_FILENO)); - proc_push_interactive( inter ); - - res= get_is_interactive() ? read_i():read_ni( fd, io ); + int inter = ((fd == STDIN_FILENO) && isatty(STDIN_FILENO)); + proc_push_interactive( inter ); - /* - If the exit command was called in a script, only exit the - script, not the program. - */ - if( data ) - data->end_loop = 0; - end_loop = 0; - - proc_pop_interactive(); - return res; + res= get_is_interactive() ? read_i():read_ni( fd, io ); + + /* + If the exit command was called in a script, only exit the + script, not the program. + */ + if( data ) + data->end_loop = 0; + end_loop = 0; + + proc_pop_interactive(); + return res; } diff --git a/reader.h b/reader.h index 5ce998821..db8dfcb4d 100644 --- a/reader.h +++ b/reader.h @@ -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 &, 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(); diff --git a/sanity.cpp b/sanity.cpp index 37e7e7e9f..cbce3c82a 100644 --- a/sanity.cpp +++ b/sanity.cpp @@ -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(); + } } diff --git a/sanity.h b/sanity.h index 0ca8cd2a6..40b92dec2 100644 --- a/sanity.h +++ b/sanity.h @@ -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 diff --git a/screen.cpp b/screen.cpp index 5caa3d010..80865c3f4 100644 --- a/screen.cpp +++ b/screen.cpp @@ -78,14 +78,14 @@ static int s_writeb( char c ); class scoped_buffer_t { data_buffer_t * const old_buff; int (* const old_writer)(char); - + public: scoped_buffer_t(data_buffer_t *buff) : old_buff(s_writeb_buffer), old_writer(output_get_writer()) { s_writeb_buffer = buff; output_set_writer(s_writeb); } - + ~scoped_buffer_t() { s_writeb_buffer = old_buff; @@ -100,18 +100,18 @@ class scoped_buffer_t { */ static int try_sequence( const char *seq, const wchar_t *str ) { - int i; + int i; - for( i=0;; i++ ) - { - if( !seq[i] ) - return i; + for( i=0;; i++ ) + { + if( !seq[i] ) + return i; - if( seq[i] != str[i] ) - return 0; - } + if( seq[i] != str[i] ) + return 0; + } - return 0; + return 0; } /** @@ -120,32 +120,32 @@ static int try_sequence( const char *seq, const wchar_t *str ) */ static size_t next_tab_stop( size_t in ) { - /* - Assume tab stops every 8 characters if undefined - */ - size_t tab_width = (init_tabs > 0 ? (size_t)init_tabs : 8); - return ( (in/tab_width)+1 )*tab_width; + /* + Assume tab stops every 8 characters if undefined + */ + size_t tab_width = (init_tabs > 0 ? (size_t)init_tabs : 8); + return ( (in/tab_width)+1 )*tab_width; } // PCA for term256 support, let's just detect the escape codes directly static int is_term256_escape(const wchar_t *str) { - // An escape code looks like this: \x1b[38;5;m - // or like this: \x1b[48;5;m - + // An escape code looks like this: \x1b[38;5;m + // or like this: \x1b[48;5;m + // parse out the required prefix int len = try_sequence("\x1b[38;5;", str); if (! len) len = try_sequence("\x1b[48;5;", str); if (! len) return 0; - + // now try parsing out a string of digits // we need at least one if (! iswdigit(str[len])) return 0; while (iswdigit(str[len])) len++; - + // look for the terminating m if (str[len++] != L'm') return 0; - + // success return len; } @@ -164,39 +164,39 @@ static bool allow_soft_wrap(void) */ static size_t calc_prompt_width_and_lines( const wchar_t *prompt, size_t *out_prompt_lines ) { - size_t res = 0; - size_t j, k; + size_t res = 0; + size_t j, k; *out_prompt_lines = 1; - - for( j=0; prompt[j]; j++ ) - { - if( prompt[j] == L'\x1b' ) - { - /* + + for( j=0; prompt[j]; j++ ) + { + if( prompt[j] == L'\x1b' ) + { + /* This is the start of an escape code. Try to guess it's width. */ - size_t p; - int len=0; - bool found = false; - - /* + size_t p; + int len=0; + bool found = false; + + /* Detect these terminfo color escapes with parameter value 0..7, all of which don't move the cursor */ - char * const esc[] = + char * const esc[] = { set_a_foreground, set_a_background, set_foreground, set_background, } - ; - - /* + ; + + /* Detect these semi-common terminfo escapes without any parameter values, all of which don't move the cursor */ - char * const esc2[] = + char * const esc2[] = { enter_bold_mode, exit_attribute_mode, @@ -219,25 +219,25 @@ static size_t calc_prompt_width_and_lines( const wchar_t *prompt, size_t *out_pr exit_standout_mode, enter_secure_mode } - ; - - for( p=0; p < sizeof esc / sizeof *esc && !found; p++ ) - { - if( !esc[p] ) - continue; - - for( k=0; k<8; k++ ) - { - len = try_sequence( tparm(esc[p],k), &prompt[j] ); - if( len ) - { - j += (len-1); - found = true; - break; - } - } - } - + ; + + for( p=0; p < sizeof esc / sizeof *esc && !found; p++ ) + { + if( !esc[p] ) + continue; + + for( k=0; k<8; k++ ) + { + len = try_sequence( tparm(esc[p],k), &prompt[j] ); + if( len ) + { + j += (len-1); + found = true; + break; + } + } + } + // PCA for term256 support, let's just detect the escape codes directly if (! found) { len = is_term256_escape(&prompt[j]); @@ -246,41 +246,41 @@ static size_t calc_prompt_width_and_lines( const wchar_t *prompt, size_t *out_pr found = true; } } - - - for( p=0; p < (sizeof(esc2)/sizeof(char *)) && !found; p++ ) - { - if( !esc2[p] ) - continue; - /* + + + for( p=0; p < (sizeof(esc2)/sizeof(char *)) && !found; p++ ) + { + if( !esc2[p] ) + continue; + /* Test both padded and unpadded version, just to be safe. Most versions of tparm don't actually seem to do anything these days. */ - len = maxi( try_sequence( tparm(esc2[p]), &prompt[j] ), + len = maxi( try_sequence( tparm(esc2[p]), &prompt[j] ), try_sequence( esc2[p], &prompt[j] )); - - if( len ) - { - j += (len-1); - found = true; - } - } - - if( !found ) - { - if( prompt[j+1] == L'k' ) - { - const env_var_t term_name = env_get_string( L"TERM" ); - if( !term_name.missing() && wcsstr( term_name.c_str(), L"screen" ) == term_name ) - { - const wchar_t *end; - j+=2; - found = true; - end = wcsstr( &prompt[j], L"\x1b\\" ); - if( end ) - { - /* + + if( len ) + { + j += (len-1); + found = true; + } + } + + if( !found ) + { + if( prompt[j+1] == L'k' ) + { + const env_var_t term_name = env_get_string( L"TERM" ); + if( !term_name.missing() && wcsstr( term_name.c_str(), L"screen" ) == term_name ) + { + const wchar_t *end; + j+=2; + found = true; + end = wcsstr( &prompt[j], L"\x1b\\" ); + if( end ) + { + /* You'd thing this should be '(end-prompt)+2', in order to move j past the end of the string, but there is @@ -288,35 +288,35 @@ static size_t calc_prompt_width_and_lines( const wchar_t *prompt, size_t *out_pr should always point to the last menged character, e.g. +1. */ - j = (end-prompt)+1; - } - else - { - break; - } - } - } - } - - } - else if( prompt[j] == L'\t' ) - { - res = next_tab_stop( res ); - } - else if( prompt[j] == L'\n' ) - { - res = 0; + j = (end-prompt)+1; + } + else + { + break; + } + } + } + } + + } + else if( prompt[j] == L'\t' ) + { + res = next_tab_stop( res ); + } + else if( prompt[j] == L'\n' ) + { + res = 0; *out_prompt_lines += 1; - } - else - { - /* + } + else + { + /* Ordinary decent character. Just add width. */ - res += fish_wcwidth( prompt[j] ); - } - } - return res; + res += fish_wcwidth( prompt[j] ); + } + } + return res; } static size_t calc_prompt_width(const wchar_t *prompt) @@ -345,9 +345,9 @@ static size_t calc_prompt_lines(const wchar_t *prompt) */ static int room_for_usec(struct stat *st) { - int res = ((&(st->st_atime) + 2) == &(st->st_mtime) && - (&(st->st_atime) + 4) == &(st->st_ctime)); - return res; + int res = ((&(st->st_atime) + 2) == &(st->st_mtime) && + (&(st->st_atime) + 4) == &(st->st_ctime)); + return res; } /** @@ -363,37 +363,37 @@ static void s_save_status( screen_t *s) // tty's ctime as part of its tty_tickets feature // Disabling this should fix https://github.com/fish-shell/fish-shell/issues/122 #if 0 - /* - This futimes call tries to trick the system into using st_mtime - as a tampering flag. This of course only works on systems where - futimes is defined, but it should make the status saving stuff - failsafe. - */ - struct timeval t[]= - { - { - time(0)-1, - 0 - } - , - { - time(0)-1, - 0 - } - } - ; + /* + This futimes call tries to trick the system into using st_mtime + as a tampering flag. This of course only works on systems where + futimes is defined, but it should make the status saving stuff + failsafe. + */ + struct timeval t[]= + { + { + time(0)-1, + 0 + } + , + { + time(0)-1, + 0 + } + } + ; - /* - Don't check return value on these. We don't care if they fail, - really. This is all just to make the prompt look ok, which is - impossible to do 100% reliably. We try, at least. - */ - futimes( 1, t ); - futimes( 2, t ); + /* + Don't check return value on these. We don't care if they fail, + really. This is all just to make the prompt look ok, which is + impossible to do 100% reliably. We try, at least. + */ + futimes( 1, t ); + futimes( 2, t ); #endif - fstat( 1, &s->prev_buff_1 ); - fstat( 2, &s->prev_buff_2 ); + fstat( 1, &s->prev_buff_1 ); + fstat( 2, &s->prev_buff_2 ); } /** @@ -406,43 +406,43 @@ static void s_save_status( screen_t *s) static void s_check_status( screen_t *s) { - fflush( stdout ); - fflush( stderr ); + fflush( stdout ); + fflush( stderr ); - fstat( 1, &s->post_buff_1 ); - fstat( 2, &s->post_buff_2 ); + fstat( 1, &s->post_buff_1 ); + fstat( 2, &s->post_buff_2 ); - int changed = ( s->prev_buff_1.st_mtime != s->post_buff_1.st_mtime ) || - ( s->prev_buff_2.st_mtime != s->post_buff_2.st_mtime ); + int changed = ( s->prev_buff_1.st_mtime != s->post_buff_1.st_mtime ) || + ( s->prev_buff_2.st_mtime != s->post_buff_2.st_mtime ); - if (room_for_usec( &s->post_buff_1)) - { - changed = changed || ( (&s->prev_buff_1.st_mtime)[1] != (&s->post_buff_1.st_mtime)[1] ) || - ( (&s->prev_buff_2.st_mtime)[1] != (&s->post_buff_2.st_mtime)[1] ); - } + if (room_for_usec( &s->post_buff_1)) + { + changed = changed || ( (&s->prev_buff_1.st_mtime)[1] != (&s->post_buff_1.st_mtime)[1] ) || + ( (&s->prev_buff_2.st_mtime)[1] != (&s->post_buff_2.st_mtime)[1] ); + } - if( changed ) - { - /* - Ok, someone has been messing with our screen. We will want - to repaint. However, we do not know where the cursor is. It - is our best bet that we are still on the same line, so we - move to the beginning of the line, reset the modelled screen - contents, and then set the modeled cursor y-pos to its - earlier value. - */ - - int prev_line = s->actual.cursor.y; - write_loop( 1, "\r", 1 ); - s_reset( s, false ); - s->actual.cursor.y = prev_line; - } + if( changed ) + { + /* + Ok, someone has been messing with our screen. We will want + to repaint. However, we do not know where the cursor is. It + is our best bet that we are still on the same line, so we + move to the beginning of the line, reset the modelled screen + contents, and then set the modeled cursor y-pos to its + earlier value. + */ + + int prev_line = s->actual.cursor.y; + write_loop( 1, "\r", 1 ); + s_reset( s, false ); + s->actual.cursor.y = prev_line; + } } /** Appends a character to the end of the line that the output cursor is on. This function automatically handles linebreaks and lines longer - than the screen width. + than the screen width. */ static void s_desired_append_char( screen_t *s, wchar_t b, @@ -450,63 +450,63 @@ static void s_desired_append_char( screen_t *s, int indent, size_t prompt_width ) { - int line_no = s->desired.cursor.y; - - switch( b ) - { - case L'\n': - { - int i; + int line_no = s->desired.cursor.y; + + switch( b ) + { + case L'\n': + { + int i; /* Current line is definitely hard wrapped */ s->desired.line(s->desired.cursor.y).is_soft_wrapped = false; s->desired.create_line(s->desired.line_count()); - s->desired.cursor.y++; - s->desired.cursor.x=0; - for( i=0; i < prompt_width+indent*INDENT_STEP; i++ ) - { - s_desired_append_char( s, L' ', 0, indent, prompt_width ); - } - break; - } - - case L'\r': - { + s->desired.cursor.y++; + s->desired.cursor.x=0; + for( i=0; i < prompt_width+indent*INDENT_STEP; i++ ) + { + s_desired_append_char( s, L' ', 0, indent, prompt_width ); + } + break; + } + + case L'\r': + { line_t ¤t = s->desired.line(line_no); current.clear(); s->desired.cursor.x = 0; - break; - } - - default: - { - int screen_width = common_get_width(); - int cw = fish_wcwidth(b); - + break; + } + + default: + { + int screen_width = common_get_width(); + int cw = fish_wcwidth(b); + s->desired.create_line(line_no); - - /* + + /* Check if we are at the end of the line. If so, continue on the next line. */ - if( (s->desired.cursor.x + cw) > screen_width ) - { + if( (s->desired.cursor.x + cw) > screen_width ) + { /* Current line is soft wrapped (assuming we support it) */ s->desired.line(s->desired.cursor.y).is_soft_wrapped = true; //fprintf(stderr, "\n\n1 Soft wrapping %d\n\n", s->desired.cursor.y); - + line_no = (int)s->desired.line_count(); s->desired.add_line(); - s->desired.cursor.y++; - s->desired.cursor.x=0; + s->desired.cursor.y++; + s->desired.cursor.x=0; for( size_t i=0; i < prompt_width; i++ ) { s_desired_append_char( s, L' ', 0, indent, prompt_width ); } - } - + } + line_t &line = s->desired.line(line_no); line.append(b, c); - s->desired.cursor.x+= cw; - + s->desired.cursor.x+= cw; + /* Maybe wrap the cursor to the next line, even if the line itself did not wrap. This avoids wonkiness in the last column. */ if (s->desired.cursor.x >= screen_width) { @@ -514,10 +514,10 @@ static void s_desired_append_char( screen_t *s, s->desired.cursor.x = 0; s->desired.cursor.y++; } - break; - } - } - + break; + } + } + } /** @@ -526,14 +526,14 @@ static void s_desired_append_char( screen_t *s, static int s_writeb( char c ) { s_writeb_buffer->push_back(c); - return 0; + return 0; } /** Write the bytes needed to move screen cursor to the specified position to the specified buffer. The actual_cursor field of the specified screen_t will be updated. - + \param s the screen to operate on \param b the buffer to send the output escape codes to \param new_x the new x position @@ -543,73 +543,73 @@ static void s_move( screen_t *s, data_buffer_t *b, int new_x, int new_y ) { if (s->actual.cursor.x == new_x && s->actual.cursor.y == new_y) return; - - int i; - int x_steps, y_steps; - - char *str; + + int i; + int x_steps, y_steps; + + char *str; /* - debug( 0, L"move from %d %d to %d %d", - s->screen_cursor[0], s->screen_cursor[1], + debug( 0, L"move from %d %d to %d %d", + s->screen_cursor[0], s->screen_cursor[1], new_x, new_y ); */ scoped_buffer_t scoped_buffer(b); - - y_steps = new_y - s->actual.cursor.y; - if( y_steps > 0 && (strcmp( cursor_down, "\n")==0)) - { - /* - This is very strange - it seems some (all?) consoles use a - simple newline as the cursor down escape. This will of - course move the cursor to the beginning of the line as well - as moving it down one step. The cursor_up does not have this - behaviour... - */ - s->actual.cursor.x=0; - } - - if( y_steps < 0 ) - { - str = cursor_up; - } - else - { - str = cursor_down; - - } - - for( i=0; iactual.cursor.y; + + if( y_steps > 0 && (strcmp( cursor_down, "\n")==0)) + { + /* + This is very strange - it seems some (all?) consoles use a + simple newline as the cursor down escape. This will of + course move the cursor to the beginning of the line as well + as moving it down one step. The cursor_up does not have this + behaviour... + */ + s->actual.cursor.x=0; + } + + if( y_steps < 0 ) + { + str = cursor_up; + } + else + { + str = cursor_down; + + } + + for( i=0; iactual.cursor.x; - - if( x_steps && new_x == 0 ) - { + x_steps = new_x - s->actual.cursor.x; + + if( x_steps && new_x == 0 ) + { b->push_back('\r'); - x_steps = 0; - } - - if( x_steps < 0 ) + x_steps = 0; + } + + if( x_steps < 0 ) { - str = cursor_left; - } - else - { - str = cursor_right; - } - - for( i=0; iactual.cursor.x = new_x; - s->actual.cursor.y = new_y; + s->actual.cursor.x = new_x; + s->actual.cursor.y = new_y; } /** @@ -618,10 +618,10 @@ static void s_move( screen_t *s, data_buffer_t *b, int new_x, int new_y ) static void s_set_color( screen_t *s, data_buffer_t *b, int c ) { scoped_buffer_t scoped_buffer(b); - + unsigned int uc = (unsigned int)c; - set_color( highlight_get_color( uc & 0xffff, false ), - highlight_get_color( (uc>>16)&0xffff, true ) ); + set_color( highlight_get_color( uc & 0xffff, false ), + highlight_get_color( (uc>>16)&0xffff, true ) ); } /** @@ -630,14 +630,14 @@ static void s_set_color( screen_t *s, data_buffer_t *b, int c ) */ static void s_write_char( screen_t *s, data_buffer_t *b, wchar_t c ) { - scoped_buffer_t scoped_buffer(b); - s->actual.cursor.x+=fish_wcwidth( c ); - writech( c ); + scoped_buffer_t scoped_buffer(b); + s->actual.cursor.x+=fish_wcwidth( c ); + writech( c ); if (s->actual.cursor.x == s->actual_width && allow_soft_wrap()) { s->soft_wrap_location.x = 0; s->soft_wrap_location.y = s->actual.cursor.y + 1; - + /* If auto_right_margin is set, then the cursor sticks to the right edge when it's in the rightmost column, so reflect that fact */ if (auto_right_margin) s->actual.cursor.x--; @@ -655,7 +655,7 @@ static void s_write_char( screen_t *s, data_buffer_t *b, wchar_t c ) static void s_write_mbs( data_buffer_t *b, char *s ) { scoped_buffer_t scoped_buffer(b); - writembs( s ); + writembs( s ); } /** @@ -665,7 +665,7 @@ static void s_write_mbs( data_buffer_t *b, char *s ) static void s_write_str( data_buffer_t *b, const wchar_t *s ) { scoped_buffer_t scoped_buffer(b); - writestr( s ); + writestr( s ); } /** Returns the length of the "shared prefix" of the two lines, which is the run of matching text and colors. @@ -675,7 +675,7 @@ static size_t line_shared_prefix(const line_t &a, const line_t &b) { size_t idx, max = std::min(a.size(), b.size()); for (idx=0; idx < max; idx++) - { + { wchar_t ac = a.char_at(idx), bc = b.char_at(idx); if (fish_wcwidth(ac) < 1 || fish_wcwidth(bc) < 1) { @@ -683,7 +683,7 @@ static size_t line_shared_prefix(const line_t &a, const line_t &b) if (idx > 0) idx--; break; } - + /* We're done if the text or colors are different */ if (ac != bc || a.color_at(idx) != b.color_at(idx)) break; @@ -717,29 +717,29 @@ static void invalidate_soft_wrap(screen_t *scr) */ static void s_update( screen_t *scr, const wchar_t *left_prompt, const wchar_t *right_prompt ) { - const size_t left_prompt_width = calc_prompt_width( left_prompt ); + const size_t left_prompt_width = calc_prompt_width( left_prompt ); const size_t right_prompt_width = calc_prompt_width( right_prompt ); - - int screen_width = common_get_width(); - bool need_clear = scr->need_clear; + + int screen_width = common_get_width(); + bool need_clear = scr->need_clear; bool has_cleared_screen = false; - + /* Figure out how many following lines we need to clear (probably 0) */ size_t actual_lines_before_reset = scr->actual_lines_before_reset; scr->actual_lines_before_reset = 0; - - data_buffer_t output; - scr->need_clear = false; - - if( scr->actual_width != screen_width ) - { - need_clear = true; - s_move( scr, &output, 0, 0 ); - scr->actual_width = screen_width; - s_reset( scr, false, false /* don't clear prompt */); - } - + data_buffer_t output; + + scr->need_clear = false; + + if( scr->actual_width != screen_width ) + { + need_clear = true; + s_move( scr, &output, 0, 0 ); + scr->actual_width = screen_width; + s_reset( scr, false, false /* don't clear prompt */); + } + /* Determine how many lines have stuff on them; we need to clear lines with stuff that we don't want */ const size_t lines_with_stuff = maxi(actual_lines_before_reset, scr->actual.line_count()); if (lines_with_stuff > scr->desired.line_count()) @@ -747,28 +747,28 @@ static void s_update( screen_t *scr, const wchar_t *left_prompt, const wchar_t * /* There are lines that we output to previously that will need to be cleared */ need_clear = true; } - - if( wcscmp( left_prompt, scr->actual_left_prompt.c_str() ) ) - { - s_move( scr, &output, 0, 0 ); - s_write_str( &output, left_prompt ); + + if( wcscmp( left_prompt, scr->actual_left_prompt.c_str() ) ) + { + s_move( scr, &output, 0, 0 ); + s_write_str( &output, left_prompt ); scr->actual_left_prompt = left_prompt; - scr->actual.cursor.x = (int)left_prompt_width; - } - + scr->actual.cursor.x = (int)left_prompt_width; + } + for (size_t i=0; i < scr->desired.line_count(); i++) - { - const line_t &o_line = scr->desired.line(i); - line_t &s_line = scr->actual.create_line(i); - size_t start_pos = (i==0 ? left_prompt_width : 0); + { + const line_t &o_line = scr->desired.line(i); + line_t &s_line = scr->actual.create_line(i); + size_t start_pos = (i==0 ? left_prompt_width : 0); int current_width = 0; - + /* If this is the last line, maybe we should clear the screen */ const bool should_clear_screen_this_line = (need_clear && i + 1 == scr->desired.line_count() && clr_eos != NULL); - + /* Note that skip_remaining is a width, not a character count */ size_t skip_remaining = start_pos; - + if (! should_clear_screen_this_line) { /* Compute how much we should skip. At a minimum we skip over the prompt. But also skip over the shared prefix of what we want to output now, and what we output before, to avoid repeatedly outputting it. */ const size_t shared_prefix = line_shared_prefix(o_line, s_line); @@ -778,12 +778,12 @@ static void s_update( screen_t *scr, const wchar_t *left_prompt, const wchar_t * if (prefix_width > skip_remaining) skip_remaining = prefix_width; } - + /* Don't skip over the last two characters in a soft-wrapped line, so that we maintain soft-wrapping */ if (o_line.is_soft_wrapped) skip_remaining = mini(skip_remaining, (size_t)(scr->actual_width - 2)); } - + /* Skip over skip_remaining width worth of characters */ size_t j = 0; for ( ; j < o_line.size(); j++) @@ -794,7 +794,7 @@ static void s_update( screen_t *scr, const wchar_t *left_prompt, const wchar_t * skip_remaining -= width; current_width += width; } - + /* Skip over zero-width characters (e.g. combining marks at the end of the prompt) */ for ( ; j < o_line.size(); j++) { @@ -802,14 +802,14 @@ static void s_update( screen_t *scr, const wchar_t *left_prompt, const wchar_t * if (width > 0) break; } - + /* Maybe clear the screen before outputting. If we clear the screen after outputting, then we may erase the last character due to the sticky right margin. */ if (should_clear_screen_this_line) { s_move( scr, &output, current_width, (int)i ); s_write_mbs(&output, clr_eos); has_cleared_screen = true; } - + /* Now actually output stuff */ for ( ; j < o_line.size(); j++) { @@ -819,7 +819,7 @@ static void s_update( screen_t *scr, const wchar_t *left_prompt, const wchar_t * s_write_char( scr, &output, o_line.char_at(j) ); current_width += fish_wcwidth(o_line.char_at(j)); } - + bool clear_remainder = false; /* Clear the remainder of the line if we need to clear and if we didn't write to the end of the line. If we did write to the end of the line, the "sticky right edge" (as part of auto_right_margin) means that we'll be clearing the last character we wrote! */ if (has_cleared_screen) @@ -839,14 +839,14 @@ static void s_update( screen_t *scr, const wchar_t *left_prompt, const wchar_t * { int prev_width = (s_line.text.empty() ? 0 : fish_wcswidth(&s_line.text.at(0), s_line.text.size())); clear_remainder = prev_width > current_width; - + } if (clear_remainder) - { - s_move( scr, &output, current_width, (int)i ); - s_write_mbs( &output, clr_eol); - } - + { + s_move( scr, &output, current_width, (int)i ); + s_write_mbs( &output, clr_eol); + } + /* Output any rprompt if this is the first line. */ if (i == 0 && right_prompt_width > 0) { @@ -857,8 +857,8 @@ static void s_update( screen_t *scr, const wchar_t *left_prompt, const wchar_t * s_write_str( &output, L"\r"); scr->actual.cursor.x = 0; } - } - + } + /* Clear remaining lines if we haven't done so already */ if (need_clear && ! has_cleared_screen) @@ -870,15 +870,15 @@ static void s_update( screen_t *scr, const wchar_t *left_prompt, const wchar_t * s_write_mbs( &output, clr_eol); } } - - s_move( scr, &output, scr->desired.cursor.x, scr->desired.cursor.y ); - s_set_color( scr, &output, 0xffffffff); - if( ! output.empty() ) - { - write_loop( 1, &output.at(0), output.size() ); - } - + s_move( scr, &output, scr->desired.cursor.x, scr->desired.cursor.y ); + s_set_color( scr, &output, 0xffffffff); + + if( ! output.empty() ) + { + write_loop( 1, &output.at(0), output.size() ); + } + /* We have now synced our actual screen against our desired screen. Note that this is a big assignment! */ scr->actual = scr->desired; scr->last_right_prompt_width = right_prompt_width; @@ -887,22 +887,22 @@ static void s_update( screen_t *scr, const wchar_t *left_prompt, const wchar_t * /** Returns true if we are using a dumb terminal. */ static bool is_dumb(void) { - return ( !cursor_up || !cursor_down || !cursor_left || !cursor_right ); + return ( !cursor_up || !cursor_down || !cursor_left || !cursor_right ); } struct screen_layout_t { /* The left prompt that we're going to use */ wcstring left_prompt; - + /* How much space to leave for it */ size_t left_prompt_space; - + /* The right prompt */ wcstring right_prompt; - + /* The autosuggestion */ wcstring autosuggestion; - + /* Whether the prompts get their own line or not */ bool prompts_get_own_line; }; @@ -922,67 +922,67 @@ static size_t truncation_offset_for_width(const std::vector &width_by_of } static screen_layout_t compute_layout(screen_t *s, - size_t screen_width, - const wcstring &left_prompt_str, + size_t screen_width, + const wcstring &left_prompt_str, const wcstring &right_prompt_str, - const wcstring &commandline, + const wcstring &commandline, const wcstring &autosuggestion_str, - const int *indent) + const int *indent) { screen_layout_t result = {}; - + /* Start by ensuring that the prompts themselves can fit */ const wchar_t *left_prompt = left_prompt_str.c_str(); const wchar_t *right_prompt = right_prompt_str.c_str(); const wchar_t *autosuggestion = autosuggestion_str.c_str(); - - size_t left_prompt_width = calc_prompt_width( left_prompt ); + + size_t left_prompt_width = calc_prompt_width( left_prompt ); size_t right_prompt_width = calc_prompt_width( right_prompt ); - + if (left_prompt_width + right_prompt_width >= screen_width) { /* Nix right_prompt */ right_prompt = L""; right_prompt_width = 0; } - + if (left_prompt_width + right_prompt_width >= screen_width) { /* Still doesn't fit, neuter left_prompt */ left_prompt = L"> "; left_prompt_width = 2; } - + /* Now we should definitely fit */ assert(left_prompt_width + right_prompt_width < screen_width); - + /* Convert commandline to a list of lines and their widths */ wcstring_list_t command_lines(1); std::vector line_widths(1); for (size_t i=0; i < commandline.size(); i++) - { + { wchar_t c = commandline.at(i); - if (c == L'\n') - { + if (c == L'\n') + { /* Make a new line */ command_lines.push_back(wcstring()); line_widths.push_back(indent[i]*INDENT_STEP); - } - else - { + } + else + { command_lines.back() += c; - line_widths.back() += fish_wcwidth(c); - } - } + line_widths.back() += fish_wcwidth(c); + } + } const size_t first_command_line_width = line_widths.at(0); - + /* If we have more than one line, ensure we have no autosuggestion */ if (command_lines.size() > 1) { autosuggestion = L""; } - + /* Compute the width of the autosuggestion at all possible truncation offsets */ std::vector autosuggestion_truncated_widths; autosuggestion_truncated_widths.reserve(1 + wcslen(autosuggestion)); @@ -992,17 +992,17 @@ static screen_layout_t compute_layout(screen_t *s, autosuggestion_truncated_widths.push_back(autosuggestion_total_width); autosuggestion_total_width += fish_wcwidth(autosuggestion[i]); } - + /* Here are the layouts we try in turn: - + 1. Left prompt visible, right prompt visible, command line visible, autosuggestion visible 2. Left prompt visible, right prompt visible, command line visible, autosuggestion truncated (possibly to zero) 3. Left prompt visible, right prompt hidden, command line visible, autosuggestion hidden 4. Newline separator (left prompt visible, right prompt visible, command line visible, autosuggestion visible) */ - + bool done = false; - + /* Case 1 */ if (! done && left_prompt_width + right_prompt_width + first_command_line_width + autosuggestion_total_width + 10 < screen_width) { @@ -1012,14 +1012,14 @@ static screen_layout_t compute_layout(screen_t *s, result.autosuggestion = autosuggestion; done = true; } - + /* Case 2. Note that we require strict inequality so that there's always at least one space between the left edge and the rprompt */ if (! done && left_prompt_width + right_prompt_width + first_command_line_width < screen_width) { result.left_prompt = left_prompt; result.left_prompt_space = left_prompt_width; result.right_prompt = right_prompt; - + /* Need at least two characters to show an autosuggestion */ size_t available_autosuggestion_space = screen_width - (left_prompt_width + right_prompt_width + first_command_line_width); if (autosuggestion_total_width > 0 && available_autosuggestion_space > 2) @@ -1030,7 +1030,7 @@ static screen_layout_t compute_layout(screen_t *s, } done = true; } - + /* Case 3 */ if (! done && left_prompt_width + first_command_line_width < screen_width) { @@ -1038,7 +1038,7 @@ static screen_layout_t compute_layout(screen_t *s, result.left_prompt_space = left_prompt_width; done = true; } - + /* Case 4 */ if (! done) { @@ -1048,7 +1048,7 @@ static screen_layout_t compute_layout(screen_t *s, result.prompts_get_own_line = true; done = true; } - + assert(done); return result; } @@ -1063,193 +1063,193 @@ void s_write( screen_t *s, const int *indent, size_t cursor_pos ) { - screen_data_t::cursor_t cursor_arr; + screen_data_t::cursor_t cursor_arr; + + CHECK( s, ); + CHECK( indent, ); - CHECK( s, ); - CHECK( indent, ); - /* Turn the command line into the explicit portion and the autosuggestion */ const wcstring explicit_command_line = commandline.substr(0, explicit_len); const wcstring autosuggestion = commandline.substr(explicit_len); - - /* - If we are using a dumb terminal, don't try any fancy stuff, - just print out the text. right_prompt not supported. - */ - if( is_dumb() ) - { - const std::string prompt_narrow = wcs2string( left_prompt ); - const std::string command_line_narrow = wcs2string( explicit_command_line ); - - write_loop( 1, "\r", 1 ); - write_loop( 1, prompt_narrow.c_str(), prompt_narrow.size() ); - write_loop( 1, command_line_narrow.c_str(), command_line_narrow.size() ); - - return; - } - - s_check_status( s ); + + /* + If we are using a dumb terminal, don't try any fancy stuff, + just print out the text. right_prompt not supported. + */ + if( is_dumb() ) + { + const std::string prompt_narrow = wcs2string( left_prompt ); + const std::string command_line_narrow = wcs2string( explicit_command_line ); + + write_loop( 1, "\r", 1 ); + write_loop( 1, prompt_narrow.c_str(), prompt_narrow.size() ); + write_loop( 1, command_line_narrow.c_str(), command_line_narrow.size() ); + + return; + } + + s_check_status( s ); const size_t screen_width = common_get_width(); - - /* Completely ignore impossibly small screens */ - if( screen_width < 4 ) - { - return; - } - + + /* Completely ignore impossibly small screens */ + if( screen_width < 4 ) + { + return; + } + /* Compute a layout */ const screen_layout_t layout = compute_layout(s, screen_width, left_prompt, right_prompt, explicit_command_line, autosuggestion, indent); /* Clear the desired screen */ s->desired.resize(0); - s->desired.cursor.x = s->desired.cursor.y = 0; + s->desired.cursor.x = s->desired.cursor.y = 0; /* Append spaces for the left prompt */ - for (size_t i=0; i < layout.left_prompt_space; i++) - { - s_desired_append_char( s, L' ', 0, 0, layout.left_prompt_space ); - } - - /* If overflowing, give the prompt its own line to improve the situation. */ + for (size_t i=0; i < layout.left_prompt_space; i++) + { + s_desired_append_char( s, L' ', 0, 0, layout.left_prompt_space ); + } + + /* If overflowing, give the prompt its own line to improve the situation. */ size_t first_line_prompt_space = layout.left_prompt_space; if (layout.prompts_get_own_line) { - s_desired_append_char( s, L'\n', 0, 0, 0 ); + s_desired_append_char( s, L'\n', 0, 0, 0 ); first_line_prompt_space = 0; } - + /* Reconstruct the command line */ wcstring effective_commandline = explicit_command_line + layout.autosuggestion; - + /* Output the command line */ size_t i; - for( i=0; i < effective_commandline.size(); i++ ) - { - int color = colors[i]; - - if( i == cursor_pos ) - { - color = 0; - } - - if( i == cursor_pos ) - { + for( i=0; i < effective_commandline.size(); i++ ) + { + int color = colors[i]; + + if( i == cursor_pos ) + { + color = 0; + } + + if( i == cursor_pos ) + { cursor_arr = s->desired.cursor; - } - - s_desired_append_char( s, effective_commandline.at(i), color, indent[i], first_line_prompt_space ); - } - if( i == cursor_pos ) - { + } + + s_desired_append_char( s, effective_commandline.at(i), color, indent[i], first_line_prompt_space ); + } + if( i == cursor_pos ) + { cursor_arr = s->desired.cursor; - } - + } + s->desired.cursor = cursor_arr; - s_update( s, layout.left_prompt.c_str(), layout.right_prompt.c_str()); - s_save_status( s ); + s_update( s, layout.left_prompt.c_str(), layout.right_prompt.c_str()); + s_save_status( s ); } void s_write_OLD( screen_t *s, - const wchar_t *left_prompt, + const wchar_t *left_prompt, const wchar_t *right_prompt, - const wchar_t *commandline, - size_t explicit_len, - const int *c, - const int *indent, - size_t cursor_pos ) + const wchar_t *commandline, + size_t explicit_len, + const int *c, + const int *indent, + size_t cursor_pos ) { - screen_data_t::cursor_t cursor_arr; + screen_data_t::cursor_t cursor_arr; - int current_line_width = 0, newline_count = 0, explicit_portion_width = 0; + int current_line_width = 0, newline_count = 0, explicit_portion_width = 0; size_t max_line_width = 0; - - CHECK( s, ); - CHECK( left_prompt, ); - CHECK( right_prompt, ); - CHECK( commandline, ); - CHECK( c, ); - CHECK( indent, ); - /* - If we are using a dumb terminal, don't try any fancy stuff, - just print out the text. right_prompt not supported. - */ - if( is_dumb() ) - { - char *prompt_narrow = wcs2str( left_prompt ); - char *buffer_narrow = wcs2str( commandline ); - - write_loop( 1, "\r", 1 ); - write_loop( 1, prompt_narrow, strlen( prompt_narrow ) ); - write_loop( 1, buffer_narrow, strlen( buffer_narrow ) ); + CHECK( s, ); + CHECK( left_prompt, ); + CHECK( right_prompt, ); + CHECK( commandline, ); + CHECK( c, ); + CHECK( indent, ); - free( prompt_narrow ); - free( buffer_narrow ); - - return; - } - - size_t left_prompt_width = calc_prompt_width( left_prompt ); + /* + If we are using a dumb terminal, don't try any fancy stuff, + just print out the text. right_prompt not supported. + */ + if( is_dumb() ) + { + char *prompt_narrow = wcs2str( left_prompt ); + char *buffer_narrow = wcs2str( commandline ); + + write_loop( 1, "\r", 1 ); + write_loop( 1, prompt_narrow, strlen( prompt_narrow ) ); + write_loop( 1, buffer_narrow, strlen( buffer_narrow ) ); + + free( prompt_narrow ); + free( buffer_narrow ); + + return; + } + + size_t left_prompt_width = calc_prompt_width( left_prompt ); size_t right_prompt_width = calc_prompt_width( right_prompt ); - const size_t screen_width = common_get_width(); + const size_t screen_width = common_get_width(); - s_check_status( s ); + s_check_status( s ); - /* - Ignore prompts wider than the screen - only print a two - character placeholder... + /* + Ignore prompts wider than the screen - only print a two + character placeholder... + + It would be cool to truncate the prompt, but because it can + contain escape sequences, this is harder than you'd think. + */ + if( left_prompt_width >= screen_width ) + { + left_prompt = L"> "; + left_prompt_width = 2; - It would be cool to truncate the prompt, but because it can - contain escape sequences, this is harder than you'd think. - */ - if( left_prompt_width >= screen_width ) - { - left_prompt = L"> "; - left_prompt_width = 2; - right_prompt = L""; right_prompt_width = 0; - } - - /* - Completely ignore impossibly small screens - */ - if( screen_width < 4 ) - { - return; - } + } - /* - Check if we are overflowing - */ + /* + Completely ignore impossibly small screens + */ + if( screen_width < 4 ) + { + return; + } + + /* + Check if we are overflowing + */ size_t last_char_that_fits = 0; - for( size_t i=0; commandline[i]; i++ ) - { - if( commandline[i] == L'\n' ) - { - if( current_line_width > max_line_width ) - max_line_width = current_line_width; - current_line_width = indent[i]*INDENT_STEP; + for( size_t i=0; commandline[i]; i++ ) + { + if( commandline[i] == L'\n' ) + { + if( current_line_width > max_line_width ) + max_line_width = current_line_width; + current_line_width = indent[i]*INDENT_STEP; newline_count++; - } - else - { + } + else + { int width = fish_wcwidth(commandline[i]); - current_line_width += width; + current_line_width += width; if (i < explicit_len) explicit_portion_width += width; - + if (left_prompt_width + current_line_width < screen_width) last_char_that_fits = i; - } - } - if( current_line_width > max_line_width ) - max_line_width = current_line_width; + } + } + if( current_line_width > max_line_width ) + max_line_width = current_line_width; s->desired.resize(0); - s->desired.cursor.x = s->desired.cursor.y = 0; - + s->desired.cursor.x = s->desired.cursor.y = 0; + /* If we cannot fit with the autosuggestion, but we can fit without it, truncate the autosuggestion. We limit this check to just one line to avoid confusion; not sure how well this would work with multiple lines */ wcstring truncated_autosuggestion_line; if (newline_count == 0 && left_prompt_width + right_prompt_width + max_line_width >= screen_width && left_prompt_width + explicit_portion_width < screen_width) @@ -1260,88 +1260,88 @@ void s_write_OLD( screen_t *s, truncated_autosuggestion_line.push_back(ellipsis_char); commandline = truncated_autosuggestion_line.c_str(); } - for( size_t i=0; i= screen_width ) + { + s_desired_append_char( s, L'\n', 0, 0, 0 ); + left_prompt_width = 0; + } - /* - If overflowing, give the prompt its own line to improve the - situation. - */ - if( max_line_width + left_prompt_width >= screen_width ) - { - s_desired_append_char( s, L'\n', 0, 0, 0 ); - left_prompt_width = 0; - } - size_t i; - for( i=0; commandline[i]; i++ ) - { - int col = c[i]; - - if( i == cursor_pos ) - { - col = 0; - } - - if( i == cursor_pos ) - { + for( i=0; commandline[i]; i++ ) + { + int col = c[i]; + + if( i == cursor_pos ) + { + col = 0; + } + + if( i == cursor_pos ) + { cursor_arr = s->desired.cursor; - } - - s_desired_append_char( s, commandline[i], col, indent[i], left_prompt_width ); - } - if( i == cursor_pos ) - { + } + + s_desired_append_char( s, commandline[i], col, indent[i], left_prompt_width ); + } + if( i == cursor_pos ) + { cursor_arr = s->desired.cursor; - } - + } + s->desired.cursor = cursor_arr; - s_update( s, left_prompt, right_prompt); - s_save_status( s ); + s_update( s, left_prompt, right_prompt); + s_save_status( s ); } void s_reset( screen_t *s, bool reset_cursor, bool reset_prompt ) { - CHECK( s, ); - + CHECK( s, ); + /* If we're resetting the cursor, we must also be resetting the prompt */ assert(! reset_cursor || reset_prompt); - + /* If we are resetting the cursor, we're going to make a new line and leave junk behind. If we are not resetting the cursor, we need to remember how many lines we had output to, so we can clear the remaining lines in the next call to s_update. This prevents leaving junk underneath the cursor when resizing a window wider such that it reduces our desired line count. */ if (! reset_cursor) { s->actual_lines_before_reset = s->actual.line_count(); } - - int prev_line = s->actual.cursor.y; - + + int prev_line = s->actual.cursor.y; + if (reset_prompt) { /* If the prompt is multi-line, we need to move up to the prompt's initial line. We do this by lying to ourselves and claiming that we're really below what we consider "line 0" (which is the last line of the prompt). This will cause is to move up to try to get back to line 0, but really we're getting back to the initial line of the prompt. */ const size_t prompt_line_count = calc_prompt_lines(s->actual_left_prompt.c_str()); assert(prompt_line_count >= 1); prev_line += (prompt_line_count - 1); - + /* Clear the prompt */ s->actual_left_prompt.clear(); } - + s->actual.resize(0); s->actual.cursor.x = 0; s->actual.cursor.y = 0; - s->need_clear=true; - - if( !reset_cursor ) - { - /* + s->need_clear=true; + + if( !reset_cursor ) + { + /* This should prevent reseting the cursor position during the next repaint. */ - write_loop( 1, "\r", 1 ); - s->actual.cursor.y = prev_line; - } - fstat( 1, &s->prev_buff_1 ); - fstat( 2, &s->prev_buff_2 ); + write_loop( 1, "\r", 1 ); + s->actual.cursor.y = prev_line; + } + fstat( 1, &s->prev_buff_1 ); + fstat( 2, &s->prev_buff_2 ); } screen_t::screen_t() : diff --git a/screen.h b/screen.h index 31a5cf705..2c36f11aa 100644 --- a/screen.h +++ b/screen.h @@ -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 text; std::vector 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_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. diff --git a/set_color.cpp b/set_color.cpp index 78799b66c..4de3ba605 100644 --- a/set_color.cpp +++ b/set_color.cpp @@ -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' 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' diff --git a/share/completions/xdg-mime.fish b/share/completions/xdg-mime.fish index 13ff942c0..e2b597dce 100644 --- a/share/completions/xdg-mime.fish +++ b/share/completions/xdg-mime.fish @@ -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 diff --git a/share/completions/xrandr.fish b/share/completions/xrandr.fish index a49fd2b49..a05c832fa 100644 --- a/share/completions/xrandr.fish +++ b/share/completions/xrandr.fish @@ -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' diff --git a/share/completions/xrdb.fish b/share/completions/xrdb.fish index bddafb5f3..edb5b2912 100644 --- a/share/completions/xrdb.fish +++ b/share/completions/xrdb.fish @@ -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' diff --git a/share/functions/__fish_complete_atool.fish b/share/functions/__fish_complete_atool.fish index 62f6dd051..896129a68 100644 --- a/share/functions/__fish_complete_atool.fish +++ b/share/functions/__fish_complete_atool.fish @@ -27,7 +27,7 @@ function __fish_complete_atool --description 'Complete atool' --argument-names c complete -c $cmd -s S -l simulate -d 'simulation mode - no filesystem changes are made' complete -c $cmd -s o -l option -x -d 'override a configuration optioni (KEY=VALUE)' complete -c $cmd -l config -r -d 'load configuration defaults from file' - + switch $cmd case als aunpack acat complete -c $cmd -a '(__fish_complete_atool_archive_contents)' -d 'Achive content' diff --git a/share/functions/__fish_complete_cd.fish b/share/functions/__fish_complete_cd.fish index cb10b7cf3..9ca3e93b5 100644 --- a/share/functions/__fish_complete_cd.fish +++ b/share/functions/__fish_complete_cd.fish @@ -18,7 +18,7 @@ function __fish_complete_cd -d "Completions for the cd command" else set mycdpath $CDPATH end - + # Note how this works: we evaluate $ctoken*/ # That trailing slash ensures that we only expand directories diff --git a/share/functions/__fish_complete_list.fish b/share/functions/__fish_complete_list.fish index e9a97bb74..c02a561e3 100644 --- a/share/functions/__fish_complete_list.fish +++ b/share/functions/__fish_complete_list.fish @@ -1,6 +1,6 @@ function __fish_complete_list --argument div cmd prefix iprefix if not set -q cmd[1] - echo "Usage: + echo "Usage: __fish_complete_list where: separator - a symbol, separating individual entries diff --git a/share/functions/__fish_complete_lpr.fish b/share/functions/__fish_complete_lpr.fish index 930a21395..c6b2cbf45 100644 --- a/share/functions/__fish_complete_lpr.fish +++ b/share/functions/__fish_complete_lpr.fish @@ -2,26 +2,26 @@ function __fish_complete_lpr -d 'Complete lpr common options' --argument-names c complete -c $cmd -s E -d 'Forces encryption when connecting to the server' complete -c $cmd -s U -d 'Specifies an alternate username' -xa '(__fish_complete_users)' - switch $cmd + switch $cmd case lpr lpq lprm complete -c $cmd -s P -d 'Specifies an alternate printer or class name' -xa '(__fish_print_lpr_printers)' end - switch $cmd - case lpq cancel + switch $cmd + case lpq cancel complete -c $cmd -s a -d 'Apply command to all printers' end - switch $cmd + switch $cmd case lpq cancel lpmove lpstat lprm lpoptions lp reject accept cupsaccept cupsreject cupsenable cupsdisable complete -c $cmd -s h -d 'Specifies an alternate server' -xa '(__fish_print_hostnames)' end - - switch $cmd + + switch $cmd case lp lpr complete -c $cmd -s o -d 'Sets a job option' -xa '(__fish_complete_lpr_option)' complete -c $cmd -s m -d 'Send an email on job completion' - + complete -c $cmd -s o -xa landscape -d 'Landscape mode' complete -c $cmd -s o -xa "media=a4 media=letter media=legal" -d 'Media size' complete -c $cmd -s o -xa page-ranges= -d 'Page ranges' diff --git a/share/functions/__fish_complete_pacman.fish b/share/functions/__fish_complete_pacman.fish index 70cd65224..9967df3a2 100644 --- a/share/functions/__fish_complete_pacman.fish +++ b/share/functions/__fish_complete_pacman.fish @@ -71,7 +71,7 @@ function __fish_complete_pacman -d 'Complete pacman (ARCH package manager)' --ar # Query and sync options for condition in query sync - complete -c $progname -n $$condition -s g -l groups -d 'Display all packages in GROUP' -xa "$listgroups" + complete -c $progname -n $$condition -s g -l groups -d 'Display all packages in GROUP' -xa "$listgroups" complete -c $progname -n $$condition -s i -l info -d 'Display information on PACKAGE' complete -c $progname -n $$condition -s q -l quiet -d 'Show less information' complete -c $progname -n $$condition -s s -l search -r -d 'Search packages for regexp' diff --git a/share/functions/__fish_complete_python.fish b/share/functions/__fish_complete_python.fish index 33bc2cb65..899add7fe 100644 --- a/share/functions/__fish_complete_python.fish +++ b/share/functions/__fish_complete_python.fish @@ -17,18 +17,18 @@ function __fish_complete_python -d 'Make completion for python' --argument-names complete -c $cmd -s x -d 'Skip first line of source, allowing use of non-Unix forms of #!cmd' complete -c $cmd -a "(__fish_complete_suffix .py)" complete -c $cmd -a '-' -d 'Read program from stdin' - + switch (eval $cmd -V 2>| sed 's/^.*\s\(.\).*/\1/') case 2 complete -c $cmd -s 3 -d 'Warn about Python 3.x incompatibilities that 2to3 cannot trivially fix' complete -c $cmd -s t --description "Warn on mixed tabs and spaces" complete -c $cmd -s Q -x -a "old new warn warnall" --description "Division control" - + case 3 complete -c $cmd -s q --description 'Don\'t print version and copyright messages on interactive startup' complete -c $cmd -s X -x -d 'Set implementation-specific option' complete -c $cmd -s b -d 'Issue warnings about str(bytes_instance), str(bytearray_instance) and comparing bytes/bytearray with str' complete -c $cmd -o bb -d 'Issue errors' - + end end diff --git a/share/functions/__fish_complete_subcommand.fish b/share/functions/__fish_complete_subcommand.fish index e263684a1..ace0db2d5 100644 --- a/share/functions/__fish_complete_subcommand.fish +++ b/share/functions/__fish_complete_subcommand.fish @@ -32,7 +32,7 @@ function __fish_complete_subcommand -d "Complete subcommand" case '-*' case '*=*' case '*' - + set had_cmd 1 set res $i end diff --git a/share/functions/__fish_complete_svn.fish b/share/functions/__fish_complete_svn.fish index 52b4392e3..51030313d 100644 --- a/share/functions/__fish_complete_svn.fish +++ b/share/functions/__fish_complete_svn.fish @@ -1,5 +1,5 @@ -function __fish_complete_svn -d 'Complete svn and its wrappers' --argument-names svn +function __fish_complete_svn -d 'Complete svn and its wrappers' --argument-names svn function _svn_cmpl_ -d 'Make a completion for a subcommand' --no-scope-shadowing --argument-names subcommand set -e argv[1] complete -c $svn -n "__fish_seen_subcommand_from $subcommand" $argv @@ -16,17 +16,17 @@ function __fish_complete_svn -d 'Complete svn and its wrappers' --argument-names set -l cleanup cleanup set -l commit 'ci commit' set -l copy 'cp copy' - set -l diff 'di diff' + set -l diff 'di diff' set -l export export - set -l help '\? h help' + set -l help '\? h help' set -l import import set -l info info - set -l list 'ls list' + set -l list 'ls list' set -l lock lock set -l log log set -l merge merge set -l mergeinfo mergeinfo - set -l mkdir mkdir + set -l mkdir mkdir set -l move 'mv move ren rename' set -l patch patch set -l propdel 'pd pdel propdel' @@ -39,8 +39,8 @@ function __fish_complete_svn -d 'Complete svn and its wrappers' --argument-names set -l resolve resolve set -l resolved resolved set -l revert revert - set -l stat 'st stat status' - set -l switch 'sw switch' + set -l stat 'st stat status' + set -l switch 'sw switch' set -l unlock unlock set -l update 'up update' set -l upgrade upgrade @@ -97,7 +97,7 @@ function __fish_complete_svn -d 'Complete svn and its wrappers' --argument-names # local commands # for cmd in $commit $copy $import $lock $mkdir $move $propedit $remove - if not test $cmd = lock + if not test $cmd = lock _svn_cmpl_ $cmd -l editor-cmd -x --description 'Use ARG as external editor' end _svn_cmpl_ $cmd -l message -s m --description 'Specify log message' @@ -142,7 +142,7 @@ function __fish_complete_svn -d 'Complete svn and its wrappers' --argument-names _svn_cmpl_ $cmd -l change -s c -d 'The change made in revision ARG' -xa '(__fish_print_svn_rev)' end - for cmd in $blame $cat $checkout $copy $diff $export $info $list $log $merge $mergeinfo $move $propedit $propget $propdel $proplist $propset $switch $update + for cmd in $blame $cat $checkout $copy $diff $export $info $list $log $merge $mergeinfo $move $propedit $propget $propdel $proplist $propset $switch $update _svn_cmpl_ $cmd -l revision -s r -d 'Which revision the target is first looked up' -xa '(__fish_print_svn_rev)' end @@ -169,8 +169,8 @@ function __fish_complete_svn -d 'Complete svn and its wrappers' --argument-names _svn_cmpl_ $cmd -l extensions -s x -d 'Ignore eol style' -xa '-w --ignore-eol-style' _svn_cmpl_ $cmd -l extensions -s x -d 'Show C function name' -xa '-p --shoe-c-function' - # Next completion doesn't work, since fish doesn't respect -x key - #_svn_cmpl_ $cmd -l extensions -n '__fish_seen_subcommand_from --diff-cmd' -xa '(__fish_complete_svn_diff)' + # Next completion doesn't work, since fish doesn't respect -x key + #_svn_cmpl_ $cmd -l extensions -n '__fish_seen_subcommand_from --diff-cmd' -xa '(__fish_complete_svn_diff)' end for cmd in $cleanup $merge $switch $update @@ -185,28 +185,28 @@ function __fish_complete_svn -d 'Complete svn and its wrappers' --argument-names _svn_cmpl_ $cmd -l no-ignore -d 'Disregard default and svn:ignore property ignores' end - for cmd in $merge $patch + for cmd in $merge $patch _svn_cmpl_ $cmd -l dry-run --description 'Try operation but make no changes' end - for cmd in $merge $switch + for cmd in $merge $switch _svn_cmpl_ $cmd -l ignore-ancestry --description 'Ignore ancestry when calculating merges' end - for cmd in $diff $log + for cmd in $diff $log _svn_cmpl_ $cmd -l internal-diff --description 'Override diff-cmd specified in config file' _svn_cmpl_ $cmd -l diff-cmd --description 'Use external diff command' -xa "(complete -C(commandline -ct))" end - for cmd in $add $import + for cmd in $add $import _svn_cmpl_ $cmd -l no-auto-props --description 'Disable automatic properties' end - for cmd in $switch $update + for cmd in $switch $update _svn_cmpl_ $cmd -l set-depth --description 'Set new working copy depth' -xa 'exclude empty files immediates infinity' end - for cmd in $blame $log + for cmd in $blame $log _svn_cmpl_ $cmd -l use-merge-history -s g -d 'Use/display additional information from merge history' end diff --git a/share/functions/__fish_complete_svn_diff.fish b/share/functions/__fish_complete_svn_diff.fish index 1969c1643..ada2dd0ed 100644 --- a/share/functions/__fish_complete_svn_diff.fish +++ b/share/functions/__fish_complete_svn_diff.fish @@ -2,7 +2,7 @@ function __fish_complete_svn_diff --description 'Complete "svn diff" arguments' set -l cmdl (commandline -cop) #set -l cmdl svn diff --diff-cmd diff --extensions '-a -b' set -l diff diff -set -l args +set -l args while set -q cmdl[1] switch $cmdl[1] case --diff-cmd diff --git a/share/functions/__fish_complete_tex.fish b/share/functions/__fish_complete_tex.fish index 1c8876feb..1334dddad 100644 --- a/share/functions/__fish_complete_tex.fish +++ b/share/functions/__fish_complete_tex.fish @@ -14,7 +14,7 @@ function __fish_complete_tex -d "Common completions for all tex commands" complete -c $argv -o interaction=nonstopmode -d "Set interation mode" complete -c $argv -o interaction=scrollmode -d "Set interation mode" complete -c $argv -o interaction=errorstopmode -d "Set interation mode" - complete -c $argv -o output-directory= -x -a "(__fish_complete_directories (commandline -ct))" -d "Output directory" + complete -c $argv -o output-directory= -x -a "(__fish_complete_directories (commandline -ct))" -d "Output directory" complete -c $argv -o shell-escape -d "Enable \write18{SHELL COMMAND}" complete -c $argv -o no-shell-escape -d "Disable \write18{SHELL COMMAND}" complete -c $argv -o src-specials -d "Insert source specials into the DVI file" diff --git a/share/functions/__fish_make_completion_signals.fish b/share/functions/__fish_make_completion_signals.fish index a8d978192..81347569d 100644 --- a/share/functions/__fish_make_completion_signals.fish +++ b/share/functions/__fish_make_completion_signals.fish @@ -2,7 +2,7 @@ function __fish_make_completion_signals --description 'Make list of kill signals set -q __kill_signals; and return 0 if kill -L ^/dev/null >/dev/null - # Debian and some related systems use 'kill -L' to write out a numbered list + # Debian and some related systems use 'kill -L' to write out a numbered list # of signals. Use this to complete on both number _and_ on signal name. complete -c kill -s L --description "List codes and names of available signals" set -g __kill_signals (kill -L | sed -e 's/\([0-9][0-9]*\) *\([A-Z,0-9][A-Z,0-9]*\)/\1 \2\n/g;s/ +/ /g' | sed -e 's/^ \+//' | sgrep -E '^[^ ]+') diff --git a/share/functions/__fish_print_help.fish b/share/functions/__fish_print_help.fish index 07df075c3..8e4d30004 100644 --- a/share/functions/__fish_print_help.fish +++ b/share/functions/__fish_print_help.fish @@ -8,7 +8,7 @@ function __fish_print_help --description "Print help message for the specified f case '*' set item $argv[1] end - + # Do nothing if the file does not exist if not test -e "$__fish_datadir/man/man1/$item.1" return diff --git a/share/functions/dirh.fish b/share/functions/dirh.fish index 639e920a2..44244e8c9 100644 --- a/share/functions/dirh.fish +++ b/share/functions/dirh.fish @@ -25,7 +25,7 @@ function dirh --description "Print the current directory history (the back- and set_color $fish_color_history_current echo -n -e $current$separator set_color normal - + # BSD seq 0 outputs '1 0' instead of nothing if count $dirnext > /dev/null for i in (seq (echo (count $dirnext)) -1 1) diff --git a/share/functions/funced.fish b/share/functions/funced.fish index f35a72fa4..399439fda 100644 --- a/share/functions/funced.fish +++ b/share/functions/funced.fish @@ -11,7 +11,7 @@ function funced --description 'Edit function definition' case -e --editor set editor $argv[2] set -e argv[2] - + case -i --interactive set interactive 1 @@ -83,7 +83,7 @@ function funced --description 'Edit function definition' if eval $editor $tmpname . $tmpname end - set -l stat $status + set -l stat $status rm -f $tmpname >/dev/null return $stat end diff --git a/share/functions/history.fish b/share/functions/history.fish index 73fea995e..b943719e5 100644 --- a/share/functions/history.fish +++ b/share/functions/history.fish @@ -12,7 +12,7 @@ function history --description "Deletes an item from history" set -l search_mode none - if test $argc -gt 0 + if test $argc -gt 0 for i in (seq $argc) switch $argv[$i] case --delete @@ -43,7 +43,7 @@ function history --description "Deletes an item from history" # Note this may end up passing --search twice to the builtin, # but that's harmless builtin history --search $argv - + case delete # Interactively delete history set -l found_items "" @@ -57,43 +57,43 @@ function history --description "Deletes an item from history" #Save changes after deleting item builtin history --save - return 0 + return 0 end - + set found_items_count (count $found_items) - if test $found_items_count -gt 0 + if test $found_items_count -gt 0 echo "[0] cancel" echo "[1] all" echo - + for i in (seq $found_items_count) printf "[%s] %s \n" (math $i + 1) $found_items[$i] end - + read --local --prompt "echo 'Delete which entries? > '" choice set choice (echo $choice | tr " " "\n") - + for i in $choice - + # Skip empty input, for example, if the user just hits return if test -z $i continue end - - #Following two validations could be embedded with "and" but I find the syntax kind of weird. + + #Following two validations could be embedded with "and" but I find the syntax kind of weird. if not echo $i | grep -E -q "^[0-9]+\$" - printf "Invalid input: %s\n" $i - continue - end - - if test $i -gt (math $found_items_count + 1) - printf "Invalid input : %s\n" $i + printf "Invalid input: %s\n" $i continue end - + + if test $i -gt (math $found_items_count + 1) + printf "Invalid input : %s\n" $i + continue + end + if test $i = "0" printf "Cancel\n" - return + return else if test $i = "1" for item in $found_items diff --git a/share/tools/create_manpage_completions.py b/share/tools/create_manpage_completions.py index 7b0c6de97..775fa5c37 100755 --- a/share/tools/create_manpage_completions.py +++ b/share/tools/create_manpage_completions.py @@ -4,10 +4,10 @@ # Run me like this: ./create_manpage_completions.py /usr/share/man/man1/* > man_completions.fish """ - = Siteshwar Vashisht - = 2012 + = Siteshwar Vashisht + = 2012 -Copyright (c) 2012, Siteshwar Vashisht +Copyright (c) 2012, Siteshwar Vashisht All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: @@ -47,7 +47,7 @@ def add_diagnostic(dgn, msg_verbosity = VERY_VERBOSE): # Add a diagnostic message, if msg_verbosity <= VERBOSITY if msg_verbosity <= VERBOSITY: diagnostic_output.append(' '*diagnostic_indent + dgn) - + def flush_diagnostics(where): if diagnostic_output: output_str = '\n'.join(diagnostic_output) + '\n' @@ -77,7 +77,7 @@ def unquoteSingleQuotes(data): if data[0] == '`' and data[len(data)-1] == '\'': data = data[1:len(data)-1] return data - + # Make a string of characters that are deemed safe in fish without needing to be escaped # Note that space is not included @@ -88,7 +88,7 @@ def fish_escape_single_quote(str): # If it has no non-safe chars, there's nothing to do if g_fish_safe_chars.issuperset(str): return str - + str = str.replace('\\', '\\\\') # Replace one backslash with two str = str.replace("'", "\\'") # Replace one single quote with a backslash-single-quote return "'" + str + "'" @@ -107,10 +107,10 @@ def builtcommand(options, description): fish_options = [] for option in man_optionlist: option = option.strip() - + # Skip some problematic cases if option in ['-', '--']: continue - + if option.startswith('--'): # New style long option (--recursive) fish_options.append('-l ' + fish_escape_single_quote(option[2:])) @@ -120,29 +120,29 @@ def builtcommand(options, description): elif option.startswith('-') and len(option) > 2: # Old style long option (-recursive) fish_options.append('-o ' + fish_escape_single_quote(option[1:])) - + # Determine which options are new (not already in existing_options) # Then add those to the existing options existing_options = already_output_completions.setdefault(CMDNAME, set()) fish_options = [opt for opt in fish_options if opt not in existing_options] existing_options.update(fish_options) - + # Maybe it's all for naught if not fish_options: return - - first_period = description.find(".") + + first_period = description.find(".") if first_period >= 45 or (first_period == -1 and len(description) > 45): description = description[:45] + '... [See Man Page]' elif first_period >= 0: description = description[:first_period] - + # Escape some more things description = fish_escape_single_quote(description) escaped_cmd = fish_escape_single_quote(CMDNAME) - + output_complete_command(escaped_cmd, fish_options, description, built_command_output) - + def removeGroffFormatting(data): # data = data.replace("\fI","") @@ -193,7 +193,7 @@ class Type1ManParser(ManParser): def parseManPage(self, manpage): options_section_regex = re.compile( "\.SH \"OPTIONS\"(.*?)(\.SH|\Z)", re.DOTALL) options_section_matched = re.search( options_section_regex, manpage) - + options_section = options_section_matched.group(0) # print options_section options_parts_regex = re.compile("\.PP(.*?)\.RE", re.DOTALL) @@ -208,7 +208,7 @@ class Type1ManParser(ManParser): elif (self.fallback2(options_section) ): return True return False - + while (options_matched != None): # print len(options_matched.groups()) # print options_matched.group() @@ -222,7 +222,7 @@ class Type1ManParser(ManParser): # print data if (len (data) > 1): #and len(data[1]) <= 300): optionName = data[0].strip() - + if ( optionName.find("-") == -1): add_diagnostic(optionName + " doesn't contain - ") # return False @@ -232,7 +232,7 @@ class Type1ManParser(ManParser): optionDescription = data[1].strip().replace("\n"," ") # print >> sys.stderr, "Option: ", optionName," Description: ", optionDescription , '\n' builtcommand(optionName, optionDescription) - + else: add_diagnostic('Unable to split option from description') return False @@ -283,7 +283,7 @@ class Type1ManParser(ManParser): return False while options_matched != None: data = options_matched.group(1) - + # print "Data is : ", data data = removeGroffFormatting(data) data = data.strip() @@ -293,7 +293,7 @@ class Type1ManParser(ManParser): # data = re.sub(trailing_num_regex, "", data) optionName = re.sub(trailing_num_regex, "", data[0].strip()) - + if ('-' not in optionName): add_diagnostic(optionName + " doesn't contain -") else: @@ -311,7 +311,7 @@ class Type1ManParser(ManParser): options_section = options_section[options_matched.end()-3:] options_matched = re.search(options_parts_regex, options_section) return True - + def name(self): return "Type1" @@ -328,7 +328,7 @@ class Type2ManParser(ManParser): def parseManPage(self, manpage): options_section_regex = re.compile( "\.SH OPTIONS(.*?)(\.SH|\Z)", re.DOTALL) options_section_matched = re.search( options_section_regex, manpage) - + # if (options_section_matched == None): # print "Falling Back" # options_section_regex = re.compile( "\.SH OPTIONS(.*?)$", re.DOTALL) @@ -372,13 +372,13 @@ class Type2ManParser(ManParser): else: # print >> sys.stderr, data add_diagnostic('Unable to split option from description') - + # return False options_section = options_section[options_matched.end()-3:] options_matched = re.search(options_parts_regex, options_section) - + def name(self): return "Type2" @@ -396,7 +396,7 @@ class Type3ManParser(ManParser): def parseManPage(self, manpage): options_section_regex = re.compile( "\.SH DESCRIPTION(.*?)(\.SH|\Z)", re.DOTALL) options_section_matched = re.search( options_section_regex, manpage) - + options_section = options_section_matched.group(1) # print options_section # sys.exit(1) @@ -452,7 +452,7 @@ class Type4ManParser(ManParser): def parseManPage(self, manpage): options_section_regex = re.compile( "\.SH FUNCTION LETTERS(.*?)(\.SH|\Z)", re.DOTALL) options_section_matched = re.search( options_section_regex, manpage) - + options_section = options_section_matched.group(1) # print options_section # sys.exit(1) @@ -499,7 +499,7 @@ class TypeDarwinManParser(ManParser): def isMyType(self, manpage): options_section_matched = compileAndSearch("\.S[hH] DESCRIPTION", manpage) return options_section_matched != None - + def trim_groff(self, line): # Remove initial period if line.startswith('.'): @@ -508,49 +508,49 @@ class TypeDarwinManParser(ManParser): while re.match('[A-Z][a-z]\s', line): line = line[3:] return line - + # Replace some groff escapes. There's a lot we don't bother to handle. def groff_replace_escapes(self, line): line = line.replace('\\ ', ' ') line = line.replace('\& ', '') return line - + def is_option(self, line): return line.startswith('.It Fl') - + def parseManPage(self, manpage): got_something = False lines = manpage.splitlines() # Discard lines until we get to ".sh Description" while lines and not (lines[0].startswith('.Sh DESCRIPTION') or lines[0].startswith('.SH DESCRIPTION')): lines.pop(0) - + while lines: # Pop until we get to the next option while lines and not self.is_option(lines[0]): lines.pop(0) - + if not lines: continue - + # Get the line and clean it up line = lines.pop(0) line = self.groff_replace_escapes(line) line = self.trim_groff(line) line = line.strip() if not line: continue - + # Extract the name name = line.split(None, 2)[0] - + # Extract the description desc = '' while lines and not self.is_option(lines[0]): # print "*", lines[0] desc = desc + lines.pop(0) - + # print "name: ", name - + if name == '-': # Skip double -- arguments continue @@ -561,9 +561,9 @@ class TypeDarwinManParser(ManParser): elif len(name) == 1: builtcommand('-' + name, desc) got_something = True - + return got_something - + def name(self): return "Darwin man parser" @@ -571,25 +571,25 @@ class TypeDarwinManParser(ManParser): class TypeDeroffManParser(ManParser): def isMyType(self, manpage): return True # We're optimists - + def is_option(self, line): return line.startswith('-') - + def could_be_description(self, line): return len(line) > 0 and not line.startswith('-') - + def parseManPage(self, manpage): d = Deroffer() d.deroff(manpage) output = d.get_output() lines = output.split('\n') - + got_something = False - + # Discard lines until we get to DESCRIPTION or OPTIONS while lines and not (lines[0].startswith('DESCRIPTION') or lines[0].startswith('OPTIONS') or lines[0].startswith('COMMAND OPTIONS')): lines.pop(0) - + # Look for BUGS and stop there for idx in range(len(lines)): line = lines[idx] @@ -597,32 +597,32 @@ class TypeDeroffManParser(ManParser): # Drop remaining elements lines[idx:] = [] break - + while lines: # Pop until we get to the next option while lines and not self.is_option(lines[0]): line = lines.pop(0) - + if not lines: continue - + options = lines.pop(0) - + # Pop until we get to either an empty line or a line starting with - description = '' while lines and self.could_be_description(lines[0]): if description: description += ' ' description += lines.pop(0) - + builtcommand(options, description) got_something = True - + return got_something - - + + def name(self): return "Deroffing man parser" - + # Return whether the file at the given path is overwritable # Raises IOError if it cannot be opened def file_is_overwritable(path): @@ -633,16 +633,16 @@ def file_is_overwritable(path): line = line.strip() if not line: continue - + # We look in the initial run of lines that start with # if not line.startswith('#'): break - + # See if this contains the magic word if 'Autogenerated' in line: result = True break - + file.close() return result @@ -658,7 +658,7 @@ def file_missing_or_overwritable(path): else: # Something else happened return False - + # Delete the file if it is autogenerated def cleanup_autogenerated_file(path): try: @@ -678,7 +678,7 @@ def parse_manpage_at_path(manpage_path, yield_to_dirs, output_directory): # Set up some diagnostics add_diagnostic('Considering ' + manpage_path) diagnostic_indent += 1 - + if manpage_path.endswith('.gz'): fd = gzip.open(manpage_path, 'r') manpage = fd.read() @@ -692,32 +692,32 @@ def parse_manpage_at_path(manpage_path, yield_to_dirs, output_directory): fd.close() manpage = str(manpage) - + # Get the "base" command, e.g. gcc.1.gz -> gcc cmd_base = CMDNAME.split('.', 1)[0] ignoredcommands = ["cc", "g++", "gcc", "c++", "cpp", "emacs", "gprof", "wget", "ld", "awk"] if cmd_base in ignoredcommands: return - + # Ignore perl's gazillion man pages ignored_prefixes = ['perl', 'zsh'] for prefix in ignored_prefixes: if cmd_base.startswith(prefix): return - + # Ignore the millions of links to BUILTIN(1) if manpage.find('BUILTIN 1') != -1: return - + # Clear the output list built_command_output[:] = [] - + if DEROFF_ONLY: parsers = [TypeDeroffManParser()] else: parsers = [Type1ManParser(), Type2ManParser(), Type4ManParser(), Type3ManParser(), TypeDarwinManParser(), TypeDeroffManParser()] parsersToTry = [p for p in parsers if p.isMyType(manpage)] - + success = False if not parsersToTry: add_diagnostic(manpage_path + ": Not supported") @@ -731,7 +731,7 @@ def parse_manpage_at_path(manpage_path, yield_to_dirs, output_directory): if success: PARSER_INFO.setdefault(parser_name, []).append(CMDNAME) break - + if success: if WRITE_TO_STDOUT: output_file = sys.stdout @@ -742,13 +742,13 @@ def parse_manpage_at_path(manpage_path, yield_to_dirs, output_directory): output_file = open(fullpath, 'w') else: add_diagnostic("Not overwriting the file at '%s'" % fullpath) - + except IOError as err: add_diagnostic("Unable to open file '%s': error(%d): %s" % (fullpath, err.errno, err.strerror)) return False - + built_command_output.insert(0, "# %s: %s" % (CMDNAME, parser.name())) - + # Output the magic word Autogenerated so we can tell if we can overwrite this built_command_output.insert(1, "# Autogenerated from man pages") built_command_output.insert(2, "# using " + parser_name) @@ -780,22 +780,22 @@ def should_skip_man_page(output_path, filename, yield_to_dirs): # No reason to skip if we're writing to stdout if WRITE_TO_STDOUT: return false - + # Check all the yield directories for yield_dir in yield_to_dirs: test_path = os.path.join(yield_dir, filename) if os.path.isfile(test_path): # Yield to the existing file return true - + # See if there's a hand-written file already if not file_missing_or_overwritable(output_path): return true - + # We made it through, so don't skip return false - - + + def parse_and_output_man_pages(paths, output_directory, yield_to_dirs, show_progress): global diagnostic_indent, CMDNAME @@ -808,12 +808,12 @@ def parse_and_output_man_pages(paths, output_directory, yield_to_dirs, show_prog print("Parsing man pages and writing completions to {0}".format(output_directory)) for manpage_path in paths: index += 1 - + # Get the "base" command, e.g. gcc.1.gz -> gcc man_file_name = os.path.basename(manpage_path) CMDNAME = man_file_name.split('.', 1)[0] output_file_name = CMDNAME + '.fish' - + # Show progress if we're doing that if show_progress: progress_str = ' {0} / {1} : {2}'.format((str(index).rjust(padding_len)), total_count, man_file_name) @@ -822,7 +822,7 @@ def parse_and_output_man_pages(paths, output_directory, yield_to_dirs, show_prog last_progress_string_length = len(progress_str) sys.stdout.write("\r{0}\r".format(padded_progress_str)) sys.stdout.flush() - + # Maybe we want to skip this item skip = False if not WRITE_TO_STDOUT: @@ -840,7 +840,7 @@ def parse_and_output_man_pages(paths, output_directory, yield_to_dirs, show_prog # Now skip if requested if skip: continue - + try: if parse_manpage_at_path(manpage_path, yield_to_dirs, output_directory): successful_count += 1 @@ -878,8 +878,8 @@ def get_paths_from_manpath(): for name in names: result.append(os.path.join(directory_path, name)) return result - - + + def usage(script_name): print("Usage: {0} [-v, --verbose] [-s, --stdout] [-d, --directory] [-p, --progress] files...".format(script_name)) @@ -901,11 +901,11 @@ if __name__ == "__main__": print(err.msg) # will print something like "option -a not recognized" usage(script_name) sys.exit(2) - + # If a completion already exists in one of the yield-to directories, then don't overwrite it # And even delete an existing autogenerated one yield_to_dirs = [] - + use_manpath, show_progress, custom_dir = False, False, False output_directory = '' for opt, value in opts: @@ -930,15 +930,15 @@ if __name__ == "__main__": DEROFF_ONLY = True else: assert False, "unhandled option" - + if use_manpath: # Fetch all man1 files from the manpath file_paths.extend(get_paths_from_manpath()) - + if not file_paths: print("No paths specified") sys.exit(0) - + if not WRITE_TO_STDOUT and not output_directory: # Default to ~/.config/fish/completions/ # Create it if it doesn't exist @@ -948,7 +948,7 @@ if __name__ == "__main__": except OSError as e: if e.errno != errno.EEXIST: raise - + if True: parse_and_output_man_pages(file_paths, output_directory, yield_to_dirs, show_progress) else: @@ -957,7 +957,7 @@ if __name__ == "__main__": cProfile.run('parse_and_output_man_pages(file_paths, output_directory, yield_to_dirs, show_progress)', 'fooprof') p = pstats.Stats('fooprof') p.sort_stats('cumulative').print_stats(100) - + # Here we can write out all the parser infos if False: for name in PARSER_INFO: diff --git a/share/tools/deroff.py b/share/tools/deroff.py index eb8b0d6c5..13737aa16 100755 --- a/share/tools/deroff.py +++ b/share/tools/deroff.py @@ -78,17 +78,17 @@ class Deroffer: '/O': '\330', 'oA': '\305', 'oa': '\345', - + # Ligatures 'fi': 'fi', 'ff': 'ff', 'fl': 'fl', - + 'Fi': 'ffi', 'Ff': 'fff', 'Fl': 'ffl' } - + g_specs = { 'mi': '-', 'en': '-', @@ -212,7 +212,7 @@ class Deroffer: '/_': "/_", 'lz': "<>", 'an': '-', - + # Output Greek '*A': "Alpha", '*B': "Beta", @@ -267,16 +267,16 @@ class Deroffer: '*z': "zeta", 'ts': "sigma", } - + g_re_word = re.compile(r'[a-zA-Z_]+') # equivalent to the word() method g_re_number = re.compile(r'[+-]?\d+') # equivalent to the number() method g_re_esc_char = re.compile(r"""([a-zA-Z_]) | # Word ([+-]?\d) | # Number \\ # Backslash (for escape seq) """, re.VERBOSE) - + g_re_not_backslash_or_whitespace = re.compile(r'[^ \t\n\r\f\v\\]+') # Match a sequence of not backslash or whitespace - + g_re_newline_collapse = re.compile(r'\n{3,}') g_re_font = re.compile(r"""\\f( # Starts with backslash f @@ -284,10 +284,10 @@ class Deroffer: (\[\S*?\]) | # Open bracket, zero or more printable characters, then close bracket \S) # Any printable character """, re.VERBOSE) - + # This gets filled in in __init__ below g_macro_dict = False - + def __init__(self): self.reg_table = {} self.tr_from = '' @@ -310,13 +310,13 @@ class Deroffer: self.ignore_sonx = False self.output = [] self.name = '' - + self.OPTIONS = 0 self.FORMAT = 1 self.DATA = 2 - + # words is uninteresting and should be treated as false - + if not Deroffer.g_macro_dict: Deroffer.g_macro_dict = { 'SH': Deroffer.macro_sh, @@ -363,41 +363,41 @@ class Deroffer: 'tr': Deroffer.macro_tr, 'sp': Deroffer.macro_sp } - + def flush_output(self, where): if where: where.write(self.get_output()) self.output[:] = [] - + def get_output(self): res = ''.join(self.output) clean_res = Deroffer.g_re_newline_collapse.sub('\n', res) return clean_res - + def putchar(self, c): self.output.append(c) return c - + # This gets swapped in in place of condputs the first time tr gets modified def condputs_tr(self, str): special = self.pic or self.eqn or self.refer or self.macro or (self.skiplists and self.inlist) or (self.skipheaders and self.inheader) if not special: self.output.append(str.translate(self.tr)) - + def condputs(self, str): special = self.pic or self.eqn or self.refer or self.macro or (self.skiplists and self.inlist) or (self.skipheaders and self.inheader) - if not special: + if not special: self.output.append(str) - + def str_at(self, idx): return self.s[idx:idx+1] - + def skip_char(self, amt=1): self.s = self.s[amt:] - + def skip_leading_whitespace(self): self.s = self.s.lstrip() - + def is_white(self, idx): # Note this returns false for empty strings (idx >= len(self.s)) return self.s[idx:idx+1].isspace() @@ -415,7 +415,7 @@ class Deroffer: if not match: return False self.skip_char(match.end()) return True - + def font2(self): if self.s[0:2] == '\\f': c = self.str_at(2) @@ -430,7 +430,7 @@ class Deroffer: self.skip_char(3) return True return False - + def comment(self): # Here we require that the string start with \" while self.str_at(0) and self.str_at(0) != '\n': self.skip_char() @@ -485,7 +485,7 @@ class Deroffer: self.skip_char(3) else: return False - + if reg in self.reg_table: old_s = self.s self.s = self.reg_table[reg] @@ -518,7 +518,7 @@ class Deroffer: return True else: return False - + def esc(self): # We require that the string start with backslash c = self.s[1:2] @@ -544,11 +544,11 @@ class Deroffer: got_something = True self.condputs(match.group(0)) self.skip_char(match.end(0)) - + # Consume all specials while self.spec(): if not self.specletter: break - + return got_something @@ -571,7 +571,7 @@ class Deroffer: ch = self.str_at(idx) return ch.isalpha() or ch == '_' # underscore is used in C identifiers - + def digit(self, idx): ch = self.str_at(idx) return ch.isdigit() @@ -584,7 +584,7 @@ class Deroffer: self.condputs(match.group(0)) self.skip_char(match.end()) return True - + def esc_char_backslash(self): # Like esc_char, but we know the string starts with a backslash c = self.s[1:2] @@ -620,7 +620,7 @@ class Deroffer: return True else: return False - + def text_arg(self): # PCA: The deroff.c textArg() disallowed quotes at the start of an argument # I'm not sure if this was a bug or not @@ -632,21 +632,21 @@ class Deroffer: self.condputs(match.group(0)) self.skip_char(match.end(0)) got_something = True - + # Next is either an escape, or whitespace, or the end # If it's the whitespace or the end, we're done if not self.s or self.is_white(0): return got_something - + # Try an escape if not self.esc_char(): # Some busted escape? Just output it self.condputs(self.str_at(0)) self.skip_char() got_something = True - - - + + + def text_arg2(self): if not self.esc_char(): if self.s and not self.is_white(0): @@ -673,95 +673,95 @@ class Deroffer: # Did not find a header string self.inheader = False self.nobody = True - + def macro_ss_ip(self): self.nobody = True return False - + def macro_i_ir(self): pass return False - + def macro_Nm(self): if self.s == 'Nm\n': self.condputs(self.name) else: self.name = self.s[3:].strip() + ' ' return True - + def macro_close_bracket(self): self.refer = False return False - + def macro_ps(self): if self.is_white(2): self.pic = True self.condputs('\n') return True - + def macro_pe(self): if self.is_white(2): self.pic = False self.condputs('\n') return True - + def macro_ts(self): if self.is_white(2): self.tbl, self.tblstate = True, self.OPTIONS self.condputs('\n') return True - + def macro_t_and(self): if self.is_white(2): self.tbl, self.tblstate = True, self.FORMAT self.condputs('\n') return True - + def macro_te(self): if self.is_white(2): self.tbl = False self.condputs('\n') return True - + def macro_eq(self): if self.is_white(2): self.eqn = True self.condputs('\n') return True - + def macro_en(self): if self.is_white(2): self.eqn = False self.condputs('\n') return True - + def macro_r1(self): if self.is_white(2): self.refer2 = True self.condputs('\n') return True - + def macro_r2(self): if self.is_white(2): self.refer2 = False self.condputs('\n') return True - + def macro_de(self): macro=True self.condputs('\n') return True - + def macro_bl_vl(self): if self.is_white(2): self.inlist = True self.condputs('\n') return True - + def macro_bv(self): if self.str_at(2) == 'L' and self.white(self.str_at(3)): self.inlist = True self.condputs('\n') return True - + def macro_le(self): if self.is_white(2): self.inlist = False self.condputs('\n') return True - + def macro_lp_pp(self): self.condputs('\n') return True - + def macro_ds(self): self.skip_char(2) self.skip_leading_whitespace() @@ -774,13 +774,13 @@ class Deroffer: self.reg_table[name] = value self.condputs('\n') return True - + def macro_so_nx(self): # We always ignore include directives # deroff.c for some reason allowed this to fall through to the 'tr' case # I think that was just a bug so I won't replicate it return True - + def macro_tr(self): self.skip_char(2) self.skip_leading_whitespace() @@ -791,7 +791,7 @@ class Deroffer: if not ns or ns == '\n': ns = ' ' self.tr_from += c self.tr_to += ns - + # Update our table, then swap in the slower tr-savvy condputs try: #Python2 self.tr = string.maketrans(self.tr_from, self.tr_to) @@ -799,11 +799,11 @@ class Deroffer: self.tr = "".maketrans(self.tr_from, self.tr_to) self.condputs = self.condputs_tr return True - + def macro_sp(self): self.condputs('\n') return True - + def macro_other(self): self.condputs('\n') return True @@ -830,16 +830,16 @@ class Deroffer: self.macro = False self.condputs('\n') return True - + self.nobody = False s0s1 = self.s[0:2] - + macro_func = Deroffer.g_macro_dict.get(s0s1, Deroffer.macro_other) if macro_func(self): return True - + if self.skipheaders and self.nobody: return True - + self.skip_leading_whitespace() while self.s and not self.is_white(0): self.skip_char() self.skip_leading_whitespace() @@ -873,7 +873,7 @@ class Deroffer: self.macro = False self.condputs('\n') return True - + self.nobody = False s0s1 = self.s[0:2] if s0s1 == 'SH': @@ -973,14 +973,14 @@ class Deroffer: if not ns or ns == '\n': ns = ' ' self.tr_from += c self.tr_to += ns - + # Update our table, then swap in the slower tr-savvy condputs try: #Python2 self.tr = string.maketrans(self.tr_from, self.tr_to) except AttributeError: #Python3 self.tr = "".maketrans(self.tr_from, self.tr_to) self.condputs = self.condputs_tr - + return True elif s0s1 in ['sp']: self.condputs('\n') @@ -988,9 +988,9 @@ class Deroffer: else: self.condputs('\n') return True - + if self.skipheaders and self.nobody: return True - + self.skip_leading_whitespace() while self.s and not self.is_white(0): self.skip_char() self.skip_leading_whitespace() @@ -1011,21 +1011,21 @@ class Deroffer: # deroff.c has a bug where it can loop forever here...we try to work around it self.skip_char() else: # Parse option - + option = self.s arg = '' - + idx = 0 while option[idx:idx+1].isalpha(): idx += 1 - + if option[idx:idx+1] == '(': option = option[:idx] self.s = self.s[idx+1:] arg = self.s else: self.s = '' - + if arg: idx = arg.find(')') if idx != -1: @@ -1034,18 +1034,18 @@ class Deroffer: else: #self.skip_char() pass - + if option.lower() == 'tab': self.tblTab = arg[0:1] - + self.tblstate = self.FORMAT self.condputs('\n') - + elif self.tblstate == self.FORMAT: while self.s and self.str_at(0) != '.' and self.str_at(0) != '\n': self.skip_leading_whitespace() if self.str_at(0): self.skip_char() - + if self.str_at(0) == '.': self.tblstate = self.DATA self.condputs('\n') elif self.tblstate == self.DATA: @@ -1062,7 +1062,7 @@ class Deroffer: else: self.text() return True - + def deroff(self, str): lines = str.split('\n') for line in lines: @@ -1086,7 +1086,7 @@ def deroff_files(files): d.flush_output(sys.stdout) f.close() - + if __name__ == "__main__": import gzip diff --git a/signal.cpp b/signal.cpp index 2c51e9d2f..d25f2b063 100644 --- a/signal.cpp +++ b/signal.cpp @@ -36,18 +36,18 @@ The library for various signal related issues */ struct lookup_entry { - /** - Signal id - */ - int signal; - /** - Signal name - */ - const wchar_t *name; - /** - Signal description - */ - const wchar_t *desc; + /** + Signal id + */ + int signal; + /** + Signal name + */ + const wchar_t *name; + /** + Signal description + */ + const wchar_t *desc; }; /** @@ -63,363 +63,363 @@ static int block_count=0; static const struct lookup_entry lookup[] = { #ifdef SIGHUP - { - SIGHUP, - L"SIGHUP", - N_( L"Terminal hung up" ) - } - , + { + SIGHUP, + L"SIGHUP", + N_( L"Terminal hung up" ) + } + , #endif #ifdef SIGINT - { - SIGINT, - L"SIGINT", - N_( L"Quit request from job control (^C)" ) - } - , + { + SIGINT, + L"SIGINT", + N_( L"Quit request from job control (^C)" ) + } + , #endif #ifdef SIGQUIT - { - SIGQUIT, - L"SIGQUIT", - N_( L"Quit request from job control with core dump (^\\)" ) - } - , + { + SIGQUIT, + L"SIGQUIT", + N_( L"Quit request from job control with core dump (^\\)" ) + } + , #endif #ifdef SIGILL - { - SIGILL, - L"SIGILL", - N_( L"Illegal instruction" ) - } - , + { + SIGILL, + L"SIGILL", + N_( L"Illegal instruction" ) + } + , #endif #ifdef SIGTRAP - { - SIGTRAP, - L"SIGTRAP", - N_( L"Trace or breakpoint trap" ) - } - , + { + SIGTRAP, + L"SIGTRAP", + N_( L"Trace or breakpoint trap" ) + } + , #endif #ifdef SIGABRT - { - SIGABRT, - L"SIGABRT", - N_( L"Abort" ) - } - , + { + SIGABRT, + L"SIGABRT", + N_( L"Abort" ) + } + , #endif #ifdef SIGBUS - { - SIGBUS, - L"SIGBUS", - N_( L"Misaligned address error" ) - } - , + { + SIGBUS, + L"SIGBUS", + N_( L"Misaligned address error" ) + } + , #endif #ifdef SIGFPE - { - SIGFPE, - L"SIGFPE", - N_( L"Floating point exception" ) - } - , + { + SIGFPE, + L"SIGFPE", + N_( L"Floating point exception" ) + } + , #endif #ifdef SIGKILL - { - SIGKILL, - L"SIGKILL", - N_( L"Forced quit" ) - } - , + { + SIGKILL, + L"SIGKILL", + N_( L"Forced quit" ) + } + , #endif #ifdef SIGUSR1 - { - SIGUSR1, - L"SIGUSR1", - N_( L"User defined signal 1" ) - } - , + { + SIGUSR1, + L"SIGUSR1", + N_( L"User defined signal 1" ) + } + , #endif #ifdef SIGUSR2 - { - SIGUSR2, L"SIGUSR2", - N_( L"User defined signal 2" ) - } - , + { + SIGUSR2, L"SIGUSR2", + N_( L"User defined signal 2" ) + } + , #endif #ifdef SIGSEGV - { - SIGSEGV, - L"SIGSEGV", - N_( L"Address boundary error" ) - } - , + { + SIGSEGV, + L"SIGSEGV", + N_( L"Address boundary error" ) + } + , #endif #ifdef SIGPIPE - { - SIGPIPE, - L"SIGPIPE", - N_( L"Broken pipe" ) - } - , + { + SIGPIPE, + L"SIGPIPE", + N_( L"Broken pipe" ) + } + , #endif #ifdef SIGALRM - { - SIGALRM, - L"SIGALRM", - N_( L"Timer expired" ) - } - , + { + SIGALRM, + L"SIGALRM", + N_( L"Timer expired" ) + } + , #endif #ifdef SIGTERM - { - SIGTERM, - L"SIGTERM", - N_( L"Polite quit request" ) - } - , + { + SIGTERM, + L"SIGTERM", + N_( L"Polite quit request" ) + } + , #endif #ifdef SIGCHLD - { - SIGCHLD, - L"SIGCHLD", - N_( L"Child process status changed" ) - } - , + { + SIGCHLD, + L"SIGCHLD", + N_( L"Child process status changed" ) + } + , #endif #ifdef SIGCONT - { - SIGCONT, - L"SIGCONT", - N_( L"Continue previously stopped process" ) - } - , + { + SIGCONT, + L"SIGCONT", + N_( L"Continue previously stopped process" ) + } + , #endif #ifdef SIGSTOP - { - SIGSTOP, - L"SIGSTOP", - N_( L"Forced stop" ) - } - , + { + SIGSTOP, + L"SIGSTOP", + N_( L"Forced stop" ) + } + , #endif #ifdef SIGTSTP - { - SIGTSTP, - L"SIGTSTP", - N_( L"Stop request from job control (^Z)" ) - } - , + { + SIGTSTP, + L"SIGTSTP", + N_( L"Stop request from job control (^Z)" ) + } + , #endif #ifdef SIGTTIN - { - SIGTTIN, - L"SIGTTIN", - N_( L"Stop from terminal input" ) - } - , + { + SIGTTIN, + L"SIGTTIN", + N_( L"Stop from terminal input" ) + } + , #endif #ifdef SIGTTOU - { - SIGTTOU, - L"SIGTTOU", - N_( L"Stop from terminal output" ) - } - , + { + SIGTTOU, + L"SIGTTOU", + N_( L"Stop from terminal output" ) + } + , #endif #ifdef SIGURG - { - SIGURG, - L"SIGURG", - N_( L"Urgent socket condition" ) - } - , + { + SIGURG, + L"SIGURG", + N_( L"Urgent socket condition" ) + } + , #endif #ifdef SIGXCPU - { - SIGXCPU, - L"SIGXCPU", - N_( L"CPU time limit exceeded" ) - } - , + { + SIGXCPU, + L"SIGXCPU", + N_( L"CPU time limit exceeded" ) + } + , #endif #ifdef SIGXFSZ - { - SIGXFSZ, - L"SIGXFSZ", - N_( L"File size limit exceeded" ) - } - , + { + SIGXFSZ, + L"SIGXFSZ", + N_( L"File size limit exceeded" ) + } + , #endif #ifdef SIGVTALRM - { - SIGVTALRM, - L"SIGVTALRM", - N_( L"Virtual timer expired" ) - } - , + { + SIGVTALRM, + L"SIGVTALRM", + N_( L"Virtual timer expired" ) + } + , #endif #ifdef SIGPROF - { - SIGPROF, - L"SIGPROF", - N_( L"Profiling timer expired" ) - } - , + { + SIGPROF, + L"SIGPROF", + N_( L"Profiling timer expired" ) + } + , #endif #ifdef SIGWINCH - { - SIGWINCH, - L"SIGWINCH", - N_( L"Window size change" ) - } - , + { + SIGWINCH, + L"SIGWINCH", + N_( L"Window size change" ) + } + , #endif #ifdef SIGWIND - { - SIGWIND, - L"SIGWIND", - N_( L"Window size change" ) - } - , + { + SIGWIND, + L"SIGWIND", + N_( L"Window size change" ) + } + , #endif #ifdef SIGIO - { - SIGIO, - L"SIGIO", - N_( L"I/O on asynchronous file descriptor is possible" ) - } - , + { + SIGIO, + L"SIGIO", + N_( L"I/O on asynchronous file descriptor is possible" ) + } + , #endif #ifdef SIGPWR - { - SIGPWR, - L"SIGPWR", - N_( L"Power failure" ) - } - , + { + SIGPWR, + L"SIGPWR", + N_( L"Power failure" ) + } + , #endif #ifdef SIGSYS - { - SIGSYS, - L"SIGSYS", - N_( L"Bad system call" ) - } - , + { + SIGSYS, + L"SIGSYS", + N_( L"Bad system call" ) + } + , #endif #ifdef SIGINFO - { - SIGINFO, - L"SIGINFO", - N_( L"Information request" ) - } - , + { + SIGINFO, + L"SIGINFO", + N_( L"Information request" ) + } + , #endif #ifdef SIGSTKFLT - { - SIGSTKFLT, - L"SISTKFLT", - N_( L"Stack fault" ) - } - , + { + SIGSTKFLT, + L"SISTKFLT", + N_( L"Stack fault" ) + } + , #endif #ifdef SIGEMT - { - SIGEMT, - L"SIGEMT", - N_( L"Emulator trap" ) - } - , + { + SIGEMT, + L"SIGEMT", + N_( L"Emulator trap" ) + } + , #endif #ifdef SIGIOT - { - SIGIOT, - L"SIGIOT", - N_( L"Abort (Alias for SIGABRT)" ) - } - , + { + SIGIOT, + L"SIGIOT", + N_( L"Abort (Alias for SIGABRT)" ) + } + , #endif #ifdef SIGUNUSED - { - SIGUNUSED, - L"SIGUNUSED", - N_( L"Unused signal" ) - } - , + { + SIGUNUSED, + L"SIGUNUSED", + N_( L"Unused signal" ) + } + , #endif - { - 0, - 0, - 0 - } + { + 0, + 0, + 0 + } } - ; + ; /** - Test if \c name is a string describing the signal named \c canonical. + Test if \c name is a string describing the signal named \c canonical. */ -static int match_signal_name( const wchar_t *canonical, - const wchar_t *name ) +static int match_signal_name( const wchar_t *canonical, + const wchar_t *name ) { - if( wcsncasecmp( name, L"sig", 3 )==0) - name +=3; + if( wcsncasecmp( name, L"sig", 3 )==0) + name +=3; - return wcscasecmp( canonical+3,name ) == 0; + return wcscasecmp( canonical+3,name ) == 0; } int wcs2sig( const wchar_t *str ) { - int i; - wchar_t *end=0; - - for( i=0; lookup[i].desc ; i++ ) - { - if( match_signal_name( lookup[i].name, str) ) - { - return lookup[i].signal; - } - } - errno=0; - int res = fish_wcstoi( str, &end, 10 ); - if( !errno && res>=0 && !*end ) - return res; + int i; + wchar_t *end=0; - return -1; + for( i=0; lookup[i].desc ; i++ ) + { + if( match_signal_name( lookup[i].name, str) ) + { + return lookup[i].signal; + } + } + errno=0; + int res = fish_wcstoi( str, &end, 10 ); + if( !errno && res>=0 && !*end ) + return res; + + return -1; } const wchar_t *sig2wcs( int sig ) { - int i; + int i; - for( i=0; lookup[i].desc ; i++ ) - { - if( lookup[i].signal == sig ) - { - return lookup[i].name; - } - } + for( i=0; lookup[i].desc ; i++ ) + { + if( lookup[i].signal == sig ) + { + return lookup[i].name; + } + } - return _(L"Unknown"); + return _(L"Unknown"); } const wchar_t *signal_get_desc( int sig ) { - int i; + int i; - for( i=0; lookup[i].desc ; i++ ) - { - if( lookup[i].signal == sig ) - { - return _(lookup[i].desc); - } - } + for( i=0; lookup[i].desc ; i++ ) + { + if( lookup[i].signal == sig ) + { + return _(lookup[i].desc); + } + } - return _(L"Unknown"); + return _(L"Unknown"); } /** @@ -429,8 +429,8 @@ static void default_handler(int signal, siginfo_t *info, void *context) { if (event_is_signal_observed(signal)) { - event_fire_signal(signal); - } + event_fire_signal(signal); + } } /** @@ -438,8 +438,8 @@ static void default_handler(int signal, siginfo_t *info, void *context) */ static void handle_winch( int sig, siginfo_t *info, void *context ) { - common_handle_winch( sig ); - default_handler( sig, 0, 0 ); + common_handle_winch( sig ); + default_handler( sig, 0, 0 ); } /** @@ -448,14 +448,14 @@ static void handle_winch( int sig, siginfo_t *info, void *context ) */ static void handle_hup( int sig, siginfo_t *info, void *context ) { - if (event_is_signal_observed(SIGHUP)) - { - default_handler(sig, 0, 0); - } - else - { - reader_exit(1, 1); - } + if (event_is_signal_observed(SIGHUP)) + { + default_handler(sig, 0, 0); + } + else + { + reader_exit(1, 1); + } } /** @@ -464,8 +464,8 @@ static void handle_hup( int sig, siginfo_t *info, void *context ) */ static void handle_int( int sig, siginfo_t *info, void *context ) { - reader_handle_int( sig ); - default_handler( sig, info, context); + reader_handle_int( sig ); + default_handler( sig, info, context); } /** @@ -473,23 +473,23 @@ static void handle_int( int sig, siginfo_t *info, void *context ) */ static void handle_chld( int sig, siginfo_t *info, void *context ) { - job_handle_signal( sig, info, context ); - default_handler( sig, info, context); + job_handle_signal( sig, info, context ); + default_handler( sig, info, context); } void signal_reset_handlers() { - int i; - - struct sigaction act; - sigemptyset( & act.sa_mask ); - act.sa_flags=0; - act.sa_handler=SIG_DFL; - - for( i=0; lookup[i].desc ; i++ ) - { - sigaction( lookup[i].signal, &act, 0); - } + int i; + + struct sigaction act; + sigemptyset( & act.sa_mask ); + act.sa_flags=0; + act.sa_handler=SIG_DFL; + + for( i=0; lookup[i].desc ; i++ ) + { + sigaction( lookup[i].signal, &act, 0); + } } @@ -498,187 +498,187 @@ void signal_reset_handlers() */ void signal_set_handlers() { - struct sigaction act; + struct sigaction act; - if( get_is_interactive() == -1 ) - return; - - sigemptyset( & act.sa_mask ); - act.sa_flags=SA_SIGINFO; - act.sa_sigaction = &default_handler; - - /* - First reset everything to a use default_handler, a function - whose sole action is to fire of an event - */ - 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. - */ - sigaction( SIGPIPE, &act, 0); + if( get_is_interactive() == -1 ) + return; - if( get_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); + sigemptyset( & act.sa_mask ); + act.sa_flags=SA_SIGINFO; + act.sa_sigaction = &default_handler; - act.sa_sigaction = &handle_int; - act.sa_flags = SA_SIGINFO; - if( sigaction( SIGINT, &act, 0) ) - { - wperror( L"sigaction" ); - FATAL_EXIT(); - } + /* + First reset everything to a use default_handler, a function + whose sole action is to fire of an event + */ + 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. + */ + sigaction( SIGPIPE, &act, 0); + + if( get_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_sigaction = &handle_int; + act.sa_flags = SA_SIGINFO; + if( sigaction( SIGINT, &act, 0) ) + { + wperror( L"sigaction" ); + FATAL_EXIT(); + } + + act.sa_sigaction = &handle_chld; + act.sa_flags = SA_SIGINFO; + if( sigaction( SIGCHLD, &act, 0) ) + { + wperror( L"sigaction" ); + FATAL_EXIT(); + } - act.sa_sigaction = &handle_chld; - act.sa_flags = SA_SIGINFO; - if( sigaction( SIGCHLD, &act, 0) ) - { - wperror( L"sigaction" ); - FATAL_EXIT(); - } - #ifdef SIGWINCH - act.sa_flags = SA_SIGINFO; - act.sa_sigaction= &handle_winch; - if( sigaction( SIGWINCH, &act, 0 ) ) - { - wperror( L"sigaction" ); - FATAL_EXIT(); - } + act.sa_flags = SA_SIGINFO; + act.sa_sigaction= &handle_winch; + if( sigaction( SIGWINCH, &act, 0 ) ) + { + wperror( L"sigaction" ); + FATAL_EXIT(); + } #endif - act.sa_flags = SA_SIGINFO; - act.sa_sigaction= &handle_hup; - if( sigaction( SIGHUP, &act, 0 ) ) - { - wperror( L"sigaction" ); - FATAL_EXIT(); - } + act.sa_flags = SA_SIGINFO; + act.sa_sigaction= &handle_hup; + if( sigaction( SIGHUP, &act, 0 ) ) + { + wperror( L"sigaction" ); + FATAL_EXIT(); + } - } - else - { - /* - Non-interactive. Ignore interrupt, check exit status of - processes to determine result instead. - */ - act.sa_handler=SIG_IGN; + } + 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); + sigaction( SIGINT, &act, 0); + sigaction( SIGQUIT, &act, 0); - act.sa_handler=SIG_DFL; + act.sa_handler=SIG_DFL; + + act.sa_sigaction = &handle_chld; + act.sa_flags = SA_SIGINFO; + if( sigaction( SIGCHLD, &act, 0) ) + { + wperror( L"sigaction" ); + exit_without_destructors(1); + } + } - act.sa_sigaction = &handle_chld; - act.sa_flags = SA_SIGINFO; - if( sigaction( SIGCHLD, &act, 0) ) - { - wperror( L"sigaction" ); - exit_without_destructors(1); - } - } - } void signal_handle( int sig, int do_handle ) { - struct sigaction act; - - /* - These should always be handled - */ - if( (sig == SIGINT) || - (sig == SIGQUIT) || - (sig == SIGTSTP) || - (sig == SIGTTIN) || - (sig == SIGTTOU) || - (sig == SIGCHLD) ) - return; - - sigemptyset( &act.sa_mask ); - if( do_handle ) - { - act.sa_flags = SA_SIGINFO; - act.sa_sigaction = &default_handler; - } - else - { - act.sa_flags = 0; - act.sa_handler = SIG_DFL; - } - - sigaction( sig, &act, 0); + struct sigaction act; + + /* + These should always be handled + */ + if( (sig == SIGINT) || + (sig == SIGQUIT) || + (sig == SIGTSTP) || + (sig == SIGTTIN) || + (sig == SIGTTOU) || + (sig == SIGCHLD) ) + return; + + sigemptyset( &act.sa_mask ); + if( do_handle ) + { + act.sa_flags = SA_SIGINFO; + act.sa_sigaction = &default_handler; + } + else + { + act.sa_flags = 0; + act.sa_handler = SIG_DFL; + } + + sigaction( sig, &act, 0); } void get_signals_with_handlers(sigset_t *set) { sigemptyset(set); - for( int i=0; lookup[i].desc ; i++ ) - { + for( int i=0; lookup[i].desc ; i++ ) + { struct sigaction act = {}; - sigaction(lookup[i].signal, NULL, &act); + sigaction(lookup[i].signal, NULL, &act); if (act.sa_handler != SIG_DFL) sigaddset(set, lookup[i].signal); - } + } } void signal_block() { ASSERT_IS_MAIN_THREAD(); - sigset_t chldset; - - if( !block_count ) - { - sigfillset( &chldset ); - VOMIT_ON_FAILURE(pthread_sigmask(SIG_BLOCK, &chldset, NULL)); - } - - block_count++; -// debug( 0, L"signal block level increased to %d", block_count ); + sigset_t chldset; + + if( !block_count ) + { + sigfillset( &chldset ); + VOMIT_ON_FAILURE(pthread_sigmask(SIG_BLOCK, &chldset, NULL)); + } + + block_count++; +// debug( 0, L"signal block level increased to %d", block_count ); } void signal_unblock() { ASSERT_IS_MAIN_THREAD(); - sigset_t chldset; + sigset_t chldset; - block_count--; + block_count--; - if( block_count < 0 ) - { - debug( 0, _( L"Signal block mismatch" ) ); - bugreport(); - FATAL_EXIT(); - } - - if( !block_count ) - { - sigfillset( &chldset ); - VOMIT_ON_FAILURE(pthread_sigmask(SIG_UNBLOCK, &chldset, 0)); - } -// debug( 0, L"signal block level decreased to %d", block_count ); + if( block_count < 0 ) + { + debug( 0, _( L"Signal block mismatch" ) ); + bugreport(); + FATAL_EXIT(); + } + + if( !block_count ) + { + sigfillset( &chldset ); + VOMIT_ON_FAILURE(pthread_sigmask(SIG_UNBLOCK, &chldset, 0)); + } +// debug( 0, L"signal block level decreased to %d", block_count ); } int signal_is_blocked() { - return !!block_count; + return !!block_count; } diff --git a/tokenizer.cpp b/tokenizer.cpp index 5af71bf90..537f1332f 100644 --- a/tokenizer.cpp +++ b/tokenizer.cpp @@ -61,21 +61,21 @@ segments. */ static const wchar_t *tok_desc[] = { - N_(L"Tokenizer not yet initialized"), - N_( L"Tokenizer error" ), - N_( L"Invalid token" ), - N_( L"String" ), - N_( L"Pipe" ), - N_( L"End of command" ), - N_( L"Redirect output to file" ), - N_( L"Append output to file" ), - N_( L"Redirect input to file" ), - N_( L"Redirect to file descriptor" ), - N_( L"Redirect output to file if file does not exist" ), - N_( L"Run job in background" ), - N_( L"Comment" ) + N_(L"Tokenizer not yet initialized"), + N_( L"Tokenizer error" ), + N_( L"Invalid token" ), + N_( L"String" ), + N_( L"Pipe" ), + N_( L"End of command" ), + N_( L"Redirect output to file" ), + N_( L"Append output to file" ), + N_( L"Redirect input to file" ), + N_( L"Redirect to file descriptor" ), + N_( L"Redirect output to file if file does not exist" ), + N_( L"Run job in background" ), + N_( L"Comment" ) } - ; + ; /** Tests if the tokenizer buffer is large enough to hold contents of @@ -85,19 +85,19 @@ static const wchar_t *tok_desc[] = */ static int check_size( tokenizer *tok, size_t len ) { - if( tok->last_len <= len ) - { - wchar_t *tmp; - tok->last_len = len +1; - tmp = (wchar_t *)realloc( tok->last, sizeof(wchar_t)*tok->last_len ); - if( tmp == 0 ) - { - wperror( L"realloc" ); - return 0; - } - tok->last = tmp; - } - return 1; + if( tok->last_len <= len ) + { + wchar_t *tmp; + tok->last_len = len +1; + tmp = (wchar_t *)realloc( tok->last, sizeof(wchar_t)*tok->last_len ); + if( tmp == 0 ) + { + wperror( L"realloc" ); + return 0; + } + tok->last = tmp; + } + return 1; } /** @@ -105,21 +105,21 @@ static int check_size( tokenizer *tok, size_t len ) */ static void tok_call_error( tokenizer *tok, int error_type, const wchar_t *error_message ) { - tok->last_type = TOK_ERROR; - tok->error = error_type; - if( !check_size( tok, wcslen( error_message)+1 )) - { - if( tok->last != 0 ) - *tok->last=0; - return; - } + tok->last_type = TOK_ERROR; + tok->error = error_type; + if( !check_size( tok, wcslen( error_message)+1 )) + { + if( tok->last != 0 ) + *tok->last=0; + return; + } - wcscpy( tok->last, error_message ); + wcscpy( tok->last, error_message ); } int tok_get_error( tokenizer *tok ) { - return tok->error; + return tok->error; } @@ -130,58 +130,58 @@ void tok_init( tokenizer *tok, const wchar_t *b, int flags ) if (! (flags & TOK_SQUASH_ERRORS)) { ASSERT_IS_MAIN_THREAD(); } - - CHECK( tok, ); - memset( tok, 0, sizeof( tokenizer) ); + CHECK( tok, ); - CHECK( b, ); + memset( tok, 0, sizeof( tokenizer) ); + + CHECK( b, ); - tok->accept_unfinished = !! (flags & TOK_ACCEPT_UNFINISHED); - tok->show_comments = !! (flags & TOK_SHOW_COMMENTS); + tok->accept_unfinished = !! (flags & TOK_ACCEPT_UNFINISHED); + tok->show_comments = !! (flags & TOK_SHOW_COMMENTS); tok->squash_errors = !! (flags & TOK_SQUASH_ERRORS); - tok->has_next=true; + tok->has_next=true; - tok->has_next = (*b != L'\0'); - tok->orig_buff = tok->buff = b; + tok->has_next = (*b != L'\0'); + tok->orig_buff = tok->buff = b; tok->cached_lineno_offset = 0; tok->cached_lineno_count = 0; - tok_next( tok ); + tok_next( tok ); } void tok_destroy( tokenizer *tok ) { - CHECK( tok, ); - - free( tok->last ); + CHECK( tok, ); + + free( tok->last ); } int tok_last_type( tokenizer *tok ) { - CHECK( tok, TOK_ERROR ); - CHECK( tok->buff, TOK_ERROR ); - - return tok->last_type; + CHECK( tok, TOK_ERROR ); + CHECK( tok->buff, TOK_ERROR ); + + return tok->last_type; } wchar_t *tok_last( tokenizer *tok ) { - CHECK( tok, 0 ); - - return tok->last; + CHECK( tok, 0 ); + + return tok->last; } int tok_has_next( tokenizer *tok ) { - /* - Return 1 on broken tokenizer - */ - CHECK( tok, 1 ); - CHECK( tok->buff, 1 ); + /* + Return 1 on broken tokenizer + */ + CHECK( tok, 1 ); + CHECK( tok->buff, 1 ); -/* fwprintf( stderr, L"has_next is %ls \n", tok->has_next?L"true":L"false" );*/ - return tok->has_next; +/* fwprintf( stderr, L"has_next is %ls \n", tok->has_next?L"true":L"false" );*/ + return tok->has_next; } int tokenizer::line_number_of_character_at_offset(size_t offset) @@ -189,13 +189,13 @@ int tokenizer::line_number_of_character_at_offset(size_t offset) // we want to return (one plus) the number of newlines at offsets less than the given offset // cached_lineno_count is the number of newlines at indexes less than cached_lineno_offset const wchar_t *str = orig_buff; - if (! str) - return 0; - + if (! str) + return 0; + // easy hack to handle 0 if (offset == 0) return 1; - + size_t i; if (offset > cached_lineno_offset) { @@ -217,7 +217,7 @@ int tokenizer::line_number_of_character_at_offset(size_t offset) } cached_lineno_offset = offset; } - return cached_lineno_count + 1; + return cached_lineno_count + 1; } /** @@ -240,14 +240,14 @@ static bool is_string_char( wchar_t c, bool is_first ) case L'>': case L'&': return false; - + /* Conditional separator */ case L'^': return ! is_first; - + default: return true; - + } } @@ -259,7 +259,7 @@ static bool is_string_char( wchar_t c, bool is_first ) */ static int myal( wchar_t c ) { - return (c>=L'a' && c<=L'z') || (c>=L'A'&&c<=L'Z'); + return (c>=L'a' && c<=L'z') || (c>=L'A'&&c<=L'Z'); } /** @@ -267,199 +267,199 @@ static int myal( wchar_t c ) */ static void read_string( tokenizer *tok ) { - const wchar_t *start; - long len; - int mode=0; - int do_loop=1; - int paran_count=0; + const wchar_t *start; + long len; + int mode=0; + int do_loop=1; + int paran_count=0; - start = tok->buff; + start = tok->buff; bool is_first = true; - - while( 1 ) - { - if( !myal( *tok->buff ) ) - { -// debug(1, L"%lc", *tok->buff ); + while( 1 ) + { - if( *tok->buff == L'\\' ) - { - tok->buff++; - if( *tok->buff == L'\0' ) - { - if( (!tok->accept_unfinished) ) - { - TOK_CALL_ERROR( tok, TOK_UNTERMINATED_ESCAPE, QUOTE_ERROR ); - return; - } - else - { - do_loop = 0; - } - + if( !myal( *tok->buff ) ) + { +// debug(1, L"%lc", *tok->buff ); - } - else if( *tok->buff == L'\n' && mode == 0) - { - tok->buff--; - do_loop = 0; - break; - } - - tok->buff++; - continue; - } - - - /* - The modes are as follows: - - 0: regular text - 1: inside of subshell - 2: inside of array brackets - 3: inside of array brackets and subshell, like in '$foo[(ech' - */ - switch( mode ) - { - case 0: - { - switch( *tok->buff ) - { - case L'(': - { - paran_count=1; - mode = 1; - break; - } - - case L'[': - { - if( tok->buff != start ) - mode=2; - break; - } - - case L'\'': - case L'"': - { - - const wchar_t *end = quote_end( tok->buff ); - tok->last_quote = *tok->buff; - if( end ) - { - tok->buff=(wchar_t *)end; - } - else - { - tok->buff += wcslen( tok->buff ); - - if( (!tok->accept_unfinished) ) - { - TOK_CALL_ERROR( tok, TOK_UNTERMINATED_QUOTE, QUOTE_ERROR ); - return; - } - do_loop = 0; - - } - break; - } - - default: - { - if( !is_string_char(*(tok->buff), is_first) ) - { - do_loop=0; - } - } - } - break; - } - - case 3: - case 1: - switch( *tok->buff ) - { - case L'\'': - case L'\"': - { - const wchar_t *end = quote_end( tok->buff ); - if( end ) - { - tok->buff=(wchar_t *)end; - } - else - { - tok->buff += wcslen( tok->buff ); - if( (!tok->accept_unfinished) ) - { - TOK_CALL_ERROR( tok, TOK_UNTERMINATED_QUOTE, QUOTE_ERROR ); - return; - } - do_loop = 0; - } - - break; - } - - case L'(': - paran_count++; - break; - case L')': - paran_count--; - if( paran_count == 0 ) - { - mode--; - } - break; - case L'\0': - do_loop = 0; - break; - } - break; - case 2: - switch( *tok->buff ) - { - case L'(': - paran_count=1; - mode = 3; - break; - - case L']': - mode=0; - break; - - case L'\0': - do_loop = 0; - break; - } - break; - } - } + if( *tok->buff == L'\\' ) + { + tok->buff++; + if( *tok->buff == L'\0' ) + { + if( (!tok->accept_unfinished) ) + { + TOK_CALL_ERROR( tok, TOK_UNTERMINATED_ESCAPE, QUOTE_ERROR ); + return; + } + else + { + do_loop = 0; + } - if( !do_loop ) - break; + } + else if( *tok->buff == L'\n' && mode == 0) + { + tok->buff--; + do_loop = 0; + break; + } - tok->buff++; + tok->buff++; + continue; + } + + + /* + The modes are as follows: + + 0: regular text + 1: inside of subshell + 2: inside of array brackets + 3: inside of array brackets and subshell, like in '$foo[(ech' + */ + switch( mode ) + { + case 0: + { + switch( *tok->buff ) + { + case L'(': + { + paran_count=1; + mode = 1; + break; + } + + case L'[': + { + if( tok->buff != start ) + mode=2; + break; + } + + case L'\'': + case L'"': + { + + const wchar_t *end = quote_end( tok->buff ); + tok->last_quote = *tok->buff; + if( end ) + { + tok->buff=(wchar_t *)end; + } + else + { + tok->buff += wcslen( tok->buff ); + + if( (!tok->accept_unfinished) ) + { + TOK_CALL_ERROR( tok, TOK_UNTERMINATED_QUOTE, QUOTE_ERROR ); + return; + } + do_loop = 0; + + } + break; + } + + default: + { + if( !is_string_char(*(tok->buff), is_first) ) + { + do_loop=0; + } + } + } + break; + } + + case 3: + case 1: + switch( *tok->buff ) + { + case L'\'': + case L'\"': + { + const wchar_t *end = quote_end( tok->buff ); + if( end ) + { + tok->buff=(wchar_t *)end; + } + else + { + tok->buff += wcslen( tok->buff ); + if( (!tok->accept_unfinished) ) + { + TOK_CALL_ERROR( tok, TOK_UNTERMINATED_QUOTE, QUOTE_ERROR ); + return; + } + do_loop = 0; + } + + break; + } + + case L'(': + paran_count++; + break; + case L')': + paran_count--; + if( paran_count == 0 ) + { + mode--; + } + break; + case L'\0': + do_loop = 0; + break; + } + break; + case 2: + switch( *tok->buff ) + { + case L'(': + paran_count=1; + mode = 3; + break; + + case L']': + mode=0; + break; + + case L'\0': + do_loop = 0; + break; + } + break; + } + } + + + if( !do_loop ) + break; + + tok->buff++; is_first = false; - } + } - if( (!tok->accept_unfinished) && (mode!=0) ) - { - TOK_CALL_ERROR( tok, TOK_UNTERMINATED_SUBSHELL, PARAN_ERROR ); - return; - } + if( (!tok->accept_unfinished) && (mode!=0) ) + { + TOK_CALL_ERROR( tok, TOK_UNTERMINATED_SUBSHELL, PARAN_ERROR ); + return; + } - len = tok->buff - start; + len = tok->buff - start; - if( !check_size( tok, len )) - return; + if( !check_size( tok, len )) + return; - memcpy( tok->last, start, sizeof(wchar_t)*len ); - tok->last[len] = L'\0'; - tok->last_type = TOK_STRING; + memcpy( tok->last, start, sizeof(wchar_t)*len ); + tok->last[len] = L'\0'; + tok->last_type = TOK_STRING; } /** @@ -467,19 +467,19 @@ static void read_string( tokenizer *tok ) */ static void read_comment( tokenizer *tok ) { - const wchar_t *start; + const wchar_t *start; - start = tok->buff; - while( *(tok->buff)!= L'\n' && *(tok->buff)!= L'\0' ) - tok->buff++; + start = tok->buff; + while( *(tok->buff)!= L'\n' && *(tok->buff)!= L'\0' ) + tok->buff++; - size_t len = tok->buff - start; - if( !check_size( tok, len )) - return; + size_t len = tok->buff - start; + if( !check_size( tok, len )) + return; - memcpy( tok->last, start, sizeof(wchar_t)*len ); - tok->last[len] = L'\0'; - tok->last_type = TOK_COMMENT; + memcpy( tok->last, start, sizeof(wchar_t)*len ); + tok->last[len] = L'\0'; + tok->last_type = TOK_COMMENT; } /** @@ -487,74 +487,74 @@ static void read_comment( tokenizer *tok ) */ static void read_redirect( tokenizer *tok, int fd ) { - int mode = -1; + int mode = -1; - if( (*tok->buff == L'>') || - (*tok->buff == L'^') ) - { - tok->buff++; - if( *tok->buff == *(tok->buff-1) ) - { - tok->buff++; - mode = 1; - } - else - { - mode = 0; - } + if( (*tok->buff == L'>') || + (*tok->buff == L'^') ) + { + tok->buff++; + if( *tok->buff == *(tok->buff-1) ) + { + tok->buff++; + mode = 1; + } + else + { + mode = 0; + } - if( *tok->buff == L'|' ) - { - if( fd == 0 ) - { - TOK_CALL_ERROR( tok, TOK_OTHER, PIPE_ERROR ); - return; - } - check_size( tok, FD_STR_MAX_LEN ); - tok->buff++; - swprintf( tok->last, FD_STR_MAX_LEN, L"%d", fd ); - tok->last_type = TOK_PIPE; - return; - } - } - else if( *tok->buff == L'<' ) - { - tok->buff++; - mode = 2; - } - else - { - TOK_CALL_ERROR( tok, TOK_OTHER, REDIRECT_ERROR); - } + if( *tok->buff == L'|' ) + { + if( fd == 0 ) + { + TOK_CALL_ERROR( tok, TOK_OTHER, PIPE_ERROR ); + return; + } + check_size( tok, FD_STR_MAX_LEN ); + tok->buff++; + swprintf( tok->last, FD_STR_MAX_LEN, L"%d", fd ); + tok->last_type = TOK_PIPE; + return; + } + } + else if( *tok->buff == L'<' ) + { + tok->buff++; + mode = 2; + } + else + { + TOK_CALL_ERROR( tok, TOK_OTHER, REDIRECT_ERROR); + } - if( !check_size( tok, 2 )) - { - return; - } + if( !check_size( tok, 2 )) + { + return; + } - swprintf( tok->last, tok->last_len, L"%d", fd ); + swprintf( tok->last, tok->last_len, L"%d", fd ); - if( *tok->buff == L'&' ) - { - tok->buff++; - tok->last_type = TOK_REDIRECT_FD; - } - else if( *tok->buff == L'?' ) - { - tok->buff++; - tok->last_type = TOK_REDIRECT_NOCLOB; - } - else - { - tok->last_type = TOK_REDIRECT_OUT + mode; - } + if( *tok->buff == L'&' ) + { + tok->buff++; + tok->last_type = TOK_REDIRECT_FD; + } + else if( *tok->buff == L'?' ) + { + tok->buff++; + tok->last_type = TOK_REDIRECT_NOCLOB; + } + else + { + tok->last_type = TOK_REDIRECT_OUT + mode; + } } wchar_t tok_last_quote( tokenizer *tok ) { - CHECK( tok, 0 ); - - return tok->last_quote; + CHECK( tok, 0 ); + + return tok->last_quote; } /** @@ -563,189 +563,189 @@ wchar_t tok_last_quote( tokenizer *tok ) */ static int my_iswspace( wchar_t c ) { - if( c == L'\n' ) - return 0; - else - return iswspace( c ); + if( c == L'\n' ) + return 0; + else + return iswspace( c ); } const wchar_t *tok_get_desc( int type ) { - if( type < 0 || (size_t)type >= sizeof( tok_desc ) ) - { - return _(L"Invalid token type"); - } - return _(tok_desc[type]); + if( type < 0 || (size_t)type >= sizeof( tok_desc ) ) + { + return _(L"Invalid token type"); + } + return _(tok_desc[type]); } void tok_next( tokenizer *tok ) { - CHECK( tok, ); - CHECK( tok->buff, ); - - if( tok_last_type( tok ) == TOK_ERROR ) - { - tok->has_next=false; - return; - } + CHECK( tok, ); + CHECK( tok->buff, ); - if( !tok->has_next ) - { -/* wprintf( L"EOL\n" );*/ - tok->last_type = TOK_END; - return; - } + if( tok_last_type( tok ) == TOK_ERROR ) + { + tok->has_next=false; + return; + } - while( 1 ) - { - if( my_iswspace(*(tok->buff) ) ) - { - tok->buff++; - } - else - { - if(( *(tok->buff) == L'\\') &&( *(tok->buff+1) == L'\n') ) - { - tok->last_pos = tok->buff - tok->orig_buff; - tok->buff+=2; - tok->last_type = TOK_END; - return; - } - break; - } - } - + if( !tok->has_next ) + { +/* wprintf( L"EOL\n" );*/ + tok->last_type = TOK_END; + return; + } - if( *tok->buff == L'#') - { - if( tok->show_comments ) - { - tok->last_pos = tok->buff - tok->orig_buff; - read_comment( tok ); - return; - } - else - { - while( *(tok->buff)!= L'\n' && *(tok->buff)!= L'\0' ) - tok->buff++; - } + while( 1 ) + { + if( my_iswspace(*(tok->buff) ) ) + { + tok->buff++; + } + else + { + if(( *(tok->buff) == L'\\') &&( *(tok->buff+1) == L'\n') ) + { + tok->last_pos = tok->buff - tok->orig_buff; + tok->buff+=2; + tok->last_type = TOK_END; + return; + } + break; + } + } - while( my_iswspace(*(tok->buff) ) ) - tok->buff++; - } - tok->last_pos = tok->buff - tok->orig_buff; + if( *tok->buff == L'#') + { + if( tok->show_comments ) + { + tok->last_pos = tok->buff - tok->orig_buff; + read_comment( tok ); + return; + } + else + { + while( *(tok->buff)!= L'\n' && *(tok->buff)!= L'\0' ) + tok->buff++; + } - switch( *tok->buff ) - { + while( my_iswspace(*(tok->buff) ) ) + tok->buff++; + } - case L'\0': - tok->last_type = TOK_END; - /*fwprintf( stderr, L"End of string\n" );*/ - tok->has_next = false; - break; - case 13: - case L'\n': - case L';': - tok->last_type = TOK_END; - tok->buff++; - break; - case L'&': - tok->last_type = TOK_BACKGROUND; - tok->buff++; - break; + tok->last_pos = tok->buff - tok->orig_buff; - case L'|': - check_size( tok, 2 ); + switch( *tok->buff ) + { - tok->last[0]=L'1'; - tok->last[1]=L'\0'; - tok->last_type = TOK_PIPE; - tok->buff++; - break; + case L'\0': + tok->last_type = TOK_END; + /*fwprintf( stderr, L"End of string\n" );*/ + tok->has_next = false; + break; + case 13: + case L'\n': + case L';': + tok->last_type = TOK_END; + tok->buff++; + break; + case L'&': + tok->last_type = TOK_BACKGROUND; + tok->buff++; + break; - case L'>': - read_redirect( tok, 1 ); - return; - case L'<': - read_redirect( tok, 0 ); - return; - case L'^': - read_redirect( tok, 2 ); - return; + case L'|': + check_size( tok, 2 ); - default: - { - if( iswdigit( *tok->buff ) ) - { - const wchar_t *orig = tok->buff; - int fd = 0; - while( iswdigit( *tok->buff ) ) - fd = (fd*10) + (*(tok->buff++) - L'0'); + tok->last[0]=L'1'; + tok->last[1]=L'\0'; + tok->last_type = TOK_PIPE; + tok->buff++; + break; - switch( *(tok->buff)) - { - case L'^': - case L'>': - case L'<': - read_redirect( tok, fd ); - return; - } - tok->buff = orig; - } - read_string( tok ); - } + case L'>': + read_redirect( tok, 1 ); + return; + case L'<': + read_redirect( tok, 0 ); + return; + case L'^': + read_redirect( tok, 2 ); + return; - } + default: + { + if( iswdigit( *tok->buff ) ) + { + const wchar_t *orig = tok->buff; + int fd = 0; + while( iswdigit( *tok->buff ) ) + fd = (fd*10) + (*(tok->buff++) - L'0'); + + switch( *(tok->buff)) + { + case L'^': + case L'>': + case L'<': + read_redirect( tok, fd ); + return; + } + tok->buff = orig; + } + read_string( tok ); + } + + } } const wchar_t *tok_string( tokenizer *tok ) { - return tok?tok->orig_buff:0; + return tok?tok->orig_buff:0; } wchar_t *tok_first( const wchar_t *str ) { - tokenizer t; - wchar_t *res=0; + tokenizer t; + wchar_t *res=0; - CHECK( str, 0 ); - - tok_init( &t, str, TOK_SQUASH_ERRORS); + CHECK( str, 0 ); - switch( tok_last_type( &t ) ) - { - case TOK_STRING: -// fwprintf( stderr, L"Got token %ls\n", tok_last( &t )); - res = wcsdup(tok_last( &t )); - break; - default: - break; - } + tok_init( &t, str, TOK_SQUASH_ERRORS); - tok_destroy( &t ); - return res; + switch( tok_last_type( &t ) ) + { + case TOK_STRING: +// fwprintf( stderr, L"Got token %ls\n", tok_last( &t )); + res = wcsdup(tok_last( &t )); + break; + default: + break; + } + + tok_destroy( &t ); + return res; } int tok_get_pos( tokenizer *tok ) { - CHECK( tok, 0 ); - - return (int)tok->last_pos; + CHECK( tok, 0 ); + + return (int)tok->last_pos; } void tok_set_pos( tokenizer *tok, int pos ) { - CHECK( tok, ); - - tok->buff = tok->orig_buff + pos; - tok->has_next = true; - tok_next( tok ); + CHECK( tok, ); + + tok->buff = tok->orig_buff + pos; + tok->has_next = true; + tok_next( tok ); } @@ -756,38 +756,38 @@ void tok_set_pos( tokenizer *tok, int pos ) */ int main( int argc, char **argv ) { - tokenizer tok; - int i; - for ( i=1; i 0 ? 2 : -2; + errno = 0; + al = wcstol( a, &aend, 10 ); + bl = wcstol( b, &bend, 10 ); - secondary_diff = (aend-a) - (bend-b); + if( errno ) + { + /* + Huuuuuuuuge numbers - fall back to regular string comparison + */ + return wcscmp( a, b ); + } - a=aend-1; - b=bend-1; - } - else - { - int diff = towlower(*a) - towlower(*b); - if( diff != 0 ) - return (diff>0)?2:-2; + diff = al - bl; + if( diff ) + return diff > 0 ? 2 : -2; - secondary_diff = *a-*b; - } + secondary_diff = (aend-a) - (bend-b); - int res = wcsfilecmp( a+1, b+1 ); + a=aend-1; + b=bend-1; + } + else + { + int diff = towlower(*a) - towlower(*b); + if( diff != 0 ) + return (diff>0)?2:-2; - if( abs(res) < 2 ) - { - /* - No primary difference in rest of string. - Use secondary difference on this element if found. - */ - if( secondary_diff ) - { - return secondary_diff > 0 ? 1 :-1; - } - } - - return res; + secondary_diff = *a-*b; + } + + int res = wcsfilecmp( a+1, b+1 ); + + if( abs(res) < 2 ) + { + /* + No primary difference in rest of string. + Use secondary difference on this element if found. + */ + if( secondary_diff ) + { + return secondary_diff > 0 ? 1 :-1; + } + } + + return res; } long long get_time() { - struct timeval time_struct; - gettimeofday( &time_struct, 0 ); - return 1000000ll*time_struct.tv_sec+time_struct.tv_usec; + struct timeval time_struct; + gettimeofday( &time_struct, 0 ); + return 1000000ll*time_struct.tv_sec+time_struct.tv_usec; } diff --git a/util.h b/util.h index b3893cb12..c16c5912b 100644 --- a/util.h +++ b/util.h @@ -1,11 +1,11 @@ /** \file util.h - Generic utilities library. + Generic utilities library. - All containers in this library except strinb_buffer_t are written - so that they don't allocate any memory until the first element is - inserted into them. That way it is known to be very cheap to - initialize various containers at startup, supporting the fish - notion of doing as much lazy initalization as possible. + All containers in this library except strinb_buffer_t are written + so that they don't allocate any memory until the first element is + inserted into them. That way it is known to be very cheap to + initialize various containers at startup, supporting the fish + notion of doing as much lazy initalization as possible. */ #ifndef FISH_UTIL_H @@ -20,9 +20,9 @@ */ typedef struct buffer { - char *buff; /**the fish homepage. Extract - the sourcode, copy wgetopt.c and wgetopt.h into your program - directory, include wgetopt.h in your program, and use all the - regular getopt functions, prefixing every function, global - variable and structure with a 'w', and use only wide character - strings. There are no other functional changes in this version of - getopt besides using wide character strings. + If you want to use this version of getopt in your program, + download the fish sourcecode, available at the fish homepage. Extract + the sourcode, copy wgetopt.c and wgetopt.h into your program + directory, include wgetopt.h in your program, and use all the + regular getopt functions, prefixing every function, global + variable and structure with a 'w', and use only wide character + strings. There are no other functional changes in this version of + getopt besides using wide character strings. - For examples of how to use wgetopt, see the fish builtin - functions, many of which are defined in builtin.c. + For examples of how to use wgetopt, see the fish builtin + functions, many of which are defined in builtin.c. */ @@ -70,11 +70,11 @@ /* This needs to come after some library #include to get __GNU_LIBRARY__ defined. */ -#ifdef __GNU_LIBRARY__ +#ifdef __GNU_LIBRARY__ /* Don't include stdlib.h for non-GNU C libraries because some of them contain conflicting prototypes for getopt. */ #include -#endif /* GNU C library. */ +#endif /* GNU C library. */ /* This version of `getopt' appears to the caller like standard Unix `getopt' but it behaves differently for the user, since it allows the user @@ -170,7 +170,7 @@ of the value of `ordering'. In the case of RETURN_IN_ORDER, only static enum { - REQUIRE_ORDER, PERMUTE, RETURN_IN_ORDER + REQUIRE_ORDER, PERMUTE, RETURN_IN_ORDER } ordering; /* Value of POSIXLY_CORRECT environment variable. */ @@ -193,13 +193,13 @@ static char *posixly_correct; #define _(wstr) wstr #endif -#ifdef __GNU_LIBRARY__ +#ifdef __GNU_LIBRARY__ /* We want to avoid inclusion of string.h with non-GNU libraries because there are many ways it can cause trouble. On some systems, it contains special magic macros that don't work in GCC. */ #include -#define my_index wcschr +#define my_index wcschr #else /* Avoid depending on library functions or files @@ -210,13 +210,13 @@ char *getenv (); static wchar_t * my_index (const wchar_t *str, int chr) { - while (*str) + while (*str) { - if (*str == chr) - return (wchar_t *) str; - str++; + if (*str == chr) + return (wchar_t *) str; + str++; } - return 0; + return 0; } /* If using GCC, we can safely declare strlen this way. @@ -254,56 +254,56 @@ static int last_nonopt; static void exchange (wchar_t **argv) { - int bottom = first_nonopt; - int middle = last_nonopt; - int top = woptind; - wchar_t *tem; - - /* Exchange the shorter segment with the far end of the longer segment. - That puts the shorter segment into the right place. - It leaves the longer segment in the right place overall, - but it consists of two parts that need to be swapped next. */ + int bottom = first_nonopt; + int middle = last_nonopt; + int top = woptind; + wchar_t *tem; - while (top > middle && middle > bottom) + /* Exchange the shorter segment with the far end of the longer segment. + That puts the shorter segment into the right place. + It leaves the longer segment in the right place overall, + but it consists of two parts that need to be swapped next. */ + + while (top > middle && middle > bottom) { - if (top - middle > middle - bottom) - { - /* Bottom segment is the short one. */ - int len = middle - bottom; - register int i; + if (top - middle > middle - bottom) + { + /* Bottom segment is the short one. */ + int len = middle - bottom; + register int i; - /* Swap it with the top part of the top segment. */ - for (i = 0; i < len; i++) - { - tem = argv[bottom + i]; - argv[bottom + i] = argv[top - (middle - bottom) + i]; - argv[top - (middle - bottom) + i] = tem; - } - /* Exclude the moved bottom segment from further swapping. */ - top -= len; - } - else - { - /* Top segment is the short one. */ - int len = top - middle; - register int i; + /* Swap it with the top part of the top segment. */ + for (i = 0; i < len; i++) + { + tem = argv[bottom + i]; + argv[bottom + i] = argv[top - (middle - bottom) + i]; + argv[top - (middle - bottom) + i] = tem; + } + /* Exclude the moved bottom segment from further swapping. */ + top -= len; + } + else + { + /* Top segment is the short one. */ + int len = top - middle; + register int i; - /* Swap it with the bottom part of the bottom segment. */ - for (i = 0; i < len; i++) - { - tem = argv[bottom + i]; - argv[bottom + i] = argv[middle + i]; - argv[middle + i] = tem; - } - /* Exclude the moved top segment from further swapping. */ - bottom += len; - } + /* Swap it with the bottom part of the bottom segment. */ + for (i = 0; i < len; i++) + { + tem = argv[bottom + i]; + argv[bottom + i] = argv[middle + i]; + argv[middle + i] = tem; + } + /* Exclude the moved top segment from further swapping. */ + bottom += len; + } } - /* Update records for the slots the non-options now occupy. */ + /* Update records for the slots the non-options now occupy. */ - first_nonopt += (woptind - last_nonopt); - last_nonopt = woptind; + first_nonopt += (woptind - last_nonopt); + last_nonopt = woptind; } /* Initialize the internal data when the first call is made. */ @@ -311,34 +311,34 @@ exchange (wchar_t **argv) static const wchar_t * _wgetopt_initialize (const wchar_t *optstring) { - /* Start processing options with ARGV-element 1 (since ARGV-element 0 - is the program name); the sequence of previously skipped - non-option ARGV-elements is empty. */ + /* Start processing options with ARGV-element 1 (since ARGV-element 0 + is the program name); the sequence of previously skipped + non-option ARGV-elements is empty. */ - first_nonopt = last_nonopt = woptind = 1; + first_nonopt = last_nonopt = woptind = 1; - nextchar = NULL; + nextchar = NULL; - posixly_correct = getenv ("POSIXLY_CORRECT"); + posixly_correct = getenv ("POSIXLY_CORRECT"); - /* Determine how to handle the ordering of options and nonoptions. */ + /* Determine how to handle the ordering of options and nonoptions. */ - if (optstring[0] == '-') + if (optstring[0] == '-') { - ordering = RETURN_IN_ORDER; - ++optstring; + ordering = RETURN_IN_ORDER; + ++optstring; } - else if (optstring[0] == '+') + else if (optstring[0] == '+') { - ordering = REQUIRE_ORDER; - ++optstring; + ordering = REQUIRE_ORDER; + ++optstring; } - else if (posixly_correct != NULL) - ordering = REQUIRE_ORDER; - else - ordering = PERMUTE; + else if (posixly_correct != NULL) + ordering = REQUIRE_ORDER; + else + ordering = PERMUTE; - return optstring; + return optstring; } /* Scan elements of ARGV (whose length is ARGC) for option characters @@ -400,312 +400,312 @@ _wgetopt_initialize (const wchar_t *optstring) int _wgetopt_internal (int argc, wchar_t *const *argv, const wchar_t *optstring, const struct woption *longopts, int *longind, int long_only) { - woptarg = NULL; + woptarg = NULL; - if (woptind == 0) - optstring = _wgetopt_initialize (optstring); + if (woptind == 0) + optstring = _wgetopt_initialize (optstring); - if (nextchar == NULL || *nextchar == '\0') + if (nextchar == NULL || *nextchar == '\0') { - /* Advance to the next ARGV-element. */ + /* Advance to the next ARGV-element. */ - if (ordering == PERMUTE) - { - /* If we have just processed some options following some non-options, - exchange them so that the options come first. */ + if (ordering == PERMUTE) + { + /* If we have just processed some options following some non-options, + exchange them so that the options come first. */ - if (first_nonopt != last_nonopt && last_nonopt != woptind) - exchange ((wchar_t **) argv); - else if (last_nonopt != woptind) - first_nonopt = woptind; + if (first_nonopt != last_nonopt && last_nonopt != woptind) + exchange ((wchar_t **) argv); + else if (last_nonopt != woptind) + first_nonopt = woptind; - /* Skip any additional non-options - and extend the range of non-options previously skipped. */ + /* Skip any additional non-options + and extend the range of non-options previously skipped. */ - while (woptind < argc - && (argv[woptind][0] != '-' || argv[woptind][1] == '\0')) - woptind++; - last_nonopt = woptind; - } - - /* The special ARGV-element `--' means premature end of options. - Skip it like a null option, - then exchange with previous non-options as if it were an option, - then skip everything else like a non-option. */ - - if (woptind != argc && !wcscmp (argv[woptind], L"--")) - { - woptind++; - - if (first_nonopt != last_nonopt && last_nonopt != woptind) - exchange ((wchar_t **) argv); - else if (first_nonopt == last_nonopt) - first_nonopt = woptind; - last_nonopt = argc; - - woptind = argc; - } - - /* If we have done all the ARGV-elements, stop the scan - and back over any non-options that we skipped and permuted. */ - - if (woptind == argc) - { - /* Set the next-arg-index to point at the non-options - that we previously skipped, so the caller will digest them. */ - if (first_nonopt != last_nonopt) - woptind = first_nonopt; - return EOF; - } - - /* If we have come to a non-option and did not permute it, - either stop the scan or describe it to the caller and pass it by. */ - - if ((argv[woptind][0] != '-' || argv[woptind][1] == '\0')) - { - if (ordering == REQUIRE_ORDER) - return EOF; - woptarg = argv[woptind++]; - return 1; - } - - /* We have found another option-ARGV-element. - Skip the initial punctuation. */ - - nextchar = (argv[woptind] + 1 - + (longopts != NULL && argv[woptind][1] == '-')); + while (woptind < argc + && (argv[woptind][0] != '-' || argv[woptind][1] == '\0')) + woptind++; + last_nonopt = woptind; } - /* Decode the current option-ARGV-element. */ + /* The special ARGV-element `--' means premature end of options. + Skip it like a null option, + then exchange with previous non-options as if it were an option, + then skip everything else like a non-option. */ - /* Check whether the ARGV-element is a long option. - - If long_only and the ARGV-element has the form "-f", where f is - a valid short option, don't consider it an abbreviated form of - a long option that starts with f. Otherwise there would be no - way to give the -f short option. - - On the other hand, if there's a long option "fubar" and - the ARGV-element is "-fu", do consider that an abbreviation of - the long option, just like "--fu", and not "-f" with arg "u". - - This distinction seems to be the most useful approach. */ - - if (longopts != NULL - && (argv[woptind][1] == '-' - || (long_only && (argv[woptind][2] || !my_index (optstring, argv[woptind][1]))))) + if (woptind != argc && !wcscmp (argv[woptind], L"--")) { - wchar_t *nameend; - const struct woption *p; - const struct woption *pfound = NULL; - int exact = 0; - int ambig = 0; - int indfound = 0; /* set to zero by Anton */ - int option_index; + woptind++; - for (nameend = nextchar; *nameend && *nameend != '='; nameend++) - /* Do nothing. */ ; + if (first_nonopt != last_nonopt && last_nonopt != woptind) + exchange ((wchar_t **) argv); + else if (first_nonopt == last_nonopt) + first_nonopt = woptind; + last_nonopt = argc; - /* Test all long options for either exact match - or abbreviated matches. */ - for (p = longopts, option_index = 0; p->name; p++, option_index++) - if (!wcsncmp(p->name, nextchar, nameend - nextchar)) - { - if ((unsigned int)(nameend - nextchar) == (unsigned int)wcslen (p->name)) - { - /* Exact match found. */ - pfound = p; - indfound = option_index; - exact = 1; - break; - } - else if (pfound == NULL) - { - /* First nonexact match found. */ - pfound = p; - indfound = option_index; - } - else - /* Second or later nonexact match found. */ - ambig = 1; - } - - if (ambig && !exact) - { - if (wopterr) - fwprintf (stderr, _(L"%ls: Option '%ls' is ambiguous\n"), - argv[0], argv[woptind]); - nextchar += wcslen (nextchar); - woptind++; - return '?'; - } - - if (pfound != NULL) - { - option_index = indfound; - woptind++; - if (*nameend) - { - /* Don't test has_arg with >, because some C compilers don't - allow it to be used on enums. */ - if (pfound->has_arg) - woptarg = nameend + 1; - else - { - if (wopterr) - { - if (argv[woptind - 1][1] == '-') - /* --option */ - fwprintf (stderr, - _(L"%ls: Option '--%ls' doesn't allow an argument\n"), - argv[0], pfound->name); - else - /* +option or -option */ - fwprintf (stderr, - _(L"%ls: Option '%lc%ls' doesn't allow an argument\n"), - argv[0], argv[woptind - 1][0], pfound->name); - } - nextchar += wcslen (nextchar); - return '?'; - } - } - else if (pfound->has_arg == 1) - { - if (woptind < argc) - woptarg = argv[woptind++]; - else - { - if (wopterr) - fwprintf (stderr, _(L"%ls: Option '%ls' requires an argument\n"), - argv[0], argv[woptind - 1]); - nextchar += wcslen (nextchar); - return optstring[0] == ':' ? ':' : '?'; - } - } - nextchar += wcslen (nextchar); - if (longind != NULL) - *longind = option_index; - if (pfound->flag) - { - *(pfound->flag) = pfound->val; - return 0; - } - return pfound->val; - } - - /* Can't find it as a long option. If this is not getopt_long_only, - or the option starts with '--' or is not a valid short - option, then it's an error. - Otherwise interpret it as a short option. */ - if (!long_only || argv[woptind][1] == '-' - || my_index (optstring, *nextchar) == NULL) - { - if (wopterr) - { - if (argv[woptind][1] == '-') - /* --option */ - fwprintf (stderr, _(L"%ls: Unrecognized option '--%ls'\n"), - argv[0], nextchar); - else - /* +option or -option */ - fwprintf (stderr, _(L"%ls: Unrecognized option '%lc%ls'\n"), - argv[0], argv[woptind][0], nextchar); - } - nextchar = (wchar_t *) L""; - woptind++; - return '?'; - } + woptind = argc; } - /* Look at and handle the next short option-character. */ + /* If we have done all the ARGV-elements, stop the scan + and back over any non-options that we skipped and permuted. */ - { - wchar_t c = *nextchar++; - wchar_t *temp = const_cast(my_index (optstring, c)); + if (woptind == argc) + { + /* Set the next-arg-index to point at the non-options + that we previously skipped, so the caller will digest them. */ + if (first_nonopt != last_nonopt) + woptind = first_nonopt; + return EOF; + } - /* Increment `woptind' when we start to process its last character. */ - if (*nextchar == '\0') - ++woptind; + /* If we have come to a non-option and did not permute it, + either stop the scan or describe it to the caller and pass it by. */ - if (temp == NULL || c == ':') - { - if (wopterr) - { - if (posixly_correct) - /* 1003.2 specifies the format of this message. */ - fwprintf (stderr, _(L"%ls: Illegal option -- %lc\n"), argv[0], c); - else - fwprintf (stderr, _(L"%ls: Invalid option -- %lc\n"), argv[0], c); - } - woptopt = c; - return '?'; - } - if (temp[1] == ':') - { - if (temp[2] == ':') - { - /* This is an option that accepts an argument optionally. */ - if (*nextchar != '\0') - { - woptarg = nextchar; - woptind++; - } - else - woptarg = NULL; - nextchar = NULL; - } - else - { - /* This is an option that requires an argument. */ - if (*nextchar != '\0') - { - woptarg = nextchar; - /* If we end this ARGV-element by taking the rest as an arg, - we must advance to the next element now. */ - woptind++; - } - else if (woptind == argc) - { - if (wopterr) - { - /* 1003.2 specifies the format of this message. */ - fwprintf (stderr, _(L"%ls: Option requires an argument -- %lc\n"), - argv[0], c); - } - woptopt = c; - if (optstring[0] == ':') - c = ':'; - else - c = '?'; - } - else - /* We already incremented `woptind' once; - increment it again when taking next ARGV-elt as argument. */ - woptarg = argv[woptind++]; - nextchar = NULL; - } - } - return c; - } + if ((argv[woptind][0] != '-' || argv[woptind][1] == '\0')) + { + if (ordering == REQUIRE_ORDER) + return EOF; + woptarg = argv[woptind++]; + return 1; + } + + /* We have found another option-ARGV-element. + Skip the initial punctuation. */ + + nextchar = (argv[woptind] + 1 + + (longopts != NULL && argv[woptind][1] == '-')); + } + + /* Decode the current option-ARGV-element. */ + + /* Check whether the ARGV-element is a long option. + + If long_only and the ARGV-element has the form "-f", where f is + a valid short option, don't consider it an abbreviated form of + a long option that starts with f. Otherwise there would be no + way to give the -f short option. + + On the other hand, if there's a long option "fubar" and + the ARGV-element is "-fu", do consider that an abbreviation of + the long option, just like "--fu", and not "-f" with arg "u". + + This distinction seems to be the most useful approach. */ + + if (longopts != NULL + && (argv[woptind][1] == '-' + || (long_only && (argv[woptind][2] || !my_index (optstring, argv[woptind][1]))))) + { + wchar_t *nameend; + const struct woption *p; + const struct woption *pfound = NULL; + int exact = 0; + int ambig = 0; + int indfound = 0; /* set to zero by Anton */ + int option_index; + + for (nameend = nextchar; *nameend && *nameend != '='; nameend++) + /* Do nothing. */ ; + + /* Test all long options for either exact match + or abbreviated matches. */ + for (p = longopts, option_index = 0; p->name; p++, option_index++) + if (!wcsncmp(p->name, nextchar, nameend - nextchar)) + { + if ((unsigned int)(nameend - nextchar) == (unsigned int)wcslen (p->name)) + { + /* Exact match found. */ + pfound = p; + indfound = option_index; + exact = 1; + break; + } + else if (pfound == NULL) + { + /* First nonexact match found. */ + pfound = p; + indfound = option_index; + } + else + /* Second or later nonexact match found. */ + ambig = 1; + } + + if (ambig && !exact) + { + if (wopterr) + fwprintf (stderr, _(L"%ls: Option '%ls' is ambiguous\n"), + argv[0], argv[woptind]); + nextchar += wcslen (nextchar); + woptind++; + return '?'; + } + + if (pfound != NULL) + { + option_index = indfound; + woptind++; + if (*nameend) + { + /* Don't test has_arg with >, because some C compilers don't + allow it to be used on enums. */ + if (pfound->has_arg) + woptarg = nameend + 1; + else + { + if (wopterr) + { + if (argv[woptind - 1][1] == '-') + /* --option */ + fwprintf (stderr, + _(L"%ls: Option '--%ls' doesn't allow an argument\n"), + argv[0], pfound->name); + else + /* +option or -option */ + fwprintf (stderr, + _(L"%ls: Option '%lc%ls' doesn't allow an argument\n"), + argv[0], argv[woptind - 1][0], pfound->name); + } + nextchar += wcslen (nextchar); + return '?'; + } + } + else if (pfound->has_arg == 1) + { + if (woptind < argc) + woptarg = argv[woptind++]; + else + { + if (wopterr) + fwprintf (stderr, _(L"%ls: Option '%ls' requires an argument\n"), + argv[0], argv[woptind - 1]); + nextchar += wcslen (nextchar); + return optstring[0] == ':' ? ':' : '?'; + } + } + nextchar += wcslen (nextchar); + if (longind != NULL) + *longind = option_index; + if (pfound->flag) + { + *(pfound->flag) = pfound->val; + return 0; + } + return pfound->val; + } + + /* Can't find it as a long option. If this is not getopt_long_only, + or the option starts with '--' or is not a valid short + option, then it's an error. + Otherwise interpret it as a short option. */ + if (!long_only || argv[woptind][1] == '-' + || my_index (optstring, *nextchar) == NULL) + { + if (wopterr) + { + if (argv[woptind][1] == '-') + /* --option */ + fwprintf (stderr, _(L"%ls: Unrecognized option '--%ls'\n"), + argv[0], nextchar); + else + /* +option or -option */ + fwprintf (stderr, _(L"%ls: Unrecognized option '%lc%ls'\n"), + argv[0], argv[woptind][0], nextchar); + } + nextchar = (wchar_t *) L""; + woptind++; + return '?'; + } + } + + /* Look at and handle the next short option-character. */ + + { + wchar_t c = *nextchar++; + wchar_t *temp = const_cast(my_index (optstring, c)); + + /* Increment `woptind' when we start to process its last character. */ + if (*nextchar == '\0') + ++woptind; + + if (temp == NULL || c == ':') + { + if (wopterr) + { + if (posixly_correct) + /* 1003.2 specifies the format of this message. */ + fwprintf (stderr, _(L"%ls: Illegal option -- %lc\n"), argv[0], c); + else + fwprintf (stderr, _(L"%ls: Invalid option -- %lc\n"), argv[0], c); + } + woptopt = c; + return '?'; + } + if (temp[1] == ':') + { + if (temp[2] == ':') + { + /* This is an option that accepts an argument optionally. */ + if (*nextchar != '\0') + { + woptarg = nextchar; + woptind++; + } + else + woptarg = NULL; + nextchar = NULL; + } + else + { + /* This is an option that requires an argument. */ + if (*nextchar != '\0') + { + woptarg = nextchar; + /* If we end this ARGV-element by taking the rest as an arg, + we must advance to the next element now. */ + woptind++; + } + else if (woptind == argc) + { + if (wopterr) + { + /* 1003.2 specifies the format of this message. */ + fwprintf (stderr, _(L"%ls: Option requires an argument -- %lc\n"), + argv[0], c); + } + woptopt = c; + if (optstring[0] == ':') + c = ':'; + else + c = '?'; + } + else + /* We already incremented `woptind' once; + increment it again when taking next ARGV-elt as argument. */ + woptarg = argv[woptind++]; + nextchar = NULL; + } + } + return c; + } } int wgetopt (int argc, wchar_t *const *argv, const wchar_t *optstring) { - return _wgetopt_internal (argc, argv, optstring, - (const struct woption *) 0, - (int *) 0, - 0); + return _wgetopt_internal (argc, argv, optstring, + (const struct woption *) 0, + (int *) 0, + 0); } int wgetopt_long (int argc, wchar_t *const *argv, const wchar_t *options, const struct woption *long_options, int *opt_index) { - return _wgetopt_internal (argc, argv, options, long_options, opt_index, 0); + return _wgetopt_internal (argc, argv, options, long_options, opt_index, 0); } int wgetopt_long_only (int argc, wchar_t *const *argv, const wchar_t *options, const struct woption *long_options, int *opt_index) { - return _wgetopt_internal (argc, argv, options, long_options, opt_index, 1); + return _wgetopt_internal (argc, argv, options, long_options, opt_index, 1); } diff --git a/wgetopt.h b/wgetopt.h index b27f2de1f..a2e90bbe8 100644 --- a/wgetopt.h +++ b/wgetopt.h @@ -1,24 +1,24 @@ /** \file wgetopt.h - A version of the getopt library for use with wide character strings. + A version of the getopt library for use with wide character strings. - This is simply the gnu getopt library, but converted for use with - wchar_t instead of char. This is not usually useful since the argv - array is always defined to be of type char**, but in fish, all - internal commands use wide characters and hence this library is - useful. + This is simply the gnu getopt library, but converted for use with + wchar_t instead of char. This is not usually useful since the argv + array is always defined to be of type char**, but in fish, all + internal commands use wide characters and hence this library is + useful. - If you want to use this version of getopt in your program, - download the fish sourcecode, available at the fish homepage. Extract - the sourcode, copy wgetopt.c and wgetopt.h into your program - directory, include wgetopt.h in your program, and use all the - regular getopt functions, prefixing every function, global - variable and structure with a 'w', and use only wide character - strings. There are no other functional changes in this version of - getopt besides using wide character strings. + If you want to use this version of getopt in your program, + download the fish sourcecode, available at the fish homepage. Extract + the sourcode, copy wgetopt.c and wgetopt.h into your program + directory, include wgetopt.h in your program, and use all the + regular getopt functions, prefixing every function, global + variable and structure with a 'w', and use only wide character + strings. There are no other functional changes in this version of + getopt besides using wide character strings. - For examples of how to use wgetopt, see the fish builtin - functions, many of which are defined in builtin.c. + For examples of how to use wgetopt, see the fish builtin + functions, many of which are defined in builtin.c. */ @@ -49,7 +49,7 @@ Cambridge, MA 02139, USA. */ #include -#ifdef __cplusplus +#ifdef __cplusplus extern "C" { #endif @@ -90,9 +90,9 @@ extern int woptopt; zero. The field `has_arg' is: - no_argument (or 0) if the option does not take an argument, - required_argument (or 1) if the option requires an argument, - optional_argument (or 2) if the option takes an optional argument. + no_argument (or 0) if the option does not take an argument, + required_argument (or 1) if the option requires an argument, + optional_argument (or 2) if the option takes an optional argument. If the field `flag' is not NULL, it points to a variable that is set to the value given in the field `val' when the option is found, but @@ -107,32 +107,32 @@ extern int woptopt; struct woption { - /** - long name for switch - */ + /** + long name for switch + */ #if defined (__STDC__) && __STDC__ const wchar_t *name; #else wchar_t *name; #endif - /** - Must be one of no_argument, required_argument and - optional_argument. + /** + Must be one of no_argument, required_argument and + optional_argument. - has_arg can't be an enum because some compilers complain about - type mismatches in all the code that assumes it is an int. - */ + has_arg can't be an enum because some compilers complain about + type mismatches in all the code that assumes it is an int. + */ int has_arg; - /** - If non-null, the flag whose value should be set if this switch is encountered - */ + /** + If non-null, the flag whose value should be set if this switch is encountered + */ int *flag; - /** - If \c flag is non-null, this is the value that flag will be set - to. Otherwise, this is the return-value of the function call. - */ + /** + If \c flag is non-null, this is the value that flag will be set + to. Otherwise, this is the return-value of the function call. + */ int val; }; @@ -141,69 +141,69 @@ struct woption /** Specifies that a switch does not accept an argument */ -#define no_argument 0 +#define no_argument 0 /** Specifies that a switch requires an argument */ -#define required_argument 1 +#define required_argument 1 /** Specifies that a switch accepts an optional argument */ -#define optional_argument 2 +#define optional_argument 2 #if defined (__STDC__) && __STDC__ #ifdef __GNU_LIBRARY__ -/** - Get options from argument list. See the glibc manual for information on how to use this function. +/** + Get options from argument list. See the glibc manual for information on how to use this function. */ extern int wgetopt (int argc, wchar_t *const *argv, const wchar_t *shortopts); #else /* not __GNU_LIBRARY__ */ extern int wgetopt (); #endif /* __GNU_LIBRARY__ */ -/** - Get options from argument list. See the glibc manual for information on how to use this function. +/** + Get options from argument list. See the glibc manual for information on how to use this function. */ extern int wgetopt_long (int argc, wchar_t *const *argv, const wchar_t *shortopts, - const struct woption *longopts, int *longind); -/** - Get options from argument list. See the glibc manual for information on how to use this function. + const struct woption *longopts, int *longind); +/** + Get options from argument list. See the glibc manual for information on how to use this function. */ extern int wgetopt_long_only (int argc, wchar_t *const *argv, - const wchar_t *shortopts, - const struct woption *longopts, int *longind); + const wchar_t *shortopts, + const struct woption *longopts, int *longind); -/** - Internal only. Users should not call this directly. +/** + Internal only. Users should not call this directly. */ extern int _wgetopt_internal (int argc, wchar_t *const *argv, - const wchar_t *shortopts, - const struct woption *longopts, int *longind, - int long_only); + const wchar_t *shortopts, + const struct woption *longopts, int *longind, + int long_only); #else /* not __STDC__ */ -/** - Get options from argument list. See the glibc manual for information on how to use this function. +/** + Get options from argument list. See the glibc manual for information on how to use this function. */ extern int wgetopt (); -/** - Get options from argument list. See the glibc manual for information on how to use this function. +/** + Get options from argument list. See the glibc manual for information on how to use this function. */ extern int wgetopt_long (); -/** - Get options from argument list. See the glibc manual for information on how to use this function. +/** + Get options from argument list. See the glibc manual for information on how to use this function. */ extern int wgetopt_long_only (); -/** - Internal only. Users should not call this directly. +/** + Internal only. Users should not call this directly. */ extern int _wgetopt_internal (); #endif /* __STDC__ */ -#ifdef __cplusplus +#ifdef __cplusplus } #endif diff --git a/wildcard.cpp b/wildcard.cpp index d32c8e01b..07cfcb7cd 100644 --- a/wildcard.cpp +++ b/wildcard.cpp @@ -43,7 +43,7 @@ wildcards using **. /** The maximum length of a filename token. This is a fallback value, - an attempt to find the true value using patchconf is always made. + an attempt to find the true value using patchconf is always made. */ #define MAX_FILE_LENGTH 1024 @@ -108,95 +108,95 @@ static std::map suffix_map; int wildcard_has( const wchar_t *str, int internal ) { - if( !str ) - { - debug( 2, L"Got null string on line %d of file %s", __LINE__, __FILE__ ); - return 0; - } + if( !str ) + { + debug( 2, L"Got null string on line %d of file %s", __LINE__, __FILE__ ); + return 0; + } - if( internal ) - { - for( ; *str; str++ ) - { - if( ( *str == ANY_CHAR ) || (*str == ANY_STRING) || (*str == ANY_STRING_RECURSIVE) ) - return 1; - } - } - else - { + if( internal ) + { + for( ; *str; str++ ) + { + if( ( *str == ANY_CHAR ) || (*str == ANY_STRING) || (*str == ANY_STRING_RECURSIVE) ) + return 1; + } + } + else + { wchar_t prev=0; - for( ; *str; str++ ) - { - if( ( (*str == L'*' ) || (*str == L'?' ) ) && (prev != L'\\') ) - return 1; - prev = *str; - } - } - - return 0; + for( ; *str; str++ ) + { + if( ( (*str == L'*' ) || (*str == L'?' ) ) && (prev != L'\\') ) + return 1; + prev = *str; + } + } + + return 0; } /** Check whether the string str matches the wildcard string wc. - + \param str String to be matched. \param wc The wildcard. - \param is_first Whether files beginning with dots should not be matched against wildcards. + \param is_first Whether files beginning with dots should not be matched against wildcards. */ static bool wildcard_match2(const wchar_t *str, - const wchar_t *wc, - bool is_first ) + const wchar_t *wc, + bool is_first ) { - if( *str == 0 && *wc==0 ) - return true; - + if( *str == 0 && *wc==0 ) + return true; + /* Hackish fix for https://github.com/fish-shell/fish-shell/issues/270. Prevent wildcards from matching . or .., but we must still allow literal matches. */ if (is_first && contains(str, L".", L"..")) { /* The string is '.' or '..'. Return true if the wildcard exactly matches. */ return ! wcscmp(str, wc); } - - if( *wc == ANY_STRING || *wc == ANY_STRING_RECURSIVE) - { - /* Ignore hidden file */ - if( is_first && *str == L'.' ) - { - return false; - } - - /* Try all submatches */ - do - { - if( wildcard_match2( str, wc+1, false ) ) - return true; - } - while( *(str++) != 0 ); - return false; - } - else if( *str == 0 ) - { - /* - End of string, but not end of wildcard, and the next wildcard - element is not a '*', so this is not a match. - */ - return false; - } - if( *wc == ANY_CHAR ) - { - if( is_first && *str == L'.' ) - { - return false; - } - - return wildcard_match2( str+1, wc+1, false ); - } - - if( *wc == *str ) - return wildcard_match2( str+1, wc+1, false ); + if( *wc == ANY_STRING || *wc == ANY_STRING_RECURSIVE) + { + /* Ignore hidden file */ + if( is_first && *str == L'.' ) + { + return false; + } - return false; + /* Try all submatches */ + do + { + if( wildcard_match2( str, wc+1, false ) ) + return true; + } + while( *(str++) != 0 ); + return false; + } + else if( *str == 0 ) + { + /* + End of string, but not end of wildcard, and the next wildcard + element is not a '*', so this is not a match. + */ + return false; + } + + if( *wc == ANY_CHAR ) + { + if( is_first && *str == L'.' ) + { + return false; + } + + return wildcard_match2( str+1, wc+1, false ); + } + + if( *wc == *str ) + return wildcard_match2( str+1, wc+1, false ); + + return false; } /** @@ -204,119 +204,119 @@ static bool wildcard_match2(const wchar_t *str, possible completion of the string, the remainder of the string is inserted into the out vector. */ -static bool wildcard_complete_internal(const wcstring &orig, - const wchar_t *str, - const wchar_t *wc, - bool is_first, - const wchar_t *desc, - wcstring (*desc_func)(const wcstring &), - std::vector &out, - int flags ) +static bool wildcard_complete_internal(const wcstring &orig, + const wchar_t *str, + const wchar_t *wc, + bool is_first, + const wchar_t *desc, + wcstring (*desc_func)(const wcstring &), + std::vector &out, + int flags ) { - if( !wc || ! str || orig.empty()) - { - debug( 2, L"Got null string on line %d of file %s", __LINE__, __FILE__ ); - return 0; - } + if( !wc || ! str || orig.empty()) + { + debug( 2, L"Got null string on line %d of file %s", __LINE__, __FILE__ ); + return 0; + } - if( *wc == 0 && - ( (str[0] != L'.') || (!is_first)) ) - { - wcstring out_completion; + if( *wc == 0 && + ( (str[0] != L'.') || (!is_first)) ) + { + wcstring out_completion; wcstring out_desc = (desc ? desc : L""); - if( flags & COMPLETE_NO_CASE ) - { - out_completion = orig; - } - else - { - out_completion = str; - } + if( flags & COMPLETE_NO_CASE ) + { + out_completion = orig; + } + else + { + out_completion = str; + } size_t complete_sep_loc = out_completion.find(PROG_COMPLETE_SEP); if (complete_sep_loc != wcstring::npos) - { - /* This completion has an embedded description, do not use the generic description */ + { + /* This completion has an embedded description, do not use the generic description */ out_desc.assign(out_completion, complete_sep_loc + 1, out_completion.size() - complete_sep_loc - 1); - out_completion.resize(complete_sep_loc); - } - else - { - if( desc_func ) - { - /* - A description generating function is specified, call - it. If it returns something, use that as the - description. - */ - wcstring func_desc = desc_func( orig ); - if (! func_desc.empty()) - out_desc = func_desc; - } - - } - + out_completion.resize(complete_sep_loc); + } + else + { + if( desc_func ) + { + /* + A description generating function is specified, call + it. If it returns something, use that as the + description. + */ + wcstring func_desc = desc_func( orig ); + if (! func_desc.empty()) + out_desc = func_desc; + } + + } + /* Note: out_completion may be empty if the completion really is empty, e.g. tab-completing 'foo' when a file 'foo' exists. */ append_completion(out, out_completion, out_desc, flags); - return true; - } - - - if( *wc == ANY_STRING ) - { - bool res=false; - - /* Ignore hidden file */ - if( is_first && str[0] == L'.' ) - return false; - - /* Try all submatches */ - do - { - res = wildcard_complete_internal( orig, str, wc+1, 0, desc, desc_func, out, flags ); - if (res) - break; - } - while (*str++ != 0); - return res; - - } - else if( *wc == ANY_CHAR ) - { - return wildcard_complete_internal( orig, str+1, wc+1, 0, desc, desc_func, out, flags ); - } - else if( *wc == *str ) - { - return wildcard_complete_internal( orig, str+1, wc+1, 0, desc, desc_func, out, flags ); - } - else if( towlower(*wc) == towlower(*str) ) - { - return wildcard_complete_internal( orig, str+1, wc+1, 0, desc, desc_func, out, flags | COMPLETE_NO_CASE ); - } - return false; + return true; + } + + + if( *wc == ANY_STRING ) + { + bool res=false; + + /* Ignore hidden file */ + if( is_first && str[0] == L'.' ) + return false; + + /* Try all submatches */ + do + { + res = wildcard_complete_internal( orig, str, wc+1, 0, desc, desc_func, out, flags ); + if (res) + break; + } + while (*str++ != 0); + return res; + + } + else if( *wc == ANY_CHAR ) + { + return wildcard_complete_internal( orig, str+1, wc+1, 0, desc, desc_func, out, flags ); + } + else if( *wc == *str ) + { + return wildcard_complete_internal( orig, str+1, wc+1, 0, desc, desc_func, out, flags ); + } + else if( towlower(*wc) == towlower(*str) ) + { + return wildcard_complete_internal( orig, str+1, wc+1, 0, desc, desc_func, out, flags | COMPLETE_NO_CASE ); + } + return false; } bool wildcard_complete(const wcstring &str, - const wchar_t *wc, - const wchar_t *desc, - wcstring (*desc_func)(const wcstring &), - std::vector &out, - int flags ) + const wchar_t *wc, + const wchar_t *desc, + wcstring (*desc_func)(const wcstring &), + std::vector &out, + int flags ) { - bool res; - res = wildcard_complete_internal( str, str.c_str(), wc, true, desc, desc_func, out, flags ); - return res; + bool res; + res = wildcard_complete_internal( str, str.c_str(), wc, true, desc, desc_func, out, flags ); + return res; } bool wildcard_match( const wcstring &str, const wcstring &wc ) { - return wildcard_match2( str.c_str(), wc.c_str(), true ); + return wildcard_match2( str.c_str(), wc.c_str(), true ); } /** - Creates a path from the specified directory and filename. + Creates a path from the specified directory and filename. */ static wcstring make_path(const wcstring &base_dir, const wcstring &name) { return base_dir + name; @@ -331,35 +331,35 @@ static wcstring complete_get_desc_suffix_internal( const wcstring &suff ) { wcstring cmd = wcstring(SUFFIX_CMD_STR) + suff; - + wcstring_list_t lst; wcstring desc; - - if( exec_subshell( cmd, lst ) != -1 ) - { - if( lst.size()>0 ) - { + + if( exec_subshell( cmd, lst ) != -1 ) + { + if( lst.size()>0 ) + { const wcstring & ln = lst.at(0); - if( ln.size() > 0 && ln != L"unknown" ) - { - desc = ln; - /* - I have decided I prefer to have the description - begin in uppercase and the whole universe will just - have to accept it. Hah! - */ - desc[0]=towupper(desc[0]); - } - } - } - - if( desc.empty() ) - { - desc = COMPLETE_FILE_DESC; - } - + if( ln.size() > 0 && ln != L"unknown" ) + { + desc = ln; + /* + I have decided I prefer to have the description + begin in uppercase and the whole universe will just + have to accept it. Hah! + */ + desc[0]=towupper(desc[0]); + } + } + } + + if( desc.empty() ) + { + desc = COMPLETE_FILE_DESC; + } + suffix_map[suff] = desc.c_str(); - return desc; + return desc; } @@ -369,34 +369,34 @@ static wcstring complete_get_desc_suffix_internal( const wcstring &suff ) static wcstring complete_get_desc_suffix( const wchar_t *suff_orig ) { - size_t len; - wchar_t *suff; - wchar_t *pos; - wchar_t *tmp; + size_t len; + wchar_t *suff; + wchar_t *pos; + wchar_t *tmp; - len = wcslen(suff_orig ); - - if( len == 0 ) - return COMPLETE_FILE_DESC; + len = wcslen(suff_orig ); - suff = wcsdup(suff_orig); + if( len == 0 ) + return COMPLETE_FILE_DESC; + + suff = wcsdup(suff_orig); + + /* + Drop characters that are commonly used as backup suffixes from the suffix + */ + for( pos=suff; *pos; pos++ ) + { + if( wcschr( L"?;#~@&", *pos ) ) + { + *pos=0; + break; + } + } + + tmp = escape( suff, 1 ); + free(suff); + suff = tmp; - /* - Drop characters that are commonly used as backup suffixes from the suffix - */ - for( pos=suff; *pos; pos++ ) - { - if( wcschr( L"?;#~@&", *pos ) ) - { - *pos=0; - break; - } - } - - tmp = escape( suff, 1 ); - free(suff); - suff = tmp; - std::map::iterator iter = suffix_map.find(suff); wcstring desc; if (iter != suffix_map.end()) { @@ -405,9 +405,9 @@ static wcstring complete_get_desc_suffix( const wchar_t *suff_orig ) desc = complete_get_desc_suffix_internal( suff ); } - free( suff ); + free( suff ); - return desc; + return desc; } @@ -421,130 +421,130 @@ static wcstring complete_get_desc_suffix( const wchar_t *suff_orig ) \param lbuf The struct buf output of calling lstat on the file \param stat_res The result of calling stat on the file \param buf The struct buf output of calling stat on the file - \param err The errno value after a failed stat call on the file. + \param err The errno value after a failed stat call on the file. */ -static wcstring file_get_desc( const wcstring &filename, - int lstat_res, - struct stat lbuf, - int stat_res, - struct stat buf, - int err ) +static wcstring file_get_desc( const wcstring &filename, + int lstat_res, + struct stat lbuf, + int stat_res, + struct stat buf, + int err ) { - const wchar_t *suffix; + const wchar_t *suffix; - if( !lstat_res ) - { - if( S_ISLNK(lbuf.st_mode)) - { - if( !stat_res ) - { - if( S_ISDIR(buf.st_mode) ) - { - return COMPLETE_DIRECTORY_SYMLINK_DESC; - } - else - { + if( !lstat_res ) + { + if( S_ISLNK(lbuf.st_mode)) + { + if( !stat_res ) + { + if( S_ISDIR(buf.st_mode) ) + { + return COMPLETE_DIRECTORY_SYMLINK_DESC; + } + else + { - if( ( buf.st_mode & S_IXUSR ) || - ( buf.st_mode & S_IXGRP ) || - ( buf.st_mode & S_IXOTH ) ) - { - - if( waccess( filename, X_OK ) == 0 ) - { - /* - Weird group permissions and other such - issues make it non-trivial to find out - if we can actually execute a file using - the result from stat. It is much safer - to use the access function, since it - tells us exactly what we want to know. - */ - return COMPLETE_EXEC_LINK_DESC; - } - } - } - - return COMPLETE_SYMLINK_DESC; + if( ( buf.st_mode & S_IXUSR ) || + ( buf.st_mode & S_IXGRP ) || + ( buf.st_mode & S_IXOTH ) ) + { - } - else - { - switch( err ) - { - case ENOENT: - { - return COMPLETE_ROTTEN_SYMLINK_DESC; - } - - case ELOOP: - { - return COMPLETE_LOOP_SYMLINK_DESC; - } - } - /* - On unknown errors we do nothing. The file will be - given the default 'File' description or one based on the suffix. - */ - } + if( waccess( filename, X_OK ) == 0 ) + { + /* + Weird group permissions and other such + issues make it non-trivial to find out + if we can actually execute a file using + the result from stat. It is much safer + to use the access function, since it + tells us exactly what we want to know. + */ + return COMPLETE_EXEC_LINK_DESC; + } + } + } - } - else if( S_ISCHR(buf.st_mode) ) - { - return COMPLETE_CHAR_DESC; - } - else if( S_ISBLK(buf.st_mode) ) - { - return COMPLETE_BLOCK_DESC; - } - else if( S_ISFIFO(buf.st_mode) ) - { - return COMPLETE_FIFO_DESC; - } - else if( S_ISSOCK(buf.st_mode)) - { - return COMPLETE_SOCKET_DESC; - } - else if( S_ISDIR(buf.st_mode) ) - { - return COMPLETE_DIRECTORY_DESC; - } - else - { - if( ( buf.st_mode & S_IXUSR ) || - ( buf.st_mode & S_IXGRP ) || - ( buf.st_mode & S_IXOTH ) ) - { - - if( waccess( filename, X_OK ) == 0 ) - { - /* - Weird group permissions and other such issues - make it non-trivial to find out if we can - actually execute a file using the result from - stat. It is much safer to use the access - function, since it tells us exactly what we want - to know. - */ - return COMPLETE_EXEC_DESC; - } - } - } - } - - suffix = wcsrchr( filename.c_str(), L'.' ); - if( suffix != 0 && !wcsrchr( suffix, L'/' ) ) - { - return complete_get_desc_suffix( suffix ); - } - - return COMPLETE_FILE_DESC ; + return COMPLETE_SYMLINK_DESC; + + } + else + { + switch( err ) + { + case ENOENT: + { + return COMPLETE_ROTTEN_SYMLINK_DESC; + } + + case ELOOP: + { + return COMPLETE_LOOP_SYMLINK_DESC; + } + } + /* + On unknown errors we do nothing. The file will be + given the default 'File' description or one based on the suffix. + */ + } + + } + else if( S_ISCHR(buf.st_mode) ) + { + return COMPLETE_CHAR_DESC; + } + else if( S_ISBLK(buf.st_mode) ) + { + return COMPLETE_BLOCK_DESC; + } + else if( S_ISFIFO(buf.st_mode) ) + { + return COMPLETE_FIFO_DESC; + } + else if( S_ISSOCK(buf.st_mode)) + { + return COMPLETE_SOCKET_DESC; + } + else if( S_ISDIR(buf.st_mode) ) + { + return COMPLETE_DIRECTORY_DESC; + } + else + { + if( ( buf.st_mode & S_IXUSR ) || + ( buf.st_mode & S_IXGRP ) || + ( buf.st_mode & S_IXOTH ) ) + { + + if( waccess( filename, X_OK ) == 0 ) + { + /* + Weird group permissions and other such issues + make it non-trivial to find out if we can + actually execute a file using the result from + stat. It is much safer to use the access + function, since it tells us exactly what we want + to know. + */ + return COMPLETE_EXEC_DESC; + } + } + } + } + + suffix = wcsrchr( filename.c_str(), L'.' ); + if( suffix != 0 && !wcsrchr( suffix, L'/' ) ) + { + return complete_get_desc_suffix( suffix ); + } + + return COMPLETE_FILE_DESC ; } /** - Add the specified filename if it matches the specified wildcard. + Add the specified filename if it matches the specified wildcard. If the filename matches, first get the description of the specified filename. If this is a regular file, append the filesize to the @@ -556,78 +556,78 @@ static wcstring file_get_desc( const wcstring &filename, \param wc the wildcard to match against \param is_cmd whether we are performing command completion */ -static void wildcard_completion_allocate( std::vector &list, - const wcstring &fullname, - const wcstring &completion, - const wchar_t *wc, +static void wildcard_completion_allocate( std::vector &list, + const wcstring &fullname, + const wcstring &completion, + const wchar_t *wc, expand_flags_t expand_flags) { - struct stat buf, lbuf; + struct stat buf, lbuf; wcstring sb; - wcstring munged_completion; - - int flags = 0; - int stat_res, lstat_res; - int stat_errno=0; - - long long sz; + wcstring munged_completion; - /* - If the file is a symlink, we need to stat both the file itself - _and_ the destination file. But we try to avoid this with - non-symlinks by first doing an lstat, and if the file is not a - link we copy the results over to the regular stat buffer. - */ - if( ( lstat_res = lwstat( fullname, &lbuf ) ) ) - { + int flags = 0; + int stat_res, lstat_res; + int stat_errno=0; + + long long sz; + + /* + If the file is a symlink, we need to stat both the file itself + _and_ the destination file. But we try to avoid this with + non-symlinks by first doing an lstat, and if the file is not a + link we copy the results over to the regular stat buffer. + */ + if( ( lstat_res = lwstat( fullname, &lbuf ) ) ) + { /* lstat failed! */ - sz=-1; - stat_res = lstat_res; - } - else - { - if (S_ISLNK(lbuf.st_mode)) - { - - if( ( stat_res = wstat( fullname, &buf ) ) ) - { - sz=-1; - } - else - { - sz = (long long)buf.st_size; - } - - /* - In order to differentiate between e.g. rotten symlinks - and symlink loops, we also need to know the error status of wstat. - */ - stat_errno = errno; - } - else - { - stat_res = lstat_res; - memcpy( &buf, &lbuf, sizeof( struct stat ) ); - sz = (long long)buf.st_size; - } - } - - + sz=-1; + stat_res = lstat_res; + } + else + { + if (S_ISLNK(lbuf.st_mode)) + { + + if( ( stat_res = wstat( fullname, &buf ) ) ) + { + sz=-1; + } + else + { + sz = (long long)buf.st_size; + } + + /* + In order to differentiate between e.g. rotten symlinks + and symlink loops, we also need to know the error status of wstat. + */ + stat_errno = errno; + } + else + { + stat_res = lstat_res; + memcpy( &buf, &lbuf, sizeof( struct stat ) ); + sz = (long long)buf.st_size; + } + } + + bool wants_desc = ! (expand_flags & EXPAND_NO_DESCRIPTIONS); - wcstring desc; + wcstring desc; if (wants_desc) desc = file_get_desc( fullname.c_str(), lstat_res, lbuf, stat_res, buf, stat_errno ); - - if( sz >= 0 && S_ISDIR(buf.st_mode) ) - { - flags = flags | COMPLETE_NO_SPACE; + + if( sz >= 0 && S_ISDIR(buf.st_mode) ) + { + flags = flags | COMPLETE_NO_SPACE; munged_completion = completion; munged_completion.push_back(L'/'); if (wants_desc) sb.append(desc); - } - else - { + } + else + { if (wants_desc) { if (! desc.empty()) @@ -637,10 +637,10 @@ static void wildcard_completion_allocate( std::vector &list, } sb.append(format_size(sz)); } - } - + } + const wcstring &completion_to_use = munged_completion.empty() ? completion : munged_completion; - wildcard_complete(completion_to_use, wc, sb.c_str(), NULL, list, flags); + wildcard_complete(completion_to_use, wc, sb.c_str(), NULL, list, flags); } /** @@ -649,30 +649,30 @@ static void wildcard_completion_allocate( std::vector &list, EXECUTABLES_ONLY and DIRECTORIES_ONLY. */ static int test_flags( const wchar_t *filename, - int flags ) + int flags ) { - if( flags & DIRECTORIES_ONLY ) - { - struct stat buf; - if( wstat( filename, &buf ) == -1 ) - { - return 0; - } + if( flags & DIRECTORIES_ONLY ) + { + struct stat buf; + if( wstat( filename, &buf ) == -1 ) + { + return 0; + } - if( !S_ISDIR( buf.st_mode ) ) - { - return 0; - } - } - - - if( flags & EXECUTABLES_ONLY ) - { - if ( waccess( filename, X_OK ) != 0) - return 0; - } - - return 1; + if( !S_ISDIR( buf.st_mode ) ) + { + return 0; + } + } + + + if( flags & EXECUTABLES_ONLY ) + { + if ( waccess( filename, X_OK ) != 0) + return 0; + } + + return 1; } /** Appends a completion to the completion list, if the string is missing from the set. */ @@ -692,415 +692,415 @@ typedef std::pair file_id_t; This function traverses the relevant directory tree looking for matches, and recurses when needed to handle wildcrards spanning multiple components and recursive wildcards. - + Because this function calls itself recursively with substrings, it's important that the parameters be raw pointers instead of wcstring, which would be too expensive to construct for all substrings. */ static int wildcard_expand_internal( const wchar_t *wc, - const wchar_t *base_dir, - expand_flags_t flags, - std::vector &out, + const wchar_t *base_dir, + expand_flags_t flags, + std::vector &out, std::set &completion_set, std::set &visited_files ) { - /* Points to the end of the current wildcard segment */ - const wchar_t *wc_end; + /* Points to the end of the current wildcard segment */ + const wchar_t *wc_end; - /* Variables for traversing a directory */ - DIR *dir; - - /* The result returned */ - int res = 0; - - /* Length of the directory to search in */ - size_t base_len; + /* Variables for traversing a directory */ + DIR *dir; - /* Variables for testing for presense of recursive wildcards */ - const wchar_t *wc_recursive; - bool is_recursive; + /* The result returned */ + int res = 0; - /* Slightly mangled version of base_dir */ - const wchar_t *dir_string; - - // debug( 3, L"WILDCARD_EXPAND %ls in %ls", wc, base_dir ); + /* Length of the directory to search in */ + size_t base_len; - if( reader_interrupted() ) - { - return -1; - } - - if( !wc || !base_dir ) - { - debug( 2, L"Got null string on line %d of file %s", __LINE__, __FILE__ ); - return 0; - } + /* Variables for testing for presense of recursive wildcards */ + const wchar_t *wc_recursive; + bool is_recursive; - if( flags & ACCEPT_INCOMPLETE ) - { - /* - Avoid excessive number of returned matches for wc ending with a * - */ - size_t len = wcslen(wc); - if( len && (wc[len-1]==ANY_STRING) ) - { - wchar_t * foo = wcsdup( wc ); - foo[len-1]=0; - int res = wildcard_expand_internal( foo, base_dir, flags, out, completion_set, visited_files ); - free( foo ); - return res; - } - } + /* Slightly mangled version of base_dir */ + const wchar_t *dir_string; - /* - Initialize various variables - */ + // debug( 3, L"WILDCARD_EXPAND %ls in %ls", wc, base_dir ); - dir_string = base_dir[0]==L'\0'?L".":base_dir; - - if( !(dir = wopendir( dir_string ))) - { - return 0; - } + if( reader_interrupted() ) + { + return -1; + } - wc_end = wcschr(wc,L'/'); - base_len = wcslen( base_dir ); + if( !wc || !base_dir ) + { + debug( 2, L"Got null string on line %d of file %s", __LINE__, __FILE__ ); + return 0; + } - /* - Test for recursive match string in current segment - */ - wc_recursive = wcschr( wc, ANY_STRING_RECURSIVE ); - is_recursive = ( wc_recursive && (!wc_end || wc_recursive < wc_end)); + if( flags & ACCEPT_INCOMPLETE ) + { + /* + Avoid excessive number of returned matches for wc ending with a * + */ + size_t len = wcslen(wc); + if( len && (wc[len-1]==ANY_STRING) ) + { + wchar_t * foo = wcsdup( wc ); + foo[len-1]=0; + int res = wildcard_expand_internal( foo, base_dir, flags, out, completion_set, visited_files ); + free( foo ); + return res; + } + } - /* - Is this segment of the wildcard the last? - */ - if( !wc_end ) - { - /* - Wildcard segment is the last segment, + /* + Initialize various variables + */ - Insert all matching files/directories - */ - if( wc[0]=='\0' ) - { - /* - The last wildcard segment is empty. Insert everything if - completing, the directory itself otherwise. - */ - if( flags & ACCEPT_INCOMPLETE ) - { + dir_string = base_dir[0]==L'\0'?L".":base_dir; + + if( !(dir = wopendir( dir_string ))) + { + return 0; + } + + wc_end = wcschr(wc,L'/'); + base_len = wcslen( base_dir ); + + /* + Test for recursive match string in current segment + */ + wc_recursive = wcschr( wc, ANY_STRING_RECURSIVE ); + is_recursive = ( wc_recursive && (!wc_end || wc_recursive < wc_end)); + + /* + Is this segment of the wildcard the last? + */ + if( !wc_end ) + { + /* + Wildcard segment is the last segment, + + Insert all matching files/directories + */ + if( wc[0]=='\0' ) + { + /* + The last wildcard segment is empty. Insert everything if + completing, the directory itself otherwise. + */ + if( flags & ACCEPT_INCOMPLETE ) + { wcstring next; - while(wreaddir(dir, next)) - { - if( next[0] != L'.' ) - { - wcstring long_name = make_path( base_dir, next ); - - if( test_flags( long_name.c_str(), flags ) ) - { - wildcard_completion_allocate( out, - long_name, - next, - L"", + while(wreaddir(dir, next)) + { + if( next[0] != L'.' ) + { + wcstring long_name = make_path( base_dir, next ); + + if( test_flags( long_name.c_str(), flags ) ) + { + wildcard_completion_allocate( out, + long_name, + next, + L"", flags); - } - } - } - } - else - { - res = 1; + } + } + } + } + else + { + res = 1; insert_completion_if_missing(base_dir, out, completion_set); - } - } - else - { - /* - This is the last wildcard segment, and it is not empty. Match files/directories. - */ + } + } + else + { + /* + This is the last wildcard segment, and it is not empty. Match files/directories. + */ wcstring next; - while (wreaddir(dir, next)) - { + while (wreaddir(dir, next)) + { const wchar_t * const name = next.c_str(); - if( flags & ACCEPT_INCOMPLETE ) - { - - const wcstring long_name = make_path( base_dir, next ); + if( flags & ACCEPT_INCOMPLETE ) + { - /* - Test for matches before stating file, so as to minimize the number of calls to the much slower stat function - */ - std::vector test; - if( wildcard_complete( name, - wc, - L"", - 0, - test, - 0 ) ) - { - if( test_flags( long_name.c_str(), flags ) ) - { - wildcard_completion_allocate( out, - long_name, + const wcstring long_name = make_path( base_dir, next ); + + /* + Test for matches before stating file, so as to minimize the number of calls to the much slower stat function + */ + std::vector test; + if( wildcard_complete( name, + wc, + L"", + 0, + test, + 0 ) ) + { + if( test_flags( long_name.c_str(), flags ) ) + { + wildcard_completion_allocate( out, + long_name, name, - wc, + wc, flags); - - } - } - } - else - { - if( wildcard_match2( name, wc, true ) ) - { + + } + } + } + else + { + if( wildcard_match2( name, wc, true ) ) + { const wcstring long_name = make_path(base_dir, next); - int skip = 0; - - if( is_recursive ) - { - /* - In recursive mode, we are only - interested in adding files -directories - will be added in the next pass. - */ - struct stat buf; - if( !wstat( long_name, &buf ) ) - { - skip = S_ISDIR(buf.st_mode); - } - } - if (! skip) - { + int skip = 0; + + if( is_recursive ) + { + /* + In recursive mode, we are only + interested in adding files -directories + will be added in the next pass. + */ + struct stat buf; + if( !wstat( long_name, &buf ) ) + { + skip = S_ISDIR(buf.st_mode); + } + } + if (! skip) + { insert_completion_if_missing(long_name, out, completion_set); - } - res = 1; - } - } - } - } - } + } + res = 1; + } + } + } + } + } - if( wc_end || is_recursive ) - { - /* - Wilcard segment is not the last segment. Recursively call - wildcard_expand for all matching subdirectories. - */ - - /* - wc_str is the part of the wildcarded string from the - beginning to the first slash - */ - wchar_t *wc_str; + if( wc_end || is_recursive ) + { + /* + Wilcard segment is not the last segment. Recursively call + wildcard_expand for all matching subdirectories. + */ - /* - new_dir is a scratch area containing the full path to a - file/directory we are iterating over - */ - wchar_t *new_dir; + /* + wc_str is the part of the wildcarded string from the + beginning to the first slash + */ + wchar_t *wc_str; - /* - The maximum length of a file element - */ - long ln=MAX_FILE_LENGTH; - char * narrow_dir_string = wcs2str( dir_string ); + /* + new_dir is a scratch area containing the full path to a + file/directory we are iterating over + */ + wchar_t *new_dir; - /* - In recursive mode, we look through the directory twice. If - so, this rewind is needed. - */ - rewinddir( dir ); + /* + The maximum length of a file element + */ + long ln=MAX_FILE_LENGTH; + char * narrow_dir_string = wcs2str( dir_string ); - if( narrow_dir_string ) - { - /* - Find out how long the filename can be in a worst case - scenario - */ - ln = pathconf( narrow_dir_string, _PC_NAME_MAX ); + /* + In recursive mode, we look through the directory twice. If + so, this rewind is needed. + */ + rewinddir( dir ); - /* - If not specified, use som large number as fallback - */ - if( ln < 0 ) - ln = MAX_FILE_LENGTH; - free( narrow_dir_string ); - } - new_dir= (wchar_t *)malloc( sizeof(wchar_t)*(base_len+ln+2) ); + if( narrow_dir_string ) + { + /* + Find out how long the filename can be in a worst case + scenario + */ + ln = pathconf( narrow_dir_string, _PC_NAME_MAX ); - wc_str = wc_end?wcsndup(wc, wc_end-wc):wcsdup(wc); + /* + If not specified, use som large number as fallback + */ + if( ln < 0 ) + ln = MAX_FILE_LENGTH; + free( narrow_dir_string ); + } + new_dir= (wchar_t *)malloc( sizeof(wchar_t)*(base_len+ln+2) ); - if( (!new_dir) || (!wc_str) ) - { - DIE_MEM(); - } + wc_str = wc_end?wcsndup(wc, wc_end-wc):wcsdup(wc); + + if( (!new_dir) || (!wc_str) ) + { + DIE_MEM(); + } + + wcscpy( new_dir, base_dir ); - wcscpy( new_dir, base_dir ); - wcstring next; - while (wreaddir(dir, next)) - { - const wchar_t *name = next.c_str(); - - /* - Test if the file/directory name matches the whole - wildcard element, i.e. regular matching. - */ - int whole_match = wildcard_match2( name, wc_str, true ); - int partial_match = 0; - - /* - If we are doing recursive matching, also check if this - directory matches the part up to the recusrive - wildcard, if so, then we can search all subdirectories - for matches. - */ - if( is_recursive ) - { - const wchar_t *end = wcschr( wc, ANY_STRING_RECURSIVE ); - wchar_t *wc_sub = wcsndup( wc, end-wc+1); - partial_match = wildcard_match2( name, wc_sub, true ); - free( wc_sub ); - } + while (wreaddir(dir, next)) + { + const wchar_t *name = next.c_str(); - if( whole_match || partial_match ) - { - struct stat buf; - char *dir_str; - int stat_res; - int new_res; + /* + Test if the file/directory name matches the whole + wildcard element, i.e. regular matching. + */ + int whole_match = wildcard_match2( name, wc_str, true ); + int partial_match = 0; - wcscpy(&new_dir[base_len], name ); - dir_str = wcs2str( new_dir ); - - if( dir_str ) - { - stat_res = stat( dir_str, &buf ); - free( dir_str ); - - if( !stat_res ) - { + /* + If we are doing recursive matching, also check if this + directory matches the part up to the recusrive + wildcard, if so, then we can search all subdirectories + for matches. + */ + if( is_recursive ) + { + const wchar_t *end = wcschr( wc, ANY_STRING_RECURSIVE ); + wchar_t *wc_sub = wcsndup( wc, end-wc+1); + partial_match = wildcard_match2( name, wc_sub, true ); + free( wc_sub ); + } + + if( whole_match || partial_match ) + { + struct stat buf; + char *dir_str; + int stat_res; + int new_res; + + wcscpy(&new_dir[base_len], name ); + dir_str = wcs2str( new_dir ); + + if( dir_str ) + { + stat_res = stat( dir_str, &buf ); + free( dir_str ); + + if( !stat_res ) + { // Insert a "file ID" into visited_files // If the insertion fails, we've already visited this file (i.e. a symlink loop) // If we're not recursive, insert anyways (in case we loop back around in a future recursive segment), but continue on; the idea being that literal path components should still work const file_id_t file_id(buf.st_dev, buf.st_ino); - if( S_ISDIR(buf.st_mode) && (visited_files.insert(file_id).second || ! is_recursive)) - { - size_t new_len = wcslen( new_dir ); - new_dir[new_len] = L'/'; - new_dir[new_len+1] = L'\0'; - - /* - Regular matching - */ - if( whole_match ) - { - const wchar_t *new_wc = L""; - if( wc_end ) - { - new_wc=wc_end+1; - /* - Accept multiple '/' as a single direcotry separator - */ - while(*new_wc==L'/') - { - new_wc++; - } - } - - new_res = wildcard_expand_internal( new_wc, - new_dir, - flags, - out, + if( S_ISDIR(buf.st_mode) && (visited_files.insert(file_id).second || ! is_recursive)) + { + size_t new_len = wcslen( new_dir ); + new_dir[new_len] = L'/'; + new_dir[new_len+1] = L'\0'; + + /* + Regular matching + */ + if( whole_match ) + { + const wchar_t *new_wc = L""; + if( wc_end ) + { + new_wc=wc_end+1; + /* + Accept multiple '/' as a single direcotry separator + */ + while(*new_wc==L'/') + { + new_wc++; + } + } + + new_res = wildcard_expand_internal( new_wc, + new_dir, + flags, + out, completion_set, visited_files ); - if( new_res == -1 ) - { - res = -1; - break; - } - res |= new_res; - - } - - /* - Recursive matching - */ - if( partial_match ) - { - - new_res = wildcard_expand_internal( wcschr( wc, ANY_STRING_RECURSIVE ), - new_dir, - flags | WILDCARD_RECURSIVE, - out, + if( new_res == -1 ) + { + res = -1; + break; + } + res |= new_res; + + } + + /* + Recursive matching + */ + if( partial_match ) + { + + new_res = wildcard_expand_internal( wcschr( wc, ANY_STRING_RECURSIVE ), + new_dir, + flags | WILDCARD_RECURSIVE, + out, completion_set, visited_files); - if( new_res == -1 ) - { - res = -1; - break; - } - res |= new_res; - - } - } - } - } - } - } - - free( wc_str ); - free( new_dir ); - } - closedir( dir ); + if( new_res == -1 ) + { + res = -1; + break; + } + res |= new_res; - return res; + } + } + } + } + } + } + + free( wc_str ); + free( new_dir ); + } + closedir( dir ); + + return res; } int wildcard_expand( const wchar_t *wc, - const wchar_t *base_dir, - expand_flags_t flags, - std::vector &out ) + const wchar_t *base_dir, + expand_flags_t flags, + std::vector &out ) { - size_t c = out.size(); - + size_t c = out.size(); + /* Make a set of used completion strings so we can do fast membership tests inside wildcard_expand_internal. Otherwise wildcards like '**' are very slow, because we end up with an N^2 membership test. */ - std::set completion_set; - for (std::vector::const_iterator iter = out.begin(); iter != out.end(); ++iter) - { + std::set completion_set; + for (std::vector::const_iterator iter = out.begin(); iter != out.end(); ++iter) + { completion_set.insert(iter->completion); - } - - std::set visited_files; - int res = wildcard_expand_internal( wc, base_dir, flags, out, completion_set, visited_files ); - - if( flags & ACCEPT_INCOMPLETE ) - { - wcstring wc_base; - const wchar_t *wc_base_ptr = wcsrchr( wc, L'/' ); - if( wc_base_ptr ) - { - wc_base = wcstring(wc, (wc_base_ptr-wc)+1); - } + } - for( size_t i=c; i visited_files; + int res = wildcard_expand_internal( wc, base_dir, flags, out, completion_set, visited_files ); + + if( flags & ACCEPT_INCOMPLETE ) + { + wcstring wc_base; + const wchar_t *wc_base_ptr = wcsrchr( wc, L'/' ); + if( wc_base_ptr ) + { + wc_base = wcstring(wc, (wc_base_ptr-wc)+1); + } + + for( size_t i=c; i &outputs ) diff --git a/wildcard.h b/wildcard.h index 9380375c5..f2bc54577 100644 --- a/wildcard.h +++ b/wildcard.h @@ -1,4 +1,4 @@ -/** \file wildcard.h +/** \file wildcard.h My own globbing implementation. Needed to implement this instead of using libs globbing to support tab-expansion of globbed @@ -31,41 +31,41 @@ class completion_t; */ enum { - /** Character representing any character except '/' */ - ANY_CHAR = WILDCARD_RESERVED, + /** Character representing any character except '/' */ + ANY_CHAR = WILDCARD_RESERVED, - /** Character representing any character string not containing '/' (A slash) */ - ANY_STRING, + /** Character representing any character string not containing '/' (A slash) */ + ANY_STRING, - /** Character representing any character string */ - ANY_STRING_RECURSIVE, + /** Character representing any character string */ + ANY_STRING_RECURSIVE, } - ; + ; /** - Expand the wildcard by matching against the filesystem. + Expand the wildcard by matching against the filesystem. - New strings are allocated using malloc and should be freed by the caller. + New strings are allocated using malloc and should be freed by the caller. - wildcard_expand works by dividing the wildcard into segments at - each directory boundary. Each segment is processed separatly. All - except the last segment are handled by matching the wildcard - segment against all subdirectories of matching directories, and - recursively calling wildcard_expand for matches. On the last - segment, matching is made to any file, and all matches are - inserted to the list. + wildcard_expand works by dividing the wildcard into segments at + each directory boundary. Each segment is processed separatly. All + except the last segment are handled by matching the wildcard + segment against all subdirectories of matching directories, and + recursively calling wildcard_expand for matches. On the last + segment, matching is made to any file, and all matches are + inserted to the list. - If wildcard_expand encounters any errors (such as insufficient - priviliges) during matching, no error messages will be printed and - wildcard_expand will continue the matching process. + If wildcard_expand encounters any errors (such as insufficient + priviliges) during matching, no error messages will be printed and + wildcard_expand will continue the matching process. - \param wc The wildcard string - \param base_dir The base directory of the filesystem to perform the match against - \param flags flags for the search. Can be any combination of ACCEPT_INCOMPLETE and EXECUTABLES_ONLY - \param out The list in which to put the output + \param wc The wildcard string + \param base_dir The base directory of the filesystem to perform the match against + \param flags flags for the search. Can be any combination of ACCEPT_INCOMPLETE and EXECUTABLES_ONLY + \param out The list in which to put the output + + \return 1 if matches where found, 0 otherwise. Return -1 on abort (I.e. ^C was pressed). - \return 1 if matches where found, 0 otherwise. Return -1 on abort (I.e. ^C was pressed). - */ int wildcard_expand_string(const wcstring &wc, const wcstring &base_dir, expand_flags_t flags, std::vector &out ); /** @@ -87,10 +87,10 @@ int wildcard_has( const wchar_t *str, int internal ); Test wildcard completion */ bool wildcard_complete(const wcstring &str, - const wchar_t *wc, - const wchar_t *desc, - wcstring (*desc_func)(const wcstring &), - std::vector &out, - expand_flags_t flags ); + const wchar_t *wc, + const wchar_t *desc, + wcstring (*desc_func)(const wcstring &), + std::vector &out, + expand_flags_t flags ); #endif diff --git a/wutil.cpp b/wutil.cpp index 661c4e931..fdd6680ba 100644 --- a/wutil.cpp +++ b/wutil.cpp @@ -1,6 +1,6 @@ /** \file wutil.c - Wide character equivalents of various standard unix - functions. + Wide character equivalents of various standard unix + functions. */ #include "config.h" @@ -70,7 +70,7 @@ bool wreaddir_resolving(DIR *dir, const std::wstring &dir_path, std::wstring &ou { struct dirent *d = readdir( dir ); if ( !d ) return false; - + out_name = str2wcstring(d->d_name); if (out_is_dir) { /* The caller cares if this is a directory, so check */ @@ -100,7 +100,7 @@ bool wreaddir(DIR *dir, std::wstring &out_name) { struct dirent *d = readdir( dir ); if ( !d ) return false; - + out_name = str2wcstring(d->d_name); return true; } @@ -108,34 +108,34 @@ bool wreaddir(DIR *dir, std::wstring &out_name) wchar_t *wgetcwd( wchar_t *buff, size_t sz ) { - char *buffc = (char *)malloc( sz*MAX_UTF8_BYTES); - char *res; - wchar_t *ret = 0; - - if( !buffc ) - { - errno = ENOMEM; - return 0; - } - - res = getcwd( buffc, sz*MAX_UTF8_BYTES ); - if( res ) - { - if( (size_t)-1 != mbstowcs( buff, buffc, sizeof( wchar_t ) * sz ) ) - { - ret = buff; - } - } - - free( buffc ); - - return ret; + char *buffc = (char *)malloc( sz*MAX_UTF8_BYTES); + char *res; + wchar_t *ret = 0; + + if( !buffc ) + { + errno = ENOMEM; + return 0; + } + + res = getcwd( buffc, sz*MAX_UTF8_BYTES ); + if( res ) + { + if( (size_t)-1 != mbstowcs( buff, buffc, sizeof( wchar_t ) * sz ) ) + { + ret = buff; + } + } + + free( buffc ); + + return ret; } int wchdir( const wcstring &dir ) { cstring tmp = wcs2string(dir); - return chdir( tmp.c_str() ); + return chdir( tmp.c_str() ); } FILE *wfopen(const wcstring &path, const char *mode) @@ -162,11 +162,11 @@ FILE *wfopen(const wcstring &path, const char *mode) /* Skip binary */ if (mode[idx] == 'b') idx++; - + /* Consider append option */ if (mode[idx] == '+') permissions = O_RDWR; - + int fd = wopen_cloexec(path, permissions | options, 0666); if (fd < 0) return NULL; @@ -210,7 +210,7 @@ static int wopen_internal(const wcstring &pathname, int flags, mode_t mode, bool fd = -1; } return fd; - + } int wopen(const wcstring &pathname, int flags, mode_t mode) { @@ -266,40 +266,40 @@ int wunlink(const wcstring &file_name) void wperror(const wcstring &s) { - int e = errno; - if( !s.empty() ) - { - fwprintf( stderr, L"%ls: ", s.c_str() ); - } - fwprintf( stderr, L"%s\n", strerror( e ) ); + int e = errno; + if( !s.empty() ) + { + fwprintf( stderr, L"%ls: ", s.c_str() ); + } + fwprintf( stderr, L"%s\n", strerror( e ) ); } #ifdef HAVE_REALPATH_NULL wchar_t *wrealpath(const wcstring &pathname, wchar_t *resolved_path) { - cstring tmp = wcs2string(pathname); - char *narrow_res = realpath( tmp.c_str(), 0 ); - wchar_t *res; + cstring tmp = wcs2string(pathname); + char *narrow_res = realpath( tmp.c_str(), 0 ); + wchar_t *res; - if( !narrow_res ) - return 0; - - if( resolved_path ) - { - wchar_t *tmp2 = str2wcs( narrow_res ); - wcslcpy( resolved_path, tmp2, PATH_MAX ); - free( tmp2 ); - res = resolved_path; - } - else - { - res = str2wcs( narrow_res ); - } + if( !narrow_res ) + return 0; + + if( resolved_path ) + { + wchar_t *tmp2 = str2wcs( narrow_res ); + wcslcpy( resolved_path, tmp2, PATH_MAX ); + free( tmp2 ); + res = resolved_path; + } + else + { + res = str2wcs( narrow_res ); + } free( narrow_res ); - return res; + return res; } #else @@ -307,25 +307,25 @@ wchar_t *wrealpath(const wcstring &pathname, wchar_t *resolved_path) wchar_t *wrealpath(const wcstring &pathname, wchar_t *resolved_path) { cstring tmp = wcs2string(pathname); - char narrow_buff[PATH_MAX]; - char *narrow_res = realpath( tmp.c_str(), narrow_buff ); - wchar_t *res; + char narrow_buff[PATH_MAX]; + char *narrow_res = realpath( tmp.c_str(), narrow_buff ); + wchar_t *res; - if( !narrow_res ) - return 0; - - if( resolved_path ) - { - wchar_t *tmp2 = str2wcs( narrow_res ); - wcslcpy( resolved_path, tmp2, PATH_MAX ); - free( tmp2 ); - res = resolved_path; - } - else - { - res = str2wcs( narrow_res ); - } - return res; + if( !narrow_res ) + return 0; + + if( resolved_path ) + { + wchar_t *tmp2 = str2wcs( narrow_res ); + wcslcpy( resolved_path, tmp2, PATH_MAX ); + free( tmp2 ); + res = resolved_path; + } + else + { + res = str2wcs( narrow_res ); + } + return res; } #endif @@ -352,8 +352,8 @@ wcstring wbasename( const wcstring &path ) /* Really init wgettext */ static void wgettext_really_init() { pthread_mutex_init(&wgettext_lock, NULL); - bindtextdomain( PACKAGE_NAME, LOCALEDIR ); - textdomain( PACKAGE_NAME ); + bindtextdomain( PACKAGE_NAME, LOCALEDIR ); + textdomain( PACKAGE_NAME ); } /** @@ -367,31 +367,31 @@ static void wgettext_init_if_necessary() const wchar_t *wgettext( const wchar_t *in ) { - if( !in ) - return in; - - // preserve errno across this since this is often used in printing error messages - int err = errno; - + if( !in ) + return in; + + // preserve errno across this since this is often used in printing error messages + int err = errno; + wgettext_init_if_necessary(); - - wcstring key = in; + + wcstring key = in; scoped_lock lock(wgettext_lock); - + wcstring *& val = wgettext_map[key]; if (val == NULL) { cstring mbs_in = wcs2string(key); char *out = gettext(mbs_in.c_str()); val = new wcstring(format_string(L"%s", out)); } - errno = err; - return val->c_str(); + errno = err; + return val->c_str(); } wcstring wgettext2(const wcstring &in) { wgettext_init_if_necessary(); - std::string mbs_in = wcs2string(in); - char *out = gettext( mbs_in.c_str() ); + std::string mbs_in = wcs2string(in); + char *out = gettext( mbs_in.c_str() ); wcstring result = format_string(L"%s", out); return result; } @@ -400,28 +400,28 @@ const wchar_t *wgetenv( const wcstring &name ) { ASSERT_IS_MAIN_THREAD(); cstring name_narrow = wcs2string(name); - char *res_narrow = getenv( name_narrow.c_str() ); - static wcstring out; + char *res_narrow = getenv( name_narrow.c_str() ); + static wcstring out; + + if( !res_narrow ) + return 0; - if( !res_narrow ) - return 0; - out = format_string(L"%s", res_narrow); - return out.c_str(); - + return out.c_str(); + } int wmkdir( const wcstring &name, int mode ) { - cstring name_narrow = wcs2string(name); - return mkdir( name_narrow.c_str(), mode ); + cstring name_narrow = wcs2string(name); + return mkdir( name_narrow.c_str(), mode ); } int wrename( const wcstring &old, const wcstring &newv ) { cstring old_narrow = wcs2string(old); - cstring new_narrow =wcs2string(newv); - return rename( old_narrow.c_str(), new_narrow.c_str() ); + cstring new_narrow =wcs2string(newv); + return rename( old_narrow.c_str(), new_narrow.c_str() ); } int fish_wcstoi(const wchar_t *str, wchar_t ** endptr, int base) diff --git a/wutil.h b/wutil.h index 1b7cb3089..d1bf770bb 100644 --- a/wutil.h +++ b/wutil.h @@ -1,7 +1,7 @@ /** \file wutil.h Prototypes for wide character equivalents of various standard unix - functions. + functions. */ #ifndef FISH_WUTIL_H #define FISH_WUTIL_H @@ -91,11 +91,11 @@ wchar_t *wgetcwd( wchar_t *buff, size_t sz ); */ int wchdir( const wcstring &dir ); -/** - Wide character version of realpath function. Just like the GNU - version of realpath, wrealpath will accept 0 as the value for the - second argument, in which case the result will be allocated using - malloc, and must be free'd by the user. +/** + Wide character version of realpath function. Just like the GNU + version of realpath, wrealpath will accept 0 as the value for the + second argument, in which case the result will be allocated using + malloc, and must be free'd by the user. */ wchar_t *wrealpath(const wcstring &pathname, wchar_t *resolved_path); diff --git a/xdgmime.cpp b/xdgmime.cpp index 571c77c64..5db32268f 100644 --- a/xdgmime.cpp +++ b/xdgmime.cpp @@ -2,13 +2,13 @@ /* xdgmime.c: XDG Mime Spec mime resolver. Based on version 0.11 of the spec. * * More info can be found at http://www.freedesktop.org/standards/ - * + * * Copyright (C) 2003,2004 Red Hat, Inc. * Copyright (C) 2003,2004 Jonathan Blandford * * Licensed under the Academic Free License version 2.0 * Or under the following terms: - * + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either @@ -16,7 +16,7 @@ * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public @@ -60,9 +60,9 @@ const char *xdg_mime_type_unknown = "application/octet-stream"; enum { - XDG_CHECKED_UNCHECKED, - XDG_CHECKED_VALID, - XDG_CHECKED_INVALID + XDG_CHECKED_UNCHECKED, + XDG_CHECKED_VALID, + XDG_CHECKED_INVALID }; struct XdgDirTimeList @@ -86,7 +86,7 @@ struct XdgCallbackList /* Function called by xdg_run_command_on_dirs. If it returns TRUE, further * directories aren't looked at */ typedef int (*XdgDirectoryFunc) (const char *directory, - void *user_data); + void *user_data); static XdgDirTimeList * xdg_dir_time_list_new (void) @@ -172,7 +172,7 @@ xdg_mime_init_from_directory (const char *directory) /* Runs a command on all the directories in the search path */ static void xdg_run_command_on_dirs (XdgDirectoryFunc func, - void *user_data) + void *user_data) { const char *xdg_data_home; const char *xdg_data_dirs; @@ -182,7 +182,7 @@ xdg_run_command_on_dirs (XdgDirectoryFunc func, if (xdg_data_home) { if ((func) (xdg_data_home, user_data)) - return; + return; } else { @@ -190,19 +190,19 @@ xdg_run_command_on_dirs (XdgDirectoryFunc func, home = getenv ("HOME"); if (home != NULL) - { - char *guessed_xdg_home; - int stop_processing; + { + char *guessed_xdg_home; + int stop_processing; - guessed_xdg_home = (char *)malloc (strlen (home) + strlen ("/.local/share/") + 1); - strcpy (guessed_xdg_home, home); - strcat (guessed_xdg_home, "/.local/share/"); - stop_processing = (func) (guessed_xdg_home, user_data); - free (guessed_xdg_home); + guessed_xdg_home = (char *)malloc (strlen (home) + strlen ("/.local/share/") + 1); + strcpy (guessed_xdg_home, home); + strcat (guessed_xdg_home, "/.local/share/"); + stop_processing = (func) (guessed_xdg_home, user_data); + free (guessed_xdg_home); - if (stop_processing) - return; - } + if (stop_processing) + return; + } } xdg_data_dirs = getenv ("XDG_DATA_DIRS"); @@ -220,18 +220,18 @@ xdg_run_command_on_dirs (XdgDirectoryFunc func, end_ptr = ptr; while (*end_ptr != ':' && *end_ptr != '\000') - end_ptr ++; + end_ptr ++; if (end_ptr == ptr) - { - ptr++; - continue; - } + { + ptr++; + continue; + } if (*end_ptr == ':') - len = end_ptr - ptr; + len = end_ptr - ptr; else - len = end_ptr - ptr + 1; + len = end_ptr - ptr + 1; dir = (char *)malloc (len + 1); strncpy (dir, ptr, len); dir[len] = '\0'; @@ -239,7 +239,7 @@ xdg_run_command_on_dirs (XdgDirectoryFunc func, free (dir); if (stop_processing) - return; + return; ptr = end_ptr; } @@ -262,18 +262,18 @@ xdg_check_file (const char *file_path) XdgDirTimeList *list; for (list = dir_time_list; list; list = list->next) - { - if (! strcmp (list->directory_name, file_path) && - st.st_mtime == list->mtime) - { - if (list->checked == XDG_CHECKED_UNCHECKED) - list->checked = XDG_CHECKED_VALID; - else if (list->checked == XDG_CHECKED_VALID) - list->checked = XDG_CHECKED_INVALID; + { + if (! strcmp (list->directory_name, file_path) && + st.st_mtime == list->mtime) + { + if (list->checked == XDG_CHECKED_UNCHECKED) + list->checked = XDG_CHECKED_VALID; + else if (list->checked == XDG_CHECKED_VALID) + list->checked = XDG_CHECKED_INVALID; - return (list->checked != XDG_CHECKED_VALID); - } - } + return (list->checked != XDG_CHECKED_VALID); + } + } return TRUE; } @@ -282,7 +282,7 @@ xdg_check_file (const char *file_path) static int xdg_check_dir (const char *directory, - int *invalid_dir_list) + int *invalid_dir_list) { int invalid; char *file_name; @@ -326,7 +326,7 @@ xdg_check_dirs (void) list->checked = XDG_CHECKED_UNCHECKED; xdg_run_command_on_dirs ((XdgDirectoryFunc) xdg_check_dir, - &invalid_dir_list); + &invalid_dir_list); if (invalid_dir_list) return TRUE; @@ -334,7 +334,7 @@ xdg_check_dirs (void) for (list = dir_time_list; list; list = list->next) { if (list->checked != XDG_CHECKED_VALID) - return TRUE; + return TRUE; } return FALSE; @@ -381,7 +381,7 @@ xdg_mime_init (void) parent_list = _xdg_mime_parent_list_new (); xdg_run_command_on_dirs ((XdgDirectoryFunc) xdg_mime_init_from_directory, - NULL); + NULL); need_reread = FALSE; } @@ -389,7 +389,7 @@ xdg_mime_init (void) const char * xdg_mime_get_mime_type_for_data (const void *data, - size_t len) + size_t len) { const char *mime_type; @@ -501,7 +501,7 @@ xdg_mime_shutdown (void) xdg_dir_time_list_free (dir_time_list); dir_time_list = NULL; } - + if (global_hash) { _xdg_glob_hash_free (global_hash); @@ -520,11 +520,11 @@ xdg_mime_shutdown (void) } if( parent_list ) - { - _xdg_mime_parent_list_free ( parent_list); - } - - + { + _xdg_mime_parent_list_free ( parent_list); + } + + for (list = callback_list; list; list = list->next) (list->callback) (list->data); @@ -535,7 +535,7 @@ int xdg_mime_get_max_buffer_extents (void) { xdg_mime_init (); - + return _xdg_mime_magic_get_buffer_extents (global_magic); } @@ -554,7 +554,7 @@ xdg_mime_unalias_mime_type (const char *mime_type) int xdg_mime_mime_type_equal (const char *mime_a, - const char *mime_b) + const char *mime_b) { const char *unalias_a, *unalias_b; @@ -571,14 +571,14 @@ xdg_mime_mime_type_equal (const char *mime_a, int xdg_mime_media_type_equal (const char *mime_a, - const char *mime_b) + const char *mime_b) { char *sep; xdg_mime_init (); sep = const_cast(strchr (mime_a, '/')); - + if (sep && strncmp (mime_a, mime_b, sep - mime_a + 1) == 0) return 1; @@ -604,7 +604,7 @@ xdg_mime_is_super_type (const char *mime) int xdg_mime_mime_type_subclass (const char *mime, - const char *base) + const char *base) { const char *umime, *ubase; const char **parents; @@ -617,7 +617,7 @@ xdg_mime_mime_type_subclass (const char *mime, if (strcmp (umime, ubase) == 0) return 1; -#if 0 +#if 0 /* Handle supertypes */ if (xdg_mime_is_super_type (ubase) && xdg_mime_media_type_equal (umime, ubase)) @@ -625,18 +625,18 @@ xdg_mime_mime_type_subclass (const char *mime, #endif /* Handle special cases text/plain and application/octet-stream */ - if (strcmp (ubase, "text/plain") == 0 && + if (strcmp (ubase, "text/plain") == 0 && strncmp (umime, "text/", 5) == 0) return 1; if (strcmp (ubase, "application/octet-stream") == 0) return 1; - + parents = _xdg_mime_parent_list_lookup (parent_list, umime); for (; parents && *parents; parents++) { if (xdg_mime_mime_type_subclass (*parents, ubase)) - return 1; + return 1; } return 0; @@ -654,7 +654,7 @@ xdg_mime_get_mime_parents (const char *mime) return _xdg_mime_parent_list_lookup (parent_list, umime); } -void +void xdg_mime_dump (void) { printf ("*** ALIASES ***\n\n"); @@ -668,8 +668,8 @@ xdg_mime_dump (void) */ int xdg_mime_register_reload_callback (XdgMimeCallback callback, - void *data, - XdgMimeDestroy destroy) + void *data, + XdgMimeDestroy destroy) { XdgCallbackList *list_el; static int callback_id = 1; @@ -698,19 +698,19 @@ xdg_mime_remove_callback (int callback_id) for (list = callback_list; list; list = list->next) { if (list->callback_id == callback_id) - { - if (list->next) - list->next = list->prev; + { + if (list->next) + list->next = list->prev; - if (list->prev) - list->prev->next = list->next; - else - callback_list = list->next; + if (list->prev) + list->prev->next = list->next; + else + callback_list = list->next; - /* invoke the destroy handler */ - (list->destroy) (list->data); - free (list); - return; - } + /* invoke the destroy handler */ + (list->destroy) (list->data); + free (list); + return; + } } } diff --git a/xdgmime.h b/xdgmime.h index fd3647b59..36c61338f 100644 --- a/xdgmime.h +++ b/xdgmime.h @@ -2,13 +2,13 @@ /* xdgmime.h: XDG Mime Spec mime resolver. Based on version 0.11 of the spec. * * More info can be found at http://www.freedesktop.org/standards/ - * + * * Copyright (C) 2003 Red Hat, Inc. * Copyright (C) 2003 Jonathan Blandford * * Licensed under the Academic Free License version 2.0 * Or under the following terms: - * + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either @@ -16,7 +16,7 @@ * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public @@ -44,7 +44,7 @@ extern "C" { typedef void (*XdgMimeCallback) (void *user_data); typedef void (*XdgMimeDestroy) (void *user_data); - + #ifdef XDG_PREFIX #define xdg_mime_get_mime_type_for_data XDG_ENTRY(get_mime_type_for_data) #define xdg_mime_get_mime_type_for_file XDG_ENTRY(get_mime_type_for_file) @@ -66,24 +66,24 @@ extern const char *xdg_mime_type_unknown; #define XDG_MIME_TYPE_UNKNOWN xdg_mime_type_unknown const char *xdg_mime_get_mime_type_for_data (const void *data, - size_t len); + size_t len); const char *xdg_mime_get_mime_type_for_file (const char *file_name); const char *xdg_mime_get_mime_type_from_file_name (const char *file_name); int xdg_mime_is_valid_mime_type (const char *mime_type); int xdg_mime_mime_type_equal (const char *mime_a, - const char *mime_b); + const char *mime_b); int xdg_mime_media_type_equal (const char *mime_a, - const char *mime_b); + const char *mime_b); int xdg_mime_mime_type_subclass (const char *mime_a, - const char *mime_b); -const char **xdg_mime_get_mime_parents (const char *mime); -const char *xdg_mime_unalias_mime_type (const char *mime); + const char *mime_b); +const char **xdg_mime_get_mime_parents (const char *mime); +const char *xdg_mime_unalias_mime_type (const char *mime); int xdg_mime_get_max_buffer_extents (void); void xdg_mime_shutdown (void); void xdg_mime_dump (void); int xdg_mime_register_reload_callback (XdgMimeCallback callback, - void *data, - XdgMimeDestroy destroy); + void *data, + XdgMimeDestroy destroy); void xdg_mime_remove_callback (int callback_id); #ifdef __cplusplus diff --git a/xdgmimealias.cpp b/xdgmimealias.cpp index e1701dbc7..4bb411eff 100644 --- a/xdgmimealias.cpp +++ b/xdgmimealias.cpp @@ -16,7 +16,7 @@ * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public @@ -37,17 +37,17 @@ #include #include -#ifndef FALSE -#define FALSE (0) +#ifndef FALSE +#define FALSE (0) #endif -#ifndef TRUE -#define TRUE (!FALSE) +#ifndef TRUE +#define TRUE (!FALSE) #endif typedef struct XdgAlias XdgAlias; -struct XdgAlias +struct XdgAlias { char *alias; char *mime_type; @@ -72,7 +72,7 @@ _xdg_mime_alias_list_new (void) return list; } -void +void _xdg_mime_alias_list_free (XdgAliasList *list) { int i; @@ -80,10 +80,10 @@ _xdg_mime_alias_list_free (XdgAliasList *list) if (list->aliases) { for (i = 0; i < list->n_aliases; i++) - { - free (list->aliases[i].alias); - free (list->aliases[i].mime_type); - } + { + free (list->aliases[i].alias); + free (list->aliases[i].mime_type); + } free (list->aliases); } free (list); @@ -97,7 +97,7 @@ alias_entry_cmp (const void *v1, const void *v2) const char * _xdg_mime_alias_list_lookup (XdgAliasList *list, - const char *alias) + const char *alias) { XdgAlias *entry; XdgAlias key; @@ -108,7 +108,7 @@ _xdg_mime_alias_list_lookup (XdgAliasList *list, key.mime_type = 0; entry = (XdgAlias *)bsearch (&key, list->aliases, list->n_aliases, - sizeof (XdgAlias), alias_entry_cmp); + sizeof (XdgAlias), alias_entry_cmp); if (entry) return entry->mime_type; } @@ -118,7 +118,7 @@ _xdg_mime_alias_list_lookup (XdgAliasList *list, void _xdg_mime_alias_read_from_file (XdgAliasList *list, - const char *file_name) + const char *file_name) { FILE *file; char line[255]; @@ -138,30 +138,30 @@ _xdg_mime_alias_read_from_file (XdgAliasList *list, { char *sep; if (line[0] == '#') - continue; + continue; sep = strchr (line, ' '); if (sep == NULL) - continue; + continue; *(sep++) = '\000'; sep[strlen (sep) -1] = '\000'; if (list->n_aliases == alloc) - { - alloc <<= 1; - list->aliases = (XdgAlias *)realloc (list->aliases, - alloc * sizeof (XdgAlias)); - } + { + alloc <<= 1; + list->aliases = (XdgAlias *)realloc (list->aliases, + alloc * sizeof (XdgAlias)); + } list->aliases[list->n_aliases].alias = strdup (line); list->aliases[list->n_aliases].mime_type = strdup (sep); list->n_aliases++; } - list->aliases = (XdgAlias *)realloc (list->aliases, - list->n_aliases * sizeof (XdgAlias)); + list->aliases = (XdgAlias *)realloc (list->aliases, + list->n_aliases * sizeof (XdgAlias)); + + fclose (file); - fclose (file); - if (list->n_aliases > 1) - qsort (list->aliases, list->n_aliases, + qsort (list->aliases, list->n_aliases, sizeof (XdgAlias), alias_entry_cmp); } @@ -174,11 +174,11 @@ _xdg_mime_alias_list_dump (XdgAliasList *list) if (list->aliases) { for (i = 0; i < list->n_aliases; i++) - { - printf ("%s %s\n", - list->aliases[i].alias, - list->aliases[i].mime_type); - } + { + printf ("%s %s\n", + list->aliases[i].alias, + list->aliases[i].mime_type); + } } } diff --git a/xdgmimealias.h b/xdgmimealias.h index 3df18d660..d69593f0d 100644 --- a/xdgmimealias.h +++ b/xdgmimealias.h @@ -16,7 +16,7 @@ * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public @@ -40,11 +40,11 @@ typedef struct XdgAliasList XdgAliasList; #endif void _xdg_mime_alias_read_from_file (XdgAliasList *list, - const char *file_name); + const char *file_name); XdgAliasList *_xdg_mime_alias_list_new (void); void _xdg_mime_alias_list_free (XdgAliasList *list); const char *_xdg_mime_alias_list_lookup (XdgAliasList *list, - const char *alias); + const char *alias); void _xdg_mime_alias_list_dump (XdgAliasList *list); #endif /* __XDG_MIME_ALIAS_H__ */ diff --git a/xdgmimeglob.cpp b/xdgmimeglob.cpp index a485765ba..6b6c410eb 100644 --- a/xdgmimeglob.cpp +++ b/xdgmimeglob.cpp @@ -16,7 +16,7 @@ * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public @@ -37,12 +37,12 @@ #include #include -#ifndef FALSE -#define FALSE (0) +#ifndef FALSE +#define FALSE (0) #endif -#ifndef TRUE -#define TRUE (!FALSE) +#ifndef TRUE +#define TRUE (!FALSE) #endif typedef struct XdgGlobHashNode XdgGlobHashNode; @@ -95,9 +95,9 @@ _xdg_glob_list_free (XdgGlobList *glob_list) next = ptr->next; if (ptr->data) - free ((void *) ptr->data); + free ((void *) ptr->data); if (ptr->mime_type) - free ((void *) ptr->mime_type); + free ((void *) ptr->mime_type); free (ptr); ptr = next; @@ -106,8 +106,8 @@ _xdg_glob_list_free (XdgGlobList *glob_list) static XdgGlobList * _xdg_glob_list_append (XdgGlobList *glob_list, - void *data, - const char *mime_type) + void *data, + const char *mime_type) { XdgGlobList *new_element; XdgGlobList *tmp_element; @@ -130,8 +130,8 @@ _xdg_glob_list_append (XdgGlobList *glob_list, #if 0 static XdgGlobList * _xdg_glob_list_prepend (XdgGlobList *glob_list, - void *data, - const char *mime_type) + void *data, + const char *mime_type) { XdgGlobList *new_element; @@ -159,7 +159,7 @@ _xdg_glob_hash_node_new (void) static void _xdg_glob_hash_node_dump (XdgGlobHashNode *glob_hash_node, - int depth) + int depth) { int i; for (i = 0; i < depth; i++) @@ -178,8 +178,8 @@ _xdg_glob_hash_node_dump (XdgGlobHashNode *glob_hash_node, static XdgGlobHashNode * _xdg_glob_hash_insert_text (XdgGlobHashNode *glob_hash_node, - const char *text, - const char *mime_type) + const char *text, + const char *mime_type) { XdgGlobHashNode *node; xdg_unichar_t character; @@ -209,33 +209,33 @@ _xdg_glob_hash_insert_text (XdgGlobHashNode *glob_hash_node, node = prev_node->next; while (node != NULL) - { - if (character < node->character) - { - node = _xdg_glob_hash_node_new (); - node->character = character; - node->next = prev_node->next; - prev_node->next = node; + { + if (character < node->character) + { + node = _xdg_glob_hash_node_new (); + node->character = character; + node->next = prev_node->next; + prev_node->next = node; - found_node = TRUE; - break; - } - else if (character == node->character) - { - found_node = TRUE; - break; - } - prev_node = node; - node = node->next; - } + found_node = TRUE; + break; + } + else if (character == node->character) + { + found_node = TRUE; + break; + } + prev_node = node; + node = node->next; + } if (! found_node) - { - node = _xdg_glob_hash_node_new (); - node->character = character; - node->next = prev_node->next; - prev_node->next = node; - } + { + node = _xdg_glob_hash_node_new (); + node->character = character; + node->next = prev_node->next; + prev_node->next = node; + } } text = _xdg_utf8_next_char (text); @@ -252,8 +252,8 @@ _xdg_glob_hash_insert_text (XdgGlobHashNode *glob_hash_node, static const char * _xdg_glob_hash_node_lookup_file_name (XdgGlobHashNode *glob_hash_node, - const char *file_name, - int ignore_case) + const char *file_name, + int ignore_case) { XdgGlobHashNode *node; xdg_unichar_t character; @@ -268,22 +268,22 @@ _xdg_glob_hash_node_lookup_file_name (XdgGlobHashNode *glob_hash_node, for (node = glob_hash_node; node && character >= node->character; node = node->next) { if (character == node->character) - { - file_name = _xdg_utf8_next_char (file_name); - if (*file_name == '\000') - return node->mime_type; - else - return _xdg_glob_hash_node_lookup_file_name (node->child, - file_name, - ignore_case); - } + { + file_name = _xdg_utf8_next_char (file_name); + if (*file_name == '\000') + return node->mime_type; + else + return _xdg_glob_hash_node_lookup_file_name (node->child, + file_name, + ignore_case); + } } return NULL; } const char * _xdg_glob_hash_lookup_file_name (XdgGlobHash *glob_hash, - const char *file_name) + const char *file_name) { XdgGlobList *list; const char *mime_type; @@ -302,11 +302,11 @@ _xdg_glob_hash_lookup_file_name (XdgGlobHash *glob_hash, mime_type = (_xdg_glob_hash_node_lookup_file_name (glob_hash->simple_node, ptr, FALSE)); if (mime_type != NULL) return mime_type; - + mime_type = (_xdg_glob_hash_node_lookup_file_name (glob_hash->simple_node, ptr, TRUE)); if (mime_type != NULL) return mime_type; - + ptr = strchr (ptr+1, '.'); } @@ -344,7 +344,7 @@ _xdg_glob_hash_free_nodes (XdgGlobHashNode *node) if (node->next) _xdg_glob_hash_free_nodes (node->next); if (node->mime_type) - free ((void *) node->mime_type); + free ((void *) node->mime_type); free (node); } } @@ -370,9 +370,9 @@ _xdg_glob_determine_type (const char *glob) while (*ptr != '\000') { if (*ptr == '*' && first_char) - maybe_in_simple_glob = TRUE; + maybe_in_simple_glob = TRUE; else if (*ptr == '\\' || *ptr == '[' || *ptr == '?' || *ptr == '*') - return XDG_GLOB_FULL; + return XDG_GLOB_FULL; first_char = FALSE; ptr = _xdg_utf8_next_char (ptr); @@ -386,8 +386,8 @@ _xdg_glob_determine_type (const char *glob) /* glob must be valid UTF-8 */ void _xdg_glob_hash_append_glob (XdgGlobHash *glob_hash, - const char *glob, - const char *mime_type) + const char *glob, + const char *mime_type) { XdgGlobType type; @@ -422,7 +422,7 @@ _xdg_glob_hash_dump (XdgGlobHash *glob_hash) else { for (list = glob_hash->literal_list; list; list = list->next) - printf (" %s - %s\n", (char *)list->data, list->mime_type); + printf (" %s - %s\n", (char *)list->data, list->mime_type); } printf ("\nSIMPLE GLOBS\n"); _xdg_glob_hash_node_dump (glob_hash->simple_node, 4); @@ -435,14 +435,14 @@ _xdg_glob_hash_dump (XdgGlobHash *glob_hash) else { for (list = glob_hash->full_list; list; list = list->next) - printf (" %s - %s\n", (char *)list->data, list->mime_type); + printf (" %s - %s\n", (char *)list->data, list->mime_type); } } void _xdg_mime_glob_read_from_file (XdgGlobHash *glob_hash, - const char *file_name) + const char *file_name) { FILE *glob_file; char line[255]; @@ -459,11 +459,11 @@ _xdg_mime_glob_read_from_file (XdgGlobHash *glob_hash, { char *colon; if (line[0] == '#') - continue; + continue; colon = strchr (line, ':'); if (colon == NULL) - continue; + continue; *(colon++) = '\000'; colon[strlen (colon) -1] = '\000'; _xdg_glob_hash_append_glob (glob_hash, colon, line); diff --git a/xdgmimeglob.h b/xdgmimeglob.h index 771c4527f..2e6a577d5 100644 --- a/xdgmimeglob.h +++ b/xdgmimeglob.h @@ -16,7 +16,7 @@ * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public @@ -39,7 +39,7 @@ typedef enum XDG_GLOB_FULL /* x*.[ch] */ } XdgGlobType; - + #ifdef XDG_PREFIX #define _xdg_mime_glob_read_from_file XDG_ENTRY(glob_read_from_file) #define _xdg_glob_hash_new XDG_ENTRY(hash_new) @@ -51,14 +51,14 @@ typedef enum #endif void _xdg_mime_glob_read_from_file (XdgGlobHash *glob_hash, - const char *file_name); + const char *file_name); XdgGlobHash *_xdg_glob_hash_new (void); void _xdg_glob_hash_free (XdgGlobHash *glob_hash); const char *_xdg_glob_hash_lookup_file_name (XdgGlobHash *glob_hash, - const char *text); + const char *text); void _xdg_glob_hash_append_glob (XdgGlobHash *glob_hash, - const char *glob, - const char *mime_type); + const char *glob, + const char *mime_type); XdgGlobType _xdg_glob_determine_type (const char *glob); void _xdg_glob_hash_dump (XdgGlobHash *glob_hash); diff --git a/xdgmimeint.cpp b/xdgmimeint.cpp index 4a0ac4cc3..72e1d78fe 100644 --- a/xdgmimeint.cpp +++ b/xdgmimeint.cpp @@ -16,7 +16,7 @@ * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public @@ -33,12 +33,12 @@ #include #include -#ifndef FALSE -#define FALSE (0) +#ifndef FALSE +#define FALSE (0) #endif -#ifndef TRUE -#define TRUE (!FALSE) +#ifndef TRUE +#define TRUE (!FALSE) #endif static const char _xdg_utf8_skip_data[256] = { @@ -70,49 +70,49 @@ _xdg_utf8_to_ucs4(const char *source) int bytelength = 0; xdg_unichar_t result; if ( ! (*source & 0x40) ) - { - ucs32 = *source; - } + { + ucs32 = *source; + } else - { - if ( ! (*source & 0x20) ) - { - result = *source++ & 0x1F; - bytelength = 2; - } - else if ( ! (*source & 0x10) ) - { - result = *source++ & 0x0F; - bytelength = 3; - } - else if ( ! (*source & 0x08) ) - { - result = *source++ & 0x07; - bytelength = 4; - } - else if ( ! (*source & 0x04) ) - { - result = *source++ & 0x03; - bytelength = 5; - } - else if ( ! (*source & 0x02) ) - { - result = *source++ & 0x01; - bytelength = 6; - } - else - { - result = *source++; - bytelength = 1; - } + { + if ( ! (*source & 0x20) ) + { + result = *source++ & 0x1F; + bytelength = 2; + } + else if ( ! (*source & 0x10) ) + { + result = *source++ & 0x0F; + bytelength = 3; + } + else if ( ! (*source & 0x08) ) + { + result = *source++ & 0x07; + bytelength = 4; + } + else if ( ! (*source & 0x04) ) + { + result = *source++ & 0x03; + bytelength = 5; + } + else if ( ! (*source & 0x02) ) + { + result = *source++ & 0x01; + bytelength = 6; + } + else + { + result = *source++; + bytelength = 1; + } - for ( bytelength --; bytelength > 0; bytelength -- ) - { - result <<= 6; - result |= *source++ & 0x3F; - } - ucs32 = result; - } + for ( bytelength --; bytelength > 0; bytelength -- ) + { + result <<= 6; + result |= *source++ & 0x3F; + } + ucs32 = result; + } } return ucs32; } diff --git a/xdgmimeint.h b/xdgmimeint.h index 288148719..4aa9d1a2c 100644 --- a/xdgmimeint.h +++ b/xdgmimeint.h @@ -16,7 +16,7 @@ * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public @@ -31,12 +31,12 @@ #include "xdgmime.h" -#ifndef FALSE -#define FALSE (0) +#ifndef FALSE +#define FALSE (0) #endif -#ifndef TRUE -#define TRUE (!FALSE) +#ifndef TRUE +#define TRUE (!FALSE) #endif /* FIXME: Needs to be configure check */ @@ -55,10 +55,10 @@ typedef unsigned int xdg_uint32_t; #define SWAP_BE16_TO_LE16(val) (xdg_uint16_t)(((xdg_uint16_t)(val) << 8)|((xdg_uint16_t)(val) >> 8)) -#define SWAP_BE32_TO_LE32(val) (xdg_uint32_t)((((xdg_uint32_t)(val) & 0xFF000000U) >> 24) | \ - (((xdg_uint32_t)(val) & 0x00FF0000U) >> 8) | \ - (((xdg_uint32_t)(val) & 0x0000FF00U) << 8) | \ - (((xdg_uint32_t)(val) & 0x000000FFU) << 24)) +#define SWAP_BE32_TO_LE32(val) (xdg_uint32_t)((((xdg_uint32_t)(val) & 0xFF000000U) >> 24) | \ + (((xdg_uint32_t)(val) & 0x00FF0000U) >> 8) | \ + (((xdg_uint32_t)(val) & 0x0000FF00U) << 8) | \ + (((xdg_uint32_t)(val) & 0x000000FFU) << 24)) /* UTF-8 utils */ extern const char *const _xdg_utf8_skip; diff --git a/xdgmimemagic.cpp b/xdgmimemagic.cpp index 5b624fe26..5caf10433 100644 --- a/xdgmimemagic.cpp +++ b/xdgmimemagic.cpp @@ -16,7 +16,7 @@ * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public @@ -39,12 +39,12 @@ #include #include -#ifndef FALSE -#define FALSE (0) +#ifndef FALSE +#define FALSE (0) #endif -#ifndef TRUE -#define TRUE (!FALSE) +#ifndef TRUE +#define TRUE (!FALSE) #endif extern int errno; @@ -121,11 +121,11 @@ _xdg_mime_magic_matchlet_free (XdgMimeMagicMatchlet *mime_magic_matchlet) if (mime_magic_matchlet) { if (mime_magic_matchlet->next) - _xdg_mime_magic_matchlet_free (mime_magic_matchlet->next); + _xdg_mime_magic_matchlet_free (mime_magic_matchlet->next); if (mime_magic_matchlet->value) - free (mime_magic_matchlet->value); + free (mime_magic_matchlet->value); if (mime_magic_matchlet->mask) - free (mime_magic_matchlet->mask); + free (mime_magic_matchlet->mask); free (mime_magic_matchlet); } } @@ -144,9 +144,9 @@ _xdg_mime_magic_match_free (XdgMimeMagicMatch *mime_magic_match) next = ptr->next; if (ptr->mime_type) - free ((void *) ptr->mime_type); + free ((void *) ptr->mime_type); if (ptr->matchlet) - _xdg_mime_magic_matchlet_free (ptr->matchlet); + _xdg_mime_magic_matchlet_free (ptr->matchlet); free (ptr); ptr = next; @@ -158,7 +158,7 @@ _xdg_mime_magic_match_free (XdgMimeMagicMatch *mime_magic_match) */ static char * _xdg_mime_magic_read_to_newline (FILE *magic_file, - int *end_of_file) + int *end_of_file) { char *retval; int c; @@ -173,18 +173,18 @@ _xdg_mime_magic_read_to_newline (FILE *magic_file, { c = getc_unlocked (magic_file); if (c == EOF) - { - *end_of_file = TRUE; - break; - } + { + *end_of_file = TRUE; + break; + } if (c == '\n' || c == '\000') - break; + break; retval[pos++] = (char) c; if (pos % 128 == 127) - { - len = len + 128; - retval = (char *)realloc (retval, len); - } + { + len = len + 128; + retval = (char *)realloc (retval, len); + } } retval[pos] = '\000'; @@ -195,7 +195,7 @@ _xdg_mime_magic_read_to_newline (FILE *magic_file, */ static int _xdg_mime_magic_read_a_number (FILE *magic_file, - int *end_of_file) + int *end_of_file) { /* LONG_MAX is about 20 characters on my system */ #define MAX_NUMBER_SIZE 30 @@ -209,19 +209,19 @@ _xdg_mime_magic_read_a_number (FILE *magic_file, c = getc_unlocked (magic_file); if (c == EOF) - { - *end_of_file = TRUE; - break; - } + { + *end_of_file = TRUE; + break; + } if (! isdigit (c)) - { - ungetc (c, magic_file); - break; - } + { + ungetc (c, magic_file); + break; + } number_string[pos] = (char) c; pos++; if (pos == MAX_NUMBER_SIZE) - break; + break; } if (pos > 0) { @@ -230,7 +230,7 @@ _xdg_mime_magic_read_a_number (FILE *magic_file, retval = strtol (number_string, NULL, 10); if ((retval < INT_MIN) || (retval > INT_MAX) || (errno != 0)) - return -1; + return -1; } return retval; @@ -297,9 +297,9 @@ _xdg_mime_magic_parse_error (FILE *magic_file) { c = getc_unlocked (magic_file); if (c == EOF) - return XDG_MIME_MAGIC_EOF; + return XDG_MIME_MAGIC_EOF; if (c == '\n') - return XDG_MIME_MAGIC_SECTION; + return XDG_MIME_MAGIC_SECTION; } } @@ -309,7 +309,7 @@ _xdg_mime_magic_parse_error (FILE *magic_file) */ static XdgMimeMagicState _xdg_mime_magic_parse_magic_line (FILE *magic_file, - XdgMimeMagicMatch *match) + XdgMimeMagicMatch *match) { XdgMimeMagicMatchlet *matchlet; int c; @@ -338,12 +338,12 @@ _xdg_mime_magic_parse_magic_line (FILE *magic_file, ungetc (c, magic_file); indent = _xdg_mime_magic_read_a_number (magic_file, &end_of_file); if (end_of_file) - return XDG_MIME_MAGIC_EOF; + return XDG_MIME_MAGIC_EOF; if (indent == -1) - return XDG_MIME_MAGIC_ERROR; + return XDG_MIME_MAGIC_ERROR; c = getc_unlocked (magic_file); if (c == EOF) - return XDG_MIME_MAGIC_EOF; + return XDG_MIME_MAGIC_EOF; } if (c != '>') @@ -406,9 +406,9 @@ _xdg_mime_magic_parse_magic_line (FILE *magic_file, { _xdg_mime_magic_matchlet_free (matchlet); if (feof (magic_file)) - return XDG_MIME_MAGIC_EOF; + return XDG_MIME_MAGIC_EOF; else - return XDG_MIME_MAGIC_ERROR; + return XDG_MIME_MAGIC_ERROR; } c = getc_unlocked (magic_file); @@ -417,19 +417,19 @@ _xdg_mime_magic_parse_magic_line (FILE *magic_file, matchlet->mask = (unsigned char *)malloc (matchlet->value_length); /* OOM */ if (matchlet->mask == NULL) - { - _xdg_mime_magic_matchlet_free (matchlet); - return XDG_MIME_MAGIC_ERROR; - } + { + _xdg_mime_magic_matchlet_free (matchlet); + return XDG_MIME_MAGIC_ERROR; + } bytes_read = fread (matchlet->mask, 1, matchlet->value_length, magic_file); if (bytes_read != matchlet->value_length) - { - _xdg_mime_magic_matchlet_free (matchlet); - if (feof (magic_file)) - return XDG_MIME_MAGIC_EOF; - else - return XDG_MIME_MAGIC_ERROR; - } + { + _xdg_mime_magic_matchlet_free (matchlet); + if (feof (magic_file)) + return XDG_MIME_MAGIC_EOF; + else + return XDG_MIME_MAGIC_ERROR; + } c = getc_unlocked (magic_file); } @@ -437,18 +437,18 @@ _xdg_mime_magic_parse_magic_line (FILE *magic_file, { matchlet->word_size = _xdg_mime_magic_read_a_number (magic_file, &end_of_file); if (end_of_file) - { - _xdg_mime_magic_matchlet_free (matchlet); - return XDG_MIME_MAGIC_EOF; - } + { + _xdg_mime_magic_matchlet_free (matchlet); + return XDG_MIME_MAGIC_EOF; + } if (matchlet->word_size != 0 && - matchlet->word_size != 1 && - matchlet->word_size != 2 && - matchlet->word_size != 4) - { - _xdg_mime_magic_matchlet_free (matchlet); - return XDG_MIME_MAGIC_ERROR; - } + matchlet->word_size != 1 && + matchlet->word_size != 2 && + matchlet->word_size != 4) + { + _xdg_mime_magic_matchlet_free (matchlet); + return XDG_MIME_MAGIC_ERROR; + } c = getc_unlocked (magic_file); } @@ -456,15 +456,15 @@ _xdg_mime_magic_parse_magic_line (FILE *magic_file, { matchlet->range_length = _xdg_mime_magic_read_a_number (magic_file, &end_of_file); if (end_of_file) - { - _xdg_mime_magic_matchlet_free (matchlet); - return XDG_MIME_MAGIC_EOF; - } + { + _xdg_mime_magic_matchlet_free (matchlet); + return XDG_MIME_MAGIC_EOF; + } if (matchlet->range_length == (unsigned int)-1) - { - _xdg_mime_magic_matchlet_free (matchlet); - return XDG_MIME_MAGIC_ERROR; - } + { + _xdg_mime_magic_matchlet_free (matchlet); + return XDG_MIME_MAGIC_ERROR; + } c = getc_unlocked (magic_file); } @@ -473,32 +473,32 @@ _xdg_mime_magic_parse_magic_line (FILE *magic_file, { /* We clean up the matchlet, byte swapping if needed */ if (matchlet->word_size > 1) - { - size_t i; - if (matchlet->value_length % matchlet->word_size != 0) - { - _xdg_mime_magic_matchlet_free (matchlet); - return XDG_MIME_MAGIC_ERROR; - } - /* FIXME: need to get this defined in a style file */ + { + size_t i; + if (matchlet->value_length % matchlet->word_size != 0) + { + _xdg_mime_magic_matchlet_free (matchlet); + return XDG_MIME_MAGIC_ERROR; + } + /* FIXME: need to get this defined in a style file */ #if LITTLE_ENDIAN - for (i = 0; i < matchlet->value_length; i = i + matchlet->word_size) - { - if (matchlet->word_size == 2) - *((xdg_uint16_t *) matchlet->value + i) = SWAP_BE16_TO_LE16 (*((xdg_uint16_t *) (matchlet->value + i))); - else if (matchlet->word_size == 4) - *((xdg_uint32_t *) matchlet->value + i) = SWAP_BE32_TO_LE32 (*((xdg_uint32_t *) (matchlet->value + i))); - if (matchlet->mask) - { - if (matchlet->word_size == 2) - *((xdg_uint16_t *) matchlet->mask + i) = SWAP_BE16_TO_LE16 (*((xdg_uint16_t *) (matchlet->mask + i))); - else if (matchlet->word_size == 4) - *((xdg_uint32_t *) matchlet->mask + i) = SWAP_BE32_TO_LE32 (*((xdg_uint32_t *) (matchlet->mask + i))); + for (i = 0; i < matchlet->value_length; i = i + matchlet->word_size) + { + if (matchlet->word_size == 2) + *((xdg_uint16_t *) matchlet->value + i) = SWAP_BE16_TO_LE16 (*((xdg_uint16_t *) (matchlet->value + i))); + else if (matchlet->word_size == 4) + *((xdg_uint32_t *) matchlet->value + i) = SWAP_BE32_TO_LE32 (*((xdg_uint32_t *) (matchlet->value + i))); + if (matchlet->mask) + { + if (matchlet->word_size == 2) + *((xdg_uint16_t *) matchlet->mask + i) = SWAP_BE16_TO_LE16 (*((xdg_uint16_t *) (matchlet->mask + i))); + else if (matchlet->word_size == 4) + *((xdg_uint32_t *) matchlet->mask + i) = SWAP_BE32_TO_LE32 (*((xdg_uint32_t *) (matchlet->mask + i))); - } - } + } + } #endif - } + } matchlet->next = match->matchlet; match->matchlet = matchlet; @@ -516,8 +516,8 @@ _xdg_mime_magic_parse_magic_line (FILE *magic_file, static int _xdg_mime_magic_matchlet_compare_to_data (XdgMimeMagicMatchlet *matchlet, - const void *data, - size_t len) + const void *data, + size_t len) { size_t i, j; @@ -526,62 +526,62 @@ _xdg_mime_magic_matchlet_compare_to_data (XdgMimeMagicMatchlet *matchlet, int valid_matchlet = TRUE; if (i + matchlet->value_length > len) - return FALSE; + return FALSE; if (matchlet->mask) - { - for (j = 0; j < matchlet->value_length; j++) - { - if ((matchlet->value[j] & matchlet->mask[j]) != - ((((unsigned char *) data)[j + i]) & matchlet->mask[j])) - { - valid_matchlet = FALSE; - break; - } - } - } + { + for (j = 0; j < matchlet->value_length; j++) + { + if ((matchlet->value[j] & matchlet->mask[j]) != + ((((unsigned char *) data)[j + i]) & matchlet->mask[j])) + { + valid_matchlet = FALSE; + break; + } + } + } else - { - for (j = 0; j < matchlet->value_length; j++) - { - if (matchlet->value[j] != ((unsigned char *) data)[j + i]) - { - valid_matchlet = FALSE; - break; - } - } - } + { + for (j = 0; j < matchlet->value_length; j++) + { + if (matchlet->value[j] != ((unsigned char *) data)[j + i]) + { + valid_matchlet = FALSE; + break; + } + } + } if (valid_matchlet) - return TRUE; + return TRUE; } return FALSE; } static int _xdg_mime_magic_matchlet_compare_level (XdgMimeMagicMatchlet *matchlet, - const void *data, - size_t len, - int indent) + const void *data, + size_t len, + int indent) { while ((matchlet != NULL) && (matchlet->indent == indent)) { if (_xdg_mime_magic_matchlet_compare_to_data (matchlet, data, len)) - { - if ((matchlet->next == NULL) || - (matchlet->next->indent <= indent)) - return TRUE; + { + if ((matchlet->next == NULL) || + (matchlet->next->indent <= indent)) + return TRUE; - if (_xdg_mime_magic_matchlet_compare_level (matchlet->next, - data, - len, - indent + 1)) - return TRUE; - } + if (_xdg_mime_magic_matchlet_compare_level (matchlet->next, + data, + len, + indent + 1)) + return TRUE; + } do - { - matchlet = matchlet->next; - } + { + matchlet = matchlet->next; + } while (matchlet && matchlet->indent > indent); } @@ -590,15 +590,15 @@ _xdg_mime_magic_matchlet_compare_level (XdgMimeMagicMatchlet *matchlet, static int _xdg_mime_magic_match_compare_to_data (XdgMimeMagicMatch *match, - const void *data, - size_t len) + const void *data, + size_t len) { return _xdg_mime_magic_matchlet_compare_level (match->matchlet, data, len, 0); } static void _xdg_mime_magic_insert_match (XdgMimeMagic *mime_magic, - XdgMimeMagicMatch *match) + XdgMimeMagicMatch *match) { XdgMimeMagicMatch *list; @@ -619,11 +619,11 @@ _xdg_mime_magic_insert_match (XdgMimeMagic *mime_magic, while (list->next != NULL) { if (list->next->priority < match->priority) - { - match->next = list->next; - list->next = match; - return; - } + { + match->next = list->next; + list->next = match; + return; + } list = list->next; } list->next = match; @@ -653,17 +653,17 @@ _xdg_mime_magic_get_buffer_extents (XdgMimeMagic *mime_magic) const char * _xdg_mime_magic_lookup_data (XdgMimeMagic *mime_magic, - const void *data, - size_t len) + const void *data, + size_t len) { XdgMimeMagicMatch *match; for (match = mime_magic->match_list; match; match = match->next) { if (_xdg_mime_magic_match_compare_to_data (match, data, len)) - { - return match->mime_type; - } + { + return match->mime_type; + } } return NULL; @@ -680,13 +680,13 @@ _xdg_mime_update_mime_magic_extents (XdgMimeMagic *mime_magic) XdgMimeMagicMatchlet *matchlet; for (matchlet = match->matchlet; matchlet; matchlet = matchlet->next) - { - int extent; + { + int extent; - extent = matchlet->value_length + matchlet->offset + matchlet->range_length; - if (max_extent < extent) - max_extent = extent; - } + extent = matchlet->value_length + matchlet->offset + matchlet->range_length; + if (max_extent < extent) + max_extent = extent; + } } mime_magic->max_extent = max_extent; @@ -719,7 +719,7 @@ _xdg_mime_magic_matchlet_mirror (XdgMimeMagicMatchlet *matchlets) static void _xdg_mime_magic_read_magic_file (XdgMimeMagic *mime_magic, - FILE *magic_file) + FILE *magic_file) { XdgMimeMagicState state; XdgMimeMagicMatch *match = NULL; /* Quiet compiler */ @@ -729,39 +729,39 @@ _xdg_mime_magic_read_magic_file (XdgMimeMagic *mime_magic, while (state != XDG_MIME_MAGIC_EOF) { switch (state) - { - case XDG_MIME_MAGIC_SECTION: - match = _xdg_mime_magic_match_new (); - state = _xdg_mime_magic_parse_header (magic_file, match); - if (state == XDG_MIME_MAGIC_EOF || state == XDG_MIME_MAGIC_ERROR) - _xdg_mime_magic_match_free (match); - break; - case XDG_MIME_MAGIC_MAGIC: - state = _xdg_mime_magic_parse_magic_line (magic_file, match); - if (state == XDG_MIME_MAGIC_SECTION || - (state == XDG_MIME_MAGIC_EOF && match->mime_type)) - { - match->matchlet = _xdg_mime_magic_matchlet_mirror (match->matchlet); - _xdg_mime_magic_insert_match (mime_magic, match); - } - else if (state == XDG_MIME_MAGIC_EOF || state == XDG_MIME_MAGIC_ERROR) - _xdg_mime_magic_match_free (match); - break; - case XDG_MIME_MAGIC_ERROR: - state = _xdg_mime_magic_parse_error (magic_file); - break; - case XDG_MIME_MAGIC_EOF: - default: - /* Make the compiler happy */ - assert (0); - } + { + case XDG_MIME_MAGIC_SECTION: + match = _xdg_mime_magic_match_new (); + state = _xdg_mime_magic_parse_header (magic_file, match); + if (state == XDG_MIME_MAGIC_EOF || state == XDG_MIME_MAGIC_ERROR) + _xdg_mime_magic_match_free (match); + break; + case XDG_MIME_MAGIC_MAGIC: + state = _xdg_mime_magic_parse_magic_line (magic_file, match); + if (state == XDG_MIME_MAGIC_SECTION || + (state == XDG_MIME_MAGIC_EOF && match->mime_type)) + { + match->matchlet = _xdg_mime_magic_matchlet_mirror (match->matchlet); + _xdg_mime_magic_insert_match (mime_magic, match); + } + else if (state == XDG_MIME_MAGIC_EOF || state == XDG_MIME_MAGIC_ERROR) + _xdg_mime_magic_match_free (match); + break; + case XDG_MIME_MAGIC_ERROR: + state = _xdg_mime_magic_parse_error (magic_file); + break; + case XDG_MIME_MAGIC_EOF: + default: + /* Make the compiler happy */ + assert (0); + } } _xdg_mime_update_mime_magic_extents (mime_magic); } void _xdg_mime_magic_read_from_file (XdgMimeMagic *mime_magic, - const char *file_name) + const char *file_name) { FILE *magic_file; char header[12]; diff --git a/xdgmimemagic.h b/xdgmimemagic.h index dea0a3c04..558032681 100644 --- a/xdgmimemagic.h +++ b/xdgmimemagic.h @@ -16,7 +16,7 @@ * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public @@ -44,11 +44,11 @@ typedef struct XdgMimeMagic XdgMimeMagic; XdgMimeMagic *_xdg_mime_magic_new (void); void _xdg_mime_magic_read_from_file (XdgMimeMagic *mime_magic, - const char *file_name); + const char *file_name); void _xdg_mime_magic_free (XdgMimeMagic *mime_magic); int _xdg_mime_magic_get_buffer_extents (XdgMimeMagic *mime_magic); const char *_xdg_mime_magic_lookup_data (XdgMimeMagic *mime_magic, - const void *data, - size_t len); + const void *data, + size_t len); #endif /* __XDG_MIME_MAGIC_H__ */ diff --git a/xdgmimeparent.cpp b/xdgmimeparent.cpp index 1585628a4..3024d9f9d 100644 --- a/xdgmimeparent.cpp +++ b/xdgmimeparent.cpp @@ -16,7 +16,7 @@ * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public @@ -37,12 +37,12 @@ #include #include -#ifndef FALSE -#define FALSE (0) +#ifndef FALSE +#define FALSE (0) #endif -#ifndef TRUE -#define TRUE (!FALSE) +#ifndef TRUE +#define TRUE (!FALSE) #endif typedef struct XdgMimeParents XdgMimeParents; @@ -73,7 +73,7 @@ _xdg_mime_parent_list_new (void) return list; } -void +void _xdg_mime_parent_list_free (XdgParentList *list) { int i; @@ -82,13 +82,13 @@ _xdg_mime_parent_list_free (XdgParentList *list) if (list->parents) { for (i = 0; i < list->n_mimes; i++) - { - for (p = list->parents[i].parents; *p; p++) - free (*p); + { + for (p = list->parents[i].parents; *p; p++) + free (*p); - free (list->parents[i].parents); - free (list->parents[i].mime); - } + free (list->parents[i].parents); + free (list->parents[i].mime); + } free (list->parents); } free (list); @@ -102,7 +102,7 @@ parent_entry_cmp (const void *v1, const void *v2) const char ** _xdg_mime_parent_list_lookup (XdgParentList *list, - const char *mime) + const char *mime) { XdgMimeParents *entry; XdgMimeParents key; @@ -113,7 +113,7 @@ _xdg_mime_parent_list_lookup (XdgParentList *list, key.parents = NULL; entry = (XdgMimeParents *)bsearch (&key, list->parents, list->n_mimes, - sizeof (XdgMimeParents), &parent_entry_cmp); + sizeof (XdgMimeParents), &parent_entry_cmp); if (entry) return (const char **)entry->parents; } @@ -123,7 +123,7 @@ _xdg_mime_parent_list_lookup (XdgParentList *list, void _xdg_mime_parent_read_from_file (XdgParentList *list, - const char *file_name) + const char *file_name) { FILE *file; char line[255]; @@ -144,64 +144,64 @@ _xdg_mime_parent_read_from_file (XdgParentList *list, { char *sep; if (line[0] == '#') - continue; + continue; sep = strchr (line, ' '); if (sep == NULL) - continue; + continue; *(sep++) = '\000'; sep[strlen (sep) -1] = '\000'; entry = NULL; for (i = 0; i < list->n_mimes; i++) - { - if (strcmp (list->parents[i].mime, line) == 0) - { - entry = &(list->parents[i]); - break; - } - } - + { + if (strcmp (list->parents[i].mime, line) == 0) + { + entry = &(list->parents[i]); + break; + } + } + if (!entry) - { - if (list->n_mimes == alloc) - { - alloc <<= 1; - list->parents = (XdgMimeParents *)realloc (list->parents, - alloc * sizeof (XdgMimeParents)); - } - list->parents[list->n_mimes].mime = strdup (line); - list->parents[list->n_mimes].parents = NULL; - entry = &(list->parents[list->n_mimes]); - list->n_mimes++; - } + { + if (list->n_mimes == alloc) + { + alloc <<= 1; + list->parents = (XdgMimeParents *)realloc (list->parents, + alloc * sizeof (XdgMimeParents)); + } + list->parents[list->n_mimes].mime = strdup (line); + list->parents[list->n_mimes].parents = NULL; + entry = &(list->parents[list->n_mimes]); + list->n_mimes++; + } if (!entry->parents) - { - entry->n_parents = 1; - entry->parents = (char **)malloc ((entry->n_parents + 1) * sizeof (char *)); - } + { + entry->n_parents = 1; + entry->parents = (char **)malloc ((entry->n_parents + 1) * sizeof (char *)); + } else - { - entry->n_parents += 1; - entry->parents = (char **)realloc (entry->parents, - (entry->n_parents + 2) * sizeof (char *)); - } + { + entry->n_parents += 1; + entry->parents = (char **)realloc (entry->parents, + (entry->n_parents + 2) * sizeof (char *)); + } entry->parents[entry->n_parents - 1] = strdup (sep); entry->parents[entry->n_parents] = NULL; } - list->parents = (XdgMimeParents *)realloc (list->parents, - list->n_mimes * sizeof (XdgMimeParents)); + list->parents = (XdgMimeParents *)realloc (list->parents, + list->n_mimes * sizeof (XdgMimeParents)); + + fclose (file); - fclose (file); - if (list->n_mimes > 1) - qsort (list->parents, list->n_mimes, + qsort (list->parents, list->n_mimes, sizeof (XdgMimeParents), &parent_entry_cmp); } -void +void _xdg_mime_parent_list_dump (XdgParentList *list) { int i; @@ -210,10 +210,10 @@ _xdg_mime_parent_list_dump (XdgParentList *list) if (list->parents) { for (i = 0; i < list->n_mimes; i++) - { - for (p = list->parents[i].parents; *p; p++) - printf ("%s %s\n", list->parents[i].mime, *p); - } + { + for (p = list->parents[i].parents; *p; p++) + printf ("%s %s\n", list->parents[i].mime, *p); + } } } diff --git a/xdgmimeparent.h b/xdgmimeparent.h index da29452cb..a10846b39 100644 --- a/xdgmimeparent.h +++ b/xdgmimeparent.h @@ -16,7 +16,7 @@ * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public @@ -40,11 +40,11 @@ typedef struct XdgParentList XdgParentList; #endif void _xdg_mime_parent_read_from_file (XdgParentList *list, - const char *file_name); + const char *file_name); XdgParentList *_xdg_mime_parent_list_new (void); void _xdg_mime_parent_list_free (XdgParentList *list); const char **_xdg_mime_parent_list_lookup (XdgParentList *list, - const char *mime); + const char *mime); void _xdg_mime_parent_list_dump (XdgParentList *list); #endif /* __XDG_MIME_PARENT_H__ */