From 9992b8eb0e3366ff8a3948aa0b66a19c3c12c737 Mon Sep 17 00:00:00 2001 From: ridiculousfish Date: Sun, 18 Nov 2012 16:30:30 -0800 Subject: [PATCH] Apply new indentation, brace, and whitespace style --- autoload.cpp | 151 +- autoload.h | 38 +- builtin.cpp | 5907 +++++++++++++++++++------------------- builtin.h | 16 +- builtin_commandline.cpp | 892 +++--- builtin_complete.cpp | 1052 +++---- builtin_jobs.cpp | 456 +-- builtin_set.cpp | 1133 ++++---- builtin_test.cpp | 1291 +++++---- builtin_ulimit.cpp | 698 ++--- color.cpp | 243 +- color.h | 77 +- common.cpp | 2661 ++++++++--------- common.h | 264 +- complete.cpp | 2141 +++++++------- complete.h | 105 +- env.cpp | 1568 +++++----- env.h | 41 +- env_universal.cpp | 562 ++-- env_universal.h | 18 +- env_universal_common.cpp | 1084 +++---- env_universal_common.h | 120 +- event.cpp | 615 ++-- event.h | 67 +- exec.cpp | 2025 ++++++------- exec.h | 14 +- expand.cpp | 1510 +++++----- expand.h | 67 +- fallback.cpp | 1662 +++++------ fallback.h | 132 +- fish.cpp | 632 ++-- fish_indent.cpp | 503 ++-- fish_pager.cpp | 1914 ++++++------ fish_tests.cpp | 931 +++--- fishd.cpp | 1074 +++---- function.cpp | 155 +- function.h | 93 +- highlight.cpp | 1883 ++++++------ highlight.h | 9 +- history.cpp | 636 ++-- history.h | 86 +- input.cpp | 974 +++---- input.h | 88 +- input_common.cpp | 313 +- input_common.h | 22 +- intern.cpp | 36 +- intern.h | 4 +- io.cpp | 193 +- io.h | 84 +- iothread.cpp | 217 +- iothread.h | 3 +- key_reader.cpp | 106 +- kill.cpp | 160 +- kill.h | 4 +- lru.h | 101 +- mimedb.cpp | 1926 ++++++------- output.cpp | 692 ++--- output.h | 44 +- parse_util.cpp | 1248 ++++---- parse_util.h | 60 +- parser.cpp | 5051 ++++++++++++++++---------------- parser.h | 290 +- parser_keywords.cpp | 81 +- parser_keywords.h | 16 +- path.cpp | 398 +-- path.h | 2 +- postfork.cpp | 618 ++-- postfork.h | 6 +- print_help.cpp | 18 +- print_help.h | 2 +- proc.cpp | 1684 +++++------ proc.h | 281 +- reader.cpp | 4135 +++++++++++++------------- reader.h | 44 +- sanity.cpp | 52 +- sanity.h | 2 +- screen.cpp | 1106 +++---- screen.h | 73 +- set_color.cpp | 415 +-- signal.cpp | 916 +++--- signal.h | 8 +- tokenizer.cpp | 987 +++---- tokenizer.h | 106 +- util.cpp | 130 +- util.h | 12 +- wgetopt.cpp | 710 ++--- wgetopt.h | 220 +- wildcard.cpp | 1416 ++++----- wildcard.h | 30 +- wutil.cpp | 271 +- wutil.h | 16 +- xdgmime.cpp | 766 ++--- xdgmime.h | 46 +- xdgmimealias.cpp | 160 +- xdgmimealias.h | 14 +- xdgmimeglob.cpp | 518 ++-- xdgmimeglob.h | 28 +- xdgmimeint.cpp | 155 +- xdgmimeint.h | 8 +- xdgmimemagic.cpp | 1019 +++---- xdgmimemagic.h | 16 +- xdgmimeparent.cpp | 222 +- xdgmimeparent.h | 14 +- 103 files changed, 32091 insertions(+), 30772 deletions(-) diff --git a/autoload.cpp b/autoload.cpp index d57e69d9b..01b36c41f 100644 --- a/autoload.cpp +++ b/autoload.cpp @@ -17,17 +17,24 @@ The classes responsible for autoloading functions and completions. /* The time before we'll recheck an autoloaded file */ static const int kAutoloadStalenessInterval = 15; -file_access_attempt_t access_file(const wcstring &path, int mode) { +file_access_attempt_t access_file(const wcstring &path, int mode) +{ //printf("Touch %ls\n", path.c_str()); file_access_attempt_t result = {0}; struct stat statbuf; - if (wstat(path, &statbuf)) { + if (wstat(path, &statbuf)) + { result.error = errno; - } else { + } + else + { result.mod_time = statbuf.st_mtime; - if (waccess(path, mode)) { + if (waccess(path, mode)) + { result.error = errno; - } else { + } + else + { result.accessible = true; } } @@ -39,21 +46,23 @@ file_access_attempt_t access_file(const wcstring &path, int mode) { } autoload_t::autoload_t(const wcstring &env_var_name_var, const builtin_script_t * const scripts, size_t script_count) : - lock(), - env_var_name(env_var_name_var), - builtin_scripts(scripts), - builtin_script_count(script_count), - last_path(), - is_loading_set() + lock(), + env_var_name(env_var_name_var), + builtin_scripts(scripts), + builtin_script_count(script_count), + last_path(), + is_loading_set() { pthread_mutex_init(&lock, NULL); } -autoload_t::~autoload_t() { +autoload_t::~autoload_t() +{ pthread_mutex_destroy(&lock); } -void autoload_t::node_was_evicted(autoload_function_t *node) { +void autoload_t::node_was_evicted(autoload_function_t *node) +{ // This should only ever happen on the main thread ASSERT_IS_MAIN_THREAD(); @@ -63,27 +72,27 @@ void autoload_t::node_was_evicted(autoload_function_t *node) { delete node; } -int autoload_t::unload( const wcstring &cmd ) +int autoload_t::unload(const wcstring &cmd) { return this->evict_node(cmd); } -int autoload_t::load( const wcstring &cmd, bool reload ) +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() ) + 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 ) + if (path_var != this->last_path) { this->last_path = path_var; scoped_lock locker(lock); @@ -93,10 +102,10 @@ int autoload_t::load( const wcstring &cmd, bool reload ) /** 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."), - cmd.c_str() ); + 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; } @@ -105,27 +114,27 @@ int autoload_t::load( const wcstring &cmd, bool reload ) /* 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 ) +bool autoload_t::can_load(const wcstring &cmd, const env_vars_snapshot_t &vars) { const env_var_t path_var = vars.get(env_var_name); if (path_var.missing_or_empty()) return false; std::vector path_list; - tokenize_variable_array( path_var, path_list ); - return this->locate_file_and_maybe_load_it( cmd, false, false, path_list ); + tokenize_variable_array(path_var, path_list); + return this->locate_file_and_maybe_load_it(cmd, false, false, path_list); } static bool script_name_precedes_script_name(const builtin_script_t &script1, const builtin_script_t &script2) @@ -133,20 +142,22 @@ static bool script_name_precedes_script_name(const builtin_script_t &script1, co return wcscmp(script1.name, script2.name) < 0; } -void autoload_t::unload_all(void) { +void autoload_t::unload_all(void) +{ scoped_lock locker(lock); this->evict_all_nodes(); } /** Check whether the given command is loaded. */ -bool autoload_t::has_tried_loading( const wcstring &cmd ) +bool autoload_t::has_tried_loading(const wcstring &cmd) { scoped_lock locker(lock); autoload_function_t * func = this->get_node(cmd); return func != NULL; } -static bool is_stale(const autoload_function_t *func) { +static bool is_stale(const autoload_function_t *func) +{ /** Return whether this function is stale. Internalized functions can never be stale. */ return ! func->is_internalized && time(NULL) - func->access.last_checked > kAutoloadStalenessInterval; } @@ -155,11 +166,15 @@ autoload_function_t *autoload_t::get_autoloaded_function_with_creation(const wcs { ASSERT_IS_LOCKED(lock); autoload_function_t *func = this->get_node(cmd); - if (! func) { + if (! func) + { func = new autoload_function_t(cmd); - if (allow_eviction) { + if (allow_eviction) + { this->add_node(func); - } else { + } + else + { this->add_node_without_eviction(func); } } @@ -178,11 +193,11 @@ autoload_function_t *autoload_t::get_autoloaded_function_with_creation(const wcs Result: if really_load is true, returns whether the function was loaded. Otherwise returns whether the function existed. */ -bool autoload_t::locate_file_and_maybe_load_it( const wcstring &cmd, bool really_load, bool reload, const wcstring_list_t &path_list ) +bool autoload_t::locate_file_and_maybe_load_it(const wcstring &cmd, bool really_load, bool reload, const wcstring_list_t &path_list) { /* Note that we are NOT locked in this function! */ - 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. */ { @@ -196,22 +211,30 @@ bool autoload_t::locate_file_and_maybe_load_it( const wcstring &cmd, bool really /* Determine if we can use this cached function */ bool use_cached; - if (! func) { + if (! func) + { /* Can't use a function that doesn't exist */ use_cached = false; - } else if (really_load && ! func->is_placeholder && ! func->is_loaded) { + } + else if (really_load && ! func->is_placeholder && ! func->is_loaded) + { /* Can't use an unloaded function */ use_cached = false; - } else if ( ! allow_stale_functions && is_stale(func)) { + } + else if (! allow_stale_functions && is_stale(func)) + { /* Can't use a stale function */ use_cached = false; - } else { + } + else + { /* 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) { + if (use_cached) + { return func->is_internalized || func->access.accessible; } } @@ -235,7 +258,8 @@ bool autoload_t::locate_file_and_maybe_load_it( const wcstring &cmd, bool really matching_builtin_script = found; } } - if (matching_builtin_script) { + if (matching_builtin_script) + { has_script_source = true; script_source = str2wcstring(matching_builtin_script->def); @@ -253,13 +277,14 @@ bool autoload_t::locate_file_and_maybe_load_it( const wcstring &cmd, bool really if (! has_script_source) { /* Iterate over path searching for suitable completion files */ - for( i=0; iaccess.mod_time != access.mod_time || ! func->is_loaded); - if (need_to_load_function) { + if (need_to_load_function) + { /* Generate the script source */ wcstring esc = escape_string(path, 1); @@ -277,7 +303,8 @@ bool autoload_t::locate_file_and_maybe_load_it( const wcstring &cmd, bool really 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) { + if (func && func->is_loaded) + { command_removed(cmd); func->is_placeholder = false; } @@ -287,7 +314,8 @@ bool autoload_t::locate_file_and_maybe_load_it( const wcstring &cmd, bool really } /* Create the function if we haven't yet. This does not load it. Do not trigger eviction unless we are actually loading, because we don't want to evict off of the main thread. */ - if (! func) { + if (! func) + { func = get_autoloaded_function_with_creation(cmd, really_load); } @@ -306,17 +334,21 @@ bool autoload_t::locate_file_and_maybe_load_it( const wcstring &cmd, bool really Later we only research if the current time is at least five seconds later. This way, the files won't be searched over and over again. */ - if( ! found_file && ! has_script_source ) + if (! found_file && ! has_script_source) { scoped_lock locker(lock); /* Generate a placeholder */ autoload_function_t *func = this->get_node(cmd); - if (! func) { + if (! func) + { func = new autoload_function_t(cmd); func->is_placeholder = true; - if (really_load) { + if (really_load) + { this->add_node(func); - } else { + } + else + { this->add_node_without_eviction(func); } } @@ -327,7 +359,7 @@ bool autoload_t::locate_file_and_maybe_load_it( const wcstring &cmd, bool really /* If we have a script, either built-in or a file source, then run it */ if (really_load && has_script_source) { - if( exec_subshell( script_source) == -1 ) + if (exec_subshell(script_source) == -1) { /* Do nothing on failiure @@ -336,9 +368,12 @@ bool autoload_t::locate_file_and_maybe_load_it( const wcstring &cmd, bool really } - if (really_load) { + if (really_load) + { return reloaded; - } else { + } + else + { return found_file || has_script_source; } } diff --git a/autoload.h b/autoload.h index 3635d5f20..b58b98bb5 100644 --- a/autoload.h +++ b/autoload.h @@ -14,7 +14,8 @@ #include "lru.h" /** A struct responsible for recording an attempt to access a file. */ -struct file_access_attempt_t { +struct file_access_attempt_t +{ time_t mod_time; /** The modification time of the file */ time_t last_checked; /** When we last checked the file */ bool accessible; /** Whether we believe we could access this file */ @@ -40,7 +41,8 @@ class env_vars_snapshot_t; /** A class that represents a path from which we can autoload, and the autoloaded contents. */ -class autoload_t : private lru_cache_t { +class autoload_t : private lru_cache_t +{ private: /** Lock for thread safety */ @@ -58,34 +60,36 @@ private: /** 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 { + bool is_loading(const wcstring &name) const + { return is_loading_set.find(name) != is_loading_set.end(); } - void remove_all_functions(void) { + 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 ); + 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: +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 ); + autoload_t(const wcstring &env_var_name_var, const builtin_script_t *scripts, size_t script_count); /** Destructor */ virtual ~autoload_t(); @@ -101,10 +105,10 @@ private: \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 ); + 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 ); + bool has_tried_loading(const wcstring &cmd); /** Tell the autoloader that the specified file, in the specified path, @@ -114,15 +118,15 @@ private: \param on_unload a callback function which will be called before (re)loading a file, may be used to unload the previous file. \return non-zero if the file was removed, zero if the file had not yet been loaded */ - int unload( const wcstring &cmd ); + int unload(const wcstring &cmd); /** Unloads all files. */ - void unload_all( ); + 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 ); + bool can_load(const wcstring &cmd, const env_vars_snapshot_t &vars); }; diff --git a/builtin.cpp b/builtin.cpp index 7e7a87dc4..4ca4e30f9 100644 --- a/builtin.cpp +++ b/builtin.cpp @@ -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; }; @@ -119,17 +119,20 @@ int builtin_err_redirect; /* Buffers for storing the output of builtin functions */ wcstring stdout_buffer, stderr_buffer; -const wcstring &get_stdout_buffer() { +const wcstring &get_stdout_buffer() +{ ASSERT_IS_MAIN_THREAD(); return stdout_buffer; } -const wcstring &get_stderr_buffer() { +const wcstring &get_stderr_buffer() +{ ASSERT_IS_MAIN_THREAD(); return stderr_buffer; } -void builtin_show_error(const wcstring &err) { +void builtin_show_error(const wcstring &err) +{ ASSERT_IS_MAIN_THREAD(); stderr_buffer.append(err); } @@ -137,7 +140,8 @@ void builtin_show_error(const wcstring &err) { /** Stack containing builtin I/O for recursive builtin calls. */ -struct io_stack_elem_t { +struct io_stack_elem_t +{ int in; wcstring out; wcstring err; @@ -160,67 +164,67 @@ static const io_chain_t *real_io; /** Counts the number of non null pointers in the specified array */ -static int builtin_count_args( wchar_t **argv ) +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. */ -static void builtin_wperror( const wchar_t *s) +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); + } } /** Count the number of times the specified character occurs in the specified string */ -static int count_char( const wchar_t *str, wchar_t c ) +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 ) +wcstring builtin_help_get(parser_t &parser, const wchar_t *name) { wcstring_list_t lst; 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); + } } /** Perform error reporting for encounter with unknown option */ -static void builtin_unknown_option( parser_t &parser, const wchar_t *cmd, const wchar_t *opt ) +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); } /** Perform error reporting for encounter with missing argument */ -static void builtin_missing_argument( parser_t &parser, const wchar_t *cmd, const wchar_t *opt ) +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); } /* @@ -389,36 +393,36 @@ static void builtin_missing_argument( parser_t &parser, const wchar_t *cmd, cons #include "builtin_jobs.cpp" /* builtin_test lives in builtin_test.cpp */ -int builtin_test( parser_t &parser, wchar_t **argv ); +int builtin_test(parser_t &parser, wchar_t **argv); /** List all current key bindings */ 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; } @@ -867,154 +871,154 @@ static int builtin_block( parser_t &parser, wchar_t **argv ) additional operational modes, such as printing a list of all builtins, printing help, etc. */ -static int builtin_builtin( 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; - append_format(stderr_buffer, - BUILTIN_ERR_UNKNOWN, - argv[0], - long_options[opt_index].name ); - builtin_print_help( parser, argv[0], stderr_buffer ); + 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); - 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'-'); - if ( ! defer_function_name ){ + if (! defer_function_name) + { 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() ); - } - } + } + + + 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()); + } + } /* Output the function name if we deferred it */ - if ( defer_function_name ){ + 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_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)) { + if (! string_suffixes_string(L"\n", def)) + { out.push_back(L'\n'); } out.append(L"end\n"); @@ -1194,273 +1201,273 @@ static void functions_def( const wcstring &name, wcstring &out ) /** The functions builtin, used for listing and erasing functions. */ -static int builtin_functions( parser_t &parser, wchar_t **argv ) +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; - append_format(stderr_buffer, - BUILTIN_ERR_UNKNOWN, - argv[0], - long_options[opt_index].name ); - builtin_print_help( parser, argv[0], stderr_buffer ); + 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); - 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) { @@ -1553,33 +1582,44 @@ static bool builtin_echo_parse_numeric_sequence(const wchar_t *str, size_t *cons We also support a new option -s to mean "no spaces" */ -static int builtin_echo( parser_t &parser, wchar_t **argv ) +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) { - if (! wcscmp(*argv, L"-n")) { + while (*argv) + { + if (! wcscmp(*argv, L"-n")) + { print_newline = false; - } else if (! wcscmp(*argv, L"-s")) { + } + else if (! wcscmp(*argv, L"-s")) + { print_spaces = false; - } else if (! wcscmp(*argv, L"-e")) { + } + else if (! wcscmp(*argv, L"-e")) + { interpret_special_chars = true; - } else if (! wcscmp(*argv, L"-E")) { + } + else if (! wcscmp(*argv, L"-E")) + { interpret_special_chars = false; - } else { + } + else + { break; } 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++) { + + for (size_t idx = 0; continue_output && argv[idx] != NULL; idx++) + { if (print_spaces && idx > 0) stdout_buffer.push_back(' '); @@ -1599,40 +1639,61 @@ static int builtin_echo( parser_t &parser, wchar_t **argv ) size_t consumed = 1; switch (str[j+1]) { - case L'a': wc = L'\a'; break; - case L'b': wc = L'\b'; break; - case L'e': wc = L'\e'; break; - case L'f': wc = L'\f'; break; - case L'n': wc = L'\n'; break; - case L'r': wc = L'\r'; break; - 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: + case L'a': + wc = L'\a'; + break; + case L'b': + wc = L'\b'; + break; + case L'e': + wc = L'\e'; + break; + case L'f': + wc = L'\f'; + break; + case L'n': + wc = L'\n'; + break; + case L'r': + wc = L'\r'; + break; + 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 */ + unsigned char narrow_val = 0; + if (builtin_echo_parse_numeric_sequence(str + j + 1, &consumed, &narrow_val)) { - /* Octal and hex escape sequences */ - unsigned char narrow_val = 0; - if (builtin_echo_parse_numeric_sequence(str + j + 1, &consumed, &narrow_val)) - { - /* Here consumed must have been set to something */ - wc = narrow_val; //is this OK for conversion? - } - else - { - /* Not a recognized escape. We consume only the backslash. */ - wc = L'\\'; - consumed = 0; - } - break; + /* Here consumed must have been set to something */ + wc = narrow_val; //is this OK for conversion? } + else + { + /* Not a recognized escape. We consume only the backslash. */ + wc = L'\\'; + consumed = 0; + } + 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); } @@ -1644,13 +1705,16 @@ 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 ) +static int builtin_pwd(parser_t &parser, wchar_t **argv) { - wchar_t dir_path[4096]; - wchar_t *res = wgetcwd( dir_path, 4096 ); - if (res == NULL) { + wchar_t dir_path[4096]; + wchar_t *res = wgetcwd(dir_path, 4096); + if (res == NULL) + { return STATUS_BUILTIN_ERROR; - } else { + } + else + { stdout_buffer.append(dir_path); stdout_buffer.push_back(L'\n'); return STATUS_BUILTIN_OK; @@ -1661,1070 +1725,1070 @@ static int builtin_pwd( parser_t &parser, wchar_t **argv ) The function builtin, used for providing subroutines. It calls various functions from function.c to perform any heavy lifting. */ -static int builtin_function( 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; + + case 'd': + desc=woptarg; + break; + + case 's': + { + int sig = wcs2sig(woptarg); + + if (sig < 0) + { append_format(stderr_buffer, - BUILTIN_ERR_UNKNOWN, - argv[0], - long_options[opt_index].name ); + _(L"%ls: Unknown signal '%ls'\n"), + argv[0], + woptarg); + res=1; + break; + } + events.push_back(event_t::signal_event(sig)); + break; + } - res = 1; - 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 'd': - desc=woptarg; - break; - - 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; - } - events.push_back(event_t::signal_event(sig)); - 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; - } + events.push_back(event_t::variable_event(woptarg)); + break; + } - case 'e': - { - events.push_back(event_t::generic_event(woptarg)); - break; - } + case 'e': + { + events.push_back(event_t::generic_event(woptarg)); + break; + } - 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; + case 'j': + case 'p': + { + pid_t pid; + wchar_t *end; + event_t e(EVENT_ANY); - 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 ) - { - /* nothing */ - } - else - { - events.push_back(e); - } - break; - } + e.type = EVENT_EXIT; + e.param1.pid = (opt=='j'?-1:1)*abs(pid); + } + if (res) + { + /* nothing */ + } + else + { + events.push_back(e); + } + 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; } /** The random builtin. For generating random numbers. */ -static int builtin_random( 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; - append_format(stderr_buffer, - BUILTIN_ERR_UNKNOWN, - argv[0], - long_options[opt_index].name ); - builtin_print_help( parser, argv[0], stderr_buffer ); + int opt = wgetopt_long(argc, + argv, + L"h", + long_options, + &opt_index); + if (opt == -1) + break; - return STATUS_BUILTIN_ERROR; + 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); - case 'h': - builtin_print_help( parser, argv[0], stdout_buffer ); - break; + return STATUS_BUILTIN_ERROR; - case '?': - builtin_unknown_option( parser, argv[0], argv[woptind-1] ); - return STATUS_BUILTIN_ERROR; + 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; - } + } - 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; - } + switch (argc-woptind) + { - case 1: - { - long foo; - wchar_t *end=0; + case 0: + { + long 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] ); + if (!seeded) + { + seeded=1; + srand48_r(time(0), &seed_buffer); + } + lrand48_r(&seed_buffer, &res); - return STATUS_BUILTIN_ERROR; - } - seeded=1; - srand48_r( foo, &seed_buffer); - break; - } + append_format(stdout_buffer, L"%ld\n", labs(res%32767)); + 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; + case 1: + { + long foo; + wchar_t *end=0; + + 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; } /** The read builtin. Reads from stdin and stores the values in environment variables. */ -static int builtin_read( 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; - append_format(stderr_buffer, - BUILTIN_ERR_UNKNOWN, - argv[0], - long_options[opt_index].name ); - builtin_print_help( parser, argv[0], stderr_buffer ); + int opt_index = 0; - return STATUS_BUILTIN_ERROR; + int opt = wgetopt_long(argc, + argv, + L"xglUup:c:hm:s", + long_options, + &opt_index); + if (opt == -1) + break; - case L'x': - place |= ENV_EXPORT; - 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); - case L'g': - place |= ENV_GLOBAL; - break; + return STATUS_BUILTIN_ERROR; - case L'l': - place |= ENV_LOCAL; - break; + case L'x': + place |= ENV_EXPORT; + break; - case L'U': - place |= ENV_UNIVERSAL; - break; + case L'g': + place |= ENV_GLOBAL; + break; - case L'u': - place |= ENV_UNEXPORT; - break; + case L'l': + place |= ENV_LOCAL; + break; - case L'p': - prompt = woptarg; - break; + case L'U': + place |= ENV_UNIVERSAL; + break; - case L'c': - commandline = woptarg; - break; + case L'u': + place |= ENV_UNEXPORT; + break; - case L'm': - mode_name = woptarg; - break; + case L'p': + prompt = woptarg; + break; - case 's': - shell = 1; - break; - - case 'h': - builtin_print_help( parser, argv[0], stdout_buffer ); - return STATUS_BUILTIN_OK; + case L'c': + commandline = woptarg; + break; - case L'?': - builtin_unknown_option( parser, argv[0], argv[woptind-1] ); - return STATUS_BUILTIN_ERROR; - } + case L'm': + mode_name = woptarg; + break; - } + case 's': + shell = 1; + break; - if( ( place & ENV_UNEXPORT ) && ( place & ENV_EXPORT ) ) - { - append_format(stderr_buffer, - BUILTIN_ERR_EXPUNEXP, - argv[0] ); + 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]); - 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; + int opt = wgetopt_long(argc, + argv, + L"+hi", + long_options, + &opt_index); + if (opt == -1) + break; + + switch (opt) + { + case 0: + assert(opt_index >= 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; - 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; } /** @@ -3050,7 +3115,7 @@ static int builtin_source( parser_t &parser, wchar_t ** argv ) around in the list makes the list reflect the order in which the jobs were used. */ -static void make_first( job_t *j ) +static void make_first(job_t *j) { job_promote(j); } @@ -3059,329 +3124,329 @@ static void make_first( job_t *j ) /** Builtin for putting a job in the foreground */ -static int builtin_fg( parser_t &parser, wchar_t **argv ) +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; } /** Helper function for builtin_bg() */ -static int send_to_bg( parser_t &parser, job_t *j, const wchar_t *name ) +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; } /** Builtin for putting a job in the background */ -static int builtin_bg( parser_t &parser, wchar_t **argv ) +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; } /** Builtin for looping over a list */ -static int builtin_for( 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; } /** The begin builtin. Creates a nex block. */ -static int builtin_begin( 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,137 +3455,137 @@ static int builtin_begin( parser_t &parser, wchar_t **argv ) The end command is whare a lot of the block-level magic happens. */ -static int builtin_end( 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); - while_block_t *blk = static_cast(parser.current_block); - blk->status = WHILE_TEST_AGAIN; - } + 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 SWITCH: - case FAKE: - /* - Nothing special happens at the end of these commands. The scope just ends. - */ + case IF: + case SUBST: + case BEGIN: + case SWITCH: + case FAKE: + /* + 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. - */ - for_block_t *fb = static_cast(parser.current_block); - wcstring_list_t &for_vars = fb->sequence; - if( parser.current_block->loop_status == LOOP_BREAK ) - { - for_vars.clear(); - } + 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) + { + for_vars.clear(); + } - 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; - } + 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; - 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); - } - else - { - /** - Copy the text from the beginning of the function - until the end command and use as the new definition - for the specified function - */ + kill_block = 0; + parser.set_pos(parser.current_block->tok_pos); + } + break; + } - 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 ); - free( def ); - } - } - break; - - default: - assert(false); //should never get here - break; + case FUNCTION_DEF: + { + function_def_block_t *fdb = static_cast(parser.current_block); + function_data_t &d = fdb->function_data; - } - if( kill_block ) - { - parser.pop_block(); - } + if (d.name.empty()) + { + /* Disallow empty function names */ + append_format(stderr_buffer, _(L"%ls: No function name given\n"), argv[0]); - /* - If everything goes ok, return status of last command to execute. - */ - return proc_get_last_status(); - } + /* 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); + } + else + { + /** + Copy the text from the beginning of the function + until the end command and use as the new definition + for the specified function + */ + + 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); + free(def); + } + } + break; + + default: + assert(false); //should never get here + break; + + } + if (kill_block) + { + parser.pop_block(); + } + + /* + If everything goes ok, return status of last command to execute. + */ + return proc_get_last_status(); + } } /** Builtin for executing commands if an if statement is false */ -static int builtin_else( parser_t &parser, wchar_t **argv ) +static int builtin_else(parser_t &parser, wchar_t **argv) { bool block_ok = false; if_block_t *if_block = NULL; @@ -3533,80 +3598,80 @@ 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(); } /** This function handles both the 'continue' and the 'break' builtins that are used for loop control. */ -static int builtin_break_continue( 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,234 +3679,234 @@ static int builtin_break_continue( parser_t &parser, wchar_t **argv ) interactive debugger. */ -static int builtin_breakpoint( 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(); } /** Function for handling the \c return builtin */ -static int builtin_return( 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; } /** Builtin for executing one of several blocks of commands depending on the value of an argument. */ -static int builtin_switch( 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; } /** Builtin used together with the switch builtin for conditional execution */ -static int builtin_case( 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(); } /** History of commands executed by user */ -static int builtin_history( parser_t &parser, wchar_t **argv ) +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; bool clear_history = false; static const struct woption long_options[] = - { - { L"prefix", no_argument, 0, 'p' }, - { L"delete", no_argument, 0, 'd' }, - { L"search", no_argument, 0, 's' }, - { L"contains", no_argument, 0, 'c' }, - { L"save", no_argument, 0, 'v' }, - { L"clear", no_argument, 0, 'l' }, - { L"help", no_argument, 0, 'h' }, - { 0, 0, 0, 0 } - }; + { + { L"prefix", no_argument, 0, 'p' }, + { L"delete", no_argument, 0, 'd' }, + { L"search", no_argument, 0, 's' }, + { L"contains", no_argument, 0, 'c' }, + { L"save", no_argument, 0, 'v' }, + { L"clear", no_argument, 0, 'l' }, + { L"help", no_argument, 0, 'h' }, + { 0, 0, 0, 0 } + }; int opt = 0; 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) + + while ((opt = wgetopt_long_only(argc, argv, L"pdscvl", long_options, &opt_index)) != -1) { - switch(opt) + switch (opt) { - case 'p': - search_prefix = true; - break; - case 'd': - delete_item = true; - break; - case 's': - search_history = true; - break; - case 'c': - break; - case 'v': - save_history = true; - break; - case 'l': - clear_history = true; - break; - case 'h': - builtin_print_help( parser, argv[0], stdout_buffer ); - return STATUS_BUILTIN_OK; - break; - case EOF: - /* Remainder are arguments */ - break; - case '?': - append_format(stderr_buffer, BUILTIN_ERR_UNKNOWN, argv[0], argv[woptind-1]); - return STATUS_BUILTIN_ERROR; - break; - default: - append_format(stderr_buffer, BUILTIN_ERR_UNKNOWN, argv[0], argv[woptind-1]); - return STATUS_BUILTIN_ERROR; + case 'p': + search_prefix = true; + break; + case 'd': + delete_item = true; + break; + case 's': + search_history = true; + break; + case 'c': + break; + case 'v': + save_history = true; + break; + case 'l': + clear_history = true; + break; + case 'h': + builtin_print_help(parser, argv[0], stdout_buffer); + return STATUS_BUILTIN_OK; + break; + case EOF: + /* Remainder are arguments */ + break; + case '?': + append_format(stderr_buffer, BUILTIN_ERR_UNKNOWN, argv[0], argv[woptind-1]); + return STATUS_BUILTIN_ERROR; + break; + default: + 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); @@ -3871,7 +3936,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; } } @@ -3885,7 +3950,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; @@ -3921,126 +3986,130 @@ 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) -static const builtin_data_t *builtin_lookup(const wcstring &name) { +static const builtin_data_t *builtin_lookup(const wcstring &name) +{ const builtin_data_t *array_end = builtin_datas + BUILTIN_COUNT; const builtin_data_t *found = std::lower_bound(builtin_datas, array_end, name); - if (found != array_end && name == found->name) { + if (found != array_end && name == found->name) + { return found; - } else { + } + else + { return NULL; } } 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() { } -int builtin_exists( const wcstring &cmd ) +int builtin_exists(const wcstring &cmd) { - return !!builtin_lookup(cmd); + return !!builtin_lookup(cmd); } /** Return true if the specified builtin should handle it's own help, false otherwise. */ -static int internal_help( const wchar_t *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 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; } @@ -4048,37 +4117,41 @@ wcstring_list_t builtin_get_names(void) { wcstring_list_t result; result.reserve(BUILTIN_COUNT); - for (size_t i=0; i < BUILTIN_COUNT; i++) { + for (size_t i=0; i < BUILTIN_COUNT; i++) + { result.push_back(builtin_datas[i].name); } return result; } -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)); - } +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)); + } } -wcstring builtin_get_desc( const wcstring &name ) +wcstring builtin_get_desc(const wcstring &name) { wcstring result; - const builtin_data_t *builtin = builtin_lookup(name); - if (builtin) { + const builtin_data_t *builtin = builtin_lookup(name); + if (builtin) + { result = _(builtin->desc); } return result; } -void builtin_push_io( parser_t &parser, int in ) +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(); } @@ -4086,20 +4159,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 cb6483382..ae06bba26 100644 --- a/builtin.h +++ b/builtin.h @@ -15,9 +15,9 @@ class parser_t; enum { - COMMAND_NOT_BUILTIN, - BUILTIN_REGULAR, - BUILTIN_FUNCTION + COMMAND_NOT_BUILTIN, + BUILTIN_REGULAR, + BUILTIN_FUNCTION } ; @@ -125,7 +125,7 @@ void builtin_destroy(); /** Is there a builtin command with the given name? */ -int builtin_exists( const wcstring &cmd ); +int builtin_exists(const wcstring &cmd); /** Execute a builtin command @@ -139,7 +139,7 @@ int builtin_exists( const wcstring &cmd ); \return the exit status of the builtin command */ -int builtin_run( parser_t &parser, const wchar_t * const *argv, const io_chain_t &io ); +int builtin_run(parser_t &parser, const wchar_t * const *argv, const io_chain_t &io); /** Returns a list of all builtin names */ wcstring_list_t builtin_get_names(void); @@ -150,7 +150,7 @@ void builtin_get_names(std::vector &list); /** Pushes a new set of input/output to the stack. The new stdin is supplied, a new set of output strings is created. */ -void builtin_push_io( parser_t &parser, int stdin_fd ); +void builtin_push_io(parser_t &parser, int stdin_fd); /** Pops a set of input/output from the stack. The output strings are destroued, but the input file is not closed. @@ -161,7 +161,7 @@ void builtin_pop_io(parser_t &parser); /** Return a one-line description of the specified builtin. */ -wcstring builtin_get_desc( const wcstring &b ); +wcstring builtin_get_desc(const wcstring &b); /** @@ -177,6 +177,6 @@ const wchar_t *builtin_complete_get_temporary_buffer(); for the specified command. */ -wcstring builtin_help_get( parser_t &parser, const wchar_t *cmd ); +wcstring builtin_help_get(parser_t &parser, const wchar_t *cmd); #endif diff --git a/builtin_commandline.cpp b/builtin_commandline.cpp index 212893848..eabffe236 100644 --- a/builtin_commandline.cpp +++ b/builtin_commandline.cpp @@ -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; } @@ -88,46 +88,46 @@ static size_t get_cursor_pos() \param insert the string to insert \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 ) +static void replace_part(const wchar_t *begin, + 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) - { + switch (append_mode) + { case REPLACE_MODE: { - out.append(insert); - out_pos = wcslen( insert ) + (begin-buff); - break; + out.append(insert); + out_pos = wcslen(insert) + (begin-buff); + break; } case APPEND_MODE: { - out.append( begin, end-begin ); - out.append( insert ); - break; + 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; + 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 ); + } + out.append(end); + reader_set_buffer(out, out_pos); } /** @@ -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 ) +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; + 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 (tokenize) { - if( (cut_at_cursor) && - (tok_get_pos( &tok)+wcslen(tok_last( &tok)) >= pos) ) - break; + buff = wcsndup(begin, end-begin); +// fwprintf( stderr, L"Subshell: %ls, end char %lc\n", buff, *end ); + out.clear(); - switch( tok_last_type( &tok ) ) - { - case TOK_STRING: + for (tok_init(&tok, buff, TOK_ACCEPT_UNFINISHED); + tok_has_next(&tok); + tok_next(&tok)) { - out.append(escape_string(tok_last( &tok ), UNESCAPE_INCOMPLETE)); - out.push_back(L'\n'); - break; - } + 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; + } + + } + } stdout_buffer.append(out); - free( buff ); - tok_destroy( &tok ); - } - else - { - if( cut_at_cursor ) - { - end = begin+pos; + 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"); - } + } } @@ -201,446 +201,446 @@ static void write_part( const wchar_t *begin, The commandline builtin. It is used for specifying a new value for the commandline. */ -static int builtin_commandline( parser_t &parser, wchar_t **argv ) +static int builtin_commandline(parser_t &parser, wchar_t **argv) { - int buffer_part=0; - int cut_at_cursor=0; + int buffer_part=0; + int cut_at_cursor=0; - int argc = builtin_count_args( argv ); - int append_mode=0; + int argc = builtin_count_args(argv); + int append_mode=0; - int function_mode = 0; + int function_mode = 0; - int tokenize = 0; + int tokenize = 0; - int cursor_mode = 0; - int line_mode = 0; - int search_mode = 0; - const wchar_t *begin, *end; + 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) + current_buffer = (wchar_t *)builtin_complete_get_temporary_buffer(); + if (current_buffer) { - /* - Prompt change requested while we don't have - a prompt, most probably while reading the - init files. Just ignore it. - */ - return 1; + 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; + } 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; - } - - 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; - append_format( stderr_buffer, - BUILTIN_ERR_UNKNOWN, - argv[0], - long_options[opt_index].name ); - builtin_print_help( parser, argv[0], stderr_buffer ); - - return 1; - - case L'a': - append_mode = APPEND_MODE; - break; - - case L'b': - buffer_part = STRING_MODE; - break; - - - case L'i': - append_mode = INSERT_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 'j': - buffer_part = JOB_MODE; - break; - - case 'p': - buffer_part = PROCESS_MODE; - break; - - case 'f': - function_mode=1; - break; - - case 'o': - tokenize=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 L'?': - builtin_unknown_option( parser, argv[0], argv[woptind-1] ); + builtin_print_help(parser, argv[0], stderr_buffer); return 1; } - } - if( function_mode ) - { - int i; + 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; + append_format(stderr_buffer, + BUILTIN_ERR_UNKNOWN, + argv[0], + long_options[opt_index].name); + builtin_print_help(parser, argv[0], stderr_buffer); + + return 1; + + case L'a': + append_mode = APPEND_MODE; + break; + + case L'b': + buffer_part = STRING_MODE; + break; + + + case L'i': + append_mode = INSERT_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 'j': + buffer_part = JOB_MODE; + break; + + case 'p': + buffer_part = PROCESS_MODE; + break; + + case 'f': + function_mode=1; + break; + + case 'o': + tokenize=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 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, - 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" ); - - 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; - } - - if( cursor_mode ) - { - if( argc-woptind ) + if ((buffer_part || tokenize || cut_at_cursor) && (cursor_mode || line_mode || search_mode)) { - 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 ); - } + BUILTIN_ERR_COMBO, + argv[0]); - 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; + builtin_print_help(parser, argv[0], stderr_buffer); + return 1; } - else + + + if ((tokenize || cut_at_cursor) && (argc-woptind)) { - append_format(stdout_buffer, L"%lu\n", (unsigned long)reader_get_cursor_pos() ); - return 0; + 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"); - 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; + builtin_print_help(parser, argv[0], stderr_buffer); + return 1; + } - } + /* + Set default modes + */ + if (!append_mode) + { + append_mode = REPLACE_MODE; + } - if( search_mode ) - { - return !reader_search_mode(); - } + if (!buffer_part) + { + buffer_part = STRING_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 ) - { + switch (buffer_part) + { case STRING_MODE: { - begin = get_buffer(); - end = begin+wcslen(begin); - break; + begin = get_buffer(); + end = begin+wcslen(begin); + break; } case PROCESS_MODE: { - parse_util_process_extent( get_buffer(), - get_cursor_pos(), - &begin, - &end ); - break; + 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; + 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; + parse_util_token_extent(get_buffer(), + get_cursor_pos(), + &begin, + &end, + 0, 0); + break; } - } + } - switch(argc-woptind) - { + switch (argc-woptind) + { case 0: { - write_part( begin, end, cut_at_cursor, tokenize ); - break; + write_part(begin, end, cut_at_cursor, tokenize); + break; } case 1: { - replace_part( begin, end, argv[woptind], append_mode ); - break; + replace_part(begin, end, argv[woptind], append_mode); + break; } default: { - wcstring sb = argv[woptind]; - int i; + 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); + + const wchar_t *prepend; + + if (next.flags & COMPLETE_NO_CASE) + { + prepend = L""; + } else - cmd.push_back(tmp); + { + prepend = token; + } + + + 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 { - append_format(stderr_buffer, L"%ls: Invalid token '%ls'\n", argv[0], woptarg ); - res = true; + 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); + } + } - break; - } - - case 'd': - desc = woptarg; - break; - - case 'u': - authoritative=0; - break; - - case 'A': - authoritative=1; - break; - - case 's': - short_opt.append(woptarg); - break; - - case 'l': - gnu_opt.push_back(woptarg); - break; - - case 'o': - old_opt.push_back(woptarg); - break; - - case 'a': - comp = woptarg; - break; - - case 'e': - remove = 1; - break; - - case 'n': - condition = woptarg; - break; - - case 'C': - do_complete = true; - do_complete_param = woptarg ? woptarg : reader_get_buffer(); - break; - - case 'h': - builtin_print_help( parser, argv[0], stdout_buffer ); - return 0; - - case '?': - builtin_unknown_option( parser, argv[0], argv[woptind-1] ); - res = true; - break; - } - } - - if( !res ) - { - if( condition && wcslen( condition ) ) - { - if( parser.test( condition, 0, 0, 0 ) ) - { - append_format(stderr_buffer, - L"%ls: Condition '%ls' contained a syntax error\n", - argv[0], - condition ); - - parser.test( condition, 0, &stderr_buffer, argv[0] ); - - res = true; - } - } - } - - if( !res ) - { - if( comp && wcslen( comp ) ) - { - if( parser.test_args( comp, 0, 0 ) ) - { - append_format(stderr_buffer, - L"%ls: Completion '%ls' contained a syntax error\n", - argv[0], - comp ); - - parser.test_args( comp, &stderr_buffer, argv[0] ); - - res = true; - } - } - } - - if( !res ) - { - if( do_complete ) - { - const wchar_t *token; - - parse_util_token_extent( do_complete_param.c_str(), do_complete_param.size(), &token, 0, 0, 0 ); - - const wchar_t *prev_temporary_buffer = temporary_buffer; - temporary_buffer = do_complete_param.c_str(); - - if( recursion_level < 1 ) - { - recursion_level++; - - std::vector comp; - complete( do_complete_param, comp, COMPLETE_DEFAULT ); - - for( size_t i=0; i< comp.size() ; i++ ) - { - const completion_t &next = comp.at( i ); - - const wchar_t *prepend; - - if( next.flags & COMPLETE_NO_CASE ) - { - prepend = L""; - } - else - { - prepend = token; - } - - - 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; + return res ? 1 : 0; } diff --git a/builtin_jobs.cpp b/builtin_jobs.cpp index 4725f5dd4..ab2da51bb 100644 --- a/builtin_jobs.cpp +++ b/builtin_jobs.cpp @@ -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 */ } - ; +; @@ -43,113 +43,113 @@ enum /** Calculates the cpu usage (in percent) of the specified job. */ -static int cpu_use( const job_t *j ) +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", - t1, t2, jiffies, p->last_jiffies ); -*/ + /* 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 /** Print information about the specified job */ -static void builtin_jobs_print( const job_t *j, int mode, int header ) +static void builtin_jobs_print(const job_t *j, int mode, int header) { - process_t *p; - switch( mode ) - { + 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(L"\t"); - stdout_buffer.append(j->command_wcstr()); - stdout_buffer.append(L"\n"); - break; + 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; } 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; + 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" )); - } + 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" )); - } + 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; + } } - } } @@ -158,194 +158,194 @@ static void builtin_jobs_print( const job_t *j, int mode, int header ) /** The jobs builtin. Used fopr printing running jobs. Defined in builtin_jobs.c. */ -static int builtin_jobs( parser_t &parser, wchar_t **argv ) +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 - } - } - ; - - int opt_index = 0; - - int opt = wgetopt_long( argc, - argv, - L"pclgh", - long_options, - &opt_index ); - if( opt == -1 ) - break; - - switch( opt ) + while (1) { - case 0: - if(long_options[opt_index].flag != 0) - break; - append_format(stderr_buffer, - BUILTIN_ERR_UNKNOWN, - argv[0], - long_options[opt_index].name ); + 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 + } + } + ; - builtin_print_help( parser, argv[0], stderr_buffer ); + int opt_index = 0; + + 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; + append_format(stderr_buffer, + BUILTIN_ERR_UNKNOWN, + argv[0], + long_options[opt_index].name); + + 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; - } - - if( print_last ) - { /* - Ignore unconstructed jobs, i.e. ourself. + Do not babble if not interactive */ + if (builtin_out_redirect) + { + found=1; + } + + 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; - - found = 1; - - for( i=woptind; iflags & JOB_CONSTRUCTED) && !job_is_completed(j)) + { + builtin_jobs_print(j, mode, !found); + return 0; + } } - j = job_get_from_pid( pid ); - - if( j && !job_is_completed( j ) ) - { - builtin_jobs_print( j, mode, !found ); - } - else - { - append_format(stderr_buffer, - _( L"%ls: No suitable job: %d\n" ), - argv[0], - pid ); - return 1; - } - } } else { + if (woptind < argc) + { + int i; + + found = 1; + + 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 5b34227ec..8c6ed593b 100644 --- a/builtin_set.cpp +++ b/builtin_set.cpp @@ -47,27 +47,27 @@ extern wcstring stdout_buffer, stderr_buffer; /** Test if the specified variable should be subject to path validation */ -static int is_path_variable( const wchar_t *env ) +static int is_path_variable(const wchar_t *env) { - return contains(env, L"PATH", L"CDPATH" ); + return contains(env, L"PATH", L"CDPATH"); } /** Call env_set. If this is a path variable, e.g. PATH, validate the elements. On error, print a description of the problem to stderr. */ -static int my_env_set( const wchar_t *key, const wcstring_list_t &val, int scope ) +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 ) ) + 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++ ) + for (i=0; i< val.size() ; i++) { bool show_perror = false; int show_hint = 0; @@ -76,18 +76,18 @@ static int my_env_set( const wchar_t *key, const wcstring_list_t &val, int scope struct stat buff; const wchar_t *dir = val[i].c_str(); - if( wstat( dir, &buff ) ) + if (wstat(dir, &buff)) { error = true; show_perror = true; } - if( !( S_ISDIR(buff.st_mode) ) ) + if (!(S_ISDIR(buff.st_mode))) { error = true; } - if( !error ) + if (!error) { any_success = true; } @@ -96,29 +96,29 @@ static int my_env_set( const wchar_t *key, const wcstring_list_t &val, int scope any_error = true; const wchar_t *colon; append_format(stderr_buffer, _(BUILTIN_SET_PATH_ERROR), L"set", dir, key); - colon = wcschr( dir, L':' ); + colon = wcschr(dir, L':'); - if( colon && *(colon+1) ) + if (colon && *(colon+1)) { show_hint = 1; } } - if( show_perror ) + if (show_perror) { - builtin_wperror( L"set" ); + builtin_wperror(L"set"); } - if( show_hint ) + if (show_hint) { - append_format(stderr_buffer, _(BUILTIN_SET_PATH_HINT), L"set", key, key, wcschr( dir, L':' )+1); + 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 ) + if (! val.empty() && ! any_success) { return 1; } @@ -126,34 +126,34 @@ static int my_env_set( const wchar_t *key, const wcstring_list_t &val, int scope } wcstring sb; - if( val.size() ) + if (val.size()) { - for( i=0; i< val.size() ; i++ ) + for (i=0; i< val.size() ; i++) { sb.append(val[i]); - if( i &indexes, - const wchar_t *src, - const wchar_t *name, - size_t var_count ) +static int parse_index(std::vector &indexes, + const wchar_t *src, + const wchar_t *name, + size_t var_count) { - size_t len; + size_t len; - int count = 0; - const wchar_t *src_orig = src; + 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; - } - - src++; - - while (iswspace(*src)) - { - src++; - } - - while (*src != L']') - { - wchar_t *end; - - long l_ind; - - errno = 0; - - l_ind = wcstol(src, &end, 10); - - if( end==src || errno ) + if (src == 0) { - append_format(stderr_buffer, _(L"%ls: Invalid index starting at '%ls'\n"), L"set", src); - return 0; + return 0; } - if( l_ind < 0 ) + while (*src != L'\0' && (iswalnum(*src) || *src == L'_')) { - l_ind = var_count+l_ind+1; + src++; } - 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( 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 ) + /* Replace values where needed */ + for (i = 0; i < indexes.size(); i++) { - return 1; - } - if ( (size_t)ind >= list.size() ) + /* + 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) { - list.resize( ind+1 ); + return 1; + } + if ((size_t)ind >= list.size()) + { + list.resize(ind+1); } // free((void *) al_get(list, ind)); - list[ ind ] = newv; - } + list[ ind ] = newv; + } - return 0; + return 0; } /** @@ -311,9 +314,11 @@ static void erase_values(wcstring_list_t &list, const std::vector &indexes // 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++) { + for (iter = indexes_set.rbegin(); iter != indexes_set.rend(); iter++) + { long val = *iter; - if (val > 0 && (size_t)val <= list.size()) { + if (val > 0 && (size_t)val <= list.size()) + { // One-based indexing! list.erase(list.begin() + val - 1); } @@ -330,41 +335,41 @@ static void print_variables(int include_values, int esc, bool shorten_ok, int sc 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); + 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 ) + if (include_values) { - shorten = 1; - value.resize(60); - } + env_var_t value = env_get_string(key); + if (!value.missing()) + { + int shorten = 0; - wcstring e_value = esc ? expand_escape_variable(value) : value; + 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"); + if (shorten) + { + stdout_buffer.append(L"\u2026"); + } + + } } - } + stdout_buffer.append(L"\n"); } - - stdout_buffer.append(L"\n"); - } } @@ -373,422 +378,422 @@ 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' - } - , - { - 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"; - - int argc = builtin_count_args(argv); - - /* - 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; - - /* - 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); - - if (c == -1) - { - break; - } - - switch(c) - { - case 0: - 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 'L': - shorten_ok = false; - break; - - case 'q': - query = 1; - break; - - case 'h': - builtin_print_help( parser, argv[0], stdout_buffer ); - return 0; - - case '?': - builtin_unknown_option( parser, argv[0], argv[woptind-1] ); - return 1; - - default: - break; - } - } - - /* - 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( query && (erase || list) ) - { - append_format(stderr_buffer, - BUILTIN_ERR_COMBO, - 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] ); - - 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; - } - - /* - 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; - } - - /* - 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; - - if( query ) - { - /* - Query mode. Return the number of variables that do not exist - out of the specified variables. + /** + Variables used for parsing the argument list */ - int i; - for( i=woptind; i indexes; - wcstring_list_t result; - size_t j; + /* + 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; + + /* + 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); + + if (c == -1) + { + break; + } + + switch (c) + { + case 0: + 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 'L': + shorten_ok = false; + break; + + case 'q': + query = 1; + break; + + case 'h': + builtin_print_help(parser, argv[0], stdout_buffer); + return 0; + + case '?': + builtin_unknown_option(parser, argv[0], argv[woptind-1]); + return 1; + + default: + break; + } + } + + /* + 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 (query && (erase || list)) + { + append_format(stderr_buffer, + BUILTIN_ERR_COMBO, + 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]); + + 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; + } + + /* + 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; + } + + /* + 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; + + 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; env_var_t dest_str = env_get_string(dest); if (! dest_str.missing()) - tokenize_variable_array( dest_str, result ); + 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++; - } - } + 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 ); + free(dest); + } + return retcode; } - 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 ) + if (list) { - append_format(stderr_buffer, - _(L"%ls: Erase needs a variable name\n"), - argv[0] ); - - builtin_print_help( parser, argv[0], stderr_buffer ); - retcode = 1; + /* Maybe we should issue an error if there are any other arguments? */ + print_variables(0, 0, shorten_ok, scope); + return 0; } - else + + if (woptind == argc) { - print_variables( 1, 1, shorten_ok, scope ); + /* + 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; } - return retcode; - } + if (!(dest = wcsdup(argv[woptind]))) + { + DIE_MEM(); + } - if( !(dest = wcsdup(argv[woptind]))) - { - DIE_MEM(); - } + if (wcschr(dest, L'[')) + { + slice = 1; + *wcschr(dest, L'[')=0; + } - 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( !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( (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 ) - { + 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; + } /* - Slice mode + set assignment can work in two modes, either using slices or + using the whole array. We detect which mode is used here. */ - size_t idx_count, val_count; - wcstring_list_t values; - std::vector indexes; - wcstring_list_t result; + + if (slice) + { + + /* + 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 ); + tokenize_variable_array(dest_str, result); - for( ; woptind -enum { +enum +{ BUILTIN_TEST_SUCCESS = STATUS_BUILTIN_OK, BUILTIN_TEST_FAIL = STATUS_BUILTIN_ERROR }; -int builtin_test( parser_t &parser, wchar_t **argv ); +int builtin_test(parser_t &parser, wchar_t **argv); -static const wchar_t * const condstr[] = { +static const wchar_t * const condstr[] = +{ L"!", L"&&", L"||", L"==", L"!=", L"<", L">", L"-nt", L"-ot", L"-ef", L"-eq", L"-ne", L"-lt", L"-gt", L"-le", L"-ge", L"=~" }; -namespace test_expressions { +namespace test_expressions +{ - enum token_t { - test_unknown, // arbitrary string +enum token_t +{ + test_unknown, // arbitrary string - test_bang, // "!", inverts sense + test_bang, // "!", inverts sense - test_filetype_b, // "-b", for block special files - test_filetype_c, // "-c" for character special files - test_filetype_d, // "-d" for directories - test_filetype_e, // "-e" for files that exist - test_filetype_f, // "-f" for for regular files - test_filetype_g, // "-g" for set-group-id - test_filetype_h, // "-h" for symbolic links - test_filetype_L, // "-L", same as -h - test_filetype_p, // "-p", for FIFO - test_filetype_S, // "-S", socket + test_filetype_b, // "-b", for block special files + test_filetype_c, // "-c" for character special files + test_filetype_d, // "-d" for directories + test_filetype_e, // "-e" for files that exist + test_filetype_f, // "-f" for for regular files + test_filetype_g, // "-g" for set-group-id + test_filetype_h, // "-h" for symbolic links + test_filetype_L, // "-L", same as -h + test_filetype_p, // "-p", for FIFO + test_filetype_S, // "-S", socket - test_filesize_s, // "-s", size greater than zero + test_filesize_s, // "-s", size greater than zero - test_filedesc_t, // "-t", whether the fd is associated with a terminal + test_filedesc_t, // "-t", whether the fd is associated with a terminal - test_fileperm_r, // "-r", read permission - test_fileperm_u, // "-u", whether file is setuid - test_fileperm_w, // "-w", whether file write permission is allowed - test_fileperm_x, // "-x", whether file execute/search is allowed + test_fileperm_r, // "-r", read permission + test_fileperm_u, // "-u", whether file is setuid + test_fileperm_w, // "-w", whether file write permission is allowed + test_fileperm_x, // "-x", whether file execute/search is allowed - test_string_n, // "-n", non-empty string - test_string_z, // "-z", true if length of string is 0 - test_string_equal, // "=", true if strings are identical - test_string_not_equal, // "!=", true if strings are not identical + test_string_n, // "-n", non-empty string + test_string_z, // "-z", true if length of string is 0 + test_string_equal, // "=", true if strings are identical + test_string_not_equal, // "!=", true if strings are not identical - test_number_equal, // "-eq", true if numbers are equal - test_number_not_equal, // "-ne", true if numbers are not equal - test_number_greater, // "-gt", true if first number is larger than second - test_number_greater_equal, // "-ge", true if first number is at least second - test_number_lesser, // "-lt", true if first number is smaller than second - test_number_lesser_equal, // "-le", true if first number is at most second + test_number_equal, // "-eq", true if numbers are equal + test_number_not_equal, // "-ne", true if numbers are not equal + test_number_greater, // "-gt", true if first number is larger than second + test_number_greater_equal, // "-ge", true if first number is at least second + test_number_lesser, // "-lt", true if first number is smaller than second + test_number_lesser_equal, // "-le", true if first number is at most second - test_combine_and, // "-a", true if left and right are both true - test_combine_or, // "-o", true if either left or right is true + test_combine_and, // "-a", true if left and right are both true + test_combine_or, // "-o", true if either left or right is true - test_paren_open, // "(", open paren - test_paren_close, // ")", close paren - }; + test_paren_open, // "(", open paren + test_paren_close, // ")", close paren +}; - static bool binary_primary_evaluate(test_expressions::token_t token, const wcstring &left, const wcstring &right, wcstring_list_t &errors); - static bool unary_primary_evaluate(test_expressions::token_t token, const wcstring &arg, wcstring_list_t &errors); +static bool binary_primary_evaluate(test_expressions::token_t token, const wcstring &left, const wcstring &right, wcstring_list_t &errors); +static bool unary_primary_evaluate(test_expressions::token_t token, const wcstring &arg, wcstring_list_t &errors); - enum { - UNARY_PRIMARY = 1 << 0, - BINARY_PRIMARY = 1 << 1 - }; +enum +{ + UNARY_PRIMARY = 1 << 0, + BINARY_PRIMARY = 1 << 1 +}; - static const struct token_info_t { token_t tok; const wchar_t *string; unsigned int flags; } token_infos[] = +static const struct token_info_t +{ + token_t tok; + const wchar_t *string; + unsigned int flags; +} token_infos[] = +{ + {test_unknown, L"", 0}, + {test_bang, L"!", 0}, + {test_filetype_b, L"-b", UNARY_PRIMARY}, + {test_filetype_c, L"-c", UNARY_PRIMARY}, + {test_filetype_d, L"-d", UNARY_PRIMARY}, + {test_filetype_e, L"-e", UNARY_PRIMARY}, + {test_filetype_f, L"-f", UNARY_PRIMARY}, + {test_filetype_g, L"-g", UNARY_PRIMARY}, + {test_filetype_h, L"-h", UNARY_PRIMARY}, + {test_filetype_L, L"-L", UNARY_PRIMARY}, + {test_filetype_p, L"-p", UNARY_PRIMARY}, + {test_filetype_S, L"-S", UNARY_PRIMARY}, + {test_filesize_s, L"-s", UNARY_PRIMARY}, + {test_filedesc_t, L"-t", UNARY_PRIMARY}, + {test_fileperm_r, L"-r", UNARY_PRIMARY}, + {test_fileperm_u, L"-u", UNARY_PRIMARY}, + {test_fileperm_w, L"-w", UNARY_PRIMARY}, + {test_fileperm_x, L"-x", UNARY_PRIMARY}, + {test_string_n, L"-n", UNARY_PRIMARY}, + {test_string_z, L"-z", UNARY_PRIMARY}, + {test_string_equal, L"=", BINARY_PRIMARY}, + {test_string_not_equal, L"!=", BINARY_PRIMARY}, + {test_number_equal, L"-eq", BINARY_PRIMARY}, + {test_number_not_equal, L"-ne", BINARY_PRIMARY}, + {test_number_greater, L"-gt", BINARY_PRIMARY}, + {test_number_greater_equal, L"-ge", BINARY_PRIMARY}, + {test_number_lesser, L"-lt", BINARY_PRIMARY}, + {test_number_lesser_equal, L"-le", BINARY_PRIMARY}, + {test_combine_and, L"-a", 0}, + {test_combine_or, L"-o", 0}, + {test_paren_open, L"(", 0}, + {test_paren_close, L")", 0} +}; + +const token_info_t *token_for_string(const wcstring &str) +{ + for (size_t i=0; i < sizeof token_infos / sizeof *token_infos; i++) { - {test_unknown, L"", 0}, - {test_bang, L"!", 0}, - {test_filetype_b, L"-b", UNARY_PRIMARY}, - {test_filetype_c, L"-c", UNARY_PRIMARY}, - {test_filetype_d, L"-d", UNARY_PRIMARY}, - {test_filetype_e, L"-e", UNARY_PRIMARY}, - {test_filetype_f, L"-f", UNARY_PRIMARY}, - {test_filetype_g, L"-g", UNARY_PRIMARY}, - {test_filetype_h, L"-h", UNARY_PRIMARY}, - {test_filetype_L, L"-L", UNARY_PRIMARY}, - {test_filetype_p, L"-p", UNARY_PRIMARY}, - {test_filetype_S, L"-S", UNARY_PRIMARY}, - {test_filesize_s, L"-s", UNARY_PRIMARY}, - {test_filedesc_t, L"-t", UNARY_PRIMARY}, - {test_fileperm_r, L"-r", UNARY_PRIMARY}, - {test_fileperm_u, L"-u", UNARY_PRIMARY}, - {test_fileperm_w, L"-w", UNARY_PRIMARY}, - {test_fileperm_x, L"-x", UNARY_PRIMARY}, - {test_string_n, L"-n", UNARY_PRIMARY}, - {test_string_z, L"-z", UNARY_PRIMARY}, - {test_string_equal, L"=", BINARY_PRIMARY}, - {test_string_not_equal, L"!=", BINARY_PRIMARY}, - {test_number_equal, L"-eq", BINARY_PRIMARY}, - {test_number_not_equal, L"-ne", BINARY_PRIMARY}, - {test_number_greater, L"-gt", BINARY_PRIMARY}, - {test_number_greater_equal, L"-ge", BINARY_PRIMARY}, - {test_number_lesser, L"-lt", BINARY_PRIMARY}, - {test_number_lesser_equal, L"-le", BINARY_PRIMARY}, - {test_combine_and, L"-a", 0}, - {test_combine_or, L"-o", 0}, - {test_paren_open, L"(", 0}, - {test_paren_close, L")", 0} - }; - - const token_info_t *token_for_string(const wcstring &str) { - for (size_t i=0; i < sizeof token_infos / sizeof *token_infos; i++) { - if (str == token_infos[i].string) { - return &token_infos[i]; - } - } - return &token_infos[0]; //unknown - } - - - /* Grammar. - - = - - = 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) { } - }; - - - /* Base class for 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; - }; - - typedef std::auto_ptr expr_ref_t; - - /* Single argument like -n foo or "just a string" */ - class unary_primary : public expression { - public: - wcstring arg; - unary_primary(token_t tok, range_t where, const wcstring &what) : expression(tok, where), arg(what) { } - bool evaluate(wcstring_list_t &errors); - }; - - /* Two argument primary like foo != bar */ - class binary_primary : public expression { - 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; - 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) + if (str == token_infos[i].string) { - /* We should have one more subject than combiner */ - assert(subjects.size() == combiners.size() + 1); + return &token_infos[i]; } + } + return &token_infos[0]; //unknown +} - /* 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); - }; +/* Grammar. - /* 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); - }; + = and/or | + - 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); + = 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); } - expression *test_parser::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); - return NULL; +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) { } +}; + + +/* Base class for 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; +}; + +typedef std::auto_ptr expr_ref_t; + +/* Single argument like -n foo or "just a string" */ +class unary_primary : public expression +{ +public: + wcstring arg; + unary_primary(token_t tok, range_t where, const wcstring &what) : expression(tok, where), arg(what) { } + bool evaluate(wcstring_list_t &errors); +}; + +/* Two argument primary like foo != bar */ +class binary_primary : public expression +{ +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; + 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); } - expression *test_parser::parse_unary_expression(unsigned int start, unsigned int end) { - if (start >= end) { - return error(L"Missing argument at index %u", start); - } - token_t tok = token_for_string(arg(start))->tok; - if (tok == test_bang) { - expr_ref_t subject(parse_unary_expression(start + 1, end)); - if (subject.get()) { - return new unary_operator(tok, range_t(start, subject->range.end), subject); - } else { - return NULL; - } - } else { - return parse_primary(start, end); + /* We are responsible for destroying our expressions */ + virtual ~combining_expression() + { + for (size_t i=0; i < subjects.size(); i++) + { + delete subjects[i]; } } - /* Parse a combining expression (AND, OR) */ - expression *test_parser::parse_combining_expression(unsigned int start, unsigned int end) { - if (start >= end) + 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); +} + +expression *test_parser::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); + 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); + } + token_t tok = token_for_string(arg(start))->tok; + if (tok == test_bang) + { + expr_ref_t subject(parse_unary_expression(start + 1, end)); + if (subject.get()) + { + return new unary_operator(tok, range_t(start, subject->range.end), subject); + } + else + { return NULL; + } + } + else + { + return parse_primary(start, end); + } +} - std::vector subjects; - std::vector combiners; - unsigned int idx = start; +/* Parse a combining expression (AND, OR) */ +expression *test_parser::parse_combining_expression(unsigned int start, unsigned int end) +{ + if (start >= end) + return NULL; - while (idx < end) { + std::vector subjects; + std::vector combiners; + unsigned int idx = start; - if (! subjects.empty()) { - /* This is not the first expression, so we expect a combiner. */ - token_t combiner = token_for_string(arg(idx))->tok; - if (combiner != test_combine_and && combiner != test_combine_or) { - /* Not a combiner, we're done */ - break; - } - combiners.push_back(combiner); - idx++; - } + while (idx < end) + { - /* Parse another expression */ - expression *expr = parse_unary_expression(idx, end); - if (! expr) { - add_error(L"Missing argument at index %u", idx); + if (! subjects.empty()) + { + /* This is not the first expression, so we expect a combiner. */ + token_t combiner = token_for_string(arg(idx))->tok; + if (combiner != test_combine_and && combiner != test_combine_or) + { + /* Not a combiner, we're done */ break; } - - /* Go to the end of this expression */ - idx = expr->range.end; - subjects.push_back(expr); + combiners.push_back(combiner); + idx++; } - 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); - } else { - /* No subjects */ - return NULL; - } - } - - expression *test_parser::parse_unary_primary(unsigned int start, unsigned int end) { - /* We need two arguments */ - if (start >= end) { - return error(L"Missing argument at index %u", start); - } - 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; - } else { - /* 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++) { - if (idx >= end) { - 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); - if (close_index == end) { - return error(L"Missing close paren at index %u", close_index); - } - const token_info_t *close_paren = token_for_string(arg(close_index)); - 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); - } - - expression *test_parser::parse_primary(unsigned int start, unsigned int end) { - 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); - if (! expr) expr = parse_binary_primary(start, end); - if (! expr) expr = parse_just_a_string(start, end); - return expr; - } - - expression *test_parser::parse_expression(unsigned int start, unsigned int end) { - 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++) { - err.append(L"test: "); - err.append(parser.errors.at(i)); - err.push_back(L'\n'); - errored = true; - // For now we only show the first error + /* Parse another expression */ + expression *expr = parse_unary_expression(idx, end); + if (! expr) + { + add_error(L"Missing argument at index %u", idx); 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()); - if (result->range.end < args.size()) { - append_format(err, L"test: unexpected argument at index %lu: '%ls'\n", (unsigned long)result->range.end, args.at(result->range.end).c_str()); - delete result; - result = NULL; - errored = true; - } - } - - - return result; + /* Go to the end of this expression */ + idx = expr->range.end; + subjects.push_back(expr); } - bool unary_primary::evaluate(wcstring_list_t &errors) { - return unary_primary_evaluate(token, arg, errors); + 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); + } + else + { + /* No subjects */ + return NULL; + } +} + +expression *test_parser::parse_unary_primary(unsigned int start, unsigned int end) +{ + /* We need two arguments */ + if (start >= end) + { + return error(L"Missing argument at index %u", start); + } + if (start + 1 >= end) + { + return error(L"Missing argument at index %u", start + 1); } - bool binary_primary::evaluate(wcstring_list_t &errors) { - return binary_primary_evaluate(token, arg_left, arg_right, errors); + /* 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); } - bool unary_operator::evaluate(wcstring_list_t &errors) { - switch (token) { - case test_bang: - assert(subject.get()); - return ! subject->evaluate(errors); - default: - errors.push_back(format_string(L"Unknown token type in %s", __func__)); - return false; + 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; + } + else + { + /* 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++) + { + if (idx >= end) + { + return error(L"Missing argument at index %u", idx); } } - bool combining_expression::evaluate(wcstring_list_t &errors) { - switch (token) { - case test_combine_and: - case test_combine_or: + /* 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); + if (close_index == end) + { + return error(L"Missing close paren at index %u", close_index); + } + const token_info_t *close_paren = token_for_string(arg(close_index)); + 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); +} + +expression *test_parser::parse_primary(unsigned int start, unsigned int end) +{ + 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); + if (! expr) expr = parse_binary_primary(start, end); + if (! expr) expr = parse_just_a_string(start, end); + return expr; +} + +expression *test_parser::parse_expression(unsigned int start, unsigned int end) +{ + 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++) + { + err.append(L"test: "); + err.append(parser.errors.at(i)); + err.push_back(L'\n'); + errored = true; + // 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()); + if (result->range.end < args.size()) + { + append_format(err, L"test: unexpected argument at index %lu: '%ls'\n", (unsigned long)result->range.end, args.at(result->range.end).c_str()); + delete result; + result = NULL; + 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: + assert(subject.get()); + return ! subject->evaluate(errors); + default: + errors.push_back(format_string(L"Unknown token type in %s", __func__)); + return false; + + } +} + +bool combining_expression::evaluate(wcstring_list_t &errors) +{ + switch (token) + { + case test_combine_and: + case test_combine_or: + { + /* 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) + { + if (or_result) { - /* 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) { - if (or_result) { - /* 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; + /* Short circuit */ + break; } - default: - errors.push_back(format_string(L"Unknown token type in %s", __func__)); - return BUILTIN_TEST_FAIL; + /* 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; } - bool parenthetical_expression::evaluate(wcstring_list_t &errors) { - return contents->evaluate(errors); + default: + errors.push_back(format_string(L"Unknown token type in %s", __func__)); + return BUILTIN_TEST_FAIL; + } +} - /* IEEE 1003.1 says nothing about what it means for two strings to be "algebraically equal". For example, should we interpret 0x10 as 0, 10, or 16? Here we use only base 10 and use wcstoll, which allows for leading + and -, and leading whitespace. This matches bash. */ - static bool parse_number(const wcstring &arg, long long *out) { - const wchar_t *str = arg.c_str(); - wchar_t *endptr = NULL; - *out = wcstoll(str, &endptr, 10); - return endptr && *endptr == L'\0'; +bool parenthetical_expression::evaluate(wcstring_list_t &errors) +{ + return contents->evaluate(errors); +} + +/* IEEE 1003.1 says nothing about what it means for two strings to be "algebraically equal". For example, should we interpret 0x10 as 0, 10, or 16? Here we use only base 10 and use wcstoll, which allows for leading + and -, and leading whitespace. This matches bash. */ +static bool parse_number(const wcstring &arg, long long *out) +{ + const wchar_t *str = arg.c_str(); + wchar_t *endptr = NULL; + *out = wcstoll(str, &endptr, 10); + return endptr && *endptr == L'\0'; +} + +static bool binary_primary_evaluate(test_expressions::token_t token, const wcstring &left, const wcstring &right, wcstring_list_t &errors) +{ + using namespace test_expressions; + long long left_num, right_num; + 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; } +} - static bool binary_primary_evaluate(test_expressions::token_t token, const wcstring &left, const wcstring &right, wcstring_list_t &errors) { - using namespace test_expressions; - long long left_num, right_num; - switch (token) { - case test_string_equal: - return left == right; - case test_string_not_equal: - return left != right; +static bool unary_primary_evaluate(test_expressions::token_t token, const wcstring &arg, wcstring_list_t &errors) +{ + using namespace test_expressions; + struct stat buf; + long long num; + switch (token) + { + case test_filetype_b: // "-b", for block special files + return !wstat(arg, &buf) && S_ISBLK(buf.st_mode); - case test_number_equal: - return parse_number(left, &left_num) && parse_number(right, &right_num) && left_num == right_num; + case test_filetype_c: // "-c" for character special files + return !wstat(arg, &buf) && S_ISCHR(buf.st_mode); - case test_number_not_equal: - return parse_number(left, &left_num) && parse_number(right, &right_num) && left_num != right_num; + case test_filetype_d: // "-d" for directories + return !wstat(arg, &buf) && S_ISDIR(buf.st_mode); - case test_number_greater: - return parse_number(left, &left_num) && parse_number(right, &right_num) && left_num > right_num; + case test_filetype_e: // "-e" for files that exist + return !wstat(arg, &buf); - case test_number_greater_equal: - return parse_number(left, &left_num) && parse_number(right, &right_num) && left_num >= right_num; + case test_filetype_f: // "-f" for for regular files + return !wstat(arg, &buf) && S_ISREG(buf.st_mode); - case test_number_lesser: - return parse_number(left, &left_num) && parse_number(right, &right_num) && left_num < right_num; + case test_filetype_g: // "-g" for set-group-id + return !wstat(arg, &buf) && (S_ISGID & buf.st_mode); - case test_number_lesser_equal: - return parse_number(left, &left_num) && parse_number(right, &right_num) && left_num <= right_num; + 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); - default: - errors.push_back(format_string(L"Unknown token type in %s", __func__)); - return false; - } - } - - - static bool unary_primary_evaluate(test_expressions::token_t token, const wcstring &arg, wcstring_list_t &errors) { - using namespace test_expressions; - struct stat buf; - long long num; - 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; - } + 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; } +} }; @@ -695,7 +783,7 @@ namespace test_expressions { * Return status is the final shell status, i.e. 0 for true, * 1 for false and 2 for error. */ -int builtin_test( parser_t &parser, wchar_t **argv ) +int builtin_test(parser_t &parser, wchar_t **argv) { using namespace test_expressions; @@ -708,32 +796,43 @@ int builtin_test( parser_t &parser, wchar_t **argv ) argc++; const wcstring_list_t args(argv + 1, argv + 1 + argc); - if (argc == 0) { + if (argc == 0) + { // Per 1003.1, exit false return BUILTIN_TEST_FAIL; - } else if (argc == 1) { + } + else if (argc == 1) + { // Per 1003.1, exit true if the arg is non-empty return args.at(0).empty() ? BUILTIN_TEST_FAIL : BUILTIN_TEST_SUCCESS; - } else { + } + else + { // Try parsing. If expr is not nil, we are responsible for deleting it. wcstring err; expression *expr = test_parser::parse_args(args, err); - if (! expr) { + if (! expr) + { #if 0 printf("Oops! test was given args:\n"); - for (size_t i=0; i < argc; i++) { + for (size_t i=0; i < argc; i++) + { printf("\t%ls\n", args.at(i).c_str()); } printf("and returned parse error: %ls\n", err.c_str()); #endif builtin_show_error(err); return BUILTIN_TEST_FAIL; - } else { + } + else + { wcstring_list_t eval_errors; bool result = expr->evaluate(eval_errors); - if (! eval_errors.empty()) { + if (! eval_errors.empty()) + { printf("test returned eval errors:\n"); - for (size_t i=0; i < eval_errors.size(); i++) { + for (size_t i=0; i < eval_errors.size(); i++) + { printf("\t%ls\n", eval_errors.at(i).c_str()); } } diff --git a/builtin_ulimit.cpp b/builtin_ulimit.cpp index d20abeed9..4a244ad60 100644 --- a/builtin_ulimit.cpp +++ b/builtin_ulimit.cpp @@ -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 ) +static int get_multiplier(int what) { - int i; + int i; - for( i=0; resource_arr[i].desc; i++ ) - { - if( resource_arr[i].resource == what ) + for (i=0; resource_arr[i].desc; i++) { - return resource_arr[i].multiplier; + if (resource_arr[i].resource == what) + { + return resource_arr[i].multiplier; + } } - } - return -1; + return -1; } /** @@ -127,85 +127,85 @@ static int get_multiplier( int what ) does _not_ multiply the limit value by the multiplier constant used by the commandline ulimit. */ -static rlim_t get( int resource, int hard ) +static rlim_t get(int resource, int hard) { - struct rlimit ls; + struct rlimit ls; - getrlimit( resource, &ls ); + getrlimit(resource, &ls); - return hard ? ls.rlim_max:ls.rlim_cur; + return hard ? ls.rlim_max:ls.rlim_cur; } /** Print the value of the specified resource limit */ -static void print( int resource, int hard ) +static void print(int resource, int hard) { - rlim_t l = get( resource, hard ); + rlim_t l = get(resource, hard); - if( l == RLIM_INFINITY ) - stdout_buffer.append( L"unlimited\n" ); - else - append_format(stdout_buffer, L"%d\n", l / get_multiplier( resource ) ); + if (l == RLIM_INFINITY) + stdout_buffer.append(L"unlimited\n"); + else + append_format(stdout_buffer, L"%d\n", l / get_multiplier(resource)); } /** Print values of all resource limits */ -static void print_all( int hard ) +static void print_all(int hard) { - int i; - int w=0; + 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 ) + for (i=0; resource_arr[i].desc; i++) { - stdout_buffer.append( L"unlimited\n" ); + w=maxi(w, my_wcswidth(resource_arr[i].desc)); } - else + + for (i=0; resource_arr[i].desc; i++) { - append_format(stdout_buffer, L"%d\n", l/get_multiplier(resource_arr[i].resource) ); + 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)); + } } - } } /** Returns the description for the specified resource limit */ -static const wchar_t *get_desc( int what ) +static const wchar_t *get_desc(int what) { - int i; + int i; - for( i=0; resource_arr[i].desc; i++ ) - { - if( resource_arr[i].resource == what ) + for (i=0; resource_arr[i].desc; i++) { - return resource_arr[i].desc; + if (resource_arr[i].resource == what) + { + return resource_arr[i].desc; + } } - } - return L"Not a resource"; + return L"Not a resource"; } /** @@ -213,300 +213,300 @@ static const wchar_t *get_desc( int what ) does _not_ multiply the limit value by the multiplier constant used by the commandline ulimit. */ -static int set( int resource, int hard, int soft, rlim_t value ) +static int set(int resource, int hard, int soft, rlim_t value) { - struct rlimit ls; - getrlimit( resource, &ls ); + 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)) + if (hard) { - ls.rlim_cur = ls.rlim_max; + ls.rlim_max = value; } - } - 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; + 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; } /** The ulimit builtin, used for setting resource limits. Defined in builtin_ulimit.c. */ -static int builtin_ulimit( parser_t &parser, wchar_t ** argv ) +static int builtin_ulimit(parser_t &parser, wchar_t ** argv) { - int hard=0; - int soft=0; + int hard=0; + int soft=0; - int what = RLIMIT_FSIZE; - int report_all = 0; + int what = RLIMIT_FSIZE; + int report_all = 0; - int argc = builtin_count_args( argv ); + int argc = builtin_count_args(argv); - woptind=0; + woptind=0; - while( 1 ) - { - static const struct woption - long_options[] = - { - { - L"all", no_argument, 0, 'a' - } - , - { - L"hard", no_argument, 0, 'H' - } - , - { - L"soft", no_argument, 0, 'S' - } - , - { - L"core-size", no_argument, 0, 'c' - } - , - { - L"data-size", no_argument, 0, 'd' - } - , - { - L"file-size", no_argument, 0, 'f' - } - , - { - L"lock-size", no_argument, 0, 'l' - } - , - { - L"resident-set-size", no_argument, 0, 'm' - } - , - { - L"file-descriptor-count", no_argument, 0, 'n' - } - , - { - L"stack-size", no_argument, 0, 's' - } - , - { - L"cpu-time", no_argument, 0, 't' - } - , - { - L"process-count", no_argument, 0, 'u' - } - , - { - L"virtual-memory-size", no_argument, 0, 'v' - } - , - { - L"help", no_argument, 0, 'h' - } - , - { - 0, 0, 0, 0 - } - } - ; - - - int opt_index = 0; - - int opt = wgetopt_long( argc, - argv, - L"aHScdflmnstuvh", - long_options, - &opt_index ); - if( opt == -1 ) - break; - - switch( opt ) + while (1) { - 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 ); + 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 + } + } + ; - return 1; - case L'a': - report_all=1; - break; + int opt_index = 0; - case L'H': - hard=1; - break; + int opt = wgetopt_long(argc, + argv, + L"aHScdflmnstuvh", + long_options, + &opt_index); + if (opt == -1) + break; - case L'S': - soft=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); - case L'c': - what=RLIMIT_CORE; - break; + return 1; - case L'd': - what=RLIMIT_DATA; - break; + case L'a': + report_all=1; + break; - case L'f': - what=RLIMIT_FSIZE; - break; + case L'H': + hard=1; + 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; + case L'm': + what=RLIMIT_RSS; + break; #endif - case L'n': - what=RLIMIT_NOFILE; - break; + case L'n': + what=RLIMIT_NOFILE; + break; - case L's': - what=RLIMIT_STACK; - break; + case L's': + what=RLIMIT_STACK; + break; - case L't': - what=RLIMIT_CPU; - break; + case L't': + what=RLIMIT_CPU; + break; #ifdef RLIMIT_NPROC - case L'u': - what=RLIMIT_NPROC; - break; + case L'u': + what=RLIMIT_NPROC; + break; #endif #ifdef RLIMIT_AS - case L'v': - what=RLIMIT_AS; - break; + case L'v': + what=RLIMIT_AS; + break; #endif - case L'h': - builtin_print_help( parser, argv[0], stdout_buffer ); - return 0; + 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'?': + builtin_unknown_option(parser, argv[0], argv[woptind-1]); + return 1; + } } - } - if( report_all ) - { - if( argc - woptind == 0 ) - { - print_all( hard ); - } - else + 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; } - return 0; - } - - switch( argc - woptind ) - { + switch (argc - woptind) + { case 0: { - /* - Show current limit value - */ - print( what, hard ); - break; + /* + Show current limit value + */ + print(what, hard); + break; } case 1: { - /* - Change current limit value - */ - rlim_t new_limit; - wchar_t *end; + /* + 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 ) + /* + Set both hard and soft limits if nothing else was specified + */ + if (!(hard+soft)) { - append_format(stderr_buffer, - L"%ls: Invalid limit '%ls'\n", - argv[0], - argv[woptind] ); - builtin_print_help( parser, argv[0], stderr_buffer ); - return 1; + hard=soft=1; } - new_limit *= get_multiplier( what ); - } - return set( what, hard, soft, new_limit ); + 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; + 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; + } + return 0; } diff --git a/color.cpp b/color.cpp index f590bcd89..f0926b271 100644 --- a/color.cpp +++ b/color.cpp @@ -4,40 +4,73 @@ #include "color.h" #include "fallback.h" -bool rgb_color_t::try_parse_special(const wcstring &special) { +bool rgb_color_t::try_parse_special(const wcstring &special) +{ bzero(&data, sizeof data); const wchar_t *name = special.c_str(); - if (! wcscasecmp(name, L"normal")) { + if (! wcscasecmp(name, L"normal")) + { this->type = type_normal; - } else if (! wcscasecmp(name, L"reset")) { + } + else if (! wcscasecmp(name, L"reset")) + { this->type = type_reset; - } else if (! wcscasecmp(name, L"ignore")) { + } + else if (! wcscasecmp(name, L"ignore")) + { this->type = type_ignore; - } else { + } + else + { this->type = type_none; } return this->type != type_none; } -static int parse_hex_digit(wchar_t x) { - switch (x) { - case L'0': return 0x0; - case L'1': return 0x1; - case L'2': return 0x2; - case L'3': return 0x3; - case L'4': return 0x4; - case L'5': return 0x5; - case L'6': return 0x6; - case L'7': return 0x7; - case L'8': return 0x8; - case L'9': return 0x9; - case L'a':case L'A': return 0xA; - case L'b':case L'B': return 0xB; - case L'c':case L'C': return 0xC; - case L'd':case L'D': return 0xD; - case L'e':case L'E': return 0xE; - case L'f':case L'F': return 0xF; - default: return -1; +static int parse_hex_digit(wchar_t x) +{ + switch (x) + { + case L'0': + return 0x0; + case L'1': + return 0x1; + case L'2': + return 0x2; + case L'3': + return 0x3; + case L'4': + return 0x4; + case L'5': + return 0x5; + case L'6': + return 0x6; + case L'7': + return 0x7; + case L'8': + return 0x8; + case L'9': + return 0x9; + case L'a': + case L'A': + return 0xA; + case L'b': + case L'B': + return 0xB; + case L'c': + case L'C': + return 0xC; + case L'd': + case L'D': + return 0xD; + case L'e': + case L'E': + return 0xE; + case L'f': + case L'F': + return 0xF; + default: + return -1; } } @@ -47,15 +80,18 @@ static unsigned long squared_difference(long p1, long p2) return diff * diff; } -static unsigned char convert_color(const unsigned char rgb[3], const uint32_t *colors, size_t color_count) { +static unsigned char convert_color(const unsigned char rgb[3], const uint32_t *colors, size_t color_count) +{ long r = rgb[0], g = rgb[1], b = rgb[2]; unsigned long best_distance = (unsigned long)(-1); unsigned char best_index = (unsigned char)(-1); - for (unsigned char idx = 0; idx < color_count; idx++) { + for (unsigned char idx = 0; idx < color_count; idx++) + { uint32_t color = colors[idx]; long test_r = (color >> 16) & 0xFF, test_g = (color >> 8) & 0xFF, test_b = (color >> 0) & 0xFF; unsigned long distance = squared_difference(r, test_r) + squared_difference(g, test_g) + squared_difference(b, test_b); - if (distance <= best_distance) { + if (distance <= best_distance) + { best_index = idx; best_distance = distance; } @@ -64,7 +100,8 @@ static unsigned char convert_color(const unsigned char rgb[3], const uint32_t *c } -bool rgb_color_t::try_parse_rgb(const wcstring &name) { +bool rgb_color_t::try_parse_rgb(const wcstring &name) +{ bzero(&data, sizeof data); /* We support the following style of rgb formats (case insensitive): #FA3 @@ -81,17 +118,22 @@ bool rgb_color_t::try_parse_rgb(const wcstring &name) { bool success = false; size_t i; - if (len - digit_idx == 3) { + if (len - digit_idx == 3) + { // type FA3 - for (i=0; i < 3; i++) { + for (i=0; i < 3; i++) + { int val = parse_hex_digit(name.at(digit_idx++)); if (val < 0) break; data.rgb[i] = val*16+val; } success = (i == 3); - } else if (len - digit_idx == 6) { + } + else if (len - digit_idx == 6) + { // type F3A035 - for (i=0; i < 3; i++) { + for (i=0; i < 3; i++) + { int hi = parse_hex_digit(name.at(digit_idx++)); int lo = parse_hex_digit(name.at(digit_idx++)); if (lo < 0 || hi < 0) break; @@ -99,19 +141,22 @@ bool rgb_color_t::try_parse_rgb(const wcstring &name) { } success = (i == 3); } - if (success) { + if (success) + { this->type = type_rgb; } return success; } -struct named_color_t { +struct named_color_t +{ const wchar_t * name; unsigned char idx; unsigned char rgb[3]; }; -static const named_color_t named_colors[11] = { +static const named_color_t named_colors[11] = +{ {L"black", 0, {0, 0, 0}}, {L"red", 1, {0xFF, 0, 0}}, {L"green", 2, {0, 0xFF, 0}}, @@ -125,11 +170,14 @@ static const named_color_t named_colors[11] = { {L"normal", 8, {0xFF, 0xFF, 0XFF}} }; -bool rgb_color_t::try_parse_named(const wcstring &str) { +bool rgb_color_t::try_parse_named(const wcstring &str) +{ bzero(&data, sizeof data); size_t max = sizeof named_colors / sizeof *named_colors; - for (size_t idx=0; idx < max; idx++) { - if (0 == wcscasecmp(str.c_str(), named_colors[idx].name)) { + for (size_t idx=0; idx < max; idx++) + { + if (0 == wcscasecmp(str.c_str(), named_colors[idx].name)) + { data.name_idx = named_colors[idx].idx; this->type = type_named; return true; @@ -138,29 +186,53 @@ bool rgb_color_t::try_parse_named(const wcstring &str) { return false; } -static const wchar_t *name_for_color_idx(unsigned char idx) { +static const wchar_t *name_for_color_idx(unsigned char idx) +{ size_t max = sizeof named_colors / sizeof *named_colors; - for (size_t i=0; i < max; i++) { - if (named_colors[i].idx == idx) { + for (size_t i=0; i < max; i++) + { + if (named_colors[i].idx == idx) + { return named_colors[i].name; } } return L"unknown"; } -rgb_color_t::rgb_color_t(unsigned char t, unsigned char i) : type(t), flags(), data() { +rgb_color_t::rgb_color_t(unsigned char t, unsigned char i) : type(t), flags(), data() +{ data.name_idx = i; } -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); } -rgb_color_t rgb_color_t::white() { return rgb_color_t(type_named, 7); } -rgb_color_t rgb_color_t::black() { return rgb_color_t(type_named, 0); } +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); +} +rgb_color_t rgb_color_t::white() +{ + return rgb_color_t(type_named, 7); +} +rgb_color_t rgb_color_t::black() +{ + return rgb_color_t(type_named, 0); +} -static unsigned char term8_color_for_rgb(const unsigned char rgb[3]) { - const uint32_t kColors[] = { +static unsigned char term8_color_for_rgb(const unsigned char rgb[3]) +{ + const uint32_t kColors[] = + { 0x000000, //Black 0xFF0000, //Red 0x00FF00, //Green @@ -173,8 +245,10 @@ static unsigned char term8_color_for_rgb(const unsigned char rgb[3]) { return convert_color(rgb, kColors, sizeof kColors / sizeof *kColors); } -static unsigned char term256_color_for_rgb(const unsigned char rgb[3]) { - const uint32_t kColors[240] = { +static unsigned char term256_color_for_rgb(const unsigned char rgb[3]) +{ + const uint32_t kColors[240] = + { 0x000000, 0x00005f, 0x000087, 0x0000af, 0x0000d7, 0x0000ff, 0x005f00, 0x005f5f, 0x005f87, 0x005faf, 0x005fd7, 0x005fff, 0x008700, 0x00875f, 0x008787, 0x0087af, 0x0087d7, 0x0087ff, 0x00af00, 0x00af5f, 0x00af87, 0x00afaf, 0x00afd7, 0x00afff, @@ -209,58 +283,71 @@ static unsigned char term256_color_for_rgb(const unsigned char rgb[3]) { return 16 + convert_color(rgb, kColors, sizeof kColors / sizeof *kColors); } -unsigned char rgb_color_t::to_term256_index() const { +unsigned char rgb_color_t::to_term256_index() const +{ assert(type == type_rgb); return term256_color_for_rgb(data.rgb); } -unsigned char rgb_color_t::to_name_index() const { +unsigned char rgb_color_t::to_name_index() const +{ assert(type == type_named || type == type_rgb); - if (type == type_named) { + if (type == type_named) + { return data.name_idx; - } else if (type == type_rgb) { + } + else if (type == type_rgb) + { return term8_color_for_rgb(data.rgb); - } else { + } + else + { /* This is an error */ return (unsigned char)(-1); } } -void rgb_color_t::parse(const wcstring &str) { +void rgb_color_t::parse(const wcstring &str) +{ bool success = false; if (! success) success = try_parse_special(str); if (! success) success = try_parse_named(str); if (! success) success = try_parse_rgb(str); - if (! success) { + if (! success) + { bzero(this->data.rgb, sizeof this->data.rgb); this->type = type_none; } } -rgb_color_t::rgb_color_t(const wcstring &str) { +rgb_color_t::rgb_color_t(const wcstring &str) +{ this->parse(str); } -rgb_color_t::rgb_color_t(const std::string &str) { +rgb_color_t::rgb_color_t(const std::string &str) +{ this->parse(str2wcstring(str)); } -wcstring rgb_color_t::description() const { - switch (type) { - case type_none: - return L"none"; - case type_named: - return format_string(L"named(%d: %ls)", (int)data.name_idx, name_for_color_idx(data.name_idx)); - case type_rgb: - return format_string(L"rgb(0x%02x%02x%02x)", data.rgb[0], data.rgb[1], data.rgb[2]); - case type_reset: - return L"reset"; - case type_normal: - return L"normal"; - case type_ignore: - return L"ignore"; - default: - abort(); - return L""; +wcstring rgb_color_t::description() const +{ + switch (type) + { + case type_none: + return L"none"; + case type_named: + return format_string(L"named(%d: %ls)", (int)data.name_idx, name_for_color_idx(data.name_idx)); + case type_rgb: + return format_string(L"rgb(0x%02x%02x%02x)", data.rgb[0], data.rgb[1], data.rgb[2]); + case type_reset: + return L"reset"; + case type_normal: + return L"normal"; + case type_ignore: + return L"ignore"; + default: + abort(); + return L""; } } diff --git a/color.h b/color.h index bcfbfcd0a..f95efb29d 100644 --- a/color.h +++ b/color.h @@ -10,10 +10,12 @@ /* A type that represents a color. We work hard to keep it at a size of 4 bytes. */ -class rgb_color_t { +class rgb_color_t +{ /* Types */ - enum { + enum + { type_none, type_named, type_rgb, @@ -24,13 +26,15 @@ class rgb_color_t { unsigned char type:4; /* Flags */ - enum { + enum + { flag_bold = 1 << 0, flag_underline = 1 << 1 }; unsigned char flags:4; - union { + union + { unsigned char name_idx; //0-10 unsigned char rgb[3]; } data; @@ -50,7 +54,7 @@ class rgb_color_t { /** Private constructor */ explicit rgb_color_t(unsigned char t, unsigned char i=0); - public: +public: /** Default constructor of type none */ explicit rgb_color_t() : type(type_none), flags(), data() {} @@ -78,25 +82,46 @@ class rgb_color_t { static rgb_color_t none(); /** Returns whether the color is the ignore special color */ - bool is_ignore(void) const { return type == type_ignore; } + bool is_ignore(void) const + { + return type == type_ignore; + } /** Returns whether the color is the normal special color */ - bool is_normal(void) const { return type == type_normal; } + bool is_normal(void) const + { + return type == type_normal; + } /** Returns whether the color is the reset special color */ - bool is_reset(void) const { return type == type_reset; } + bool is_reset(void) const + { + return type == type_reset; + } /** Returns whether the color is the none special color */ - bool is_none(void) const { return type == type_none; } + bool is_none(void) const + { + return type == type_none; + } /** Returns whether the color is a named color (like "magenta") */ - bool is_named(void) const { return type == type_named; } + bool is_named(void) const + { + return type == type_named; + } /** Returns whether the color is specified via RGB components */ - bool is_rgb(void) const { return type == type_rgb; } + bool is_rgb(void) const + { + return type == type_rgb; + } /** Returns whether the color is special, that is, not rgb or named */ - bool is_special(void) const { return type != type_named && type != type_rgb; } + bool is_special(void) const + { + return type != type_named && type != type_rgb; + } /** Returns a description of the color */ wcstring description() const; @@ -108,24 +133,40 @@ class rgb_color_t { unsigned char to_term256_index() const; /** Returns whether the color is bold */ - bool is_bold() const { return flags & flag_bold; } + bool is_bold() const + { + return flags & flag_bold; + } /** Set whether the color is bold */ - void set_bold(bool x) { if (x) flags |= flag_bold; else flags &= ~flag_bold; } + void set_bold(bool x) + { + if (x) flags |= flag_bold; + else flags &= ~flag_bold; + } /** Returns whether the color is underlined */ - bool is_underline() const { return !! (flags & flag_underline); } + 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; } + 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 { + 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 { + bool operator!=(const rgb_color_t &other) const + { return !(*this == other); } }; diff --git a/common.cpp b/common.cpp index fce8c459e..9147805a3 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,81 +102,81 @@ 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; - } - } + c = getwc(f); + + if (errno == EILSEQ) + { + continue; + } + + switch (c) + { + /* End of line */ + case WEOF: + case L'\n': + case L'\0': + return i; + /* Ignore carriage returns */ + case L'\r': + break; + + default: + i++; + s->push_back((wchar_t)c); + break; + } + } } -wchar_t *str2wcs( const char *in ) +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 ) +wcstring str2wcstring(const char *in) { wchar_t *tmp = str2wcs(in); wcstring result = tmp; @@ -184,7 +184,7 @@ wcstring str2wcstring( const char *in ) return result; } -wcstring str2wcstring( const std::string &in ) +wcstring str2wcstring(const std::string &in) { wchar_t *tmp = str2wcs(in.c_str()); wcstring result = tmp; @@ -192,97 +192,103 @@ wcstring str2wcstring( const std::string &in ) return result; } -wchar_t *str2wcs_internal( const char *in, wchar_t *out ) +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 ) +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) { + if (desired_size <= sizeof local_buff / sizeof *local_buff) + { // convert into local buff, then use strdup() so we don't waste malloc'd space char *result = wcs2str_internal(in, local_buff); - if (result) { + if (result) + { // It converted into the local buffer, so copy it result = strdup(result); - if (! result) { + if (! result) + { DIE_MEM(); } } return result; - - } else { + + } + 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 ); - if (!out) { + out = (char *)malloc(MAX_UTF8_BYTES*wcslen(in)+1); + if (!out) + { DIE_MEM(); } - return wcs2str_internal( in, out ); + return wcs2str_internal(in, out); } - return wcs2str_internal( in, out ); + return wcs2str_internal(in, out); } std::string wcs2string(const wcstring &input) @@ -293,87 +299,87 @@ std::string wcs2string(const wcstring &input) return result; } -char *wcs2str_internal( const wchar_t *in, char *out ) +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 ) +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= max_size) { + if (size >= max_size) + { buff[0] = '\0'; break; } - buff = (wchar_t *)realloc( (buff == static_buff ? NULL : buff), size); - if (buff == NULL) { + buff = (wchar_t *)realloc((buff == static_buff ? NULL : buff), size); + if (buff == NULL) + { DIE_MEM(); } } - + /* Try printing */ - va_list va; - va_copy( va, va_orig ); + va_list va; + va_copy(va, va_orig); status = vswprintf(buff, size / sizeof(wchar_t), format, va); - va_end(va); + va_end(va); } - + wcstring result = wcstring(buff); - + if (buff != static_buff) free(buff); - + errno = saved_err; return result; } @@ -424,234 +436,237 @@ void append_format(wcstring &str, const wchar_t *format, ...) { /* Preserve errno across this call since it likes to stomp on it */ int err = errno; - va_list va; - va_start( va, format ); + va_list va; + va_start(va, format); str.append(vformat_string(format, va)); - va_end( va ); + va_end(va); errno = err; } -wchar_t *wcsvarname( const wchar_t *str ) +wchar_t *wcsvarname(const wchar_t *str) { - while( *str ) - { - if( (!iswalnum(*str)) && (*str != L'_' ) ) - { - return (wchar_t *)str; - } - str++; - } - return 0; + while (*str) + { + if ((!iswalnum(*str)) && (*str != L'_')) + { + return (wchar_t *)str; + } + str++; + } + return 0; } -const wchar_t *wcsfuncname( const wchar_t *str ) +const wchar_t *wcsfuncname(const wchar_t *str) { - return wcschr( str, L'/' ); + return wcschr(str, L'/'); } -int wcsvarchr( wchar_t chr ) +int wcsvarchr(wchar_t chr) { - return iswalnum(chr) || chr == L'_'; + return iswalnum(chr) || chr == L'_'; } -/** +/** The glibc version of wcswidth seems to hang on some strings. fish uses this replacement. */ -int my_wcswidth( const wchar_t *c ) +int my_wcswidth(const wchar_t *c) { - return fish_wcswidth(c, wcslen(c)); + return fish_wcswidth(c, wcslen(c)); } -wchar_t *quote_end( const wchar_t *pos ) +wchar_t *quote_end(const wchar_t *pos) { - wchar_t c = *pos; - - while( 1 ) - { - pos++; - - if( !*pos ) - return 0; - - if( *pos == L'\\') - { - pos++; - if( !*pos ) - return 0; - } - else - { - if( *pos == c ) - { - return (wchar_t *)pos; - } - } - } - return 0; - + wchar_t c = *pos; + + while (1) + { + pos++; + + if (!*pos) + return 0; + + if (*pos == L'\\') + { + pos++; + if (!*pos) + return 0; + } + else + { + if (*pos == c) + { + return (wchar_t *)pos; + } + } + } + return 0; + } - + wcstring wsetlocale(int category, const wchar_t *locale) { - char *lang = NULL; - if (locale){ - lang = wcs2str( locale ); - } - char * res = setlocale(category,lang); - free( lang ); + char *lang = NULL; + if (locale) + { + lang = wcs2str(locale); + } + char * res = setlocale(category,lang); + free(lang); - /* - Use ellipsis if on known unicode system, otherwise use $ - */ - char *ctype = setlocale( LC_CTYPE, NULL ); - ellipsis_char = (strstr( ctype, ".UTF")||strstr( ctype, ".utf") )?L'\x2026':L'$'; - - if( !res ) - return wcstring(); + /* + Use ellipsis if on known unicode system, otherwise use $ + */ + char *ctype = setlocale(LC_CTYPE, NULL); + ellipsis_char = (strstr(ctype, ".UTF")||strstr(ctype, ".utf"))?L'\x2026':L'$'; + + if (!res) + return wcstring(); else - return format_string(L"%s", res); + return format_string(L"%s", res); } -bool contains_internal( const wchar_t *a, ... ) +bool contains_internal(const wchar_t *a, ...) { - const wchar_t *arg; - va_list va; - bool res = false; + const wchar_t *arg; + va_list va; + bool res = false; - CHECK( a, 0 ); - - va_start( va, a ); - while( (arg=va_arg(va, const wchar_t *) )!= 0 ) - { - if( wcscmp( a,arg) == 0 ) - { - res = true; - break; - } - - } - va_end( va ); - return res; + CHECK(a, 0); + + va_start(va, a); + while ((arg=va_arg(va, const wchar_t *))!= 0) + { + if (wcscmp(a,arg) == 0) + { + res = true; + break; + } + + } + va_end(va); + return res; } /* wcstring variant of contains_internal. The first parameter is a wcstring, the rest are const wchar_t* */ -__sentinel bool contains_internal( const wcstring &needle, ... ) +__sentinel bool contains_internal(const wcstring &needle, ...) { - const wchar_t *arg; - va_list va; - int res = 0; + const wchar_t *arg; + va_list va; + int res = 0; - va_start( va, needle ); - while( (arg=va_arg(va, const wchar_t *) )!= 0 ) - { - if( needle == arg) - { - res=1; - break; - } - - } - va_end( va ); - return res; + va_start(va, needle); + while ((arg=va_arg(va, const wchar_t *))!= 0) + { + if (needle == arg) + { + res=1; + break; + } + + } + va_end(va); + return res; } long read_blocked(int fd, void *buf, size_t count) { - ssize_t res; - sigset_t chldset, oldset; + ssize_t res; + sigset_t chldset, oldset; - sigemptyset( &chldset ); - sigaddset( &chldset, SIGCHLD ); - VOMIT_ON_FAILURE(pthread_sigmask(SIG_BLOCK, &chldset, &oldset)); - res = read( fd, buf, count ); - VOMIT_ON_FAILURE(pthread_sigmask(SIG_SETMASK, &oldset, NULL)); - return res; + sigemptyset(&chldset); + sigaddset(&chldset, SIGCHLD); + VOMIT_ON_FAILURE(pthread_sigmask(SIG_BLOCK, &chldset, &oldset)); + res = read(fd, buf, count); + VOMIT_ON_FAILURE(pthread_sigmask(SIG_SETMASK, &oldset, NULL)); + return res; } ssize_t write_loop(int fd, const char *buff, size_t count) { - 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 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) { ssize_t result; - do { + do + { result = read(fd, buff, count); - } while (result < 0 && (errno == EAGAIN || errno == EINTR)); + } + while (result < 0 && (errno == EAGAIN || errno == EINTR)); return result; } 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)") ) + if (program_name && ! wcscmp(program_name, L"(ignore)")) return false; - + return true; } -static void debug_shared( const wcstring &msg ) +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, ... ) +void debug(int level, const wchar_t *msg, ...) { if (! should_debug(level)) 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; } -void debug( int level, const char *msg, ... ) +void debug(int level, const char *msg, ...) { if (! should_debug(level)) return; 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,55 +677,66 @@ 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') { + 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') { + 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; - } else if (end[0] == '\0') { + } + else if (end[0] == '\0') + { /* Must be at the end of the string */ cursor = end; - } else { + } + else + { /* Some other format specifier, just skip it */ cursor = end + 1; } } - + // We always append a newline write(STDERR_FILENO, "\n", 1); - + errno = errno_old; } -void format_long_safe(char buff[128], long val) { - if (val == 0) { +void format_long_safe(char buff[128], long val) +{ + if (val == 0) + { strcpy(buff, "0"); - } else { + } + else + { /* 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) { + while (val != 0) + { long rem = val % 10; buff[idx++] = '0' + (rem < 0 ? -rem : rem); val /= 10; @@ -718,9 +744,10 @@ 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) { + while (left < right) + { char tmp = buff[left]; buff[left++] = buff[right]; buff[right--] = tmp; @@ -728,15 +755,20 @@ void format_long_safe(char buff[128], long val) { } } -void format_long_safe(wchar_t buff[128], long val) { - if (val == 0) { +void format_long_safe(wchar_t buff[128], long val) +{ + if (val == 0) + { wcscpy(buff, L"0"); - } else { + } + else + { /* Generate the string in reverse */ size_t idx = 0; bool negative = (val < 0); - while (val > 0) { + while (val > 0) + { long rem = val % 10; /* Here we're assuming that wide character digits are contiguous - is that a correct assumption? */ buff[idx++] = L'0' + (wchar_t)(rem < 0 ? -rem : rem); @@ -745,9 +777,10 @@ 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) { + while (left < right) + { wchar_t tmp = buff[left]; buff[left++] = buff[right]; buff[right--] = tmp; @@ -755,92 +788,92 @@ void format_long_safe(wchar_t buff[128], long val) { } } -void write_screen( const wcstring &msg, wcstring &buff ) +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; - } - 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 - { + 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 + { buff.append(msg); - } + } buff.push_back(L'\n'); } @@ -849,754 +882,756 @@ void write_screen( const wcstring &msg, wcstring &buff ) the string has already been checked for characters that can not be escaped this way. */ -static wchar_t *escape_simple( const wchar_t *in ) +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 ) +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); - bool no_tilde = !! (flags & ESCAPE_NO_TILDE); - - wchar_t *out; - wchar_t *pos; + const wchar_t *in = in_orig; - int need_escape=0; - int need_complex_escape=0; + bool escape_all = !!(flags & ESCAPE_ALL); + bool no_quoted = !!(flags & ESCAPE_NO_QUOTED); + bool no_tilde = !!(flags & ESCAPE_NO_TILDE); - if( !in ) - { - debug( 0, L"%s called with null input", __func__ ); - FATAL_EXIT(); - } + wchar_t *out; + wchar_t *pos; - 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 ) - { + int need_escape=0; + int need_complex_escape=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 - { + if (!in) + { + debug(0, L"%s called with null input", __func__); + FATAL_EXIT(); + } + + 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'~': - { - if (! no_tilde || c != 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) { - need_escape=1; - if( escape_all ) - *pos++ = L'\\'; + *(pos++) = L'\\'; + *(pos++) = L'c'; + *(pos++) = L'a' + *in -1; + + need_escape=need_complex_escape=1; + break; + } - *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; - - } - - 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; + 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 ) { +wcstring escape_string(const wcstring &in, escape_flags_t flags) +{ wchar_t *tmp = escape(in.c_str(), flags); wcstring result(tmp); free(tmp); return result; } -wchar_t *unescape( const wchar_t * orig, int 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; - // 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; - } + for (in_pos=0, out_pos=0; + in_pos=0)?in[out_pos]:0), out_pos++, in_pos++) + { + c = in[in_pos]; + switch (mode) + { - /* - \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; - } + /* + Mode 0 means unquoted string + */ + case 0: + { + if (c == L'\\') + { + switch (in[++in_pos]) + { - /* - \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; - } + /* + A null character after a backslash is an + error, return null + */ + case L'\0': + { + if (!allow_incomplete) + { + 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; - } + /* + Numeric escape sequences. No prefix means + octal escape, otherwise hexadecimal. + */ - 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'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; - case L'?': - { - if( unescape_special ) - { - in[out_pos]=ANY_CHAR; - } - else - { - in[out_pos]=in[in_pos]; - } - break; - } + int byte = 0; + wchar_t max_val = ASCII_MAX; - case L'$': - { - if( unescape_special ) - { - in[out_pos]=VARIABLE_EXPAND; - } - else - { - in[out_pos]=in[in_pos]; - } - break; - } + switch (in[in_pos]) + { + case L'u': + { + chars=4; + max_val = UCS2_MAX; + 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; - } + case L'U': + { + chars=8; + max_val = WCHAR_MAX; + break; + } - default: - { - in[out_pos] = in[in_pos]; - break; - } - } - } - break; - } + case L'x': + { + 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; - } + case L'X': + { + byte=1; + max_val = BYTE_MAX; + 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: + { + base=8; + chars=3; + // note in_pod must be larger than 0 since we incremented it above + assert(in_pos > 0); + 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; - } - } - } + 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; + } + + /* + \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; + } + + /* + \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) { bool success = false; wchar_t *result = unescape(str.c_str(), escape_special); - if (result) { + if (result) + { str.replace(str.begin(), str.end(), result); free(result); success = true; @@ -1606,34 +1641,35 @@ bool unescape_string(wcstring &str, int escape_special) -void common_handle_winch( int signal ) +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) +void tokenize_variable_array(const wcstring &val, std::vector &out) { size_t pos = 0, end = val.size(); - while (pos < end) { + while (pos < end) + { size_t next_pos = val.find(ARRAY_SEP, pos); if (next_pos == wcstring::npos) break; out.push_back(val.substr(pos, next_pos - pos)); @@ -1655,17 +1691,20 @@ bool string_prefixes_string(const wcstring &proposed_prefix, const wcstring &val return prefix_size <= value.size() && value.compare(0, prefix_size, proposed_prefix) == 0; } -bool string_prefixes_string_case_insensitive(const wcstring &proposed_prefix, const wcstring &value) { +bool string_prefixes_string_case_insensitive(const wcstring &proposed_prefix, const wcstring &value) +{ size_t prefix_size = proposed_prefix.size(); return prefix_size <= value.size() && wcsncasecmp(proposed_prefix.c_str(), value.c_str(), prefix_size) == 0; } -bool string_suffixes_string(const wcstring &proposed_suffix, const wcstring &value) { +bool string_suffixes_string(const wcstring &proposed_suffix, const wcstring &value) +{ size_t suffix_size = proposed_suffix.size(); return suffix_size <= value.size() && value.compare(value.size() - suffix_size, suffix_size, proposed_suffix) == 0; } -bool string_suffixes_string(const wchar_t *proposed_suffix, const wcstring &value) { +bool string_suffixes_string(const wchar_t *proposed_suffix, const wcstring &value) +{ size_t suffix_size = wcslen(proposed_suffix); return suffix_size <= value.size() && value.compare(value.size() - suffix_size, suffix_size, proposed_suffix) == 0; } @@ -1675,98 +1714,101 @@ bool list_contains_string(const wcstring_list_t &list, const wcstring &str) return std::find(list.begin(), list.end(), str) != list.end(); } -int create_directory( const wcstring &d ) +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; } /* Crappy function to extract the most significant digit of an unsigned long long value */ -static char extract_most_significant_digit(unsigned long long *xp) { +static char extract_most_significant_digit(unsigned long long *xp) +{ unsigned long long place_value = 1; unsigned long long x = *xp; - while (x >= 10) { + while (x >= 10) + { x /= 10; place_value *= 10; } @@ -1774,29 +1816,33 @@ static char extract_most_significant_digit(unsigned long long *xp) { return x + '0'; } -void append_ull(char *buff, unsigned long long val, size_t *inout_idx, size_t max_len) { +void append_ull(char *buff, unsigned long long val, size_t *inout_idx, size_t max_len) +{ size_t idx = *inout_idx; while (val > 0 && idx < max_len) buff[idx++] = extract_most_significant_digit(&val); *inout_idx = idx; } -void append_str(char *buff, const char *str, size_t *inout_idx, size_t max_len) { +void append_str(char *buff, const char *str, size_t *inout_idx, size_t max_len) +{ size_t idx = *inout_idx; while (*str && idx < max_len) buff[idx++] = *str++; *inout_idx = idx; } -void format_size_safe(char buff[128], unsigned long long sz) { +void format_size_safe(char buff[128], unsigned long long sz) +{ const size_t buff_size = 128; 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 +1853,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 +1872,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,47 +1882,50 @@ 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) { +void exit_without_destructors(int code) +{ _exit(code); } /* Helper function to convert from a null_terminated_array_t to a null_terminated_array_t */ -null_terminated_array_t convert_wide_array_to_narrow(const null_terminated_array_t &wide_arr) { +null_terminated_array_t convert_wide_array_to_narrow(const null_terminated_array_t &wide_arr) +{ 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++) { + for (size_t i=0; arr[i]; i++) + { list.push_back(wcs2string(arr[i])); } return null_terminated_array_t(list); @@ -1884,16 +1933,22 @@ null_terminated_array_t convert_wide_array_to_narrow(const null_terminated void append_path_component(wcstring &path, const wcstring &component) { - if (path.empty() || component.empty()) { + if (path.empty() || component.empty()) + { path.append(component); - } else { + } + else + { size_t path_len = path.size(); bool path_slash = path.at(path_len-1) == L'/'; bool comp_slash = component.at(0) == L'/'; - if (! path_slash && ! comp_slash) { + if (! path_slash && ! comp_slash) + { // Need a slash path.push_back(L'/'); - } else if (path_slash && comp_slash) { + } + else if (path_slash && comp_slash) + { // Too many slashes path.erase(path_len - 1, 1); } @@ -1902,15 +1957,20 @@ void append_path_component(wcstring &path, const wcstring &component) } extern "C" { -__attribute__((noinline)) void debug_thread_error(void) { while (1) sleep(9999999); } + __attribute__((noinline)) void debug_thread_error(void) + { + while (1) sleep(9999999); + } } - -void set_main_thread() { - main_thread_id = pthread_self(); + +void set_main_thread() +{ + main_thread_id = pthread_self(); } -void configure_thread_assertions_for_testing(void) { +void configure_thread_assertions_for_testing(void) +{ thread_assertions_configured_for_testing = true; } @@ -1920,12 +1980,14 @@ static pid_t initial_pid = 0; /* Be able to restore the term's foreground process group */ static pid_t initial_foreground_process_group = -1; -bool is_forked_child(void) { +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) { + if (is_child_of_fork) + { printf("Uh-oh: %d\n", getpid()); while (1) sleep(10000); } @@ -1951,14 +2013,16 @@ void restore_term_foreground_process_group(void) } } -bool is_main_thread() { - assert (main_thread_id != 0); - return main_thread_id == pthread_self(); +bool is_main_thread() +{ + assert(main_thread_id != 0); + return main_thread_id == pthread_self(); } void assert_is_main_thread(const char *who) { - if (! is_main_thread() && ! thread_assertions_configured_for_testing) { + if (! is_main_thread() && ! thread_assertions_configured_for_testing) + { fprintf(stderr, "Warning: %s called off of main thread. Break on debug_thread_error to debug.\n", who); debug_thread_error(); } @@ -1966,7 +2030,8 @@ void assert_is_main_thread(const char *who) void assert_is_not_forked_child(const char *who) { - if (is_forked_child()) { + if (is_forked_child()) + { fprintf(stderr, "Warning: %s called in a forked child. Break on debug_thread_error to debug.\n", who); debug_thread_error(); } @@ -1974,7 +2039,8 @@ void assert_is_not_forked_child(const char *who) void assert_is_background_thread(const char *who) { - if (is_main_thread() && ! thread_assertions_configured_for_testing) { + if (is_main_thread() && ! thread_assertions_configured_for_testing) + { fprintf(stderr, "Warning: %s called on the main thread (may block!). Break on debug_thread_error to debug.\n", who); debug_thread_error(); } @@ -1983,32 +2049,37 @@ void assert_is_background_thread(const char *who) void assert_is_locked(void *vmutex, const char *who, const char *caller) { pthread_mutex_t *mutex = static_cast(vmutex); - if (0 == pthread_mutex_trylock(mutex)) { + if (0 == pthread_mutex_trylock(mutex)) + { fprintf(stderr, "Warning: %s is not locked when it should be in '%s'. Break on debug_thread_error to debug.\n", who, caller); debug_thread_error(); pthread_mutex_unlock(mutex); } } -void scoped_lock::lock(void) { +void scoped_lock::lock(void) +{ assert(! locked); assert(! is_forked_child()); VOMIT_ON_FAILURE(pthread_mutex_lock(lock_obj)); locked = true; } -void scoped_lock::unlock(void) { +void scoped_lock::unlock(void) +{ assert(locked); assert(! is_forked_child()); VOMIT_ON_FAILURE(pthread_mutex_unlock(lock_obj)); locked = false; } -scoped_lock::scoped_lock(pthread_mutex_t &mutex) : lock_obj(&mutex), locked(false) { +scoped_lock::scoped_lock(pthread_mutex_t &mutex) : lock_obj(&mutex), locked(false) +{ this->lock(); } -scoped_lock::~scoped_lock() { +scoped_lock::~scoped_lock() +{ if (locked) this->unlock(); } @@ -2023,13 +2094,15 @@ wcstokenizer::wcstokenizer(const wcstring &s, const wcstring &separator) : state = NULL; } -bool wcstokenizer::next(wcstring &result) { +bool wcstokenizer::next(wcstring &result) +{ wchar_t *tmp = wcstok(str, sep.c_str(), &state); str = NULL; if (tmp) result = tmp; return tmp != NULL; } - -wcstokenizer::~wcstokenizer() { + +wcstokenizer::~wcstokenizer() +{ free(buffer); } diff --git a/common.h b/common.h index 9cb574886..b9eb4ddce 100644 --- a/common.h +++ b/common.h @@ -64,13 +64,14 @@ typedef std::vector wcstring_list_t; #define UNESCAPE_INCOMPLETE 2 /* Flags for the escape() and escape_string() functions */ -enum { +enum +{ /** Escape all characters, including magic characters like the semicolon */ - ESCAPE_ALL = 1 << 0, - + ESCAPE_ALL = 1 << 0, + /** Do not try to use 'simplified' quoted escapes, and do not use empty quotes as the empty string */ ESCAPE_NO_QUOTED = 1 << 1, - + /** Do not escape tildes */ ESCAPE_NO_TILDE = 1 << 2 }; @@ -82,12 +83,12 @@ typedef unsigned int escape_flags_t; #define VOMIT_ON_FAILURE(a) do { if (0 != (a)) { int err = errno; fprintf(stderr, "%s failed on line %d in file %s: %d (%s)\n", #a, __LINE__, __FILE__, err, strerror(err)); abort(); }} while (0) /** Exits without invoking destructors (via _exit), useful for code after fork. */ -void exit_without_destructors(int code) __attribute__ ((noreturn)); +void exit_without_destructors(int code) __attribute__((noreturn)); -/** +/** Save the shell mode on startup so we can restore them on exit */ -extern struct termios shell_modes; +extern struct termios shell_modes; /** The character to use where the text has been truncated. Is an @@ -119,7 +120,7 @@ extern const wchar_t *program_name; parameter is the return value of the current function on failiure. */ #define CHECK( arg, retval ) \ - if( !(arg) ) \ + if (!(arg)) \ { \ debug( 0, \ "function %s called with null value for argument %s. ", \ @@ -140,7 +141,7 @@ extern const wchar_t *program_name; read( 0, &exit_read_buff, 1 ); \ exit_without_destructors( 1 ); \ } \ - + /** Exit program at once, leaving an error message about running out of memory. @@ -158,8 +159,8 @@ extern const wchar_t *program_name; 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() ) \ +#define CHECK_BLOCK(retval) \ + if (signal_is_blocked()) \ { \ debug( 0, \ "function %s called while blocking signals. ", \ @@ -168,7 +169,7 @@ extern const wchar_t *program_name; show_stackframe(); \ return retval; \ } - + /** Shorthand for wgettext call */ @@ -176,7 +177,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 +193,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 @@ -209,17 +210,17 @@ int fgetws2(wcstring *s, FILE *f); This function encodes illegal character sequences in a reversible way using the private use area. */ -wchar_t *str2wcs( const char *in ); +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. */ -wcstring str2wcstring( const char *in ); -wcstring str2wcstring( const std::string &in ); +wcstring str2wcstring(const char *in); +wcstring str2wcstring(const std::string &in); /** Converts the narrow character string \c in into it's wide @@ -229,7 +230,7 @@ wcstring str2wcstring( const std::string &in ); This function encodes illegal character sequences in a reversible way using the private use area. */ -wchar_t *str2wcs_internal( const char *in, wchar_t *out ); +wchar_t *str2wcs_internal(const char *in, wchar_t *out); /** Returns a newly allocated multibyte character string equivalent of @@ -238,7 +239,7 @@ wchar_t *str2wcs_internal( const char *in, wchar_t *out ); This function decodes illegal character sequences in a reversible way using the private use area. */ -char *wcs2str( const wchar_t *in ); +char *wcs2str(const wchar_t *in); std::string wcs2string(const wcstring &input); /** Test if a string prefixes another. Returns true if a is a prefix of b */ @@ -277,7 +278,7 @@ void assert_is_locked(void *mutex, const char *who, const char *caller); This function decodes illegal character sequences in a reversible way using the private use area. */ -char *wcs2str_internal( const wchar_t *in, char *out ); +char *wcs2str_internal(const wchar_t *in, char *out); /** Format the specified size (in bytes, kilobytes, etc.) into the specified stringbuffer. */ wcstring format_size(long long sz); @@ -294,7 +295,8 @@ void format_long_safe(wchar_t buff[128], long val); template -T from_string(const wcstring &x) { +T from_string(const wcstring &x) +{ T result; std::wstringstream stream(x); stream >> result; @@ -302,7 +304,8 @@ T from_string(const wcstring &x) { } template -T from_string(const std::string &x) { +T from_string(const std::string &x) +{ T result = T(); std::stringstream stream(x); stream >> result; @@ -310,7 +313,8 @@ T from_string(const std::string &x) { } template -wcstring to_string(const T &x) { +wcstring to_string(const T &x) +{ std::wstringstream stream; stream << x; return stream.str(); @@ -318,86 +322,109 @@ wcstring to_string(const T &x) { /* wstringstream is a huge memory pig. Let's provide some specializations where we can. */ template<> -inline wcstring to_string(const long &x) { +inline wcstring to_string(const long &x) +{ wchar_t buff[128]; format_long_safe(buff, x); return wcstring(buff); } template<> -inline bool from_string(const std::string &x) { +inline bool from_string(const std::string &x) +{ return ! x.empty() && strchr("YTyt1", x.at(0)); } template<> -inline bool from_string(const wcstring &x) { +inline bool from_string(const wcstring &x) +{ return ! x.empty() && wcschr(L"YTyt1", x.at(0)); } template<> -inline wcstring to_string(const int &x) { +inline wcstring to_string(const int &x) +{ return to_string(static_cast(x)); } /* Helper class for managing a null-terminated array of null-terminated strings (of some char type) */ template -class null_terminated_array_t { +class null_terminated_array_t +{ CharType_t **array; - + typedef std::basic_string string_t; typedef std::vector string_list_t; - void swap(null_terminated_array_t &him) { std::swap(array, him.array); } + void swap(null_terminated_array_t &him) + { + std::swap(array, him.array); + } /* Silly function to get the length of a null terminated array of...something */ template - static size_t count_not_null(const T *arr) { + static size_t count_not_null(const T *arr) + { size_t len; for (len=0; arr[len] != T(0); len++) ; - return len; + return len; } - size_t size() const { + size_t size() const + { return count_not_null(array); } - void free(void) { - if (array != NULL) { - for (size_t i = 0; array[i] != NULL; i++) { + void free(void) + { + if (array != NULL) + { + for (size_t i = 0; array[i] != NULL; i++) + { delete [] array[i]; } delete [] array; array = NULL; } } - - public: + +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(); } - + 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) { + 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) { + null_terminated_array_t(const null_terminated_array_t &him) : array(NULL) + { this->set(him.array); } - - void set(const string_list_t &argv) { + + void set(const string_list_t &argv) + { /* Get rid of the old argv */ this->free(); /* Allocate our null-terminated array of null-terminated strings */ size_t i, count = argv.size(); this->array = new CharType_t * [count + 1]; - for (i=0; i < count; i++) { + for (i=0; i < count; i++) + { const string_t &str = argv.at(i); this->array[i] = new CharType_t [1 + str.size()]; std::copy(str.begin(), str.end(), this->array[i]); @@ -405,19 +432,22 @@ class null_terminated_array_t { } this->array[count] = NULL; } - - void set(const CharType_t * const *new_array) { + + 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) { + if (new_array) + { size_t i, count = count_not_null(new_array); this->array = new CharType_t * [count + 1]; - for (i=0; i < count; i++) { + for (i=0; i < count; i++) + { size_t len = count_not_null(new_array[i]); this->array[i] = new CharType_t [1 + len]; std::copy(new_array[i], new_array[i] + len, this->array[i]); @@ -426,13 +456,21 @@ 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 { + + 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) { + if (array != NULL) + { size_t count = this->size(); lst.reserve(count); lst.insert(lst.end(), array, array + count); @@ -445,27 +483,31 @@ class null_terminated_array_t { null_terminated_array_t convert_wide_array_to_narrow(const null_terminated_array_t &arr); /* Helper class to cache a narrow version of a wcstring in a malloc'd buffer, so that we can read it after fork() */ -class narrow_string_rep_t { - private: +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() { + +public: + ~narrow_string_rep_t() + { free((void *)str); } - + narrow_string_rep_t() : str(NULL) {} - - void set(const wcstring &s) { + + void set(const wcstring &s) + { free((void *)str); str = wcs2str(s.c_str()); } - - const char *get() const { + + const char *get() const + { return str; } }; @@ -473,10 +515,11 @@ class narrow_string_rep_t { bool is_forked_child(); /* Basic scoped lock class */ -class scoped_lock { +class scoped_lock +{ pthread_mutex_t *lock_obj; bool locked; - + /* No copying */ scoped_lock &operator=(const scoped_lock &); scoped_lock(const scoped_lock &); @@ -489,21 +532,22 @@ public: }; /* Wrapper around wcstok */ -class wcstokenizer { +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); @@ -516,38 +560,38 @@ void append_format(wcstring &str, const wchar_t *format, ...); Returns a newly allocated wide character string array equivalent of the specified multibyte character string array */ -char **wcsv2strv( const wchar_t * const *in ); +char **wcsv2strv(const wchar_t * const *in); /** - Test if the given string is a valid variable name. + Test if the given string is a valid variable name. \return null if this is a valid name, and a pointer to the first invalid character otherwise */ -wchar_t *wcsvarname( const wchar_t *str ); +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 */ -const wchar_t *wcsfuncname( const wchar_t *str ); +const wchar_t *wcsfuncname(const wchar_t *str); /** - Test if the given string is valid in a variable name + Test if the given string is valid in a variable name \return 1 if this is a valid name, 0 otherwise */ -int wcsvarchr( wchar_t chr ); +int wcsvarchr(wchar_t chr); /** A wcswidth workalike. Fish uses this since the regular wcswidth seems flaky. */ -int my_wcswidth( const wchar_t *c ); +int my_wcswidth(const wchar_t *c); /** This functions returns the end of the quoted substring beginning at @@ -556,7 +600,7 @@ int my_wcswidth( const wchar_t *c ); \param in the position of the opening quote */ -wchar_t *quote_end( const wchar_t *in ); +wchar_t *quote_end(const wchar_t *in); /** A call to this function will reset the error counter. Some @@ -572,19 +616,19 @@ 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 ); +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 */ -__sentinel bool contains_internal( const wchar_t *needle, ... ); -__sentinel bool contains_internal( const wcstring &needle, ... ); +__sentinel bool contains_internal(const wchar_t *needle, ...); +__sentinel bool contains_internal(const wcstring &needle, ...); /** Call read while blocking the SIGCHLD signal. Should only be called @@ -614,9 +658,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: @@ -624,20 +668,20 @@ ssize_t read_loop(int fd, void *buff, size_t count); will print the string 'fish: Pi = 3.141', given that debug_level is 1 or higher, and that program_name is 'fish'. */ -void debug( int level, const char *msg, ... ); -void debug( int level, const wchar_t *msg, ... ); +void debug(int level, const char *msg, ...); +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 \return The escaped string, or 0 if there is not enough memory */ -wchar_t *escape( const wchar_t *in, escape_flags_t flags ); -wcstring escape_string( const wcstring &in, escape_flags_t flags ); +wchar_t *escape(const wchar_t *in, escape_flags_t flags); +wcstring escape_string(const wcstring &in, escape_flags_t flags); /** Expand backslashed escapes and substitute them with their unescaped @@ -650,14 +694,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, - int escape_special ); +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. @@ -679,20 +723,20 @@ int common_get_height(); saving it in an internal variable used by common_get_wisth and common_get_height(). */ -void common_handle_winch( int signal ); +void common_handle_winch(int signal); /** Write paragraph of output to the specified stringbuffer, and redo the linebreaks to fit the current screen. */ -void write_screen( const wcstring &msg, wcstring &buff ); +void write_screen(const wcstring &msg, wcstring &buff); /** - Tokenize the specified string into the specified wcstring_list_t. + Tokenize the specified string into the specified wcstring_list_t. \param val the input string. The contents of this string is not changed. - \param out the list in which to place the elements. + \param out the list in which to place the elements. */ -void tokenize_variable_array( const wcstring &val, wcstring_list_t &out); +void tokenize_variable_array(const wcstring &val, wcstring_list_t &out); /** Make sure the specified direcotry exists. If needed, try to create @@ -700,7 +744,7 @@ void tokenize_variable_array( const wcstring &val, wcstring_list_t &out); \return 0 if, at the time of function return the directory exists, -1 otherwise. */ -int create_directory( const wcstring &d ); +int create_directory(const wcstring &d); /** Print a short message about how to file a bug report to stderr @@ -740,7 +784,7 @@ void assert_is_not_forked_child(const char *who); #define ASSERT_IS_NOT_FORKED_CHILD() ASSERT_IS_NOT_FORKED_CHILD_TRAMPOLINE(__FUNCTION__) extern "C" { -__attribute__((noinline)) void debug_thread_error(void); + __attribute__((noinline)) void debug_thread_error(void); } diff --git a/complete.cpp b/complete.cpp index 8418c15bd..77018810d 100644 --- a/complete.cpp +++ b/complete.cpp @@ -127,24 +127,24 @@ */ 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 { @@ -163,22 +163,22 @@ typedef std::list option_list_t; class completion_entry_t { public: - /** List of all options */ - option_list_t options; + /** List of all options */ + option_list_t options; - /** String containing all short option characters */ - wcstring short_opt_str; + /** String containing all short option characters */ + wcstring short_opt_str; - public: +public: - /** Command string */ - const wcstring cmd; + /** Command string */ + const wcstring cmd; - /** True if command is a path */ - const bool cmd_is_path; + /** True if command is a path */ + const bool cmd_is_path; - /** True if no other options than the ones supplied are possible */ - bool authoritative; + /** 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; @@ -205,13 +205,18 @@ public: }; /** Set of all completion entries */ -struct completion_entry_set_comparer { +struct completion_entry_set_comparer +{ /** Comparison for std::set */ - bool operator()(completion_entry_t *p1, completion_entry_t *p2) const { + bool operator()(completion_entry_t *p1, completion_entry_t *p2) const + { /* Paths always come last for no particular reason */ - if (p1->cmd_is_path != p2->cmd_is_path) { + if (p1->cmd_is_path != p2->cmd_is_path) + { return p1->cmd_is_path < p2->cmd_is_path; - } else { + } + else + { return p1->cmd < p2->cmd; } } @@ -220,7 +225,8 @@ typedef std::set completion static completion_entry_set_t completion_set; // Comparison function to sort completions by their order field -static bool compare_completions_by_order(const completion_entry_t *p1, const completion_entry_t *p2) { +static bool compare_completions_by_order(const completion_entry_t *p1, const completion_entry_t *p2) +{ return p1->order < p2->order; } @@ -235,22 +241,26 @@ static pthread_mutex_t completion_lock = PTHREAD_MUTEX_INITIALIZER; static pthread_mutex_t completion_entry_lock = PTHREAD_MUTEX_INITIALIZER; -void completion_entry_t::add_option(const complete_entry_opt_t &opt) { +void completion_entry_t::add_option(const complete_entry_opt_t &opt) +{ ASSERT_IS_LOCKED(completion_entry_lock); options.push_front(opt); } -const option_list_t &completion_entry_t::get_options() const { +const option_list_t &completion_entry_t::get_options() const +{ ASSERT_IS_LOCKED(completion_entry_lock); return options; } -wcstring &completion_entry_t::get_short_opt_str() { +wcstring &completion_entry_t::get_short_opt_str() +{ ASSERT_IS_LOCKED(completion_entry_lock); return short_opt_str; } -const wcstring &completion_entry_t::get_short_opt_str() const { +const wcstring &completion_entry_t::get_short_opt_str() const +{ ASSERT_IS_LOCKED(completion_entry_lock); return short_opt_str; } @@ -258,11 +268,11 @@ const wcstring &completion_entry_t::get_short_opt_str() const { /* completion_t functions */ completion_t::completion_t(const wcstring &comp, const wcstring &desc, int flags_val) : completion(comp), description(desc), flags(flags_val) { - if( flags & COMPLETE_AUTO_SPACE ) + if (flags & COMPLETE_AUTO_SPACE) { flags = flags & ~COMPLETE_AUTO_SPACE; size_t len = completion.size(); - if (len > 0 && ( wcschr( L"/=@:", comp.at(len-1)) != 0 )) + if (len > 0 && (wcschr(L"/=@:", comp.at(len-1)) != 0)) flags |= COMPLETE_NO_SPACE; } @@ -295,26 +305,28 @@ bool completion_t::operator == (const completion_t& rhs) const bool completion_t::operator != (const completion_t& rhs) const { - return ! (*this == rhs); + return !(*this == rhs); } -wcstring_list_t completions_to_wcstring_list( const std::vector &list ) +wcstring_list_t completions_to_wcstring_list(const std::vector &list) { wcstring_list_t strings; strings.reserve(list.size()); - for (std::vector::const_iterator iter = list.begin(); iter != list.end(); ++iter) { + for (std::vector::const_iterator iter = list.begin(); iter != list.end(); ++iter) + { strings.push_back(iter->completion); } return strings; } -void sort_completions( std::vector &completions) +void sort_completions(std::vector &completions) { std::sort(completions.begin(), completions.end()); } /** Class representing an attempt to compute completions */ -class completer_t { +class completer_t +{ const complete_type_t type; const wcstring initial_cmd; std::vector completions; @@ -324,51 +336,58 @@ class completer_t { typedef std::map condition_cache_t; condition_cache_t condition_cache; - public: +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 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 ); + bool try_complete_variable(const wcstring &str); + bool try_complete_user(const wcstring &str); - bool complete_param( const wcstring &cmd_orig, - const wcstring &popt, - const wcstring &str, - bool use_switches); + bool complete_param(const wcstring &cmd_orig, + 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_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_from_args(const wcstring &str, + const wcstring &args, + const wcstring &desc, + complete_flags_t flags); - void complete_cmd_desc( const wcstring &str ); + void complete_cmd_desc(const wcstring &str); bool complete_variable(const wcstring &str, size_t start_offset); - bool condition_test( const wcstring &condition ); + 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 ); + 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 { + 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; if (type == COMPLETE_AUTOSUGGEST) @@ -376,7 +395,8 @@ class completer_t { return result; } - void get_commands_to_load(wcstring_list_t *lst) { + void get_commands_to_load(wcstring_list_t *lst) + { if (lst) lst->insert(lst->end(), commands_to_load.begin(), commands_to_load.end()); } @@ -384,7 +404,8 @@ class completer_t { }; /* Autoloader for completions */ -class completion_autoload_t : public autoload_t { +class completion_autoload_t : public autoload_t +{ public: completion_autoload_t(); virtual void command_removed(const wcstring &cmd); @@ -394,14 +415,15 @@ static completion_autoload_t completion_autoloader; /** Constructor */ completion_autoload_t::completion_autoload_t() : autoload_t(L"fish_complete_path", - internal_completion_scripts, - sizeof internal_completion_scripts / sizeof *internal_completion_scripts) + internal_completion_scripts, + sizeof internal_completion_scripts / sizeof *internal_completion_scripts) { } /** Callback when an autoloaded completion is removed */ -void completion_autoload_t::command_removed(const wcstring &cmd) { - complete_remove( cmd.c_str(), COMMAND, 0, 0 ); +void completion_autoload_t::command_removed(const wcstring &cmd) +{ + complete_remove(cmd.c_str(), COMMAND, 0, 0); } @@ -420,13 +442,13 @@ void append_completion(std::vector &completions, const wcstring &c be evaluated once. condition_cache_clear must be called after a completion run to make sure that there are no stale completions. */ -bool completer_t::condition_test( const wcstring &condition ) +bool completer_t::condition_test(const wcstring &condition) { - if( condition.empty() ) - { + if (condition.empty()) + { // fwprintf( stderr, L"No condition specified\n" ); - return 1; - } + return 1; + } if (this->type == COMPLETE_AUTOSUGGEST) { @@ -438,11 +460,14 @@ bool completer_t::condition_test( const wcstring &condition ) bool test_res; condition_cache_t::iterator cached_entry = condition_cache.find(condition); - if (cached_entry == condition_cache.end()) { + if (cached_entry == condition_cache.end()) + { /* Compute new value and reinsert it */ - test_res = (0 == exec_subshell( condition)); + test_res = (0 == exec_subshell(condition)); condition_cache[condition] = test_res; - } else { + } + else + { /* Use the old value */ test_res = cached_entry->second; } @@ -451,92 +476,94 @@ bool completer_t::condition_test( const wcstring &condition ) /** Search for an exactly matching completion entry. Must be called while locked. */ -static completion_entry_t *complete_find_exact_entry( const wcstring &cmd, const bool cmd_is_path ) +static completion_entry_t *complete_find_exact_entry(const wcstring &cmd, const bool cmd_is_path) { ASSERT_IS_LOCKED(completion_lock); completion_entry_t *result = NULL; 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()) { + 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 ) +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 ) +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) { - CHECK( 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) +{ + 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 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); + /* 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); - - c->get_short_opt_str().push_back(short_opt); - if( len == 2 ) + /* Create our new option */ + complete_entry_opt_t opt; + if (short_opt != L'\0') { - c->get_short_opt_str().push_back(L':'); + int len = 1 + ((result_mode & NO_COMMON) != 0); + + c->get_short_opt_str().push_back(short_opt); + 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; + 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; - c->add_option(opt); + c->add_option(opt); } /** @@ -544,28 +571,28 @@ void complete_add(const wchar_t *cmd, specified short / long option strings. Returns true if it is now empty and should be deleted, false if it's not empty. Must be called while locked. */ -bool completion_entry_t::remove_option( wchar_t short_opt, const wchar_t *long_opt ) +bool completion_entry_t::remove_option(wchar_t short_opt, const wchar_t *long_opt) { ASSERT_IS_LOCKED(completion_lock); ASSERT_IS_LOCKED(completion_entry_lock); - if(( short_opt == 0 ) && (long_opt == 0 ) ) - { - this->options.clear(); - } - else - { - for (option_list_t::iterator iter = this->options.begin(); iter != this->options.end(); ) + if ((short_opt == 0) && (long_opt == 0)) { - 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 ) + this->options.clear(); + } + 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) + { wcstring &short_opt_str = this->get_short_opt_str(); size_t idx = short_opt_str.find(o.short_opt); if (idx != wcstring::npos) @@ -576,37 +603,39 @@ 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 ) +void complete_remove(const wchar_t *cmd, + 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()) { + if (iter != completion_set.end()) + { completion_entry_t *entry = *iter; bool delete_it = entry->remove_option(short_opt, long_opt); - if (delete_it) { + if (delete_it) + { /* Delete this entry */ completion_set.erase(iter); delete entry; @@ -615,7 +644,8 @@ void complete_remove( const wchar_t *cmd, } /* Formats an error string by prepending the prefix and then appending the str in single quotes */ -static wcstring format_error(const wchar_t *prefix, const wcstring &str) { +static wcstring format_error(const wchar_t *prefix, const wcstring &str) +{ wcstring result = prefix; result.push_back(L'\''); result.append(str); @@ -626,237 +656,243 @@ static wcstring format_error(const wchar_t *prefix, const wcstring &str) { /** Find the full path and commandname from a command string '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. */ +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. */ 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) { + if (last_slash != wcstring::npos) + { cmd = str.substr(last_slash + 1); - } else { + } + else + { cmd = str; } } -int complete_is_valid_option( const wcstring &str, - const wcstring &opt, - wcstring_list_t *errors, - bool allow_autoload ) +int complete_is_valid_option(const wcstring &str, + 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() ) - { + /* + Check some generic things like -- and - options. + */ + switch (opt.size()) + { case 0: case 1: { - return true; + return true; } case 2: { - if( opt == L"--" ) - { - return true; - } - break; + if (opt == L"--") + { + return true; + } + break; + } } - } - if( opt.at(0) != L'-' ) - { - if( errors ) + 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 ) - { + 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 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; - - if( !wildcard_match( match, i->cmd ) ) - { - continue; - } - - found_match = true; - - if (! i->authoritative) - { - authoritative = false; - break; - } - - const option_list_t &options = i->get_options(); - 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 ) + if (opt_end != wcstring::npos) { - 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 */ - 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( opt.compare(1, wcstring::npos, o.long_opt )==0) - { - opt_found = true; - is_old_opt = true; - 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. - */ - 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; - } - } - } - } - } - - if( authoritative ) - { - - 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; - } - - } - } - - 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: "); + gnu_opt_len = opt_end-2; } else { - prefix = _(L"Multiple matches for option: "); + 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 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; + + if (!wildcard_match(match, i->cmd)) + { + continue; + } + + found_match = true; + + if (! i->authoritative) + { + authoritative = false; + break; + } + + const option_list_t &options = i->get_options(); + 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 (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 */ + 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 (opt.compare(1, wcstring::npos, o.long_opt)==0) + { + opt_found = true; + is_old_opt = true; + 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. + */ + 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; + } + } + } + } + } + + if (authoritative) + { + + 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; + } + + } + } + + 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 + { + prefix = _(L"Multiple matches for option: "); + } + errors->push_back(format_error(prefix, opt)); + } } - 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 ) +bool complete_is_valid_argument(const wcstring &str, const wcstring &opt, const wcstring &arg) { - return true; + return true; } @@ -878,82 +914,82 @@ bool complete_is_valid_argument( const wcstring &str, const wcstring &opt, const \param possible_comp the list of possible completions to iterate over */ -void completer_t::complete_strings( const wcstring &wc_escaped, - const wchar_t *desc, - wcstring (*desc_func)(const wcstring &), - std::vector &possible_comp, - complete_flags_t flags ) +void completer_t::complete_strings(const wcstring &wc_escaped, + const wchar_t *desc, + wcstring(*desc_func)(const wcstring &), + std::vector &possible_comp, + complete_flags_t flags) { 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() ); + 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 ) + for (size_t i=0; i< possible_comp.size(); i++) { - wildcard_complete( next_str, wc, desc, desc_func, this->completions, flags ); - } - } + wcstring temp = possible_comp.at(i).completion; + const wchar_t *next_str = temp.empty()?NULL:temp.c_str(); - free( (void *)wc ); + if (next_str) + { + wildcard_complete(next_str, wc, desc, desc_func, this->completions, flags); + } + } + + free((void *)wc); } /** If command to complete is short enough, substitute the description with the whatis information for the executable. */ -void completer_t::complete_cmd_desc( const wcstring &str ) +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; - } - - 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'/' )) + if (wildcard_has(cmd_start, 0)) { - skip = 0; - break; + return; } - } + skip = 1; - if( skip ) - { - return; - } + 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 "); @@ -961,57 +997,57 @@ void completer_t::complete_cmd_desc( const wcstring &str ) 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. - */ - 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. - - Should be reasonably fast, since no memory allocations are needed. + 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. */ - for( size_t i=0; i < list.size(); i++ ) + 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. + + 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()) @@ -1020,22 +1056,23 @@ void completer_t::complete_cmd_desc( const wcstring &str ) std::map::iterator new_desc_iter = lookup.find(el); if (new_desc_iter != lookup.end()) completion.description = new_desc_iter->second; + } } - } } /** Returns a description for the specified function, or an empty string if none */ -static wcstring complete_function_desc( const wcstring &fn ) +static wcstring complete_function_desc(const wcstring &fn) { wcstring result; bool has_description = function_get_desc(fn, &result); - if (! has_description) { + if (! has_description) + { function_get_definition(fn, &result); } - return result; + return result; } @@ -1048,13 +1085,13 @@ static wcstring complete_function_desc( const wcstring &fn ) \param comp the list to add all completions to */ -void completer_t::complete_cmd( const wcstring &str_cmd, bool use_function, bool use_builtin, bool use_command) +void completer_t::complete_cmd(const wcstring &str_cmd, bool use_function, bool use_builtin, bool use_command) { /* 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()) @@ -1063,31 +1100,32 @@ void completer_t::complete_cmd( const wcstring &str_cmd, bool use_function, bool 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 ); + 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 ) + else { + if (use_command) + { - const env_var_t path = env_get_string(L"PATH"); - if( !path.missing() ) - { + 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; @@ -1099,51 +1137,52 @@ 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 ) - { + 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()); - } + c.completion.erase(0, base_path.size()); + } + } + } + } + if (wants_description) + this->complete_cmd_desc(str_cmd); } - } } - if (wants_description) - this->complete_cmd_desc( str_cmd ); - } - } - /* - These return the original strings - don't free them - */ + /* + 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++) { + 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 ); + 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); + } + } - - possible_comp.clear(); - - if( use_builtin ) - { - builtin_get_names( possible_comp ); - this->complete_strings( str_cmd, 0, &builtin_get_desc, possible_comp, 0 ); - } - - } } @@ -1159,13 +1198,13 @@ void completer_t::complete_cmd( const wcstring &str_cmd, bool use_function, bool \param desc Description of the completion \param comp_out The list into which the results will be inserted */ -void completer_t::complete_from_args( const wcstring &str, - const wcstring &args, - const wcstring &desc, - complete_flags_t flags ) +void completer_t::complete_from_args(const wcstring &str, + const wcstring &args, + 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); @@ -1174,107 +1213,107 @@ void completer_t::complete_from_args( const wcstring &str, 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 ) +static int param_match_old(const complete_entry_opt_t *e, + 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 ) +static int param_match(const complete_entry_opt_t *e, + 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]) + if (!e->old_mode && (wcsncmp(L"--", optstr, 2) == 0)) { - return 1; + 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 ) +static wchar_t *param_match2(const complete_entry_opt_t *e, + 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( wcsncmp( e->long_opt.c_str(), &optstr[2],len ) == 0 ) + 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)) { - if( optstr[len+2] == L'=' ) - return (wchar_t *)&optstr[len+3]; + 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; + return 0; } /** Tests whether a short option is a viable completion */ -static int short_ok( const wcstring &arg_str, wchar_t nextopt, const wcstring &allopt_str ) +static int short_ok(const wcstring &arg_str, wchar_t nextopt, const wcstring &allopt_str) { 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 ) + for (ptr = arg+1; *ptr; ptr++) { - /*fwprintf( stderr, L"Unknown option %lc", *ptr );*/ + const wchar_t *tmp = wcschr(allopt, *ptr); + /* Unknown option */ + if (tmp == 0) + { + /*fwprintf( stderr, L"Unknown option %lc", *ptr );*/ + + return 0; + } + + if (*(tmp+1) == L':') + { + /* fwprintf( stderr, L"Woot %ls", allopt );*/ + return 0; + } - 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 ) +void complete_load(const wcstring &name, bool reload) { - completion_autoloader.load( name, reload ); + completion_autoloader.load(name, reload); } /** @@ -1282,23 +1321,24 @@ void complete_load( const wcstring &name, bool reload ) previous option popt. Insert results into comp_out. Return 0 if file completion should be disabled, 1 otherwise. */ -struct local_options_t { +struct local_options_t +{ wcstring short_opt_str; option_list_t options; }; -bool completer_t::complete_param( const wcstring &scmd_orig, const wcstring &spopt, const wcstring &sstr, bool use_switches) +bool completer_t::complete_param(const wcstring &scmd_orig, const wcstring &spopt, const wcstring &sstr, bool use_switches) { 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); if (this->type == COMPLETE_DEFAULT) { - complete_load( cmd, true ); + complete_load(cmd, true); } else if (this->type == COMPLETE_AUTOSUGGEST) { @@ -1336,125 +1376,125 @@ bool completer_t::complete_param( const wcstring &scmd_orig, const wcstring &spo 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) */ - for (option_list_t::const_iterator oiter = options.begin(); oiter != options.end(); ++oiter) + use_common=1; + if (use_switches) { - const complete_entry_opt_t *o = &*oiter; - wchar_t *arg; - if( (arg=param_match2( o, str ))!=0 && this->condition_test( o->condition )) - { + + 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)) + { 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 - */ - 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 )) + } + } + else if (popt[0] == L'-') { - old_style_match = 1; + /* 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->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 ); - } - } - } + 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 ) - { + /* + 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 ((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; + 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 ); + 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"--"); @@ -1462,78 +1502,78 @@ bool completer_t::complete_param( const wcstring &scmd_orig, const wcstring &spo 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 = 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 */ + + size_t offset = 0; + complete_flags_t flags = 0; + + + 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); + } + } + } } - - 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 */ - - size_t offset = 0; - complete_flags_t flags = 0; - - - 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; + return use_files; } /** Perform file completion on the specified string */ -void completer_t::complete_param_expand( const wcstring &sstr, bool do_file) +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; + const wchar_t *comp_str; - if (string_prefixes_string( L"--", sstr) && (comp_str = wcschr(str, L'=' ) ) ) - { - comp_str++; - } - else - { - comp_str = 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; @@ -1544,17 +1584,18 @@ void completer_t::complete_param_expand( const wcstring &sstr, bool do_file) 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() { - for (size_t i=0; i < completions.size(); i++) { + for (size_t i=0; i < completions.size(); i++) + { printf("- Completion: %ls\n", completions.at(i).completion.c_str()); } } @@ -1565,34 +1606,34 @@ 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; - - match = string_prefixes_string(var, env_name); - - if( !match ) + for (size_t i=0; i namelen) + continue; + + match = string_prefixes_string(var, env_name); + + 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 ) + if (match) { comp.append(env_name.c_str() + varlen); } @@ -1606,22 +1647,22 @@ bool completer_t::complete_variable(const wcstring &str, size_t start_offset) wcstring desc; if (wants_description) { - env_var_t value_unescaped = env_get_string( env_name ); + env_var_t value_unescaped = env_get_string(env_name); if (value_unescaped.missing()) continue; - wcstring value = expand_escape_variable( value_unescaped ); + 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 ); + append_completion(this->completions, comp.c_str(), desc.c_str(), flags); res =1; + } } - } - return res; + return res; } /** @@ -1630,23 +1671,23 @@ bool completer_t::complete_variable(const wcstring &str, size_t start_offset) \return 0 if unable to complete, 1 otherwise */ -bool completer_t::try_complete_variable( const wcstring &str ) +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 (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; + } } - if( !isalnum(c) && c != L'_' ) - { - return false; - } - } - return false; + return false; } /** @@ -1655,303 +1696,303 @@ bool completer_t::try_complete_variable( const wcstring &str ) \return 0 if unable to complete, 1 otherwise */ -bool completer_t::try_complete_user( 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(); + 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 ) + if (*first_char ==L'~' && !wcschr(first_char, L'/')) { - struct passwd *pw; - size_t name_len = wcslen( user_name ); - - setpwent(); - - while((pw=getpwent()) != 0) - { - double current_time = timef(); - wchar_t *pw_name; - - if( current_time - start_time > 0.2 ) + const wchar_t *user_name = first_char+1; + const wchar_t *name_end = wcschr(user_name, L'~'); + if (name_end == 0) { - return 1; - } + struct passwd *pw; + size_t name_len = wcslen(user_name); - pw_name = str2wcs( pw->pw_name ); + setpwent(); - if( pw_name ) - { - if( wcsncmp( user_name, pw_name, name_len )==0 ) - { + 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, - &pw_name[name_len], - desc, - COMPLETE_NO_SPACE ); + 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); + 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 ); + append_completion(this->completions, + name, + desc, + COMPLETE_NO_CASE | COMPLETE_DONT_ESCAPE | COMPLETE_NO_SPACE); + res=1; + } + free(pw_name); + } + } + endpwent(); } - } - endpwent(); } - } - return res; + return res; } -void complete( const wcstring &cmd, std::vector &comps, complete_type_t type, wcstring_list_t *commands_to_load ) +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; + 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; + 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 ) - { - 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 ); - - int had_cmd=0; - int end_loop=0; - - tok_init( &tok, buff.c_str(), TOK_ACCEPT_UNFINISHED | TOK_SQUASH_ERRORS ); - - while( tok_has_next( &tok) && !end_loop ) - { - - switch( tok_last_type( &tok ) ) - { - - case TOK_STRING: - { - - const wcstring ncmd = tok_last( &tok ); - int is_ddash = (ncmd == L"--") && ( (tok_get_pos( &tok )+2) < (long)pos ); - - if( !had_cmd ) - { - - 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( !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; - } - - } - 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( tok_get_pos( &tok ) >= (long)pos ) - { - end_loop=1; - } - - tok_next( &tok ); - - } - - tok_destroy( &tok ); - - /* - Get the string to complete + /** + If we are completing a variable name or a tilde expansion user + name, we do that and return. No need for any other competions. */ - current_token = wcsndup( tok_begin, cursor_pos-(tok_begin-cmd_cstr) ); + if (!done) + { + wcstring tmp = tok_begin; + done = completer.try_complete_variable(tmp) || completer.try_complete_user(tmp); + } - prev_token = prev_begin ? wcsndup( prev_begin, prev_end - prev_begin ): wcsdup(L""); + if (!done) + { + pos = cursor_pos-(cmdsubst_begin-cmd_cstr); + + wcstring buff = wcstring(cmdsubst_begin, cmdsubst_end-cmdsubst_begin); + + int had_cmd=0; + int end_loop=0; + + tok_init(&tok, buff.c_str(), TOK_ACCEPT_UNFINISHED | TOK_SQUASH_ERRORS); + + while (tok_has_next(&tok) && !end_loop) + { + + switch (tok_last_type(&tok)) + { + + case TOK_STRING: + { + + const wcstring ncmd = tok_last(&tok); + int is_ddash = (ncmd == L"--") && ((tok_get_pos(&tok)+2) < (long)pos); + + if (!had_cmd) + { + + 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 (!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; + } + + } + 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 (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. - */ + /* + 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"; + 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; - } + had_cmd = 1; + on_command = 0; + } - /* - Use command completions if in between commands - */ - if( !had_cmd ) - { - on_command=1; - } + /* + Use command completions if in between commands + */ + if (!had_cmd) + { + on_command=1; + } - /* - We don't want these to be null - */ + /* + We don't want these to be null + */ - if( !current_token ) - { - current_token = wcsdup(L""); - } + if (!current_token) + { + current_token = wcsdup(L""); + } - if( !prev_token ) - { - prev_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 ) && - 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, - !had_ddash ); - } + 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, + !had_ddash); + } /* - If we have found no command specific completions at - all, fall back to using file completions. - */ - if( completer.empty() ) - do_file = 1; + 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 ); - } + /* + This function wants the unescaped string + */ + completer.complete_param_expand(current_token, do_file); + } + } } - } - free( (void *)current_token ); - free( (void *)prev_token ); + free((void *)current_token); + free((void *)prev_token); - comps = completer.get_completions(); + comps = completer.get_completions(); completer.get_commands_to_load(commands_to_load); } @@ -1962,18 +2003,18 @@ void complete( const wcstring &cmd, std::vector &comps, complete_t argument to the specified stringbuffer, but only if arguemnt is non-null and longer than 0 characters. */ -static void append_switch( wcstring &out, - const wcstring &opt, - const wcstring &argument ) +static void append_switch(wcstring &out, + 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() ); + wcstring esc = escape_string(argument, 1); + append_format(out, L" --%ls %ls", opt.c_str(), esc.c_str()); } -void complete_print( wcstring &out ) +void complete_print(wcstring &out) { scoped_lock locker(completion_lock); scoped_lock locker2(completion_entry_lock); @@ -1989,49 +2030,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_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); + } + + + 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"arguments", + o->comp); + + append_switch(out, + L"condition", + o->condition); + + out.append(L"\n"); } - ; - - append_format( out, - L"complete%ls", - modestr[o->result_mode] ); - - 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 ); - } - - - 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"arguments", - o->comp ); - - append_switch( out, - L"condition", - o->condition ); - - out.append( L"\n" ); } - } } diff --git a/complete.h b/complete.h index b74d0e21c..7797eb83f 100644 --- a/complete.h +++ b/complete.h @@ -67,7 +67,8 @@ */ #define PROG_COMPLETE_SEP L'\t' -enum { +enum +{ /** Do not insert space afterwards if this is the only completion. (The default is to try insert a space) @@ -109,32 +110,35 @@ 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); } + 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); @@ -142,21 +146,22 @@ public: 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 { +enum complete_type_t +{ COMPLETE_DEFAULT, COMPLETE_AUTOSUGGEST }; /** Given a list of completions, returns a list of their completion fields */ -wcstring_list_t completions_to_wcstring_list( const std::vector &completions ); +wcstring_list_t completions_to_wcstring_list(const std::vector &completions); /** Sorts a list of completions */ -void sort_completions( std::vector &completions); +void sort_completions(std::vector &completions); /** @@ -203,30 +208,30 @@ void sort_completions( std::vector &completions); If \c condition is empty, the completion is always used. \param flags A set of completion flags */ -void complete_add( const wchar_t *cmd, - bool cmd_is_path, - wchar_t short_opt, - const wchar_t *long_opt, - int long_mode, - int result_mode, - const wchar_t *condition, - const wchar_t *comp, - const wchar_t *desc, - int flags ); +void complete_add(const wchar_t *cmd, + bool cmd_is_path, + wchar_t short_opt, + const wchar_t *long_opt, + int long_mode, + int result_mode, + const wchar_t *condition, + const wchar_t *comp, + const wchar_t *desc, + 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 flagged as an error by syntax highlighting. */ -void complete_set_authoritative( const wchar_t *cmd, bool cmd_type, bool authoritative ); +void complete_set_authoritative(const wchar_t *cmd, bool cmd_type, bool authoritative); /** Remove a previously defined completion */ -void complete_remove( const wchar_t *cmd, - bool cmd_is_path, - wchar_t short_opt, - const wchar_t *long_opt ); +void complete_remove(const wchar_t *cmd, + bool cmd_is_path, + wchar_t short_opt, + const wchar_t *long_opt); /** Find all completions of the command cmd, insert them into out. If to_load is @@ -243,23 +248,23 @@ void complete(const wcstring &cmd, \param out The string to write completions to */ -void complete_print( wcstring &out ); +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 ); +int complete_is_valid_option(const wcstring &str, + 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 ); +bool complete_is_valid_argument(const wcstring &str, + const wcstring &opt, + const wcstring &arg); /** @@ -273,7 +278,7 @@ bool complete_is_valid_argument( const wcstring &str, previously loaded. (This is set to true on actual completions, so that changed completion are updated in running shells) */ -void complete_load( const wcstring &cmd, bool reload ); +void complete_load(const wcstring &cmd, bool reload); /** Create a new completion entry diff --git a/env.cpp b/env.cpp index f6b097ce1..3c12da348 100644 --- a/env.cpp +++ b/env.cpp @@ -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,32 +109,33 @@ 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; + /** + 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; + /** + 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 { +class variable_entry_t +{ bool exportv; /**< Whether the variable should be exported */ wcstring value; /**< Value of the variable */ }; @@ -216,15 +217,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,15 +238,15 @@ static const wchar_t * const locale_variable[] = */ static void start_fishd() { - struct passwd *pw = getpwuid(getuid()); + struct passwd *pw = getpwuid(getuid()); - debug( 3, L"Spawning new copy of fishd" ); + debug(3, L"Spawning new copy of fishd"); - if( !pw ) - { - debug( 0, _( L"Could not get user information" ) ); - return; - } + if (!pw) + { + debug(0, _(L"Could not get user information")); + return; + } wcstring cmd = format_string(FISHD_CMD, pw->pw_name); @@ -262,7 +263,7 @@ static void start_fishd() } parser_t &parser = parser_t::principal_parser(); - parser.eval( cmd, io_chain_t(), TOP ); + parser.eval(cmd, io_chain_t(), TOP); } /** @@ -270,20 +271,23 @@ 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; +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; + return false; } /** @@ -291,82 +295,88 @@ 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 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] ); - - if( !val.missing() ) - { - wsetlocale( cat[i], val.c_str() ); - } - } - } - - const wcstring new_locale = wsetlocale(LC_MESSAGES, NULL); - if( old_locale != new_locale ) - { + const env_var_t lc_all = env_get_string(L"LC_ALL"); + int i; + const wcstring old_locale = wsetlocale(LC_MESSAGES, NULL); /* - 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. + Array of locale constants corresponding to the local variable names defined in locale_variable */ - - extern int _nl_msg_cat_cntr; - _nl_msg_cat_cntr++; - - dcgettext( "fish", "Changing language to English", LC_MESSAGES ); - - if( get_is_interactive() ) + static const int cat[] = { - debug( 0, _(L"Changing language to English") ); + 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()); + } + } + } + + const wcstring new_locale = wsetlocale(LC_MESSAGES, NULL); + 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. + */ + + 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")); + } } - } } /** React to modifying hte given variable */ -static void react_to_variable_change(const wcstring &key) { - if(var_is_locale(key)){ +static void react_to_variable_change(const wcstring &key) +{ + if (var_is_locale(key)) + { handle_locale(); - } else if (key == L"fish_term256") { + } + else if (key == L"fish_term256") + { update_fish_term256(); reader_react_to_color_change(); - } else if (string_prefixes_string(L"fish_color_", key)) { + } + else if (string_prefixes_string(L"fish_color_", key)) + { reader_react_to_color_change(); } } @@ -375,43 +385,43 @@ static void react_to_variable_change(const wcstring &key) { Universal variable callback function. This function makes sure the 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 ) +static void universal_callback(fish_message_type_t type, + const wchar_t *name, + const wchar_t *val) { - const wchar_t *str=0; + const wchar_t *str=0; - switch( type ) - { + switch (type) + { case SET: case SET_EXPORT: { - str=L"SET"; - break; + str=L"SET"; + break; } case ERASE: { - str=L"ERASE"; - break; + str=L"ERASE"; + break; } default: break; - } + } - if( str ) - { - mark_changed_exported(); + 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); @@ -423,80 +433,80 @@ static void universal_callback( fish_message_type_t type, static void setup_path() { size_t i; - int j; - wcstring_list_t lst; + int j; + wcstring_list_t lst; - const wchar_t *path_el[] = + const wchar_t *path_el[] = { L"/bin", L"/usr/bin", PREFIX L"/bin", 0 } - ; + ; - env_var_t path = env_get_string( L"PATH" ); + 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; - } + tokenize_variable_array(path, lst); } - if( !has_el ) + for (j=0; path_el[j]; j++) { - wcstring buffer; - debug( 3, L"directory %ls was missing", path_el[j] ); + int has_el=0; - if( !path.missing() ) - { + 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()) + { buffer += path; - } + } buffer += ARRAY_SEP_STR; buffer += path_el[j]; - env_set( L"PATH", buffer.empty()?NULL:buffer.c_str(), ENV_GLOBAL | ENV_EXPORT ); + env_set(L"PATH", buffer.empty()?NULL:buffer.c_str(), ENV_GLOBAL | ENV_EXPORT); - path = env_get_string( L"PATH" ); + 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,50 +515,55 @@ 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. -static bool variable_can_be_array(const wchar_t *key) { - if (! wcscmp(key, L"DISPLAY")) { +static bool variable_can_be_array(const wchar_t *key) +{ + if (! wcscmp(key, L"DISPLAY")) + { return false; - } else { + } + else + { return true; } } void env_init(const struct config_paths_t *paths /* or NULL */) { - char **p; - struct passwd *pw; - wchar_t *uname; - wchar_t *version; + char **p; + struct passwd *pw; + wchar_t *uname; + wchar_t *version; - /* - env_read_only variables can not be altered directly by the user - */ + /* + env_read_only variables can not be altered directly by the user + */ - const wchar_t * const ro_keys[] = { + const wchar_t * const ro_keys[] = + { L"status", L"history", L"version", @@ -559,74 +574,78 @@ void env_init(const struct config_paths_t *paths /* or NULL */) L"SHLVL", L"FISH_VERSION", }; - for (size_t i=0; i < sizeof ro_keys / sizeof *ro_keys; i++) { + 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 - */ + /* + 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; + 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 - */ + /* + 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 ) + /* + Import environment variables + */ + for (p=environ?environ:__environ; p && *p; p++) { - continue; - } + wchar_t *key, *val; - val = wcschr( key, L'=' ); + key = str2wcs(*p); - if( val == 0 ) - { - env_set( key, L"", ENV_EXPORT ); - } - else - { - *val = L'\0'; - val++; + if (!key) + { + continue; + } + + val = wcschr(key, L'='); + + 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++) { - if( val[i] == L':' ) { + if (variable_can_be_array(val)) + { + for (size_t i=0; val[i] != L'\0'; i++) + { + if (val[i] == L':') + { val[i] = ARRAY_SEP; } } } - env_set( key, val, ENV_EXPORT | ENV_GLOBAL ); + env_set(key, val, ENV_EXPORT | ENV_GLOBAL); + } + free(key); } - free(key); - } /* Set the given paths in the environment, if we have any */ if (paths != NULL) @@ -637,57 +656,57 @@ void env_init(const struct config_paths_t *paths /* or NULL */) env_set(FISH_BIN_DIR, paths->bin.c_str(), ENV_GLOBAL | ENV_EXPORT); } - /* - Set up the PATH variable - */ - setup_path(); + /* + 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 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 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 version variables + */ + version = str2wcs(PACKAGE_VERSION); + env_set(L"version", version, ENV_GLOBAL); + env_set(L"FISH_VERSION", version, ENV_GLOBAL); + free(version); - 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" ); + 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"); - 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()); + 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()); - env_universal_init(fishd_dir , user_dir , - &start_fishd, - &universal_callback ); + 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" ); + /* + 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 ); + } + 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"); @@ -700,253 +719,255 @@ void env_init(const struct config_paths_t *paths /* or NULL */) void env_destroy() { - env_universal_destroy(); + env_universal_destroy(); - while( &top->env != global ) - { - env_pop(); - } + 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 ) + var_table_t::iterator iter; + for (iter = global->begin(); iter != global->end(); ++iter) { - mark_changed_exported(); + var_entry_t *entry = iter->second; + if (entry->exportv) + { + mark_changed_exported(); + } + + delete entry; } - delete entry; - } - - delete top; + delete top; } /** Search all visible scopes in order for the specified key. Return the first scope in which it was found. */ -static env_node_t *env_get_node( const wcstring &key ) +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() ) + env_node_t *env = top; + while (env != NULL) { - break; - } + if (env->env.find(key) != env->env.end()) + { + break; + } - if( env->new_scope ) - { - env = global_env; + if (env->new_scope) + { + env = global_env; + } + else + { + env = env->next; + } } - else - { - env = env->next; - } - } - return env; + 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; + 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; + int is_universal = 0; - if( val && contains( key, L"PWD", L"HOME" ) ) - { + 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 (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 ((var_mode & ENV_USER) && is_read_only(key)) + { + return ENV_PERM; + } - if (key == L"umask") - { - wchar_t *end; + 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); + } + } + /* + Do not actually create a umask variable, on env_get, it will + be calculated dynamically + */ + return 0; + } /* - Set the new umask - */ - if( val && wcslen(val) ) + Zero element arrays are internaly not coded as null but as this + placeholder string + */ + if (!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; - } - - /* - 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 ) && - !(var_mode & ENV_UNEXPORT ) ) - { - env_universal_get_export( key ); - } - else - { - exportv = (var_mode & ENV_EXPORT ); + val = ENV_NULL; } - env_universal_set(key, val, exportv); - is_universal = 1; - - } - else - { - - node = env_get_node( key ); - if( node ) + if (var_mode & ENV_UNIVERSAL) { - var_table_t::iterator result = node->env.find(key); - assert(result != node->env.end()); - e = result->second; + int exportv = 0; - 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 ) && - !(var_mode & ENV_UNEXPORT ) ) + 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 & ENV_UNEXPORT ) ) - { - env_universal_get_export( key ); - } - else - { - exportv = (var_mode & ENV_EXPORT ); - } - - env_universal_set(key, val, exportv); - is_universal = 1; - - done = 1; - + env_universal_get_export(key); } 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. - */ - node = top; - while( node->next && !node->new_scope ) - { - node = node->next; - } + exportv = (var_mode & ENV_EXPORT); } - } + + env_universal_set(key, val, exportv); + is_universal = 1; + } + else + { - if( !done ) + node = env_get_node(key); + if (node) { - 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_table_t::iterator result = node->env.find(key); + assert(result != node->env.end()); + e = result->second; + + if (e->exportv) + { + has_changed_new = true; + } } - var_entry_t *entry = NULL; - if( old_entry ) + if ((var_mode & ENV_LOCAL) || + (var_mode & ENV_GLOBAL)) + { + node = (var_mode & ENV_GLOBAL)?global_env:top; + } + else + { + if (node) { - entry = old_entry; + 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( (var_mode & ENV_EXPORT) || entry->exportv ) + 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 + { + /* + 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. + */ + 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) + { + entry = old_entry; + + if ((var_mode & ENV_EXPORT) || entry->exportv) { entry->exportv = !!(var_mode & ENV_EXPORT); has_changed_new = true; } } - else + else { - entry = new var_entry_t; + entry = new var_entry_t; - if( var_mode & ENV_EXPORT) + if (var_mode & ENV_EXPORT) { entry->exportv = 1; has_changed_new = true; } - else + else { entry->exportv = 0; } } - entry->val = val; - node->env[key] = entry; + entry->val = val; + node->env[key] = entry; - if( entry->exportv ) + if (entry->exportv) { - node->exportv=1; + node->exportv=1; } if (has_changed_old || has_changed_new) @@ -955,7 +976,7 @@ int env_set(const wcstring &key, const wchar_t *val, int var_mode) } - if( !is_universal ) + if (!is_universal) { event_t ev = event_t::variable_event(key); ev.arguments.reset(new wcstring_list_t); @@ -964,7 +985,7 @@ int env_set(const wcstring &key, const wchar_t *val, int var_mode) ev.arguments->push_back(key); // debug( 1, L"env_set: fire events on variable %ls", key ); - event_fire( &ev ); + event_fire(&ev); // debug( 1, L"env_set: return from event firing" ); ev.arguments.reset(NULL); } @@ -981,138 +1002,141 @@ 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 ) +static int try_remove(env_node_t *n, + const wchar_t *key, + int var_mode) { - 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; - - if( v->exportv ) + if (n == 0) { - mark_changed_exported(); + return 0; } - n->env.erase(result); - delete v; - return 1; - } + var_table_t::iterator result = n->env.find(key); + if (result != n->env.end()) + { + var_entry_t *v = result->second; - if( var_mode & ENV_LOCAL ) - { - return 0; - } + if (v->exportv) + { + mark_changed_exported(); + } - 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 ) +int env_remove(const wcstring &key, int var_mode) { ASSERT_IS_MAIN_THREAD(); - env_node_t *first_node; - int erased = 0; + 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 ) + if ((var_mode & ENV_USER) && is_read_only(key)) { - first_node = global_env; + return 2; } - if( try_remove( first_node, key.c_str(), var_mode ) ) + first_node = top; + + if (!(var_mode & ENV_UNIVERSAL)) { - event_t ev = event_t::variable_event(key); + + 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 ); + event_fire(&ev); - ev.arguments.reset(NULL); - erased = 1; + ev.arguments.reset(NULL); + erased = 1; + } } - } - if( !erased && - !(var_mode & ENV_GLOBAL) && - !(var_mode & ENV_LOCAL) ) - { - erased = ! env_universal_remove( key.c_str() ); - } + 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) { +env_var_t env_var_t::missing_var(void) +{ env_var_t result(L""); result.is_missing = true; return result; } -const wchar_t *env_var_t::c_str(void) const { +const wchar_t *env_var_t::c_str(void) const +{ assert(! is_missing); return wcstring::c_str(); } -env_var_t env_get_string( const wcstring &key ) +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) { + 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" ) - { + 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" ) - { - return format_string(L"0%0.3o", get_umask() ); - } - else + } + else if (key == L"umask") + { + return format_string(L"0%0.3o", get_umask()); + } + else { { /* Lock around a local region */ @@ -1122,16 +1146,16 @@ env_var_t env_get_string( const wcstring &key ) env_node_t *env = top; wcstring result; - while( env != NULL ) + while (env != NULL) { var_table_t::iterator result = env->env.find(key); - if ( result != env->env.end() ) + if (result != env->env.end()) res = result->second; - if( res != NULL ) + if (res != NULL) { - if( res->val == ENV_NULL ) + if (res->val == ENV_NULL) { return env_var_t::missing_var(); } @@ -1141,7 +1165,7 @@ env_var_t env_get_string( const wcstring &key ) } } - if( env->new_scope ) + if (env->new_scope) { env = global_env; } @@ -1154,15 +1178,15 @@ 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()) + if (is_main && ! get_proc_had_barrier()) { set_proc_had_barrier(true); env_universal_barrier(); } - wchar_t *item = env_universal_get( key ); + wchar_t *item = env_universal_get(key); - if( !item || (wcscmp( item, ENV_NULL )==0)) + if (!item || (wcscmp(item, ENV_NULL)==0)) { return env_var_t::missing_var(); } @@ -1173,22 +1197,22 @@ env_var_t env_get_string( const wcstring &key ) } } -int env_exist( const wchar_t *key, int mode ) +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 ); + 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) ) + /* + 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 +1222,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 ) + if (!(mode & ENV_UNIVERSAL)) { - 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) { @@ -1224,31 +1248,31 @@ int env_exist( const wchar_t *key, int mode ) } return 1; - } + } - if ( mode & ENV_LOCAL ) + if (mode & ENV_LOCAL) break; - if( env->new_scope ) - { - env = global_env; - } - else - { - env = env->next; - } + if (env->new_scope) + { + env = global_env; + } + else + { + env = env->next; + } + } } - } - if( !(mode & ENV_LOCAL) && !(mode & ENV_GLOBAL) ) - { - if( ! get_proc_had_barrier()) + if (!(mode & ENV_LOCAL) && !(mode & ENV_GLOBAL)) { - set_proc_had_barrier(true); - env_universal_barrier(); - } + if (! get_proc_had_barrier()) + { + set_proc_had_barrier(true); + env_universal_barrier(); + } - item = env_universal_get( key ); + item = env_universal_get(key); if (item != NULL) { @@ -1263,95 +1287,95 @@ int env_exist( const wchar_t *key, int mode ) return 1; } - } + } - return 0; + return 0; } /** Returns true if the specified scope or any non-shadowed non-global subscopes contain an exported variable. */ -static int local_scope_exports( env_node_t *n ) +static int local_scope_exports(env_node_t *n) { - if( n==global_env ) - return 0; + if (n==global_env) + return 0; - if( n->exportv ) - return 1; + if (n->exportv) + return 1; - if( n->new_scope ) - return 0; + if (n->new_scope) + return 0; - return local_scope_exports( n->next ); + return local_scope_exports(n->next); } -void env_push( int new_scope ) +void env_push(int new_scope) { - env_node_t *node = new env_node_t; - node->next = top; - node->new_scope=new_scope; + env_node_t *node = new env_node_t; + node->next = top; + node->new_scope=new_scope; - if( 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; - - for( i=0; locale_variable[i]; i++ ) + if (&top->env != global) { - var_table_t::iterator result = killme->env.find( locale_variable[i] ); - if ( result != killme->env.end() ) - { - locale_changed = 1; - break; - } - } + int i; + int locale_changed = 0; - if( killme->new_scope ) - { - if (killme->exportv || local_scope_exports( killme->next )) + env_node_t *killme = top; + + 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; + } + + delete killme; + + if (locale_changed) + handle_locale(); + } - - top = top->next; - - var_table_t::iterator iter; - for (iter = killme->env.begin(); iter != killme->env.end(); ++iter) + else { - var_entry_t *entry = iter->second; - if( entry->exportv ) - { - mark_changed_exported(); - } - delete entry; + 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,84 +1383,85 @@ 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; - - if( ( e->exportv && get_names_show_exported) || - ( !e->exportv && get_names_show_unexported) ) + var_table_t::const_iterator iter; + for (iter = envs.begin(); iter != envs.end(); ++iter) { - /*Insert Key*/ - strSet.insert(iter->first); - } + 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); + } + + } } -wcstring_list_t env_get_names( int flags ) +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); + 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 (!show_local && !show_global && !show_universal) { - if( n == global_env ) - break; - - add_key_to_string_set(n->env, names); - if( n->new_scope ) - break; - else - n = n->next; - + show_local =show_universal = show_global=1; } - } - if( show_global ) - { - add_key_to_string_set(global_env->env, names); - if( get_names_show_unexported ) { + 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); names.insert(uni_list.begin(), uni_list.end()); - } + } result.insert(result.end(), names.begin(), names.end()); return result; @@ -1446,43 +1471,43 @@ wcstring_list_t env_get_names( int flags ) Get list of all exported variables */ -static void get_exported( const env_node_t *n, std::map &h ) +static void get_exported(const env_node_t *n, std::map &h) { - if( !n ) - return; + if (!n) + return; - if( n->new_scope ) - get_exported( global_env, h ); - else - get_exported( n->next, h ); + 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 ) ) + 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 ) + std::map::const_iterator iter; + for (iter = envs.begin(); iter != envs.end(); ++iter) { - if( *pos == ARRAY_SEP ) - *pos = ':'; - pos++; - } + 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()); @@ -1493,51 +1518,53 @@ static void export_func(const std::map &envs, std::vector(key, val)); - } + 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); + + wcstring_list_t uni; + env_universal_get_names2(uni, 1, 0); + for (i=0; i(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; + } } -char **env_export_arr( bool recalc ) +char **env_export_arr(bool recalc) { ASSERT_IS_MAIN_THREAD(); update_export_array_if_necessary(recalc); @@ -1555,10 +1582,12 @@ env_vars_snapshot_t::env_vars_snapshot_t(const wchar_t * const *keys) { ASSERT_IS_MAIN_THREAD(); wcstring key; - for (size_t i=0; keys[i]; i++) { + for (size_t i=0; keys[i]; i++) + { key.assign(keys[i]); const env_var_t val = env_get_string(key); - if (! val.missing()) { + if (! val.missing()) + { vars[key] = val; } } @@ -1585,7 +1614,8 @@ env_var_t env_vars_snapshot_t::get(const wcstring &key) const { return env_get_string(key); } - else { + else + { std::map::const_iterator iter = vars.find(key); return (iter == vars.end() ? env_var_t::missing_var() : env_var_t(iter->second)); } diff --git a/env.h b/env.h index 9a00488ff..543e9c7e6 100644 --- a/env.h +++ b/env.h @@ -46,9 +46,10 @@ /** Error code for trying to alter read-only variable */ -enum{ - ENV_PERM = 1, - ENV_INVALID +enum +{ + ENV_PERM = 1, + ENV_INVALID } ; @@ -102,7 +103,8 @@ int env_set(const wcstring &key, const wchar_t *val, int mode); */ //const wchar_t *env_get( const wchar_t *key ); -class env_var_t : public wcstring { +class env_var_t : public wcstring +{ private: bool is_missing; public: @@ -111,16 +113,24 @@ public: env_var_t(const wcstring & x) : wcstring(x), is_missing(false) { } env_var_t(const wchar_t *x) : wcstring(x), is_missing(false) { } env_var_t() : wcstring(L""), is_missing(false) { } - bool missing(void) const { return is_missing; } - bool missing_or_empty(void) const { return missing() || empty(); } + bool missing(void) const + { + return is_missing; + } + bool missing_or_empty(void) const + { + return missing() || empty(); + } const wchar_t *c_str(void) const; - env_var_t &operator=(const env_var_t &s) { + env_var_t &operator=(const env_var_t &s) + { is_missing = s.is_missing; wcstring::operator=(s); return *this; } - bool operator==(const env_var_t &s) const { + bool operator==(const env_var_t &s) const + { if (is_missing && s.is_missing) return true; else if (s.is_missing || s.is_missing) @@ -133,7 +143,7 @@ public: /** Gets the variable with the specified name, or an empty string if it does not exist. */ -env_var_t env_get_string( const wcstring &key ); +env_var_t env_get_string(const wcstring &key); /** Returns 1 if the specified key exists. This can't be reliably done @@ -142,7 +152,7 @@ env_var_t env_get_string( const wcstring &key ); \param key The name of the variable to remove \param mode the scope to search in. All scopes are searched if unset */ -int env_exist( const wchar_t *key, int mode ); +int env_exist(const wchar_t *key, int mode); /** Remove environemnt variable @@ -152,12 +162,12 @@ int env_exist( const wchar_t *key, int mode ); \return zero if the variable existed, and non-zero if the variable did not exist */ -int env_remove( const wcstring &key, int mode ); +int env_remove(const wcstring &key, int mode); /** Push the variable stack. Used for implementing local variables for functions and for-loops. */ -void env_push( int new_scope ); +void env_push(int new_scope); /** Pop the variable stack. Used for implementing local variables for functions and for-loops. @@ -165,13 +175,13 @@ void env_push( int new_scope ); void env_pop(); /** Returns an array containing all exported variables in a format suitable for execv. */ -char **env_export_arr( bool recalc ); +char **env_export_arr(bool recalc); void env_export_arr(bool recalc, null_terminated_array_t &result); /** Returns all variable names. */ -wcstring_list_t env_get_names( int flags ); +wcstring_list_t env_get_names(int flags); @@ -181,7 +191,8 @@ wcstring_list_t env_get_names( int flags ); */ int env_set_pwd(); -class env_vars_snapshot_t { +class env_vars_snapshot_t +{ std::map vars; bool is_current() const; diff --git a/env_universal.cpp b/env_universal.cpp index c1729be13..e6187c78d 100644 --- a/env_universal.cpp +++ b/env_universal.cpp @@ -61,7 +61,7 @@ static int get_socket_count = 0; static wchar_t * path; static wchar_t *user; static void (*start_fishd)(); -static void (*external_callback)( fish_message_type_t type, const wchar_t *name, const wchar_t *val ); +static void (*external_callback)(fish_message_type_t type, const wchar_t *name, const wchar_t *val); /** Flag set to 1 when a barrier reply is recieved @@ -72,110 +72,110 @@ void env_universal_barrier(); static int is_dead() { - return env_universal_server.fd < 0; + return env_universal_server.fd < 0; } /** Get a socket for reading from the server */ -static int get_socket( int fork_ok ) +static int get_socket(int fork_ok) { - int s, len; - struct sockaddr_un local; + int s, len; + struct sockaddr_un local; - char *name; - wchar_t *wdir; - wchar_t *wuname; - char *dir =0, *uname=0; + char *name; + wchar_t *wdir; + wchar_t *wuname; + char *dir =0, *uname=0; - get_socket_count++; - wdir = path; - wuname = user; + 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 ) + if ((s = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) { - debug( 2, L"Could not connect to socket %d, starting fishd", s ); - - start_fishd(); - - return get_socket( 0 ); + wperror(L"socket"); + return -1; } - 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 (wdir) + dir = wcs2str(wdir); + else + dir = strdup("/tmp"); - if( (fcntl( s, F_SETFL, O_NONBLOCK ) != 0) || (fcntl( s, F_SETFD, FD_CLOEXEC ) != 0) ) - { - wperror( L"fcntl" ); - close( s ); + if (wuname) + uname = wcs2str(wuname); + else + { + struct passwd *pw; + pw = getpwuid(getuid()); + uname = strdup(pw->pw_name); + } - return -1; - } + name = (char *)malloc(strlen(dir) + + strlen(uname) + + strlen(SOCK_FILENAME) + + 2); - debug( 3, L"Connected to fd %d", s ); + strcpy(name, dir); + strcat(name, "/"); + strcat(name, SOCK_FILENAME); + strcat(name, uname); - return s; + 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 ) +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 (!init) + return; - if( env_universal_server.killme ) - { - debug( 3, L"Lost connection to universal variable server." ); - - if( close( env_universal_server.fd ) ) + if (env_universal_server.killme) { - wperror( L"close" ); - } + debug(3, L"Lost connection to universal variable server."); - env_universal_server.fd = -1; - env_universal_server.killme=0; - env_universal_server.input.clear(); - env_universal_read_all(); - } + 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,17 +208,17 @@ static void check_connection() */ static void env_universal_remove_all() { - size_t i; + size_t i; - wcstring_list_t lst; - env_universal_common_get_names( lst, - 1, - 1 ); - for( i=0; i= RECONNECT_COUNT ) - return; + if (get_socket_count >= RECONNECT_COUNT) + return; - debug( 3, L"Get new fishd connection" ); + 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(); - } + 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 ); + 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(); - } + 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 ) + /* + Go into blocking mode and send all data before exiting + */ + if (env_universal_server.fd >= 0) { - wperror( L"fcntl" ); + if (fcntl(env_universal_server.fd, F_SETFL, 0) != 0) + { + wperror(L"fcntl"); + } + try_send_all(&env_universal_server); } - 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) + { + 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) + { + 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 ) +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 ) +int env_universal_get_export(const wcstring &name) { - if( !init) - return 0; + if (!init) + return 0; - return env_universal_common_get_export( name ); + 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 ) + /* + Wait until barrier request has been sent + */ + debug(3, L"Create barrier"); + while (1) { - reconnect(); - debug( 2, L"barrier interrupted, exiting" ); - return; + 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); } - 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 ) + /* + Wait for barrier reply + */ + debug(3, L"Sent barrier request"); + while (!barrier_reply) { - reconnect(); - debug( 2, L"barrier interrupted, exiting (2)" ); - return; + 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(); } - 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" ); + debug(3, L"End barrier"); } -void env_universal_set( const wcstring &name, const wcstring &value, int exportv ) +void env_universal_set(const wcstring &name, const wcstring &value, int exportv) { - message_t *msg; + message_t *msg; - if( !init ) - return; + if (!init) + return; - debug( 3, L"env_universal_set( \"%ls\", \"%ls\" )", name.c_str(), value.c_str() ); + debug(3, L"env_universal_set( \"%ls\", \"%ls\" )", name.c_str(), value.c_str()); - 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 ) + if (is_dead()) { - debug( 1, L"Could not create universal variable message" ); - return; + 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(); + } +} + +int env_universal_remove(const wchar_t *name) +{ + int res; + + 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(); } - msg->count=1; - env_universal_server.unsent->push(msg); - env_universal_barrier(); - } + return res; } -int env_universal_remove( const wchar_t *name ) -{ - int res; - - 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; -} - -void env_universal_get_names2( wcstring_list_t &lst, +void env_universal_get_names2(wcstring_list_t &lst, int show_exported, - int show_unexported ) + 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 8c1b3925f..3e91bfdb8 100644 --- a/env_universal.h +++ b/env_universal.h @@ -17,10 +17,10 @@ extern connection_t env_universal_server; /** Initialize the envuni library */ -void env_universal_init( wchar_t * p, +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 (*cb)(fish_message_type_t type, const wchar_t *name, const wchar_t *val)); /** Free memory used by envuni */ @@ -29,24 +29,24 @@ void env_universal_destroy(); /** Get the value of a universal variable */ -wchar_t *env_universal_get( const wcstring &name ); +wchar_t *env_universal_get(const wcstring &name); /** Get the export flag of the variable with the specified name. Returns 0 if the variable doesn't exist. */ -int env_universal_get_export( const wcstring &name ); +int env_universal_get_export(const wcstring &name); /** Set the value of a universal variable */ -void env_universal_set( const wcstring &name, const wcstring &val, int exportv ); +void env_universal_set(const wcstring &name, const wcstring &val, int exportv); /** Erase a universal variable \return zero if the variable existed, and non-zero if the variable did not exist */ -int env_universal_remove( const wchar_t *name ); +int env_universal_remove(const wchar_t *name); /** Read all available messages from the server. @@ -60,9 +60,9 @@ int env_universal_read_all(); \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 ); +void env_universal_get_names2(wcstring_list_t &list, + int show_exported, + int show_unexported); /** Synchronize with fishd diff --git a/env_universal_common.cpp b/env_universal_common.cpp index 47df13056..1f78705d9 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 ); +static void parse_message(wchar_t *msg, + connection_t *src); /** The table of all universal variables @@ -112,37 +112,37 @@ 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 ); +static void (*callback)(fish_message_type_t type, + const wchar_t *key, + const wchar_t *val); /** List of names for the UTF-8 character set. */ static const char *iconv_utf8_names[]= - { +{ "utf-8", "UTF-8", "utf8", "UTF8", 0 - } - ; +} +; /** List of wide character names, undefined byte length. */ static const char *iconv_wide_names_unknown[]= - { +{ "wchar_t", "WCHAR_T", "wchar", "WCHAR", 0 - } - ; +} +; /** List of wide character names, 4 bytes long. */ static const char *iconv_wide_names_4[]= - { +{ "wchar_t", "WCHAR_T", "wchar", "WCHAR", "ucs-4", "UCS-4", @@ -150,14 +150,14 @@ static const char *iconv_wide_names_4[]= "utf-32", "UTF-32", "utf32", "UTF32", 0 - } - ; +} +; /** List of wide character names, 2 bytes long. */ static const char *iconv_wide_names_2[]= - { +{ "wchar_t", "WCHAR_T", "wchar", "WCHAR", "ucs-2", "UCS-2", @@ -165,8 +165,8 @@ static const char *iconv_wide_names_2[]= "utf-16", "UTF-16", "utf16", "UTF16", 0 - } - ; +} +; template class sloppy {}; @@ -181,129 +181,135 @@ static size_t hack_iconv(iconv_t cd, const char * const* inbuf, size_t *inbytesl struct sloppy_char { const char * const * t; - operator char** () const { return (char **)t; } - operator const char** () const { return (const char**)t; } + 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 ); + return iconv(cd, slop_inbuf, inbytesleft, outbuf, outbytesleft); } /** Convert utf-8 string to wide string */ -static wchar_t *utf2wcs( const char *in ) +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)) - { + switch (sizeof(wchar_t)) + { case 2: - to_name = iconv_wide_names_2; - break; + to_name = iconv_wide_names_2; + break; case 4: - to_name = iconv_wide_names_4; - break; + to_name = iconv_wide_names_4; + break; default: - to_name = iconv_wide_names_unknown; - break; - } - - - /* - 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; - - } + to_name = iconv_wide_names_unknown; + break; } - } - 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" ); + /* + The line protocol fish uses is always utf-8. + */ + const char **from_name = iconv_utf8_names; - /* Terminate the output string. */ - free(out); - return 0; - } + 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; + } /* 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 ); + 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 ) + if (nconv == (size_t) -1) { - debug(0, L"FNORD!!!!"); - free( out_old ); - return 0; + 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); } - free( out_old ); - } - if (iconv_close (cd) != 0) - wperror (L"iconv_close"); + if (iconv_close(cd) != 0) + wperror(L"iconv_close"); - return out; + return out; } @@ -311,372 +317,372 @@ static wchar_t *utf2wcs( const char *in ) /** Convert wide string to utf-8 */ -static char *wcs2utf( const wchar_t *in ) +static char *wcs2utf(const wchar_t *in) { - iconv_t cd=(iconv_t) -1; - int i,j; + iconv_t cd=(iconv_t) -1; + int i,j; - char *char_in = (char *)in; - char *out; + char *char_in = (char *)in; + char *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 **from_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 **from_name=0; - switch (sizeof (wchar_t)) - { + switch (sizeof(wchar_t)) + { case 2: - from_name = iconv_wide_names_2; - break; + from_name = iconv_wide_names_2; + break; case 4: - from_name = iconv_wide_names_4; - break; + from_name = iconv_wide_names_4; + break; 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; - - } + from_name = iconv_wide_names_unknown; + break; } - } - start_conversion: + const char **to_name = iconv_utf8_names; - if (cd == (iconv_t) -1) - { - /* Something went wrong. */ - debug( 0, L"Could not perform utf-8 conversion" ); - if(errno != EINVAL) - wperror( L"iconv_open" ); + size_t in_len = wcslen(in); + size_t out_len = sizeof(char)*((MAX_UTF8_BYTES*in_len)+1); + size_t nconv; + char *nout; - /* Terminate the output string. */ - free(out); - return 0; - } + out = (char *)malloc(out_len); + nout = (char *)out; + in_len *= sizeof(wchar_t); - nconv = hack_iconv( cd, &char_in, &in_len, &nout, &out_len ); + 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 (nconv == (size_t) -1) - { - debug( 0, L"%d %d", in_len, out_len ); - debug( 0, L"Error while converting from to string" ); - return 0; - } + 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'; + *nout = '\0'; - if (iconv_close (cd) != 0) - wperror (L"iconv_close"); + if (iconv_close(cd) != 0) + wperror(L"iconv_close"); - return out; + return out; } -void env_universal_common_init( void (*cb)(fish_message_type_t type, const wchar_t *key, const wchar_t *val ) ) +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; + 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; - } + for (iter = env_universal_var.begin(); iter != env_universal_var.end(); ++iter) + { + var_uni_entry_t* value = iter->second; + delete value; + } } /** Read one byte of date form the specified connection */ -static int read_byte( connection_t *src ) +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 ); + ssize_t res = read(src->fd, src->buffer, ENV_UNIVERSAL_BUFFER_SIZE); // debug(4, L"Read chunk '%.*s'", res, src->buffer ); - if( res < 0 ) - { + if (res < 0) + { - if( errno == EAGAIN || - errno == EINTR ) - { - return ENV_UNIVERSAL_AGAIN; - } + if (errno == EAGAIN || + errno == EINTR) + { + return ENV_UNIVERSAL_AGAIN; + } - return ENV_UNIVERSAL_ERROR; + return ENV_UNIVERSAL_ERROR; + } + + if (res == 0) + { + return ENV_UNIVERSAL_EOF; + } + + src->buffer_consumed = 0; + src->buffer_used = res; } - if( res == 0 ) - { - return ENV_UNIVERSAL_EOF; - } - - src->buffer_consumed = 0; - src->buffer_used = res; - } - - return src->buffer[src->buffer_consumed++]; + return src->buffer[src->buffer_consumed++]; } -void read_message( connection_t *src ) +void read_message(connection_t *src) { - while( 1 ) - { - - int ib = read_byte( src ); - char b; - - switch( ib ) + while (1) { - 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; - } + 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 ) + switch (ib) { - 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)); + case ENV_UNIVERSAL_AGAIN: + { + return; } - return; - } - } - b = (char)ib; + 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; + } - if( b == '\n' ) - { - wchar_t *msg; + 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 = 0; + b = (char)ib; + + if (b == '\n') + { + wchar_t *msg; + + b = 0; src->input.push_back(b); - msg = utf2wcs( &src->input.at(0) ); + 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(); + /* + 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) ); - } + 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 ); + free(msg); - } - else - { + } + else + { src->input.push_back(b); + } } - } } /** Remove variable with specified name */ -void env_universal_common_remove( const wcstring &name ) +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; + } } /** Test if the message msg contains the command cmd */ -static int match( const wchar_t *msg, const wchar_t *cmd ) +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; + if (msg[len] && msg[len]!= L' ' && msg[len] != L'\t') + return 0; - return 1; + return 1; } -void env_universal_common_set( const wchar_t *key, const wchar_t *val, int exportv ) +void env_universal_common_set(const wchar_t *key, const wchar_t *val, int exportv) { - var_uni_entry_t *entry; + var_uni_entry_t *entry; - CHECK( key, ); - CHECK( val, ); + CHECK(key,); + CHECK(val,); - entry = new var_uni_entry_t; + 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); + } } /** Parse message msg */ -static void parse_message( wchar_t *msg, - connection_t *src ) +static void parse_message(wchar_t *msg, + connection_t *src) { // debug( 3, L"parse_message( %ls );", msg ); - if( msg[0] == L'#' ) - return; + 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 ) + if (match(msg, SET_STR) || match(msg, SET_EXPORT_STR)) { - wchar_t *key; - wchar_t *val; + wchar_t *name, *tmp; + int exportv = match(msg, SET_EXPORT_STR); - key = (wchar_t *)malloc( sizeof( wchar_t)*(tmp-name+1)); - memcpy( key, name, sizeof( wchar_t)*(tmp-name)); - key[tmp-name]=0; + name = msg+(exportv?wcslen(SET_EXPORT_STR):wcslen(SET_STR)); + while (wcschr(L"\t ", *name)) + name++; - val = tmp+1; - val = unescape( val, 0 ); + tmp = wcschr(name, L':'); + if (tmp) + { + wchar_t *key; + wchar_t *val; - if (key && val) - env_universal_common_set( key, val, exportv ); + key = (wchar_t *)malloc(sizeof(wchar_t)*(tmp-name+1)); + memcpy(key, name, sizeof(wchar_t)*(tmp-name)); + key[tmp-name]=0; - free( val ); - free( key ); + 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 ); + 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 ); - } } /** @@ -684,73 +690,73 @@ 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 ) +static int try_send(message_t *msg, + 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 ) + if (res != -1) { - 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; + debug(4, L"Wrote message '%s'", msg->body.c_str()); + } + else + { + debug(4, L"Failed to write message '%s'", msg->body.c_str()); } - } - msg->count--; - if( !msg->count ) - { - delete msg; - } - return 1; + 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 ) +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 ) ) + /* debug( 3, + L"Send all updates to connection on fd %d", + c->fd );*/ + while (!c->unsent->empty()) { - case 1: - c->unsent->pop(); - break; + 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 0: + debug(4, + L"Socket full, send rest later"); + return; - case -1: - c->killme = 1; - return; + case -1: + c->killme = 1; + return; + } } - } } /* The universal variable format has some funny escaping requirements; here we try to be safe */ @@ -765,30 +771,30 @@ static bool is_universal_safe_to_encode_directly(wchar_t c) /** Escape specified string */ -static wcstring full_escape( const wchar_t *in ) +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 < 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); + } } - else if (c < 65536) - { - append_format(out, L"\\u%.4x", c); - } - else - { - append_format(out, L"\\U%.8x", c); - } - } - return out; + return out; } /* Sets the body of a message to the null-terminated list of null terminated const char *. */ @@ -798,189 +804,189 @@ 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 */ -message_t *create_message( fish_message_type_t type, +message_t *create_message(fish_message_type_t type, const wchar_t *key_in, - const wchar_t *val_in ) + const wchar_t *val_in) { - message_t *msg = new message_t; + message_t *msg = new message_t; msg->count = 0; - char *key=0; + char *key=0; // debug( 4, L"Crete message of type %d", type ); - if( key_in ) - { - if( wcsvarname( key_in ) ) + if (key_in) { - debug( 0, L"Illegal variable name: '%ls'", key_in ); - return 0; - } + if (wcsvarname(key_in)) + { + debug(0, L"Illegal variable name: '%ls'", key_in); + return 0; + } - key = wcs2utf(key_in); - if( !key ) - { - debug( 0, + key = wcs2utf(key_in); + if (!key) + { + debug(0, L"Could not convert %ls to narrow character string", - key_in ); - return 0; + key_in); + return 0; + } } - } - switch( type ) - { + switch (type) + { case SET: case SET_EXPORT: { - if( !val_in ) - { - val_in=L""; - } + 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 ); + 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; + break; } case ERASE: { - set_body(msg, ERASE_MBS, " ", key, "\n", NULL); - break; + set_body(msg, ERASE_MBS, " ", key, "\n", NULL); + break; } case BARRIER: { - set_body(msg, BARRIER_MBS, "\n", NULL); - break; + set_body(msg, BARRIER_MBS, "\n", NULL); + break; } case BARRIER_REPLY: { - set_body(msg, BARRIER_REPLY_MBS, "\n", NULL); - break; + set_body(msg, BARRIER_REPLY_MBS, "\n", NULL); + break; } default: { - debug( 0, L"create_message: Unknown message type" ); + debug(0, L"create_message: Unknown message type"); + } } - } - free( key ); + free(key); // debug( 4, L"Message body is '%s'", msg->body ); - return msg; + return msg; } /** 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 ) +void env_universal_common_get_names(wcstring_list_t &lst, + 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) ) + env_var_table_t::const_iterator iter; + for (iter = env_universal_var.begin(); iter != env_universal_var.end(); ++iter) { - lst.push_back(key); + 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 ) +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 ) +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 ) +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; + 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); - } + 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 ); + try_send_all(c); } -void connection_init( connection_t *c, int fd ) +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) +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 ) ) + /* + A connection need not always be open - we only try to close it + if it is open. + */ + if (c->fd >= 0) { - wperror( L"close" ); + if (close(c->fd)) + { + wperror(L"close"); + } } - } } diff --git a/env_universal_common.h b/env_universal_common.h index d783da549..1f90b6bb5 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; /** @@ -59,15 +59,15 @@ typedef enum */ 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; @@ -78,66 +78,66 @@ 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; + /** + 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]; + /** + The read buffer. + */ + char buffer[ENV_UNIVERSAL_BUFFER_SIZE]; - /** - Number of bytes that have already been consumed. - */ - size_t buffer_consumed; + /** + 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; + /** + Number of bytes that have been read into the buffer. + */ + size_t buffer_used; - /** - Link to the next connection - */ - struct connection *next; + /** + Link to the next connection + */ + struct connection *next; } - connection_t; +connection_t; /** Read all available messages on this connection */ -void read_message( connection_t * ); +void read_message(connection_t *); /** Send as many messages as possible without blocking to the connection */ -void try_send_all( connection_t *c ); +void try_send_all(connection_t *c); /** Create a messge with the specified properties */ -message_t *create_message( fish_message_type_t type, const wchar_t *key, const wchar_t *val ); +message_t *create_message(fish_message_type_t type, const wchar_t *key, const wchar_t *val); /** Init the library */ -void env_universal_common_init(void (*cb)(fish_message_type_t type, const wchar_t *key, const wchar_t *val ) ); +void env_universal_common_init(void (*cb)(fish_message_type_t type, const wchar_t *key, const wchar_t *val)); /** Destroy library data @@ -150,9 +150,9 @@ void env_universal_common_destroy(); This function operate agains the local copy of all universal 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 ); +void env_universal_common_get_names(wcstring_list_t &lst, + int show_exported, + int show_unexported); /** Perform the specified variable assignment. @@ -163,7 +163,7 @@ void env_universal_common_get_names( wcstring_list_t &lst, Do not call this function. Create a message to do it. This function is only to be used when fishd is dead. */ -void env_universal_common_set( const wchar_t *key, const wchar_t *val, int exportv ); +void env_universal_common_set(const wchar_t *key, const wchar_t *val, int exportv); /** Remove the specified variable. @@ -174,7 +174,7 @@ void env_universal_common_set( const wchar_t *key, const wchar_t *val, int expor Do not call this function. Create a message to do it. This function is only to be used when fishd is dead. */ -void env_universal_common_remove( const wcstring &key ); +void env_universal_common_remove(const wcstring &key); /** Get the value of the variable with the specified name @@ -182,7 +182,7 @@ void env_universal_common_remove( const wcstring &key ); This function operate agains the local copy of all universal variables, it does not communicate with any other process. */ -wchar_t *env_universal_common_get( const wcstring &name ); +wchar_t *env_universal_common_get(const wcstring &name); /** Get the export flag of the variable with the specified @@ -191,24 +191,24 @@ wchar_t *env_universal_common_get( const wcstring &name ); This function operate agains the local copy of all universal variables, it does not communicate with any other process. */ -int env_universal_common_get_export( const wcstring &name ); +int env_universal_common_get_export(const wcstring &name); /** Add messages about all existing variables to the specified connection */ -void enqueue_all( connection_t *c ); +void enqueue_all(connection_t *c); /** Fill in the specified connection_t struct. Use the specified file descriptor for communication. */ -void connection_init( connection_t *c, int fd ); +void connection_init(connection_t *c, int fd); /** Close and destroy the specified connection struct. This frees allstructures allocated by the connection, such as ques of unsent messages. */ -void connection_destroy( connection_t *c); +void connection_destroy(connection_t *c); #endif diff --git a/event.cpp b/event.cpp index 371a3a349..8f4f6b173 100644 --- a/event.cpp +++ b/event.cpp @@ -36,27 +36,27 @@ */ 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 active, which is the one that new events is written to. The inactive one contains the events that are currently beeing performed. */ -static signal_list_t sig_list[]={{0,0},{0,0}}; +static signal_list_t sig_list[]= {{0,0},{0,0}}; /** The index of sig_list that is the list of signals currently written to @@ -86,52 +86,52 @@ static event_list_t blocked; they must name the same function. */ -static int event_match( const event_t *classv, const event_t *instance ) +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() && - ! instance->function_name.empty() && - classv->function_name != instance->function_name) - { + 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 == EVENT_ANY) + return 1; - if( classv->type != instance->type ) - return 0; + if (classv->type != instance->type) + return 0; - switch( classv->type ) - { + switch (classv->type) + { case EVENT_SIGNAL: - if( classv->param1.signal == EVENT_ANY_SIGNAL ) - return 1; - return classv->param1.signal == instance->param1.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; + 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->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; + return classv->param1.job_id == instance->param1.job_id; case EVENT_GENERIC: - return instance->str_param1 == classv->str_param1; + return instance->str_param1 == classv->str_param1; - } + } - /** - This should never be reached - */ - return 0; + /** + This should never be reached + */ + return 0; } @@ -139,95 +139,97 @@ static int event_match( const event_t *classv, const event_t *instance ) Create an identical copy of an event. Use deep copying, i.e. make duplicates of any strings used as well. */ -static event_t *event_copy( const event_t *event, int copy_arguments ) +static event_t *event_copy(const event_t *event, int copy_arguments) { event_t *e = new event_t(*event); e->arguments.reset(new wcstring_list_t); - if( copy_arguments && event->arguments.get() != NULL ) - { + if (copy_arguments && event->arguments.get() != NULL) + { *(e->arguments) = *(event->arguments); - } + } - return e; + return e; } /** Test if specified event is blocked */ -static int event_is_blocked( event_t *e ) +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 ) +wcstring event_get_desc(const event_t *e) { - CHECK( e, 0 ); + CHECK(e, 0); - wcstring result; - switch( e->type ) - { + 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; + 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; + 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() ); + if (e->param1.pid > 0) + { + result = format_string(_(L"exit handler for process %d"), e->param1.pid); + } else - result = format_string(_(L"exit handler for job with process group %d"), -e->param1.pid ); - } + { + 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; + 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 ); + 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; + result = format_string(_(L"handler for generic event '%ls'"), e->str_param1.c_str()); + break; default: - result = format_string(_(L"Unknown event type") ); - break; + result = format_string(_(L"Unknown event type")); + break; - } + } - return result; + return result; } #if 0 -static void show_all_handlers(void) { +static void show_all_handlers(void) +{ puts("event handlers:"); - for (event_list_t::const_iterator iter = events.begin(); iter != events.end(); ++iter) { + for (event_list_t::const_iterator iter = events.begin(); iter != events.end(); ++iter) + { const event_t *foo = *iter; wcstring tmp = event_get_desc(foo); printf(" handler now %ls\n", tmp.c_str()); @@ -235,18 +237,18 @@ static void show_all_handlers(void) { } #endif -void event_add_handler( const event_t *event ) +void event_add_handler(const event_t *event) { - event_t *e; + event_t *e; - CHECK( event, ); + CHECK(event,); - e = event_copy( event, 0 ); + e = event_copy(event, 0); - if( e->type == EVENT_SIGNAL ) - { - signal_handle( e->param1.signal, 1 ); - } + if (e->type == EVENT_SIGNAL) + { + signal_handle(e->param1.signal, 1); + } // Block around updating the events vector signal_block(); @@ -254,78 +256,78 @@ void event_add_handler( const event_t *event ) signal_unblock(); } -void event_remove( event_t *criterion ) +void event_remove(event_t *criterion) { - size_t i; - event_list_t new_list; + size_t i; + event_list_t new_list; - CHECK( criterion, ); + CHECK(criterion,); - /* - Because of concurrency issues (env_remove could remove an event - that is currently being executed), env_remove does not actually - free any events - instead it simply moves all events that should - be removed from the event list to the killme list, and the ones - that shouldn't be killed to new_list, and then drops the empty - events-list. - */ + /* + 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; + if (events.empty()) + return; - 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 ); + if (event_get(&e, 0) == 1) + { + signal_handle(e.param1.signal, 0); + } + } } - } - } - else - { + 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 ) +int event_get(event_t *criterion, std::vector *out) { - size_t i; - int found = 0; + size_t i; + int found = 0; - if( events.empty() ) - return 0; + if (events.empty()) + return 0; - CHECK( criterion, 0 ); + CHECK(criterion, 0); - for( i=0; ipush_back(n); + } } - } - return found; + return found; } bool event_is_signal_observed(int sig) @@ -343,7 +345,7 @@ bool event_is_signal_observed(int sig) } else if (event->type == EVENT_SIGNAL) { - if( event->param1.signal == EVENT_ANY_SIGNAL || event->param1.signal == sig) + if (event->param1.signal == EVENT_ANY_SIGNAL || event->param1.signal == sig) return true; } } @@ -362,7 +364,7 @@ static void event_free_kills() /** Test if the specified event is waiting to be killed */ -static int event_is_killed( event_t *e ) +static int event_is_killed(event_t *e) { return std::find(killme.begin(), killme.end(), e) != killme.end(); } @@ -373,71 +375,71 @@ static int event_is_killed( event_t *e ) optimize the 'no matches' path. This means that nothing is allocated/initialized unless needed. */ -static void event_fire_internal( const event_t *event ) +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(); - - if( events.empty() ) - return; - - /* - Then we iterate over all events, adding events that should be - fired to a second list. We need to do this in a separate step - since an event handler might call event_remove or - event_add_handler, which will change the contents of the \c - events list. - */ - for( i=0; ifunction_name; + + for (i=0; ifunction_name; if (event->arguments.get()) { - for( j=0; j< event->arguments->size(); j++ ) + for (j=0; j< event->arguments->size(); j++) { - wcstring arg_esc = escape_string( event->arguments->at(j), 1 ); + wcstring arg_esc = escape_string(event->arguments->at(j), 1); buffer += L" "; buffer += arg_esc; } @@ -445,26 +447,26 @@ static void event_fire_internal( const event_t *event ) // 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(); + /* + 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 ); - } + 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 +476,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; + while (sig_list[active_list].count > 0) + { + signal_list_t *lst; - /* - Switch signal lists - */ - sig_list[1-active_list].count=0; - sig_list[1-active_list].overflow=0; - active_list=1-active_list; + /* + Switch signal lists + */ + sig_list[1-active_list].count=0; + sig_list[1-active_list].overflow=0; + active_list=1-active_list; - /* - Set up - */ + /* + 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]; + lst = &sig_list[1-active_list]; - if( lst->overflow ) - { - debug( 0, _( L"Signal list overflow. Signals have been ignored." ) ); - } + 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 ) ) - { + /* + 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)) + { 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) @@ -556,42 +558,42 @@ void event_fire_signal(int signal) allocation or something else that might be bad when in a signal handler. */ - if( sig_list[active_list].count < SIG_UNHANDLED_MAX ) + if (sig_list[active_list].count < SIG_UNHANDLED_MAX) sig_list[active_list].signal[sig_list[active_list].count++]=signal; else sig_list[active_list].overflow=1; } -void event_fire( event_t *event ) +void event_fire(event_t *event) { - if( event && (event->type == EVENT_SIGNAL) ) - { + if (event && (event->type == EVENT_SIGNAL)) + { event_fire_signal(event->param1.signal); - } - else - { + } + else + { is_event++; - /* - Fire events triggered by signals - */ - event_fire_delayed(); + /* + Fire events triggered by signals + */ + event_fire_delayed(); - if( event ) - { - if( event_is_blocked( event ) ) - { + 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--; - } + } } @@ -609,57 +611,60 @@ void event_destroy() killme.clear(); } -void event_free( event_t *e ) +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 ); + } + va_end(va); - event_fire( &ev ); + event_fire(&ev); ev.arguments.reset(NULL); } -event_t event_t::signal_event(int sig) { +event_t event_t::signal_event(int sig) +{ event_t event(EVENT_SIGNAL); event.param1.signal = sig; return event; } -event_t event_t::variable_event(const wcstring &str) { +event_t event_t::variable_event(const wcstring &str) +{ event_t event(EVENT_VARIABLE); event.str_param1 = str; return event; } -event_t event_t::generic_event(const wcstring &str) { +event_t event_t::generic_event(const wcstring &str) +{ event_t event(EVENT_GENERIC); event.str_param1 = str; return event; } event_t::event_t(const event_t &x) : - type(x.type), - param1(x.param1), - str_param1(x.str_param1), - function_name(x.function_name) + type(x.type), + param1(x.param1), + str_param1(x.str_param1), + function_name(x.function_name) { const wcstring_list_t *ptr = x.arguments.get(); if (ptr) diff --git a/event.h b/event.h index 7ed5efacf..034dafab2 100644 --- a/event.h +++ b/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,18 +49,19 @@ enum */ struct event_t { - /** - 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 - 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 + /** + Type of event */ - union { + 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 + 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; @@ -73,16 +74,16 @@ struct event_t */ 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() { } @@ -100,14 +101,14 @@ struct event_t May not be called by a signal handler, since it may allocate new memory. */ -void event_add_handler( const event_t *event ); +void event_add_handler(const event_t *event); /** Remove all events matching the specified criterion. May not be called by a signal handler, since it may free allocated memory. */ -void event_remove( event_t *event ); +void event_remove(event_t *event); /** Return all events which match the specified event class @@ -120,7 +121,7 @@ void event_remove( event_t *event ); \return the number of found matches */ -int event_get( event_t *criterion, std::vector *out ); +int event_get(event_t *criterion, std::vector *out); /** Returns whether an event listener is registered for the given signal. @@ -144,7 +145,7 @@ bool event_is_signal_observed(int signal); \param event the specific event whose handlers should fire. If null, then all delayed events will be fired. */ -void event_fire( event_t *event ); +void event_fire(event_t *event); /** Like event_fire, but takes a signal directly. */ void event_fire_signal(int signal); @@ -162,12 +163,12 @@ void event_destroy(); /** Free all memory used by the specified event */ -void event_free( event_t *e ); +void event_free(event_t *e); /** Returns a string describing the specified event. */ -wcstring event_get_desc( const event_t *e ); +wcstring event_get_desc(const event_t *e); /** Fire a generic event with the specified name diff --git a/exec.cpp b/exec.cpp index 92ce69cf2..2f1506f14 100644 --- a/exec.cpp +++ b/exec.cpp @@ -82,68 +82,70 @@ 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 ) +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" ); + if (write_loop(fd, buff, count) == -1) + { + debug(0, WRITE_ERROR); + wperror(L"write"); + exit_without_destructors(status); + } exit_without_destructors(status); - } - exit_without_destructors( status ); } -void exec_close( int fd ) +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 ) + if (fd < 0) { - debug( 1, FD_ERROR, fd ); - wperror( L"close" ); - break; + 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()) { + if ((size_t)fd < open_fds.size()) + { open_fds[fd] = false; } } -int exec_pipe( int fd[2]) +int exec_pipe(int fd[2]) { - int res; + int res; - while ((res=pipe(fd))) - { - if( errno != EINTR ) + while ((res=pipe(fd))) { - wperror(L"pipe"); - return res; + if (errno != EINTR) + { + wperror(L"pipe"); + return res; + } } - } - debug( 4, L"Created pipe using fds %d and %d", fd[0], fd[1]); + 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) { + 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; } /** @@ -154,16 +156,16 @@ int exec_pipe( int fd[2]) \param fd the fd to search for \param io_chain the set of io redirections to search in */ -static bool use_fd_in_pipe(int fd, const io_chain_t &io_chain ) +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 ) || - ( io->io_mode == IO_PIPE ) ) + if ((io->io_mode == IO_BUFFER) || + (io->io_mode == IO_PIPE)) { - if( io->param1.pipe_fd[0] == fd || - io->param1.pipe_fd[1] == fd ) + if (io->param1.pipe_fd[0] == fd || + io->param1.pipe_fd[1] == fd) return true; } } @@ -179,16 +181,18 @@ static bool use_fd_in_pipe(int fd, const io_chain_t &io_chain ) \param io the list of io redirections for this job. Pipes mentioned here should not be closed. */ -void close_unused_internal_pipes( const io_chain_t &io ) +void close_unused_internal_pipes(const io_chain_t &io) { /* A call to exec_close will modify open_fds, so be careful how we walk */ - for (size_t i=0; i < open_fds.size(); i++) { - if (open_fds[i]) { + for (size_t i=0; i < open_fds.size(); i++) + { + if (open_fds[i]) + { int fd = (int)i; - if( !use_fd_in_pipe(fd, io)) + if (!use_fd_in_pipe(fd, io)) { - debug( 4, L"Close fd %d, used in other context", fd ); - exec_close( fd ); + debug(4, L"Close fd %d, used in other context", fd); + exec_close(fd); i--; } } @@ -197,10 +201,12 @@ void close_unused_internal_pipes( const io_chain_t &io ) void get_unused_internal_pipes(std::vector &fds, const io_chain_t &io) { - for (size_t i=0; i < open_fds.size(); i++) { - if (open_fds[i]) { + for (size_t i=0; i < open_fds.size(); i++) + { + if (open_fds[i]) + { int fd = (int)i; - if( !use_fd_in_pipe(fd, io)) + if (!use_fd_in_pipe(fd, io)) { fds.push_back(fd); } @@ -212,31 +218,36 @@ void get_unused_internal_pipes(std::vector &fds, const io_chain_t &io) Returns the interpreter for the specified script. Returns NULL if file is not a script with a shebang. */ -char *get_interpreter( const char *command, char *interpreter, size_t buff_size ) +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 ) - { - size_t idx = 0; - while( idx + 1 < buff_size ) + int fd = open(command, O_RDONLY); + if (fd >= 0) { + size_t idx = 0; + 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) { + } + else if (strncmp(interpreter, "#!/", 3) == 0) + { return interpreter + 2; - } else { + } + else + { return NULL; } } @@ -248,61 +259,62 @@ char *get_interpreter( const char *command, char *interpreter, size_t buff_size in \c p. It never returns. */ /* 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 ) +static void safe_launch_process(process_t *p, const char *actual_cmd, char **argv, char **envv) { - int err; + int err; // debug( 1, L"exec '%ls'", p->argv[0] ); // Wow, this wcs2str call totally allocates memory - execve ( actual_cmd, argv, envv ); + execve(actual_cmd, argv, envv); - err = errno; + 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. - */ + /* + 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]; argv2[0] = sh_command; - for (size_t i=1; i < sizeof argv2 / sizeof *argv2; i++) { + for (size_t i=1; i < sizeof argv2 / sizeof *argv2; i++) + { argv2[i] = argv[i-1]; if (argv2[i] == NULL) break; } - execve(sh_command, argv2, envv); + execve(sh_command, argv2, envv); + } } - } - errno = err; + errno = err; safe_report_exec_error(errno, actual_cmd, argv, envv); - exit_without_destructors(STATUS_EXEC_FAIL); + exit_without_destructors(STATUS_EXEC_FAIL); } /** This function is similar to launch_process, except it is not called after a fork (i.e. it only calls exec) and therefore it can allocate memory. */ -static void launch_process_nofork( process_t *p ) +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. */ @@ -314,9 +326,9 @@ static void launch_process_nofork( process_t *p ) Check if the IO redirection chains contains redirections for the specified file descriptor */ -static int has_fd( const io_chain_t &d, int fd ) +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; } /** @@ -324,9 +336,11 @@ static int has_fd( const io_chain_t &d, int fd ) used by a transmogrified IO_FILE redirection are freed, since the original chain may still be needed. */ -static void io_cleanup_chains(io_chain_t &chains, const std::vector &opened_fds) { +static void io_cleanup_chains(io_chain_t &chains, const std::vector &opened_fds) +{ /* Close all the fds */ - for (size_t idx = 0; idx < opened_fds.size(); idx++) { + for (size_t idx = 0; idx < opened_fds.size(); idx++) + { close(opened_fds.at(idx)); } @@ -343,12 +357,14 @@ static void io_cleanup_chains(io_chain_t &chains, const std::vector &opened \return the transmogrified chain on sucess, or 0 on failiure */ -static bool io_transmogrify(const io_chain_t &in_chain, io_chain_t &out_chain, std::vector &out_opened_fds) { +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()) { + if (in_chain.empty()) + { return true; } @@ -365,70 +381,74 @@ static bool io_transmogrify(const io_chain_t &in_chain, io_chain_t &out_chain, s io_data_t *in = in_chain.at(idx); io_data_t *out = NULL; //gets allocated via new - switch( in->io_mode ) + switch (in->io_mode) { - default: - /* Unknown type, should never happen */ - fprintf(stderr, "Unknown io_mode %ld\n", (long)in->io_mode); - abort(); - break; + default: + /* Unknown type, should never happen */ + fprintf(stderr, "Unknown io_mode %ld\n", (long)in->io_mode); + abort(); + break; /* These redirections don't need transmogrification. They can be passed through. */ - case IO_PIPE: - case IO_FD: - case IO_BUFFER: - case IO_CLOSE: + case IO_PIPE: + case IO_FD: + case IO_BUFFER: + case IO_CLOSE: + { + out = new io_data_t(*in); + break; + } + + /* + Transmogrify file redirections + */ + case IO_FILE: + { + out = new io_data_t(); + out->fd = in->fd; + out->io_mode = IO_FD; + out->param2.close_old = 1; + + int fd; + if ((fd=open(in->filename_cstr, in->param2.flags, OPEN_MASK))==-1) { - out = new io_data_t(*in); + debug(1, + FILE_ERROR, + in->filename_cstr); + + wperror(L"open"); + success = false; break; } - /* - Transmogrify file redirections - */ - case IO_FILE: - { - out = new io_data_t(); - out->fd = in->fd; - out->io_mode = IO_FD; - out->param2.close_old = 1; + opened_fds.push_back(fd); + out->param1.old_fd = fd; - int fd; - if ((fd=open(in->filename_cstr, in->param2.flags, OPEN_MASK))==-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; - } + 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) { + if (! success) + { break; } } /* Now either return success, or clean up */ - if (success) { + if (success) + { /* Yay */ out_chain.swap(result_chain); out_opened_fds.swap(opened_fds); - } else { + } + else + { /* No dice - clean up */ io_cleanup_chains(result_chain, opened_fds); } @@ -445,63 +465,63 @@ static bool io_transmogrify(const io_chain_t &in_chain, io_chain_t &out_chain, s \param io the io redirections to be performed on this block */ -static void internal_exec_helper( parser_t &parser, - const wchar_t *def, - enum block_type_t block_type, - io_chain_t &ios ) +static void internal_exec_helper(parser_t &parser, + 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; + 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; - } + /* + Did the transmogrification fail - if so, set error status and return + */ + if (! transmorgrified) + { + proc_set_last_status(STATUS_EXEC_FAIL); + return; + } - signal_unblock(); + signal_unblock(); - parser.eval( def, morphed_chain, block_type ); + parser.eval(def, morphed_chain, block_type); - signal_block(); + signal_block(); - io_cleanup_chains(morphed_chain, opened_fds); - job_reap( 0 ); - is_block=is_block_old; + 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 ) +static void do_builtin_io(const char *out, const char *err) { size_t len; - if (out && (len = strlen(out))) - { - - if (write_loop(STDOUT_FILENO, out, len) == -1) + if (out && (len = strlen(out))) { - 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. @@ -512,10 +532,10 @@ static void do_builtin_io( const char *out, const char *err ) */ static bool can_use_posix_spawn_for_job(const job_t *job, const process_t *process) { - if ( job_get_flag( job, JOB_CONTROL ) ) + if (job_get_flag(job, JOB_CONTROL)) { /* We are going to use job control; therefore when we launch this job it will get its own process group ID. But will it be foregrounded? */ - if ( job_get_flag( job, JOB_TERMINAL ) && job_get_flag( job, JOB_FOREGROUND ) ) + if (job_get_flag(job, JOB_TERMINAL) && job_get_flag(job, JOB_FOREGROUND)) { /* It will be foregrounded, so we will call tcsetpgrp(), therefore do not use posix_spawn */ return false; @@ -541,971 +561,978 @@ static bool can_use_posix_spawn_for_job(const job_t *job, const process_t *proce } -void exec( parser_t &parser, job_t *j ) +void exec(parser_t &parser, job_t *j) { - process_t *p; - pid_t pid = 0; - int mypipe[2]; - sigset_t chldset; + process_t *p; + pid_t pid = 0; + int mypipe[2]; + sigset_t chldset; - io_data_t pipe_read, pipe_write; + io_data_t pipe_read, pipe_write; - io_data_t *io_buffer =0; + io_data_t *io_buffer =0; - /* - 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; + /* + 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; - bool needs_keepalive = false; - process_t keepalive; + bool needs_keepalive = false; + process_t keepalive; - CHECK( j, ); - CHECK_BLOCK(); + CHECK(j,); + CHECK_BLOCK(); - if( no_exec ) - return; + if (no_exec) + return; - sigemptyset( &chldset ); - sigaddset( &chldset, SIGCHLD ); + sigemptyset(&chldset); + sigaddset(&chldset, SIGCHLD); - debug( 4, L"Exec job '%ls' with id %d", j->command_wcstr(), j->job_id ); + debug(4, L"Exec job '%ls' with id %d", j->command_wcstr(), j->job_id); - if( ! parser.block_io.empty() ) - { + 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; + 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(); - - /* - setup_child_process makes sure signals are properly set - up. It will also call signal_unblock - */ - if( !setup_child_process( j, 0 ) ) + if (j->first_process->type==INTERNAL_EXEC) { - /* - launch_process _never_ returns - */ - launch_process_nofork( j->first_process ); - } - else - { - job_set_flag( j, JOB_CONSTRUCTED, 1 ); - j->first_process->completed=1; - return; + /* + 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(); + 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. - */ + /* + 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 (job_get_flag(j, JOB_CONTROL)) { - if( p->type != EXTERNAL ) - { - if( p->next ) + for (p=j->first_process; p; p = p->next) { - needs_keepalive = true; - break; - } - if( p != j->first_process ) - { - needs_keepalive = true; - break; - } + if (p->type != EXTERNAL) + { + if (p->next) + { + needs_keepalive = true; + break; + } + if (p != j->first_process) + { + needs_keepalive = true; + break; + } - } + } + } } - } - if( needs_keepalive ) - { + if (needs_keepalive) + { /* Call fork. No need to wait for threads since our use is confined and simple. */ - if (g_log_forks) { + 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 ); + 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. + /* + 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. - The loop also has to handle pipelining between the jobs. - */ + The loop also has to handle pipelining between the jobs. + */ - for( p=j->first_process; p; p = p->next ) - { - const bool p_wants_pipe = (p->next != NULL); - mypipe[1]=-1; + 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; + 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 ); + /* + 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 - */ + /* + Set up fd:s that will be used in the pipe + */ - if( p == j->first_process->next ) - { + if (p == j->first_process->next) + { j->io.push_back(&pipe_read); - } + } - if( p_wants_pipe ) - { + 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; - } + 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. - */ + 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; - - - /* - 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; - } - function_block_t *newv = new function_block_t(p, p->argv0(), shadows); - 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 ); - j->io.push_back(io_buffer); } - internal_exec_helper( parser, def, TOP, j->io ); - - parser.allow_function(); - parser.pop_block(); - free(def); - - break; - } - - case INTERNAL_BLOCK: - { - if( p->next ) + switch (p->type) { - 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; - - /* - If this is the first process, check the io - redirections and see where we should be reading - from. - */ - if( p == j->first_process ) + case INTERNAL_FUNCTION: { - const io_data_t *in = io_chain_get( j->io, 0 ); + wchar_t * def=0; + int shadows; - if( in ) - { - switch( in->io_mode ) + + /* + 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) { - - case IO_FD: - { - builtin_stdin = in->param1.old_fd; + debug(0, _(L"Unknown function '%ls'"), p->argv0()); 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, - 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: - - 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]; - } + function_block_t *newv = new function_block_t(p, p->argv0(), shadows); + parser.push_block(newv); - 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 ); - - const char *buffer = io_buffer->out_buffer_ptr(); - size_t count = io_buffer->out_buffer_size(); - - 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. + set_argv might trigger an event + handler, hence we need to unblock + signals. */ - p->pid = getpid(); - setup_child_process( j, p ); + 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(); + free(def); + + 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; - 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. + If this is the first process, check the io + redirections and see where we should be reading + from. */ - p->pid = pid; - set_child_group( j, p, 0 ); + if (p == j->first_process) + { + const io_data_t *in = io_chain_get(j->io, 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(); - - /* 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 ); - } - - 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. - */ - - 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) { - printf("fork #-: Skipping fork for internal builtin for '%ls'\n", p->argv0()); - } - const wcstring &out = get_stdout_buffer(), &err = get_stderr_buffer(); - char *outbuff = wcs2str(out.c_str()), *errbuff = wcs2str(err.c_str()); - do_builtin_io(outbuff, errbuff); - free(outbuff); - free(errbuff); - skip_fork = 1; - } - - 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; - 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. */ - - /* 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 - { - /* 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: - { - /* 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); - if (use_posix_spawn) + if (in) { - /* Create posix spawn attributes and actions */ - posix_spawnattr_t attr = posix_spawnattr_t(); - posix_spawn_file_actions_t actions = posix_spawn_file_actions_t(); - bool made_it = fork_actions_make_spawn_properties(&attr, &actions, j, p); - if (made_it) + switch (in->io_mode) { - /* 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); + case IO_FD: + { + builtin_stdin = in->param1.old_fd; + break; + } + case IO_PIPE: + { + builtin_stdin = in->param1.pipe_fd[0]; + break; + } - if (spawn_ret != 0) + case IO_FILE: + { + /* Do not set CLO_EXEC because child needs access */ + builtin_stdin=open(in->filename_cstr, + in->param2.flags, OPEN_MASK); + if (builtin_stdin == -1) { - safe_report_exec_error(spawn_ret, actual_cmd, argv, envv); - /* Make sure our pid isn't set */ - pid = 0; + debug(1, + FILE_ERROR, + in->filename_cstr); + wperror(L"open"); + } + else + { + close_stdin = 1; } - /* Clean up our actions */ - posix_spawn_file_actions_destroy(&actions); - posix_spawnattr_destroy(&attr); + break; } - /* 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) + case IO_CLOSE: { - job_mark_process_as_failed(j, p); - exec_error = true; + /* + FIXME: + + 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]; + } + + 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); + + const char *buffer = io_buffer->out_buffer_ptr(); + size_t count = io_buffer->out_buffer_size(); + + 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); + + exec_write_and_exit(io_buffer->fd, buffer, count, status); } else -#endif { - pid = execute_fork(false); - if (pid == 0) - { - /* This is the child process. */ - p->pid = getpid(); - setup_child_process( j, p ); - safe_launch_process( p, actual_cmd, argv, envv ); + /* + 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); - /* - safe_launch_process _never_ returns... - */ - } } + } + 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(); + + /* 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); + } + + 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. + */ + + 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) + { + printf("fork #-: Skipping fork for internal builtin for '%ls'\n", p->argv0()); + } + const wcstring &out = get_stdout_buffer(), &err = get_stderr_buffer(); + char *outbuff = wcs2str(out.c_str()), *errbuff = wcs2str(err.c_str()); + do_builtin_io(outbuff, errbuff); + free(outbuff); + free(errbuff); + skip_fork = 1; + } + + 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; + 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. */ + + /* 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 + { + /* Free the strings in the parent */ + free(outbuff); + free(errbuff); /* This is the parent process. Store away - information on the child, and possibly fice + information on the child, and possibly give it control over the terminal. */ p->pid = pid; - set_child_group( j, p, 0 ); + set_child_group(j, p, 0); - break; - } + } + 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); + if (use_posix_spawn) + { + /* Create posix spawn attributes and actions */ + posix_spawnattr_t attr = posix_spawnattr_t(); + posix_spawn_file_actions_t actions = posix_spawn_file_actions_t(); + bool made_it = fork_actions_make_spawn_properties(&attr, &actions, j, p); + if (made_it) + { + /* 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) + { + job_mark_process_as_failed(j, p); + exec_error = true; + } + } + else +#endif + { + pid = execute_fork(false); + if (pid == 0) + { + /* This is the child process. */ + p->pid = getpid(); + setup_child_process(j, p); + safe_launch_process(p, actual_cmd, argv, envv); + + /* + safe_launch_process _never_ returns... + */ + } + } + + + /* + This is the parent process. Store away + information on the child, and possibly fice + it control over the terminal. + */ + p->pid = pid; + + set_child_group(j, p, 0); + + 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]); + } } - if( p->type == INTERNAL_BUILTIN ) - builtin_pop_io(parser); - /* - Close the pipe the current process uses to read from the - previous process_t + The keepalive process is no longer needed, so we terminate it + with extreme prejudice */ - 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 ) + if (needs_keepalive) { - exec_close(mypipe[1]); + kill(keepalive.pid, SIGKILL); } - } - /* - The keepalive process is no longer needed, so we terminate it - with extreme prejudice - */ - if( needs_keepalive ) - { - kill( keepalive.pid, SIGKILL ); - } + signal_unblock(); - signal_unblock(); + debug(3, L"Job is constructed"); - 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 ); + io_remove(j->io, *iter); } - job_set_flag( j, JOB_CONSTRUCTED, 1 ); + job_set_flag(j, JOB_CONSTRUCTED, 1); - if( !job_get_flag( j, JOB_FOREGROUND ) ) - { - proc_last_bg_pid = j->pgid; - } + 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 ) +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; + 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"); + const env_var_t ifs = env_get_string(L"IFS"); - if( ! ifs.missing_or_empty() ) - { - if( ifs.at(0) < 128 ) + if (! ifs.missing_or_empty()) { - 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( lst ) - { - while( 1 ) - { - if( *end == 0 ) - { - if( begin != end ) + if (ifs.at(0) < 128) { - 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); + sep = '\n';//ifs[0]; } else { - debug( 2, L"Got null string on line %d of file %s", __LINE__, __FILE__ ); + sep = 0; + debug(0, L"Warning - invalid command substitution separator '%lc'. Please change the firsta character of IFS", ifs[0]); } - begin = end+1; - } - end++; + } - } - io_buffer_destroy( io_buffer ); + is_subshell=1; + io_buffer= io_buffer_create(0); - return status; + 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 (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); + + return status; } -int exec_subshell( const wcstring &cmd, std::vector &outputs ) +int exec_subshell(const wcstring &cmd, std::vector &outputs) { ASSERT_IS_MAIN_THREAD(); return exec_subshell_internal(cmd, &outputs); } -__warn_unused int exec_subshell( const wcstring &cmd ) +__warn_unused int exec_subshell(const wcstring &cmd) { ASSERT_IS_MAIN_THREAD(); return exec_subshell_internal(cmd, NULL); diff --git a/exec.h b/exec.h index 9ca30839f..f437095bf 100644 --- a/exec.h +++ b/exec.h @@ -42,7 +42,7 @@ */ class parser_t; -void exec( parser_t &parser, job_t *j ); +void exec(parser_t &parser, job_t *j); /** Evaluate the expression cmd in a subshell, add the outputs into the @@ -54,29 +54,29 @@ void exec( parser_t &parser, job_t *j ); \return the status of the last job to exit, or -1 if en error was encountered. */ -__warn_unused int exec_subshell(const wcstring &cmd, std::vector &outputs ); -__warn_unused int exec_subshell(const wcstring &cmd ); +__warn_unused int exec_subshell(const wcstring &cmd, std::vector &outputs); +__warn_unused int exec_subshell(const wcstring &cmd); /** Loops over close until the syscall was run without being interrupted. Then removes the fd from the open_fds list. */ -void exec_close( int fd ); +void exec_close(int fd); /** Call pipe(), and add resulting fds to open_fds, the list of opend file descriptors for pipes. */ -int exec_pipe( int fd[2]); +int exec_pipe(int fd[2]); /* Close all fds in open_fds. This is called from postfork.cpp */ -void close_unused_internal_pipes( const io_chain_t &io ); +void close_unused_internal_pipes(const io_chain_t &io); /* Gets all unused internal pipes into fds */ void get_unused_internal_pipes(std::vector &fds, const io_chain_t &io); /** Gets the interpreter for a given command */ -char *get_interpreter( const char *command, char *interpreter, size_t buff_size ); +char *get_interpreter(const char *command, char *interpreter, size_t buff_size); #endif diff --git a/expand.cpp b/expand.cpp index b6806c81b..f81c4c0ee 100644 --- a/expand.cpp +++ b/expand.cpp @@ -123,32 +123,32 @@ parameter expansion. */ #define UNCLEAN L"$*?\\\"'({})" -static void remove_internal_separator( wcstring &s, bool conv ); +static void remove_internal_separator(wcstring &s, bool conv); -int expand_is_clean( const wchar_t *in ) +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,125 +156,126 @@ 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); } /** Test if the specified string does not contain character which can not be used inside a quoted string. */ -static int is_quotable( const wchar_t *str ) +static int is_quotable(const wchar_t *str) { - switch( *str ) - { + switch (*str) + { case 0: - return 1; + return 1; case L'\n': case L'\t': case L'\r': case L'\b': case L'\x1b': - return 0; + return 0; default: - return is_quotable(str+1); - } - return 0; + return is_quotable(str+1); + } + return 0; } -static int is_quotable(const wcstring &str) { +static int is_quotable(const wcstring &str) +{ return is_quotable(str.c_str()); } -wcstring expand_escape_variable( const wcstring &in ) +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() ) - { + switch (lst.size()) + { case 0: - buff.append(L"''"); - break; + buff.append(L"''"); + break; case 1: { - const wcstring &el = lst.at(0); + const wcstring &el = lst.at(0); - if( el.find(L' ') != wcstring::npos && is_quotable( el ) ) - { - buff.append(L"'"); - buff.append(el); - buff.append(L"'"); - } - else - { - buff.append(escape_string(el, 1)); - } - break; - } - default: - { - for( size_t j=0; j L'9' ) + for (; *n; n++) { - return 0; + if (*n < L'0' || *n > L'9') + { + return 0; + } } - } - return 1; + return 1; } /** See if the process described by \c proc matches the commandline \c cmd */ -static bool match_pid( const wcstring &cmd, - const wchar_t *proc, - int flags, - size_t *offset) +static bool match_pid(const wcstring &cmd, + 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); @@ -294,13 +295,14 @@ static bool match_pid( const wcstring &cmd, /* BSD / OS X process completions */ -class process_iterator_t { +class process_iterator_t +{ std::vector pids; size_t idx; wcstring name_for_pid(pid_t pid); - public: +public: process_iterator_t(); bool next_process(wcstring *str, pid_t *pid); }; @@ -316,12 +318,14 @@ wcstring process_iterator_t::name_for_pid(pid_t pid) mib[1] = KERN_ARGMAX; size = sizeof(maxarg); - if (sysctl(mib, 2, &maxarg, &size, NULL, 0) == -1) { + if (sysctl(mib, 2, &maxarg, &size, NULL, 0) == -1) + { return result; } - args = (char *)malloc( maxarg ); - if ( args == NULL ) { + args = (char *)malloc(maxarg); + if (args == NULL) + { return result; } @@ -330,12 +334,13 @@ wcstring process_iterator_t::name_for_pid(pid_t pid) mib[2] = pid; size = (size_t)maxarg; - if ( sysctl(mib, 3, args, &size, NULL, 0) == -1 ) { - free( args ); + if (sysctl(mib, 3, args, &size, NULL, 0) == -1) + { + free(args); return result;; } - memcpy( &numArgs, args, sizeof(numArgs) ); + memcpy(&numArgs, args, sizeof(numArgs)); stringPtr = args + sizeof(numArgs); result = str2wcstring(stringPtr); free(args); @@ -388,25 +393,29 @@ process_iterator_t::process_iterator_t() : idx(0) result = NULL; done = false; - do { + do + { assert(result == NULL); // Call sysctl with a NULL buffer. length = 0; - err = sysctl( (int *) name, (sizeof(name) / sizeof(*name)) - 1, + err = sysctl((int *) name, (sizeof(name) / sizeof(*name)) - 1, NULL, &length, NULL, 0); - if (err == -1) { + if (err == -1) + { err = errno; } // Allocate an appropriately sized buffer based on the results // from the previous call. - if (err == 0) { + if (err == 0) + { result = (struct kinfo_proc *)malloc(length); - if (result == NULL) { + if (result == NULL) + { err = ENOMEM; } } @@ -414,23 +423,29 @@ process_iterator_t::process_iterator_t() : idx(0) // 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, + if (err == 0) + { + err = sysctl((int *) name, (sizeof(name) / sizeof(*name)) - 1, result, &length, NULL, 0); - if (err == -1) { + if (err == -1) + { err = errno; } - if (err == 0) { + if (err == 0) + { done = true; - } else if (err == ENOMEM) { + } + else if (err == ENOMEM) + { assert(result != NULL); free(result); result = NULL; err = 0; } } - } while (err == 0 && ! done); + } + while (err == 0 && ! done); // Clean up and establish post conditions. if (err == 0 && result != NULL) @@ -446,10 +461,11 @@ process_iterator_t::process_iterator_t() : idx(0) #else /* /proc style process completions */ -class process_iterator_t { +class process_iterator_t +{ DIR *dir; - public: +public: process_iterator_t(); ~process_iterator_t(); @@ -458,7 +474,7 @@ class process_iterator_t { process_iterator_t::process_iterator_t(void) { - dir = opendir( "/proc" ); + dir = opendir("/proc"); } process_iterator_t::~process_iterator_t(void) @@ -477,16 +493,16 @@ bool process_iterator_t::next_process(wcstring *out_str, pid_t *out_pid) 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); @@ -496,12 +512,12 @@ bool process_iterator_t::next_process(wcstring *out_str, pid_t *out_pid) 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() ); + wchar_t *first_arg = tok_first(full_command_line.c_str()); if (first_arg) { cmd = first_arg; @@ -561,41 +577,41 @@ std::vector expand_get_all_process_names(void) processes are searched for matches. */ -static int find_process( const wchar_t *proc, - expand_flags_t flags, - std::vector &out ) +static int find_process(const wchar_t *proc, + expand_flags_t flags, + std::vector &out) { - int found = 0; + int found = 0; - if (! (flags & EXPAND_SKIP_JOBS)) + if (!(flags & EXPAND_SKIP_JOBS)) { ASSERT_IS_MAIN_THREAD(); const job_t *j; - if( iswnumeric(proc) || (wcslen(proc)==0) ) + if (iswnumeric(proc) || (wcslen(proc)==0)) { /* This is a numeric job string, like '%2' */ - if( flags & ACCEPT_INCOMPLETE ) + if (flags & ACCEPT_INCOMPLETE) { job_iterator_t jobs; while ((j = jobs.next())) { wchar_t jid[16]; - if( j->command_is_empty() ) + if (j->command_is_empty()) continue; - swprintf( jid, 16, L"%d", j->job_id ); + swprintf(jid, 16, L"%d", j->job_id); - if( wcsncmp( proc, jid, wcslen(proc ) )==0 ) + if (wcsncmp(proc, jid, wcslen(proc))==0) { wcstring desc_buff = format_string(COMPLETE_JOB_DESC_VAL, j->command_wcstr()); - append_completion( out, - jid+wcslen(proc), - desc_buff, - 0 ); + append_completion(out, + jid+wcslen(proc), + desc_buff, + 0); } } @@ -607,11 +623,11 @@ static int find_process( const wchar_t *proc, wchar_t *end; errno = 0; - jid = fish_wcstoi( proc, &end, 10 ); - if( jid > 0 && !errno && !*end ) + jid = fish_wcstoi(proc, &end, 10); + if (jid > 0 && !errno && !*end) { - j = job_get( jid ); - if( (j != 0) && (j->command_wcstr() != 0 ) ) + j = job_get(jid); + if ((j != 0) && (j->command_wcstr() != 0)) { { append_completion(out, to_string(j->pgid)); @@ -621,25 +637,25 @@ static int find_process( const wchar_t *proc, } } } - if( found ) + if (found) return 1; job_iterator_t jobs; while ((j = jobs.next())) { - if( j->command_is_empty() ) + if (j->command_is_empty()) continue; size_t offset; - if( match_pid( j->command(), proc, flags, &offset ) ) + if (match_pid(j->command(), proc, flags, &offset)) { - if( flags & ACCEPT_INCOMPLETE ) + if (flags & ACCEPT_INCOMPLETE) { - append_completion( out, - j->command_wcstr() + offset + wcslen(proc), - COMPLETE_JOB_DESC, - 0 ); + append_completion(out, + j->command_wcstr() + offset + wcslen(proc), + COMPLETE_JOB_DESC, + 0); } else { @@ -649,7 +665,7 @@ static int find_process( const wchar_t *proc, } } - if( found ) + if (found) { return 1; } @@ -658,36 +674,36 @@ static int find_process( const wchar_t *proc, while ((j = jobs.next())) { process_t *p; - if( j->command_is_empty() ) + if (j->command_is_empty()) continue; - for( p=j->first_process; p; p=p->next ) + for (p=j->first_process; p; p=p->next) { - if( p->actual_cmd.empty() ) + if (p->actual_cmd.empty()) continue; size_t offset; - if( match_pid( p->actual_cmd, proc, flags, &offset ) ) + if (match_pid(p->actual_cmd, proc, flags, &offset)) { - if( flags & ACCEPT_INCOMPLETE ) + if (flags & ACCEPT_INCOMPLETE) { - append_completion( out, - wcstring(p->actual_cmd, offset + wcslen(proc)), - COMPLETE_CHILD_PROCESS_DESC, - 0 ); + append_completion(out, + wcstring(p->actual_cmd, offset + wcslen(proc)), + COMPLETE_CHILD_PROCESS_DESC, + 0); } else { - append_completion (out, - to_string(p->pid), - L"", - 0); + append_completion(out, + to_string(p->pid), + L"", + 0); found = 1; } } } } - if( found ) + if (found) { return 1; } @@ -700,14 +716,14 @@ static int find_process( const wchar_t *proc, while (iterator.next_process(&process_name, &process_pid)) { size_t offset; - if( match_pid( process_name, proc, flags, &offset ) ) + if (match_pid(process_name, proc, flags, &offset)) { - if( flags & ACCEPT_INCOMPLETE ) + if (flags & ACCEPT_INCOMPLETE) { - append_completion( out, - process_name.c_str() + offset + wcslen(proc), - COMPLETE_PROCESS_DESC, - 0 ); + append_completion(out, + process_name.c_str() + offset + wcslen(proc), + COMPLETE_PROCESS_DESC, + 0); } else { @@ -716,236 +732,238 @@ 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 ) +static int expand_pid(const wcstring &instr_with_sep, + 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 ) + if (flags & ACCEPT_INCOMPLETE) { - append_completion( out, - SELF_STR+wcslen(in+1), - COMPLETE_SELF_DESC, - 0 ); + 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( 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 ) + 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) ) + if (prev == out.size()) { - return 0; + 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 ) +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; + size_t stop_pos = token_pos+1; - switch( token[stop_pos] ) - { + 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 ) ) + 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) { - is_var = 1; + 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 ); + 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; + break; } case INTERNAL_SEPARATOR: { - parser.error( SYNTAX_ERROR, - error_pos, - COMPLETE_VAR_PARAN_DESC ); - break; + parser.error(SYNTAX_ERROR, + error_pos, + COMPLETE_VAR_PARAN_DESC); + break; } case 0: { - parser.error( SYNTAX_ERROR, - error_pos, - COMPLETE_VAR_NULL_DESC ); - break; + 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'*'; + 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; + } } - } } /** Parse an array slicing specification */ -static int parse_slice( const wchar_t *in, wchar_t **end_ptr, std::vector &idx, size_t array_size ) +static int parse_slice(const wchar_t *in, wchar_t **end_ptr, std::vector &idx, size_t array_size) { - wchar_t *end; + wchar_t *end; const long size = (long)array_size; - size_t pos = 1; //skip past the opening square bracket + 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']' ) + while (1) { - pos++; - break; - } + long tmp; - errno=0; - tmp = wcstol( &in[pos], &end, 10 ); - if( ( errno ) || ( end == &in[pos] ) ) - { - return 1; - } + 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; + 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; - // 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 ? tmp1 : size+tmp1+1; + // debug( 0, L"Push range idx %d %d", i1, i2 ); + short direction = i2 happens, don't edit it unless you know exactly what you are doing, and do proper testing afterwards. */ -static int expand_variables_internal( parser_t &parser, wchar_t * const in, std::vector &out, long last_idx ); +static int expand_variables_internal(parser_t &parser, wchar_t * const in, std::vector &out, long last_idx); -static int expand_variables2( parser_t &parser, const wcstring &instr, std::vector &out, long last_idx ) { +static int expand_variables2(parser_t &parser, const wcstring &instr, std::vector &out, long last_idx) +{ wchar_t *in = wcsdup(instr.c_str()); int result = expand_variables_internal(parser, in, out, last_idx); free(in); return result; } -static int expand_variables_internal( parser_t &parser, wchar_t * const in, std::vector &out, long last_idx ) +static int expand_variables_internal(parser_t &parser, wchar_t * const in, std::vector &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 ) ) + for (long i=last_idx; (i>=0) && is_ok && !empty; i--) { - long start_pos = i+1; - long stop_pos; - long var_len; - int is_single = (c==VARIABLE_EXPAND_SINGLE); + 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; + stop_pos = start_pos; - while( 1 ) - { - if( !(in[stop_pos ]) ) - break; - if( !( iswalnum( in[stop_pos] ) || - (wcschr(L"_", in[stop_pos])!= 0) ) ) - break; + while (1) + { + if (!(in[stop_pos ])) + break; + if (!(iswalnum(in[stop_pos]) || + (wcschr(L"_", in[stop_pos])!= 0))) + break; - stop_pos++; - } + stop_pos++; + } /* printf( "Stop for '%c'\n", in[stop_pos]);*/ - var_len = stop_pos - start_pos; + var_len = stop_pos - start_pos; - if( var_len == 0 ) - { - expand_variable_error( parser, in, stop_pos-1, -1 ); + if (var_len == 0) + { + expand_variable_error(parser, in, stop_pos-1, -1); - is_ok = 0; - break; - } + is_ok = 0; + break; + } var_tmp.append(in + start_pos, var_len); - env_var_t var_val = expand_var(var_tmp.c_str() ); + env_var_t var_val = expand_var(var_tmp.c_str()); - if( ! var_val.missing() ) - { - int all_vars=1; + 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 (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 (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 (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 ) - { + 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, + for (size_t j=0; j 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 ) ) ); + 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 ) ) ); 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_ok) + { - if( is_single ) - { + 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) + 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); - is_ok &= expand_variables2( parser, new_in, out, i ); + is_ok &= expand_variables2(parser, new_in, out, i); + } + } + + } + } } - } + return is_ok; } - } - } - - return is_ok; - } - else - { - /* - Expand a non-existing variable - */ - if( c == VARIABLE_EXPAND ) - { - /* - Regular expansion, i.e. expand this argument to nothing - */ - empty = 1; - } - else - { - /* - Expansion to single argument. - */ - wcstring res; + else + { + /* + Expand a non-existing variable + */ + if (c == VARIABLE_EXPAND) + { + /* + Regular expansion, i.e. expand this argument to nothing + */ + empty = 1; + } + else + { + /* + Expansion to single argument. + */ + wcstring res; in[i] = 0; res.append(in); res.append(in + stop_pos); - is_ok &= expand_variables2( parser, res, out, i ); - return is_ok; + is_ok &= expand_variables2(parser, res, out, i); + return is_ok; + } + } + + } - } - - } - } - if( !empty ) - { + if (!empty) + { append_completion(out, in); - } + } - return is_ok; + return is_ok; } /** Perform bracket expansion */ -static int expand_brackets(parser_t &parser, const wcstring &instr, int flags, std::vector &out ) +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 ) + for (pos=in; + (*pos) && !syntax_error; + pos++) { - case BRACKET_BEGIN: - { - bracket_begin = pos; - - bracket_count++; - break; - - } - case BRACKET_END: - { - bracket_count--; - if( bracket_end < bracket_begin ) + switch (*pos) { - bracket_end = pos; - } - - if( bracket_count < 0 ) + case BRACKET_BEGIN: { - syntax_error = true; - } - break; - } - case BRACKET_SEP: - { - if( bracket_count == 1 ) - last_sep = pos; - } - } - } + bracket_begin = pos; - if( bracket_count > 0 ) - { - if( !(flags & ACCEPT_INCOMPLETE) ) - { - syntax_error = true; + bracket_count++; + break; + + } + 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; + } + } } - else + + 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( 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 (syntax_error) { - if( (*pos == BRACKET_SEP) || (pos==bracket_end) ) - { + parser.error(SYNTAX_ERROR, + -1, + _(L"Mismatched brackets")); + return 0; + } + + 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)) + { 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 ); + expand_brackets(parser, whole_item, flags, out); - item_begin = pos+1; - if( pos == bracket_end ) - break; - } - } + item_begin = pos+1; + if (pos == bracket_end) + break; + } + } - if( *pos == BRACKET_BEGIN ) - { - bracket_count++; - } + if (*pos == BRACKET_BEGIN) + { + bracket_count++; + } - if( *pos == BRACKET_END ) - { - bracket_count--; + if (*pos == BRACKET_END) + { + bracket_count--; + } } - } - return 1; + return 1; } /** Perform cmdsubst expansion */ -static int expand_cmdsubst( parser_t &parser, const wcstring &input, std::vector &outList ) +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, - ¶n_begin, - ¶n_end, - 0 ) ) - { + int parse_ret; + switch (parse_ret = parse_util_locate_cmdsubst(in, + ¶n_begin, + ¶n_end, + 0)) + { case -1: - parser.error( SYNTAX_ERROR, - -1, - L"Mismatched parenthesis" ); - return 0; + parser.error(SYNTAX_ERROR, + -1, + L"Mismatched parenthesis"); + return 0; case 0: - outList.push_back(completion_t(input)); - return 1; + outList.push_back(completion_t(input)); + return 1; case 1: - break; - } + 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'[' ) - { - std::vector slice_idx; - wchar_t *slice_end; - - if( parse_slice( tail_begin, &slice_end, slice_idx, sub_res.size() ) ) + if (exec_subshell(subcmd, sub_res) == -1) { - parser.error( SYNTAX_ERROR, -1, L"Invalid index value" ); - return 0; + parser.error(CMDSUBST_ERROR, -1, L"Unknown error while evaulating command substitution"); + return 0; } - else + + tail_begin = paran_end + 1; + if (*tail_begin == L'[') { - 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() ) + 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, - ARRAY_BOUNDS_ERR ); - return 0; + parser.error(SYNTAX_ERROR, -1, L"Invalid index value"); + return 0; } - idx = idx-1; + 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; 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; + //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 - */ + /* + 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 - */ - for( i=0; ipw_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); + } } /** Remove any internal separators. Also optionally convert wildcard characters to regular equivalents. This is done to support EXPAND_SKIP_WILDCARDS. */ -static void remove_internal_separator( wcstring &str, bool conv ) +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()); @@ -1528,12 +1547,12 @@ static void remove_internal_separator( wcstring &str, bool conv ) { switch (str.at(idx)) { - case ANY_CHAR: - str.at(idx) = L'?'; - break; - case ANY_STRING: - case ANY_STRING_RECURSIVE: - str.at(idx) = L'*'; + case ANY_CHAR: + str.at(idx) = L'?'; + break; + case ANY_STRING: + case ANY_STRING_RECURSIVE: + str.at(idx) = L'*'; break; } } @@ -1541,46 +1560,46 @@ static void remove_internal_separator( wcstring &str, bool conv ) } -int expand_string( const wcstring &input, std::vector &output, expand_flags_t flags ) +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; + 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; + 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 ((!(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 (EXPAND_SKIP_CMDSUBST & flags) + { + wchar_t *begin, *end; - if( parse_util_locate_cmdsubst( input.c_str(), + 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; + 1) != 0) + { + parser.error(CMDSUBST_ERROR, -1, L"Command substitutions not allowed"); + return EXPAND_ERROR; + } + list1.push_back(completion_t(input)); } - list1.push_back(completion_t(input)); - } - else - { + 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++ ) + for (i=0; i < in->size(); i++) { /* We accept incomplete strings here, since complete uses @@ -1588,12 +1607,14 @@ int expand_string( const wcstring &input, std::vector &output, exp commandline. */ int unescape_flags = UNESCAPE_SPECIAL | UNESCAPE_INCOMPLETE; - wcstring next = expand_unescape_string( in->at(i).completion, unescape_flags ); + wcstring next = expand_unescape_string(in->at(i).completion, unescape_flags); - if( EXPAND_SKIP_VARIABLES & flags ) + if (EXPAND_SKIP_VARIABLES & flags) { - for (size_t i=0; i < next.size(); i++) { - if (next.at(i) == VARIABLE_EXPAND) { + for (size_t i=0; i < next.size(); i++) + { + if (next.at(i) == VARIABLE_EXPAND) + { next[i] = L'$'; } } @@ -1601,7 +1622,7 @@ int expand_string( const wcstring &input, std::vector &output, exp } else { - if(!expand_variables2( parser, next, *out, next.size() - 1 )) + if (!expand_variables2(parser, next, *out, next.size() - 1)) { return EXPAND_ERROR; } @@ -1613,11 +1634,11 @@ int expand_string( const wcstring &input, std::vector &output, exp in = &list2; out = &list1; - for( i=0; i < in->size(); i++ ) + for (i=0; i < in->size(); i++) { wcstring next = in->at(i).completion; - if( !expand_brackets( parser, next, flags, *out )) + if (!expand_brackets(parser, next, flags, *out)) { return EXPAND_ERROR; } @@ -1627,24 +1648,24 @@ int expand_string( const wcstring &input, std::vector &output, exp in = &list1; out = &list2; - for( i=0; i < in->size(); i++ ) + for (i=0; i < in->size(); i++) { wcstring next = in->at(i).completion; expand_home_directory(next); - if( flags & ACCEPT_INCOMPLETE ) + if (flags & ACCEPT_INCOMPLETE) { - if( next[0] == PROCESS_EXPAND ) + if (next[0] == PROCESS_EXPAND) { /* If process expansion matches, we are not interested in other completions, so we short-circut and return */ - if (! (flags & EXPAND_SKIP_PROCESS )) - expand_pid( next, flags, output ); + if (!(flags & EXPAND_SKIP_PROCESS)) + expand_pid(next, flags, output); return EXPAND_OK; } else @@ -1654,7 +1675,7 @@ int expand_string( const wcstring &input, std::vector &output, exp } else { - if( ! (flags & EXPAND_SKIP_PROCESS ) && ! expand_pid( next, flags, *out ) ) + if (!(flags & EXPAND_SKIP_PROCESS) && ! expand_pid(next, flags, *out)) { return EXPAND_ERROR; } @@ -1666,21 +1687,21 @@ int expand_string( const wcstring &input, std::vector &output, exp in = &list2; out = &list1; - for( i=0; i < in->size(); i++ ) + 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 ) ) + if (((flags & ACCEPT_INCOMPLETE) && (!(flags & EXPAND_SKIP_WILDCARDS))) || + wildcard_has(next, 1)) { const wchar_t *start, *rest; std::vector *list = out; - if( next[0] == '/' ) + if (next[0] == '/') { start = L"/"; rest = &next[1]; @@ -1691,46 +1712,46 @@ int expand_string( const wcstring &input, std::vector &output, exp rest = next; } - if( flags & ACCEPT_INCOMPLETE ) + if (flags & ACCEPT_INCOMPLETE) { list = &output; } wc_res = wildcard_expand_string(rest, start, flags, *list); - if( !(flags & ACCEPT_INCOMPLETE) ) + if (!(flags & ACCEPT_INCOMPLETE)) { - switch( wc_res ) + switch (wc_res) { - case 0: + case 0: + { + if (!(flags & ACCEPT_INCOMPLETE)) { - if( !(flags & ACCEPT_INCOMPLETE) ) - { - if( res == EXPAND_OK ) - res = EXPAND_WILDCARD_NO_MATCH; - 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) ); - } - out->clear(); + if (res == EXPAND_OK) + res = EXPAND_WILDCARD_NO_MATCH; break; } + } - case -1: + case 1: + { + size_t j; + res = EXPAND_WILDCARD_MATCH; + sort_completions(*out); + + for (j=0; j< out->size(); j++) { - return EXPAND_ERROR; + output.push_back(out->at(j)); } + out->clear(); + break; + } + + case -1: + { + return EXPAND_ERROR; + } } } @@ -1738,7 +1759,7 @@ int expand_string( const wcstring &input, std::vector &output, exp } else { - if( flags & ACCEPT_INCOMPLETE) + if (flags & ACCEPT_INCOMPLETE) { } else @@ -1749,23 +1770,26 @@ int expand_string( const wcstring &input, std::vector &output, exp } - return res; + return res; } -bool expand_one(wcstring &string, expand_flags_t flags) { - std::vector completions; - bool result = false; +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() ) ) - { + if ((!(flags & ACCEPT_INCOMPLETE)) && expand_is_clean(string.c_str())) + { return true; - } + } - if (expand_string(string, completions, flags)) { - if (completions.size() == 1) { + 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 799425751..b748bae07 100644 --- a/expand.h +++ b/expand.h @@ -21,7 +21,8 @@ #include "common.h" #include -enum { +enum +{ /** Flag specifying that cmdsubst expansion should be skipped */ EXPAND_SKIP_CMDSUBST = 1 << 0, @@ -69,33 +70,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 represeting process expansion */ + PROCESS_EXPAND, - /** Character representing variable expansion */ - VARIABLE_EXPAND, + /** Character representing variable expansion */ + VARIABLE_EXPAND, - /** Character rpresenting variable expansion into a single element*/ - VARIABLE_EXPAND_SINGLE, + /** Character rpresenting variable expansion into a single element*/ + VARIABLE_EXPAND_SINGLE, - /** Character representing the start of a bracket expansion */ - BRACKET_BEGIN, + /** Character representing the start of a bracket expansion */ + BRACKET_BEGIN, - /** Character representing the end of a bracket expansion */ - BRACKET_END, + /** 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, + /** Character representing separation between two bracket elements */ + BRACKET_SEP, + /** + Separate subtokens in a token with this character. + */ + INTERNAL_SEPARATOR, } - ; +; /** @@ -103,14 +104,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. */ @@ -141,7 +142,7 @@ class parser_t; \param flag Specifies if any expansion pass should be skipped. Legal values are any combination of EXPAND_SKIP_CMDSUBST EXPAND_SKIP_VARIABLES and EXPAND_SKIP_WILDCARDS \return One of EXPAND_OK, EXPAND_ERROR, EXPAND_WILDCARD_MATCH and EXPAND_WILDCARD_NO_MATCH. EXPAND_WILDCARD_NO_MATCH and EXPAND_WILDCARD_MATCH are normal exit conditions used only on strings containing wildcards to tell if the wildcard produced any matches. */ -__warn_unused int expand_string( const wcstring &input, std::vector &output, expand_flags_t flags ); +__warn_unused int expand_string(const wcstring &input, std::vector &output, expand_flags_t flags); /** @@ -153,14 +154,14 @@ __warn_unused int expand_string( const wcstring &input, std::vector= L'0') && (*filter <= L'9')) + if (*filter == L'%') { - width=10*width+(*filter++ - L'0'); - } - } - - while( loop ) - { - - switch(*filter) - { - case L'l': - /* Long variable */ - is_long++; + int is_long=0; + int width = -1; filter++; - break; + int loop=1; + int precision=-1; + int pad_left = 1; - case L'*': - /* Set minimum field width */ - width = va_arg( va, int ); - filter++; - break; - - case L'-': - filter++; - pad_left=0; - break; - - case L'.': - /* - Set precision. - */ - filter++; - if( *filter == L'*' ) + if (iswdigit(*filter)) { - 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( 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; - } - - 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: + width=0; + while ((*filter >= L'0') && (*filter <= L'9')) { - int d = va_arg( va, int ); - if( precision >= 0 ) - snprintf( str, 32, format, precision, d ); - else - snprintf( str, 32, format, d ); - - break; + width=10*width+(*filter++ - L'0'); } + } - case 1: + while (loop) + { + + switch (*filter) { - long d = va_arg( va, long ); - if( precision >= 0 ) - snprintf( str, 32, format, precision, d ); - else - snprintf( str, 32, format, d ); - break; - } + case L'l': + /* Long variable */ + is_long++; + filter++; + 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; - } + case L'*': + /* Set minimum field width */ + width = va_arg(va, int); + filter++; + break; + + 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: - debug( 0, L"Invalid length modifier in string %ls\n", filter_org ); - return -1; - } - break; - + loop=0; + break; + } } - case L'u': + 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 (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; + } + + 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; - switch( is_long ) - { - case 0: + format[0]=0; + strcat(format, "%"); + if (precision >= 0) + strcat(format, ".*"); + switch (is_long) { - 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: + strcat(format, "ll"); + break; + case 1: + strcat(format, "l"); + break; + } + + len = strlen(format); + format[len++]=(char)*filter; + format[len]=0; + + switch (*filter) { - 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; + 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 length modifier in string %ls\n", filter_org ); - return -1; - } - break; + 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"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++) ); + debug(0, L"Unknown switch %lc in string %ls\n", *filter, filter_org); + return -1; + } + } + else + { + writer(*filter); 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; + return count; } /** @@ -552,53 +552,53 @@ 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; /** Writers for string output */ -static void sw_writer( wchar_t c ) +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 vswprintf(wchar_t *out, size_t n, const wchar_t *filter, va_list va) { - int 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; - } + 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; + return written; } -int swprintf( wchar_t *out, size_t n, const wchar_t *filter, ... ) +int swprintf(wchar_t *out, size_t n, const wchar_t *filter, ...) { - va_list va; - int written; + va_list va; + int written; - va_start( va, filter ); - written = vswprintf( out, n, filter, va ); - va_end( va ); - return written; + va_start(va, filter); + written = vswprintf(out, n, filter, va); + va_end(va); + return written; } /** @@ -606,45 +606,45 @@ int swprintf( wchar_t *out, size_t n, const wchar_t *filter, ... ) */ static FILE *fw_data; -static void fw_writer( wchar_t c ) +static void fw_writer(wchar_t c) { - putwc( c, fw_data ); + putwc(c, fw_data); } /* Writers for file output */ -int vfwprintf( FILE *f, const wchar_t *filter, va_list va ) +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, ... ) +int fwprintf(FILE *f, const wchar_t *filter, ...) { - va_list va; - int written; + va_list va; + int written; - va_start( va, filter ); - written = vfwprintf( f, filter, va ); - va_end( va ); - return written; + va_start(va, filter); + written = vfwprintf(f, filter, va); + va_end(va); + return written; } -int vwprintf( const wchar_t *filter, va_list va ) +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, ... ) +int wprintf(const wchar_t *filter, ...) { - va_list va; - int written; + va_list va; + int written; - va_start( va, filter ); - written=vwprintf( filter, va ); - va_end( va ); - return 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; - - bb=b; - - sz = mbrtowc( &res, &bb, 1, &state ); - - switch( sz ) + while (1) { - case -1: - memset (&state, '\0', sizeof (state)); - return WEOF; + int b = fgetc(stream); + char bb; - case -2: - break; - case 0: - return 0; - default: - return res; + int sz; + + 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 @@ -721,38 +721,38 @@ 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 ) +static size_t fish_wcsspn(const wchar_t *wcs, + 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; + for (a = accept; *a != L'\0'; ++a) + if (*p == *a) + break; - if (*a == L'\0') - return count; - else - ++count; + if (*a == L'\0') + return count; + else + ++count; } - return count; + return count; } /* Used by fallback wcstok. Borrowed from glibc */ -static wchar_t *fish_wcspbrk (const wchar_t *wcs, const wchar_t *accept) +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; + } + + /* Scan leading delimiters. */ + wcs += fish_wcsspn(wcs, delim); + + if (*wcs == L'\0') + { + *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 - wcs = *save_ptr; - } - - /* Scan leading delimiters. */ - wcs += fish_wcsspn (wcs, delim); - - if (*wcs == L'\0') { - *save_ptr = NULL; - return NULL; + /* Terminate the token and make *SAVE_PTR point past it. */ + *wcs = L'\0'; + *save_ptr = wcs + 1; } - - /* 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; - } - 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 ) +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); } @@ -854,142 +854,142 @@ int wcscasecmp_use_weak(const wchar_t *a, const wchar_t *b) #else //__APPLE__ - #ifndef HAVE_WCSDUP -wchar_t *wcsdup( const wchar_t *in ) +#ifndef HAVE_WCSDUP +wchar_t *wcsdup(const wchar_t *in) { - return wcsdup_fallback(in); + return wcsdup_fallback(in); } - #endif +#endif - #ifndef HAVE_WCSCASECMP -int wcscasecmp( const wchar_t *a, const wchar_t *b ) +#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 #endif //__APPLE__ #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 ) +int wcsncasecmp(const wchar_t *a, const wchar_t *b, int count) { - if( count == 0 ) - return 0; + 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 (*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 ) +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 *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 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; - } + 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; + return res; } #ifndef HAVE_WCSTOL long wcstol(const wchar_t *nptr, - wchar_t **endptr, - int base) + wchar_t **endptr, + int base) { - 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 ) + long long res=0; + int is_set=0; + if (base > 36) { - if( !is_set ) - { errno = EINVAL; - } - return res; + return 0; } - res = (res*base)+nxt; - is_set = 1; - if( res > LONG_MAX ) + + while (1) { - errno = ERANGE; - return LONG_MAX; + 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++; } - if( res < LONG_MIN ) - { - errno = ERANGE; - return LONG_MIN; - } - nptr++; - } } #endif @@ -1017,34 +1017,34 @@ 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; + 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++; + /* 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; + dlen = d - dst; + n = siz - dlen; - if (n == 0) - return(dlen + wcslen(s)); + if (n == 0) + return(dlen + wcslen(s)); - while (*s != '\0') - { - if (n != 1) + while (*s != '\0') { - *d++ = *s; - n--; + if (n != 1) + { + *d++ = *s; + n--; + } + s++; } - s++; - } - *d = '\0'; + *d = '\0'; - return(dlen + (s - src)); - /* count does not include NUL */ + 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; + 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 + /* Copy as many bytes as will fit */ + if (n != 0 && --n != 0) { - if ((*d++ = *s++) == 0) - break; + do + { + if ((*d++ = *s++) == 0) + break; + } + while (--n != 0); } - 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 */ + /* 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,38 +1121,38 @@ 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 #ifndef HAVE_GETTEXT -char * gettext (const char * msgid) +char * gettext(const char * msgid) { - return (char *)msgid; + return (char *)msgid; } -char * bindtextdomain (const char * domainname, const char * dirname) +char * bindtextdomain(const char * domainname, const char * dirname) { - return 0; + return 0; } -char * textdomain (const char * domainname) +char * textdomain(const char * domainname) { - return 0; + return 0; } #endif #ifndef HAVE_DCGETTEXT -char * dcgettext ( const char * domainname, - const char * msgid, - int category) +char * dcgettext(const char * domainname, + const char * msgid, + int category) { - return (char *)msgid; + return (char *)msgid; } @@ -1165,38 +1165,38 @@ int _nl_msg_cat_cntr=0; #endif #ifndef HAVE_KILLPG -int killpg( int pgr, int sig ) +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); } #endif #ifndef HAVE_BACKTRACE -int backtrace (void **buffer, int size) +int backtrace(void **buffer, int size) { - return 0; + return 0; } #endif #ifndef HAVE_BACKTRACE_SYMBOLS -char ** backtrace_symbols (void *const *buffer, int size) +char ** backtrace_symbols(void *const *buffer, int size) { - return 0; + return 0; } #endif @@ -1204,14 +1204,14 @@ 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 @@ -1219,17 +1219,17 @@ long sysconf(int name) #ifndef HAVE_NAN double nan(char *tagp) { - return 0.0/0.0; + return 0.0/0.0; } #endif /* Big hack to use our versions of wcswidth where we know them to be broken, like on OS X */ #ifndef HAVE_BROKEN_WCWIDTH - #if __APPLE__ - #define HAVE_BROKEN_WCWIDTH 1 - #else - #define HAVE_BROKEN_WCWIDTH 0 - #endif +#if __APPLE__ +#define HAVE_BROKEN_WCWIDTH 1 +#else +#define HAVE_BROKEN_WCWIDTH 0 +#endif #endif #if ! HAVE_BROKEN_WCWIDTH @@ -1322,29 +1322,32 @@ int fish_wcswidth(const wchar_t *str, size_t n) #include -struct interval { - int first; - int last; +struct interval +{ + int first; + int last; }; /* auxiliary function for binary search in interval table */ -static int bisearch(wchar_t ucs, const struct interval *table, int max) { - int min = 0; - int mid; +static int bisearch(wchar_t ucs, const struct interval *table, int max) +{ + int min = 0; + int mid; + + if (ucs < table[0].first || ucs > table[max].last) + return 0; + while (max >= min) + { + mid = (min + max) / 2; + if (ucs > table[mid].last) + min = mid + 1; + else if (ucs < table[mid].first) + max = mid - 1; + else + return 1; + } - if (ucs < table[0].first || ucs > table[max].last) return 0; - while (max >= min) { - mid = (min + max) / 2; - if (ucs > table[mid].last) - min = mid + 1; - else if (ucs < table[mid].first) - max = mid - 1; - else - return 1; - } - - return 0; } @@ -1382,99 +1385,100 @@ static int bisearch(wchar_t ucs, const struct interval *table, int max) { static int mk_wcwidth(wchar_t ucs) { - /* sorted list of non-overlapping intervals of non-spacing characters */ - /* generated by "uniset +cat=Me +cat=Mn +cat=Cf -00AD +1160-11FF +200B c" */ - static const struct interval combining[] = { - { 0x0300, 0x036F }, { 0x0483, 0x0486 }, { 0x0488, 0x0489 }, - { 0x0591, 0x05BD }, { 0x05BF, 0x05BF }, { 0x05C1, 0x05C2 }, - { 0x05C4, 0x05C5 }, { 0x05C7, 0x05C7 }, { 0x0600, 0x0603 }, - { 0x0610, 0x0615 }, { 0x064B, 0x065E }, { 0x0670, 0x0670 }, - { 0x06D6, 0x06E4 }, { 0x06E7, 0x06E8 }, { 0x06EA, 0x06ED }, - { 0x070F, 0x070F }, { 0x0711, 0x0711 }, { 0x0730, 0x074A }, - { 0x07A6, 0x07B0 }, { 0x07EB, 0x07F3 }, { 0x0901, 0x0902 }, - { 0x093C, 0x093C }, { 0x0941, 0x0948 }, { 0x094D, 0x094D }, - { 0x0951, 0x0954 }, { 0x0962, 0x0963 }, { 0x0981, 0x0981 }, - { 0x09BC, 0x09BC }, { 0x09C1, 0x09C4 }, { 0x09CD, 0x09CD }, - { 0x09E2, 0x09E3 }, { 0x0A01, 0x0A02 }, { 0x0A3C, 0x0A3C }, - { 0x0A41, 0x0A42 }, { 0x0A47, 0x0A48 }, { 0x0A4B, 0x0A4D }, - { 0x0A70, 0x0A71 }, { 0x0A81, 0x0A82 }, { 0x0ABC, 0x0ABC }, - { 0x0AC1, 0x0AC5 }, { 0x0AC7, 0x0AC8 }, { 0x0ACD, 0x0ACD }, - { 0x0AE2, 0x0AE3 }, { 0x0B01, 0x0B01 }, { 0x0B3C, 0x0B3C }, - { 0x0B3F, 0x0B3F }, { 0x0B41, 0x0B43 }, { 0x0B4D, 0x0B4D }, - { 0x0B56, 0x0B56 }, { 0x0B82, 0x0B82 }, { 0x0BC0, 0x0BC0 }, - { 0x0BCD, 0x0BCD }, { 0x0C3E, 0x0C40 }, { 0x0C46, 0x0C48 }, - { 0x0C4A, 0x0C4D }, { 0x0C55, 0x0C56 }, { 0x0CBC, 0x0CBC }, - { 0x0CBF, 0x0CBF }, { 0x0CC6, 0x0CC6 }, { 0x0CCC, 0x0CCD }, - { 0x0CE2, 0x0CE3 }, { 0x0D41, 0x0D43 }, { 0x0D4D, 0x0D4D }, - { 0x0DCA, 0x0DCA }, { 0x0DD2, 0x0DD4 }, { 0x0DD6, 0x0DD6 }, - { 0x0E31, 0x0E31 }, { 0x0E34, 0x0E3A }, { 0x0E47, 0x0E4E }, - { 0x0EB1, 0x0EB1 }, { 0x0EB4, 0x0EB9 }, { 0x0EBB, 0x0EBC }, - { 0x0EC8, 0x0ECD }, { 0x0F18, 0x0F19 }, { 0x0F35, 0x0F35 }, - { 0x0F37, 0x0F37 }, { 0x0F39, 0x0F39 }, { 0x0F71, 0x0F7E }, - { 0x0F80, 0x0F84 }, { 0x0F86, 0x0F87 }, { 0x0F90, 0x0F97 }, - { 0x0F99, 0x0FBC }, { 0x0FC6, 0x0FC6 }, { 0x102D, 0x1030 }, - { 0x1032, 0x1032 }, { 0x1036, 0x1037 }, { 0x1039, 0x1039 }, - { 0x1058, 0x1059 }, { 0x1160, 0x11FF }, { 0x135F, 0x135F }, - { 0x1712, 0x1714 }, { 0x1732, 0x1734 }, { 0x1752, 0x1753 }, - { 0x1772, 0x1773 }, { 0x17B4, 0x17B5 }, { 0x17B7, 0x17BD }, - { 0x17C6, 0x17C6 }, { 0x17C9, 0x17D3 }, { 0x17DD, 0x17DD }, - { 0x180B, 0x180D }, { 0x18A9, 0x18A9 }, { 0x1920, 0x1922 }, - { 0x1927, 0x1928 }, { 0x1932, 0x1932 }, { 0x1939, 0x193B }, - { 0x1A17, 0x1A18 }, { 0x1B00, 0x1B03 }, { 0x1B34, 0x1B34 }, - { 0x1B36, 0x1B3A }, { 0x1B3C, 0x1B3C }, { 0x1B42, 0x1B42 }, - { 0x1B6B, 0x1B73 }, { 0x1DC0, 0x1DCA }, { 0x1DFE, 0x1DFF }, - { 0x200B, 0x200F }, { 0x202A, 0x202E }, { 0x2060, 0x2063 }, - { 0x206A, 0x206F }, { 0x20D0, 0x20EF }, { 0x302A, 0x302F }, - { 0x3099, 0x309A }, { 0xA806, 0xA806 }, { 0xA80B, 0xA80B }, - { 0xA825, 0xA826 }, { 0xFB1E, 0xFB1E }, { 0xFE00, 0xFE0F }, - { 0xFE20, 0xFE23 }, { 0xFEFF, 0xFEFF }, { 0xFFF9, 0xFFFB }, - { 0x10A01, 0x10A03 }, { 0x10A05, 0x10A06 }, { 0x10A0C, 0x10A0F }, - { 0x10A38, 0x10A3A }, { 0x10A3F, 0x10A3F }, { 0x1D167, 0x1D169 }, - { 0x1D173, 0x1D182 }, { 0x1D185, 0x1D18B }, { 0x1D1AA, 0x1D1AD }, - { 0x1D242, 0x1D244 }, { 0xE0001, 0xE0001 }, { 0xE0020, 0xE007F }, - { 0xE0100, 0xE01EF } - }; + /* sorted list of non-overlapping intervals of non-spacing characters */ + /* generated by "uniset +cat=Me +cat=Mn +cat=Cf -00AD +1160-11FF +200B c" */ + static const struct interval combining[] = + { + { 0x0300, 0x036F }, { 0x0483, 0x0486 }, { 0x0488, 0x0489 }, + { 0x0591, 0x05BD }, { 0x05BF, 0x05BF }, { 0x05C1, 0x05C2 }, + { 0x05C4, 0x05C5 }, { 0x05C7, 0x05C7 }, { 0x0600, 0x0603 }, + { 0x0610, 0x0615 }, { 0x064B, 0x065E }, { 0x0670, 0x0670 }, + { 0x06D6, 0x06E4 }, { 0x06E7, 0x06E8 }, { 0x06EA, 0x06ED }, + { 0x070F, 0x070F }, { 0x0711, 0x0711 }, { 0x0730, 0x074A }, + { 0x07A6, 0x07B0 }, { 0x07EB, 0x07F3 }, { 0x0901, 0x0902 }, + { 0x093C, 0x093C }, { 0x0941, 0x0948 }, { 0x094D, 0x094D }, + { 0x0951, 0x0954 }, { 0x0962, 0x0963 }, { 0x0981, 0x0981 }, + { 0x09BC, 0x09BC }, { 0x09C1, 0x09C4 }, { 0x09CD, 0x09CD }, + { 0x09E2, 0x09E3 }, { 0x0A01, 0x0A02 }, { 0x0A3C, 0x0A3C }, + { 0x0A41, 0x0A42 }, { 0x0A47, 0x0A48 }, { 0x0A4B, 0x0A4D }, + { 0x0A70, 0x0A71 }, { 0x0A81, 0x0A82 }, { 0x0ABC, 0x0ABC }, + { 0x0AC1, 0x0AC5 }, { 0x0AC7, 0x0AC8 }, { 0x0ACD, 0x0ACD }, + { 0x0AE2, 0x0AE3 }, { 0x0B01, 0x0B01 }, { 0x0B3C, 0x0B3C }, + { 0x0B3F, 0x0B3F }, { 0x0B41, 0x0B43 }, { 0x0B4D, 0x0B4D }, + { 0x0B56, 0x0B56 }, { 0x0B82, 0x0B82 }, { 0x0BC0, 0x0BC0 }, + { 0x0BCD, 0x0BCD }, { 0x0C3E, 0x0C40 }, { 0x0C46, 0x0C48 }, + { 0x0C4A, 0x0C4D }, { 0x0C55, 0x0C56 }, { 0x0CBC, 0x0CBC }, + { 0x0CBF, 0x0CBF }, { 0x0CC6, 0x0CC6 }, { 0x0CCC, 0x0CCD }, + { 0x0CE2, 0x0CE3 }, { 0x0D41, 0x0D43 }, { 0x0D4D, 0x0D4D }, + { 0x0DCA, 0x0DCA }, { 0x0DD2, 0x0DD4 }, { 0x0DD6, 0x0DD6 }, + { 0x0E31, 0x0E31 }, { 0x0E34, 0x0E3A }, { 0x0E47, 0x0E4E }, + { 0x0EB1, 0x0EB1 }, { 0x0EB4, 0x0EB9 }, { 0x0EBB, 0x0EBC }, + { 0x0EC8, 0x0ECD }, { 0x0F18, 0x0F19 }, { 0x0F35, 0x0F35 }, + { 0x0F37, 0x0F37 }, { 0x0F39, 0x0F39 }, { 0x0F71, 0x0F7E }, + { 0x0F80, 0x0F84 }, { 0x0F86, 0x0F87 }, { 0x0F90, 0x0F97 }, + { 0x0F99, 0x0FBC }, { 0x0FC6, 0x0FC6 }, { 0x102D, 0x1030 }, + { 0x1032, 0x1032 }, { 0x1036, 0x1037 }, { 0x1039, 0x1039 }, + { 0x1058, 0x1059 }, { 0x1160, 0x11FF }, { 0x135F, 0x135F }, + { 0x1712, 0x1714 }, { 0x1732, 0x1734 }, { 0x1752, 0x1753 }, + { 0x1772, 0x1773 }, { 0x17B4, 0x17B5 }, { 0x17B7, 0x17BD }, + { 0x17C6, 0x17C6 }, { 0x17C9, 0x17D3 }, { 0x17DD, 0x17DD }, + { 0x180B, 0x180D }, { 0x18A9, 0x18A9 }, { 0x1920, 0x1922 }, + { 0x1927, 0x1928 }, { 0x1932, 0x1932 }, { 0x1939, 0x193B }, + { 0x1A17, 0x1A18 }, { 0x1B00, 0x1B03 }, { 0x1B34, 0x1B34 }, + { 0x1B36, 0x1B3A }, { 0x1B3C, 0x1B3C }, { 0x1B42, 0x1B42 }, + { 0x1B6B, 0x1B73 }, { 0x1DC0, 0x1DCA }, { 0x1DFE, 0x1DFF }, + { 0x200B, 0x200F }, { 0x202A, 0x202E }, { 0x2060, 0x2063 }, + { 0x206A, 0x206F }, { 0x20D0, 0x20EF }, { 0x302A, 0x302F }, + { 0x3099, 0x309A }, { 0xA806, 0xA806 }, { 0xA80B, 0xA80B }, + { 0xA825, 0xA826 }, { 0xFB1E, 0xFB1E }, { 0xFE00, 0xFE0F }, + { 0xFE20, 0xFE23 }, { 0xFEFF, 0xFEFF }, { 0xFFF9, 0xFFFB }, + { 0x10A01, 0x10A03 }, { 0x10A05, 0x10A06 }, { 0x10A0C, 0x10A0F }, + { 0x10A38, 0x10A3A }, { 0x10A3F, 0x10A3F }, { 0x1D167, 0x1D169 }, + { 0x1D173, 0x1D182 }, { 0x1D185, 0x1D18B }, { 0x1D1AA, 0x1D1AD }, + { 0x1D242, 0x1D244 }, { 0xE0001, 0xE0001 }, { 0xE0020, 0xE007F }, + { 0xE0100, 0xE01EF } + }; - /* test for 8-bit control characters */ - if (ucs == 0) - return 0; - if (ucs < 32 || (ucs >= 0x7f && ucs < 0xa0)) - return -1; + /* test for 8-bit control characters */ + if (ucs == 0) + return 0; + if (ucs < 32 || (ucs >= 0x7f && ucs < 0xa0)) + return -1; - /* binary search in table of non-spacing characters */ - if (bisearch(ucs, combining, - sizeof(combining) / sizeof(struct interval) - 1)) - return 0; + /* binary search in table of non-spacing characters */ + if (bisearch(ucs, combining, + sizeof(combining) / sizeof(struct interval) - 1)) + return 0; - /* if we arrive here, ucs is not a combining or C0/C1 control character */ + /* if we arrive here, ucs is not a combining or C0/C1 control character */ - return 1 + - (ucs >= 0x1100 && - (ucs <= 0x115f || /* Hangul Jamo init. consonants */ - ucs == 0x2329 || ucs == 0x232a || - (ucs >= 0x2e80 && ucs <= 0xa4cf && - ucs != 0x303f) || /* CJK ... Yi */ - (ucs >= 0xac00 && ucs <= 0xd7a3) || /* Hangul Syllables */ - (ucs >= 0xf900 && ucs <= 0xfaff) || /* CJK Compatibility Ideographs */ - (ucs >= 0xfe10 && ucs <= 0xfe19) || /* Vertical forms */ - (ucs >= 0xfe30 && ucs <= 0xfe6f) || /* CJK Compatibility Forms */ - (ucs >= 0xff00 && ucs <= 0xff60) || /* Fullwidth Forms */ - (ucs >= 0xffe0 && ucs <= 0xffe6) || - (ucs >= 0x20000 && ucs <= 0x2fffd) || - (ucs >= 0x30000 && ucs <= 0x3fffd))); + return 1 + + (ucs >= 0x1100 && + (ucs <= 0x115f || /* Hangul Jamo init. consonants */ + ucs == 0x2329 || ucs == 0x232a || + (ucs >= 0x2e80 && ucs <= 0xa4cf && + ucs != 0x303f) || /* CJK ... Yi */ + (ucs >= 0xac00 && ucs <= 0xd7a3) || /* Hangul Syllables */ + (ucs >= 0xf900 && ucs <= 0xfaff) || /* CJK Compatibility Ideographs */ + (ucs >= 0xfe10 && ucs <= 0xfe19) || /* Vertical forms */ + (ucs >= 0xfe30 && ucs <= 0xfe6f) || /* CJK Compatibility Forms */ + (ucs >= 0xff00 && ucs <= 0xff60) || /* Fullwidth Forms */ + (ucs >= 0xffe0 && ucs <= 0xffe6) || + (ucs >= 0x20000 && ucs <= 0x2fffd) || + (ucs >= 0x30000 && ucs <= 0x3fffd))); } static int mk_wcswidth(const wchar_t *pwcs, size_t n) { - int w, width = 0; + int w, width = 0; - for (;*pwcs && n-- > 0; pwcs++) - if ((w = mk_wcwidth(*pwcs)) < 0) - return -1; - else - width += w; + for (; *pwcs && n-- > 0; pwcs++) + if ((w = mk_wcwidth(*pwcs)) < 0) + return -1; + else + width += w; - return width; + return width; } #endif // HAVE_BROKEN_WCWIDTH diff --git a/fallback.h b/fallback.h index 3b852b30f..b9460f2b3 100644 --- a/fallback.h +++ b/fallback.h @@ -63,14 +63,14 @@ typedef char tputs_arg_t; */ 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; } ; @@ -94,7 +94,7 @@ int tputs(const char *str, int affcnt, int (*fish_putc)(tputs_arg_t)); */ #define tparm tparm_solaris_kludge -char *tparm_solaris_kludge( char *str, ... ); +char *tparm_solaris_kludge(char *str, ...); #endif @@ -107,7 +107,7 @@ char *tparm_solaris_kludge( char *str, ... ); strings and decimal numbers, position (%n), field width and precision. */ -int fwprintf( FILE *f, const wchar_t *format, ... ); +int fwprintf(FILE *f, const wchar_t *format, ...); /** @@ -117,7 +117,7 @@ int fwprintf( FILE *f, const wchar_t *format, ... ); strings and decimal numbers, position (%n), field width and precision. */ -int swprintf( wchar_t *str, size_t l, const wchar_t *format, ... ); +int swprintf(wchar_t *str, size_t l, const wchar_t *format, ...); /** Print formated string. Some operating systems (Like NetBSD) do not @@ -126,7 +126,7 @@ int swprintf( wchar_t *str, size_t l, const wchar_t *format, ... ); strings and decimal numbers, position (%n), field width and precision. */ -int wprintf( const wchar_t *format, ... ); +int wprintf(const wchar_t *format, ...); /** Print formated string. Some operating systems (Like NetBSD) do not @@ -135,7 +135,7 @@ int wprintf( const wchar_t *format, ... ); strings and decimal numbers, position (%n), field width and precision. */ -int vwprintf( const wchar_t *filter, va_list va ); +int vwprintf(const wchar_t *filter, va_list va); /** Print formated string. Some operating systems (Like NetBSD) do not @@ -144,7 +144,7 @@ int vwprintf( const wchar_t *filter, va_list va ); strings and decimal numbers, position (%n), field width and precision. */ -int vfwprintf( FILE *f, const wchar_t *filter, va_list va ); +int vfwprintf(FILE *f, const wchar_t *filter, va_list va); /** Print formated string. Some operating systems (Like NetBSD) do not @@ -153,7 +153,7 @@ int vfwprintf( FILE *f, const wchar_t *filter, va_list va ); strings and decimal numbers, position (%n), field width and precision. */ -int vswprintf( wchar_t *out, size_t n, const wchar_t *filter, va_list va ); +int vswprintf(wchar_t *out, size_t n, const wchar_t *filter, va_list va); #endif @@ -205,7 +205,7 @@ wchar_t *wcstok(wchar_t *wcs, const wchar_t *delim, wchar_t **ptr); real wcwidth. Therefore, the fallback wcwidth assumes any printing character takes up one column and anything else uses 0 columns. */ -int wcwidth( wchar_t c ); +int wcwidth(wchar_t c); #endif @@ -215,14 +215,14 @@ int wcwidth( wchar_t c ); On other platforms, use what's detected at build time. */ #if __APPLE__ && __DARWIN_C_LEVEL >= 200809L - wchar_t *wcsdup_use_weak(const wchar_t *); - 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)) +wchar_t *wcsdup_use_weak(const wchar_t *); +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 +#ifndef HAVE_WCSDUP /** Create a duplicate string. Wide string version of strdup. Will @@ -230,9 +230,9 @@ int wcwidth( wchar_t c ); */ wchar_t *wcsdup(const wchar_t *in); - #endif +#endif - #ifndef HAVE_WCSCASECMP +#ifndef HAVE_WCSCASECMP /** Case insensitive string compare function. Wide string version of strcasecmp. @@ -244,9 +244,9 @@ wchar_t *wcsdup(const wchar_t *in); fish and guaranteed to be a sane, english word. Using wcscasecmp on a user-supplied string should be considered a bug. */ -int wcscasecmp( const wchar_t *a, const wchar_t *b ); +int wcscasecmp(const wchar_t *a, const wchar_t *b); - #endif +#endif #endif //__APPLE__ @@ -273,7 +273,7 @@ size_t wcslen(const wchar_t *in); fish and guaranteed to be a sane, english word. Using wcsncasecmp on a user-supplied string should be considered a bug. */ -int wcsncasecmp( const wchar_t *a, const wchar_t *b, int count ); +int wcsncasecmp(const wchar_t *a, const wchar_t *b, int count); /** Returns a newly allocated wide character string wich is a copy of @@ -290,7 +290,7 @@ int wcsncasecmp( const wchar_t *a, const wchar_t *b, int count ); Fallback for wcsndup function. Returns a copy of \c in, truncated to a maximum length of \c c. */ -wchar_t *wcsndup( const wchar_t *in, size_t c ); +wchar_t *wcsndup(const wchar_t *in, size_t c); #endif @@ -299,7 +299,7 @@ wchar_t *wcsndup( const wchar_t *in, size_t c ); a valid digit in the specified base, return -1. This is a helper function for wcstol, but it is useful itself, so it is exported. */ -long convert_digit( wchar_t d, int base ); +long convert_digit(wchar_t d, int base); #ifndef HAVE_WCSTOL @@ -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 @@ -329,7 +329,7 @@ long wcstol(const wchar_t *nptr, and renamed to reflect this change. */ -size_t wcslcat( wchar_t *dst, const wchar_t *src, size_t siz ); +size_t wcslcat(wchar_t *dst, const wchar_t *src, size_t siz); #endif #ifndef HAVE_WCSLCPY @@ -342,7 +342,7 @@ size_t wcslcat( wchar_t *dst, const wchar_t *src, size_t siz ); This is the OpenBSD strlcpy function, modified for wide characters, and renamed to reflect this change. */ -size_t wcslcpy( wchar_t *dst, const wchar_t *src, size_t siz ); +size_t wcslcpy(wchar_t *dst, const wchar_t *src, size_t siz); #endif @@ -361,28 +361,28 @@ 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; } ; /** Fallback implementation of lrand48_r. Internally uses rand_r, so it is pretty weak. */ -int lrand48_r( struct drand48_data *buffer, long int *result ); +int lrand48_r(struct drand48_data *buffer, long int *result); /** Fallback implementation of srand48_r, the seed function for lrand48_r. */ -int srand48_r( long int seedval, struct drand48_data *buffer ); +int srand48_r(long int seedval, struct drand48_data *buffer); #endif #ifndef HAVE_FUTIMES -int futimes( int fd, const struct timeval *times ); +int futimes(int fd, const struct timeval *times); #endif @@ -391,17 +391,17 @@ int futimes( int fd, const struct timeval *times ); /** Fallback implementation of gettext. Just returns the original string. */ -char * gettext( const char * msgid ); +char * gettext(const char * msgid); /** Fallback implementation of bindtextdomain. Does nothing. */ -char * bindtextdomain( const char * domainname, const char * dirname ); +char * bindtextdomain(const char * domainname, const char * dirname); /** Fallback implementation of textdomain. Does nothing. */ -char * textdomain( const char * domainname ); +char * textdomain(const char * domainname); #endif @@ -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 @@ -432,7 +432,7 @@ extern int _nl_msg_cat_cntr; /** Send specified signal to specified process group. */ -int killpg( int pgr, int sig ); +int killpg(int pgr, int sig); #endif @@ -443,22 +443,22 @@ int killpg( int pgr, int sig ); */ 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; } ; @@ -475,10 +475,10 @@ struct option #endif int getopt_long(int argc, - char * const argv[], - const char *optstring, - const struct option *longopts, - int *longindex); + char * const argv[], + const char *optstring, + const struct option *longopts, + int *longindex); #endif diff --git a/fish.cpp b/fish.cpp index 08fc25441..08afe9478 100644 --- a/fish.cpp +++ b/fish.cpp @@ -75,7 +75,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. static bool has_suffix(const std::string &path, const char *suffix, bool ignore_case) { size_t pathlen = path.size(), suffixlen = strlen(suffix); - return pathlen >= suffixlen && ! (ignore_case ? strcasecmp : strcmp)(path.c_str() + pathlen - suffixlen, suffix); + return pathlen >= suffixlen && !(ignore_case ? strcasecmp : strcmp)(path.c_str() + pathlen - suffixlen, suffix); } /* Modifies the given path by calling realpath. Returns true if realpath succeeded, false otherwise */ @@ -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); @@ -119,13 +119,13 @@ static std::string get_executable_path(const char *argv0) { /* On other Unixes, try /proc directory. This might be worth breaking out into macros. */ if (0 < readlink("/proc/self/exe", buff, sizeof buff) || // Linux - 0 < readlink("/proc/curproc/file", buff, sizeof buff) || // BSD - 0 < readlink("/proc/self/path/a.out", buff, sizeof buff)) // Solaris + 0 < readlink("/proc/curproc/file", buff, sizeof buff) || // BSD + 0 < readlink("/proc/self/path/a.out", buff, sizeof buff)) // Solaris { 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; } @@ -245,164 +245,164 @@ static int read_init(const struct config_paths_t &paths) Parse the argument list, return the index of the first non-switch arguments. */ -static int fish_parse_opt( int argc, char **argv, const char **cmd_ptr ) +static int fish_parse_opt(int argc, char **argv, const char **cmd_ptr) { - int my_optind; - int 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; } /** @@ -410,70 +410,70 @@ static int fish_parse_opt( int argc, char **argv, const char **cmd_ptr ) parses commands from stdin or files, depending on arguments */ -static wcstring full_escape( const wchar_t *in ) +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 main(int argc, char **argv) +{ + int res=1; + const char *cmd=0; + int my_optind=0; - set_main_thread(); + set_main_thread(); setup_fork_guards(); save_term_foreground_process_group(); - - 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(); - 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(); + builtin_init(); + function_init(); + env_init(&paths); + reader_init(); + history_init(); parser_t &parser = parser_t::principal_parser(); @@ -481,92 +481,92 @@ 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 ) - sb.append( ARRAY_SEP_STR ); - sb.append( str2wcstring( *ptr )); - } - - env_set( L"argv", sb.c_str(), 0 ); - } + for (i=1,ptr = argv+my_optind; *ptr; i++, ptr++) + { + if (i != 1) + sb.append(ARRAY_SEP_STR); + sb.append(str2wcstring(*ptr)); + } - 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); - 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 ); - restore_term_foreground_process_group(); - history_destroy(); - proc_destroy(); - builtin_destroy(); - reader_destroy(); - parser.destroy(); - wutil_destroy(); - event_destroy(); - - env_destroy(); - + 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 c04e6ded0..4d13c8350 100644 --- a/fish_indent.cpp +++ b/fish_indent.cpp @@ -49,30 +49,30 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. /** Read the entire contents of a file into the specified string */ -static void read_file( FILE *f, wcstring &b ) +static void read_file(FILE *f, wcstring &b) { - while( 1 ) - { - errno=0; - wint_t c = fgetwc( f ); - if( c == WEOF ) + while (1) { - if( errno ) - { - wperror(L"fgetwc"); - exit(1); - } + errno=0; + wint_t c = fgetwc(f); + if (c == WEOF) + { + if (errno) + { + wperror(L"fgetwc"); + exit(1); + } - break; + break; + } + b.push_back((wchar_t)c); } - b.push_back((wchar_t)c); - } } /** Insert the specified number of tabs into the output buffer */ -static void insert_tabs( wcstring &out, int indent ) +static void insert_tabs(wcstring &out, int indent) { if (indent > 0) out.append((size_t)indent, L'\t'); @@ -82,184 +82,193 @@ static void insert_tabs( wcstring &out, int indent ) /** Indent the specified input */ -static int indent( wcstring &out, const wcstring &in, int flags ) +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 ); + 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 ) + for (; tok_has_next(&tok); tok_next(&tok)) { - case TOK_STRING: - { - if( is_command ) + int type = tok_last_type(&tok); + wchar_t *last = tok_last(&tok); + + switch (type) { - 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 ); - } - - append_format(out, L"%ls", last ); - - indent = next_indent; - - } - else + case TOK_STRING: { - if ( prev_type != TOK_REDIRECT_FD ) - out.append( L" " ); - out.append( last ); - } + if (is_command) + { + int next_indent = indent; + is_command = 0; - break; - } + wcstring unesc = last; + unescape_string(unesc, UNESCAPE_SPECIAL); - case TOK_END: - { - if( prev_type != TOK_END || prev_prev_type != TOK_END ) - out.append( L"\n" ); - do_indent = 1; - is_command = 1; - break; - } + 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--; + } - 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; - } + if (do_indent && flags && prev_type != TOK_PIPE) + { + insert_tabs(out, indent); + } - 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; - } + append_format(out, L"%ls", last); - case TOK_REDIRECT_IN: - { - out.append( L" " ); - if ( wcscmp( last, L"0" ) != 0 ) - out.append( last ); - out.append( L"< " ); - break; - } + indent = next_indent; + + } + else + { + if (prev_type != TOK_REDIRECT_FD) + out.append(L" "); + out.append(last); + } - 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) + case TOK_END: { - insert_tabs( out, indent ); + if (prev_type != TOK_END || prev_prev_type != TOK_END) + out.append(L"\n"); + do_indent = 1; + is_command = 1; + break; } - append_format( out, L"%ls", last ); - do_indent = 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; + } + + 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; - default: - { - debug( 0, L"Unknown token '%ls'", last ); - exit(1); - } } - prev_prev_type = prev_type; - prev_type = type; + tok_destroy(&tok); - } - - tok_destroy( &tok ); - - return res; + return res; } /** Remove any prefix and suffix newlines from the specified string. */ -static void trim( wcstring &str ) +static void trim(wcstring &str) { if (str.empty()) return; @@ -277,107 +286,107 @@ static void trim( wcstring &str ) /** The main mathod. Run the program. */ -int main( int argc, char **argv ) +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"; + 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 ) + while (1) { - case 0: - { - break; - } + 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 + } + } + ; - case 'h': - { - print_help( "fish_indent", 1 ); - exit( 0 ); - break; - } + int opt_index = 0; - case 'v': - { - fwprintf( stderr, - _(L"%ls, version %s\n"), - program_name, - PACKAGE_VERSION ); - exit( 0 ); - } + int opt = getopt_long(argc, + argv, + GETOPT_STRING, + long_options, + &opt_index); - case 'i': - { - do_indent = 0; - break; - } + 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 ); - } + case '?': + { + exit(1); + } + } } - } wcstring sb_in, sb_out; - read_file( stdin, sb_in ); + read_file(stdin, sb_in); - wutil_init(); + wutil_init(); - if( !indent( sb_out, sb_in, do_indent ) ) - { + 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(); + wutil_destroy(); - return 0; + return 0; } diff --git a/fish_pager.cpp b/fish_pager.cpp index 88ef8d3b3..d8dd42107 100644 --- a/fish_pager.cpp +++ b/fish_pager.cpp @@ -64,40 +64,40 @@ 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 @@ -150,13 +150,13 @@ static std::vector pager_buffer; */ 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,58 +172,58 @@ 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; }; /** This function translates from a highlight code to a specific color by check invironement variables */ -static rgb_color_t get_color( int highlight ) +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(); + if (highlight < 0) + return rgb_color_t::normal(); + if (highlight >= (5)) + return rgb_color_t::normal(); - val = wgetenv( hightlight_var[highlight]); + val = wgetenv(hightlight_var[highlight]); - if( !val ) - { - val = env_universal_get( hightlight_var[highlight]); - } + if (!val) + { + val = env_universal_get(hightlight_var[highlight]); + } - if( !val ) - { - return rgb_color_t::normal(); - } + if (!val) + { + return rgb_color_t::normal(); + } - return parse_color( val, false ); + return parse_color(val, false); } /** @@ -232,15 +232,15 @@ static rgb_color_t get_color( int highlight ) terminal size, so this function should be called when the terminal changes size. */ -static void recalc_width( std::vector &lst, const wchar_t *prefix ) +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; - } + c->min_width = mini(c->desc_width, maxi(0,termsize.ws_col/3 - 2)) + + mini(c->desc_width, maxi(0,termsize.ws_col/5 - 4)) +4; + } } @@ -248,27 +248,27 @@ static void recalc_width( std::vector &lst, const wchar_t *prefix ) Test if the specified character sequence has been entered on the keyboard */ -static int try_sequence( const char *seq ) +static int try_sequence(const char *seq) { - int j, k; - wint_t c=0; + int j, k; + wint_t c=0; - for( j=0; - seq[j] != '\0' && seq[j] == (c=input_common_readch( j>0 )); - j++ ) - ; + 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; + 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,75 +276,75 @@ static int try_sequence( const char *seq ) */ static wint_t readch() { - struct mapping - { - const char *seq; - wint_t bnd; - } - ; - - struct mapping m[]= + struct mapping { - { - "\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 - } + 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; + ; + int i; - for( i=0; m[i].bnd; i++ ) - { - if( !m[i].seq ) + for (i=0; m[i].bnd; i++) { - continue; - } + if (!m[i].seq) + { + continue; + } - if( try_sequence(m[i].seq ) ) - return m[i].bnd; - } - return input_common_readch(0); + if (try_sequence(m[i].seq)) + return m[i].bnd; + } + return input_common_readch(0); } /** Write specified character to the output buffer \c pager_buffer */ -static int pager_buffered_writer( char c) +static int pager_buffered_writer(char c) { - pager_buffer.push_back(c); - return 0; + pager_buffer.push_back(c); + return 0; } /** @@ -352,8 +352,9 @@ static int pager_buffered_writer( char c) */ static void pager_flush() { - if (! pager_buffer.empty()) { - write_loop( 1, & pager_buffer.at(0), pager_buffer.size() * sizeof(char) ); + if (! pager_buffer.empty()) + { + write_loop(1, & pager_buffer.at(0), pager_buffer.size() * sizeof(char)); pager_buffer.clear(); } } @@ -366,99 +367,99 @@ static void pager_flush() \param max the maximum space that may be used for printing \param has_more if this flag is true, this is not the entire string, and the string should be ellisiszed even if the string fits but takes up the whole space. */ -static int print_max( const wchar_t *str, int max, int has_more ) +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]) ) + int i; + int written = 0; + for (i=0; str[i]; i++) { - writech( ellipsis_char ); - written += wcwidth(ellipsis_char ); - break; - } - writech( str[i] ); - written+= wcwidth( str[i] ); - } - return written; + 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; } /** Print the specified item using at the specified amount of space */ -static void completion_print_item( const wchar_t *prefix, comp_t *c, int width, bool secondary ) +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; + 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; + 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 - c->desc_width=0; + { + /* + 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)) + if (desc_width) { - written++; - writech( L' '); + 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); } - 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 ) + else { - written++; - writech( L' '); + while (written < width) + { + written++; + writech(L' '); + } } - } - if ( secondary ) - set_color( rgb_color_t::normal(), rgb_color_t::normal() ); + if (secondary) + set_color(rgb_color_t::normal(), rgb_color_t::normal()); } /** @@ -474,38 +475,38 @@ static void completion_print_item( const wchar_t *prefix, comp_t *c, int width, \param is_quoted Whether to print the completions are in a quoted environment */ -static void completion_print( int cols, - int *width, - int row_start, - int row_stop, - wchar_t *prefix, - int is_quoted, - const std::vector &lst ) +static void completion_print(int cols, + 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 ) +static int completion_try_print(int cols, + 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; + /* + 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; + long i, j; - int rows = (int)((lst.size()-1)/cols+1); + 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 - */ + 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; + if (termsize.ws_col < PAGER_MIN_WIDTH) + return PAGER_DONE; - memset( pref_width, 0, sizeof(pref_width) ); - memset( min_width, 0, sizeof(min_width) ); + 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. - - 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. - */ - - int tot_width = min_tot_width; - width = min_width; - - while( tot_width < termsize.ws_col ) - { - 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); } - } - print=1; + min_tot_width += min_width[j]; + pref_tot_width += pref_width[j]; } - } - - if( print ) - { - res=PAGER_DONE; - if( rows < termsize.ws_row ) + /* + Force fit if one column + */ + if (cols == 1) { - /* List fits on screen. Print it and leave */ - if( is_ca_mode ) - { - is_ca_mode = 0; - writembs(exit_ca_mode); - } - - completion_print( cols, width, 0, rows, prefix, is_quoted, lst); - pager_flush(); + 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 { - int npos, pos = 0; - int do_loop = 1; - - /* - Enter ca_mode, which means that the terminal - content will be restored to the current - state on exit. - */ - if( enter_ca_mode && exit_ca_mode ) - { - is_ca_mode=1; - writembs(enter_ca_mode); - } - - - completion_print( cols, - width, - 0, - termsize.ws_row-1, - prefix, - is_quoted, - lst); - /* - List does not fit on screen. Print one screenfull and - leave a scrollable interface - */ - while(do_loop) - { - set_color( rgb_color_t::black(), get_color(HIGHLIGHT_PAGER_PROGRESS) ); - wcstring msg = format_string(_(L" %d to %d of %d"), pos, pos+termsize.ws_row-1, rows ); - msg.append(L" \r" ); - - writestr(msg.c_str()); - set_color( rgb_color_t::normal(), rgb_color_t::normal() ); - pager_flush(); - int c = readch(); - - switch( c ) + 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))) { - case LINE_UP: - { - if( pos > 0 ) + /* + 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. + + 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; + + while (tot_width < termsize.ws_col) { - 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 ); - + 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); + + } + + 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; + } + + case PAGE_DOWN: + { + + 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); + } + + break; + } + + case PAGE_UP: + { + npos = maxi(0, + 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); + } + 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; } /** @@ -830,71 +831,71 @@ static int completion_try_print( int cols, inside completion descriptions. Remove all whitespace from beginning/end of completion descriptions. */ -static void mangle_descriptions( wcstring_list_t &lst ) +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 ) +static std::vector 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 == COMPLETE_SEP) + { + comp->desc = next.c_str() + start; + break; + } - if( !c ) - 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->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->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; } @@ -968,12 +969,12 @@ static std::vector mangle_completions( wcstring_list_t &lst, const wch /** Respond to a winch signal by checking the terminal size */ -static void handle_winch( int sig ) +static void handle_winch(int sig) { - if (ioctl(1,TIOCGWINSZ,&termsize)!=0) - { - return; - } + if (ioctl(1,TIOCGWINSZ,&termsize)!=0) + { + return; + } } /** @@ -983,7 +984,7 @@ static void handle_winch( int sig ) */ static int interrupt_handler() { - return R_NULL; + return R_NULL; } /** @@ -991,111 +992,114 @@ static int interrupt_handler() it with a copy of stderr, so the reading of completion strings must be done before init is called. */ -static void init( int mangle_descriptors, int out ) +static void init(int mangle_descriptors, int out) { - struct sigaction act; + struct sigaction act; - static struct termios pager_modes; - char *term; + static struct termios pager_modes; + char *term; - if( mangle_descriptors ) - { + 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); + /* + 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 ((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 ); - } + 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); + } } - else + + if (!(out_file = fdopen(out, "w"))) { - debug( 0, _(L"Could not open tty for pager") ); - exit( 1 ); + debug(0, _(L"Could not initialize result pipe")); + exit(1); } - } - - if( !(out_file = fdopen( out, "w" )) ) - { - debug( 0, _(L"Could not initialize result pipe" ) ); - exit( 1 ); - } - env_universal_init( 0, 0, 0, 0); - input_common_init( &interrupt_handler ); - output_set_writer( &pager_buffered_writer ); + env_universal_init(0, 0, 0, 0); + input_common_init(&interrupt_handler); + output_set_writer(&pager_buffered_writer); - 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); - } + 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 */ + 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 */ + 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; + 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 (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); - } + 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; - if (fish_term256) { + if (fish_term256) + { support_term256 = from_string(fish_term256); - } else { + } + else + { support_term256 = term && strstr(term, "256color"); } output_set_supports_term256(support_term256); @@ -1106,336 +1110,336 @@ 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") ); - } + env_universal_destroy(); + input_common_destroy(); + wutil_destroy(); + if (del_curterm(cur_term) == ERR) + { + debug(0, _(L"Error while closing terminfo")); + } - fclose( out_file ); + fclose(out_file); } /** Read lines of input from the specified file, unescape them and insert them into the specified list. */ -static void read_array( FILE* file, wcstring_list_t &comp ) +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( 1 ) + while (!feof(file)) { - c = getc( file ); - if( c == EOF ) - { - break; - } + buffer.clear(); - if( c == '\n' ) - { - break; - } + while (1) + { + c = getc(file); + if (c == EOF) + { + 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 ) +static int get_fd(const char *str) { - char *end; - long 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; + 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 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"; - - - 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. - */ - - if( argc > 1 && argv[1][0] == '-' ) - { /* - Third mode + 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""); + + /* + 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. */ - int completion_fd = -1; - FILE *completion_file; - - while( 1 ) + if (argc > 1 && argv[1][0] == '-') { - 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; - } - - case 'p': - { - prefix = str2wcs(optarg); - break; - } - - 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 'q': - { - is_quoted = 1; - } - - } - } - - if( completion_fd == -1 || result_fd == -1 ) - { - debug( 0, _(L"Unspecified file descriptors") ); - exit( 1 ); - } - - - 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( !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. - - 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 + Third mode */ - for( i=3; i 3) + { + /* + First mode + */ + for (i=3; i completions = mangle_completions( comp, prefix ); + std::vector completions = mangle_completions(comp, prefix); - /** - 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 ) ) + /** + 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)) + { - case PAGER_RETRY: - break; + case PAGER_RETRY: + break; - case PAGER_DONE: - i=0; - break; + 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; + 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 ); + free(prefix); - fwprintf( out_file, L"%ls", out_buff.c_str() ); - if( is_ca_mode ) - { - writembs(exit_ca_mode); - pager_flush(); - } - destroy(); + 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 42f220bf3..3a1b382db 100644 --- a/fish_tests.cpp +++ b/fish_tests.cpp @@ -93,28 +93,28 @@ static int err_count=0; /** Print formatted output */ -static void say( const wchar_t *blah, ... ) +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"); } /** Print formatted error string */ -static void err( const wchar_t *blah, ... ) +static void err(const wchar_t *blah, ...) { - va_list va; - va_start( va, blah ); - err_count++; + va_list va; + va_start(va, blah); + err_count++; - wprintf( L"Error: " ); - vwprintf( blah, va ); - va_end( va ); - wprintf( L"\n" ); + wprintf(L"Error: "); + vwprintf(blah, va); + va_end(va); + wprintf(L"\n"); } /** @@ -123,45 +123,51 @@ static void err( const wchar_t *blah, ... ) */ static void test_escape() { - int i; - wcstring sb; + int i; + wcstring sb; - say( L"Testing escaping and unescaping" ); + 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[] = + if (tok_last_type(&t) != TOK_ERROR) { - 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 + err(L"Invalid input to tokenizer was undetected"); } - ; - 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 ) ) + say(L"Testing use of broken tokenizer"); + if (!tok_has_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 )) ); - } + 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) { +static int test_fork_helper(void *unused) +{ size_t i; - for (i=0; i < 100000; i++) { - delete [] (new char[4 * 1024 * 1024]); + for (i=0; i < 100000; i++) + { + delete [](new char[4 * 1024 * 1024]); } return 0; } -static void test_fork(void) { +static void test_fork(void) +{ return; // Test is disabled until I can force it to fail say(L"Testing fork"); size_t i, max = 100; - for (i=0; i < 100; i++) { + for (i=0; i < 100; i++) + { printf("%lu / %lu\n", i+1, max); /* Do something horrible to try to trigger an error */ #define THREAD_COUNT 8 #define FORK_COUNT 200 #define FORK_LOOP_COUNT 16 signal_block(); - for (size_t i=0; i < THREAD_COUNT; i++) { + for (size_t i=0; i < THREAD_COUNT; i++) + { iothread_perform(test_fork_helper, NULL, NULL); } - for (size_t q = 0; q < FORK_LOOP_COUNT; q++) { + for (size_t q = 0; q < FORK_LOOP_COUNT; q++) + { pid_t pids[FORK_COUNT]; - for (size_t i=0; i < FORK_COUNT; i++) { + for (size_t i=0; i < FORK_COUNT; i++) + { pid_t pid = execute_fork(false); - if (pid > 0) { + if (pid > 0) + { /* Parent */ pids[i] = pid; - } else if (pid == 0) { + } + else if (pid == 0) + { /* Child */ new char[4 * 1024 * 1024]; exit_without_destructors(0); - } else { + } + else + { perror("fork"); } } - for (size_t i=0; i < FORK_COUNT; i++) { + for (size_t i=0; i < FORK_COUNT; i++) + { int status = 0; - if (pids[i] != waitpid(pids[i], &status, 0)) { + if (pids[i] != waitpid(pids[i], &status, 0)) + { perror("waitpid"); assert(0); } @@ -397,99 +419,104 @@ static void test_fork(void) { */ static void test_parser() { - say( L"Testing parser" ); + say(L"Testing parser"); - parser_t parser(PARSER_TYPE_GENERAL, true); + 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 null input to parser"); + if (!parser.test(0, 0, 0, 0)) + { + err(L"Null input to parser.test 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 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 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 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" ); - } + 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" ); + 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 { - public: +class lru_node_test_t : public lru_node_t +{ +public: lru_node_test_t(const wcstring &tmp) : lru_node_t(tmp) { } }; -class test_lru_t : public lru_cache_t { - public: +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) { + 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); } }; -static void test_lru(void) { - say( L"Testing LRU cache" ); +static void test_lru(void) +{ + say(L"Testing LRU cache"); test_lru_t cache; std::vector expected_evicted; size_t total_nodes = 20; - for (size_t i=0; i < total_nodes; i++) { + for (size_t i=0; i < total_nodes; i++) + { assert(cache.size() == std::min(i, (size_t)16)); lru_node_test_t *node = new lru_node_test_t(to_string(i)); if (i < 4) expected_evicted.push_back(node); @@ -500,7 +527,8 @@ static void test_lru(void) { assert(cache.evicted_nodes == expected_evicted); cache.evict_all_nodes(); assert(cache.evicted_nodes.size() == total_nodes); - while (! cache.evicted_nodes.empty()) { + while (! cache.evicted_nodes.empty()) + { lru_node_t *node = cache.evicted_nodes.back(); cache.evicted_nodes.pop_back(); delete node; @@ -514,18 +542,18 @@ static void test_lru(void) { \param flags the flags to send to expand_string */ -static int expand_test( const wchar_t *in, int flags, ... ) +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; + std::vector output; + va_list va; + size_t i=0; + int res=1; + wchar_t *arg; - if( expand_string( in, output, flags) ) - { + if (expand_string(in, output, flags)) + { - } + } #if 0 for (size_t idx=0; idx < output.size(); idx++) @@ -534,27 +562,27 @@ static int expand_test( const wchar_t *in, int flags, ... ) } #endif - va_start( va, flags ); + va_start(va, flags); - while( (arg=va_arg(va, wchar_t *) )!= 0 ) - { - if( output.size() == i ) + while ((arg=va_arg(va, wchar_t *))!= 0) { - res=0; - break; - } + if (output.size() == i) + { + res=0; + break; + } if (output.at(i).completion != arg) - { - res=0; - break; + { + res=0; + break; + } + + i++; } + va_end(va); - i++; - } - va_end( va ); - - return res; + return res; } @@ -563,36 +591,36 @@ static int expand_test( const wchar_t *in, int flags, ... ) */ static void test_expand() { - say( L"Testing parameter expansion" ); + say(L"Testing parameter expansion"); - if( !expand_test( L"foo", 0, L"foo", 0 )) - { - err( L"Strings do not expand to themselves" ); - } + 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{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 +628,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"); } } @@ -622,7 +650,8 @@ static void test_path() static void test_is_potential_path() { say(L"Testing is_potential_path"); - if (system("rm -Rf /tmp/is_potential_path_test/")) { + if (system("rm -Rf /tmp/is_potential_path_test/")) + { err(L"Failed to remove /tmp/is_potential_path_test/"); } @@ -660,13 +689,15 @@ static void test_is_potential_path() } /** Test the 'test' builtin */ -int builtin_test( parser_t &parser, wchar_t **argv ); -static bool run_test_test(int expected, wcstring_list_t &lst) { +int builtin_test(parser_t &parser, wchar_t **argv); +static bool run_test_test(int expected, wcstring_list_t &lst) +{ parser_t parser(PARSER_TYPE_GENERAL, true); size_t i, count = lst.size(); wchar_t **argv = new wchar_t *[count+2]; argv[0] = (wchar_t *)L"test"; - for (i=0; i < count; i++) { + for (i=0; i < count; i++) + { argv[i+1] = (wchar_t *)lst.at(i).c_str(); } argv[i+1] = NULL; @@ -675,18 +706,20 @@ static bool run_test_test(int expected, wcstring_list_t &lst) { return expected == result; } -static bool run_test_test(int expected, const wcstring &str) { +static bool run_test_test(int expected, const wcstring &str) +{ using namespace std; wcstring_list_t lst; wistringstream iss(str); copy(istream_iterator >(iss), - istream_iterator >(), - back_inserter >(lst)); + istream_iterator >(), + back_inserter >(lst)); return run_test_test(expected, lst); } -static void test_test() { +static void test_test() +{ say(L"Testing test builtin"); assert(run_test_test(0, L"5 -ne 6")); @@ -778,7 +811,8 @@ static void perform_one_autosuggestion_test(const wcstring &command, const wcstr } /* Testing test_autosuggest_suggest_special, in particular for properly handling quotes and backslashes */ -static void test_autosuggest_suggest_special() { +static void test_autosuggest_suggest_special() +{ if (system("mkdir -p '/tmp/autosuggest_test/0foobar'")) err(L"mkdir failed"); if (system("mkdir -p '/tmp/autosuggest_test/1foo bar'")) err(L"mkdir failed"); if (system("mkdir -p '/tmp/autosuggest_test/2foo bar'")) err(L"mkdir failed"); @@ -845,90 +879,96 @@ 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; + 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" ); + say(L"Testing completion performance"); - reader_push(L""); - say( L"Here we go" ); + reader_push(L""); + say(L"Here we go"); - t1 = get_time(); + t1 = get_time(); - for( c=L'a'; c<=L'z'; c++ ) - { - str[0]=c; - reader_set_buffer( str, 0 ); + for (c=L'a'; c<=L'z'; c++) + { + str[0]=c; + reader_set_buffer(str, 0); - complete( str, out, COMPLETE_DEFAULT, NULL ); + complete(str, out, COMPLETE_DEFAULT, NULL); - matches += out.size(); + matches += out.size(); out.clear(); - } - t2=get_time(); + } + t2=get_time(); - t = (double)(t2-t1)/(1000000*26); + 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 ); + 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) { + if (item.str() == txt) + { result = true; break; } @@ -936,25 +976,29 @@ static bool history_contains(history_t *history, const wcstring &txt) { return result; } -class history_tests_t { +class history_tests_t +{ public: static void test_history(void); static void test_history_merge(void); static void test_history_formats(void); }; -static wcstring random_string(void) { +static wcstring random_string(void) +{ wcstring result; size_t max = 1 + rand() % 32; - while (max--) { + while (max--) + { wchar_t c = 1 + rand()%ESCAPE_TEST_CHAR; result.push_back(c); } return result; } -void history_tests_t::test_history(void) { - say( L"Testing history"); +void history_tests_t::test_history(void) +{ + say(L"Testing history"); history_t &history = history_t::history_with_name(L"test_history"); history.clear(); @@ -981,7 +1025,8 @@ void history_tests_t::test_history(void) { std::vector before, after; history.clear(); size_t i, max = 100; - for (i=1; i <= max; i++) { + for (i=1; i <= max; i++) + { /* Generate a value */ wcstring value = wcstring(L"test item ") + to_string(i); @@ -993,7 +1038,8 @@ void history_tests_t::test_history(void) { /* Generate some paths */ path_list_t paths; size_t count = rand() % 6; - while (count--) { + while (count--) + { paths.push_back(random_string()); } @@ -1005,13 +1051,15 @@ void history_tests_t::test_history(void) { history.save(); /* Read items back in reverse order and ensure they're the same */ - for (i=100; i >= 1; i--) { + for (i=100; i >= 1; i--) + { history_item_t item = history.item_at_index(i); assert(! item.empty()); after.push_back(item); } assert(before.size() == after.size()); - for (size_t i=0; i < before.size(); i++) { + for (size_t i=0; i < before.size(); i++) + { const history_item_t &bef = before.at(i), &aft = after.at(i); assert(bef.contents == aft.contents); assert(bef.creation_timestamp == aft.creation_timestamp); @@ -1023,26 +1071,31 @@ void history_tests_t::test_history(void) { } // wait until the next second -static void time_barrier(void) { +static void time_barrier(void) +{ time_t start = time(NULL); - do { + do + { usleep(1000); - } while (time(NULL) == start); + } + while (time(NULL) == start); } -void history_tests_t::test_history_merge(void) { +void history_tests_t::test_history_merge(void) +{ // In a single fish process, only one history is allowed to exist with the given name // But it's common to have multiple history instances with the same name active in different processes, // e.g. when you have multiple shells open. // We try to get that right and merge all their history together. Test that case. - say( L"Testing history merge"); + say(L"Testing history merge"); const size_t count = 3; 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++) { + for (size_t i=0; i < count; i++) + { hists[i]->clear(); } @@ -1050,18 +1103,22 @@ void history_tests_t::test_history_merge(void) { time_barrier(); /* Add a different item to each */ - for (size_t i=0; i < count; i++) { + for (size_t i=0; i < count; i++) + { hists[i]->add(texts[i]); } /* Save them */ - for (size_t i=0; i < count; i++) { + 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++) { + for (size_t i = 0; i < count; i++) + { + for (size_t j=0; j < count; j++) + { bool does_contain = history_contains(hists[i], texts[j]); bool should_contain = (i == j); assert(should_contain == does_contain); @@ -1071,22 +1128,26 @@ void history_tests_t::test_history_merge(void) { /* 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); - for (size_t i=0; i < count; i++) { + for (size_t i=0; i < count; i++) + { assert(history_contains(everything, texts[i])); } /* Clean up */ - for (size_t i=0; i < 3; i++) { + for (size_t i=0; i < 3; i++) + { delete hists[i]; } everything->clear(); delete everything; //not as scary as it looks } -static bool install_sample_history(const wchar_t *name) { +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); - if (system(command)) { + if (system(command)) + { err(L"Failed to copy sample history"); return false; } @@ -1094,26 +1155,34 @@ static bool install_sample_history(const wchar_t *name) { } /* Indicates whether the history is equal to the given null-terminated array of strings. */ -static bool history_equals(history_t &hist, const wchar_t * const *strings) { +static bool history_equals(history_t &hist, const wchar_t * const *strings) +{ /* Count our expected items */ size_t expected_count = 0; - while (strings[expected_count]) { + while (strings[expected_count]) + { expected_count++; } /* Ensure the contents are the same */ size_t history_idx = 1; size_t array_idx = 0; - for (;;) { + for (;;) + { const wchar_t *expected = strings[array_idx]; history_item_t item = hist.item_at_index(history_idx); - if (expected == NULL) { - if (! item.empty()) { + if (expected == NULL) + { + if (! item.empty()) + { err(L"Expected empty item at history index %lu", history_idx); } break; - } else { - if (item.str() != expected) { + } + else + { + if (item.str() != expected) + { err(L"Expected '%ls', found '%ls' at index %lu", expected, item.str().c_str(), history_idx); } } @@ -1124,17 +1193,22 @@ static bool history_equals(history_t &hist, const wchar_t * const *strings) { return true; } -void history_tests_t::test_history_formats(void) { +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); - if (! install_sample_history(name)) { + if (! install_sample_history(name)) + { err(L"Couldn't open file tests/%ls", name); - } else { + } + else + { /* Note: This is backwards from what appears in the file */ - const wchar_t * const expected[] = { + const wchar_t * const expected[] = + { L"#def", L"echo #abc", @@ -1151,7 +1225,8 @@ void history_tests_t::test_history_formats(void) { }; history_t &test_history = history_t::history_with_name(name); - if (! history_equals(test_history, expected)) { + if (! history_equals(test_history, expected)) + { err(L"test_history_formats failed for %ls\n", name); } test_history.clear(); @@ -1159,10 +1234,14 @@ void history_tests_t::test_history_formats(void) { name = L"history_sample_fish_2_0"; say(L"Testing %ls", name); - if (! install_sample_history(name)) { + if (! install_sample_history(name)) + { err(L"Couldn't open file tests/%ls", name); - } else { - const wchar_t * const expected[] = { + } + else + { + const wchar_t * const expected[] = + { L"echo this has\\\nbackslashes", L"function foo\n" @@ -1175,7 +1254,8 @@ void history_tests_t::test_history_formats(void) { }; history_t &test_history = history_t::history_with_name(name); - if (! history_equals(test_history, expected)) { + if (! history_equals(test_history, expected)) + { err(L"test_history_formats failed for %ls\n", name); } test_history.clear(); @@ -1183,11 +1263,15 @@ void history_tests_t::test_history_formats(void) { say(L"Testing bash import"); FILE *f = fopen("tests/history_sample_bash", "r"); - if (! f) { + if (! f) + { err(L"Couldn't open file tests/history_sample_bash"); - } else { + } + else + { // It should skip over the export command since that's a bash-ism - const wchar_t *expected[] = { + const wchar_t *expected[] = + { L"echo supsup", L"history --help", @@ -1198,7 +1282,8 @@ void history_tests_t::test_history_formats(void) { }; history_t &test_history = history_t::history_with_name(L"bash_import"); test_history.populate_from_bash(f); - if (! history_equals(test_history, expected)) { + if (! history_equals(test_history, expected)) + { err(L"test_history_formats failed for bash import\n"); } test_history.clear(); @@ -1210,35 +1295,35 @@ void history_tests_t::test_history_formats(void) { /** Main test */ -int main( int argc, char **argv ) +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)"; + 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(); + 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(); @@ -1246,19 +1331,19 @@ int main( int argc, char **argv ) history_tests_t::test_history_merge(); history_tests_t::test_history_formats(); - say( L"Encountered %d errors in low-level tests", err_count ); + 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 (?) - */ + /* + 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(); + env_destroy(); + reader_destroy(); + builtin_destroy(); + wutil_destroy(); + event_destroy(); + proc_destroy(); } diff --git a/fishd.cpp b/fishd.cpp index acfecae1c..5fdf78fdd 100644 --- a/fishd.cpp +++ b/fishd.cpp @@ -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. */ -static void handle_term( int signal ) +static void handle_term(int signal) { - quit=1; + quit=1; } @@ -212,26 +212,26 @@ static void handle_term( int signal ) Excludes chars other than digits since ANSI C only guarantees that digits are consecutive. */ -static void sprint_rand_digits( char *str, int maxlen ) +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 ); + /* + 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); 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; } @@ -244,23 +244,23 @@ static void sprint_rand_digits( char *str, int maxlen ) fallback. The memory returned should be freed using free(). */ -static std::string gen_unique_nfs_filename( const char *filename ) +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; + return newname; } @@ -279,107 +279,108 @@ static std::string gen_unique_nfs_filename( const char *filename ) 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 ) +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 ); + /* + (Re)create a unique file and check that it has one only link. + */ + 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; - } - /* - 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 " - 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 - the unique file increases to 2. - */ - if( link( linkfile, lockfile ) == 0 || - ( stat( linkfile, &statbuf ) == 0 && - statbuf.st_nlink == 2 ) ) + if ((fd = open(linkfile, O_CREAT|O_RDONLY, 0600)) == -1) { - /* Successful lock */ - ret = 1; - break; + debug(1, L"acquire_lock_file: open: %s", strerror(errno)); + goto done; } - 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 - that after the loop is entered the system time is set forward - past the loop's end time. This would otherwise result in a - (practically) infinite loop. - */ - if( timed_out || elapsed >= timeout || elapsed < 0 ) + 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 " + 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 { - 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 - { + 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 && + statbuf.st_nlink == 2)) + { + /* Successful lock */ + ret = 1; + break; + } + elapsed = end.tv_sec + end.tv_usec/1000000.0 - + (start.tv_sec + start.tv_usec/1000000.0); /* - Timed out and final try was unsuccessful or - force was not specified - */ - debug( 1, L"acquire_lock_file: timed out " + 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 + (practically) infinite loop. + */ + 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 + force was not specified + */ + debug(1, L"acquire_lock_file: timed out " L"trying to obtain lockfile %s using " - L"linkfile %s", lockfile, linkfile ); - break; - } + L"linkfile %s", lockfile, linkfile); + break; + } + } + nanosleep(&pollint, NULL); } - nanosleep( &pollint, NULL ); - } while( gettimeofday( &end, NULL ) == 0 ); + 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; } /** @@ -389,24 +390,24 @@ done: 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 ) +static char *acquire_socket_lock(const char *sock_name) { - size_t len = strlen( sock_name ); - char *lockfile = (char *)malloc( len + strlen( LOCKPOSTFIX ) + 1 ); + 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; + 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,113 +415,114 @@ 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 ); + /* + 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); + local.sun_family = AF_UNIX; + strcpy(local.sun_path, sock_name); + len = sizeof(local); - debug(1, L"Connect to socket at %s", sock_name); + 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; - } + 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; - } + /* + 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; - } + 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; - } + 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 - */ + (void)unlink(lockfile); + debug(4, L"Released lockfile: %s", lockfile); + /* + End critical section protected by lock + */ - free( lockfile ); + free(lockfile); - free( sock_name ); + free(sock_name); - if( doexit ) - { - exit( exitcode ); - } + if (doexit) + { + exit(exitcode); + } - return s; + return s; } /** Event handler. Broadcasts updates to all clients. */ -static void broadcast( fish_message_type_t type, const wchar_t *key, const wchar_t *val ) +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; + if (!conn) + return; - msg = create_message( type, key, val ); + msg = create_message(type, key, val); - /* - Don't merge these loops, or try_send_all can free the message - prematurely - */ + /* + Don't merge these loops, or try_send_all can free the message + prematurely + */ - for( c = conn; c; c=c->next ) - { - msg->count++; + 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,83 +530,83 @@ static void broadcast( fish_message_type_t type, const wchar_t *key, const wchar */ static void daemonize() { - /* - Fork, and let parent exit - */ - switch( fork() ) - { + /* + Fork, and let parent exit + */ + switch (fork()) + { case -1: - debug( 0, L"Could not put fishd in background. Quitting" ); - wperror( L"fork" ); - exit(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(); + /* 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 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; + /* + 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); + debug(0, L"Parent process exiting (This is normal)"); + exit(0); + } } - } - /* - Put ourself in out own processing group - */ - setsid(); + /* + Put ourself in out own processing group + */ + setsid(); - /* - Close stdin and stdout. We only use stderr, anyway. - */ - close( 0 ); - close( 1 ); + /* + Close stdin and stdout. We only use stderr, anyway. + */ + close(0); + close(1); } /** Get environment variable value. The resulting string needs to be free'd. */ -static wchar_t *fishd_env_get( const wchar_t *key ) +static wchar_t *fishd_env_get(const wchar_t *key) { - char *nres, *nkey; - wchar_t *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; - } + 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,39 +617,40 @@ 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; + wchar_t *xdg_dir, *home; + bool done = false; + wcstring result; - xdg_dir = fishd_env_get( L"XDG_CONFIG_HOME" ); - if (xdg_dir) - { + 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; + if (!create_directory(result)) + { + done = true; + } + free(xdg_dir); } - free(xdg_dir); - } - else - { - home = fishd_env_get( L"HOME" ); - if( home ) + 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) { + 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." )); + 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(); } @@ -657,19 +660,19 @@ static wcstring fishd_get_config() /** Load or save all variables */ -static void load_or_save( int save) +static void load_or_save(int save) { - const wcstring wdir = fishd_get_config(); - char hostname[HOSTNAME_LEN]; - connection_t c; - int fd; + const wcstring wdir = fishd_get_config(); + char hostname[HOSTNAME_LEN]; + connection_t c; + int fd; - if (wdir.empty()) - return; + if (wdir.empty()) + return; - std::string dir = wcs2string( wdir ); + std::string dir = wcs2string(wdir); - gethostname( hostname, HOSTNAME_LEN ); + gethostname(hostname, HOSTNAME_LEN); std::string name; name.append(dir); @@ -677,33 +680,33 @@ static void load_or_save( int save) 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); + 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 ); + 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); - connection_init( &c, fd ); + connection_init(&c, fd); - if( save ) - { + if (save) + { - write_loop( c.fd, SAVE_MSG, strlen(SAVE_MSG) ); - enqueue_all( &c ); - } - else - read_message( &c ); + write_loop(c.fd, SAVE_MSG, strlen(SAVE_MSG)); + enqueue_all(&c); + } + else + read_message(&c); - connection_destroy( &c ); + connection_destroy(&c); } /** @@ -711,7 +714,7 @@ static void load_or_save( int save) */ static void load() { - load_or_save(0); + load_or_save(0); } /** @@ -719,7 +722,7 @@ static void load() */ static void save() { - load_or_save(1); + load_or_save(1); } /** @@ -728,232 +731,233 @@ static void save() static void init() { - sock = get_socket(); - daemonize(); - env_universal_common_init( &broadcast ); + sock = get_socket(); + daemonize(); + env_universal_common_init(&broadcast); - load(); + load(); } /** Main function for fishd */ -int main( int argc, char ** argv ) +int main(int argc, char ** argv) { - int child_socket; - struct sockaddr_un remote; - socklen_t t; - int max_fd; - int update_count=0; + int child_socket; + struct sockaddr_un remote; + socklen_t t; + int max_fd; + int update_count=0; - fd_set read_fd, write_fd; + fd_set read_fd, write_fd; - set_main_thread(); + set_main_thread(); setup_fork_guards(); - program_name=L"fishd"; - wsetlocale( LC_ALL, L"" ); + 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 ) + /* + Parse options + */ + while (1) { - case 0: - break; + static struct option + long_options[] = + { + { + "help", no_argument, 0, 'h' + } + , + { + "version", no_argument, 0, 'v' + } + , + { + 0, 0, 0, 0 + } + } + ; - case 'h': - print_help( argv[0], 1 ); - exit(0); + int opt_index = 0; - case 'v': - debug( 0, L"%ls, version %s\n", program_name, PACKAGE_VERSION ); - exit( 0 ); + int opt = getopt_long(argc, + argv, + GETOPT_STRING, + long_options, + &opt_index); - case '?': - return 1; + if (opt == -1) + break; - } - } + switch (opt) + { + case 0: + break; - init(); - while(1) - { - connection_t *c; - int res; + case 'h': + print_help(argv[0], 1); + exit(0); - t = sizeof( remote ); + case 'v': + debug(0, L"%ls, version %s\n", program_name, PACKAGE_VERSION); + exit(0); - 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); + case '?': + return 1; - if( ! c->unsent->empty() ) - { - FD_SET( c->fd, &write_fd ); - } + } } - while( 1 ) + init(); + while (1) { - res=select( max_fd, &read_fd, &write_fd, 0, 0 ); + connection_t *c; + int res; - if( quit ) - { - save(); - exit(0); - } + t = sizeof(remote); - 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 ) + FD_ZERO(&read_fd); + FD_ZERO(&write_fd); + FD_SET(sock, &read_fd); + max_fd = sock+1; + for (c=conn; c; c=c->next) { - wperror( L"fcntl" ); - close( child_socket ); + FD_SET(c->fd, &read_fd); + max_fd = maxi(max_fd, c->fd+1); + + if (! c->unsent->empty()) + { + FD_SET(c->fd, &write_fd); + } } - else + + while (1) { - 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; + 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); + } } - } - } - 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 ) + if (FD_ISSET(sock, &read_fd)) { - save(); - update_count = 0; + 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; + } + } } - } - } - connection_t *prev=0; - c=conn; - - while( c ) - { - if( c->killme ) - { - debug( 4, L"Close connection %d", c->fd ); - - while( ! c->unsent->empty() ) + for (c=conn; c; c=c->next) { - message_t *msg = c->unsent->front(); + 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 ); + 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; + } } - connection_destroy( c ); - if( prev ) + if (!conn) { - prev->next=c->next; - } - else - { - conn=c->next; + debug(0, L"No more clients. Quitting"); + save(); + env_universal_common_destroy(); + break; } - 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 b1cdcdb2b..be21bfbf8 100644 --- a/function.cpp +++ b/function.cpp @@ -48,7 +48,8 @@ static function_map_t loaded_functions; static pthread_mutex_t functions_lock; /* Autoloader for functions */ -class function_autoload_t : public autoload_t { +class function_autoload_t : public autoload_t +{ public: function_autoload_t(); virtual void command_removed(const wcstring &cmd); @@ -58,8 +59,8 @@ static function_autoload_t function_autoloader; /** Constructor */ function_autoload_t::function_autoload_t() : autoload_t(L"fish_function_path", - internal_function_scripts, - sizeof internal_function_scripts / sizeof *internal_function_scripts) + internal_function_scripts, + sizeof internal_function_scripts / sizeof *internal_function_scripts) { } @@ -67,7 +68,8 @@ function_autoload_t::function_autoload_t() : autoload_t(L"fish_function_path", static bool function_remove_ignore_autoload(const wcstring &name); /** Callback when an autoloaded function is removed */ -void function_autoload_t::command_removed(const wcstring &cmd) { +void function_autoload_t::command_removed(const wcstring &cmd) +{ function_remove_ignore_autoload(cmd); } @@ -85,65 +87,66 @@ static bool is_autoload = false; Make sure that if the specified function is a dynamically loaded function, it has been fully loaded. */ -static int load( const wcstring &name ) +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; } /** Insert a list of all dynamically loaded functions into the specified list. */ -static void autoload_names( std::set &names, int get_hidden ) +static void autoload_names(std::set &names, int get_hidden) { - size_t i; + size_t i; - const env_var_t path_var_wstr = env_get_string( L"fish_function_path" ); + const env_var_t path_var_wstr = env_get_string(L"fish_function_path"); if (path_var_wstr.missing()) return; - const wchar_t *path_var = path_var_wstr.c_str(); + const wchar_t *path_var = path_var_wstr.c_str(); wcstring_list_t path_list; - tokenize_variable_array( path_var, path_list ); - for( i=0; i::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 ) +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(); } -int function_exists_no_autoload( const wcstring &cmd, const env_vars_snapshot_t &vars ) +int function_exists_no_autoload(const wcstring &cmd, const env_vars_snapshot_t &vars) { - if( parser_keywords_is_reserved(cmd) ) - 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); } @@ -225,19 +228,20 @@ 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; - event_remove( &ev ); + event_remove(&ev); } return erased; } -void function_remove( const wcstring &name ) +void function_remove(const wcstring &name) { if (function_remove_ignore_autoload(name)) - function_autoloader.unload( name ); + function_autoloader.unload(name); } static const function_info_t *function_get(const wcstring &name) @@ -246,9 +250,12 @@ static const function_info_t *function_get(const wcstring &name) // We need a way to correctly check if a lock is locked (or better yet, make our lock non-recursive) //ASSERT_IS_LOCKED(functions_lock); function_map_t::iterator iter = loaded_functions.find(name); - if (iter == loaded_functions.end()) { + if (iter == loaded_functions.end()) + { return NULL; - } else { + } + else + { return &iter->second; } } @@ -257,7 +264,8 @@ bool function_get_definition(const wcstring &name, wcstring *out_definition) { scoped_lock lock(functions_lock); const function_info_t *func = function_get(name); - if (func && out_definition) { + if (func && out_definition) + { out_definition->assign(func->definition); } return func != NULL; @@ -283,20 +291,24 @@ bool function_get_desc(const wcstring &name, wcstring *out_desc) /* Empty length string goes to NULL */ scoped_lock lock(functions_lock); const function_info_t *func = function_get(name); - if (out_desc && func && ! func->description.empty()) { + if (out_desc && func && ! func->description.empty()) + { out_desc->assign(_(func->description.c_str())); return true; - } else { + } + else + { return false; } } 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()) { + if (iter != loaded_functions.end()) + { iter->second.description = desc; } } @@ -306,27 +318,30 @@ bool function_copy(const wcstring &name, const wcstring &new_name) bool result = false; 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. + 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. 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) { + for (iter = loaded_functions.begin(); iter != loaded_functions.end(); ++iter) + { const wcstring &name = iter->first; /* Maybe skip hidden */ - if (! get_hidden) { + if (! get_hidden) + { if (name.empty() || name.at(0) == L'_') continue; } names.insert(name); diff --git a/function.h b/function.h index cfd39de25..2f8dfc36c 100644 --- a/function.h +++ b/function.h @@ -28,34 +28,35 @@ 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 { +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); @@ -69,20 +70,20 @@ public: /** 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; + /** Line where definition started */ + const int definition_offset; - /** List of all named arguments for this function */ + /** List of all named arguments for this function */ const wcstring_list_t named_arguments; - /** 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; }; @@ -92,46 +93,46 @@ public: void function_init(); /** Add a function. */ -void function_add( const function_data_t &data, const parser_t &parser ); +void function_add(const function_data_t &data, const parser_t &parser); /** Remove the function with the specified name. */ -void function_remove( const wcstring &name ); +void function_remove(const wcstring &name); /** Returns by reference the definition of the function with the name \c name. Returns true if successful, false if no function with the given name exists. */ -bool function_get_definition( const wcstring &name, wcstring *out_definition ); +bool function_get_definition(const wcstring &name, wcstring *out_definition); /** Returns by reference the description of the function with the name \c name. Returns true if the function exists and has a nonempty description, false if it does not. */ -bool function_get_desc( const wcstring &name, wcstring *out_desc ); +bool function_get_desc(const wcstring &name, wcstring *out_desc); /** Sets the description of the function with the name \c name. */ -void function_set_desc( const wcstring &name, const wcstring &desc ); +void function_set_desc(const wcstring &name, const wcstring &desc); /** Returns true if the function with the name name exists. */ -int function_exists( const wcstring &name ); +int function_exists(const wcstring &name); /** Returns true if the function with the name name exists, without triggering autoload. */ -int function_exists_no_autoload( const wcstring &name, const env_vars_snapshot_t &vars ); +int function_exists_no_autoload(const wcstring &name, const env_vars_snapshot_t &vars); /** 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 ); +wcstring_list_t function_get_names(int get_hidden); /** Returns tha absolute path of the file where the specified function @@ -142,7 +143,7 @@ wcstring_list_t function_get_names( int get_hidden ); This returns an intern'd string. */ -const wchar_t *function_get_definition_file( const wcstring &name ); +const wchar_t *function_get_definition_file(const wcstring &name); /** Returns the linenumber where the definition of the specified @@ -151,23 +152,23 @@ const wchar_t *function_get_definition_file( const wcstring &name ); This function does not autoload functions, it will only work on functions that have already been defined. */ -int function_get_definition_offset( const wcstring &name ); +int function_get_definition_offset(const wcstring &name); /** Returns a list of all named arguments of the specified function. */ -wcstring_list_t function_get_named_arguments( const wcstring &name ); +wcstring_list_t function_get_named_arguments(const wcstring &name); /** Creates a new function using the same definition as the specified function. Returns true if copy is successful. */ -bool function_copy( const wcstring &name, const wcstring &new_name ); +bool function_copy(const wcstring &name, const wcstring &new_name); /** Returns whether this function shadows variables of the underlying function */ -int function_get_shadows( const wcstring &name ); +int function_get_shadows(const wcstring &name); #endif diff --git a/highlight.cpp b/highlight.cpp index 0cc765029..a7ec044e7 100644 --- a/highlight.cpp +++ b/highlight.cpp @@ -40,58 +40,65 @@ */ #define VAR_COUNT ( sizeof(highlight_var)/sizeof(wchar_t *) ) -static void highlight_universal_internal( const wcstring &buff, std::vector &color, size_t pos ); +static void highlight_universal_internal(const wcstring &buff, std::vector &color, size_t pos); /** The environment variables used to specify the color of different tokens. */ 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" }; /* If the given path looks like it's relative to the working directory, then prepend that working directory. */ -static wcstring apply_working_directory(const wcstring &path, const wcstring &working_directory) { +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)) { - case L'/': - case L'~': - prepend_wd = false; - break; - default: - prepend_wd = true; - break; + switch (path.at(0)) + { + case L'/': + case L'~': + prepend_wd = false; + break; + default: + prepend_wd = true; + break; } - if (! prepend_wd) { + if (! prepend_wd) + { /* No need to prepend the wd, so just return the path we were given */ return path; - } else { + } + else + { /* Remove up to one ./ */ wcstring path_component = path; - if (string_prefixes_string(L"./", path_component)) { + if (string_prefixes_string(L"./", path_component)) + { path_component.erase(0, 2); } /* Removing leading /s */ - while (string_prefixes_string(L"/", path_component)) { + while (string_prefixes_string(L"/", path_component)) + { path_component.erase(0, 1); } @@ -111,10 +118,13 @@ bool fs_is_case_insensitive(const wcstring &path, int fd, case_sensitivity_cache #ifdef _PC_CASE_SENSITIVE /* Try the cache first */ case_sensitivity_cache_t::iterator cache = case_sensitivity_cache.find(path); - if (cache != case_sensitivity_cache.end()) { + if (cache != case_sensitivity_cache.end()) + { /* Use the cached value */ result = cache->second; - } else { + } + else + { /* Ask the system. A -1 value means error (so assume case sensitive), a 1 value means case sensitive, and a 0 value means case insensitive */ long ret = fpathconf(fd, _PC_CASE_SENSITIVE); result = (ret == 0); @@ -132,10 +142,10 @@ bool is_potential_path(const wcstring &const_path, const wcstring_list_t &direct { ASSERT_IS_BACKGROUND_THREAD(); - const bool require_dir = !! (flags & PATH_REQUIRE_DIR); + 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) @@ -143,41 +153,41 @@ bool is_potential_path(const wcstring &const_path, const wcstring_list_t &direct // debug( 1, L"%ls -> %ls ->%ls", path, tilde, unescaped ); - for( size_t i=0; i < path.size(); i++) + for (size_t i=0; i < path.size(); i++) { wchar_t c = path.at(i); - switch( c ) + switch (c) { - case PROCESS_EXPAND: - case VARIABLE_EXPAND: - case VARIABLE_EXPAND_SINGLE: - case BRACKET_BEGIN: - case BRACKET_END: - case BRACKET_SEP: - case ANY_CHAR: - case ANY_STRING: - case ANY_STRING_RECURSIVE: - { - has_magic = 1; - break; - } + case PROCESS_EXPAND: + case VARIABLE_EXPAND: + case VARIABLE_EXPAND_SINGLE: + case BRACKET_BEGIN: + case BRACKET_END: + case BRACKET_SEP: + case ANY_CHAR: + case ANY_STRING: + case ANY_STRING_RECURSIVE: + { + has_magic = 1; + break; + } - case INTERNAL_SEPARATOR: - { - break; - } + case INTERNAL_SEPARATOR: + { + break; + } - default: - { - clean_path.push_back(c); - break; - } + default: + { + clean_path.push_back(c); + break; + } } } - if( ! has_magic && ! clean_path.empty() ) + 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; @@ -185,7 +195,8 @@ bool is_potential_path(const wcstring &const_path, const wcstring_list_t &direct /* 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++) { + 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); @@ -200,7 +211,8 @@ bool is_potential_path(const wcstring &const_path, const wcstring_list_t &direct if (must_be_full_dir) { struct stat buf; - if (0 == wstat(abs_path, &buf) && S_ISDIR(buf.st_mode)) { + if (0 == wstat(abs_path, &buf) && S_ISDIR(buf.st_mode)) + { result = true; /* Return the path suffix, not the whole absolute path */ if (out_path) @@ -220,7 +232,8 @@ bool is_potential_path(const wcstring &const_path, const wcstring_list_t &direct if (out_path) *out_path = clean_path; } - else if ((dir = wopendir(dir_name))) { + else if ((dir = wopendir(dir_name))) + { // We opened the dir_name; look for a string where the base name prefixes it wcstring ent; @@ -234,23 +247,28 @@ bool is_potential_path(const wcstring &const_path, const wcstring_list_t &direct /* Determine which function to call to check for prefixes */ bool (*prefix_func)(const wcstring &, const wcstring &); - if (case_insensitive) { + if (case_insensitive) + { prefix_func = string_prefixes_string_case_insensitive; - } else { + } + else + { prefix_func = string_prefixes_string; } if (prefix_func(base_name, ent) && (! require_dir || is_dir)) { result = true; - if (out_path) { + 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)) { + if (prefix_func(path_base, const_path)) + { out_path->append(path_base); if (! string_suffixes_string(L"/", *out_path)) out_path->push_back(L'/'); @@ -277,10 +295,13 @@ static bool is_potential_cd_path(const wcstring &path, const wcstring &working_d { wcstring_list_t directories; - if (string_prefixes_string(L"./", path)) { + if (string_prefixes_string(L"./", path)) + { /* Ignore the CDPATH in this case; just use the working directory */ directories.push_back(working_directory); - } else { + } + else + { /* Get the CDPATH */ env_var_t cdpath = env_get_string(L"CDPATH"); if (cdpath.missing_or_empty()) @@ -299,362 +320,364 @@ static bool is_potential_cd_path(const wcstring &path, const wcstring &working_d /* Call is_potential_path with all of these directories */ bool result = is_potential_path(path, directories, flags | PATH_REQUIRE_DIR, out_path); #if 0 - if (out_path) { + if (out_path) + { printf("%ls -> %ls\n", path.c_str(), out_path->c_str()); } #endif return result; } -rgb_color_t highlight_get_color( int highlight, bool is_background ) +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()) + val_wstr = env_get_string(highlight_var[0]); - if( ! val_wstr.missing() ) - result = parse_color( val_wstr, is_background ); + 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 (highlight & HIGHLIGHT_VALID_PATH) { - if( result2.is_bold() ) - result.set_bold(true); - if( result2.is_underline() ) - result.set_underline(true); + 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; + return result; } /** Highlight operators (such as $, ~, %, as well as escaped characters. */ -static void highlight_param( const wcstring &buffstr, std::vector &colors, wcstring_list_t *error ) +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); + 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] ) ) + if (wcschr(L"~%", buff[in_pos])) + { + if (in_pos == 1) { - 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; + 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; + 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; + wchar_t max_val = ASCII_MAX; - switch( buff[in_pos] ) - { - case L'u': - { - chars=4; - max_val = UCS2_MAX; - break; - } + 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'U': + { + chars=8; + max_val = WCHAR_MAX; + break; + } - case L'x': - { - break; - } + case L'x': + { + break; + } - case L'X': - { - max_val = BYTE_MAX; - break; - } + case L'X': + { + max_val = BYTE_MAX; + break; + } - default: - { - base=8; - chars=3; - in_pos--; - break; - } - } + default: + { + base=8; + chars=3; + in_pos--; + break; + } + } - for( i=0; i= EXPAND_RESERVED && - *str <= EXPAND_RESERVED_END ) + while (*str) { - return 1; + if (*str >= EXPAND_RESERVED && + *str <= EXPAND_RESERVED_END) + { + return 1; + } + str++; } - str++; - } - return 0; + 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 */ @@ -669,103 +692,104 @@ static bool autosuggest_parse_command(const wcstring &str, wcstring *out_command 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)) + 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 ) + switch (last_type) { - case TOK_STRING: + case TOK_STRING: + { + if (had_cmd) { - if( had_cmd ) + /* Parameter to the command. We store these escaped. */ + args.push_back(tok_last(&tok)); + 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); + if (! expanded || has_expand_reserved(cmd.c_str())) { - /* Parameter to the command. We store these escaped. */ - args.push_back(tok_last(&tok)); - arg_pos = tok_get_pos(&tok); + /* We can't expand this cmd, ignore it */ } 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); - if (! expanded || has_expand_reserved(cmd.c_str())) + bool is_subcommand = false; + int mark = tok_get_pos(&tok); + + if (parser_keywords_is_subcommand(cmd)) { - /* We can't expand this cmd, ignore it */ - } - else - { - bool is_subcommand = false; - int mark = tok_get_pos(&tok); + int sw; + tok_next(&tok); - if (parser_keywords_is_subcommand(cmd)) + sw = parser_keywords_is_switch(tok_last(&tok)); + if (!parser_keywords_is_block(cmd) && + sw == ARG_SWITCH) { - int sw; - tok_next( &tok ); - - sw = parser_keywords_is_switch( tok_last( &tok ) ); - if( !parser_keywords_is_block( cmd ) && - sw == ARG_SWITCH ) - { - /* It's an argument to the subcommand itself */ - } - else - { - if( sw == ARG_SKIP ) - mark = tok_get_pos( &tok ); - is_subcommand = true; - } - tok_set_pos( &tok, mark ); + /* It's an argument to the subcommand itself */ } - - if (!is_subcommand) + else { - /* It's really a command */ - had_cmd = true; - cmd = local_cmd; + if (sw == ARG_SKIP) + mark = tok_get_pos(&tok); + is_subcommand = true; } + tok_set_pos(&tok, mark); } + if (!is_subcommand) + { + /* It's really a command */ + had_cmd = true; + cmd = local_cmd; + } } - break; - } - case TOK_REDIRECT_NOCLOB: - case TOK_REDIRECT_OUT: - case TOK_REDIRECT_IN: - case TOK_REDIRECT_APPEND: - case TOK_REDIRECT_FD: - { - if( !had_cmd ) - { - break; - } - tok_next( &tok ); - break; } + break; + } - case TOK_PIPE: - case TOK_BACKGROUND: - case TOK_END: - { - had_cmd = false; - cmd.empty(); - args.empty(); - arg_pos = -1; - break; - } - - case TOK_COMMENT: - case TOK_ERROR: - default: + case TOK_REDIRECT_NOCLOB: + case TOK_REDIRECT_OUT: + case TOK_REDIRECT_IN: + case TOK_REDIRECT_APPEND: + case TOK_REDIRECT_FD: + { + if (!had_cmd) { break; } + tok_next(&tok); + break; + } + + case TOK_PIPE: + case TOK_BACKGROUND: + case TOK_END: + { + had_cmd = false; + cmd.empty(); + args.empty(); + arg_pos = -1; + break; + } + + case TOK_COMMENT: + case TOK_ERROR: + default: + { + break; + } } } - tok_destroy( &tok ); + tok_destroy(&tok); /* Remember our command if we have one */ - if (had_cmd) { + if (had_cmd) + { if (out_command) out_command->swap(cmd); if (out_arguments) out_arguments->swap(args); if (out_last_arg_pos) *out_last_arg_pos = arg_pos; @@ -775,7 +799,8 @@ static bool autosuggest_parse_command(const wcstring &str, wcstring *out_command /* We have to return an escaped string here */ -bool autosuggest_suggest_special(const wcstring &str, const wcstring &working_directory, wcstring &outSuggestion) { +bool autosuggest_suggest_special(const wcstring &str, const wcstring &working_directory, wcstring &outSuggestion) +{ if (str.empty()) return false; @@ -789,7 +814,8 @@ bool autosuggest_suggest_special(const wcstring &str, const wcstring &working_di 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; @@ -821,13 +847,16 @@ bool autosuggest_suggest_special(const wcstring &str, const wcstring &working_di outSuggestion.append(escaped_suggested_path); if (quote != L'\0') outSuggestion.push_back(quote); } - } else { + } + else + { /* Either an error or some other command, so we don't handle it specially */ } return result; } -bool autosuggest_validate_from_history(const history_item_t &item, file_detection_context_t &detector, const wcstring &working_directory, const env_vars_snapshot_t &vars) { +bool autosuggest_validate_from_history(const history_item_t &item, file_detection_context_t &detector, const wcstring &working_directory, const env_vars_snapshot_t &vars) +{ ASSERT_IS_BACKGROUND_THREAD(); bool handled = false, suggestionOK = false; @@ -839,24 +868,33 @@ bool autosuggest_validate_from_history(const history_item_t &item, file_detectio 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)) { handled = true; bool is_help = string_prefixes_string(dir, L"--help") || string_prefixes_string(dir, L"-h"); - if (is_help) { + if (is_help) + { suggestionOK = false; - } else { + } + else + { wcstring path; bool can_cd = path_get_cdpath(dir, &path, working_directory.c_str(), vars); - if (! can_cd) { + if (! can_cd) + { suggestionOK = false; - } else if (paths_are_same_file(working_directory, path)) { + } + else if (paths_are_same_file(working_directory, path)) + { /* Don't suggest the working directory as the path! */ suggestionOK = false; - } else { + } + else + { suggestionOK = true; } } @@ -864,7 +902,8 @@ bool autosuggest_validate_from_history(const history_item_t &item, file_detectio } /* If not handled specially, handle it here */ - if (! handled) { + if (! handled) + { bool cmd_ok = false; if (path_get_path(parsed_command, NULL)) @@ -876,12 +915,15 @@ bool autosuggest_validate_from_history(const history_item_t &item, file_detectio cmd_ok = true; } - if (cmd_ok) { + if (cmd_ok) + { const path_list_t &paths = item.get_required_paths(); - if (paths.empty()) { + if (paths.empty()) + { suggestionOK= true; } - else { + else + { detector.potential_paths = paths; suggestionOK = detector.paths_are_valid(paths); } @@ -892,20 +934,21 @@ 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) { +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; + wcstring cmd; + int had_cmd=0; + wcstring last_cmd; - int accept_switches = 1; + int accept_switches = 1; - int use_function = 1; - int use_command = 1; - int use_builtin = 1; + int use_function = 1; + int use_command = 1; + int use_builtin = 1; - CHECK( buff, ); + CHECK(buff,); if (buff[0] == L'\0') return; @@ -913,356 +956,359 @@ static void tokenize( const wchar_t * const buff, std::vector &color, const 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 ) + for (tok_init(&tok, buff, TOK_SHOW_COMMENTS | TOK_SQUASH_ERRORS); + tok_has_next(&tok); + tok_next(&tok)) { - case TOK_STRING: - { - if( had_cmd ) - { + int last_type = tok_last_type(&tok); - /*Parameter */ - wchar_t *param = tok_last( &tok ); - if( param[0] == L'-' ) - { - if (wcscmp( param, L"--" ) == 0 ) + switch (last_type) + { + case TOK_STRING: + { + if (had_cmd) { - 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; + + /*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; + } + } + } + + /* 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 { - 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; - } - } - } - - /* 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. - */ - 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. - + Command. First check that the command actually exists. */ - use_command = 1; - use_function = 1; - use_builtin = 2; - } - else - { - if( sw == ARG_SKIP ) + 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_PARAM; - mark = tok_get_pos( &tok ); + 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); + } + + /* 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) + { + 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); + } } - 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 ); - } - - /* 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 ) { - 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 ) - error->push_back(L"Redirection without a command"); - 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: - { - 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: - { - size_t pos = tok_get_pos(&tok); - if (pos < color.size()) { - color.at(pos) = HIGHLIGHT_ERROR; - } - if( error ) - error->push_back(L"Invalid redirection"); - } - } - if( target != 0 ) + case TOK_REDIRECT_NOCLOB: + case TOK_REDIRECT_OUT: + case TOK_REDIRECT_IN: + case TOK_REDIRECT_APPEND: + case TOK_REDIRECT_FD: { - 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 ) + if (!had_cmd) { - color.at(tok_get_pos( &tok )) = HIGHLIGHT_ERROR; - if( error ) - error->push_back(format_string(L"Directory \'%ls\' does not exist", dir.c_str())); + color.at(tok_get_pos(&tok)) = HIGHLIGHT_ERROR; + if (error) + error->push_back(L"Redirection without a command"); + 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: + { + 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: + { + size_t pos = tok_get_pos(&tok); + if (pos < color.size()) + { + color.at(pos) = HIGHLIGHT_ERROR; + } + if (error) + error->push_back(L"Invalid redirection"); + } } - } - /* - 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 ) + if (target != 0) { - color.at(tok_get_pos( &tok )) = HIGHLIGHT_ERROR; - if( error ) - error->push_back(format_string(L"File \'%ls\' does not exist", target)); + 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) + 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) + 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) + error->push_back(format_string(L"File \'%ls\' exists", target)); + } + } } - } - if( last_type == TOK_REDIRECT_NOCLOB ) - { - if( wstat( target, &buff ) != -1 ) + break; + } + + case TOK_PIPE: + case TOK_BACKGROUND: + { + if (had_cmd) { - color.at(tok_get_pos( &tok )) = HIGHLIGHT_ERROR; - if( error ) - error->push_back(format_string(L"File \'%ls\' exists", target)); + 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_PIPE: - case TOK_BACKGROUND: - { - if( had_cmd ) + 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; + color.at(tok_get_pos(&tok)) = HIGHLIGHT_END; + had_cmd = 0; + use_command = 1; + use_function = 1; + use_builtin = 1; + accept_switches = 1; + break; } - else + + case TOK_COMMENT: { - color.at(tok_get_pos( &tok )) = HIGHLIGHT_ERROR; - if( error ) - error->push_back(L"No job to put in background" ); + color.at(tok_get_pos(&tok)) = HIGHLIGHT_COMMENT; + break; } - 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; - } + 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; + } + } } - } - tok_destroy( &tok ); + tok_destroy(&tok); } // PCA This function does I/O, (calls is_potential_path, path_get_path, maybe others) and so ought to only run on a background thread -void highlight_shell( const wcstring &buff, std::vector &color, size_t pos, wcstring_list_t *error, const env_vars_snapshot_t &vars ) +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(); @@ -1270,8 +1316,8 @@ void highlight_shell( const wcstring &buff, std::vector &color, size_t pos, assert(buff.size() == color.size()); - if( length == 0 ) - return; + if (length == 0) + return; std::fill(color.begin(), color.end(), -1); @@ -1281,78 +1327,79 @@ 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; + int done=0; - while( 1 ) - { - wchar_t *begin, *end; - - if( parse_util_locate_cmdsubst(subpos, &begin, &end, 1) <= 0) + while (1) { - break; - } + wchar_t *begin, *end; - if( !*end ) - done=1; - else - *end=0; + 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) { + if ((size_t)(end - subbuff) < length) + { color.at(end-subbuff)=HIGHLIGHT_OPERATOR; } - if( done ) - break; + if (done) + break; - subpos = end+1; - } + 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; - } + /* + 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 ) - { + /* + 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) + { 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. */ @@ -1360,31 +1407,32 @@ void highlight_shell( const wcstring &buff, std::vector &color, size_t pos, token.at(0) = L'~'; if (is_potential_path(token, working_directory_list, PATH_EXPAND_TILDE)) { - for( ptrdiff_t i=tok_begin-cbuff; i < (tok_end-cbuff); i++ ) + for (ptrdiff_t i=tok_begin-cbuff; i < (tok_end-cbuff); i++) { // Don't color HIGHLIGHT_ERROR because it looks dorky. For example, trying to cd into a non-directory would show an underline and also red. - if (! (color.at(i) & HIGHLIGHT_ERROR)) { + if (!(color.at(i) & HIGHLIGHT_ERROR)) + { color.at(i) |= HIGHLIGHT_VALID_PATH; } } } } + } } - } - highlight_universal_internal( buff, color, 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)) ) + /* + 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++) { - color.at(i)=0; + if (iswspace(buff.at(i))) + { + color.at(i)=0; + } } - } } @@ -1392,116 +1440,117 @@ 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 ) +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'\"') ) + if (pos < buffstr.size()) { - std::vector lst; - int level=0; - wchar_t prev_q=0; + /* + 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; + int match_found=0; - while(*str) - { - switch( *str ) - { - case L'\\': - str++; - break; - case L'\"': - case L'\'': - if( level == 0 ) + while (*str) { - level++; - lst.push_back(str-buff); - prev_q = *str; - } - else - { - if( prev_q == *str ) - { - long pos1, pos2; - - level--; - pos1 = lst.back(); - pos2 = str-buff; - if( pos1==pos || pos2==pos ) + switch (*str) { - color.at(pos1)|=HIGHLIGHT_MATCH<<16; - color.at(pos2)|=HIGHLIGHT_MATCH<<16; - match_found = 1; + 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--; + 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++; + lst.push_back(str-buff); + prev_q = *str; + } + } + + break; } - prev_q = *str==L'\"'?L'\'':L'\"'; - } - else - { - level++; - lst.push_back(str-buff); - prev_q = *str; - } + if ((*str == L'\0')) + break; + + str++; } - break; + if (!match_found) + color.at(pos) = HIGHLIGHT_ERROR<<16; } - if( (*str == L'\0')) - break; - str++; - } - - if( !match_found ) - color.at(pos) = HIGHLIGHT_ERROR<<16; - } - - /* - Highlight matching parenthesis - */ + /* + 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; - for (long i=pos; i >= 0 && (size_t)i < buffstr.size(); i+=step) { - const wchar_t test_char = buffstr.at(i); - if( test_char == inc_char ) - level++; - if( test_char == dec_char ) - level--; - if( level == 0 ) + if (wcschr(L"()[]{}", c)) { - long pos2 = i; - color.at(pos)|=HIGHLIGHT_MATCH<<16; - color.at(pos2)|=HIGHLIGHT_MATCH<<16; - match_found=1; - break; - } - } + 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); + 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; + 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 ) +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 ); + highlight_universal_internal(buff, color, pos); } diff --git a/highlight.h b/highlight.h index dc8a03059..6747bba51 100644 --- a/highlight.h +++ b/highlight.h @@ -83,7 +83,7 @@ struct file_detection_context_t; \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. */ -void highlight_shell( const wcstring &buffstr, std::vector &color, size_t pos, wcstring_list_t *error, const env_vars_snapshot_t &vars ); +void highlight_shell(const wcstring &buffstr, std::vector &color, size_t pos, wcstring_list_t *error, const env_vars_snapshot_t &vars); /** Perform syntax highlighting for the text in buff. Matching quotes and paranthesis are highlighted. The result is @@ -95,7 +95,7 @@ void highlight_shell( const wcstring &buffstr, std::vector &color, size_t p \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. */ -void highlight_universal( const wcstring &buffstr, std::vector &color, size_t pos, wcstring_list_t *error, const env_vars_snapshot_t &vars ); +void highlight_universal(const wcstring &buffstr, std::vector &color, size_t pos, wcstring_list_t *error, const env_vars_snapshot_t &vars); /** Translate from HIGHLIGHT_* to FISH_COLOR_* according to environment @@ -107,7 +107,7 @@ void highlight_universal( const wcstring &buffstr, std::vector &color, size call to highlight_get_color( HIGHLIGHT_ERROR) will return FISH_COLOR_RED. */ -rgb_color_t highlight_get_color( int highlight, bool is_background ); +rgb_color_t highlight_get_color(int highlight, bool is_background); /** Given a command 'str' from the history, try to determine whether we ought to suggest it by specially recognizing the command. Returns true if we validated the command. If so, returns by reference whether the suggestion is valid or not. @@ -122,7 +122,8 @@ bool autosuggest_suggest_special(const wcstring &str, const wcstring &working_di This is used only internally to this file, and is exposed only for testing. */ -enum { +enum +{ /* The path must be to a directory */ PATH_REQUIRE_DIR = 1 << 0, diff --git a/history.cpp b/history.cpp index 39595e7e5..07b6e3f76 100644 --- a/history.cpp +++ b/history.cpp @@ -60,20 +60,25 @@ Our history format is intended to be valid YAML. Here it is: /** Whether we print timing information */ #define LOG_TIMES 0 -class time_profiler_t { +class time_profiler_t +{ const char *what; double start; - public: +public: - time_profiler_t(const char *w) { - if (LOG_TIMES) { + time_profiler_t(const char *w) + { + if (LOG_TIMES) + { what = w; start = timef(); } } - ~time_profiler_t() { - if (LOG_TIMES) { + ~time_profiler_t() + { + if (LOG_TIMES) + { double end = timef(); printf("(LOG_TIMES %s: %02f msec)\n", what, (end - start) * 1000); } @@ -81,8 +86,9 @@ class time_profiler_t { }; /* Our LRU cache is used for restricting the amount of history we have, and limiting how long we order it. */ -class history_lru_node_t : public lru_node_t { - public: +class history_lru_node_t : public lru_node_t +{ +public: time_t timestamp; path_list_t required_paths; history_lru_node_t(const history_item_t &item) : @@ -94,29 +100,35 @@ class history_lru_node_t : public lru_node_t { bool write_yaml_to_file(FILE *f) const; }; -class history_lru_cache_t : public lru_cache_t { - protected: +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) { + virtual void node_was_evicted(history_lru_node_t *node) + { delete node; } - public: +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) { + 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) { + if (node != NULL) + { node->timestamp = std::max(node->timestamp, item.timestamp()); /* What to do about paths here? Let's just ignore them */ - } else { + } + else + { node = new history_lru_node_t(item); this->add_node(node); } @@ -139,9 +151,11 @@ static void unescape_yaml(std::string &str); bool history_item_t::merge(const history_item_t &item) { bool result = false; - if (this->contents == item.contents) { + if (this->contents == item.contents) + { this->creation_timestamp = std::max(this->creation_timestamp, item.creation_timestamp); - if (this->required_paths.size() < item.required_paths.size()) { + if (this->required_paths.size() < item.required_paths.size()) + { this->required_paths = item.required_paths; } result = true; @@ -157,25 +171,28 @@ 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) { +bool history_item_t::matches_search(const wcstring &term, enum history_search_type_t type) const +{ + switch (type) + { - case HISTORY_SEARCH_TYPE_CONTAINS: + case HISTORY_SEARCH_TYPE_CONTAINS: /* We consider equal strings to NOT match a contains search (so that you don't have to see history equal to what you typed). The length check ensures that. */ - return contents.size() > term.size() && contents.find(term) != wcstring::npos; + return contents.size() > term.size() && contents.find(term) != wcstring::npos; - case HISTORY_SEARCH_TYPE_PREFIX: - /* 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); + 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; + default: + sanity_lose(); + return false; } } /* Output our YAML to a file */ -bool history_lru_node_t::write_yaml_to_file(FILE *f) const { +bool history_lru_node_t::write_yaml_to_file(FILE *f) const +{ std::string cmd = wcs2string(key); escape_yaml(cmd); if (fprintf(f, "- cmd: %s\n", cmd.c_str()) < 0) @@ -184,11 +201,13 @@ bool history_lru_node_t::write_yaml_to_file(FILE *f) const { if (fprintf(f, " when: %ld\n", (long)timestamp) < 0) return false; - if (! required_paths.empty()) { + 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) { + for (path_list_t::const_iterator iter = required_paths.begin(); iter != required_paths.end(); ++iter) + { std::string path = wcs2string(*iter); escape_yaml(path); @@ -202,7 +221,8 @@ bool history_lru_node_t::write_yaml_to_file(FILE *f) const { // Parse a timestamp line that looks like this: spaces, "when:", spaces, timestamp, newline // The string is NOT null terminated; however we do know it contains a newline, so stop when we reach it -static bool parse_timestamp(const char *str, time_t *out_when) { +static bool parse_timestamp(const char *str, time_t *out_when) +{ const char *cursor = str; /* Advance past spaces */ while (*cursor == ' ') @@ -220,7 +240,8 @@ static bool parse_timestamp(const char *str, time_t *out_when) { /* Try to parse a timestamp. */ long timestamp = 0; - if (isdigit(*cursor) && (timestamp = strtol(cursor, NULL, 0)) > 0) { + if (isdigit(*cursor) && (timestamp = strtol(cursor, NULL, 0)) > 0) + { *out_when = (time_t)timestamp; return true; } @@ -230,7 +251,8 @@ static bool parse_timestamp(const char *str, time_t *out_when) { // Returns a pointer to the start of the next line, or NULL // The next line must itself end with a newline // Note that the string is not null terminated -static const char *next_line(const char *start, size_t length) { +static const char *next_line(const char *start, size_t length) +{ /* Handle the hopeless case */ if (length < 1) return NULL; @@ -240,17 +262,20 @@ static const char *next_line(const char *start, size_t length) { /* Skip past the next newline */ const char *nextline = (const char *)memchr(start, '\n', length); - if (! nextline || nextline >= end) { + if (! nextline || nextline >= end) + { return NULL; } /* Skip past the newline character itself */ - if (++nextline >= end) { + 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) { + if (! next_newline) + { return NULL; } @@ -267,7 +292,8 @@ static size_t offset_of_next_item_fish_2_0(const char *begin, size_t mmap_length { size_t cursor = *inout_cursor; size_t result = (size_t)(-1); - while (cursor < mmap_length) { + while (cursor < mmap_length) + { const char * const line_start = begin + cursor; /* Advance the cursor to the next line */ @@ -289,12 +315,13 @@ static size_t offset_of_next_item_fish_2_0(const char *begin, size_t mmap_length /* Try to be a little YAML compatible. Skip lines with leading %, ---, or ... */ if (! memcmp(line_start, "%", 1) || - ! memcmp(line_start, "---", 3) || - ! memcmp(line_start, "...", 3)) + ! memcmp(line_start, "---", 3) || + ! memcmp(line_start, "...", 3)) continue; /* At this point, we know line_start is at the beginning of an item. But maybe we want to skip this item because of timestamps. A 0 cutoff means we don't care; if we do care, then try parsing out a timestamp. */ - if (cutoff_timestamp != 0) { + if (cutoff_timestamp != 0) + { /* 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; @@ -304,8 +331,9 @@ static size_t offset_of_next_item_fish_2_0(const char *begin, size_t mmap_length time_t timestamp; const char *interior_line; for (interior_line = next_line(line_start, end - line_start); - interior_line != NULL && ! has_timestamp; - interior_line = next_line(interior_line, end - interior_line)) { + 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] != ' ') @@ -319,7 +347,8 @@ static size_t offset_of_next_item_fish_2_0(const char *begin, size_t mmap_length } /* Skip this item if the timestamp is at or after our cutoff. */ - if (has_timestamp && timestamp >= cutoff_timestamp) { + if (has_timestamp && timestamp >= cutoff_timestamp) + { continue; } } @@ -335,76 +364,80 @@ static size_t offset_of_next_item_fish_2_0(const char *begin, size_t mmap_length // Same as offset_of_next_item_fish_2_0, but for fish 1.x (pre fishfish) // Adapted from history_populate_from_mmap in history.c -static size_t offset_of_next_item_fish_1_x(const char *begin, size_t mmap_length, size_t *inout_cursor, time_t cutoff_timestamp) { +static size_t offset_of_next_item_fish_1_x(const char *begin, size_t mmap_length, size_t *inout_cursor, time_t cutoff_timestamp) +{ if (mmap_length == 0 || *inout_cursor >= mmap_length) return (size_t)(-1); - const char *end = begin + mmap_length; - const char *pos; + const char *end = begin + mmap_length; + const char *pos; - bool ignore_newline = false; - bool do_push = true; + 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++ ) - { - - if( do_push ) + for (pos = begin + *inout_cursor; pos < end && ! all_done; pos++) { - ignore_newline = (*pos == '#'); - do_push = false; - } - switch( *pos ) - { - case '\\': - { - pos++; - break; - } + if (do_push) + { + ignore_newline = (*pos == '#'); + do_push = false; + } - case '\n': - { - if( ignore_newline ) + switch (*pos) { - ignore_newline = false; - } - else + case '\\': { - /* Note: pos will be left pointing just after this newline, because of the ++ in the loop */ - all_done = true; + pos++; + break; + } + + 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; } // Returns the offset of the next item based on the given history type, or -1 -static size_t offset_of_next_item(const char *begin, size_t mmap_length, history_file_type_t mmap_type, size_t *inout_cursor, time_t cutoff_timestamp) { +static size_t offset_of_next_item(const char *begin, size_t mmap_length, history_file_type_t mmap_type, size_t *inout_cursor, time_t cutoff_timestamp) +{ size_t result; - switch (mmap_type) { - case history_type_fish_2_0: - result = offset_of_next_item_fish_2_0(begin, mmap_length, inout_cursor, cutoff_timestamp); - break; + switch (mmap_type) + { + 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; + 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 - result = (size_t)(-1); - break; + default: + case history_type_unknown: + // Oh well + result = (size_t)(-1); + break; } return result; } -history_t & history_t::history_with_name(const wcstring &name) { +history_t & history_t::history_with_name(const wcstring &name) +{ /* Note that histories are currently never deleted, so we can return a reference to them without using something like shared_ptr */ scoped_lock locker(hist_lock); history_t *& current = histories[name]; @@ -435,7 +468,8 @@ 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)) { + if (! new_items.empty() && new_items.back().merge(item)) + { /* We merged, so we don't have to add anything */ } else @@ -451,9 +485,10 @@ void history_t::add(const history_item_t &item) 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)) { + if ((now > save_timestamp + SAVE_INTERVAL) || (unsaved_item_count >= SAVE_COUNT)) + { time_profiler_t profiler("save_internal"); - this->save_internal(); + this->save_internal(); } } @@ -471,9 +506,12 @@ void history_t::remove(const wcstring &str) /* Remove from our list of new items */ for (std::vector::iterator iter = new_items.begin(); iter != new_items.end();) { - if (iter->str() == str) { + if (iter->str() == str) + { iter = new_items.erase(iter); - } else { + } + else + { iter++; } } @@ -486,7 +524,8 @@ void history_t::get_string_representation(wcstring &result, const wcstring &sepa bool first = true; /* Append new items */ - for (std::vector::const_reverse_iterator iter=new_items.rbegin(); iter < new_items.rend(); ++iter) { + for (std::vector::const_reverse_iterator iter=new_items.rbegin(); iter < new_items.rend(); ++iter) + { if (! first) result.append(separator); result.append(iter->str()); @@ -495,7 +534,8 @@ void history_t::get_string_representation(wcstring &result, const wcstring &sepa /* 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) @@ -505,7 +545,8 @@ void history_t::get_string_representation(wcstring &result, const wcstring &sepa } } -history_item_t history_t::item_at_index(size_t idx) { +history_item_t history_t::item_at_index(size_t idx) +{ scoped_lock locker(lock); /* 0 is considered an invalid index */ @@ -514,7 +555,8 @@ history_item_t history_t::item_at_index(size_t idx) { /* idx=0 corresponds to last item in new_items */ size_t new_item_count = new_items.size(); - if (idx < new_item_count) { + if (idx < new_item_count) + { return new_items.at(new_item_count - idx - 1); } @@ -522,7 +564,8 @@ history_item_t history_t::item_at_index(size_t idx) { idx -= new_item_count; load_old_if_needed(); size_t old_item_count = old_item_offsets.size(); - if (idx < old_item_count) { + if (idx < old_item_count) + { /* idx=0 corresponds to last item in old_item_offsets */ 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); @@ -533,18 +576,22 @@ history_item_t history_t::item_at_index(size_t idx) { } /* Read one line, stripping off any newline, and updating cursor. Note that our input string is NOT null terminated; it's just a memory mapped file. */ -static size_t read_line(const char *base, size_t cursor, size_t len, std::string &result) { +static size_t read_line(const char *base, size_t cursor, size_t len, std::string &result) +{ /* Locate the newline */ assert(cursor <= len); const char *start = base + cursor; const char *newline = (char *)memchr(start, '\n', len - cursor); - if (newline != NULL) { + 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 { + } + else + { /* We ran off the end */ result.clear(); return len - cursor; @@ -552,7 +599,8 @@ static size_t read_line(const char *base, size_t cursor, size_t len, std::string } /* Trims leading spaces in the given string, returning how many there were */ -static size_t trim_leading_spaces(std::string &str) { +static size_t trim_leading_spaces(std::string &str) +{ size_t i = 0, max = str.size(); while (i < max && str[i] == ' ') i++; @@ -560,9 +608,11 @@ static size_t trim_leading_spaces(std::string &str) { return i; } -static bool extract_prefix(std::string &key, std::string &value, const std::string &line) { +static bool extract_prefix(std::string &key, std::string &value, const std::string &line) +{ size_t where = line.find(":"); - if (where != std::string::npos) { + if (where != std::string::npos) + { key = line.substr(0, where); // skip a space after the : if necessary @@ -578,7 +628,8 @@ static bool extract_prefix(std::string &key, std::string &value, const std::stri } /* Decode an item via the fish 2.0 format */ -history_item_t history_t::decode_item_fish_2_0(const char *base, size_t len) { +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; @@ -596,7 +647,8 @@ history_item_t history_t::decode_item_fish_2_0(const char *base, size_t len) { cmd = str2wcstring(value); /* Read the remaining lines */ - for (;;) { + for (;;) + { /* Read a line */ size_t advance = read_line(base, cursor, len, line); @@ -615,15 +667,20 @@ history_item_t history_t::decode_item_fish_2_0(const char *base, size_t len) { unescape_yaml(value); cursor += advance; - if (key == "when") { + if (key == "when") + { /* Parse an int from the timestamp */ long tmp = 0; - if (sscanf(value.c_str(), "%ld", &tmp) > 0) { + if (sscanf(value.c_str(), "%ld", &tmp) > 0) + { when = tmp; } - } else if (key == "paths") { + } + else if (key == "paths") + { /* Read lines starting with " - " until we can't read any more */ - for (;;) { + for (;;) + { size_t advance = read_line(base, cursor, len, line); if (trim_leading_spaces(line) <= indent) break; @@ -645,11 +702,16 @@ done: return history_item_t(cmd, when, paths); } -history_item_t history_t::decode_item(const char *base, size_t len, history_file_type_t type) { - switch (type) { - case history_type_fish_1_x: return history_t::decode_item_fish_1_x(base, len); - case history_type_fish_2_0: return history_t::decode_item_fish_2_0(base, len); - default: return history_item_t(L""); +history_item_t history_t::decode_item(const char *base, size_t len, history_file_type_t type) +{ + switch (type) + { + case history_type_fish_1_x: + return history_t::decode_item_fish_1_x(base, len); + case history_type_fish_2_0: + return history_t::decode_item_fish_2_0(base, len); + default: + return history_item_t(L""); } } @@ -657,29 +719,30 @@ history_item_t history_t::decode_item(const char *base, size_t len, history_file Remove backslashes from all newlines. This makes a string from the history file better formated for on screen display. */ -static wcstring history_unescape_newlines_fish_1_x( const wcstring &in_str ) +static wcstring history_unescape_newlines_fish_1_x(const wcstring &in_str) { wcstring out; - for (const wchar_t *in = in_str.c_str(); *in; in++) - { - if( *in == L'\\' ) + for (const wchar_t *in = in_str.c_str(); *in; in++) { - if( *(in+1)!= L'\n') - { - out.push_back(*in); - } + if (*in == L'\\') + { + if (*(in+1)!= L'\n') + { + out.push_back(*in); + } + } + else + { + out.push_back(*in); + } } - else - { - out.push_back(*in); - } - } - return out; + return out; } /* Decode an item via the fish 1.x format. Adapted from fish 1.x's item_get(). */ -history_item_t history_t::decode_item_fish_1_x(const char *begin, size_t length) { +history_item_t history_t::decode_item_fish_1_x(const char *begin, size_t length) +{ const char *end = begin + length; const char *pos=begin; @@ -690,50 +753,50 @@ history_item_t history_t::decode_item_fish_1_x(const char *begin, size_t length) bool timestamp_mode = false; time_t timestamp = 0; - while( 1 ) + while (1) { wchar_t c; mbstate_t state; size_t res; - memset( &state, 0, sizeof(state) ); + memset(&state, 0, sizeof(state)); - res = mbrtowc( &c, pos, end-pos, &state ); + res = mbrtowc(&c, pos, end-pos, &state); - if( res == (size_t)-1 ) + if (res == (size_t)-1) { pos++; continue; } - else if( res == (size_t)-2 ) + else if (res == (size_t)-2) { break; } - else if( res == (size_t)0 ) + else if (res == (size_t)0) { pos++; continue; } pos += res; - if( c == L'\n' ) + if (c == L'\n') { - if( timestamp_mode ) + if (timestamp_mode) { const wchar_t *time_string = out.c_str(); - while( *time_string && !iswdigit(*time_string)) + while (*time_string && !iswdigit(*time_string)) time_string++; errno=0; - if( *time_string ) + if (*time_string) { time_t tm; wchar_t *end; errno = 0; - tm = (time_t)wcstol( time_string, &end, 10 ); + tm = (time_t)wcstol(time_string, &end, 10); - if( tm && !errno && !*end ) + if (tm && !errno && !*end) { timestamp = tm; } @@ -744,13 +807,13 @@ history_item_t history_t::decode_item_fish_1_x(const char *begin, size_t length) timestamp_mode = false; continue; } - if( !was_backslash ) + if (!was_backslash) break; } - if( first_char ) + if (first_char) { - if( c == L'#' ) + if (c == L'#') timestamp_mode = true; } @@ -758,7 +821,7 @@ history_item_t history_t::decode_item_fish_1_x(const char *begin, size_t length) out.push_back(c); - was_backslash = ( (c == L'\\') && !was_backslash); + was_backslash = ((c == L'\\') && !was_backslash); } @@ -768,13 +831,18 @@ history_item_t history_t::decode_item_fish_1_x(const char *begin, size_t length) /* Try to infer the history file type based on inspecting the data */ -static history_file_type_t infer_file_type(const char *data, size_t len) { +static history_file_type_t infer_file_type(const char *data, size_t len) +{ history_file_type_t result = history_type_unknown; - if (len > 0) { + if (len > 0) + { /* Old fish started with a # */ - if (data[0] == '#') { + if (data[0] == '#') + { result = history_type_fish_1_x; - } else { + } + else + { /* Assume new fish */ result = history_type_fish_2_0; } @@ -786,7 +854,8 @@ void history_t::populate_from_mmap(void) { mmap_type = infer_file_type(mmap_start, mmap_length); size_t cursor = 0; - for (;;) { + for (;;) + { size_t offset = offset_of_next_item(mmap_start, mmap_length, mmap_type, &cursor, birth_timestamp); // If we get back -1, we're done if (offset == (size_t)(-1)) @@ -805,25 +874,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; } @@ -835,39 +904,45 @@ bool history_t::load_old_if_needed(void) // PCA not sure why signals were blocked here - //signal_block(); + //signal_block(); - bool ok = false; - if (map_file(name, &mmap_start, &mmap_length)) { + 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; } -void history_search_t::skip_matches(const wcstring_list_t &skips) { +void history_search_t::skip_matches(const wcstring_list_t &skips) +{ external_skips = skips; std::sort(external_skips.begin(), external_skips.end()); } -bool history_search_t::should_skip_match(const wcstring &str) const { +bool history_search_t::should_skip_match(const wcstring &str) const +{ return std::binary_search(external_skips.begin(), external_skips.end(), str); } -bool history_search_t::go_forwards() { +bool history_search_t::go_forwards() +{ /* Pop the top index (if more than one) and return if we have any left */ - if (prev_matches.size() > 1) { + if (prev_matches.size() > 1) + { prev_matches.pop_back(); return true; } return false; } -bool history_search_t::go_backwards() { +bool history_search_t::go_backwards() +{ /* Backwards means increasing our index */ const size_t max_idx = (size_t)(-1); @@ -878,16 +953,19 @@ bool history_search_t::go_backwards() { if (idx == max_idx) return false; - while (++idx < max_idx) { + while (++idx < max_idx) + { const history_item_t item = history->item_at_index(idx); /* We're done if it's empty */ - if (item.empty()) { + if (item.empty()) + { return false; } /* Look for a term that matches and that we haven't seen before */ const wcstring &str = item.str(); - if (item.matches_search(term, search_type) && ! match_already_made(str) && ! should_skip_match(str)) { + if (item.matches_search(term, search_type) && ! match_already_made(str) && ! should_skip_match(str)) + { prev_matches.push_back(prev_match_t(idx, item)); return true; } @@ -896,36 +974,43 @@ bool history_search_t::go_backwards() { } /** Goes to the end (forwards) */ -void history_search_t::go_to_end(void) { +void history_search_t::go_to_end(void) +{ prev_matches.clear(); } /** Returns if we are at the end, which is where we start. */ -bool history_search_t::is_at_end(void) const { +bool history_search_t::is_at_end(void) const +{ return prev_matches.empty(); } /** Goes to the beginning (backwards) */ -void history_search_t::go_to_beginning(void) { +void history_search_t::go_to_beginning(void) +{ /* Just go backwards as far as we can */ while (go_backwards()) ; } -history_item_t history_search_t::current_item() const { +history_item_t history_search_t::current_item() const +{ assert(! prev_matches.empty()); return prev_matches.back().second; } -wcstring history_search_t::current_string() const { +wcstring history_search_t::current_string() const +{ history_item_t item = this->current_item(); return item.str(); } -bool history_search_t::match_already_made(const wcstring &match) const { - for (std::vector::const_iterator iter = prev_matches.begin(); iter != prev_matches.end(); ++iter) { +bool history_search_t::match_already_made(const wcstring &match) const +{ + for (std::vector::const_iterator iter = prev_matches.begin(); iter != prev_matches.end(); ++iter) + { if (iter->second.str() == match) return true; } @@ -936,34 +1021,43 @@ static void replace_all(std::string &str, const char *needle, const char *replac { size_t needle_len = strlen(needle), replacement_len = strlen(replacement); size_t offset = 0; - while((offset = str.find(needle, offset)) != std::string::npos) + while ((offset = str.find(needle, offset)) != std::string::npos) { str.replace(offset, needle_len, replacement); offset += replacement_len; } } -static void escape_yaml(std::string &str) { +static void escape_yaml(std::string &str) +{ replace_all(str, "\\", "\\\\"); //replace one backslash with two replace_all(str, "\n", "\\n"); //replace newline with backslash + literal n } -static void unescape_yaml(std::string &str) { +static void unescape_yaml(std::string &str) +{ bool prev_escape = false; - for (size_t idx = 0; idx < str.size(); idx++) { + for (size_t idx = 0; idx < str.size(); idx++) + { char c = str.at(idx); - if (prev_escape) { - if (c == '\\') { + if (prev_escape) + { + if (c == '\\') + { /* Two backslashes in a row. Delete this one */ str.erase(idx, 1); idx--; - } else if (c == 'n') { + } + else if (c == 'n') + { /* Replace backslash + n with an actual newline */ str.replace(idx - 1, 2, "\n"); idx--; } prev_escape = false; - } else { + } + else + { prev_escape = (c == '\\'); } } @@ -986,7 +1080,8 @@ static wcstring history_filename(const wcstring &name, const wcstring &suffix) void history_t::clear_file_state() { /* Erase everything we know about our file */ - if (mmap_start != NULL && mmap_start != MAP_FAILED) { + if (mmap_start != NULL && mmap_start != MAP_FAILED) + { munmap((void *)mmap_start, mmap_length); } mmap_start = NULL; @@ -996,13 +1091,16 @@ void history_t::clear_file_state() save_timestamp=time(0); } -void history_t::compact_new_items() { +void history_t::compact_new_items() +{ /* Keep only the most recent items with the given contents. This algorithm could be made more efficient, but likely would consume more memory too. */ std::set seen; size_t idx = new_items.size(); - while (idx--) { + while (idx--) + { const history_item_t &item = new_items[idx]; - if (! seen.insert(item.contents).second) { + if (! seen.insert(item.contents).second) + { // This item was not inserted because it was already in the set, so delete the item at this index new_items.erase(new_items.begin() + idx); } @@ -1022,11 +1120,11 @@ void history_t::save_internal() /* Compact our new items so we don't have duplicates */ this->compact_new_items(); - bool ok = true; + bool ok = true; wcstring tmp_name = history_filename(name, L".tmp"); - if( ! tmp_name.empty() ) - { + if (! tmp_name.empty()) + { /* Make an LRU cache to save only the last N elements */ history_lru_cache_t lru(HISTORY_SAVE_MAX); @@ -1036,10 +1134,12 @@ void history_t::save_internal() /* 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; - if (map_file(name, &local_mmap_start, &local_mmap_size)) { + if (map_file(name, &local_mmap_start, &local_mmap_size)) + { const history_file_type_t local_mmap_type = infer_file_type(local_mmap_start, local_mmap_size); size_t cursor = 0; - for (;;) { + for (;;) + { size_t offset = offset_of_next_item(local_mmap_start, local_mmap_size, local_mmap_type, &cursor, 0); /* If we get back -1, we're done */ if (offset == (size_t)(-1)) @@ -1053,11 +1153,15 @@ void history_t::save_internal() continue; } /* The old item may actually be more recent than our new item, if it came from another session. Insert all new items at the given index with an earlier timestamp. */ - for (; new_item_iter != new_items.end(); ++new_item_iter) { - if (new_item_iter->timestamp() < old_item.timestamp()) { + for (; new_item_iter != new_items.end(); ++new_item_iter) + { + if (new_item_iter->timestamp() < old_item.timestamp()) + { /* This "new item" is in fact older. */ lru.add_item(*new_item_iter); - } else { + } + else + { /* The new item is not older. */ break; } @@ -1078,31 +1182,33 @@ void history_t::save_internal() 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) { + for (history_lru_cache_t::iterator iter = lru.begin(); iter != lru.end(); ++iter) + { const history_lru_node_t *node = *iter; - if (! node->write_yaml_to_file(out)) { + if (! node->write_yaml_to_file(out)) + { ok = false; 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(); @@ -1111,13 +1217,13 @@ void history_t::save_internal() /* 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) @@ -1159,14 +1265,17 @@ static bool should_import_bash_history_line(const std::string &line) return false; /* Very naive tests! Skip export; probably should skip others. */ - const char * const ignore_prefixes[] = { + const char * const ignore_prefixes[] = + { "export ", "#" }; - for (size_t i=0; i < sizeof ignore_prefixes / sizeof *ignore_prefixes; i++) { + for (size_t i=0; i < sizeof ignore_prefixes / sizeof *ignore_prefixes; i++) + { const char *prefix = ignore_prefixes[i]; - if (! line.compare(0, strlen(prefix), prefix)) { + if (! line.compare(0, strlen(prefix), prefix)) + { return false; } } @@ -1184,15 +1293,18 @@ void history_t::populate_from_bash(FILE *stream) Ignore a few commands that are bash-specific. This list ought to be expanded. */ std::string line; - for (;;) { + for (;;) + { line.clear(); bool success = false, has_newline = false; /* Loop until we've read a line */ - do { + do + { char buff[128]; success = !! fgets(buff, sizeof buff, stream); - if (success) { + if (success) + { /* Skip the newline */ char *newline = strchr(buff, '\n'); if (newline) *newline = '\0'; @@ -1201,10 +1313,12 @@ void history_t::populate_from_bash(FILE *stream) /* Append what we've got */ line.append(buff); } - } while (success && ! has_newline); + } + while (success && ! has_newline); /* Maybe add this line */ - if (should_import_bash_history_line(line)) { + if (should_import_bash_history_line(line)) + { this->add(str2wcstring(line)); } @@ -1221,7 +1335,8 @@ void history_init() void history_destroy() { /* Save all histories */ - for (std::map::iterator iter = histories.begin(); iter != histories.end(); ++iter) { + for (std::map::iterator iter = histories.begin(); iter != histories.end(); ++iter) + { iter->second->save(); } } @@ -1229,20 +1344,25 @@ 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) { +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) { - if (path_is_valid(*iter, working_directory)) { + 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); - } else { + } + else + { /* Not a valid path */ result = 0; if (! test_all) @@ -1252,7 +1372,8 @@ int file_detection_context_t::perform_file_detection(bool test_all) { return result; } -bool file_detection_context_t::paths_are_valid(const path_list_t &paths) { +bool file_detection_context_t::paths_are_valid(const path_list_t &paths) +{ this->potential_paths = paths; return perform_file_detection(false) > 0; } @@ -1265,13 +1386,15 @@ file_detection_context_t::file_detection_context_t(history_t *hist, const wcstri { } -static int threaded_perform_file_detection(file_detection_context_t *ctx) { +static int threaded_perform_file_detection(file_detection_context_t *ctx) +{ ASSERT_IS_BACKGROUND_THREAD(); assert(ctx != NULL); return ctx->perform_file_detection(true /* test all */); } -static void perform_file_detection_done(file_detection_context_t *ctx, int success) { +static void perform_file_detection_done(file_detection_context_t *ctx, int success) +{ /* Now that file detection is done, create the history item */ ctx->history->add(ctx->command, ctx->valid_paths); @@ -1279,7 +1402,8 @@ static void perform_file_detection_done(file_detection_context_t *ctx, int succe delete ctx; } -static bool string_could_be_path(const wcstring &potential_path) { +static bool string_could_be_path(const wcstring &potential_path) +{ // Assume that things with leading dashes aren't paths if (potential_path.empty() || potential_path.at(0) == L'-') return false; @@ -1292,16 +1416,19 @@ void history_t::add_with_file_detection(const wcstring &str) path_list_t potential_paths; tokenizer tokenizer; - for( tok_init( &tokenizer, str.c_str(), TOK_SQUASH_ERRORS ); - tok_has_next( &tokenizer ); - tok_next( &tokenizer ) ) + for (tok_init(&tokenizer, str.c_str(), TOK_SQUASH_ERRORS); + tok_has_next(&tokenizer); + tok_next(&tokenizer)) { - int type = tok_last_type( &tokenizer ); - if (type == TOK_STRING) { + int type = tok_last_type(&tokenizer); + if (type == TOK_STRING) + { const wchar_t *token_cstr = tok_last(&tokenizer); - if (token_cstr) { + if (token_cstr) + { wcstring potential_path = token_cstr; - if (unescape_string(potential_path, false) && string_could_be_path(potential_path)) { + if (unescape_string(potential_path, false) && string_could_be_path(potential_path)) + { potential_paths.push_back(potential_path); } } @@ -1309,7 +1436,8 @@ void history_t::add_with_file_detection(const wcstring &str) } tok_destroy(&tokenizer); - if (! potential_paths.empty()) { + if (! potential_paths.empty()) + { /* We have some paths. Make a context. */ file_detection_context_t *context = new file_detection_context_t(this, str); diff --git a/history.h b/history.h index 3d083acbd..6b0f47453 100644 --- a/history.h +++ b/history.h @@ -15,7 +15,8 @@ typedef std::vector path_list_t; -enum history_search_type_t { +enum history_search_type_t +{ /** The history searches for strings containing the given string */ HISTORY_SEARCH_TYPE_CONTAINS, @@ -23,39 +24,53 @@ enum history_search_type_t { HISTORY_SEARCH_TYPE_PREFIX }; -class history_item_t { +class history_item_t +{ friend class history_t; friend class history_lru_node_t; friend class history_tests_t; - private: +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; + /** The actual contents of the entry */ + wcstring contents; - /** Original creation time for the entry */ - time_t creation_timestamp; + /** 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(); } +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; } + time_t timestamp() const + { + return creation_timestamp; + } - const path_list_t &get_required_paths() const { return required_paths; } + const path_list_t &get_required_paths() const + { + return required_paths; + } - bool operator==(const history_item_t &other) const { + bool operator==(const history_item_t &other) const + { return contents == other.contents && creation_timestamp == other.creation_timestamp && required_paths == other.required_paths; @@ -63,13 +78,15 @@ class history_item_t { }; /* The type of file that we mmap'd */ -enum history_file_type_t { +enum history_file_type_t +{ history_type_unknown, history_type_fish_2_0, history_type_fish_1_x }; -class history_t { +class history_t +{ friend class history_tests_t; private: /** No copying */ @@ -91,23 +108,23 @@ private: /** Internal function */ void clear_file_state(); - /** The name of this list. Used for picking a suitable filename and for switching modes. */ - const wcstring name; + /** The name of this list. Used for picking a suitable filename and for switching modes. */ + const wcstring name; - /** New items. */ - std::vector new_items; + /** New items. */ + std::vector new_items; /** Deleted item contents. */ - std::set deleted_items; + std::set deleted_items; - /** How many items we've added without saving */ - size_t unsaved_item_count; + /** 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 mmaped region for the history file */ + const char *mmap_start; - /** The size of the mmap'd region */ - size_t mmap_length; + /** 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,8 +132,8 @@ 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); @@ -174,7 +191,8 @@ public: bool is_deleted(const history_item_t &item) const; }; -class history_search_t { +class history_search_t +{ /** The history in which we are searching */ history_t * history; @@ -197,10 +215,13 @@ class history_search_t { bool should_skip_match(const wcstring &str) const; - public: +public: /** Gets the search term */ - const wcstring &get_term() const { return term; } + const wcstring &get_term() const + { + return term; + } /** Sets additional string matches to skip */ void skip_matches(const wcstring_list_t &skips); @@ -262,7 +283,8 @@ void history_destroy(); void history_sanity_check(); /* A helper class for threaded detection of paths */ -struct file_detection_context_t { +struct file_detection_context_t +{ /* Constructor */ file_detection_context_t(history_t *hist, const wcstring &cmd); diff --git a/input.cpp b/input.cpp index ac57b83c0..c6125ef20 100644 --- a/input.cpp +++ b/input.cpp @@ -74,8 +74,8 @@ */ 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,8 +86,8 @@ 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 @@ -180,43 +180,43 @@ static const wchar_t *desc_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; @@ -248,25 +248,25 @@ static void input_terminfo_init(); Returns the function description for the given function code. */ -void input_mapping_add( const wchar_t *sequence, - const wchar_t *command ) +void input_mapping_add(const wchar_t *sequence, + const wchar_t *command) { - size_t i; - CHECK( sequence, ); - CHECK( command, ); + size_t i; + CHECK(sequence,); + CHECK(command,); - // debug( 0, L"Add mapping from %ls to %ls", escape(sequence, 1), escape(command, 1 ) ); + // debug( 0, L"Add mapping from %ls to %ls", escape(sequence, 1), escape(command, 1 ) ); - for( i=0; i(fish_term256); - } else { + } + else + { env_var_t term = env_get_string(L"TERM"); - if (term.missing()) { + if (term.missing()) + { support_term256 = false; - } else if (term.find(L"256color") != wcstring::npos) { + } + else if (term.find(L"256color") != wcstring::npos) + { /* Explicitly supported */ support_term256 = true; - } else if (term.find(L"xterm") != wcstring::npos) { + } + else if (term.find(L"xterm") != wcstring::npos) + { // assume that all xterms are 256, except for OS X SnowLeopard env_var_t prog = env_get_string(L"TERM_PROGRAM"); support_term256 = (prog != L"Apple_Terminal"); - } else { + } + else + { // Don't know, default to false support_term256 = false; } @@ -329,105 +339,105 @@ void update_fish_term256(void) int input_init() { - if( is_init ) - return 1; + if (is_init) + return 1; - is_init = 1; + is_init = 1; - input_common_init( &interrupt_handler ); + input_common_init(&interrupt_handler); - if( setupterm( 0, STDOUT_FILENO, 0) == ERR ) - { - debug( 0, _( L"Could not set up terminal" ) ); - exit_without_destructors(1); - } - const env_var_t term = env_get_string( L"TERM" ); + if (setupterm(0, STDOUT_FILENO, 0) == ERR) + { + debug(0, _(L"Could not set up terminal")); + exit_without_destructors(1); + } + const env_var_t term = env_get_string(L"TERM"); assert(! term.missing()); - output_set_term( term.c_str() ); + output_set_term(term.c_str()); - input_terminfo_init(); + input_terminfo_init(); update_fish_term256(); - /* If we have no keybindings, add a few simple defaults */ - if( mapping_list.empty() ) - { - input_mapping_add( L"", L"self-insert" ); - input_mapping_add( L"\n", L"execute" ); - input_mapping_add( L"\t", L"complete" ); - input_mapping_add( L"\x3", L"commandline \"\"" ); - input_mapping_add( L"\x4", L"exit" ); - input_mapping_add( L"\x5", L"bind" ); - } + /* If we have no keybindings, add a few simple defaults */ + if (mapping_list.empty()) + { + input_mapping_add(L"", L"self-insert"); + input_mapping_add(L"\n", L"execute"); + input_mapping_add(L"\t", L"complete"); + input_mapping_add(L"\x3", L"commandline \"\""); + input_mapping_add(L"\x4", L"exit"); + input_mapping_add(L"\x5", L"bind"); + } - return 1; + return 1; } void input_destroy() { - if( !is_init ) - return; + if (!is_init) + return; - is_init=0; + is_init=0; - input_common_destroy(); + input_common_destroy(); - if( del_curterm( cur_term ) == ERR ) - { - debug( 0, _(L"Error while closing terminfo") ); - } + if (del_curterm(cur_term) == ERR) + { + debug(0, _(L"Error while closing terminfo")); + } } /** Perform the action of the specified binding */ -static wint_t input_exec_binding( const input_mapping_t &m, const wcstring &seq ) +static wint_t input_exec_binding(const input_mapping_t &m, const wcstring &seq) { - wchar_t code = input_function_get_code( m.command ); - if( code != -1 ) - { - switch( code ) + wchar_t code = input_function_get_code(m.command); + if (code != -1) + { + switch (code) + { + + case R_SELF_INSERT: + { + return seq[0]; + } + + default: + { + return code; + } + + } + } + else { - case R_SELF_INSERT: - { - return seq[0]; - } + /* + This key sequence is bound to a command, which + is sent to the parser for evaluation. + */ + int last_status = proc_get_last_status(); - default: - { - return code; - } + parser_t::principal_parser().eval(m.command.c_str(), io_chain_t(), TOP); + + proc_set_last_status(last_status); + + /* + We still need to return something to the caller, R_NULL + tells the reader that no key press needs to be handled, + and no repaint is needed. + + Bindings that produce output should emit a R_REPAINT + function by calling 'commandline -f repaint' to tell + fish that a repaint is in order. + */ + + return R_NULL; } - } - else - { - - /* - This key sequence is bound to a command, which - is sent to the parser for evaluation. - */ - int last_status = proc_get_last_status(); - - parser_t::principal_parser().eval( m.command.c_str(), io_chain_t(), TOP ); - - proc_set_last_status( last_status ); - - /* - We still need to return something to the caller, R_NULL - tells the reader that no key press needs to be handled, - and no repaint is needed. - - Bindings that produce output should emit a R_REPAINT - function by calling 'commandline -f repaint' to tell - fish that a repaint is in order. - */ - - return R_NULL; - - } } @@ -435,20 +445,20 @@ static wint_t input_exec_binding( const input_mapping_t &m, const wcstring &seq /** Try reading the specified function mapping */ -static wint_t input_try_mapping( const input_mapping_t &m) +static wint_t input_try_mapping(const input_mapping_t &m) { - wint_t c=0; - int j; + wint_t c=0; + int j; - /* - Check if the actual function code of this mapping is on the stack - */ - c = input_common_readch( 0 ); - if( c == input_function_get_code( m.command ) ) - { - return input_exec_binding( m, m.seq ); - } - input_unreadch( c ); + /* + Check if the actual function code of this mapping is on the stack + */ + c = input_common_readch(0); + if (c == input_function_get_code(m.command)) + { + return input_exec_binding(m, m.seq); + } + input_unreadch(c); const wchar_t *str = m.seq.c_str(); for (j=0; str[j] != L'\0'; j++) @@ -459,10 +469,10 @@ static wint_t input_try_mapping( const input_mapping_t &m) break; } - if( str[j] == L'\0' ) + if (str[j] == L'\0') { /* We matched the entire sequence */ - return input_exec_binding( m, m.seq ); + return input_exec_binding(m, m.seq); } else { @@ -471,75 +481,75 @@ static wint_t input_try_mapping( const input_mapping_t &m) Return the read characters */ input_unreadch(c); - for( k=j-1; k>=0; k-- ) + for (k=j-1; k>=0; k--) { - input_unreadch( m.seq[k] ); + input_unreadch(m.seq[k]); } } - return 0; + return 0; } -void input_unreadch( wint_t ch ) +void input_unreadch(wint_t ch) { - input_common_unreadch( ch ); + input_common_unreadch(ch); } wint_t input_readch() { - size_t i; + size_t i; - CHECK_BLOCK( R_NULL ); - - /* - Clear the interrupted flag - */ - reader_interrupted(); - - /* - Search for sequence in mapping tables - */ - - while( 1 ) - { - const input_mapping_t *generic = 0; - 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: + FD_ZERO(&fdset); + FD_SET(0, &fdset); + if (env_universal_server.fd > 0) { - if( interrupt_handler ) - { - int res = interrupt_handler(); - if( res ) - { - return res; - } - if( lookahead_count ) - { - return lookahead_arr[--lookahead_count]; - } - - } - - - do_loop = true; - break; + FD_SET(env_universal_server.fd, &fdset); + if (fd_max < env_universal_server.fd) fd_max = env_universal_server.fd; } - default: + if (ioport > 0) { - /* - The terminal has been closed. Save and exit. - */ - return R_EOF; + FD_SET(ioport, &fdset); + if (fd_max < ioport) fd_max = ioport; } - } - } - else - { + + 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 ) ) + 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 ) + 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 ) + 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]; -} - -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 + do_loop = false; + } } - ; - - 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; - - } } + while (do_loop); - 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]; - } + return arr[0]; } - -void input_common_unreadch( wint_t ch ) +wchar_t input_common_readch(int timed) { - lookahead_arr[lookahead_count++] = ch; + 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; + + } + } + + 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; } diff --git a/input_common.h b/input_common.h index 9b5e0e630..7af527613 100644 --- a/input_common.h +++ b/input_common.h @@ -15,20 +15,20 @@ 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 */ -void input_common_init( int (*ih)() ); +void input_common_init(int (*ih)()); /* Sets a callback to be invoked every time a byte is read */ void input_common_set_poll_callback(void (*handler)(void)); @@ -47,13 +47,13 @@ void input_common_destroy(); WAIT_ON_ESCAPE milliseconds for a character to be available for reading before returning with the value WEOF. */ -wchar_t input_common_readch( int timed ); +wchar_t input_common_readch(int timed); /** Push a character or a readline function onto the stack of unread characters that input_readch will return before actually reading from fd 0. */ -void input_common_unreadch( wint_t ch ); +void input_common_unreadch(wint_t ch); #endif diff --git a/intern.cpp b/intern.cpp index 015182cbf..56dec21a8 100644 --- a/intern.cpp +++ b/intern.cpp @@ -21,9 +21,11 @@ #include "intern.h" /** Comparison function for intern'd strings */ -class string_table_compare_t { - public: - bool operator()(const wchar_t *a, const wchar_t *b) const { +class string_table_compare_t +{ +public: + bool operator()(const wchar_t *a, const wchar_t *b) const + { return wcscmp(a, b) < 0; } }; @@ -43,10 +45,10 @@ static string_table_t string_table; /** The lock to provide thread safety for intern'd strings */ static pthread_mutex_t intern_lock = PTHREAD_MUTEX_INITIALIZER; -static const wchar_t *intern_with_dup( const wchar_t *in, bool dup ) +static const wchar_t *intern_with_dup(const wchar_t *in, bool dup) { - if( !in ) - return NULL; + if (!in) + return NULL; // debug( 0, L"intern %ls", in ); scoped_lock lock(intern_lock); @@ -54,17 +56,23 @@ static const wchar_t *intern_with_dup( const wchar_t *in, bool dup ) #if USE_SET string_table_t::const_iterator iter = string_table.find(in); - if (iter != string_table.end()) { + if (iter != string_table.end()) + { result = *iter; - } else { + } + else + { result = dup ? wcsdup(in) : in; string_table.insert(result); } #else string_table_t::iterator iter = std::lower_bound(string_table.begin(), string_table.end(), in, string_table_compare_t()); - if (iter != string_table.end() && wcscmp(*iter, in) == 0) { + if (iter != string_table.end() && wcscmp(*iter, in) == 0) + { result = *iter; - } else { + } + else + { result = dup ? wcsdup(in) : in; string_table.insert(iter, result); } @@ -72,13 +80,13 @@ static const wchar_t *intern_with_dup( const wchar_t *in, bool dup ) return result; } -const wchar_t *intern( const wchar_t *in ) +const wchar_t *intern(const wchar_t *in) { - return intern_with_dup(in, true); + return intern_with_dup(in, true); } -const wchar_t *intern_static( const wchar_t *in ) +const wchar_t *intern_static(const wchar_t *in) { - return intern_with_dup(in, false); + return intern_with_dup(in, false); } diff --git a/intern.h b/intern.h index deef31e38..1403e66c6 100644 --- a/intern.h +++ b/intern.h @@ -14,7 +14,7 @@ \param in the string to return an interned copy of */ -const wchar_t *intern( const wchar_t *in ); +const wchar_t *intern(const wchar_t *in); /** Insert the specified string literal into the pool of unique @@ -23,6 +23,6 @@ const wchar_t *intern( const wchar_t *in ); \param in the string to add to the interned pool */ -const wchar_t *intern_static( const wchar_t *in ); +const wchar_t *intern_static(const wchar_t *in); #endif diff --git a/io.cpp b/io.cpp index 525f438bd..704c4c056 100644 --- a/io.cpp +++ b/io.cpp @@ -51,78 +51,78 @@ Utilities for io redirection. #include "io.h" -void io_buffer_read( io_data_t *d ) +void io_buffer_read(io_data_t *d) { - exec_close(d->param1.pipe_fd[1] ); + exec_close(d->param1.pipe_fd[1]); - if( d->io_mode == IO_BUFFER ) - { -/* if( fcntl( d->param1.pipe_fd[0], F_SETFL, 0 ) ) + if (d->io_mode == IO_BUFFER) { - 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 ) + /* 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) { - 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" ); - } + 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 ); - } + break; + } + else + { + d->out_buffer_append(b, l); + } + } } - } } -io_data_t *io_buffer_create( bool is_input ) +io_data_t *io_buffer_create(bool is_input) { bool success = true; - 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; + 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 (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) { @@ -130,28 +130,28 @@ io_data_t *io_buffer_create( bool is_input ) buffer_redirect = NULL; } - return buffer_redirect; + return buffer_redirect; } -void io_buffer_destroy( io_data_t *io_buffer ) +void io_buffer_destroy(io_data_t *io_buffer) { - /** - If this is an input buffer, then io_read_buffer will not have - 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) @@ -218,31 +218,32 @@ void io_print(const io_chain_t &chain) } fprintf(stderr, "Chain %p (%ld items):\n", &chain, (long)chain.size()); - for (size_t i=0; i < chain.size(); i++) { + for (size_t i=0; i < chain.size(); i++) + { const io_data_t *io = chain.at(i); fprintf(stderr, "\t%lu: fd:%d, input:%s, ", (unsigned long)i, io->fd, io->is_input ? "yes" : "no"); switch (io->io_mode) { - case IO_FILE: - fprintf(stderr, "file (%s)\n", io->filename_cstr); - break; - case IO_PIPE: - fprintf(stderr, "pipe {%d, %d}\n", io->param1.pipe_fd[0], io->param1.pipe_fd[1]); - break; - case IO_FD: - fprintf(stderr, "FD map %d -> %d\n", io->param1.old_fd, io->fd); - break; - case IO_BUFFER: - fprintf(stderr, "buffer %p (size %lu)\n", io->out_buffer_ptr(), io->out_buffer_size()); - break; - case IO_CLOSE: - fprintf(stderr, "close %d\n", io->fd); - break; + case IO_FILE: + fprintf(stderr, "file (%s)\n", io->filename_cstr); + break; + case IO_PIPE: + fprintf(stderr, "pipe {%d, %d}\n", io->param1.pipe_fd[0], io->param1.pipe_fd[1]); + break; + case IO_FD: + fprintf(stderr, "FD map %d -> %d\n", io->param1.old_fd, io->fd); + break; + case IO_BUFFER: + fprintf(stderr, "buffer %p (size %lu)\n", io->out_buffer_ptr(), io->out_buffer_size()); + break; + case IO_CLOSE: + fprintf(stderr, "close %d\n", io->fd); + break; } } } -void io_duplicate_prepend( const io_chain_t &src, io_chain_t &dst ) +void io_duplicate_prepend(const io_chain_t &src, io_chain_t &dst) { return dst.duplicate_prepend(src); } @@ -259,7 +260,8 @@ const io_data_t *io_chain_t::get_io_for_fd(int fd) const while (idx--) { const io_data_t *data = this->at(idx); - if (data->fd == fd) { + if (data->fd == fd) + { return data; } } @@ -272,7 +274,8 @@ io_data_t *io_chain_t::get_io_for_fd(int fd) while (idx--) { io_data_t *data = this->at(idx); - if (data->fd == fd) { + if (data->fd == fd) + { return data; } } diff --git a/io.h b/io.h index 249995dd6..5b77838aa 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,71 +24,77 @@ 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) { + void set_filename(const wcstring &str) + { free((void *)filename_cstr); filename_cstr = wcs2str(str.c_str()); } /** Function to create the output buffer */ - void out_buffer_create() { + 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) { + void out_buffer_append(const char *ptr, size_t count) + { 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) { + 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 { + 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 { + 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(), @@ -112,12 +118,14 @@ public: { } - ~io_data_t() { + ~io_data_t() + { free((void *)filename_cstr); } }; -class io_chain_t : public std::vector { +class io_chain_t : public std::vector +{ public: io_chain_t(); io_chain_t(io_data_t *); @@ -144,7 +152,7 @@ io_chain_t io_duplicate(const io_chain_t &chain); io_chain_t io_unique(const io_chain_t &chain); /** Prepends a copy of the specified 'src' chain of redirections to 'dst.' Uses operator new. */ -void io_duplicate_prepend( const io_chain_t &src, io_chain_t &dst ); +void io_duplicate_prepend(const io_chain_t &src, io_chain_t &dst); /** Destroys an io_chain */ void io_chain_destroy(io_chain_t &chain); @@ -159,7 +167,7 @@ io_data_t *io_chain_get(io_chain_t &src, int fd); /** Free all resources used by a IO_BUFFER type io redirection. */ -void io_buffer_destroy( io_data_t *io_buffer ); +void io_buffer_destroy(io_data_t *io_buffer); /** Create a IO_BUFFER type io redirection, complete with a pipe and a @@ -170,14 +178,14 @@ void io_buffer_destroy( io_data_t *io_buffer ); used to buffer the output of a command, or non-zero to buffer the input to a command. */ -io_data_t *io_buffer_create( bool is_input ); +io_data_t *io_buffer_create(bool is_input); /** Close output pipe, and read from input pipe until eof. */ -void io_buffer_read( io_data_t *d ); +void io_buffer_read(io_data_t *d); /** Print debug information about the specified IO redirection chain to stderr. */ -void io_print( const io_chain_t &chain ); +void io_print(const io_chain_t &chain); #endif diff --git a/iothread.cpp b/iothread.cpp index 8e2ece1c6..54117dc04 100644 --- a/iothread.cpp +++ b/iothread.cpp @@ -13,38 +13,42 @@ #include #ifdef _POSIX_THREAD_THREADS_MAX - #if _POSIX_THREAD_THREADS_MAX < 64 - #define IO_MAX_THREADS _POSIX_THREAD_THREADS_MAX - #endif +#if _POSIX_THREAD_THREADS_MAX < 64 +#define IO_MAX_THREADS _POSIX_THREAD_THREADS_MAX +#endif #endif #ifndef IO_MAX_THREADS - #define IO_MAX_THREADS 64 +#define IO_MAX_THREADS 64 #endif static int s_active_thread_count; typedef unsigned char ThreadIndex_t; -static struct WorkerThread_t { - ThreadIndex_t idx; - pthread_t thread; +static struct WorkerThread_t +{ + ThreadIndex_t idx; + pthread_t thread; } threads[IO_MAX_THREADS]; -struct ThreadedRequest_t { - int sequenceNumber; +struct ThreadedRequest_t +{ + int sequenceNumber; - int (*handler)(void *); - void (*completionCallback)(void *, int); - void *context; - int handlerResult; + 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; +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; } static pthread_mutex_t s_request_queue_lock; @@ -52,40 +56,46 @@ static std::queue s_request_queue; 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; +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 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]; + /* 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); +static void add_to_queue(struct ThreadedRequest_t *req) +{ + ASSERT_IS_LOCKED(s_request_queue_lock); s_request_queue.push(req); } -static ThreadedRequest_t *dequeue_request(void) { +static ThreadedRequest_t *dequeue_request(void) +{ ThreadedRequest_t *result = NULL; scoped_lock lock(s_request_queue_lock); - if (! s_request_queue.empty()) { + if (! s_request_queue.empty()) + { result = s_request_queue.front(); s_request_queue.pop(); } @@ -93,68 +103,76 @@ 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; +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(); + /* 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); - } + /* 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)); + /* 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; + /* We're done */ + return req; } /* Spawn another thread if there's work to be done. */ -static void iothread_spawn_if_needed(void) { +static void iothread_spawn_if_needed(void) +{ ASSERT_IS_LOCKED(s_request_queue_lock); - 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); + assert(err == 0); - /* Note that we are spawned another thread */ - s_active_thread_count += 1; + /* 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) { +int iothread_perform_base(int (*handler)(void *), void (*completionCallback)(void *, int), void *context) +{ ASSERT_IS_MAIN_THREAD(); ASSERT_IS_NOT_FORKED_CHILD(); - iothread_init(); + 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; + /* 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); @@ -167,42 +185,46 @@ int iothread_perform_base(int (*handler)(void *), void (*completionCallback)(voi return 0; } -int iothread_port(void) { - iothread_init(); - return s_read_pipe; +int iothread_port(void) +{ + iothread_init(); + return s_read_pipe; } -void iothread_service_completion(void) { +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); + 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 WorkerThread_t *thread = &threads[threadIdx]; + assert(thread->thread != 0); - struct ThreadedRequest_t *req = NULL; - VOMIT_ON_FAILURE(pthread_join(thread->thread, (void **)&req)); + 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; + /* Free up this thread */ + thread->thread = 0; + assert(s_active_thread_count > 0); + s_active_thread_count -= 1; - /* Handle the request */ - if (req) { + /* Handle the request */ + if (req) + { 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) { +void iothread_drain_all(void) +{ ASSERT_IS_MAIN_THREAD(); ASSERT_IS_NOT_FORKED_CHILD(); if (s_active_thread_count == 0) @@ -212,7 +234,8 @@ void iothread_drain_all(void) { int thread_count = s_active_thread_count; double now = timef(); #endif - while (s_active_thread_count > 0) { + while (s_active_thread_count > 0) + { iothread_service_completion(); } #if TIME_DRAIN diff --git a/iothread.h b/iothread.h index 8ca103596..88c4a430c 100644 --- a/iothread.h +++ b/iothread.h @@ -30,7 +30,8 @@ void iothread_drain_all(void); /** Helper template */ template -int iothread_perform(int (*handler)(T *), void (*completionCallback)(T *, int), T *context) { +int iothread_perform(int (*handler)(T *), void (*completionCallback)(T *, int), T *context) +{ return iothread_perform_base((int (*)(void *))handler, (void (*)(void *, int))completionCallback, static_cast(context)); } diff --git a/key_reader.cpp b/key_reader.cpp index d126af7b1..fae6f33fa 100644 --- a/key_reader.cpp +++ b/key_reader.cpp @@ -20,78 +20,78 @@ #include "input_common.h" -int writestr( char *str ) +int writestr(char *str) { - write( 1, str, strlen(str) ); - return 0; + write(1, str, strlen(str)); + return 0; } -int main( int argc, char **argv) +int main(int argc, char **argv) { - set_main_thread(); + set_main_thread(); setup_fork_guards(); - setlocale( LC_ALL, "" ); + setlocale(LC_ALL, ""); - if( argc == 2 ) - { - static char term_buffer[2048]; - char *termtype = getenv ("TERM"); - char *tbuff = new char[9999]; - char *res; - - tgetent( term_buffer, termtype ); - res = tgetstr( argv[1], &tbuff ); - if( res != 0 ) + if (argc == 2) { - while( *res != 0 ) - { - printf("%d ", *res ); + static char term_buffer[2048]; + char *termtype = getenv("TERM"); + char *tbuff = new char[9999]; + char *res; + + tgetent(term_buffer, termtype); + res = tgetstr(argv[1], &tbuff); + if (res != 0) + { + while (*res != 0) + { + printf("%d ", *res); - res++; - } - printf( "\n" ); + res++; + } + printf("\n"); + } + else + { + printf("Undefined sequence\n"); + } } else { - printf("Undefined sequence\n"); - } - } - else - { - char scratch[1024]; - unsigned int c; + char scratch[1024]; + unsigned int c; - struct termios modes, /* so we can change the modes */ - savemodes; /* so we can reset the modes when we're done */ + struct termios modes, /* so we can change the modes */ + savemodes; /* so we can reset the modes when we're done */ - input_common_init(0); + input_common_init(0); - tcgetattr(0,&modes); /* get the current terminal modes */ - savemodes = modes; /* save a copy so we can reset them */ + 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) + 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 ); + 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(); } - /* reset the terminal to the saved mode */ - tcsetattr(0,TCSANOW,&savemodes); - input_common_destroy(); - } - - return 0; + return 0; } diff --git a/kill.cpp b/kill.cpp index 99f36799e..846783e58 100644 --- a/kill.cpp +++ b/kill.cpp @@ -36,7 +36,7 @@ */ #define KILL_MAX 8192 - /** Last kill string */ +/** Last kill string */ //static ll_node_t *kill_last=0; /** Current kill string */ @@ -57,76 +57,78 @@ 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 ) +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" ); + cmd.append(L"|xsel -b"); + } } - } - if (! cmd.empty()) - { - if( exec_subshell(cmd) == -1 ) + if (! cmd.empty()) { - /* - Do nothing on failiure - */ + if (exec_subshell(cmd) == -1) + { + /* + Do nothing on failiure + */ + } + + free(cut_buffer); + + cut_buffer = escaped_str; } - - free( cut_buffer ); - - cut_buffer = escaped_str; - } } /** Remove first match for specified string from circular list */ -static void kill_remove( const wcstring &s ) +static void kill_remove(const wcstring &s) { ASSERT_IS_MAIN_THREAD(); kill_list_t::iterator iter = std::find(kill_list.begin(), kill_list.end(), s); @@ -136,19 +138,22 @@ static void kill_remove( const wcstring &s ) -void kill_replace( const wcstring &old, const wcstring &newv ) +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() { ASSERT_IS_MAIN_THREAD(); // Move the first element to the end - if (kill_list.empty()) { + if (kill_list.empty()) + { return NULL; - } else { + } + else + { kill_list.splice(kill_list.end(), kill_list, kill_list.begin()); return kill_list.front().c_str(); } @@ -160,52 +165,55 @@ const wchar_t *kill_yank_rotate() */ static void kill_check_x_buffer() { - if( !has_xsel() ) - return; + 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 ) + 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 ); + kill_list.push_front(new_cut_buffer); } - } + } + } } - } } const wchar_t *kill_yank() { - kill_check_x_buffer(); - if (kill_list.empty()) { + kill_check_x_buffer(); + if (kill_list.empty()) + { return L""; - } else { + } + else + { return kill_list.front().c_str(); } } @@ -220,7 +228,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 6997341a4..35df3dbd6 100644 --- a/kill.h +++ b/kill.h @@ -12,11 +12,11 @@ /** Replace the specified string in the killring */ -void kill_replace( const wcstring &old, const wcstring &newv ); +void kill_replace(const wcstring &old, const wcstring &newv); /** Add a string to the top of the killring */ -void kill_add( const wcstring &str ); +void kill_add(const wcstring &str); /** Rotate the killring */ const wchar_t *kill_yank_rotate(); diff --git a/lru.h b/lru.h index ca49417a1..56ea28c4a 100644 --- a/lru.h +++ b/lru.h @@ -13,12 +13,17 @@ #include "common.h" /** A predicate to compare dereferenced pointers */ -struct dereference_less_t { +struct dereference_less_t +{ template - bool operator()(ptr_t p1, ptr_t p2) const { return *p1 < *p2; } + bool operator()(ptr_t p1, ptr_t p2) const + { + return *p1 < *p2; + } }; -class lru_node_t { +class lru_node_t +{ template friend class lru_cache_t; /** Our linked list pointer */ @@ -32,12 +37,16 @@ public: 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; } + bool operator<(const lru_node_t &other) const + { + return key < other.key; + } }; template -class lru_cache_t { - private: +class lru_cache_t +{ +private: /** Max node count */ const size_t max_node_count; @@ -49,7 +58,8 @@ class lru_cache_t { typedef std::set node_set_t; node_set_t node_set; - void promote_node(node_type_t *node) { + void promote_node(node_type_t *node) + { /* We should never promote the mouth */ assert(node != &mouth); @@ -64,7 +74,8 @@ class lru_cache_t { mouth.next = node; } - void evict_node(node_type_t *condemned_node) { + void evict_node(node_type_t *condemned_node) + { /* We should never evict the mouth */ assert(condemned_node != NULL && condemned_node != &mouth); @@ -80,12 +91,13 @@ class lru_cache_t { this->node_was_evicted(condemned_node); } - void evict_last_node(void) { + void evict_last_node(void) + { /* Simple */ evict_node((node_type_t *)mouth.prev); } - protected: +protected: /** Head of the linked list */ lru_node_t mouth; @@ -93,10 +105,11 @@ class lru_cache_t { /** Overridable callback for when a node is evicted */ virtual void node_was_evicted(node_type_t *node) { } - public: +public: /** Constructor */ - lru_cache_t(size_t max_size = 1024 ) : max_node_count(max_size), node_count(0), mouth(wcstring()) { + lru_cache_t(size_t max_size = 1024) : max_node_count(max_size), node_count(0), mouth(wcstring()) + { /* Hook up the mouth to itself: a one node circularly linked list! */ mouth.prev = mouth.next = &mouth; } @@ -106,7 +119,8 @@ class lru_cache_t { /** Returns the node for a given key, or NULL */ - node_type_t *get_node(const wcstring &key) { + node_type_t *get_node(const wcstring &key) + { node_type_t *result = NULL; /* Construct a fake node as our key */ @@ -116,7 +130,8 @@ class lru_cache_t { node_set_t::iterator iter = node_set.find(&node_key); /* If we found a node, promote and return it */ - if (iter != node_set.end()) { + if (iter != node_set.end()) + { result = static_cast(*iter); promote_node(result); } @@ -124,7 +139,8 @@ class lru_cache_t { } /** Evicts the node for a given key, returning true if a node was evicted. */ - bool evict_node(const wcstring &key) { + bool evict_node(const wcstring &key) + { /* Construct a fake node as our key */ lru_node_t node_key(key); @@ -139,7 +155,8 @@ class lru_cache_t { } /** Adds a node under the given key. Returns true if the node was added, false if the node was not because a node with that key is already in the set. */ - bool add_node(node_type_t *node) { + bool add_node(node_type_t *node) + { /* Add our node without eviction */ if (! this->add_node_without_eviction(node)) return false; @@ -153,7 +170,8 @@ class lru_cache_t { } /** Adds a node under the given key without triggering eviction. Returns true if the node was added, false if the node was not because a node with that key is already in the set. */ - bool add_node_without_eviction(node_type_t *node) { + bool add_node_without_eviction(node_type_t *node) + { assert(node != NULL && node != &mouth); /* Try inserting; return false if it was already in the set */ @@ -178,31 +196,56 @@ class lru_cache_t { } /** Counts nodes */ - size_t size(void) { + size_t size(void) + { return node_count; } /** Evicts all nodes */ - void evict_all_nodes(void) { - while (node_count > 0) { + void evict_all_nodes(void) + { + while (node_count > 0) + { evict_last_node(); } } /** Iterator for walking nodes, from least recently used to most */ - class iterator { + class iterator + { lru_node_t *node; - public: + public: iterator(lru_node_t *val) : node(val) { } - void operator++() { node = node->prev; } - void operator++(int x) { node = node->prev; } - bool operator==(const iterator &other) { return node == other.node; } - bool operator!=(const iterator &other) { return !(*this == other); } - node_type_t *operator*() { return static_cast(node); } + void operator++() + { + node = node->prev; + } + void operator++(int x) + { + node = node->prev; + } + bool operator==(const iterator &other) + { + return node == other.node; + } + 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); } + iterator begin() + { + return iterator(mouth.prev); + } + iterator end() + { + return iterator(&mouth); + } }; diff --git a/mimedb.cpp b/mimedb.cpp index d70e682e3..ea7e74f5e 100644 --- a/mimedb.cpp +++ b/mimedb.cpp @@ -115,12 +115,12 @@ typedef std::vector string_list_t; */ enum { - FILEDATA, - FILENAME, - MIMETYPE, - DESCRIPTION, - ACTION, - LAUNCH + FILEDATA, + FILENAME, + MIMETYPE, + DESCRIPTION, + ACTION, + LAUNCH } ; @@ -164,29 +164,29 @@ static int launch_pos=0; /** Call malloc, set error flag and print message on failure */ -void *my_malloc( size_t s ) +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; } /** Duplicate string, set error flag and print message on failure */ -char *my_strdup( const char *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; } @@ -194,100 +194,100 @@ char *my_strdup( const char *s ) Search the file \c filename for the first line starting with \c match, which is returned in a newly allocated string. */ -static const char * search_ini( const char *filename, const char *match ) +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; + 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 (!f) { - if( !feof( f ) ) - { - perror( "fgets" ); + perror("fopen"); error=1; - } - buf[0]=0; - done = 1; + return 0; } - else if( strncmp( buf, match, len ) == 0 && buf[len] == '=' ) + while (!done) { - done=1; + 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 ) + fclose(f); + if (buf[0]) { - if(res[strlen(res)-1]=='\n' ) - res[strlen(res)-1]='\0'; + char *res=strdup(buf); + if (res) + { + if (res[strlen(res)-1]=='\n') + res[strlen(res)-1]='\0'; + } + return res; } - return res; - } - else - return (char *)0; + else + return (char *)0; } /** Test if the specified file exists. If it does not, also try replacing dashes with slashes in \c in. */ -static char *file_exists( const char *dir, const char *in ) +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 ) - { + 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; - } - 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; } @@ -301,109 +301,113 @@ static char *file_exists( const char *dir, const char *in ) \param all If zero, then stop after the first filename. \return The number of filenames added to the list. */ -static int append_filenames( string_list_t &list, const char *f, int all ) +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) - { - result = file_exists( xdg_data_home, f ); - if (result) + xdg_data_home = getenv("XDG_DATA_HOME"); + if (xdg_data_home) { + 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; + 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); + strcpy(guessed_xdg_home, home); + strcat(guessed_xdg_home, "/.local/share"); + result = file_exists(guessed_xdg_home, f); + free(guessed_xdg_home); - if (result) - { + 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; + dir = (char *)my_malloc(len + 1); + if (!dir) + return 0; - strncpy (dir, ptr, len); - dir[len] = '\0'; - result = file_exists( dir, f ); + strncpy(dir, ptr, len); + dir[len] = '\0'; + result = file_exists(dir, f); - free (dir); + free(dir); - if (result) - { + if (result) + { list.push_back(result); - if ( !all ) { - return 1; - } - } + if (!all) + { + return 1; + } + } - ptr = end_ptr; + ptr = end_ptr; } - return list.size() - prev_count; + return list.size() - prev_count; } /** Find at most one file relative to the XDG data directories; returns the empty string on failure */ -static std::string get_filename( char *f ) +static std::string get_filename(char *f) { string_list_t list; - append_filenames( list, f, 0 ); - if (list.empty()) { + append_filenames(list, f, 0); + if (list.empty()) + { return ""; - } else { + } + else + { return list.back(); } } @@ -413,51 +417,51 @@ static std::string get_filename( char *f ) of whitespace with a single space. Also removes any leading and trailing whitespace */ -static char *munge( char *in ) +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; - } + char *out = (char *)my_malloc(strlen(in)+1); + char *p=out; + int had_whitespace = 0; + int printed = 0; + if (!out) + { + return 0; + } - while( 1 ) - { + 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 ) + switch (*in) { - *(p++)=' '; + case ' ': + case '\n': + case '\t': + case '\r': + { + had_whitespace = 1; + break; } - printed=1; - had_whitespace=0; - *(p++)=*in; - break; - } + case '\0': + *p = '\0'; + return out; + default: + { + if (printed && had_whitespace) + { + *(p++)=' '; + } + printed=1; + had_whitespace=0; + *(p++)=*in; + break; + } + } + in++; } - in++; - } - fprintf( stderr, _( "%s: Unknown error in munge()\n"), MIMEDB ); - error=1; - return 0; + fprintf(stderr, _("%s: Unknown error in munge()\n"), MIMEDB); + error=1; + return 0; } /** @@ -466,210 +470,210 @@ 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; + 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 ) + if ((1+strlen(lang)*4) >= BUFF_SIZE) { - case '@': - case '.': - case '_': - if( close ) - { - *out++ = ')'; - *out++ = '?'; - } - - close=1; - *out++ = '('; - *out++ = *lang; - break; - - default: - *out++ = *lang; + fprintf(stderr, _("%s: Locale string too long\n"), MIMEDB); + error = 1; + return 0; } - } - if( close ) - { - *out++ = ')'; - *out++ = '?'; - } - *out++=0; + for (; *lang; lang++) + { + switch (*lang) + { + case '@': + case '.': + case '_': + if (close) + { + *out++ = ')'; + *out++ = '?'; + } - return buff; + close=1; + *out++ = '('; + *out++ = *lang; + break; + + default: + *out++ = *lang; + } + } + + if (close) + { + *out++ = ')'; + *out++ = '?'; + } + *out++=0; + + return buff; } /** Get description for a specified mimetype. */ -static char *get_description( const char *mimetype ) +static char *get_description(const char *mimetype) { - char *fn_part; + char *fn_part; - std::string fn; - int fd; - struct stat st; - char *contents; - char *start=0, *stop=0, *best_start=0; + std::string fn; + int fd; + struct stat st; + char *contents; + char *start=0, *stop=0, *best_start=0; - if( !start_re ) - { - char *lang; - char buff[BUFF_SIZE]; + if (!start_re) + { + char *lang; + char buff[BUFF_SIZE]; - lang = get_lang_re(); - if( !lang ) - return 0; + lang = get_lang_re(); + if (!lang) + return 0; - snprintf( buff, BUFF_SIZE, START_TAG, lang, lang ); + 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 ) ) ) - { + 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 ) + 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 ); + fn_part = (char *)my_malloc(strlen(MIME_DIR) + strlen(mimetype) + strlen(MIME_SUFFIX) + 1); - if( !fn_part ) - { - return 0; - } + if (!fn_part) + { + return 0; + } - strcpy( fn_part, MIME_DIR ); - strcat( fn_part, mimetype ); - strcat( fn_part, MIME_SUFFIX ); + 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 ); + fn = get_filename(fn_part); //malloc( strlen(MIME_DIR) +strlen( MIME_SUFFIX)+ strlen( mimetype ) + 1 ); + free(fn_part); - if( fn.empty() ) - { - return 0; - } + 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 (fd == -1) + { + perror("open"); + error=1; + return 0; + } - if( stat( fn.c_str(), &st) ) - { - perror( "stat" ); - 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; - } + 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 (read(fd, contents, st.st_size) != st.st_size) + { + perror("read"); + error=1; free((void *)contents); + return 0; + } + + /* + 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; + + 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; - } - - /* - 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; - - 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; } @@ -677,353 +681,353 @@ static char *get_description( const char *mimetype ) /** Get default action for a specified mimetype. */ -static char *get_action( const char *mimetype ) +static char *get_action(const char *mimetype) { - char *res=0; + char *res=0; - const char *launcher, *end; - string_list_t mime_filenames; + const char *launcher, *end; + string_list_t mime_filenames; - const char *launcher_str = NULL; - const char *launcher_command_str, *launcher_command; - char *launcher_full; + 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; - } - - - 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 ) + if (!append_filenames(mime_filenames, DESKTOP_DEFAULT, 1)) { - for( p=parents; *p; p++ ) - { - char *a = get_action(*p); - if( a != 0 ) - return a; - } + return 0; } - /* - 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" ); + 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; + } - return 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"); + + return 0; + } // fprintf( stderr, "WOOT %s\n", launcher_str ); - launcher = const_cast(strchr( 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; - } + if (!launcher) + { + fprintf(stderr, _("%s: Could not parse launcher string '%s'\n"), MIMEDB, launcher_str); + error=1; + return 0; + } - /* Skip the = */ - launcher++; + /* Skip the = */ + launcher++; - /* Make one we can change */ - std::string mut_launcher = 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); + /* 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; - } + 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 ); + strcpy(launcher_full, APPLICATIONS_DIR); + strcat(launcher_full, mut_launcher.c_str()); + free((void *)launcher_str); - std::string launcher_filename = get_filename( launcher_full ); + std::string launcher_filename = get_filename(launcher_full); - free( launcher_full ); + free(launcher_full); - launcher_command_str = search_ini( launcher_filename.c_str(), "Exec" ); + 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; - } + 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++; + launcher_command = strchr(launcher_command_str, '='); + launcher_command++; - res = my_strdup( launcher_command ); + res = my_strdup(launcher_command); - free( (void *)launcher_command_str ); + free((void *)launcher_command_str); - return res; + return res; } /** Helper function for launch. Write the specified byte to the string we will execute */ -static void writer( char c ) +static void writer(char c) { - if( launch_len == -1 ) - return; + 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 ) + if (launch_len <= launch_pos) { - free( launch_buff ); - launch_len = -1; - error=1; - return; - } - launch_buff = new_buff; - launch_len = new_len; + 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; + } + launch_buff[launch_pos++]=c; } /** Write out the specified byte in hex */ -static void writer_hex( int num ) +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)); } /** Return current directory in newly allocated string */ -static char *my_getcwd () +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; + } } /** Return absolute filename of specified file */ -static const char *get_fullfile( const char *file ) +static const char *get_fullfile(const char *file) { - const char *fullfile; + const char *fullfile; - if( file[0] == '/' ) - { - fullfile = file; - } - else - { - char *cwd = my_getcwd(); - if( !cwd ) + if (file[0] == '/') { - error = 1; - perror( "getcwd" ); - return 0; + fullfile = file; } - - int l = strlen(cwd); - - char *tmp = (char *)my_malloc( l + strlen(file)+2 ); - if( !tmp ) + else { - free(cwd); - return 0; - } - strcpy( tmp, cwd ); - if( cwd[l-1] != '/' ) - strcat(tmp, "/" ); - strcat( tmp, file ); + char *cwd = my_getcwd(); + if (!cwd) + { + error = 1; + perror("getcwd"); + return 0; + } - free(cwd); + 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; } /** Write specified file as an URL */ -static void write_url( const char *file ) +static void write_url(const char *file) { - const char *fullfile = get_fullfile( file ); - const char *str = fullfile; + const char *fullfile = get_fullfile(file); + const char *str = fullfile; - if( str == 0 ) - { - launch_len = -1; - return; - } + 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('f'); + writer('i'); + writer('l'); + writer('e'); + writer(':'); + writer('/'); + writer('/'); + while (*str) { - writer(*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++; } - else if(strchr( "()?&=",*str) != 0) - { - writer('\\'); - writer(*str); - } - else - { - writer( '%' ); - writer_hex( (unsigned char)*str ); - } - str++; - } - if( fullfile != file ) - free( (void *)fullfile ); + if (fullfile != file) + free((void *)fullfile); } /** Write specified file */ -static void write_file( const char *file, int print_path ) +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 ) + const char *fullfile; + const char *str; + if (print_path) { - return; + fullfile = get_fullfile(file); + str = fullfile; } - str = basename( tmp ); + 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; - - case '\n': - writer('\\'); - writer('n'); - break; - - case '\r': - writer('\\'); - writer('r'); - break; - - case '\t': - writer('\\'); - writer('t'); - break; - - case '\b': - writer('\\'); - writer('b'); - break; - - case '\v': - writer('\\'); - writer('v'); - break; - - default: - writer(*str); - break; } - str++; - } - if( fullfile != file ) - free( (void *)fullfile ); + 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; + + case '\n': + writer('\\'); + writer('n'); + break; + + case '\r': + writer('\\'); + writer('r'); + break; + + case '\t': + writer('\\'); + writer('t'); + break; + + case '\b': + writer('\\'); + writer('b'); + break; + + case '\v': + writer('\\'); + writer('v'); + break; + + default: + writer(*str); + break; + } + str++; + } + + if (fullfile != file) + free((void *)fullfile); } /** @@ -1033,170 +1037,170 @@ static void write_file( const char *file, int print_path ) \param files the list of files for which to perform the action \param fileno an internal value. Should always be set to zero. */ -static void launch( char *filter, const string_list_t &files, size_t fileno ) +static void launch(char *filter, const string_list_t &files, size_t fileno) { - char *filter_org=filter; - int count=0; - int launch_again=0; + char *filter_org=filter; + int count=0; + int launch_again=0; - if( files.size() <= fileno ) - return; + if (files.size() <= fileno) + return; - launch_pos=0; + launch_pos=0; - for( ;*filter && !error; filter++) - { - if(*filter == '%') + for (; *filter && !error; filter++) { - filter++; - switch( *filter ) - { - case 'u': + if (*filter == '%') { - 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(); + 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 ) + /* + Parse options + */ + while (1) { - case 0: - break; + 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 't': - input_type=FILEDATA; - break; + int opt_index = 0; - case 'f': - input_type=FILENAME; - break; + int opt = getopt_long(argc, + argv, + GETOPT_STRING, + long_options, + &opt_index); - case 'i': - input_type=MIMETYPE; - break; + if (opt == -1) + break; - case 'm': - output_type=MIMETYPE; - break; + switch (opt) + { + case 0: + break; - case 'd': - output_type=DESCRIPTION; - break; + case 't': + input_type=FILEDATA; + break; - case 'a': - output_type=ACTION; - break; + case 'f': + input_type=FILENAME; + break; - case 'l': - output_type=LAUNCH; - break; + case 'i': + input_type=MIMETYPE; + break; - case 'h': - print_help( argv[0], 1 ); - exit(0); + case 'm': + output_type=MIMETYPE; + break; - case 'v': - printf( _("%s, version %s\n"), MIMEDB, PACKAGE_VERSION ); - exit( 0 ); + case 'd': + output_type=DESCRIPTION; + break; - case '?': - return 1; + 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++) + if ((output_type == LAUNCH)&&(input_type==MIMETYPE)) { - /* 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; + fprintf(stderr, _("%s: Can not launch a mimetype\n"), MIMEDB); + print_help(argv[0], 2); + exit(1); } /* - Convert from mimetype to whatever, if needed + Loop over all non option arguments and do the specified lookup */ - switch( output_type ) + + //fprintf( stderr, "Input %d, output %d\n", input_type, output_type ); + + for (i = optind; (i < argc)&&(!error); i++) { - case MIMETYPE: - { - output = (char *)mimetype; - break; + /* 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 DESCRIPTION: - { - output = get_description( mimetype ); - if( !output ) - output = strdup( _("Unknown") ); + 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; + } - 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. + 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; - string_list_t &l = launch_hash[mimetype]; - l.push_back(argv[i]); - } } /* - Print the glorious result + Perform the actual launching */ - if( output ) + if (output_type == LAUNCH && !error) { - printf( "%s\n", output ); - if( output != mimetype ) - free( output ); - } - output = 0; + 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); + } + } } - /* - Perform the actual launching - */ - if( output_type == LAUNCH && !error ) - { - for( launch_hash_t::iterator iter = launch_hash.begin(); iter != launch_hash.end(); ++iter) + if (launch_buff) + free(launch_buff); + + if (start_re) { - 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 ); - } + regfree(start_re); + regfree(stop_re); + free(start_re); + free(stop_re); } - } - if( launch_buff ) - free( launch_buff ); + xdg_mime_shutdown(); - if( start_re ) - { - regfree( start_re ); - regfree( stop_re ); - free( start_re ); - free( stop_re ); - } - - xdg_mime_shutdown(); - - return error; + return error; } diff --git a/output.cpp b/output.cpp index 49917239b..ee48e2198 100644 --- a/output.cpp +++ b/output.cpp @@ -62,24 +62,24 @@ */ #define COLORS (sizeof(col)/sizeof(wchar_t *)) -static int writeb_internal( char c ); +static int writeb_internal(char c); /** 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, }; /** @@ -119,46 +119,57 @@ static wcstring current_term; static bool support_term256 = false; -void output_set_writer( int (*writer)(char) ) +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) { +static bool term256_support_is_native(void) +{ /* Return YES if we think the term256 support is "native" as opposed to forced. */ return max_colors == 256; } -bool output_get_supports_term256() { +bool output_get_supports_term256() +{ return support_term256; } -void output_set_supports_term256(bool val) { +void output_set_supports_term256(bool val) +{ support_term256 = val; } -static unsigned char index_for_color(rgb_color_t c) { - if (c.is_named() || ! output_get_supports_term256()) { +static unsigned char index_for_color(rgb_color_t c) +{ + if (c.is_named() || ! output_get_supports_term256()) + { return c.to_name_index(); - } else { + } + else + { return c.to_term256_index(); } } -static bool write_color(char *todo, unsigned char idx, bool is_fg) { +static bool write_color(char *todo, unsigned char idx, bool is_fg) +{ bool result = false; - if (idx < 16 || term256_support_is_native()) { + if (idx < 16 || term256_support_is_native()) + { /* Use tparm */ - writembs( tparm( todo, idx ) ); + writembs(tparm(todo, idx)); result = true; - } else { + } + else + { /* We are attempting to bypass the term here. Generate the ANSI escape sequence ourself. */ char stridx[128]; format_long_safe(stridx, idx); @@ -168,8 +179,10 @@ static bool write_color(char *todo, unsigned char idx, bool is_fg) { strcat(buff, "m"); int (*writer)(char) = output_get_writer(); - if (writer) { - for (size_t i=0; buff[i]; i++) { + if (writer) + { + for (size_t i=0; buff[i]; i++) + { writer(buff[i]); } } @@ -179,22 +192,34 @@ static bool write_color(char *todo, unsigned char idx, bool is_fg) { return result; } -static bool write_foreground_color(unsigned char idx) { - if (set_a_foreground && set_a_foreground[0]) { +static bool write_foreground_color(unsigned char idx) +{ + if (set_a_foreground && set_a_foreground[0]) + { return write_color(set_a_foreground, idx, true); - } else if (set_foreground && set_foreground[0]) { + } + else if (set_foreground && set_foreground[0]) + { return write_color(set_foreground, idx, true); - } else { + } + else + { return false; } } -static bool write_background_color(unsigned char idx) { - if (set_a_background && set_a_background[0]) { +static bool write_background_color(unsigned char idx) +{ + if (set_a_background && set_a_background[0]) + { return write_color(set_a_background, idx, false); - } else if (set_background && set_background[0]) { + } + else if (set_background && set_background[0]) + { return write_color(set_background, idx, false); - } else { + } + else + { return false; } } @@ -212,383 +237,389 @@ void set_color(rgb_color_t c, rgb_color_t c2) 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; + 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; + 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; - } + /* + 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_bold |= c.is_bold(); + is_bold |= c2.is_bold(); - is_underline |= c.is_underline(); - is_underline |= c2.is_underline(); + 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 (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; - } + 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 (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() && - ! last_color2.is_reset() && - ! last_color2.is_ignore() ) - { - /* - Background was set - */ - last_bg_set=1; - } + if (! last_color2.is_normal() && + ! last_color2.is_reset() && + ! last_color2.is_ignore()) + { + /* + Background was set + */ + last_bg_set=1; + } - if( ! c2.is_normal() && - ! c2.is_ignore()) - { - /* - Background is set - */ - bg_set=1; - if ( c==c2 ) + if (! c2.is_normal() && + ! c2.is_ignore()) + { + /* + Background is set + */ + 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) - { - /* - 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) - { - /* - Background color changed and is no longer set, so we - exit bold mode - */ - 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() ) + if ((enter_bold_mode != 0) && (strlen(enter_bold_mode) > 0)) { - write_foreground_color(0); - writembs( exit_attribute_mode ); - - last_color2 = rgb_color_t::normal(); - was_bold=0; - was_underline=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) + { + /* + Background color changed and is no longer set, so we + exit bold mode + */ + 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(); + } + } } - else if( ! c.is_special() ) + + 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; + last_color = c; - if( last_color2 != c2 ) - { - if( c2.is_normal() ) + 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 ) + /* + Lastly, we set bold mode and underline mode correctly + */ + if ((enter_bold_mode != 0) && (strlen(enter_bold_mode) > 0) && !bg_set) { - if( enter_bold_mode ) - { - writembs( tparm( enter_bold_mode ) ); - } + if (is_bold && !was_bold) + { + if (enter_bold_mode) + { + writembs(tparm(enter_bold_mode)); + } + } + was_bold = is_bold; } - was_bold = is_bold; - } - if( was_underline && !is_underline ) - { - writembs( exit_underline_mode ); - } + if (was_underline && !is_underline) + { + writembs(exit_underline_mode); + } - if( !was_underline && is_underline ) - { - writembs( enter_underline_mode ); - } - was_underline = is_underline; + if (!was_underline && is_underline) + { + writembs(enter_underline_mode); + } + was_underline = is_underline; } /** Default output method, simply calls write() on stdout */ -static int writeb_internal( char c ) +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 ) +int writeb(tputs_arg_t b) { - out( b ); - return 0; + out(b); + return 0; } -int writembs_internal( char *str ) +int writembs_internal(char *str) { - CHECK( str, 1 ); + CHECK(str, 1); - return tputs(str,1,&writeb)==ERR?1:0; + return tputs(str,1,&writeb)==ERR?1:0; } -int writech( wint_t ch ) +int writech(wint_t ch) { - mbstate_t state; - size_t i; - char buff[MB_LEN_MAX+1]; - size_t bytes; + 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 ) + if ((ch >= ENCODE_DIRECT_BASE) && + (ch < ENCODE_DIRECT_BASE+256)) { - case (size_t)(-1): - { - return 1; - } + buff[0] = ch - ENCODE_DIRECT_BASE; + bytes=1; } - } + else + { + memset(&state, 0, sizeof(state)); + bytes= wcrtomb(buff, ch, &state); - for( i=0; imax_width ) + if (tot <= max_width) { - break; + writestr(str); + return; } - written+=w; - writech( *(str++) ); - } - written += fish_wcwidth( ellipsis_char ); - writech( ellipsis_char ); + while (*str != 0) + { + int w = fish_wcwidth(*str); + if (written+w+fish_wcwidth(ellipsis_char)>max_width) + { + break; + } + written+=w; + writech(*(str++)); + } - while( written < max_width ) - { - written++; - writestr( L" " ); - } + 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 ) +int write_escaped_str(const wchar_t *str, int max_len) { - wchar_t *out; - int i; - int len; - int written=0; + wchar_t *out; + int i; + int len; + int written=0; - CHECK( str, 0 ); + CHECK(str, 0); - out = escape( str, 1 ); - len = my_wcswidth( out ); + 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++ ) + if (max_len && (max_len < len)) { - writech( out[i] ); - written += fish_wcwidth( out[i] ); - } - writech( ellipsis_char ); - written += fish_wcwidth( ellipsis_char ); + 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 ); + tokenize_variable_array(val, el); - for(size_t j=0; j < el.size(); j++ ) { + for (size_t j=0; j < el.size(); j++) + { const wcstring &next = el.at(j); wcstring color_name; - if (is_background) { + if (is_background) + { // look for something like "--background=red" const wcstring prefix = L"--background="; - if (string_prefixes_string(prefix, next)) { + if (string_prefixes_string(prefix, next)) + { color_name = wcstring(next, prefix.size()); } - } else { + } + else + { if (next == L"--bold" || next == L"-o") is_bold = true; else if (next == L"--underline" || next == L"-u") @@ -640,9 +678,11 @@ rgb_color_t parse_color( const wcstring &val, bool is_background ) { color_name = next; } - if (! color_name.empty()) { + if (! color_name.empty()) + { rgb_color_t color = rgb_color_t(color_name); - if (! color.is_none()) { + if (! color.is_none()) + { candidates.push_back(color); } } @@ -650,7 +690,8 @@ 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++) { + for (size_t i=0; i < candidates.size(); i++) + { const rgb_color_t &color = candidates.at(i); if (color.is_rgb() && first_rgb.is_none()) first_rgb = color; @@ -660,9 +701,12 @@ rgb_color_t parse_color( const wcstring &val, bool is_background ) { // 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()) { + if ((!first_rgb.is_none() && output_get_supports_term256()) || first_named.is_none()) + { result = first_rgb; - } else { + } + else + { result = first_named; } @@ -680,9 +724,9 @@ rgb_color_t parse_color( const wcstring &val, bool is_background ) { return result; } -void output_set_term( const wchar_t *term ) +void output_set_term(const wchar_t *term) { - current_term = term; + current_term = term; } const wchar_t *output_get_term() diff --git a/output.h b/output.h index b88c7bef7..9359335b8 100644 --- a/output.h +++ b/output.h @@ -17,18 +17,18 @@ */ 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 } ; @@ -104,41 +104,41 @@ void set_color(rgb_color_t c, rgb_color_t c2); as the sending function. But a weird bug on PPC Linux means that on this platform, write is instead used directly. */ -int writembs_internal( char *str ); +int writembs_internal(char *str); /** Write a wide character using the output method specified using output_set_writer(). */ -int writech( wint_t ch ); +int writech(wint_t ch); /** Write a wide character string to FD 1. */ -void writestr( const wchar_t *str ); +void writestr(const wchar_t *str); /** Write a wide character string to FD 1. If the string is wider than the specified maximum, truncate and ellipsize it. */ -void writestr_ellipsis( const wchar_t *str, int max_width ); +void writestr_ellipsis(const wchar_t *str, int max_width); /** Escape and write a string to fd 1 */ -int write_escaped_str( const wchar_t *str, int max_len ); +int write_escaped_str(const wchar_t *str, int max_len); /** Return the internal color code representing the specified color */ -int output_color_code( const wcstring &val, bool is_background ); -rgb_color_t parse_color( const wcstring &val, bool is_background ); +int output_color_code(const wcstring &val, bool is_background); +rgb_color_t parse_color(const wcstring &val, bool is_background); /** This is for writing process notification messages. Has to write to stdout, so clr_eol and such functions will work correctly. Not an issue since this function is only used in interactive mode anyway. */ -int writeb( tputs_arg_t b ); +int writeb(tputs_arg_t b); /** Set the function used for writing in move_cursor, writespace and @@ -146,7 +146,7 @@ int writeb( tputs_arg_t b ); default, the write call is used to give completely unbuffered output to stdout. */ -void output_set_writer( int (*writer)(char) ); +void output_set_writer(int (*writer)(char)); /** Return the current output writer @@ -154,7 +154,7 @@ void output_set_writer( int (*writer)(char) ); int (*output_get_writer())(char) ; /** Set the terminal name */ -void output_set_term( const wchar_t *term ); +void output_set_term(const wchar_t *term); /** Return the terminal name */ const wchar_t *output_get_term(); diff --git a/parse_util.cpp b/parse_util.cpp index dc1116574..3b96ac671 100644 --- a/parse_util.cpp +++ b/parse_util.cpp @@ -51,614 +51,614 @@ */ #define AUTOLOAD_MIN_AGE 60 -int parse_util_lineno( const wchar_t *str, size_t offset ) +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; - } + if (line_offset2 >= off2-off-1) + { + line_offset2 = off2-off-1; + } - return off + line_offset2; + 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 ); + CHECK(in, 0); - for( pos = (wchar_t *)in; *pos; pos++ ) - { - if( prev != '\\' ) + for (pos = (wchar_t *)in; *pos; pos++) { - if( wcschr( L"\'\"", *pos ) ) - { - wchar_t *q_end = quote_end( pos ); - if( q_end && *q_end) + if (prev != '\\') { - pos=q_end; - } - else - { - break; - } - } - else - { - if( *pos == '(' ) - { - if(( paran_count == 0)&&(paran_begin==0)) - { - paran_begin = pos; - } + 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 == ')') + { + + paran_count--; + + if ((paran_count == 0) && (paran_end == 0)) + { + paran_end = pos; + break; + } + + if (paran_count < 0) + { + syntax_error = 1; + break; + } + } + } - paran_count++; } - else if( *pos == ')' ) + 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) +{ + wchar_t *begin, *end; + wchar_t *pos; + const wchar_t *cursor = buff + cursor_pos; + + CHECK(buff,); + + if (a) + { + *a = (wchar_t *)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) { - - paran_count--; - - if( (paran_count == 0) && (paran_end == 0) ) - { - paran_end = pos; + /* + No subshell found + */ break; - } - - if( paran_count < 0 ) - { - syntax_error = 1; - break; - } } - } + if (!end) + { + end = (wchar_t *)buff + wcslen(buff); + } + + if ((begin < cursor) && (end >= cursor)) + { + begin++; + + if (a) + { + *a = begin; + } + + if (b) + { + *b = end; + } + + break; + } + + if (!*end) + { + break; + } + + pos = end+1; } - 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 ) -{ - wchar_t *begin, *end; - wchar_t *pos; - const wchar_t *cursor = buff + cursor_pos; - - CHECK( buff, ); - - if( a ) - { - *a = (wchar_t *)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( !end ) - { - end = (wchar_t *)buff + wcslen(buff); - } - - if(( begin < cursor ) && (end >= cursor) ) - { - begin++; - - if( a ) - { - *a = begin; - } - - if( b ) - { - *b = end; - } - - break; - } - - 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 ) +static void job_or_process_extent(const wchar_t *buff, + 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; + const wchar_t *begin, *end; + long pos; + wchar_t *buffcpy; + int finished=0; - tokenizer tok; + tokenizer tok; - CHECK( buff, ); + CHECK(buff,); - if( a ) - { - *a=0; - } - - if( b ) - { - *b = 0; - } - - parse_util_cmdsubst_extent( buff, cursor_pos, &begin, &end ); - if( !end || !begin ) - { - return; - } - - pos = cursor_pos - (begin - buff); - - if( a ) - { - *a = begin; - } - - if( b ) - { - *b = end; - } - - 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 ) ) + if (a) { - case TOK_PIPE: - { - if( !process ) - { - break; - } - } + *a=0; + } - case TOK_END: - case TOK_BACKGROUND: - { + if (b) + { + *b = 0; + } - if( tok_begin >= pos ) + parse_util_cmdsubst_extent(buff, cursor_pos, &begin, &end); + if (!end || !begin) + { + return; + } + + pos = cursor_pos - (begin - buff); + + if (a) + { + *a = begin; + } + + if (b) + { + *b = end; + } + + 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)) { - finished=1; - if( b ) - { - *b = (wchar_t *)buff + tok_begin; - } - } - else + case TOK_PIPE: { - if( a ) - { - *a = (wchar_t *)buff + tok_begin+1; - } + if (!process) + { + break; + } } - break; - } - } - } + case TOK_END: + case TOK_BACKGROUND: + { - 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 ) -{ - 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 ) -{ - 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 ) -{ - const wchar_t *begin, *end; - long pos; - wchar_t *buffcpy; - - 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 ); - - 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(); - } - - 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); - } - 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 ); - - if( *arg ) - arg++; - } - - - } - -} - -wchar_t *parse_util_unescape_wildcards( const wchar_t *str ) -{ - wchar_t *in, *out; - wchar_t *unescaped; - - CHECK( str, 0 ); - - unescaped = wcsdup(str); - - if( !unescaped ) - { - DIE_MEM(); - } - - for( in=out=unescaped; *in; in++ ) - { - switch( *in ) - { - case L'\\': - { - switch ( *(in + 1) ) + if (tok_begin >= pos) + { + finished=1; + if (b) { - case L'*': - case L'?': - { - in++; - *(out++)=*in; - break; - } - case L'\\': - { - in++; - *(out++)=L'\\'; - *(out++)=L'\\'; - break; - } - default: - { - *(out++)=*in; - break; - } + *b = (wchar_t *)buff + tok_begin; } - break; - } + } + else + { + if (a) + { + *a = (wchar_t *)buff + tok_begin+1; + } + } - case L'*': - { - *(out++)=ANY_STRING; - break; - } - - case L'?': - { - *(out++)=ANY_CHAR; - break; - } - - default: - { - *(out++)=*in; - break; - } + 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) +{ + 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) +{ + 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) +{ + const wchar_t *begin, *end; + long pos; + wchar_t *buffcpy; + + 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); + + 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(); + } + + 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); + } + 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); + + if (*arg) + arg++; + } + + + } + +} + +wchar_t *parse_util_unescape_wildcards(const wchar_t *str) +{ + wchar_t *in, *out; + wchar_t *unescaped; + + CHECK(str, 0); + + unescaped = wcsdup(str); + + if (!unescaped) + { + DIE_MEM(); + } + + for (in=out=unescaped; *in; in++) + { + switch (*in) + { + case L'\\': + { + switch (*(in + 1)) + { + case L'*': + case L'?': + { + in++; + *(out++)=*in; + break; + } + case L'\\': + { + in++; + *(out++)=L'\\'; + *(out++)=L'\\'; + 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; } @@ -667,116 +667,116 @@ wchar_t *parse_util_unescape_wildcards( const wchar_t *str ) token is not quoted. */ -static wchar_t get_quote( const wchar_t *cmd, size_t len ) +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)) + if (cmd[i] == L'\\') { - res = cmd[i]; - break; + 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++; } - 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 ) +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); - - if( !unfinished ) + cmd_tmp[pos]=0; + size_t cmdlen = wcslen(cmd_tmp); + unfinished = (cmdlen==0); + if (!unfinished) { - if( wcschr( L" \t\n\r", cmd_tmp[cmdlen-1] ) != 0 ) - { - if( ( cmdlen == 1) || (cmd_tmp[cmdlen-2] != L'\\') ) + unfinished = (quote != 0); + + if (!unfinished) { - unfinished=1; + 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 ) + if (offset != 0) { - while( (cmd_tmp[prev_pos] != 0) && (wcschr( L";|",cmd_tmp[prev_pos])!= 0) ) - prev_pos++; + if (!unfinished) + { + while ((cmd_tmp[prev_pos] != 0) && (wcschr(L";|",cmd_tmp[prev_pos])!= 0)) + prev_pos++; - *offset = prev_pos; + *offset = prev_pos; + } + else + { + *offset = pos; + } } - else - { - *offset = pos; - } - } free(cmd_tmp); } -wcstring parse_util_escape_string_with_quote( const wcstring &cmd, wchar_t quote) +wcstring parse_util_escape_string_with_quote(const wcstring &cmd, wchar_t quote) { wcstring result; - if( quote == L'\0' ) + if (quote == L'\0') { - result = escape_string( cmd, ESCAPE_ALL | ESCAPE_NO_QUOTED | ESCAPE_NO_TILDE ); + result = escape_string(cmd, ESCAPE_ALL | ESCAPE_NO_QUOTED | ESCAPE_NO_TILDE); } else { @@ -786,17 +786,17 @@ wcstring parse_util_escape_string_with_quote( const wcstring &cmd, wchar_t quote wchar_t c = cmd.at(i); switch (c) { - case L'\n': - case L'\t': - case L'\b': - case L'\r': - unescapable = true; - break; - default: - if (c == quote) - result.push_back(L'\\'); - result.push_back(c); - break; + case L'\n': + case L'\t': + case L'\b': + case L'\r': + unescapable = true; + break; + default: + if (c == quote) + result.push_back(L'\\'); + result.push_back(c); + break; } } diff --git a/parse_util.h b/parse_util.h index 4b8699f8e..b8e370f76 100644 --- a/parse_util.h +++ b/parse_util.h @@ -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 @@ -39,10 +39,10 @@ int parse_util_locate_cmdsubst( const wchar_t *in, \param a the start of the searched string \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 ); +void parse_util_cmdsubst_extent(const wchar_t *buff, + size_t cursor_pos, + const wchar_t **a, + const wchar_t **b); /** Find the beginning and end of the process definition under the cursor @@ -52,10 +52,10 @@ void parse_util_cmdsubst_extent( const wchar_t *buff, \param a the start of the searched string \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 ); +void parse_util_process_extent(const wchar_t *buff, + size_t cursor_pos, + const wchar_t **a, + const wchar_t **b); /** @@ -66,10 +66,10 @@ void parse_util_process_extent( const wchar_t *buff, \param a the start of the searched string \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 ); +void parse_util_job_extent(const wchar_t *buff, + 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 @@ -83,46 +83,46 @@ void parse_util_job_extent( const wchar_t *buff, \param prev_begin the start o the token before the current token \param prev_end the end of the token before the current token */ -void parse_util_token_extent( const wchar_t *buff, - size_t cursor_pos, - const wchar_t **tok_begin, - const wchar_t **tok_end, - const wchar_t **prev_begin, - const wchar_t **prev_end ); +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); /** Get the linenumber at the specified character offset */ -int parse_util_lineno( const wchar_t *str, size_t len ); +int parse_util_lineno(const wchar_t *str, size_t len); /** Calculate the line number of the specified cursor position */ -int parse_util_get_line_from_offset( const wcstring &str, size_t pos ); +int parse_util_get_line_from_offset(const wcstring &str, size_t pos); /** Get the offset of the first character on the specified line */ -size_t parse_util_get_offset_from_line( const wcstring &str, int line ); +size_t parse_util_get_offset_from_line(const wcstring &str, int line); /** Return the total offset of the buffer for the cursor position nearest to the specified poition */ -size_t parse_util_get_offset( const wcstring &str, int line, long line_offset ); +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. */ -void parse_util_set_argv( const wchar_t * const *argv, const wcstring_list_t &named_arguments ); +void parse_util_set_argv(const wchar_t * const *argv, const wcstring_list_t &named_arguments); /** Make a duplicate of the specified string, unescape wildcard characters but not performing any other character transformation. */ -wchar_t *parse_util_unescape_wildcards( const wchar_t *in ); +wchar_t *parse_util_unescape_wildcards(const wchar_t *in); /** Calculates information on the parameter at the specified index. @@ -133,12 +133,12 @@ wchar_t *parse_util_unescape_wildcards( const wchar_t *in ); \param offset If not NULL, get_param will store the offset to the beginning of the parameter. \param type If not NULL, get_param will store the token type as returned by tok_last. */ -void parse_util_get_parameter_info( const wcstring &cmd, const size_t pos, wchar_t *quote, size_t *offset, int *type ); +void parse_util_get_parameter_info(const wcstring &cmd, const size_t pos, wchar_t *quote, size_t *offset, int *type); /** Attempts to escape the string 'cmd' using the given quote type, as determined by the quote character. The quote can be a single quote or double quote, or L'\0' to indicate no quoting (and thus escaping should be with backslashes). */ -wcstring parse_util_escape_string_with_quote( const wcstring &cmd, wchar_t quote); +wcstring parse_util_escape_string_with_quote(const wcstring &cmd, wchar_t quote); #endif diff --git a/parser.cpp b/parser.cpp index 48b96fb33..520316a80 100644 --- a/parser.cpp +++ b/parser.cpp @@ -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; + /** + 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; + /** + 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); @@ -404,7 +404,7 @@ void parser_t::skip_all_blocks(void) { //write(2, "Cancelling blocks\n", strlen("Cancelling blocks\n")); block_t *c = s_principal_parser->current_block; - while( c ) + while (c) { c->skip = true; //fprintf(stderr, " Cancelled %p\n", c); @@ -426,66 +426,66 @@ static int block_count( block_t *b ) } */ -void parser_t::push_block( block_t *newv ) +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->src_lineno = parser_t::get_lineno(); + newv->src_filename = parser_t::current_filename()?intern(parser_t::current_filename()):0; - newv->outer = current_block; + 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; + /* + 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; - } + /* + 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; - } + /* + 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; + newv->job = 0; + newv->loop_status=LOOP_NORMAL; - current_block = newv; + current_block = newv; - if( (newv->type() != FUNCTION_DEF) && - (newv->type() != FAKE) && - (newv->type() != TOP) ) - { - env_push( type == FUNCTION_CALL ); + 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; - } + 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; + current_block = current_block->outer; if (old->wants_pop_env) env_pop(); @@ -493,258 +493,259 @@ void parser_t::pop_block() delete old; } -const wchar_t *parser_t::get_block_desc( int block ) const +const wchar_t *parser_t::get_block_desc(int block) const { - int i; + int i; - for( i=0; block_lookup[i].desc; i++ ) - { - if( block_lookup[i].type == block ) + for (i=0; block_lookup[i].desc; i++) { - return _( block_lookup[i].desc ); + if (block_lookup[i].type == block) + { + return _(block_lookup[i].desc); + } } - } - return _(UNKNOWN_BLOCK); + return _(UNKNOWN_BLOCK); } /** Returns 1 if the specified command is a builtin that may not be used in a pipeline */ -static int parser_is_pipe_forbidden( const wcstring &word ) +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"); } /** Search the text for the end of the current block */ -static const wchar_t *parser_find_end( const wchar_t * buff ) +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 ) + for (tok_init(&tok, buff, 0); + tok_has_next(&tok) && !error; + tok_next(&tok)) { - case TOK_STRING: - { - if( !had_cmd ) + int last_type = tok_last_type(&tok); + switch (last_type) { - if( wcscmp(tok_last(&tok), L"end")==0) - { - count--; - } - else if( parser_keywords_is_block( tok_last(&tok) ) ) - { - count++; - } + 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 ) - { + if (count < 0) + { + error = 1; + } + had_cmd = 1; + } + 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_ERROR: error = 1; - } - had_cmd = 1; + break; + + default: + break; + } - break; - } - - case TOK_END: - { - had_cmd = 0; - break; - } - - case TOK_PIPE: - case TOK_BACKGROUND: - { - if( had_cmd ) + if (!count) { - had_cmd = 0; + tok_next(&tok); + mark = tok_get_pos(&tok); + break; } - else - { - error = 1; - } - break; - - } - - case TOK_ERROR: - error = 1; - break; - - default: - break; } - if(!count) + + tok_destroy(&tok); + if (!count && !error) { - tok_next( &tok ); - mark = tok_get_pos( &tok ); - break; + + return buff+mark; } - - } - - tok_destroy( &tok ); - if(!count && !error){ - - return buff+mark; - } - return 0; + return 0; } -void parser_t::forbid_function( const wcstring &function ) +void parser_t::forbid_function(const wcstring &function) { forbidden_function.push_back(function); } void parser_t::allow_function() { -/* - if( al_peek( &forbidden_function) ) - debug( 2, L"Allow %ls\n", al_peek( &forbidden_function) ); -*/ + /* + if( al_peek( &forbidden_function) ) + debug( 2, L"Allow %ls\n", al_peek( &forbidden_function) ); + */ forbidden_function.pop_back(); } -void parser_t::error( int ec, int p, const wchar_t *str, ... ) +void parser_t::error(int ec, int p, const wchar_t *str, ...) { - va_list va; + va_list va; - CHECK( str, ); + CHECK(str,); - error_code = ec; - err_pos = p; + error_code = ec; + err_pos = p; - va_start( va, str ); + va_start(va, str); err_buff = vformat_string(str, va); - va_end( va ); + va_end(va); } /** Print profiling information to the specified stream */ -static void print_profile( const std::vector &items, - size_t pos, - FILE *out ) +static void print_profile(const std::vector &items, + 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; - - for( i=pos+1; i= items.size()) { - prev = &items.at(i); - if( prev->skipped ) - { - continue; - } - - if( prev->level <= me->level ) - { - break; - } - - if( prev->level > me->level+1 ) - { - continue; - } - - my_time -= prev->parse; - my_time -= prev->exec; + return; } - if( me->cmd.size() > 0 ) + me= &items.at(pos); + if (!me->skipped) { - if( fwprintf( out, L"%d\t%d\t", my_time, me->parse+me->exec ) < 0 ) - { - wperror( L"fwprintf" ); - return; - } + my_time=me->parse+me->exec; - for( i=0; ilevel; i++ ) - { - if( fwprintf( out, L"-" ) < 0 ) + for (i=pos+1; iskipped) + { + continue; + } + + if (prev->level <= me->level) + { + break; + } + + if (prev->level > me->level+1) + { + continue; + } + + my_time -= prev->parse; + my_time -= prev->exec; } - } - if( fwprintf( out, L"> %ls\n", me->cmd.c_str() ) < 0 ) - { - wperror( L"fwprintf" ); - return; - } + 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 ); + 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 ) + if (profile) { - 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 ); - } + /* 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 (fclose(f)) + { + wperror(L"fclose"); + } + } } - } lineinfo.clear(); - forbidden_function.clear(); + forbidden_function.clear(); } @@ -754,23 +755,23 @@ void parser_t::destroy() \param target the buffer to write to \param prefix: The string token to prefix the ech line with. Usually the name of the command trying to parse something. */ -void parser_t::print_errors( wcstring &target, const wchar_t *prefix ) +void parser_t::print_errors(wcstring &target, const wchar_t *prefix) { - CHECK( prefix, ); + CHECK(prefix,); - if( error_code && ! err_buff.empty() ) - { - int tmp; + if (error_code && ! err_buff.empty()) + { + int tmp; - append_format( target, L"%ls: %ls\n", prefix, err_buff.c_str() ); + append_format(target, L"%ls: %ls\n", prefix, err_buff.c_str()); - tmp = current_tokenizer_pos; - current_tokenizer_pos = err_pos; + tmp = current_tokenizer_pos; + current_tokenizer_pos = err_pos; - append_format( target, L"%ls", this->current_line() ); + append_format(target, L"%ls", this->current_line()); - current_tokenizer_pos=tmp; - } + current_tokenizer_pos=tmp; + } } /** @@ -778,24 +779,24 @@ 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; + 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; + tmp = current_tokenizer_pos; + current_tokenizer_pos = err_pos; - fwprintf( stderr, L"%ls", this->current_line() ); + fwprintf(stderr, L"%ls", this->current_line()); - current_tokenizer_pos=tmp; - } + current_tokenizer_pos=tmp; + } } -int parser_t::eval_args( const wchar_t *line, std::vector &args ) +int parser_t::eval_args(const wchar_t *line, std::vector &args) { - tokenizer tok; + tokenizer tok; expand_flags_t eflags = 0; if (! show_errors) @@ -803,190 +804,190 @@ int parser_t::eval_args( const wchar_t *line, std::vector &args ) 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; + /* + 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(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; + current_tokenizer = &tok; + current_tokenizer_pos = 0; - tok_init( &tok, line, (show_errors ? 0 : TOK_SQUASH_ERRORS) ); - error_code=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 ) ) + for (; do_loop && tok_has_next(&tok) ; tok_next(&tok)) { - case TOK_STRING: - { - const wcstring tmp = tok_last(&tok); - if( expand_string(tmp, args, eflags) == EXPAND_ERROR ) + current_tokenizer_pos = tok_get_pos(&tok); + switch (tok_last_type(&tok)) { - err_pos=tok_get_pos( &tok ); - do_loop=0; + 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; } - break; - } - case TOK_END: - { - break; - } + case TOK_END: + { + break; + } - case TOK_ERROR: - { - if (show_errors) - error( SYNTAX_ERROR, - tok_get_pos( &tok ), - TOK_ERR_MSG, - tok_last(&tok) ); + 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: - { - if (show_errors) - error( SYNTAX_ERROR, - tok_get_pos( &tok ), - UNEXPECTED_TOKEN_ERR_MSG, - tok_get_desc( tok_last_type(&tok)) ); + 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 ); + tok_destroy(&tok); - current_tokenizer=previous_tokenizer; - current_tokenizer_pos = previous_pos; + 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) +void parser_t::stack_trace(block_t *b, wcstring &buff) { - /* - Check if we should end the recursion - */ - if( !b ) - return; - - if( b->type()==EVENT ) - { /* - This is an event handler + Check if we should end the recursion */ + if (!b) + return; + + 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" ); + wcstring description = event_get_desc(eb->event); + 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 - */ - - 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: - { - 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; - } - - default: /* Can't get here */ - break; + 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; } - const wchar_t *file = b->src_filename; - - if( file ) + if (b->type() == FUNCTION_CALL || b->type()==SOURCE || b->type()==SUBST) { - 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") ); - } + /* + These types of blocks should be printed + */ - if( b->type() == FUNCTION_CALL ) + 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: + { + 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; + } + + default: /* Can't get here */ + break; + } + + 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) { const function_block_t *fb = static_cast(b); const process_t * const process = fb->process; - if( process->argv(1) ) - { - wcstring tmp; + if (process->argv(1)) + { + wcstring tmp; - for( i=1; process->argv(i); i++ ) - { + 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"\twith parameter list '%ls'\n"), tmp.c_str() ); - } + + append_format(buff, L"\n"); } - append_format( buff, L"\n" ); - } - - /* - Recursively print the next block - */ - parser_t::stack_trace( b->outer, buff ); + /* + Recursively print the next block + */ + parser_t::stack_trace(b->outer, buff); } /** @@ -1001,44 +1002,44 @@ const wchar_t *parser_t::is_function() const 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(); + return fb->name.c_str(); + } + b=b->outer; } - b=b->outer; - } } int parser_t::get_lineno() const { - int lineno; + int lineno; - if( ! current_tokenizer || ! tok_string( current_tokenizer )) - return -1; + 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 ); - } + if ((function_name = is_function())) + { + lineno += function_get_definition_offset(function_name); + } - return lineno; + return lineno; } int parser_t::line_number_of_character_at_offset(size_t idx) const { - if( ! current_tokenizer) + if (! current_tokenizer) return -1; int result = current_tokenizer->line_number_of_character_at_offset(idx); @@ -1052,21 +1053,21 @@ const wchar_t *parser_t::current_filename() const ASSERT_IS_MAIN_THREAD(); assert(this == &principal_parser()); - block_t *b = current_block; + block_t *b = current_block; - while( 1 ) - { - if( !b ) - { - return reader_current_filename(); - } - if( b->type() == FUNCTION_CALL ) + 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); + return function_get_definition_file(fb->name); + } + b=b->outer; } - b=b->outer; - } } /** @@ -1075,175 +1076,175 @@ const wchar_t *parser_t::current_filename() const allignment of the tab character, but other wise behaves like repeatedly calling wcwidth. */ -static int printed_width( const wchar_t *str, int len ) +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) ); + return (wcscmp(L"-h", s) == 0) || + (len >= min_match && (wcsncmp(L"--help", s, len) == 0)); } job_t *parser_t::job_create(void) @@ -1251,22 +1252,25 @@ 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; } -bool parser_t::job_remove( job_t *j ) +bool parser_t::job_remove(job_t *j) { job_list_t::iterator iter = std::find(my_job_list.begin(), my_job_list.end(), j); - if (iter != my_job_list.end()) { + if (iter != my_job_list.end()) + { my_job_list.erase(iter); return true; - } else { - debug( 1, _( L"Job inconsistency" ) ); - sanity_lose(); + } + else + { + debug(1, _(L"Job inconsistency")); + sanity_lose(); return false; } } @@ -1284,22 +1288,24 @@ job_t *parser_t::job_get(job_id_t id) { job_iterator_t jobs(my_job_list); job_t *job; - while ((job = jobs.next())) { - if( id <= 0 || job->job_id == 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 ) +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; + while ((job = jobs.next())) + { + if (job->pgid == pid) + return job; + } + return 0; } /** @@ -1311,383 +1317,383 @@ job_t *parser_t::job_get_from_pid( int pid ) \param args the argument list to insert options into \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, - bool unskip ) +void parser_t::parse_job_argument_list(process_t *p, + 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" ); + /* + 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 ) - { - - switch( tok_last_type( tok ) ) + while (1) { - case TOK_PIPE: - { - wchar_t *end; - if (p->type == INTERNAL_EXEC) + switch (tok_last_type(tok)) { - 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 ) + case TOK_PIPE: { - error( SYNTAX_ERROR, - tok_get_pos( tok ), - ILLEGAL_FD_ERR_MSG, - tok_last( tok ) ); - return; - } + wchar_t *end; - p->set_argv(completions_to_wcstring_list(args)); - 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 ); - - 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); - - is_finished = 1; - - 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; - } - 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 */ - if (job_get_flag(j, JOB_ELSEIF) && job_should_skip_elseif(j, current_block)) - { - skip = 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: + if (p->type == INTERNAL_EXEC) { - 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; + error(SYNTAX_ERROR, + tok_get_pos(tok), + EXEC_ERR_MSG); + return; } - case EXPAND_WILDCARD_NO_MATCH: + errno = 0; + p->pipe_write_fd = fish_wcstoi(tok_last(tok), &end, 10); + if (p->pipe_write_fd < 0 || errno || *end) { - unmatched_wildcard = 1; - if( unmatched.empty() ) - { - unmatched = tok_last(tok); - unmatched_pos = tok_get_pos( tok ); - } - - break; + error(SYNTAX_ERROR, + tok_get_pos(tok), + ILLEGAL_FD_ERR_MSG, + tok_last(tok)); + return; } - case EXPAND_WILDCARD_MATCH: - { - matched_wildcard = 1; - break; - } + p->set_argv(completions_to_wcstring_list(args)); + p->next = new process_t(); - case EXPAND_OK: - { - 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 ); - std::auto_ptr new_io; - wcstring target; - bool has_target = false; - wchar_t *end; - - /* - Don't check redirections in skipped part - - 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)) ); - } - - break; - } - - new_io.reset(new io_data_t); - - 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 ) ); - - } - break; - } - - 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 - { - switch( type ) + /* + 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; + 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); + + is_finished = 1; + + break; + } + + case TOK_STRING: + { + int skip=0; + + if (job_get_flag(j, JOB_SKIP)) { - 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; + skip = 1; + } + else if (current_block->skip && ! unskip) + { + /* + If this command should be skipped, we do not expand the arguments + */ + skip=1; - 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_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"-" ) + /* 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) { - new_io->io_mode = IO_CLOSE; + 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 */ + if (job_get_flag(j, JOB_ELSEIF) && job_should_skip_elseif(j, current_block)) + { + skip = 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)); + + } + break; + } + + case EXPAND_WILDCARD_NO_MATCH: + { + unmatched_wildcard = 1; + if (unmatched.empty()) + { + unmatched = tok_last(tok); + unmatched_pos = tok_get_pos(tok); + } + + break; + } + + case EXPAND_WILDCARD_MATCH: + { + matched_wildcard = 1; + break; + } + + case EXPAND_OK: + { + 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); + std::auto_ptr new_io; + wcstring target; + bool has_target = false; + wchar_t *end; + + /* + Don't check redirections in skipped part + + 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))); + } + + break; + } + + new_io.reset(new io_data_t); + + 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)); + + } + break; + } + + 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 { - wchar_t *end; - new_io->io_mode = IO_FD; - errno = 0; + 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; - new_io->param1.old_fd = fish_wcstoi( target.c_str(), &end, 10 ); + 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; - 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_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_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()); + + tok_next(tok); + } + } + break; + } + } - tok_next(tok); - } } - break; - } } - } + j->io.push_back(new_io.release()); + + } + break; + + case TOK_ERROR: + { + error(SYNTAX_ERROR, + tok_get_pos(tok), + TOK_ERR_MSG, + tok_last(tok)); + + return; } - j->io.push_back(new_io.release()); + default: + error(SYNTAX_ERROR, + tok_get_pos(tok), + UNEXPECTED_TOKEN_ERR_MSG, + tok_get_desc(tok_last_type(tok))); - } - break; + tok_next(tok); + break; + } - case TOK_ERROR: - { - error( SYNTAX_ERROR, - tok_get_pos( tok ), - TOK_ERR_MSG, - tok_last(tok) ); - - return; - } - - default: - error( SYNTAX_ERROR, - tok_get_pos( tok ), - UNEXPECTED_TOKEN_ERR_MSG, - tok_get_desc( tok_last_type(tok)) ); + if ((is_finished) || (error_code != 0)) + break; tok_next(tok); - break; } - if( (is_finished) || (error_code != 0) ) - break; - - tok_next( tok ); - } - - if( !error_code ) - { - if( unmatched_wildcard && !matched_wildcard ) + if (!error_code) { - job_set_flag( j, JOB_WILDCARD_ERROR, 1 ); - proc_set_last_status( STATUS_UNMATCHED_WILDCARD ); - if( get_is_interactive() && !is_block ) - { - int tmp; + 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; + 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() ); + fwprintf(stderr, L"%ls", parser_t::current_line()); - current_tokenizer_pos=tmp; - } + current_tokenizer_pos=tmp; + } + } } - } - return; + return; } /* @@ -1710,214 +1716,214 @@ void parser_t::parse_job_argument_list( process_t *p, f \return 1 on success, 0 on error */ -int parser_t::parse_job( process_t *p, - job_t *j, - tokenizer *tok ) +int parser_t::parse_job(process_t *p, + 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 + 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) + switch (tok_last_type(tok)) { - 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) ); - - current_tokenizer_pos = prev_tokenizer_pos; - return 0; - } - - case TOK_PIPE: - { - const wchar_t *str = tok_string( tok ); - if( tok_get_pos(tok)>0 && str[tok_get_pos(tok)-1] == L'|' ) + case TOK_STRING: { - 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) ) ); + 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; } - 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) ) ); - - 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 ) + case TOK_ERROR: { - tok_next( tok ); + error(SYNTAX_ERROR, + tok_get_pos(tok), + TOK_ERR_MSG, + tok_last(tok)); + + current_tokenizer_pos = prev_tokenizer_pos; + return 0; } - consumed = true; + 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))); + } - 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; - } + current_tokenizer_pos = prev_tokenizer_pos; + return 0; } - else if( nxt == L"not" ) + + default: { - job_set_flag( j, JOB_NEGATE, !job_get_flag( j, JOB_NEGATE ) ); + 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; } - else if( nxt == L"and" ) + } + + 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( is_exec ) + else if (nxt == L"while") { - 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 ); + 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 */ @@ -1931,16 +1937,16 @@ int parser_t::parse_job( process_t *p, /* If we've already encountered an else, complain */ if (ib->else_evaluated) { - error( SYNTAX_ERROR, - else_pos, - INVALID_ELSEIF_PAST_ELSE_ERR_MSG, - L"else if"); + error(SYNTAX_ERROR, + else_pos, + INVALID_ELSEIF_PAST_ELSE_ERR_MSG, + L"else if"); } else { - job_set_flag( j, JOB_ELSEIF, 1 ); + job_set_flag(j, JOB_ELSEIF, 1); consumed = true; /* We're at the IF. Go past it. */ @@ -1956,101 +1962,101 @@ 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; - } + /* + 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; + if (use_function && (unskip || ! current_block->skip)) + { + bool nxt_forbidden=false; + wcstring forbid; - int is_function_call=0; + 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; + /* + 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 ) - { + /* + 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) { /* Infinite recursive loop */ nxt_forbidden = true; - error( SYNTAX_ERROR, tok_get_pos( tok ), INFINITE_RECURSION_ERR_MSG ); + 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 ); + 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; + } + } } - 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 ); - } + args.push_back(completion_t(nxt)); } - if( (!p->type || (p->type == INTERNAL_EXEC) ) ) + if (error_code == 0) { - /* - If we are not executing the current block, allow - non-existent commands. - */ + 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) @@ -2072,12 +2078,12 @@ int parser_t::parse_job( process_t *p, } } - /* Check if the specified command exists */ - if( ! has_command && ! use_implicit_cd ) - { + /* Check if the specified command exists */ + if (! has_command && ! use_implicit_cd) + { - int tmp; - const wchar_t *cmd = args.at( 0 ).completion.c_str(); + int tmp; + const wchar_t *cmd = args.at(0).completion.c_str(); /* We couldn't find the specified command. @@ -2096,13 +2102,13 @@ int parser_t::parse_job( process_t *p, for this, used by other shells like bash and zsh). */ - if( wcschr( cmd, L'=' ) ) + if (wcschr(cmd, L'=')) { - wchar_t *cpy = wcsdup( cmd ); - wchar_t *valpart = wcschr( cpy, L'=' ); + wchar_t *cpy = wcsdup(cmd); + wchar_t *valpart = wcschr(cpy, L'='); *valpart++=0; - debug( 0, + debug(0, COMMAND_ASSIGN_ERR_MSG, cmd, cpy, @@ -2110,189 +2116,189 @@ int parser_t::parse_job( process_t *p, free(cpy); } - else if(cmd[0]==L'$') + else if (cmd[0]==L'$') { - const env_var_t val_wstr = env_get_string( cmd+1 ); + const env_var_t val_wstr = env_get_string(cmd+1); const wchar_t *val = val_wstr.missing() ? NULL : val_wstr.c_str(); - if( val ) + if (val) { - debug( 0, - _(L"Variables may not be used as commands. Instead, define a function like 'function %ls; %ls $argv; end'. See the help section for the function command by typing 'help function'." ), + debug(0, + _(L"Variables may not be used as commands. Instead, define a function like 'function %ls; %ls $argv; end'. See the help section for the function command by typing 'help function'."), cmd+1, val, - cmd ); + cmd); } else { - 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 ); + 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'$' )) + else if (wcschr(cmd, L'$')) { - debug( 0, - _(L"Commands may not contain variables. Use the eval builtin instead, like 'eval %ls'. See the help section for the eval command by typing 'help eval'." ), + debug(0, + _(L"Commands may not contain variables. Use the eval builtin instead, like 'eval %ls'. See the help section for the eval command by typing 'help eval'."), cmd, - cmd ); + cmd); } - else if( err!=ENOENT ) + else if (err!=ENOENT) { - debug( 0, + debug(0, _(L"The file '%ls' is not executable by this user"), - cmd?cmd:L"UNKNOWN" ); + cmd?cmd:L"UNKNOWN"); } else { - debug( 0, + debug(0, _(L"Unknown command '%ls'"), - cmd?cmd:L"UNKNOWN" ); + cmd?cmd:L"UNKNOWN"); } tmp = current_tokenizer_pos; current_tokenizer_pos = tok_get_pos(tok); - fwprintf( stderr, L"%ls", parser_t::current_line() ); + 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 ); + 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( (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 (is_new_block) { - if( !make_sub_block ) - { - int done=0; + const wchar_t *end=parser_find_end(tok_string(tok) + + current_tokenizer_pos); + tokenizer subtok; + int make_sub_block = j->first_process != p; - for( tok_init( &subtok, end, 0 ); - !done && tok_has_next( &subtok ); - tok_next( &subtok ) ) + if (!end) + { + error(SYNTAX_ERROR, + tok_get_pos(tok), + BLOCK_END_ERR_MSG); + + } + else { - 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: + if (!make_sub_block) { - done = 1; - make_sub_block = 1; - break; + 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); } - case TOK_STRING: + if (make_sub_block) { - break; - } - default: - { - done = 1; - error( SYNTAX_ERROR, - current_tokenizer_pos, - BLOCK_END_ERR_MSG ); + 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(); + } + } - } + else tok_next(tok); } - 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(); - } - - } - else tok_next( tok ); } + 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 (!error_code) { - if( !p->get_argv() ) + 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 ) + if (!error_code) { - current_block->had_command = true; + if (!is_new_block) + { + current_block->had_command = true; + } } - } - if( error_code ) - { - /* - Make sure the block stack is consistent - */ - while( prev_block != current_block ) + if (error_code) { - parser_t::pop_block(); + /* + 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; + current_tokenizer_pos = prev_tokenizer_pos; + return !error_code; } /** @@ -2303,55 +2309,55 @@ int parser_t::parse_job( process_t *p, \param j the job to execute */ -void parser_t::skipped_exec( job_t * j ) +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 ) + for (p = j->first_process; p; p=p->next) { - 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 ) + if (p->type == INTERNAL_BUILTIN) { - exec( *this, j ); - return; - } - parser_t::pop_block(); - } - else if( wcscmp( p->argv0(), L"else" )==0) - { - if (current_block->type() == IF) + 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); if (ib->if_expr_evaluated && ! ib->any_branch_taken) { - exec( *this, j ); + exec(*this, j); return; } } } - else if( wcscmp( p->argv0(), L"case" )==0) - { - if(current_block->type() == SWITCH) - { - exec( *this, j ); - return; + else if (wcscmp(p->argv0(), L"case")==0) + { + if (current_block->type() == SWITCH) + { + exec(*this, j); + return; + } + } } - } } - } - job_free( j ); + job_free(j); } /* Return whether we should skip the current block, if it is an elseif. */ @@ -2382,250 +2388,250 @@ static bool job_should_skip_elseif(const job_t *job, const block_t *current_bloc \param tok The tokenizer to read tokens from */ -void parser_t::eval_job( tokenizer *tok ) +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; + bool skip = false; + int job_begin_pos, prev_tokenizer_pos; + const bool do_profile = profile; - if( do_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(); - } + t1 = get_time(); + } - switch( tok_last_type( tok ) ) - { + 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 = 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; + current_block->job = j; - if( get_is_interactive() ) - { - if( tcgetattr (0, &j->tmodes) ) + if (get_is_interactive()) { - 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 (tcgetattr(0, &j->tmodes)) + { + tok_next(tok); + wperror(L"tcgetattr"); + job_free(j); + break; + } } - /* 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)) + 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 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) { - bool skip_elseif = job_should_skip_elseif(j, current_block); - - /* Record that we're entering an elseif */ - if (! skip_elseif) - { - /* We must be an IF block here */ - 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; + /* We must be an IF block here */ + assert(current_block->type() == IF); + static_cast(current_block)->is_elseif_entry = true; } - skip = skip || current_block->skip; - skip = skip || job_get_flag( j, JOB_WILDCARD_ERROR ); - skip = skip || job_get_flag( j, JOB_SKIP ); + /* Record that in the block too. This is similar to what builtin_else does. */ + current_block->skip = skip_elseif; + } - 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; + 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); + } + + 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; + } + } + + if (current_block->type() == IF) + { + if_block_t *ib = static_cast(current_block); + + if (ib->skip) + { + /* Nothing */ + } + else if (! ib->if_expr_evaluated) + { + /* 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; + } + else if (ib->is_elseif_entry && ! ib->any_branch_taken) + { + /* Maybe mark an ELSEIF branch as taken */ + bool elseif_taken = (proc_get_last_status() == 0); + ib->any_branch_taken = elseif_taken; + current_block->skip = ! elseif_taken; + ib->is_elseif_entry = false; + } + } - /* Only external commands require a new fishd barrier */ - if( !was_builtin ) - set_proc_had_barrier(false); } else { - this->skipped_exec( j ); + /* + 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); } - - 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; - } - } - - if( current_block->type() == IF ) - { - if_block_t *ib = static_cast(current_block); - - if (ib->skip) - { - /* Nothing */ - } - else if (! ib->if_expr_evaluated) - { - /* 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; - } - else if (ib->is_elseif_entry && ! ib->any_branch_taken) - { - /* Maybe mark an ELSEIF branch as taken */ - bool elseif_taken = (proc_get_last_status() == 0); - ib->any_branch_taken = elseif_taken; - 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 ); - - proc_set_last_status( 1 ); - } - current_block->job = 0; - break; + current_block->job = 0; + break; } case TOK_END: { - if( tok_has_next( tok )) - tok_next( tok ); - break; + 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) ) ); - } + 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) ); + 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)) ); + 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 ) +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; @@ -2633,149 +2639,149 @@ int parser_t::eval( const wcstring &cmdStr, const io_chain_t &io, enum block_typ std::vector prev_forbidden = forbidden_function; - if( block_type == SUBST ) - { - forbidden_function.clear(); - } - - CHECK_BLOCK( 1 ); - - forbid_count = forbidden_function.size(); - - block_io = io; - - job_reap( 0 ); - - debug( 4, L"eval: %ls", cmd ); - - if( !cmd ) - { - debug( 1, - EVAL_NULL_ERR_MSG ); - bugreport(); - return 1; - } - - if( (block_type != TOP) && - (block_type != SUBST)) - { - debug( 1, - INVALID_SCOPE_ERR_MSG, - parser_t::get_block_desc( block_type ) ); - bugreport(); - return 1; - } - - eval_level++; - - this->push_block( new scope_block_t(block_type) ); - - current_tokenizer = new tokenizer; - tok_init( current_tokenizer, cmd, 0 ); - - error_code = 0; - - event_fire( NULL ); - - while( tok_has_next( current_tokenizer ) && - !error_code && - !sanity_check() && - !exit_status() ) - { - this->eval_job( current_tokenizer ); - event_fire( NULL ); - } - - parser_t::pop_block(); - - while( start_current_block != current_block ) - { - if( current_block == 0 ) + if (block_type == SUBST) { - debug( 0, - _(L"End of block mismatch. Program terminating.") ); - bugreport(); - FATAL_EXIT(); - break; + forbidden_function.clear(); } - if( (!error_code) && (!exit_status()) && (!proc_get_last_status()) ) + CHECK_BLOCK(1); + + forbid_count = forbidden_function.size(); + + block_io = io; + + job_reap(0); + + debug(4, L"eval: %ls", cmd); + + if (!cmd) { - - //debug( 2, L"Status %d\n", proc_get_last_status() ); - - 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() ); - - const wcstring h = builtin_help_get( *this, L"end" ); - if( h.size() ) - fwprintf( stderr, L"%ls", h.c_str() ); - break; - + debug(1, + EVAL_NULL_ERR_MSG); + bugreport(); + return 1; } + + if ((block_type != TOP) && + (block_type != SUBST)) + { + debug(1, + INVALID_SCOPE_ERR_MSG, + parser_t::get_block_desc(block_type)); + bugreport(); + return 1; + } + + eval_level++; + + this->push_block(new scope_block_t(block_type)); + + current_tokenizer = new tokenizer; + tok_init(current_tokenizer, cmd, 0); + + error_code = 0; + + event_fire(NULL); + + while (tok_has_next(current_tokenizer) && + !error_code && + !sanity_check() && + !exit_status()) + { + this->eval_job(current_tokenizer); + event_fire(NULL); + } + parser_t::pop_block(); - } - this->print_errors_stderr(); + while (start_current_block != current_block) + { + if (current_block == 0) + { + debug(0, + _(L"End of block mismatch. Program terminating.")); + bugreport(); + FATAL_EXIT(); + break; + } - tok_destroy( current_tokenizer ); - delete current_tokenizer; + if ((!error_code) && (!exit_status()) && (!proc_get_last_status())) + { + + //debug( 2, L"Status %d\n", proc_get_last_status() ); + + 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()); + + const wcstring h = builtin_help_get(*this, L"end"); + if (h.size()) + fwprintf(stderr, L"%ls", h.c_str()); + break; + + } + 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; } /** \return the block type created by the specified builtin, or -1 on error. */ -block_type_t parser_get_block_type( const wcstring &cmd ) +block_type_t parser_get_block_type(const wcstring &cmd) { - int i; + int i; - for( i=0; block_lookup[i].desc; i++ ) - { - if( block_lookup[i].name && cmd == block_lookup[i].name ) + for (i=0; block_lookup[i].desc; i++) { - return block_lookup[i].type; + if (block_lookup[i].name && cmd == block_lookup[i].name) + { + return block_lookup[i].type; + } } - } - return (block_type_t)-1; + return (block_type_t)-1; } /** \return the block command that createa the specified block type, or null on error. */ -const wchar_t *parser_get_block_command( int type ) +const wchar_t *parser_get_block_command(int type) { - int i; + int i; - for( i=0; block_lookup[i].desc; i++ ) - { - if( block_lookup[i].type == type ) + for (i=0; block_lookup[i].desc; i++) { - return block_lookup[i].name; + if (block_lookup[i].type == type) + { + return block_lookup[i].name; + } } - } - return 0; + return 0; } /** @@ -2783,864 +2789,865 @@ const wchar_t *parser_get_block_command( int type ) syntax errors in command substitutions, improperly escaped characters and improper use of the variable expansion operator. */ -int parser_t::parser_test_argument( const wchar_t *arg, wcstring *out, const wchar_t *prefix, int offset ) +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 *unesc; + wchar_t *pos; + int err=0; - wchar_t *paran_begin, *paran_end; - wchar_t *arg_cpy; - int do_loop = 1; + wchar_t *paran_begin, *paran_end; + wchar_t *arg_cpy; + int do_loop = 1; - CHECK( arg, 1 ); + CHECK(arg, 1); - arg_cpy = wcsdup( arg ); + arg_cpy = wcsdup(arg); - while( do_loop ) - { - switch( parse_util_locate_cmdsubst(arg_cpy, - ¶n_begin, - ¶n_end, - 0 ) ) + while (do_loop) { - case -1: - err=1; - if( out ) + switch (parse_util_locate_cmdsubst(arg_cpy, + ¶n_begin, + ¶n_end, + 0)) { - error( SYNTAX_ERROR, - offset, - L"Mismatched parenthesis" ); - this->print_errors( *out, prefix); - } - free( arg_cpy ); - return err; + 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 0: + do_loop = 0; + break; - case 1: - { + case 1: + { - wchar_t *subst = wcsndup( paran_begin+1, paran_end-paran_begin-1 ); - wcstring tmp; + 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); + 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 ); + err |= parser_t::test(subst, 0, out, prefix); - free( subst ); - free( arg_cpy ); - arg_cpy = wcsdup(tmp.c_str()); + 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 + { /* - 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. + Check for invalid variable expansions */ - 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: + for (pos = unesc; *pos; pos++) { - wchar_t n = *(pos+1); - - if( n != VARIABLE_EXPAND && - n != VARIABLE_EXPAND_SINGLE && - !wcsvarchr(n) ) - { - err=1; - if( out ) + switch (*pos) { - expand_variable_error( *this, unesc, pos-unesc, offset ); - print_errors( *out, prefix); + 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; + } } - } - - break; } - } } - } - free( arg_cpy ); + free(arg_cpy); - free( unesc ); - return err; + free(unesc); + return err; } -int parser_t::test_args(const wchar_t * buff, wcstring *out, const wchar_t *prefix ) +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; + tokenizer tok; + tokenizer *previous_tokenizer = current_tokenizer; + int previous_pos = current_tokenizer_pos; + int do_loop = 1; + int err = 0; - CHECK( buff, 1 ); + CHECK(buff, 1); - current_tokenizer = &tok; + 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 ) ) + for (tok_init(&tok, buff, 0); + do_loop && tok_has_next(&tok); + tok_next(&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 ) + current_tokenizer_pos = tok_get_pos(&tok); + switch (tok_last_type(&tok)) { - 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 ) + case TOK_STRING: { - error( SYNTAX_ERROR, - tok_get_pos( &tok ), - UNEXPECTED_TOKEN_ERR_MSG, - tok_get_desc( tok_last_type(&tok)) ); - print_errors( *out, prefix ); + 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; + } } - err=1; - do_loop=0; - break; - } } - } - tok_destroy( &tok ); + tok_destroy(&tok); - current_tokenizer=previous_tokenizer; - current_tokenizer_pos = previous_pos; + current_tokenizer=previous_tokenizer; + current_tokenizer_pos = previous_pos; - error_code=0; + error_code=0; - return err; + return err; } -int parser_t::test( const wchar_t * buff, - int *block_level, - wcstring *out, - const wchar_t *prefix ) +int parser_t::test(const wchar_t * buff, + 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 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; + 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; + 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 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 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; + /* + 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; + /* + 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. - */ + /* + The currently validated command. + */ wcstring command; bool has_command = false; - CHECK( buff, 1 ); + CHECK(buff, 1); - if( block_level ) - { - size_t len = wcslen(buff); - for( size_t i=0; i= BLOCK_MAX_COUNT ) - { - if (out) { - error( SYNTAX_ERROR, - tok_get_pos( &tok ), - BLOCK_ERR_MSG ); + /* + 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); + } - print_errors( *out, prefix ); + /* + Handle block commands + */ + if (parser_keywords_is_block(command)) + { + if (count >= BLOCK_MAX_COUNT) + { + if (out) + { + error(SYNTAX_ERROR, + tok_get_pos(&tok), + BLOCK_ERR_MSG); + + 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); + } + } + + /* + 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); + + print_errors(*out, prefix); + + } + } + } + + /* + 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); + + } + } + forbid_pipeline = 1; + } + + /* + 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; + + if (out) + { + error(SYNTAX_ERROR, + tok_get_pos(&tok), + INVALID_CASE_ERR_MSG); + + 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 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; + + if (out) + { + error(SYNTAX_ERROR, + tok_get_pos(&tok), + INVALID_RETURN_ERR_MSG); + print_errors(*out, prefix); + } + } + } + } + + + /* + 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 (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); + } + } + } + + /* + 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 { - block_type[count] = parser_get_block_type( command ); - block_pos[count] = current_tokenizer_pos; - tok_next( &tok ); - count++; - tok_set_pos( &tok, mark ); - } - } + err |= parser_test_argument(tok_last(&tok), out, prefix, tok_get_pos(&tok)); - /* - 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 ); - - print_errors( *out, prefix ); - - } - } - } - - /* - 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 ); - - } - } - forbid_pipeline = 1; - } - - /* - 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; - - if( out ) - { - error( SYNTAX_ERROR, - tok_get_pos( &tok ), - INVALID_CASE_ERR_MSG ); - - 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 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)) + /* If possible, keep track of number of supplied arguments */ + if (arg_count >= 0 && expand_is_clean(tok_last(&tok))) { - is_help = 1; + arg_count++; } - } - - tok_set_pos( &tok, old_pos ); - - if( !is_help ) - { - err=1; - - if( out ) + else { - error( SYNTAX_ERROR, - tok_get_pos( &tok ), - INVALID_RETURN_ERR_MSG ); - print_errors( *out, prefix ); - } - } - } - } - - - /* - 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( 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 ); - } - } - } - - /* - 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 ); - } + arg_count = -1; } - } - else if( arg_count == 2 ) - { - if( wcscmp( tok_last( &tok ), L"in" ) != 0 ) + if (has_command) { - 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") + /* + Try to make sure the second argument to 'for' is 'in' + */ + if (command == L"for") + { + if (arg_count == 1) { - 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 ); - } - } - else + if (wcsvarname(tok_last(&tok))) + { + + err = 1; + + if (out) { - /* Successfully detected "else if". Now we need a new command. */ - needs_cmd = true; - had_cmd = false; + 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); + } + } + else + { + /* Successfully detected "else if". Now we need a new command. */ + needs_cmd = true; + had_cmd = false; + } + } + } + } + + } + + break; } - break; - } - - case TOK_REDIRECT_OUT: - case TOK_REDIRECT_IN: - case TOK_REDIRECT_APPEND: - case TOK_REDIRECT_FD: - case TOK_REDIRECT_NOCLOB: - { - if( !had_cmd ) + case TOK_REDIRECT_OUT: + case TOK_REDIRECT_IN: + case TOK_REDIRECT_APPEND: + case TOK_REDIRECT_FD: + case TOK_REDIRECT_NOCLOB: { - err = 1; - if( out ) - { - error( SYNTAX_ERROR, - tok_get_pos( &tok ), - INVALID_REDIRECTION_ERR_MSG ); - print_errors( *out, prefix ); - } - } - 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_PIPE: - { - if( !had_cmd ) - { - err=1; - if( out ) - { - if( tok_get_pos(&tok)>0 && buff[tok_get_pos(&tok)-1] == L'|' ) + if (!had_cmd) { - error( SYNTAX_ERROR, - tok_get_pos( &tok ), - CMD_OR_ERR_MSG, - tok_get_desc( tok_last_type(&tok) ) ); + err = 1; + if (out) + { + error(SYNTAX_ERROR, + tok_get_pos(&tok), + INVALID_REDIRECTION_ERR_MSG); + print_errors(*out, prefix); + } + } + 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_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 { - error( SYNTAX_ERROR, - tok_get_pos( &tok ), - CMD_ERR_MSG, - tok_get_desc( tok_last_type(&tok))); + 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); + } } - print_errors( *out, prefix ); - } - } - else if( forbid_pipeline ) - { - err=1; - if( out ) - { - error( SYNTAX_ERROR, - tok_get_pos( &tok ), - EXEC_ERR_MSG ); + had_cmd = 0; + end_of_cmd = 1; - print_errors( *out, prefix ); - } + break; } - 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'&' ) + case TOK_ERROR: + default: + if (tok_get_error(&tok) == TOK_UNTERMINATED_QUOTE) { - error( SYNTAX_ERROR, - tok_get_pos( &tok ), - CMD_AND_ERR_MSG, - tok_get_desc( tok_last_type(&tok) ) ); - + unfinished = 1; } else { - error( SYNTAX_ERROR, - tok_get_pos( &tok ), - CMD_ERR_MSG, - tok_get_desc( tok_last_type(&tok))); + err = 1; + if (out) + { + error(SYNTAX_ERROR, + tok_get_pos(&tok), + TOK_ERR_MSG, + tok_last(&tok)); + + + print_errors(*out, prefix); + } } - print_errors( *out, prefix ); - } + break; } - had_cmd = 0; - end_of_cmd = 1; - - break; - } - - case TOK_ERROR: - default: - if( tok_get_error( &tok ) == TOK_UNTERMINATED_QUOTE ) + if (end_of_cmd) { - unfinished = 1; - } - else - { - err = 1; - if( out ) - { - error( SYNTAX_ERROR, - tok_get_pos( &tok ), - TOK_ERR_MSG, - tok_last(&tok) ); + 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 ); - } - } - - 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 ); - } - } - } + print_errors(*out, prefix); + } + } + } else if (has_command && command == L"else") { if (arg_count == 1) @@ -3649,148 +3656,148 @@ 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 ); + error(SYNTAX_ERROR, + tok_get_pos(&tok), + BUILTIN_ELSEIF_ERR_COUNT, + L"else", + arg_count); - print_errors( *out, prefix ); + 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 ); - - print_errors( *out, prefix ); - - 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() ); - } - } - - - } - - /* - 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 (!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); + + print_errors(*out, prefix); + + 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()); + } + } + + } /* - 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. + 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; + } + + /* + 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 - */ + /* + Cleanup + */ - tok_destroy( &tok ); + tok_destroy(&tok); - current_tokenizer=previous_tokenizer; - current_tokenizer_pos = previous_pos; + current_tokenizer=previous_tokenizer; + current_tokenizer_pos = previous_pos; - error_code=0; + error_code=0; - return res; + return res; } block_t::block_t(block_type_t t) : - block_type(t), - made_fake(false), - skip(), - had_command(), - tok_pos(), - loop_status(), - job(), - src_filename(), - src_lineno(), - wants_pop_env(false), - event_blocks(), - outer(NULL) + block_type(t), + made_fake(false), + skip(), + had_command(), + tok_pos(), + loop_status(), + job(), + src_filename(), + src_lineno(), + wants_pop_env(false), + event_blocks(), + outer(NULL) { } @@ -3816,7 +3823,7 @@ event_block_t::event_block_t(const event_t *evt) : } function_block_t::function_block_t(process_t *p, const wcstring &n, bool shadows) : - block_t( shadows ? FUNCTION_CALL : FUNCTION_CALL_NO_SHADOW ), + block_t(shadows ? FUNCTION_CALL : FUNCTION_CALL_NO_SHADOW), process(p), name(n) { diff --git a/parser.h b/parser.h index 1c51323a2..e86539836 100644 --- a/parser.h +++ b/parser.h @@ -21,24 +21,26 @@ */ 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; -inline bool event_block_list_blocks_type(const event_blockage_list_t &ebls, int type) { - for (event_blockage_list_t::const_iterator iter = ebls.begin(); iter != ebls.end(); ++iter) { - if( iter->typemask & (1<typemask & (1<typemask & (1<typemask & (1<made_fake ? FAKE : this->block_type; } +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; } + 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 */ + 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; + /** + 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; + /** + 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; @@ -333,15 +344,15 @@ class parser_t { 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 ); - void eval_job( tokenizer *tok ); - 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 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); + void eval_job(tokenizer *tok); + 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: +public: std::vector profile_items; /** @@ -381,7 +392,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 ); + 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. @@ -390,11 +401,11 @@ 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 @@ -403,7 +414,7 @@ class parser_t { \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, ... ); + 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'. @@ -426,22 +437,25 @@ class parser_t { int get_job_pos() const; /** Set the current position in the latest string of the tokenizer. */ - void set_pos( int p); + void set_pos(int p); /** 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; } + job_list_t &job_list() + { + return my_job_list; + } /** Pushes the block. pop_block will call delete on it. */ - void push_block( block_t *newv ); + void push_block(block_t *newv); /** Remove the outermost block namespace */ void pop_block(); /** Return a description of the given blocktype */ - const wchar_t *get_block_desc( int block ) const; + const wchar_t *get_block_desc(int block) const; /** Create a job */ job_t *job_create(); @@ -456,7 +470,7 @@ class parser_t { job_t *job_get(int job_id); /** Returns the job with the given pid */ - job_t *job_get_from_pid( int pid ); + job_t *job_get_from_pid(int pid); /** Test if the specified string can be parsed, or if more bytes need @@ -470,7 +484,7 @@ class parser_t { \param out if non-null, any errors in the command will be filled out into this buffer \param prefix the prefix string to prepend to each error message written to the \c out buffer */ - int test( const wchar_t * buff, int *block_level, wcstring *out, const wchar_t *prefix ); + int test(const wchar_t * buff, int *block_level, wcstring *out, const wchar_t *prefix); /** Test if the specified string can be parsed as an argument list, @@ -478,14 +492,14 @@ class parser_t { string contains errors, and the second bit is set if the string contains an unclosed block. */ - int test_args( const wchar_t * buff, wcstring *out, const wchar_t *prefix ); + int test_args(const wchar_t * buff, wcstring *out, const wchar_t *prefix); /** Tell the parser that the specified function may not be run if not inside of a conditional block. This is to remove some possibilities of infinite recursion. */ - void forbid_function( const wcstring &function ); + void forbid_function(const wcstring &function); /** Undo last call to parser_forbid_function(). */ @@ -507,7 +521,7 @@ class parser_t { \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. */ - int is_help( const wchar_t *s, int min_match ) const; + int is_help(const wchar_t *s, int min_match) const; /** Returns the file currently evaluated by the parser. This can be @@ -519,10 +533,10 @@ class parser_t { /** Write a stack trace starting at the specified block to the specified wcstring */ - void stack_trace( block_t *b, wcstring &buff); + void stack_trace(block_t *b, wcstring &buff); - int get_block_type( const wchar_t *cmd ) const; - const wchar_t *get_block_command( int type ) const; + int get_block_type(const wchar_t *cmd) const; + const wchar_t *get_block_command(int type) const; }; diff --git a/parser_keywords.cpp b/parser_keywords.cpp index 762bb6f2b..8aacd8061 100644 --- a/parser_keywords.cpp +++ b/parser_keywords.cpp @@ -14,62 +14,67 @@ Functions having to do with parser keywords, like testing if a function is a blo #include "parser_keywords.h" -bool parser_keywords_is_switch( const wcstring &cmd ) +bool parser_keywords_is_switch(const wcstring &cmd) { - if (cmd == L"--") { - return ARG_SKIP; - } else if (! cmd.empty() && cmd.at(0) == L'-') { + if (cmd == L"--") + { + return ARG_SKIP; + } + else if (! cmd.empty() && cmd.at(0) == L'-') + { return ARG_SWITCH; - } else { + } + else + { return ARG_NON_SWITCH; } } -bool parser_keywords_skip_arguments( 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 ) +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) +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) +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 d5978c4a6..41d65f471 100644 --- a/parser_keywords.h +++ b/parser_keywords.h @@ -11,9 +11,9 @@ Functions having to do with parser keywords, like testing if a function is a blo */ enum { - ARG_NON_SWITCH, - ARG_SWITCH, - ARG_SKIP + ARG_NON_SWITCH, + ARG_SWITCH, + ARG_SKIP }; @@ -22,7 +22,7 @@ enum Check if the specified argument is a switch. Return ARG_SWITCH if yes, ARG_NON_SWITCH if no and ARG_SKIP if the argument is '--' */ -bool parser_keywords_is_switch( const wcstring &cmd ); +bool parser_keywords_is_switch(const wcstring &cmd); /** @@ -32,7 +32,7 @@ bool parser_keywords_is_switch( const wcstring &cmd ); \return 1 of the command parameter is a command, 0 otherwise */ -bool parser_keywords_is_subcommand( const wcstring &cmd ); +bool parser_keywords_is_subcommand(const wcstring &cmd); /** Tests if the specified command is a reserved word, i.e. if it is @@ -43,20 +43,20 @@ bool parser_keywords_is_subcommand( const wcstring &cmd ); \param word The command name to test \return 1 of the command parameter is a command, 0 otherwise */ -bool parser_keywords_is_reserved( const wcstring &word ); +bool parser_keywords_is_reserved(const wcstring &word); /** Test if the specified string is command that opens a new block */ -bool parser_keywords_is_block( const wcstring &word); +bool parser_keywords_is_block(const wcstring &word); /** Check if the specified command is one of the builtins that cannot have arguments, any followin argument is interpreted as a new command */ -bool parser_keywords_skip_arguments( const wcstring &cmd ); +bool parser_keywords_skip_arguments(const wcstring &cmd); #endif diff --git a/path.cpp b/path.cpp index df7a95965..bea3fe3de 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; + int err = ENOENT; - debug( 3, L"path_get_path( '%ls' )", cmd.c_str() ); + debug(3, L"path_get_path( '%ls' )", cmd.c_str()); /* If the command has a slash, it must be a full path */ - if (cmd.find(L'/') != wcstring::npos) - { - if( waccess( cmd, X_OK )==0 ) + if (cmd.find(L'/') != wcstring::npos) { - struct stat buff; - if(wstat( cmd, &buff )) - { - return false; - } - - if( S_ISREG(buff.st_mode) ) + if (waccess(cmd, X_OK)==0) + { + struct stat buff; + if (wstat(cmd, &buff)) { - if (out_path) + return false; + } + + if (S_ISREG(buff.st_mode)) + { + if (out_path) out_path->assign(cmd); return true; } - else - { - errno = EACCES; - return false; - } + else + { + errno = EACCES; + return false; + } + } + else + { + struct stat buff; + wstat(cmd, &buff); + 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; + 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" ); - } + } + 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; + errno = err; + return false; } bool path_get_path(const wcstring &cmd, wcstring *out_path, const env_vars_snapshot_t &vars) @@ -143,36 +143,37 @@ 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 (dir[0] == L'/'|| (wcsncmp(dir, L"./", 2)==0)) { - if( S_ISDIR(buf.st_mode) ) - { - result = dir_str; + struct stat buf; + if (wstat(dir, &buf) == 0) + { + if (S_ISDIR(buf.st_mode)) + { + result = dir_str; success = true; - } - else - { - err = ENOTDIR; - } + } + else + { + err = ENOTDIR; + } + } } - } - else - { + else + { wcstring path = L"."; // Respect CDPATH env_var_t cdpath = env_get_string(L"CDPATH"); - if (! cdpath.missing_or_empty()) { + if (! cdpath.missing_or_empty()) + { path = cdpath.c_str(); } @@ -186,44 +187,44 @@ bool path_get_cdpath_string(const wcstring &dir_str, wcstring &result, const env 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; - } + if (!success) + { + errno = err; + } - return res; + return res; } bool path_get_cdpath(const wcstring &dir, wcstring *out, const wchar_t *wd, const env_vars_snapshot_t &env_vars) { - int err = ENOENT; - if (dir.empty()) - return false; + int err = ENOENT; + if (dir.empty()) + return false; if (wd) { @@ -232,19 +233,24 @@ bool path_get_cdpath(const wcstring &dir, wcstring *out, const wchar_t *wd, cons } wcstring_list_t paths; - if (dir.at(0) == L'/') { + if (dir.at(0) == L'/') + { /* Absolute path */ paths.push_back(dir); - } else if (string_prefixes_string(L"./", dir) || - string_prefixes_string(L"../", dir) || - dir == L"." || dir == L"..") { + } + else if (string_prefixes_string(L"./", dir) || + string_prefixes_string(L"../", dir) || + dir == L"." || dir == L"..") + { /* Path is relative to the working directory */ wcstring path; if (wd) path.append(wd); path.append(dir); paths.push_back(path); - } else { + } + else + { // Respect CDPATH env_var_t path = env_vars.get(L"CDPATH"); if (path.missing_or_empty()) @@ -255,7 +261,8 @@ bool path_get_cdpath(const wcstring &dir, wcstring *out, const wchar_t *wd, cons while (tokenizer.next(nxt_path)) { - if (nxt_path == L"." && wd != NULL) { + if (nxt_path == L"." && wd != NULL) + { // nxt_path is just '.', and we have a working directory, so use the wd instead // TODO: if nxt_path starts with ./ we need to replace the . with the wd nxt_path = wd; @@ -274,27 +281,28 @@ bool path_get_cdpath(const wcstring &dir, wcstring *out, const wchar_t *wd, cons } bool success = false; - for (wcstring_list_t::const_iterator iter = paths.begin(); iter != paths.end(); ++iter) { - struct stat buf; - const wcstring &dir = *iter; - if( wstat( dir, &buf ) == 0 ) + for (wcstring_list_t::const_iterator iter = paths.begin(); iter != paths.end(); ++iter) { - if( S_ISDIR(buf.st_mode) ) - { + struct stat buf; + const wcstring &dir = *iter; + 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; } @@ -305,9 +313,9 @@ bool path_can_be_implicit_cd(const wcstring &path, wcstring *out_path, const wch bool result = false; if (string_prefixes_string(L"/", exp_path) || - string_prefixes_string(L"./", exp_path) || - string_prefixes_string(L"../", exp_path) || - exp_path == L"..") + string_prefixes_string(L"./", exp_path) || + string_prefixes_string(L"../", exp_path) || + exp_path == L"..") { /* These paths can be implicit cd, so see if you cd to the path. Note that a single period cannot (that's used for sourcing files anyways) */ result = path_get_cdpath(exp_path, out_path, wd, vars); @@ -317,41 +325,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; + 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 ) ) + const env_var_t xdg_dir = env_get_string(L"XDG_CONFIG_HOME"); + if (! xdg_dir.missing()) { - done = 1; + 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() ) + else { - res = home + L"/.config/fish"; - if( !create_directory( res ) ) - { - done = 1; - } + 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 ) - { + 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; + } } @@ -359,25 +367,28 @@ static void replace_all(wcstring &str, const wchar_t *needle, const wchar_t *rep { size_t needle_len = wcslen(needle); size_t offset = 0; - while((offset = str.find(needle, offset)) != wcstring::npos) + while ((offset = str.find(needle, offset)) != wcstring::npos) { str.replace(offset, needle_len, replacement); offset += needle_len; } } -void path_make_canonical( wcstring &path ) +void path_make_canonical(wcstring &path) { /* Remove double slashes */ size_t size; - do { + do + { size = path.size(); replace_all(path, L"//", L"/"); - } while (path.size() != size); + } + while (path.size() != size); /* Remove trailing slashes, except don't remove the first one */ - while (size-- > 1) { + while (size-- > 1) + { if (path.at(size) != L'/') break; } @@ -389,41 +400,56 @@ bool path_is_valid(const wcstring &path, const wcstring &working_directory) { bool path_is_valid; /* Some special paths are always valid */ - if (path.empty()) { + if (path.empty()) + { path_is_valid = false; - } else if (path == L"." || path == L"./") { + } + else if (path == L"." || path == L"./") + { path_is_valid = true; - } else if (path == L".." || path == L"../") { + } + else if (path == L".." || path == L"../") + { path_is_valid = (! working_directory.empty() && working_directory != L"/"); - } else if (path.at(0) != '/') { + } + else if (path.at(0) != '/') + { /* Prepend the working directory. Note that we know path is not empty here. */ wcstring tmp = working_directory; tmp.append(path); path_is_valid = (0 == waccess(tmp, F_OK)); - } else { + } + else + { /* Simple check */ path_is_valid = (0 == waccess(path, F_OK)); } return path_is_valid; } -bool paths_are_same_file(const wcstring &path1, const wcstring &path2) { +bool paths_are_same_file(const wcstring &path1, const wcstring &path2) +{ if (path1 == path2) return true; struct stat s1, s2; - if (wstat(path1, &s1) == 0 && wstat(path2, &s2) == 0) { + if (wstat(path1, &s1) == 0 && wstat(path2, &s2) == 0) + { return s1.st_ino == s2.st_ino && s1.st_dev == s2.st_dev; - } else { + } + else + { return false; } } -wcstring get_working_directory(void) { +wcstring get_working_directory(void) +{ wcstring wd = L"./"; wchar_t dir_path[4096]; - const wchar_t *cwd = wgetcwd( dir_path, 4096 ); - if (cwd) { + const wchar_t *cwd = wgetcwd(dir_path, 4096); + if (cwd) + { wd = cwd; /* Make sure the working directory ends with a slash */ if (! wd.empty() && wd.at(wd.size() - 1) != L'/') diff --git a/path.h b/path.h index aa49b266c..74a931b65 100644 --- a/path.h +++ b/path.h @@ -71,7 +71,7 @@ bool path_can_be_implicit_cd(const wcstring &path, Remove double slashes and trailing slashes from a path, e.g. transform foo//bar/ into foo/bar. The string is modified in-place. */ -void path_make_canonical( wcstring &path ); +void path_make_canonical(wcstring &path); bool path_is_valid(const wcstring &path, const wcstring &working_directory); diff --git a/postfork.cpp b/postfork.cpp index 2609831f6..32dd44558 100644 --- a/postfork.cpp +++ b/postfork.cpp @@ -42,21 +42,21 @@ 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 set_child_group(job_t *j, process_t *p, int print_errors) { - int res = 0; + int res = 0; - if( job_get_flag( j, JOB_CONTROL ) ) - { - if (!j->pgid) + if (job_get_flag(j, JOB_CONTROL)) { - j->pgid = p->pid; - } + if (!j->pgid) + { + j->pgid = p->pid; + } - if( setpgid (p->pid, j->pgid) ) - { - if( getpgid( p->pid) != j->pgid && print_errors ) - { + 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]; @@ -64,41 +64,41 @@ int set_child_group( job_t *j, process_t *p, int print_errors ) 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(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 ); + 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; - } + 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 ) + 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. */ @@ -135,15 +135,15 @@ static void free_redirected_fds_from_pipes(io_chain_t &io_chain) replacement_fd = dup(fd_to_free); if (replacement_fd == -1 && errno != EINTR) { - debug_safe_int( 1, FD_ERROR, fd_to_free ); - wperror( L"dup" ); + debug_safe_int(1, FD_ERROR, fd_to_free); + wperror(L"dup"); FATAL_EXIT(); } } possible_conflict->param1.pipe_fd[k] = replacement_fd; } } - } + } } @@ -159,152 +159,152 @@ static void free_redirected_fds_from_pipes(io_chain_t &io_chain) \return 0 on sucess, -1 on failiure */ -static int handle_child_io( io_chain_t &io_chain ) +static int handle_child_io(io_chain_t &io_chain) { - close_unused_internal_pipes( io_chain ); + close_unused_internal_pipes(io_chain); free_redirected_fds_from_pipes(io_chain); - 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; + int tmp; - if( io->io_mode == IO_FD && io->fd == io->param1.old_fd ) - { - continue; + if (io->io_mode == IO_FD && io->fd == io->param1.old_fd) + { + continue; + } + + switch (io->io_mode) + { + case IO_CLOSE: + { + if (close(io->fd)) + { + debug_safe_int(0, "Failed to close file descriptor %s", io->fd); + wperror(L"close"); + } + break; + } + + case IO_FILE: + { + // 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); + /* + 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 (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; + } + + } } - 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); -/* - 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 (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; - } - - } - } - - return 0; + return 0; } -int setup_child_process( job_t *j, process_t *p ) +int setup_child_process(job_t *j, process_t *p) { - bool ok=true; + bool ok=true; - if( p ) - { - ok = (0 == set_child_group( j, p, 1 )); - } - - if( ok ) - { - ok = (0 == handle_child_io( j->io )); - if( p != 0 && ! ok ) + if (p) { - exit_without_destructors( 1 ); + ok = (0 == set_child_group(j, p, 1)); } - } - /* Set the handling for job control signals back to the default. */ - if( ok ) - { - signal_reset_handlers(); - } + if (ok) + { + ok = (0 == handle_child_io(j->io)); + if (p != 0 && ! ok) + { + exit_without_destructors(1); + } + } - /* Remove all signal blocks */ - signal_unblock(); + /* Set the handling for job control signals back to the default. */ + if (ok) + { + signal_reset_handlers(); + } - return ok ? 0 : -1; + /* Remove all signal blocks */ + signal_unblock(); + + return ok ? 0 : -1; } int g_fork_count = 0; @@ -319,46 +319,47 @@ pid_t execute_fork(bool wait_for_threads_to_die) { ASSERT_IS_MAIN_THREAD(); - if (wait_for_threads_to_die) { + if (wait_for_threads_to_die) + { /* Make sure we have no outstanding threads before we fork. This is a pretty sketchy thing to do here, both because exec.cpp shouldn't have to know about iothreads, and because the completion handlers may do unexpected things. */ 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) + for (i=0; i= 0) + { + return pid; + } + + 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); + } } - 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(); + debug_safe(0, FORK_ERROR); + wperror(L"fork"); + FATAL_EXIT(); return 0; } @@ -366,11 +367,13 @@ pid_t execute_fork(bool wait_for_threads_to_die) bool fork_actions_make_spawn_properties(posix_spawnattr_t *attr, posix_spawn_file_actions_t *actions, job_t *j, process_t *p) { /* Initialize the output */ - if (posix_spawnattr_init(attr) != 0) { + if (posix_spawnattr_init(attr) != 0) + { return false; } - if (posix_spawn_file_actions_init(actions) != 0) { + if (posix_spawn_file_actions_init(actions) != 0) + { posix_spawnattr_destroy(attr); return false; } @@ -389,11 +392,11 @@ bool fork_actions_make_spawn_properties(posix_spawnattr_t *attr, posix_spawn_fil desired_parent_group_id = j->pgid; } - /* Set the handling for job control signals back to the default. */ - bool reset_signal_handlers = 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; + /* Remove all signal blocks */ + bool reset_sigmask = true; /* Set our flags */ short flags = 0; @@ -440,70 +443,71 @@ bool fork_actions_make_spawn_properties(posix_spawnattr_t *attr, posix_spawn_fil { const io_data_t *io = j->io.at(idx); - if( io->io_mode == IO_FD && io->fd == io->param1.old_fd ) - { - continue; - } + 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->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: - { - if (! err) - err = posix_spawn_file_actions_addclose(actions, io->fd); - break; - } - - case IO_FILE: - { - 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: - { - 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 ) + case IO_CLOSE: { - 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]); + if (! err) + err = posix_spawn_file_actions_addclose(actions, io->fd); + break; } - else - { - if (! err) - err = posix_spawn_file_actions_addclose(actions, io->param1.pipe_fd[0]); + case IO_FILE: + { + if (! err) + err = posix_spawn_file_actions_addopen(actions, io->fd, io->filename_cstr, io->param2.flags /* mode */, OPEN_MASK); + break; } - 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: + { + 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) + 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) + err = posix_spawn_file_actions_addclose(actions, io->param1.pipe_fd[0]); + + } + break; + } } } /* Clean up on error */ - if (err) { + if (err) + { posix_spawnattr_destroy(attr); posix_spawn_file_actions_destroy(actions); } @@ -514,86 +518,86 @@ bool fork_actions_make_spawn_properties(posix_spawnattr_t *attr, posix_spawn_fil void safe_report_exec_error(int err, const char *actual_cmd, char **argv, char **envv) { - debug_safe( 0, "Failed to execute process '%s'. Reason:", actual_cmd ); + debug_safe(0, "Failed to execute process '%s'. Reason:", actual_cmd); - switch( err ) - { + switch (err) + { case E2BIG: { - char sz1[128], sz2[128]; + char sz1[128], sz2[128]; - long arg_max = -1; + long arg_max = -1; - size_t sz = 0; - char **p; - for(p=argv; *p; p++) - { - sz += strlen(*p)+1; - } + size_t sz = 0; + char **p; + for (p=argv; *p; p++) + { + sz += strlen(*p)+1; + } - for(p=envv; *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 ); + format_size_safe(sz1, sz); + 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); - } + 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; + 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); + /* 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; + 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); - } - break; + /* 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); + } + break; } case ENOMEM: { - debug_safe(0, "Out of memory"); - break; + debug_safe(0, "Out of memory"); + break; } default: { - /* Hope strerror doesn't allocate... */ - const char *err = strerror(errno); - debug_safe(0, "exec: %s", err); + /* 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); - break; + // 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 6b0857641..82c67e621 100644 --- a/postfork.h +++ b/postfork.h @@ -24,7 +24,7 @@ #endif #ifndef FISH_USE_POSIX_SPAWN - #define FISH_USE_POSIX_SPAWN HAVE_SPAWN_H +#define FISH_USE_POSIX_SPAWN HAVE_SPAWN_H #endif @@ -40,7 +40,7 @@ Returns 0 on sucess, -1 on failiure. */ -int set_child_group( job_t *j, process_t *p, int print_errors ); +int set_child_group(job_t *j, process_t *p, int print_errors); /** Initialize a new child process. This should be called right away @@ -59,7 +59,7 @@ int set_child_group( job_t *j, process_t *p, int print_errors ); signals are always unblocked. On failiure, signal handlers, io redirections and process group of the process is undefined. */ -int setup_child_process( job_t *j, process_t *p ); +int setup_child_process(job_t *j, process_t *p); /* Call fork(), optionally waiting until we are no longer multithreaded. If the forked child doesn't do anything that could allocate memory, take a lock, etc. (like call exec), then it's not necessary to wait for threads to die. If the forked child may do those things, it should wait for threads to die. */ diff --git a/print_help.cpp b/print_help.cpp index 3bb17835b..06bed30c2 100644 --- a/print_help.cpp +++ b/print_help.cpp @@ -17,18 +17,18 @@ ssize_t write_loop(int fd, const char *buff, size_t count); -void print_help( const char *c, int fd ) +void print_help(const char *c, int fd) { - char cmd[ CMD_LEN]; - int printed = snprintf( cmd, CMD_LEN, "fish -c '__fish_print_help %s >&%d'", c, 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) ) + if (printed < CMD_LEN) { - write_loop(2, HELP_ERR, strlen(HELP_ERR)); + if ((system(cmd) == -1)) + { + write_loop(2, HELP_ERR, strlen(HELP_ERR)); + } + } - } - } diff --git a/print_help.h b/print_help.h index 69f6578c6..005800b11 100644 --- a/print_help.h +++ b/print_help.h @@ -10,6 +10,6 @@ Print help message for the specified command */ -void print_help( const char *cmd, int fd ); +void print_help(const char *cmd, int fd); #endif diff --git a/proc.cpp b/proc.cpp index 7e6cb1a1f..9a1a0cdc7 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; @@ -106,12 +106,14 @@ void job_iterator_t::reset() this->end = job_list->end(); } -job_iterator_t::job_iterator_t(job_list_t &jobs) : job_list(&jobs) { +job_iterator_t::job_iterator_t(job_list_t &jobs) : job_list(&jobs) +{ this->reset(); } -job_iterator_t::job_iterator_t() : job_list(&parser_t::principal_parser().job_list()) { +job_iterator_t::job_iterator_t() : job_list(&parser_t::principal_parser().job_list()) +{ this->reset(); } @@ -129,17 +131,20 @@ static int is_interactive = -1; static bool proc_had_barrier = false; -int get_is_interactive(void) { +int get_is_interactive(void) +{ ASSERT_IS_MAIN_THREAD(); return is_interactive; } -bool get_proc_had_barrier() { +bool get_proc_had_barrier() +{ ASSERT_IS_MAIN_THREAD(); return proc_had_barrier; } -void set_proc_had_barrier(bool flag) { +void set_proc_had_barrier(bool flag) +{ ASSERT_IS_MAIN_THREAD(); proc_had_barrier = flag; } @@ -156,15 +161,15 @@ 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 ) +static int job_remove(job_t *j) { ASSERT_IS_MAIN_THREAD(); return parser_t::principal_parser().job_remove(j); @@ -181,9 +186,9 @@ void job_promote(job_t *job) Remove job from the job list and free all memory associated with it. */ -void job_free( job_t * j ) +void job_free(job_t * j) { - job_remove( j ); + job_remove(j); delete j; } @@ -191,22 +196,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 ) +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 +221,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,158 +243,159 @@ 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--) { + while (count--) + { if (consumed_job_ids.at(count)) break; } consumed_job_ids.resize(count + 1); } -job_t *job_get( job_id_t id ) +job_t *job_get(job_id_t id) { ASSERT_IS_MAIN_THREAD(); return parser_t::principal_parser().job_get(id); } -job_t *job_get_from_pid( int pid ) +job_t *job_get_from_pid(int pid) { ASSERT_IS_MAIN_THREAD(); return parser_t::principal_parser().job_get_from_pid(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 ) +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 ) +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 ) +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 ) +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; +int job_signal(job_t *j, int signal) +{ + 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. This is called from a signal handler. */ -static void mark_process_status( const job_t *j, - process_t *p, - int status ) +static void mark_process_status(const job_t *j, + 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; + 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 ) +void job_mark_process_as_failed(const job_t *job, process_t *p) { /* The given process failed to even lift off (e.g. posix_spawn failed) and so doesn't have a valid pid. Mark it as dead. */ p->completed = 1; @@ -403,99 +409,99 @@ void job_mark_process_as_failed( const job_t *job, process_t *p ) \param pid the pid of the process whose status changes \param status the status as returned by wait */ -static void handle_child_status( pid_t pid, int status ) +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, - 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; - } - } + 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; + } + } - 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, - MESS_SIZE, - "Process %d not found by %d\n", - (int) pid, (int)getpid() ); + } + } + } - write( 2, mess, strlen(mess )); -*/ - } - return; + 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; } process_t::process_t() : @@ -517,7 +523,7 @@ process_t::process_t() : #endif { } - + process_t::~process_t() { if (this->next != NULL) @@ -525,17 +531,17 @@ process_t::~process_t() } job_t::job_t(job_id_t jobid) : - command_str(), - command_narrow(), - first_process(NULL), - pgid(0), - tmodes(), - job_id(jobid), - io(), - flags(0) + command_str(), + command_narrow(), + first_process(NULL), + pgid(0), + tmodes(), + job_id(jobid), + io(), + flags(0) { } - + job_t::~job_t() { if (first_process != NULL) @@ -545,193 +551,193 @@ job_t::~job_t() } /* This is called from a signal handler */ -void job_handle_signal ( int signal, siginfo_t *info, void *con ) +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; + + got_signal = 1; // write( 2, "got signal\n", 11 ); - while(1) - { - switch(pid=waitpid( -1,&status,WUNTRACED|WNOHANG )) - { - case 0: - case -1: - { - errno=errno_old; - return; - } - default: + 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; + 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 */ -static void format_job_info( const job_t *j, const wchar_t *status ) +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 ) +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 ) +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; } @@ -745,85 +751,85 @@ int job_reap( bool interactive ) /** Get the CPU time for the specified process */ -unsigned long proc_get_jiffies( process_t *p ) +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; - } /** @@ -831,18 +837,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); + } + } } @@ -850,103 +856,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 ) +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]; + 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; - } + 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 ) +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. @@ -954,365 +960,365 @@ static void read_try( job_t *j ) a job that has previously been stopped. In that case, we need to set the terminal attributes to those saved in the job. */ -static int terminal_give_to_job( job_t *j, int cont ) +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) +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; - } - - /* 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 (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) +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 ) +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 2038f9699..3213f6f70 100644 --- a/proc.h +++ b/proc.h @@ -56,41 +56,41 @@ */ 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 @@ -127,7 +127,7 @@ enum */ class process_t { - private: +private: null_terminated_array_t argv_array; @@ -138,84 +138,95 @@ class process_t process_t(const process_t &rhs); void operator=(const process_t &rhs); - public: +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) { + 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; } + 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 *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 *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 { + 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 */ -enum { +enum +{ /** true if user was told about stopped job */ JOB_NOTIFIED = 1 << 0, @@ -223,9 +234,9 @@ enum { 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, @@ -261,12 +272,12 @@ 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; @@ -275,62 +286,75 @@ class job_t job_t(const job_t &rhs); void operator=(const job_t &); - public: +public: job_t(job_id_t jobid); ~job_t(); /** Returns whether the command is empty. */ - bool command_is_empty() const { return command_str.empty(); } + bool command_is_empty() const + { + return command_str.empty(); + } /** Returns the command as a wchar_t *. */ - const wchar_t *command_wcstr() const { return command_str.c_str(); } + const wchar_t *command_wcstr() const + { + return command_str.c_str(); + } /** Returns the command */ - const wcstring &command() const { return command_str; } + const wcstring &command() const + { + return command_str; + } /** Returns the command as a char *. */ - const char *command_cstr() const { return command_narrow.get(); } + const char *command_cstr() const + { + return command_narrow.get(); + } /** Sets the command */ - void set_command(const wcstring &cmd) { + 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; + /** + 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; + /** + 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 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; + /** + 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; + /** 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; + /** + Bitset containing information about the job. A combination of the JOB_* constants. + */ + unsigned int flags; }; /** @@ -371,16 +395,19 @@ bool job_list_is_empty(void); /** A class to aid iteration over jobs list. Note this is used from a signal handler, so it must be careful to not allocate memory. */ -class job_iterator_t { +class job_iterator_t +{ job_list_t * const job_list; job_list_t::iterator current, end; - public: +public: void reset(void); - job_t *next() { + job_t *next() + { job_t *job = NULL; - if (current != end) { + if (current != end) + { job = *current; ++current; } @@ -426,17 +453,17 @@ extern int no_exec; /** Add the specified flag to the bitset of flags for the specified job */ -void job_set_flag( job_t *j, int flag, int set ); +void job_set_flag(job_t *j, int flag, int set); /** Returns one if the specified flag is set in the specified job, 0 otherwise. */ -int job_get_flag( const job_t *j, int flag ); +int job_get_flag(const job_t *j, int flag); /** Sets the status of the last process to exit */ -void proc_set_last_status( int s ); +void proc_set_last_status(int s); /** Returns the status of the last process to exit @@ -446,7 +473,7 @@ int proc_get_last_status(); /** Remove the specified job */ -void job_free( job_t* j ); +void job_free(job_t* j); /** Promotes a job to the front of the job list. @@ -473,12 +500,12 @@ job_t *job_get_from_pid(int pid); /** Tests if the job is stopped */ -int job_is_stopped( const job_t *j ); +int job_is_stopped(const job_t *j); /** Tests if the job has completed, i.e. if the last process of the pipeline has ended. */ -int job_is_completed( const job_t *j ); +int job_is_completed(const job_t *j); /** Reassume a (possibly) stopped job. Put job j in the foreground. If @@ -488,7 +515,7 @@ int job_is_completed( const job_t *j ); \param j The job \param cont Whether the function should wait for the job to complete before returning */ -void job_continue( job_t *j, int cont ); +void job_continue(job_t *j, int cont); /** Notify the user about stopped or terminated jobs. Delete terminated @@ -496,21 +523,21 @@ void job_continue( job_t *j, int cont ); \param interactive whether interactive jobs should be reaped as well */ -int job_reap( bool interactive ); +int job_reap(bool interactive); /** Signal handler for SIGCHLD. Mark any processes with relevant information. */ -void job_handle_signal( int signal, siginfo_t *info, void *con ); +void job_handle_signal(int signal, siginfo_t *info, void *con); /** Send the specified signal to all processes in the specified job. */ -int job_signal( job_t *j, int signal ); +int job_signal(job_t *j, int signal); /* Marks a process as failed to execute (and therefore completed) */ -void job_mark_process_as_failed( const job_t *job, process_t *p ); +void job_mark_process_as_failed(const job_t *job, process_t *p); #ifdef HAVE__PROC_SELF_STAT /** @@ -518,7 +545,7 @@ void job_mark_process_as_failed( const job_t *job, process_t *p ); was used by this process. This function is only available on systems with the procfs file entry 'stat', i.e. Linux. */ -unsigned long proc_get_jiffies( process_t *p ); +unsigned long proc_get_jiffies(process_t *p); /** Update process time usage for all processes by calling the @@ -539,7 +566,7 @@ void proc_sanity_check(); Send a process/job exit event notification. This function is a conveniance wrapper around event_fire(). */ -void proc_fire_event( const wchar_t *msg, int type, pid_t pid, int status ); +void proc_fire_event(const wchar_t *msg, int type, pid_t pid, int status); /** Initializations @@ -555,7 +582,7 @@ void proc_destroy(); Set new value for is_interactive flag, saving previous value. If needed, update signal handlers. */ -void proc_push_interactive( int value ); +void proc_push_interactive(int value); /** Set is_interactive flag to the previous value. If needed, update diff --git a/reader.cpp b/reader.cpp index 921dd9870..71f49dd3d 100644 --- a/reader.cpp +++ b/reader.cpp @@ -193,11 +193,11 @@ typedef int color_t; */ class reader_data_t { - public: - - /** String containing the whole current commandline */ - wcstring command_line; - +public: + + /** String containing the whole current commandline */ + wcstring command_line; + /** String containing the autosuggestion */ wcstring autosuggestion; @@ -207,119 +207,122 @@ class reader_data_t /** When backspacing, we temporarily 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(); } - + 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() : allow_autosuggestion(0), @@ -337,8 +340,8 @@ class reader_data_t search_mode(0), repaint_needed(0), screen_reset_needed(0) - { - } + { + } }; /** @@ -383,7 +386,7 @@ static int interrupted=0; */ static struct termios saved_modes; -static void reader_super_highlight_me_plenty( size_t pos ); +static void reader_super_highlight_me_plenty(size_t pos); /** Variable to keep track of forced exits - see \c reader_exit_forced(); @@ -396,22 +399,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; + } } @@ -422,28 +425,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; } /** @@ -455,84 +458,85 @@ 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); - s_write( &data->screen, - data->left_prompt_buff, - data->right_prompt_buff, - full_line, - data->command_length(), - &colors[0], - &indents[0], - data->buff_pos); + s_write(&data->screen, + data->left_prompt_buff, + data->right_prompt_buff, + full_line, + data->command_length(), + &colors[0], + &indents[0], + data->buff_pos); - data->repaint_needed = false; + data->repaint_needed = false; } /** Internal helper function for handling killing parts of text. */ -static void reader_kill( size_t begin_idx, size_t length, int mode, int newv ) +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 ) +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() @@ -542,7 +546,7 @@ const wchar_t *reader_current_filename() } -void reader_push_current_filename( const wchar_t *fn ) +void reader_push_current_filename(const wchar_t *fn) { ASSERT_IS_MAIN_THREAD(); current_filename.push(intern(fn)); @@ -552,21 +556,22 @@ void reader_push_current_filename( const wchar_t *fn ) void reader_pop_current_filename() { ASSERT_IS_MAIN_THREAD(); - current_filename.pop(); + current_filename.pop(); } /** Make sure buffers are large enough to hold the current string length */ -void reader_data_t::command_line_changed() { +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++; } @@ -575,84 +580,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 || 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 ); - + proc_push_interactive(0); + if (data->left_prompt.size()) { wcstring_list_t prompt_list; - if( exec_subshell( data->left_prompt, prompt_list ) == 0 ) + if (exec_subshell(data->left_prompt, prompt_list) == 0) { - for( size_t i = 0; i < prompt_list.size(); i++ ) + for (size_t i = 0; i < prompt_list.size(); i++) { if (i > 0) data->left_prompt_buff += L'\n'; data->left_prompt_buff += prompt_list.at(i); } } } - + if (data->right_prompt.size()) { wcstring_list_t prompt_list; - if( exec_subshell( data->right_prompt, prompt_list ) == 0 ) + if (exec_subshell(data->right_prompt, prompt_list) == 0) { - for( size_t i = 0; i < prompt_list.size(); i++ ) + for (size_t i = 0; i < prompt_list.size(); i++) { // Right prompt does not support multiple lines, so just concatenate all of them data->right_prompt_buff += prompt_list.at(i); } } } - + 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; - #endif - +#ifdef VDSUSP + 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); } @@ -729,44 +734,50 @@ void reader_init() void reader_destroy() { - tcsetattr(0, TCSANOW, &saved_modes); + tcsetattr(0, TCSANOW, &saved_modes); } -void reader_exit( int do_exit, int forced ) +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() { - if (data && data->screen_reset_needed) { - s_reset( &data->screen, false); +void reader_repaint_if_needed() +{ + if (data && data->screen_reset_needed) + { + s_reset(&data->screen, false); data->screen_reset_needed = false; } - if (data && data->repaint_needed) { + if (data && data->repaint_needed) + { reader_repaint(); /* reader_repaint clears repaint_needed */ } } -void reader_react_to_color_change() { - if (data) { +void reader_react_to_color_change() +{ + if (data) + { data->repaint_needed = true; data->screen_reset_needed = true; - } + } } @@ -777,22 +788,24 @@ 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 { + do + { data->buff_pos -= 1; width = fish_wcwidth(data->command_line.at(data->buff_pos)); - data->command_line.erase(data->buff_pos, 1); - } while (width == 0 && data->buff_pos > 0); + 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(); } @@ -806,18 +819,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; } @@ -825,32 +838,32 @@ static int insert_string(const wcstring &str) Insert the character into the command line buffer and print it to the screen using syntax highlighting, etc. */ -static int insert_char( wchar_t c ) +static int insert_char(wchar_t c) { - return insert_string(wcstring(&c, 1)); + return insert_string(wcstring(&c, 1)); } /** Calculate the length of the common prefix substring of two strings. */ -static size_t comp_len( const wchar_t *a, const wchar_t *b ) +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; } /** Calculate the case insensitive length of the common prefix substring of two strings. */ -static size_t comp_ilen( 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; } @@ -867,71 +880,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)); @@ -940,7 +953,7 @@ static wcstring completion_apply_to_command_line(const wcstring &val_str, int fl } *inout_cursor_pos = new_cursor_pos; return result; - } + } } /** @@ -952,12 +965,12 @@ static wcstring completion_apply_to_command_line(const wcstring &val_str, int fl \param flags A union of all flags describing the completion to insert. See the completion_t struct for more information on possible values. */ -static void completion_insert( const wchar_t *val, int flags ) +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; } @@ -967,82 +980,82 @@ 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 */ -static void run_pager( const wcstring &prefix, int is_quoted, const std::vector &comp ) +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() ); - + 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 ); + + 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, ESCAPE_ALL | ESCAPE_NO_QUOTED ); + + 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); + } + + + 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. */ @@ -1056,47 +1069,48 @@ 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 { +struct autosuggestion_context_t +{ wcstring search_string; wcstring autosuggestion; size_t cursor_pos; @@ -1106,10 +1120,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), @@ -1121,43 +1135,49 @@ struct autosuggestion_context_t { has_tried_reloading(false) { } - + /* The function run in the background thread to determine an autosuggestion */ - int threaded_autosuggest(void) { + 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) { + if (generation_count != s_generation_count) + { return 0; } - + /* Let's make sure we aren't using the empty string */ - if (search_string.empty()) { + if (search_string.empty()) + { return 0; } - - while (searcher.go_backwards()) { + + 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)) { + + 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(); return 1; } } - + /* Try handling a special command like cd */ wcstring special_suggestion; - if (autosuggest_suggest_special(search_string, working_directory, 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 @@ -1174,35 +1194,39 @@ struct autosuggestion_context_t { /* Try normal completions */ std::vector completions; complete(search_string, completions, COMPLETE_AUTOSUGGEST, &this->commands_to_load); - if (! completions.empty()) { + if (! completions.empty()) + { const completion_t &comp = completions.at(0); size_t cursor = this->cursor_pos; this->autosuggestion = completion_apply_to_command_line(comp.completion.c_str(), comp.flags, this->search_string, &cursor); return 1; } - + return 0; } }; -static int threaded_autosuggest(autosuggestion_context_t *ctx) { +static int threaded_autosuggest(autosuggestion_context_t *ctx) +{ return ctx->threaded_autosuggest(); } -static bool can_autosuggest(void) { +static bool can_autosuggest(void) +{ /* We autosuggest if suppress_autosuggestion is not set, if we're not doing a history search, and our command line contains a non-whitespace character. */ const wchar_t *whitespace = L" \t\r\n\v"; return ! data->suppress_autosuggestion && - data->history_search.is_at_end() && - data->command_line.find_first_not_of(whitespace) != wcstring::npos; + data->history_search.is_at_end() && + data->command_line.find_first_not_of(whitespace) != wcstring::npos; } -static void autosuggest_completed(autosuggestion_context_t *ctx, int result) { +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) { @@ -1214,11 +1238,12 @@ 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 && - string_prefixes_string_case_insensitive(ctx->search_string, ctx->autosuggestion)) { + can_autosuggest() && + ctx->search_string == data->command_line && + string_prefixes_string_case_insensitive(ctx->search_string, ctx->autosuggestion)) + { /* Autosuggestion is active and the search term has not changed, so we're good to go */ data->autosuggestion = ctx->autosuggestion; sanity_check(); @@ -1228,29 +1253,35 @@ static void autosuggest_completed(autosuggestion_context_t *ctx, int result) { } -static void update_autosuggestion(void) { +static void update_autosuggestion(void) +{ /* Updates autosuggestion. We look for an autosuggestion if the command line is non-empty and if we're not doing a history search. */ #if 0 /* Old non-threaded mode */ data->autosuggestion.clear(); - if (can_autosuggest()) { + if (can_autosuggest()) + { history_search_t searcher = history_search_t(*data->history, data->command_line, HISTORY_SEARCH_TYPE_PREFIX); - if (searcher.go_backwards()) { + if (searcher.go_backwards()) + { data->autosuggestion = searcher.current_item().str(); } } #else data->autosuggestion.clear(); - if (data->allow_autosuggestion && ! data->suppress_autosuggestion && ! data->command_line.empty() && data->history_search.is_at_end()) { + if (data->allow_autosuggestion && ! data->suppress_autosuggestion && ! data->command_line.empty() && data->history_search.is_at_end()) + { autosuggestion_context_t *ctx = new autosuggestion_context_t(data->history, data->command_line, data->buff_pos); iothread_perform(threaded_autosuggest, autosuggest_completed, ctx); } #endif } -static void accept_autosuggestion(void) { +static void accept_autosuggestion(void) +{ /* Accept any autosuggestion by replacing the command line with it. */ - if (! data->autosuggestion.empty()) { + if (! data->autosuggestion.empty()) + { /* Accept the autosuggestion */ data->command_line = data->autosuggestion; data->buff_pos = data->command_line.size(); @@ -1267,22 +1298,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(); } /** @@ -1300,26 +1331,26 @@ static void reader_flash() exact replacement, e.g. if the COMPLETE_DONT_ESCAPE flag is set. */ -static bool reader_can_replace( const wcstring &in, int flags ) +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) */ @@ -1354,14 +1385,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)) { @@ -1370,14 +1401,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 @@ -1386,199 +1417,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 ) +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; - success = false; - 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 ); + /* + Check trivial cases + */ + switch (comp.size()) + { + /* No suitable completions found, flash screen and return */ + case 0: + { + reader_flash(); + done = true; + success = false; + break; + } - /* 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; - } - } + /* Exactly one suitable completion found - insert it */ + case 1: + { - /* If we found something to insert, do it. */ - if( len > 0 ) - { - if( count > 1 ) - flags = flags | COMPLETE_NO_SPACE; + const completion_t &c = comp.at(0); - 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( 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 ) + /* + 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)) { - 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); + 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; + } } - { - int is_quoted; + /* If we found something to insert, do it. */ + if (len > 0) + { + if (count > 1) + flags = flags | COMPLETE_NO_SPACE; - 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(); + 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 (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; } @@ -1587,102 +1618,102 @@ 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(); - - } + + } } /** Set the specified string from the history as the current buffer. Do not modify prefix_width. */ -static void set_command_line_and_position( const wcstring &new_str, size_t pos ) +static void set_command_line_and_position(const wcstring &new_str, size_t pos) { data->command_line = new_str; data->command_line_changed(); data->buff_pos = pos; - reader_super_highlight_me_plenty( data->buff_pos ); + reader_super_highlight_me_plenty(data->buff_pos); reader_repaint(); } -void reader_replace_current_token( const wchar_t *new_token ) +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); } @@ -1757,21 +1788,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,174 +1813,177 @@ static void reset_token_history() \param forward if the search should be forward or reverse \param reset whether the current token should be made the new search token */ -static void handle_token_history( int forward, int reset ) +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 - */ - if (data->history_search.go_backwards()) { + + /* + 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 )); - } + 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)); + } - } - } - } - } + } + } + } + } - 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. */ class move_word_state_machine_t { - enum { + enum + { s_whitespace, s_punctuation, s_alphanumeric_or_punctuation_except_slash, s_end } state; - - public: - + +public: + move_word_state_machine_t() : state(s_whitespace) { } - + bool consume_char(wchar_t c) { /* Note fall-through in all of these! */ switch (state) { - case s_whitespace: - 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; + case s_whitespace: + 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; } } }; @@ -1962,44 +1996,45 @@ 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 */ -enum move_word_dir_t { +enum move_word_dir_t +{ MOVE_DIR_LEFT, MOVE_DIR_RIGHT }; -static void move_word( bool move_right, bool erase, bool newv ) +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); @@ -2012,164 +2047,178 @@ 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; -} - -void reader_set_buffer( const wcstring &b, size_t pos ) +history_t *reader_get_history(void) { - if( !data ) - return; + ASSERT_IS_MAIN_THREAD(); + return data ? data->history : NULL; +} + +void reader_set_buffer(const wcstring &b, size_t pos) +{ + 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 ) +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 } -int reader_shell_test( const wchar_t *b ) +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, - empty, - 0, - tmp, - tmp2, - 0); - - - parser_t::principal_parser().test( b, 0, &sb, L"fish" ); - fwprintf( stderr, L"%ls", sb.c_str() ); - } - return res; + + s_write(&data->screen, + empty, + empty, + empty, + 0, + tmp, + tmp2, + 0); + + + parser_t::principal_parser().test(b, 0, &sb, L"fish"); + fwprintf(stderr, L"%ls", sb.c_str()); + } + return res; } /** @@ -2177,63 +2226,63 @@ int reader_shell_test( const wchar_t *b ) detection for general purpose, there are no invalid strings, so this function always returns false. */ -static int default_test( const wchar_t *b ) +static int default_test(const wchar_t *b) { - return 0; + return 0; } -void reader_push( const wchar_t *name ) +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 ) +void reader_set_left_prompt(const wcstring &new_prompt) { data->left_prompt = new_prompt; } @@ -2248,19 +2297,19 @@ void reader_set_allow_autosuggesting(bool flag) data->allow_autosuggestion = flag; } -void reader_set_complete_function( complete_function_t f ) +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 ) +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 * ) ) +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) @@ -2283,29 +2332,30 @@ void reader_import_history_if_necessary(void) } /** A class as the context pointer for a background (threaded) highlight operation. */ -class background_highlight_context_t { +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), @@ -2316,8 +2366,9 @@ public: generation_count(s_generation_count) { } - - int threaded_highlight() { + + int threaded_highlight() + { if (generation_count != s_generation_count) { // The gen count has changed, so don't do anything @@ -2325,53 +2376,57 @@ public: } if (! string_to_highlight.empty()) { - highlight_function( string_to_highlight, colors, match_highlight_pos, NULL /* error */, vars); + highlight_function(string_to_highlight, colors, match_highlight_pos, NULL /* error */, vars); } return 0; } }; /* 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()) - { +static void highlight_search(void) +{ + 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) { +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; } -static int threaded_highlight(background_highlight_context_t *ctx) { +static int threaded_highlight(background_highlight_context_t *ctx) +{ return ctx->threaded_highlight(); } @@ -2385,19 +2440,22 @@ static int threaded_highlight(background_highlight_context_t *ctx) { \param match_highlight_pos the position to use for bracket matching. This need not be the same as the surrent cursor position \param error if non-null, any possible errors in the buffer are further descibed by the strings inserted into the specified arraylist */ -static void reader_super_highlight_me_plenty( size_t match_highlight_pos ) +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)) { + if (can_autosuggest() && ! suggest.empty() && string_prefixes_string_case_insensitive(cmd, suggest)) + { /* The autosuggestion is still reasonable, so do nothing */ - } else { + } + else + { update_autosuggestion(); } } @@ -2405,10 +2463,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; } /** @@ -2419,63 +2477,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); + } + } + } + } } /** @@ -2484,82 +2542,82 @@ 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_set_allow_autosuggesting(true); - reader_import_history_if_necessary(); - - parser_t &parser = parser_t::principal_parser(); - - data->prev_end_loop=0; + reader_push(L"fish"); + reader_set_complete_function(&complete); + reader_set_highlight_function(&highlight_shell); + reader_set_test_function(&reader_shell_test); + reader_set_allow_autosuggesting(true); + reader_import_history_if_necessary(); - 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; } /** Test if there are bytes available for reading on the specified file descriptor */ -static int can_read( int fd ) +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; } @@ -2568,746 +2626,755 @@ static int can_read( int fd ) Test if the specified character is in the private use area that fish uses to store internal characters */ -static int wchar_private( wchar_t c ) +static int wchar_private(wchar_t c) { - return ( (c >= 0xe000) && (c <= 0xf8ff ) ); + return ((c >= 0xe000) && (c <= 0xf8ff)); } /** Test if the specified character in the specified string is backslashed. */ -static bool is_backslashed( const wchar_t *str, size_t pos ) +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; - const wchar_t *buff = data->command_line.c_str(); - switch( c ) - { - - /* go to beginning of line*/ - 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] && - buff[data->buff_pos] != L'\n' ) - { - 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; - } - - /* complete */ - case R_COMPLETE: - { - - if( !data->complete_func ) - break; - - if (! comp_empty && last_char == R_COMPLETE) + if (((!wchar_private(c))) && (c>31) && (c != 127)) + { + if (can_read(0)) { - /* The user typed R_COMPLETE more than once in a row. Cycle through our available completions */ - const completion_t *next_comp = cycle_competions(comp, cycle_command_line, &completion_cycle_idx); - if (next_comp != NULL) + + wchar_t arr[READAHEAD_MAX+1]; + int i; + + memset(arr, 0, sizeof(arr)); + arr[0] = c; + + for (i=1; icompletion, 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 - { - /* 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 *buff = data->command_line.c_str(); - 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); - 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; - } - - /* kill */ - 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 *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 *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; - } - - /* yank*/ - 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(); - 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; - } - - /* delete backward*/ - case R_BACKWARD_DELETE_CHAR: - { - remove_backward(); - break; - } - - /* delete forward*/ - 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; - } - - /* - Evaluate. If the current command is unfinished, or if - the charater is escaped using a backslash, insert a - newline - */ - 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: - { - /* - Finished commend, execute it - */ - 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; - } - - /* - We are incomplete, continue editing - */ - 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; - } - - /* 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 ) || - ( c == R_HISTORY_SEARCH_FORWARD ) ) - { - 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 ) || - ( c == R_HISTORY_TOKEN_SEARCH_BACKWARD ) ) - { - 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(); + + if (!can_read(0)) + { + c = 0; + break; } - 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; - } - - - /* Move left*/ - 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 { - accept_autosuggestion(); + c = input_readch(); + if ((!wchar_private(c)) && (c>31) && (c != 127)) + { + arr[i]=c; + c=0; + } + else + break; + } + + insert_string(arr); + } - break; - } - - /* kill one word left */ - 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; - } - - /* move one word left*/ - 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: - { - 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()); + } + + 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) + { + + /* go to beginning of line*/ + 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] && + buff[data->buff_pos] != L'\n') + { + 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; + } + + /* complete */ + 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 */ + const completion_t *next_comp = cycle_competions(comp, cycle_command_line, &completion_cycle_idx); + if (next_comp != NULL) + { + 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; } - - 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)) ) - { - /* Regular character */ - 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) && - (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" ); + } + 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 *buff = data->command_line.c_str(); + 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); + 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; + } + + /* kill */ + 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 *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 *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; + } + + /* yank*/ + 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(); + 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; + } + + /* delete backward*/ + case R_BACKWARD_DELETE_CHAR: + { + remove_backward(); + break; + } + + /* delete forward*/ + 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; + } + + /* + Evaluate. If the current command is unfinished, or if + the charater is escaped using a backslash, insert a + newline + */ + 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: + { + /* + Finished commend, execute it + */ + 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; + } + + /* + We are incomplete, continue editing + */ + 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; + } + + /* 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) || + (c == R_HISTORY_SEARCH_FORWARD)) + { + 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) || + (c == R_HISTORY_TOKEN_SEARCH_BACKWARD)) + { + 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) || + (c == R_HISTORY_TOKEN_SEARCH_BACKWARD)) + { + 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; + } + + /* Move right*/ + case R_FORWARD_CHAR: + { + if (data->buff_pos < data->command_length()) + { + data->buff_pos++; + reader_repaint(); + } + else + { + accept_autosuggestion(); + } + break; + } + + /* kill one word left */ + 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; + } + + /* move one word left*/ + 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: + { + 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; + } + + /* Other, if a normal character, we add it to the command */ + default: + { + + if ((!wchar_private(c)) && (((c>31) || (c==L'\n'))&& (c != 127))) + { + /* Regular character */ + 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) && + (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"); /* 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; } @@ -3316,126 +3383,126 @@ int reader_search_mode() the prompt, using syntax highlighting. This is used for reading scripts and init files. */ -static int read_ni( int fd, const io_chain_t &io ) +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 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 8a7ed5d40..923f07d8c 100644 --- a/reader.h +++ b/reader.h @@ -1,4 +1,4 @@ -/** \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, @@ -24,12 +24,12 @@ class history_t; /** Read commands from \c fd until encountering EOF */ -int reader_read( int fd, const io_chain_t &io); +int reader_read(int fd, const io_chain_t &io); /** Tell the shell that it should exit after the currently running command finishes. */ -void reader_exit( int do_exit, int force ); +void reader_exit(int do_exit, int force); /** Check that the reader is in a sane state @@ -53,10 +53,10 @@ 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 ); +void reader_push_current_filename(const wchar_t *fn); /** Pop the current filename from the stack of read files */ @@ -85,7 +85,7 @@ void reader_repaint_if_needed(); Run the specified command with the correct terminal modes, and while taking care to perform job notification, set the title, etc. */ -void reader_run_command( const wchar_t *buff ); +void reader_run_command(const wchar_t *buff); /** Get the string of character currently entered into the command @@ -103,7 +103,7 @@ history_t *reader_get_history(void); \param p the cursor position. If \c p is larger than the length of the command line, the cursor is placed on the last character. */ -void reader_set_buffer( const wcstring &b, size_t p ); +void reader_set_buffer(const wcstring &b, size_t p); /** Get the current cursor position in the command line. If interactive @@ -125,9 +125,9 @@ 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 ); +void reader_push(const wchar_t *name); /** Return to previous reader environment @@ -135,66 +135,66 @@ 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. */ -typedef void (*complete_function_t)( const wcstring &, std::vector &, complete_type_t, wcstring_list_t * lst ); -void reader_set_complete_function( complete_function_t ); +typedef void (*complete_function_t)(const wcstring &, std::vector &, complete_type_t, wcstring_list_t * lst); +void reader_set_complete_function(complete_function_t); /** The type of a highlight function. */ class env_vars_snapshot_t; -typedef void (*highlight_function_t)( const wcstring &, std::vector &, size_t, wcstring_list_t *, const env_vars_snapshot_t &vars ); +typedef void (*highlight_function_t)(const wcstring &, std::vector &, size_t, wcstring_list_t *, const env_vars_snapshot_t &vars); /** 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 - An array_list_t used for storing error messages */ -void reader_set_highlight_function( highlight_function_t ); +void reader_set_highlight_function(highlight_function_t); /** Specify function for testing if the command buffer contains syntax errors that must be corrected before returning. */ -void reader_set_test_function( int (*f)( const wchar_t * ) ); +void reader_set_test_function(int (*f)(const wchar_t *)); /** Specify string of shell commands to be run in order to generate the prompt. */ -void reader_set_left_prompt( const wcstring &prompt ); +void reader_set_left_prompt(const wcstring &prompt); /** Specify string of shell commands to be run in order to generate the right prompt. */ -void reader_set_right_prompt( const wcstring &prompt ); +void reader_set_right_prompt(const wcstring &prompt); /** Sets whether autosuggesting is allowed. */ void reader_set_allow_autosuggesting(bool flag); /** - Returns true if the shell is exiting, 0 otherwise. + Returns true if the shell is exiting, 0 otherwise. */ int exit_status(); /** Replace the current token with the specified string */ -void reader_replace_current_token( const wchar_t *new_token ); +void reader_replace_current_token(const wchar_t *new_token); /** The readers interrupt signal handler. Cancels all currently running blocks. */ -void reader_handle_int( int signal ); +void reader_handle_int(int signal); /** This function returns true if fish is exiting by force, i.e. because stdin died @@ -205,7 +205,7 @@ int reader_exit_forced(); Test if the given shell command contains errors. Uses parser_test for testing. Suitable for reader_set_test_function(). */ -int reader_shell_test( const wchar_t *b ); +int reader_shell_test(const wchar_t *b); /** Test whether the interactive reader is in search mode. diff --git a/sanity.cpp b/sanity.cpp index cbce3c82a..f89769414 100644 --- a/sanity.cpp +++ b/sanity.cpp @@ -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(); + 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; + return insane; } -void validate_pointer( const void *ptr, const wchar_t *err, int null_ok ) +void validate_pointer(const void *ptr, const wchar_t *err, int null_ok) { - /* - Test if the pointer data crosses a segment boundary. - */ + /* + Test if the pointer data crosses a segment boundary. + */ - if( (0x00000003l & (intptr_t)ptr) != 0 ) - { - debug( 0, _(L"The pointer '%ls' is invalid"), err ); - sanity_lose(); - } + 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(); - } + 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 40b92dec2..6d8e293ae 100644 --- a/sanity.h +++ b/sanity.h @@ -24,6 +24,6 @@ int sanity_check(); \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 */ -void validate_pointer( const void *ptr, const wchar_t *err, int null_ok ); +void validate_pointer(const void *ptr, const wchar_t *err, int null_ok); #endif diff --git a/screen.cpp b/screen.cpp index 80865c3f4..f0848fe1e 100644 --- a/screen.cpp +++ b/screen.cpp @@ -72,14 +72,15 @@ static void invalidate_soft_wrap(screen_t *scr); typedef std::vector data_buffer_t; static data_buffer_t *s_writeb_buffer=0; -static int s_writeb( char c ); +static int s_writeb(char c); /* Class to temporarily set s_writeb_buffer and the writer function in a scoped way */ -class scoped_buffer_t { +class scoped_buffer_t +{ data_buffer_t * const old_buff; int (* const old_writer)(char); - public: +public: scoped_buffer_t(data_buffer_t *buff) : old_buff(s_writeb_buffer), old_writer(output_get_writer()) { s_writeb_buffer = buff; @@ -98,33 +99,33 @@ class scoped_buffer_t { specified position of the specified wide character string. All of \c seq must match, but str may be longer than seq. */ -static int try_sequence( const char *seq, const wchar_t *str ) +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; } /** Returns the number of columns left until the next tab stop, given the current cursor postion. */ -static size_t next_tab_stop( size_t in ) +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 @@ -162,41 +163,41 @@ static bool allow_soft_wrap(void) to detect common escape sequences that may be embeded in a prompt, such as color codes. */ -static size_t calc_prompt_width_and_lines( const wchar_t *prompt, size_t *out_prompt_lines ) +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++) { - /* - This is the start of an escape code. Try to guess it's width. - */ - size_t p; - int len=0; - bool found = false; + 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; - /* - Detect these terminfo color escapes with parameter - value 0..7, all of which don't move the cursor - */ - char * const esc[] = + /* + Detect these terminfo color escapes with parameter + value 0..7, all of which don't move the cursor + */ + 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[] = + /* + Detect these semi-common terminfo escapes without any + parameter values, all of which don't move the cursor + */ + char * const esc2[] = { enter_bold_mode, exit_attribute_mode, @@ -219,104 +220,106 @@ 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 (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 (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) { + if (! found) + { len = is_term256_escape(&prompt[j]); - if (len) { + if (len) + { j += (len - 1); found = true; } } - 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] ), - 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 ) + for (p=0; p < (sizeof(esc2)/sizeof(char *)) && !found; p++) { - /* - You'd thing this should be - '(end-prompt)+2', in order to move j - past the end of the string, but there is - a 'j++' at the end of each lap, so j - should always point to the last menged - character, e.g. +1. - */ - j = (end-prompt)+1; - } - else - { - break; - } - } - } - } + 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]), + try_sequence(esc2[p], &prompt[j])); - } - else if( prompt[j] == L'\t' ) - { - res = next_tab_stop( res ); - } - else if( prompt[j] == L'\n' ) - { - res = 0; + 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 + a 'j++' at the end of each lap, so j + 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; *out_prompt_lines += 1; + } + else + { + /* + Ordinary decent character. Just add width. + */ + res += fish_wcwidth(prompt[j]); + } } - else - { - /* - Ordinary decent character. Just add width. - */ - res += fish_wcwidth( prompt[j] ); - } - } - return res; + return res; } static size_t calc_prompt_width(const wchar_t *prompt) @@ -345,9 +348,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; } /** @@ -356,44 +359,44 @@ static int room_for_usec(struct stat *st) This should be done before calling a function that may cause output. */ -static void s_save_status( screen_t *s) +static void s_save_status(screen_t *s) { // PCA Let's not do this futimes stuff, because sudo dumbly uses the // 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[]= + /* + 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 - } + { + 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); } /** @@ -404,39 +407,39 @@ static void s_save_status( screen_t *s) false positives, at least under Linux. */ -static void s_check_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. - */ + 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; - } + int prev_line = s->actual.cursor.y; + write_loop(1, "\r", 1); + s_reset(s, false); + s->actual.cursor.y = prev_line; + } } /** @@ -444,89 +447,89 @@ static void s_check_status( screen_t *s) on. This function automatically handles linebreaks and lines longer than the screen width. */ -static void s_desired_append_char( screen_t *s, +static void s_desired_append_char(screen_t *s, wchar_t b, int c, int indent, - size_t prompt_width ) + size_t prompt_width) { - int line_no = s->desired.cursor.y; + int line_no = s->desired.cursor.y; - switch( b ) - { + 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; + 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': { - line_t ¤t = s->desired.line(line_no); - current.clear(); - s->desired.cursor.x = 0; - break; + 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); + int screen_width = common_get_width(); + int cw = fish_wcwidth(b); - s->desired.create_line(line_no); + 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 ) - { - /* 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); + /* + 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) + { + /* 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; - 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; - - /* 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) + line_no = (int)s->desired.line_count(); + s->desired.add_line(); + s->desired.cursor.y++; + s->desired.cursor.x=0; + for (size_t i=0; i < prompt_width; i++) { - line.is_soft_wrapped = true; - s->desired.cursor.x = 0; - s->desired.cursor.y++; + s_desired_append_char(s, L' ', 0, indent, prompt_width); } - break; + } + + line_t &line = s->desired.line(line_no); + line.append(b, c); + 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) + { + line.is_soft_wrapped = true; + s->desired.cursor.x = 0; + s->desired.cursor.y++; + } + break; + } } - } } /** The writeb function offered to tputs. */ -static int s_writeb( char c ) +static int s_writeb(char c) { s_writeb_buffer->push_back(c); - return 0; + return 0; } /** @@ -539,100 +542,100 @@ static int s_writeb( char c ) \param new_x the new x position \param new_y the new y position */ -static void s_move( screen_t *s, data_buffer_t *b, int new_x, int new_y ) +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; + 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], - new_x, new_y ); -*/ + char *str; + /* + 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; + 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.x; - - if( x_steps && new_x == 0 ) - { - b->push_back('\r'); - x_steps = 0; - } - - if( x_steps < 0 ) + if (y_steps > 0 && (strcmp(cursor_down, "\n")==0)) { - str = cursor_left; - } - else - { - str = cursor_right; - } + /* + 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; + } - for( i=0; iactual.cursor.x = new_x; - s->actual.cursor.y = new_y; + 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) + { + str = cursor_left; + } + else + { + str = cursor_right; + } + + for (i=0; iactual.cursor.x = new_x; + s->actual.cursor.y = new_y; } /** Set the pen color for the terminal */ -static void s_set_color( screen_t *s, data_buffer_t *b, int c ) +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)); } /** Convert a wide character to a multibyte string and append it to the buffer. */ -static void s_write_char( screen_t *s, data_buffer_t *b, wchar_t 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; @@ -652,20 +655,20 @@ static void s_write_char( screen_t *s, data_buffer_t *b, wchar_t c ) Send the specified string through tputs and append the output to the specified buffer. */ -static void s_write_mbs( data_buffer_t *b, char *s ) +static void s_write_mbs(data_buffer_t *b, char *s) { scoped_buffer_t scoped_buffer(b); - writembs( s ); + writembs(s); } /** Convert a wide string to a multibyte string and append it to the buffer. */ -static void s_write_str( data_buffer_t *b, const wchar_t *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. @@ -715,30 +718,30 @@ static void invalidate_soft_wrap(screen_t *scr) /** Update the screen to match the desired output. */ -static void s_update( screen_t *scr, const wchar_t *left_prompt, const wchar_t *right_prompt ) +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 right_prompt_width = calc_prompt_width( right_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; + data_buffer_t output; - scr->need_clear = false; + 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 */); - } + 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()); @@ -748,19 +751,19 @@ static void s_update( screen_t *scr, const wchar_t *left_prompt, const wchar_t * 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 */ @@ -769,7 +772,8 @@ static void s_update( screen_t *scr, const wchar_t *left_prompt, const wchar_t * /* Note that skip_remaining is a width, not a character count */ size_t skip_remaining = start_pos; - if (! should_clear_screen_this_line) { + 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); if (shared_prefix > 0) @@ -786,7 +790,7 @@ static void s_update( screen_t *scr, const wchar_t *left_prompt, const wchar_t * /* Skip over skip_remaining width worth of characters */ size_t j = 0; - for ( ; j < o_line.size(); j++) + for (; j < o_line.size(); j++) { int width = fish_wcwidth(o_line.char_at(j)); if (skip_remaining < width) @@ -796,7 +800,7 @@ static void s_update( screen_t *scr, const wchar_t *left_prompt, const wchar_t * } /* Skip over zero-width characters (e.g. combining marks at the end of the prompt) */ - for ( ; j < o_line.size(); j++) + for (; j < o_line.size(); j++) { int width = fish_wcwidth(o_line.char_at(j)); if (width > 0) @@ -804,19 +808,20 @@ static void s_update( screen_t *scr, const wchar_t *left_prompt, const wchar_t * } /* 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 ); + 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++) + for (; j < o_line.size(); j++) { perform_any_impending_soft_wrap(scr, current_width, (int)i); - s_move( scr, &output, current_width, (int)i ); - s_set_color( scr, &output, o_line.color_at(j) ); - s_write_char( scr, &output, o_line.char_at(j) ); + s_move(scr, &output, current_width, (int)i); + s_set_color(scr, &output, o_line.color_at(j)); + s_write_char(scr, &output, o_line.char_at(j)); current_width += fish_wcwidth(o_line.char_at(j)); } @@ -842,42 +847,42 @@ static void s_update( screen_t *scr, const wchar_t *left_prompt, const wchar_t * } 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) { - s_move( scr, &output, (int)(screen_width - right_prompt_width), (int)i ); - s_set_color( scr, &output, 0xffffffff); - s_write_str( &output, right_prompt ); + s_move(scr, &output, (int)(screen_width - right_prompt_width), (int)i); + s_set_color(scr, &output, 0xffffffff); + s_write_str(&output, right_prompt); /* We output in the last column. Some terms (Linux) push the cursor further right, past the window. Others make it "stick." Since we don't really know which is which, issue a cr so it goes back to the left. */ - s_write_str( &output, L"\r"); + 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) { /* Clear remaining lines individually */ - for( size_t i=scr->desired.line_count(); i < lines_with_stuff; i++ ) + for (size_t i=scr->desired.line_count(); i < lines_with_stuff; i++) { - s_move( scr, &output, 0, (int)i ); - s_write_mbs( &output, clr_eol); + s_move(scr, &output, 0, (int)i); + s_write_mbs(&output, clr_eol); } } - s_move( scr, &output, scr->desired.cursor.x, scr->desired.cursor.y ); - s_set_color( scr, &output, 0xffffffff); + 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() ); - } + 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; @@ -887,10 +892,11 @@ 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 { +struct screen_layout_t +{ /* The left prompt that we're going to use */ wcstring left_prompt; @@ -922,12 +928,12 @@ 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, - const wcstring &right_prompt_str, - const wcstring &commandline, - const wcstring &autosuggestion_str, - const int *indent) + size_t screen_width, + const wcstring &left_prompt_str, + const wcstring &right_prompt_str, + const wcstring &commandline, + const wcstring &autosuggestion_str, + const int *indent) { screen_layout_t result = {}; @@ -936,8 +942,8 @@ static screen_layout_t compute_layout(screen_t *s, 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 right_prompt_width = calc_prompt_width( right_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) { @@ -961,20 +967,20 @@ static screen_layout_t compute_layout(screen_t *s, 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') { + wchar_t c = commandline.at(i); + 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 */ @@ -1054,67 +1060,67 @@ static screen_layout_t compute_layout(screen_t *s, } -void s_write( screen_t *s, - const wcstring &left_prompt, - const wcstring &right_prompt, - const wcstring &commandline, - size_t explicit_len, - const int *colors, - const int *indent, - size_t cursor_pos ) +void s_write(screen_t *s, + const wcstring &left_prompt, + const wcstring &right_prompt, + const wcstring &commandline, + size_t explicit_len, + const int *colors, + 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 ); + /* + 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() ); + 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; - } + return; + } - s_check_status( s ); + 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 ); - } + 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. */ + /* 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; } @@ -1123,132 +1129,132 @@ void s_write( screen_t *s, /* Output the command line */ size_t i; - for( i=0; i < effective_commandline.size(); i++ ) - { - int color = colors[i]; - - if( i == cursor_pos ) + for (i=0; i < effective_commandline.size(); i++) { - color = 0; - } + int color = colors[i]; - if( i == cursor_pos ) - { + 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 *right_prompt, - const wchar_t *commandline, - size_t explicit_len, - const int *c, - const int *indent, - size_t cursor_pos ) +void s_write_OLD(screen_t *s, + 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) { - 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, ); + 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 ); + /* + 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 ) ); + 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 ); + free(prompt_narrow); + free(buffer_narrow); - return; - } + 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(); + 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(); - 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 - */ - 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; - newline_count++; } - else + + /* + 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; + newline_count++; + } + 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; @@ -1260,63 +1266,65 @@ 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 ) + for (i=0; commandline[i]; i++) { - col = 0; - } + int col = c[i]; - if( i == cursor_pos ) - { + 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 ) +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) { + 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 (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); @@ -1329,19 +1337,19 @@ void s_reset( screen_t *s, bool reset_cursor, bool reset_prompt ) s->actual.resize(0); s->actual.cursor.x = 0; s->actual.cursor.y = 0; - s->need_clear=true; + 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 ); + 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); } screen_t::screen_t() : diff --git a/screen.h b/screen.h index 2c36f11aa..2dd191935 100644 --- a/screen.h +++ b/screen.h @@ -63,36 +63,43 @@ class screen_data_t { std::vector line_datas; - public: +public: - struct cursor_t { + 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_t &add_line(void) + { line_datas.resize(line_datas.size() + 1); return line_datas.back(); } - void resize(size_t size) { + void resize(size_t size) + { line_datas.resize(size); } - line_t &create_line(size_t idx) { - if (idx >= line_datas.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) { + line_t &line(size_t idx) + { return line_datas.at(idx); } - size_t line_count(void) { + size_t line_count(void) + { return line_datas.size(); } }; @@ -102,7 +109,7 @@ class screen_data_t */ class screen_t { - public: +public: /** Constructor */ screen_t(); @@ -139,17 +146,17 @@ class screen_t 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; + */ + 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; }; /** @@ -168,24 +175,24 @@ 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, - const wcstring &left_prompt, - const wcstring &right_prompt, - const wcstring &commandline, - size_t explicit_len, - const int *colors, - const int *indent, - size_t cursor_pos ); +void s_write(screen_t *s, + const wcstring &left_prompt, + const wcstring &right_prompt, + const wcstring &commandline, + size_t explicit_len, + const int *colors, + const int *indent, + size_t cursor_pos); /** This function resets the screen buffers internal knowledge about @@ -203,6 +210,6 @@ void s_write( screen_t *s, resizing, there will be one line of garbage for every repaint, which will quicly fill the screen. */ -void s_reset( screen_t *s, bool reset_cursor, bool reset_prompt = true ); +void s_reset(screen_t *s, bool reset_cursor, bool reset_prompt = true); #endif diff --git a/set_color.cpp b/set_color.cpp index 4de3ba605..ff8abe173 100644 --- a/set_color.cpp +++ b/set_color.cpp @@ -59,7 +59,7 @@ #define GETOPT_STRING "b:hvocu" #ifdef _ - #undef _ +#undef _ #endif #ifdef USE_GETTEXT @@ -70,74 +70,80 @@ 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(fish_term256); - } else { + } + else + { const char *term = getenv("TERM"); support_term256 = term && strstr(term, "256color"); } - if( !fgcolor && !bgcolor && !bold && !underline ) - { - check_locale_init(); - fprintf( stderr, _("%s: Expected an argument\n"), SET_COLOR ); - print_help( argv[0], 2 ); - return 1; - } + if (!fgcolor && !bgcolor && !bold && !underline) + { + check_locale_init(); + fprintf(stderr, _("%s: Expected an argument\n"), SET_COLOR); + print_help(argv[0], 2); + return 1; + } rgb_color_t fg = rgb_color_t(fgcolor ? fgcolor : ""); - if( fgcolor && fg.is_none()) - { - check_locale_init(); - fprintf( stderr, _("%s: Unknown color '%s'\n"), SET_COLOR, fgcolor ); - return 1; - } + if (fgcolor && fg.is_none()) + { + check_locale_init(); + fprintf(stderr, _("%s: Unknown color '%s'\n"), SET_COLOR, fgcolor); + return 1; + } rgb_color_t bg = rgb_color_t(bgcolor ? bgcolor : ""); - if( bgcolor && bg.is_none()) - { - check_locale_init(); - fprintf( stderr, _("%s: Unknown color '%s'\n"), SET_COLOR, bgcolor ); - return 1; - } - - setupterm( 0, STDOUT_FILENO, 0); - - if( bold ) - { - if( enter_bold_mode ) - putp( enter_bold_mode ); - } - - if( underline ) - { - if( enter_underline_mode ) - putp( enter_underline_mode ); - } - - if( bgcolor ) - { - if( bg.is_normal() ) + if (bgcolor && bg.is_none()) { + check_locale_init(); + fprintf(stderr, _("%s: Unknown color '%s'\n"), SET_COLOR, bgcolor); + return 1; + } + + setupterm(0, STDOUT_FILENO, 0); + + if (bold) + { + if (enter_bold_mode) + putp(enter_bold_mode); + } + + if (underline) + { + if (enter_underline_mode) + putp(enter_underline_mode); + } + + if (bgcolor) + { + if (bg.is_normal()) + { write_background_color(0); - putp( tparm(exit_attribute_mode) ); + putp(tparm(exit_attribute_mode)); + } } - } - if( fgcolor ) - { - if( fg.is_normal() ) + if (fgcolor) { + if (fg.is_normal()) + { write_foreground_color(0); - putp( tparm(exit_attribute_mode) ); - } - else - { + putp(tparm(exit_attribute_mode)); + } + else + { write_foreground_color(index_for_color(fg)); + } } - } - if( bgcolor ) - { - if( ! bg.is_normal() ) + if (bgcolor) { + if (! bg.is_normal()) + { write_background_color(index_for_color(bg)); + } } - } - if( del_curterm( cur_term ) == ERR ) - { - fprintf( stderr, "%s", _("Error while closing terminfo") ); - } + if (del_curterm(cur_term) == ERR) + { + fprintf(stderr, "%s", _("Error while closing terminfo")); + } } diff --git a/signal.cpp b/signal.cpp index 98883bf4e..f713b4e79 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 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 ) +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 ) +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,37 +429,37 @@ 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); + } } /** Respond to a winch signal by checking the terminal size */ -static void handle_winch( int sig, 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); } /** Respond to a hup signal by exiting, unless it is caught by a shellscript function, in which case we do nothing. */ -static void handle_hup( 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); + } } /** Handle sigterm. The only thing we do is restore the front process ID, then die. */ -static void handle_term( int sig, siginfo_t *info, void *context ) +static void handle_term(int sig, siginfo_t *info, void *context) { restore_term_foreground_process_group(); signal(SIGTERM, SIG_DFL); @@ -470,34 +470,34 @@ static void handle_term( int sig, siginfo_t *info, void *context ) Interactive mode ^C handler. Respond to int signal by setting interrupted-flag and stopping all loops and conditionals. */ -static void handle_int( 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); } /** sigchld handler. Does notification and calls the handler in proc.c */ -static void handle_chld( 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); + } } @@ -506,196 +506,196 @@ 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(); - } - - // SIGTERM restores the terminal controlling process before dying - act.sa_flags = SA_SIGINFO; - act.sa_sigaction= &handle_term; - if( sigaction( SIGTERM, &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; + // SIGTERM restores the terminal controlling process before dying + act.sa_flags = SA_SIGINFO; + act.sa_sigaction= &handle_term; + if (sigaction(SIGTERM, &act, 0)) + { + wperror(L"sigaction"); + FATAL_EXIT(); + } - sigaction( SIGINT, &act, 0); - sigaction( SIGQUIT, &act, 0); + } + else + { + /* + Non-interactive. Ignore interrupt, check exit status of + processes to determine result instead. + */ + act.sa_handler=SIG_IGN; - act.sa_handler=SIG_DFL; + sigaction(SIGINT, &act, 0); + sigaction(SIGQUIT, &act, 0); + + 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 ) +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++; + 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)); - } + 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/signal.h b/signal.h index 3bc7527d2..dcd2b6e92 100644 --- a/signal.h +++ b/signal.h @@ -12,17 +12,17 @@ The library for various signal related issues Get the integer signal value representing the specified signal, or -1 of no signal was found */ -int wcs2sig( const wchar_t *str ); +int wcs2sig(const wchar_t *str); /** Get string representation of a signal */ -const wchar_t *sig2wcs( int sig ); +const wchar_t *sig2wcs(int sig); /** Returns a description of the specified signal. */ -const wchar_t *signal_get_desc( int sig ); +const wchar_t *signal_get_desc(int sig); /** Set all signal handlers to SIG_DFL @@ -40,7 +40,7 @@ void signal_set_handlers(); \param sig The signal to specify the action of \param do_handle If true fish will catch the specified signal and fire an event, otherwise the default action (SIG_DFL) will be set */ -void signal_handle( int sig, int do_handle ); +void signal_handle(int sig, int do_handle); /** Block all signals diff --git a/tokenizer.cpp b/tokenizer.cpp index 537f1332f..9d6bc0c83 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 @@ -83,105 +83,106 @@ static const wchar_t *tok_desc[] = \return 0 if the system could not provide the memory needed, and 1 otherwise. */ -static int check_size( tokenizer *tok, size_t len ) +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 ) + if (tok->last_len <= len) { - wperror( L"realloc" ); - return 0; + 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; } - tok->last = tmp; - } - return 1; + return 1; } /** Set the latest tokens string to be the specified error message */ -static void tok_call_error( tokenizer *tok, int error_type, const wchar_t *error_message ) +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 ) +int tok_get_error(tokenizer *tok) { - return tok->error; + return tok->error; } -void tok_init( tokenizer *tok, const wchar_t *b, int flags ) +void tok_init(tokenizer *tok, const wchar_t *b, int flags) { /* We can only generate error messages on the main thread due to wgettext() thread safety issues. */ - if (! (flags & TOK_SQUASH_ERRORS)) { + if (!(flags & TOK_SQUASH_ERRORS)) + { ASSERT_IS_MAIN_THREAD(); } - CHECK( tok, ); + CHECK(tok,); - memset( tok, 0, sizeof( tokenizer) ); + memset(tok, 0, sizeof(tokenizer)); - CHECK( b, ); + CHECK(b,); - 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->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 = (*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 ) +void tok_destroy(tokenizer *tok) { - CHECK( tok, ); + CHECK(tok,); - free( tok->last ); + free(tok->last); } -int tok_last_type( tokenizer *tok ) +int tok_last_type(tokenizer *tok) { - CHECK( tok, TOK_ERROR ); - CHECK( tok->buff, TOK_ERROR ); + CHECK(tok, TOK_ERROR); + CHECK(tok->buff, TOK_ERROR); - return tok->last_type; + return tok->last_type; } -wchar_t *tok_last( tokenizer *tok ) +wchar_t *tok_last(tokenizer *tok) { - CHECK( tok, 0 ); + CHECK(tok, 0); - return tok->last; + return tok->last; } -int tok_has_next( tokenizer *tok ) +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,8 +190,8 @@ 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) @@ -199,10 +200,10 @@ int tokenizer::line_number_of_character_at_offset(size_t offset) size_t i; if (offset > cached_lineno_offset) { - for ( i = cached_lineno_offset; str[i] && i': - case L'&': - return false; + case L'\0': + case L' ': + case L'\n': + case L'|': + case L'\t': + case L';': + case L'#': + case L'\r': + case L'<': + case L'>': + case L'&': + return false; /* Conditional separator */ - case L'^': - return ! is_first; + case L'^': + return ! is_first; - default: - return true; + default: + return true; } } @@ -257,495 +258,495 @@ static bool is_string_char( wchar_t c, bool is_first ) common characters. This is obviously not a suitable replacement for iswalpha. */ -static int myal( wchar_t c ) +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'); } /** Read the next token as a string */ -static void read_string( tokenizer *tok ) +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 ) ) + while (1) { + + if (!myal(*tok->buff)) + { // debug(1, L"%lc", *tok->buff ); - 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 (*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; + } + } + 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; + } } - else if( *tok->buff == L'\n' && mode == 0) - { - tok->buff--; - do_loop = 0; - break; - } + + + if (!do_loop) + break; tok->buff++; - continue; - } + is_first = false; + } - - /* - 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->accept_unfinished) && (mode!=0)) + { + TOK_CALL_ERROR(tok, TOK_UNTERMINATED_SUBSHELL, PARAN_ERROR); + return; } - if( !do_loop ) - break; + len = tok->buff - start; - tok->buff++; - is_first = false; - } + if (!check_size(tok, len)) + return; - if( (!tok->accept_unfinished) && (mode!=0) ) - { - TOK_CALL_ERROR( tok, TOK_UNTERMINATED_SUBSHELL, PARAN_ERROR ); - return; - } - - - 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_STRING; + memcpy(tok->last, start, sizeof(wchar_t)*len); + tok->last[len] = L'\0'; + tok->last_type = TOK_STRING; } /** Read the next token as a comment. */ -static void read_comment( 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; } /** Read a FD redirection. */ -static void read_redirect( tokenizer *tok, int fd ) +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) ) + if ((*tok->buff == L'>') || + (*tok->buff == L'^')) { - tok->buff++; - mode = 1; + 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 { - mode = 0; + TOK_CALL_ERROR(tok, TOK_OTHER, REDIRECT_ERROR); } - if( *tok->buff == L'|' ) + if (!check_size(tok, 2)) { - 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; - } + 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 ) +wchar_t tok_last_quote(tokenizer *tok) { - CHECK( tok, 0 ); + CHECK(tok, 0); - return tok->last_quote; + return tok->last_quote; } /** Test if a character is whitespace. Differs from iswspace in that it does not consider a newline to be whitespace. */ -static int my_iswspace( wchar_t c ) +static int my_iswspace(wchar_t 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]); -} - - -void tok_next( tokenizer *tok ) -{ - - CHECK( tok, ); - CHECK( tok->buff, ); - - if( tok_last_type( tok ) == TOK_ERROR ) - { - tok->has_next=false; - return; - } - - if( !tok->has_next ) - { -/* wprintf( L"EOL\n" );*/ - tok->last_type = TOK_END; - return; - } - - while( 1 ) - { - if( my_iswspace(*(tok->buff) ) ) - { - tok->buff++; - } + 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)) { - if(( *(tok->buff) == L'\\') &&( *(tok->buff+1) == L'\n') ) - { - tok->last_pos = tok->buff - tok->orig_buff; - tok->buff+=2; + 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; + } + + if (!tok->has_next) + { + /* wprintf( L"EOL\n" );*/ tok->last_type = TOK_END; return; - } - break; } - } - - if( *tok->buff == L'#') - { - if( tok->show_comments ) + while (1) { - tok->last_pos = tok->buff - tok->orig_buff; - read_comment( tok ); - return; + 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; + } } - else + + + if (*tok->buff == L'#') { - while( *(tok->buff)!= L'\n' && *(tok->buff)!= L'\0' ) - tok->buff++; + 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 (my_iswspace(*(tok->buff))) + tok->buff++; } - while( my_iswspace(*(tok->buff) ) ) - tok->buff++; - } + tok->last_pos = tok->buff - tok->orig_buff; - tok->last_pos = tok->buff - tok->orig_buff; - - switch( *tok->buff ) - { + switch (*tok->buff) + { case L'\0': - tok->last_type = TOK_END; - /*fwprintf( stderr, L"End of string\n" );*/ - tok->has_next = false; - break; + 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; + tok->last_type = TOK_END; + tok->buff++; + break; case L'&': - tok->last_type = TOK_BACKGROUND; - tok->buff++; - break; + tok->last_type = TOK_BACKGROUND; + tok->buff++; + break; case L'|': - check_size( tok, 2 ); + check_size(tok, 2); - tok->last[0]=L'1'; - tok->last[1]=L'\0'; - tok->last_type = TOK_PIPE; - tok->buff++; - break; + tok->last[0]=L'1'; + tok->last[1]=L'\0'; + tok->last_type = TOK_PIPE; + tok->buff++; + break; case L'>': - read_redirect( tok, 1 ); - return; + read_redirect(tok, 1); + return; case L'<': - read_redirect( tok, 0 ); - return; + read_redirect(tok, 0); + return; case L'^': - read_redirect( tok, 2 ); - return; + 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)) + if (iswdigit(*tok->buff)) { - case L'^': - case L'>': - case L'<': - read_redirect( tok, fd ); - return; + 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; } - tok->buff = orig; - } - read_string( tok ); + read_string(tok); } - } + } } -const wchar_t *tok_string( tokenizer *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 ) +wchar_t *tok_first(const wchar_t *str) { - tokenizer t; - wchar_t *res=0; + tokenizer t; + wchar_t *res=0; - CHECK( str, 0 ); + CHECK(str, 0); - tok_init( &t, str, TOK_SQUASH_ERRORS); + tok_init(&t, str, TOK_SQUASH_ERRORS); - switch( tok_last_type( &t ) ) - { + switch (tok_last_type(&t)) + { case TOK_STRING: // fwprintf( stderr, L"Got token %ls\n", tok_last( &t )); - res = wcsdup(tok_last( &t )); - break; + res = wcsdup(tok_last(&t)); + break; default: - break; - } + break; + } - tok_destroy( &t ); - return res; + tok_destroy(&t); + return res; } -int tok_get_pos( tokenizer *tok ) +int tok_get_pos(tokenizer *tok) { - CHECK( tok, 0 ); + CHECK(tok, 0); - return (int)tok->last_pos; + return (int)tok->last_pos; } -void tok_set_pos( tokenizer *tok, int pos ) +void tok_set_pos(tokenizer *tok, int pos) { - CHECK( tok, ); + CHECK(tok,); - tok->buff = tok->orig_buff + pos; - tok->has_next = true; - tok_next( tok ); + tok->buff = tok->orig_buff + pos; + tok->has_next = true; + tok_next(tok); } @@ -754,40 +755,40 @@ void tok_set_pos( tokenizer *tok, int pos ) /** This main function is used for compiling the tokenizer_test command, used for testing the tokenizer. */ -int main( int argc, char **argv ) +int main(int argc, char **argv) { - tokenizer tok; - int i; - for ( i=1; i 0 ? 2 : -2; - - secondary_diff = (aend-a) - (bend-b); - - a=aend-1; - b=bend-1; - } - else - { - int diff = towlower(*a) - towlower(*b); - if( diff != 0 ) - return (diff>0)?2:-2; - - 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 ) + long secondary_diff=0; + if (iswdigit(*a) && iswdigit(*b)) { - return secondary_diff > 0 ? 1 :-1; - } - } + wchar_t *aend, *bend; + long al; + long bl; + long diff; - return res; + errno = 0; + al = wcstol(a, &aend, 10); + bl = wcstol(b, &bend, 10); + + if (errno) + { + /* + Huuuuuuuuge numbers - fall back to regular string comparison + */ + return wcscmp(a, b); + } + + diff = al - bl; + if (diff) + return diff > 0 ? 2 : -2; + + secondary_diff = (aend-a) - (bend-b); + + a=aend-1; + b=bend-1; + } + else + { + int diff = towlower(*a) - towlower(*b); + if (diff != 0) + return (diff>0)?2:-2; + + 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 c16c5912b..c3ef0ca94 100644 --- a/util.h +++ b/util.h @@ -20,9 +20,9 @@ */ typedef struct buffer { - char *buff; /** -static inline T maxi( T a, T b ) +static inline T maxi(T a, T b) { return a>b?a:b; } @@ -39,7 +39,7 @@ static inline T maxi( T a, T b ) Returns the smaller of two ints */ template -static inline T mini( T a, T b ) +static inline T mini(T a, T b) { return a middle && middle > bottom) + 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. */ static const wchar_t * -_wgetopt_initialize (const wchar_t *optstring) +_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 @@ -398,314 +398,314 @@ _wgetopt_initialize (const wchar_t *optstring) long-named options. */ int -_wgetopt_internal (int argc, wchar_t *const *argv, const wchar_t *optstring, const struct woption *longopts, int *longind, int long_only) +_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 (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. */ - - 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] == '-')); - } - - /* 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)) + if (ordering == PERMUTE) { - /* Exact match found. */ - pfound = p; - indfound = option_index; - exact = 1; - break; + /* 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; + + /* 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; } - 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 '?'; + /* 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] == '-')); } - if (pfound != NULL) + /* 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]))))) { - 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 + 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) - { - if (argv[woptind - 1][1] == '-') - /* --option */ - fwprintf (stderr, - _(L"%ls: Option '--%ls' doesn't allow an argument\n"), - argv[0], pfound->name); + 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 - /* +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 '?'; + { + /* 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; + } } - } - 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; + return c; } - - /* 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) +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) +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) +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 a2e90bbe8..4b6536e58 100644 --- a/wgetopt.h +++ b/wgetopt.h @@ -53,154 +53,154 @@ Cambridge, MA 02139, USA. */ extern "C" { #endif -/** For communication from `getopt' to the caller. - When `getopt' finds an option that takes an argument, - the argument value is returned here. - Also, when `ordering' is RETURN_IN_ORDER, - each non-option ARGV-element is returned here. */ + /** For communication from `getopt' to the caller. + When `getopt' finds an option that takes an argument, + the argument value is returned here. + Also, when `ordering' is RETURN_IN_ORDER, + each non-option ARGV-element is returned here. */ -extern wchar_t *woptarg; + extern wchar_t *woptarg; -/** Index in ARGV of the next element to be scanned. - This is used for communication to and from the caller - and for communication between successive calls to `getopt'. + /** Index in ARGV of the next element to be scanned. + This is used for communication to and from the caller + and for communication between successive calls to `getopt'. - On entry to `getopt', zero means this is the first call; initialize. + On entry to `getopt', zero means this is the first call; initialize. - When `getopt' returns EOF, this is the index of the first of the - non-option elements that the caller should itself scan. + When `getopt' returns EOF, this is the index of the first of the + non-option elements that the caller should itself scan. - Otherwise, `woptind' communicates from one call to the next - how much of ARGV has been scanned so far. */ + Otherwise, `woptind' communicates from one call to the next + how much of ARGV has been scanned so far. */ -extern int woptind; + extern int woptind; -/** Callers store zero here to inhibit the error message `getopt' prints - for unrecognized options. */ + /** Callers store zero here to inhibit the error message `getopt' prints + for unrecognized options. */ -extern int wopterr; + extern int wopterr; -/** Set to an option character which was unrecognized. */ + /** Set to an option character which was unrecognized. */ -extern int woptopt; + extern int woptopt; -/** Describe the long-named options requested by the application. - The LONG_OPTIONS argument to getopt_long or getopt_long_only is a vector - of `struct option' terminated by an element containing a name which is - zero. + /** Describe the long-named options requested by the application. + The LONG_OPTIONS argument to getopt_long or getopt_long_only is a vector + of `struct option' terminated by an element containing a name which is + 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. + 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. - 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 - left unchanged if the option is not found. + 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 + left unchanged if the option is not found. - To have a long-named option do something other than set an `int' to - a compiled-in constant, such as set a value from `optarg', set the - option's `flag' field to zero and its `val' field to a nonzero - value (the equivalent single-letter option character, if there is - one). For long options that have a zero `flag' field, `getopt' - returns the contents of the `val' field. */ + To have a long-named option do something other than set an `int' to + a compiled-in constant, such as set a value from `optarg', set the + option's `flag' field to zero and its `val' field to a nonzero + value (the equivalent single-letter option character, if there is + one). For long options that have a zero `flag' field, `getopt' + returns the contents of the `val' field. */ -struct woption -{ - /** - long name for switch - */ + struct woption + { + /** + long name for switch + */ #if defined (__STDC__) && __STDC__ - const wchar_t *name; + const wchar_t *name; #else - wchar_t *name; + 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. - */ - int has_arg; + 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 - */ - int *flag; + /** + 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. - */ - int val; -}; + /** + 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; + }; -/* Names for the values of the `has_arg' field of `struct option'. */ + /* Names for the values of the `has_arg' field of `struct option'. */ -/** - Specifies that a switch does not accept an argument -*/ + /** + Specifies that a switch does not accept an argument + */ #define no_argument 0 -/** - Specifies that a switch requires an argument -*/ + /** + Specifies that a switch requires an argument + */ #define required_argument 1 -/** - Specifies that a switch accepts an optional argument -*/ + /** + Specifies that a switch accepts an optional argument + */ #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. -*/ -extern int wgetopt (int argc, wchar_t *const *argv, const wchar_t *shortopts); + /** + 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 (); + extern int wgetopt(); #endif /* __GNU_LIBRARY__ */ -/** - 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. - */ -extern int wgetopt_long_only (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. + */ + 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. + */ + extern int wgetopt_long_only(int argc, wchar_t *const *argv, + const wchar_t *shortopts, + const struct woption *longopts, int *longind); -/** - 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); + /** + 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); #else /* not __STDC__ */ -/** - 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. + */ + extern int wgetopt(); -/** - 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. + */ + extern int wgetopt_long(); -/** - Get options from argument list. See the glibc manual for information on how to use this function. - */ -extern int wgetopt_long_only (); + /** + 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. -*/ -extern int _wgetopt_internal (); + /** + Internal only. Users should not call this directly. + */ + extern int _wgetopt_internal(); #endif /* __STDC__ */ #ifdef __cplusplus diff --git a/wildcard.cpp b/wildcard.cpp index 07cfcb7cd..da7a7a1cc 100644 --- a/wildcard.cpp +++ b/wildcard.cpp @@ -106,34 +106,34 @@ wildcards using **. static std::map suffix_map; -int wildcard_has( const wchar_t *str, int internal ) +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( internal ) - { - for( ; *str; str++ ) + if (!str) { - if( ( *str == ANY_CHAR ) || (*str == ANY_STRING) || (*str == ANY_STRING_RECURSIVE) ) - return 1; + debug(2, L"Got null string on line %d of file %s", __LINE__, __FILE__); + return 0; } - } - 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; + for (; *str; str++) + { + if (((*str == L'*') || (*str == L'?')) && (prev != L'\\')) + return 1; + prev = *str; + } } - } - return 0; + return 0; } /** @@ -144,11 +144,11 @@ int wildcard_has( const wchar_t *str, int internal ) \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"..")) @@ -157,46 +157,46 @@ static bool wildcard_match2(const wchar_t *str, return ! wcscmp(str, wc); } - if( *wc == ANY_STRING || *wc == ANY_STRING_RECURSIVE) - { - /* Ignore hidden file */ - if( is_first && *str == L'.' ) + if (*wc == ANY_STRING || *wc == ANY_STRING_RECURSIVE) { - return false; + /* 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; } - /* Try all submatches */ - do + if (*wc == ANY_CHAR) { - if( wildcard_match2( str, wc+1, false ) ) - return true; + if (is_first && *str == L'.') + { + return false; + } + + return wildcard_match2(str+1, wc+1, false); } - while( *(str++) != 0 ); + + if (*wc == *str) + return wildcard_match2(str+1, wc+1, false); + 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; } /** @@ -205,120 +205,121 @@ static bool wildcard_match2(const wchar_t *str, 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 ) + 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; - } + } + 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; + return true; } - 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; + + 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 ) +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. */ -static wcstring make_path(const wcstring &base_dir, const wcstring &name) { +static wcstring make_path(const wcstring &base_dir, const wcstring &name) +{ return base_dir + name; } @@ -327,7 +328,7 @@ static wcstring make_path(const wcstring &base_dir, const wcstring &name) { does not perform any caching, it directly calls the mimedb command to do a lookup. */ -static wcstring complete_get_desc_suffix_internal( const wcstring &suff ) +static wcstring complete_get_desc_suffix_internal(const wcstring &suff) { wcstring cmd = wcstring(SUFFIX_CMD_STR) + suff; @@ -335,79 +336,82 @@ static wcstring complete_get_desc_suffix_internal( const wcstring &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 (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 (desc.empty()) + { + desc = COMPLETE_FILE_DESC; + } suffix_map[suff] = desc.c_str(); - return desc; + return desc; } /** Use the mimedb command to look up a description for a given suffix */ -static wcstring complete_get_desc_suffix( const wchar_t *suff_orig ) +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 ); + len = wcslen(suff_orig); - if( len == 0 ) - return COMPLETE_FILE_DESC; + if (len == 0) + return COMPLETE_FILE_DESC; - suff = wcsdup(suff_orig); + 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 ) ) + /* + Drop characters that are commonly used as backup suffixes from the suffix + */ + for (pos=suff; *pos; pos++) { - *pos=0; - break; + if (wcschr(L"?;#~@&", *pos)) + { + *pos=0; + break; + } } - } - tmp = escape( suff, 1 ); - free(suff); - suff = tmp; + tmp = escape(suff, 1); + free(suff); + suff = tmp; std::map::iterator iter = suffix_map.find(suff); wcstring desc; - if (iter != suffix_map.end()) { + if (iter != suffix_map.end()) + { desc = iter->second; - } else { - desc = complete_get_desc_suffix_internal( suff ); + } + else + { + desc = complete_get_desc_suffix_internal(suff); } - free( suff ); + free(suff); - return desc; + return desc; } @@ -424,122 +428,122 @@ static wcstring complete_get_desc_suffix( const wchar_t *suff_orig ) \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 (!lstat_res) { - if( !stat_res ) - { - if( S_ISDIR(buf.st_mode) ) + if (S_ISLNK(lbuf.st_mode)) { - return COMPLETE_DIRECTORY_SYMLINK_DESC; + 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; + + } + 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 ) + if ((buf.st_mode & S_IXUSR) || + (buf.st_mode & S_IXGRP) || + (buf.st_mode & S_IXOTH)) { - /* - 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; + + 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; + } } - } } - - 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) ) + + suffix = wcsrchr(filename.c_str(), L'.'); + if (suffix != 0 && !wcsrchr(suffix, L'/')) { - return COMPLETE_CHAR_DESC; + return complete_get_desc_suffix(suffix); } - 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_FILE_DESC ; } @@ -556,78 +560,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, - expand_flags_t expand_flags) +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; + wcstring munged_completion; - int flags = 0; - int stat_res, lstat_res; - int stat_errno=0; + int flags = 0; + int stat_res, lstat_res; + int stat_errno=0; - long long sz; + 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 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))) { - - if( ( stat_res = wstat( fullname, &buf ) ) ) - { + /* lstat failed! */ 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; + stat_res = lstat_res; } else { - stat_res = lstat_res; - memcpy( &buf, &lbuf, sizeof( struct stat ) ); - sz = (long long)buf.st_size; + 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; + bool wants_desc = !(expand_flags & EXPAND_NO_DESCRIPTIONS); + wcstring desc; if (wants_desc) - desc = file_get_desc( fullname.c_str(), lstat_res, lbuf, stat_res, buf, stat_errno ); + 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 +641,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); } /** @@ -648,31 +652,31 @@ static void wildcard_completion_allocate( std::vector &list, expansion flags specified. flags can be a combination of EXECUTABLES_ONLY and DIRECTORIES_ONLY. */ -static int test_flags( const wchar_t *filename, - int flags ) +static int test_flags(const wchar_t *filename, + int flags) { - if( flags & DIRECTORIES_ONLY ) - { - struct stat buf; - if( wstat( filename, &buf ) == -1 ) + if (flags & DIRECTORIES_ONLY) { - return 0; + struct stat buf; + if (wstat(filename, &buf) == -1) + { + return 0; + } + + if (!S_ISDIR(buf.st_mode)) + { + return 0; + } } - if( !S_ISDIR( buf.st_mode ) ) + + if (flags & EXECUTABLES_ONLY) { - return 0; + if (waccess(filename, X_OK) != 0) + return 0; } - } - - if( flags & EXECUTABLES_ONLY ) - { - if ( waccess( filename, X_OK ) != 0) - return 0; - } - - return 1; + return 1; } /** Appends a completion to the completion list, if the string is missing from the set. */ @@ -697,413 +701,413 @@ typedef std::pair file_id_t; 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, - std::set &completion_set, - std::set &visited_files +static int wildcard_expand_internal(const wchar_t *wc, + 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; + /* Variables for traversing a directory */ + DIR *dir; - /* The result returned */ - int res = 0; + /* The result returned */ + int res = 0; - /* Length of the directory to search in */ - size_t base_len; + /* Length of the directory to search in */ + size_t base_len; - /* Variables for testing for presense of recursive wildcards */ - const wchar_t *wc_recursive; - bool is_recursive; + /* Variables for testing for presense of recursive wildcards */ + const wchar_t *wc_recursive; + bool is_recursive; - /* Slightly mangled version of base_dir */ - const wchar_t *dir_string; + /* Slightly mangled version of base_dir */ + const wchar_t *dir_string; - // debug( 3, L"WILDCARD_EXPAND %ls in %ls", wc, base_dir ); + // debug( 3, L"WILDCARD_EXPAND %ls in %ls", wc, base_dir ); - 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; - } - - 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) ) + if (reader_interrupted()) { - 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; + return -1; } - } - /* - Initialize various variables - */ - - 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' ) + if (!wc || !base_dir) { - /* - 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 ); + debug(2, L"Got null string on line %d of file %s", __LINE__, __FILE__); + return 0; + } - if( test_flags( long_name.c_str(), flags ) ) - { - wildcard_completion_allocate( out, - long_name, - next, - L"", - flags); - } - } + 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; } - } - else - { - res = 1; - insert_completion_if_missing(base_dir, out, completion_set); - } } - else + + /* + Initialize various variables + */ + + dir_string = base_dir[0]==L'\0'?L".":base_dir; + + if (!(dir = wopendir(dir_string))) { - /* - This is the last wildcard segment, and it is not empty. Match files/directories. - */ - wcstring next; - while (wreaddir(dir, next)) - { - const wchar_t * const name = next.c_str(); - if( flags & ACCEPT_INCOMPLETE ) + 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') { - - 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 ) ) + /* + The last wildcard segment is empty. Insert everything if + completing, the directory itself otherwise. + */ + if (flags & ACCEPT_INCOMPLETE) { - wildcard_completion_allocate( out, - long_name, - name, - wc, - flags); + 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"", + flags); + } + } + } + } + else + { + res = 1; + insert_completion_if_missing(base_dir, out, completion_set); } - } } else { - if( wildcard_match2( name, wc, true ) ) - { + /* + This is the last wildcard segment, and it is not empty. Match files/directories. + */ + wcstring 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); + + /* + 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, + flags); + + } + } + } + else + { + if (wildcard_match2(name, wc, true)) + { const wcstring long_name = make_path(base_dir, next); - int skip = 0; + 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) - { + 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; - - /* - new_dir is a scratch area containing the full path to a - file/directory we are iterating over - */ - wchar_t *new_dir; - - /* - The maximum length of a file element - */ - long ln=MAX_FILE_LENGTH; - char * narrow_dir_string = wcs2str( dir_string ); - - /* - In recursive mode, we look through the directory twice. If - so, this rewind is needed. - */ - rewinddir( dir ); - - if( narrow_dir_string ) + if (wc_end || is_recursive) { - /* - Find out how long the filename can be in a worst case - scenario - */ - ln = pathconf( narrow_dir_string, _PC_NAME_MAX ); + /* + Wilcard segment is not the last segment. Recursively call + wildcard_expand for all matching subdirectories. + */ - /* - 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) ); + /* + wc_str is the part of the wildcarded string from the + beginning to the first slash + */ + wchar_t *wc_str; - wc_str = wc_end?wcsndup(wc, wc_end-wc):wcsdup(wc); + /* + new_dir is a scratch area containing the full path to a + file/directory we are iterating over + */ + wchar_t *new_dir; - if( (!new_dir) || (!wc_str) ) - { - DIE_MEM(); - } + /* + The maximum length of a file element + */ + long ln=MAX_FILE_LENGTH; + char * narrow_dir_string = wcs2str(dir_string); - wcscpy( new_dir, base_dir ); + /* + In recursive mode, we look through the directory twice. If + so, this rewind is needed. + */ + rewinddir(dir); + + 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); + + /* + 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)); + + wc_str = wc_end?wcsndup(wc, wc_end-wc):wcsdup(wc); + + if ((!new_dir) || (!wc_str)) + { + DIE_MEM(); + } + + 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 ); - } - - 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 ) + while (wreaddir(dir, next)) { - stat_res = stat( dir_str, &buf ); - free( dir_str ); + const wchar_t *name = next.c_str(); - if( !stat_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; + + /* + 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'; + 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++; - } + /* + 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, + completion_set, + visited_files); + + if (new_res == -1) + { + res = -1; + break; + } + res |= new_res; + + } + } + } } - - 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, - completion_set, - visited_files); - - if( new_res == -1 ) - { - res = -1; - break; - } - res |= new_res; - - } } - } } - } + + free(wc_str); + free(new_dir); } + closedir(dir); - free( wc_str ); - free( new_dir ); - } - closedir( dir ); - - return res; + return res; } -int wildcard_expand( const wchar_t *wc, - const wchar_t *base_dir, - expand_flags_t flags, - std::vector &out ) +int wildcard_expand(const wchar_t *wc, + 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 ); + std::set visited_files; + int res = wildcard_expand_internal(wc, base_dir, flags, out, completion_set, visited_files); - if( flags & ACCEPT_INCOMPLETE ) - { + if (flags & ACCEPT_INCOMPLETE) + { wcstring wc_base; - const wchar_t *wc_base_ptr = wcsrchr( wc, L'/' ); - if( wc_base_ptr ) - { + 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 ) +int wildcard_expand_string(const wcstring &wc, const wcstring &base_dir, expand_flags_t flags, std::vector &outputs) { // PCA: not convinced this temporary variable is really necessary std::vector lst; diff --git a/wildcard.h b/wildcard.h index f2bc54577..1c17292a8 100644 --- a/wildcard.h +++ b/wildcard.h @@ -31,16 +31,16 @@ 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. @@ -67,7 +67,7 @@ enum \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 ); +int wildcard_expand_string(const wcstring &wc, const wcstring &base_dir, expand_flags_t flags, std::vector &out); /** Test whether the given wildcard matches the string. Does not perform any I/O. @@ -75,22 +75,22 @@ int wildcard_expand_string(const wcstring &wc, const wcstring &base_dir, expand_ \param wc The wildcard to test against \return true if the wildcard matched */ -bool wildcard_match( const wcstring &str, const wcstring &wc ); +bool wildcard_match(const wcstring &str, const wcstring &wc); /** Check if the specified string contains wildcards */ -int wildcard_has( const wchar_t *str, int internal ); +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 fdd6680ba..730ad3d37 100644 --- a/wutil.cpp +++ b/wutil.cpp @@ -68,27 +68,36 @@ void wutil_destroy() bool wreaddir_resolving(DIR *dir, const std::wstring &dir_path, std::wstring &out_name, bool *out_is_dir) { - struct dirent *d = readdir( dir ); - if ( !d ) return false; + struct dirent *d = readdir(dir); + if (!d) return false; out_name = str2wcstring(d->d_name); - if (out_is_dir) { + if (out_is_dir) + { /* The caller cares if this is a directory, so check */ bool is_dir; - if (d->d_type == DT_DIR) { + if (d->d_type == DT_DIR) + { is_dir = true; - } else if (d->d_type == DT_LNK || d->d_type == DT_UNKNOWN) { + } + else if (d->d_type == DT_LNK || d->d_type == DT_UNKNOWN) + { /* We want to treat symlinks to directories as directories. Use stat to resolve it. */ cstring fullpath = wcs2string(dir_path); fullpath.push_back('/'); fullpath.append(d->d_name); struct stat buf; - if (stat(fullpath.c_str(), &buf) != 0) { + if (stat(fullpath.c_str(), &buf) != 0) + { is_dir = false; - } else { - is_dir = !! (S_ISDIR(buf.st_mode)); } - } else { + else + { + is_dir = !!(S_ISDIR(buf.st_mode)); + } + } + else + { is_dir = false; } *out_is_dir = is_dir; @@ -98,66 +107,67 @@ bool wreaddir_resolving(DIR *dir, const std::wstring &dir_path, std::wstring &ou bool wreaddir(DIR *dir, std::wstring &out_name) { - struct dirent *d = readdir( dir ); - if ( !d ) return false; + struct dirent *d = readdir(dir); + if (!d) return false; out_name = str2wcstring(d->d_name); return true; } -wchar_t *wgetcwd( wchar_t *buff, size_t sz ) +wchar_t *wgetcwd(wchar_t *buff, size_t sz) { - char *buffc = (char *)malloc( sz*MAX_UTF8_BYTES); - char *res; - wchar_t *ret = 0; + 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 ) ) + if (!buffc) { - ret = buff; + errno = ENOMEM; + return 0; } - } - free( buffc ); + res = getcwd(buffc, sz*MAX_UTF8_BYTES); + if (res) + { + if ((size_t)-1 != mbstowcs(buff, buffc, sizeof(wchar_t) * sz)) + { + ret = buff; + } + } - return ret; + free(buffc); + + return ret; } -int wchdir( const wcstring &dir ) +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) { int permissions = 0, options = 0; size_t idx = 0; - switch (mode[idx++]) { - case 'r': - permissions = O_RDONLY; - break; - case 'w': - permissions = O_WRONLY; - options = O_CREAT | O_TRUNC; - break; - case 'a': - permissions = O_WRONLY; - options = O_CREAT | O_APPEND; - break; - default: - errno = EINVAL; - return NULL; - break; + switch (mode[idx++]) + { + case 'r': + permissions = O_RDONLY; + break; + case 'w': + permissions = O_WRONLY; + options = O_CREAT | O_TRUNC; + break; + case 'a': + permissions = O_WRONLY; + options = O_CREAT | O_APPEND; + break; + default: + errno = EINVAL; + return NULL; + break; } /* Skip binary */ if (mode[idx] == 'b') @@ -182,13 +192,19 @@ FILE *wfreopen(const wcstring &path, const char *mode, FILE *stream) return freopen(tmp.c_str(), mode, stream); } -bool set_cloexec(int fd) { +bool set_cloexec(int fd) +{ int flags = fcntl(fd, F_GETFD, 0); - if (flags < 0) { + if (flags < 0) + { return false; - } else if (flags & FD_CLOEXEC) { + } + else if (flags & FD_CLOEXEC) + { return true; - } else { + } + else + { return fcntl(fd, F_SETFD, flags | FD_CLOEXEC) >= 0; } } @@ -199,13 +215,15 @@ static int wopen_internal(const wcstring &pathname, int flags, mode_t mode, bool cstring tmp = wcs2string(pathname); /* Prefer to use O_CLOEXEC. It has to both be defined and nonzero */ #ifdef O_CLOEXEC - if (cloexec && O_CLOEXEC) { + if (cloexec && O_CLOEXEC) + { flags |= O_CLOEXEC; cloexec = false; } #endif int fd = ::open(tmp.c_str(), flags, mode); - if (cloexec && fd >= 0 && ! set_cloexec(fd)) { + if (cloexec && fd >= 0 && ! set_cloexec(fd)) + { close(fd); fd = -1; } @@ -246,7 +264,7 @@ int wstat(const wcstring &file_name, struct stat *buf) int lwstat(const wcstring &file_name, struct stat *buf) { - // fprintf(stderr, "%s\n", __PRETTY_FUNCTION__); + // fprintf(stderr, "%s\n", __PRETTY_FUNCTION__); cstring tmp = wcs2string(file_name); return lstat(tmp.c_str(), buf); } @@ -266,40 +284,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 (!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 (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 ); + free(narrow_res); - return res; + return res; } #else @@ -307,53 +325,54 @@ 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 (!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 (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 -wcstring wdirname( const wcstring &path ) +wcstring wdirname(const wcstring &path) { char *tmp = wcs2str(path.c_str()); - char *narrow_res = dirname( tmp ); + char *narrow_res = dirname(tmp); wcstring result = format_string(L"%s", narrow_res); free(tmp); return result; } -wcstring wbasename( const wcstring &path ) +wcstring wbasename(const wcstring &path) { char *tmp = wcs2str(path.c_str()); - char *narrow_res = basename( tmp ); + char *narrow_res = basename(tmp); wcstring result = format_string(L"%s", narrow_res); free(tmp); return result; } /* Really init wgettext */ -static void wgettext_really_init() { +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); } /** @@ -365,63 +384,65 @@ static void wgettext_init_if_necessary() pthread_once(&once, wgettext_really_init); } -const wchar_t *wgettext( const wchar_t *in ) +const wchar_t *wgettext(const wchar_t *in) { - if( !in ) - return in; + if (!in) + return in; - // preserve errno across this since this is often used in printing error messages - int err = errno; + // 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) { + 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) { +wcstring wgettext2(const wcstring &in) +{ wgettext_init_if_necessary(); std::string mbs_in = wcs2string(in); - char *out = gettext( mbs_in.c_str() ); + char *out = gettext(mbs_in.c_str()); wcstring result = format_string(L"%s", out); return result; } -const wchar_t *wgetenv( const wcstring &name ) +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 ) +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 ) +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 d1bf770bb..176b7cbd0 100644 --- a/wutil.h +++ b/wutil.h @@ -84,12 +84,12 @@ void wperror(const wcstring &s); /** Wide character version of getcwd(). */ -wchar_t *wgetcwd( wchar_t *buff, size_t sz ); +wchar_t *wgetcwd(wchar_t *buff, size_t sz); /** Wide character version of chdir() */ -int wchdir( const wcstring &dir ); +int wchdir(const wcstring &dir); /** Wide character version of realpath function. Just like the GNU @@ -108,12 +108,12 @@ bool wreaddir_resolving(DIR *dir, const std::wstring &dir_path, std::wstring &ou /** Wide character version of dirname() */ -std::wstring wdirname( const std::wstring &path); +std::wstring wdirname(const std::wstring &path); /** Wide character version of basename() */ -std::wstring wbasename( const std::wstring &path); +std::wstring wbasename(const std::wstring &path); /** Wide character wrapper around the gettext function. For historic @@ -123,23 +123,23 @@ std::wstring wbasename( const std::wstring &path); wgettext, so that wgettext will be nothing more than a wrapper around gettext, like all other functions in this file. */ -const wchar_t *wgettext( const wchar_t *in ); +const wchar_t *wgettext(const wchar_t *in); wcstring wgettext2(const wcstring &in); /** Wide character version of getenv */ -const wchar_t *wgetenv( const wcstring &name ); +const wchar_t *wgetenv(const wcstring &name); /** Wide character version of mkdir */ -int wmkdir( const wcstring &dir, int mode ); +int wmkdir(const wcstring &dir, int mode); /** Wide character version of rename */ -int wrename( const wcstring &oldName, const wcstring &newName ); +int wrename(const wcstring &oldName, const wcstring &newName); /** Like wcstol(), but fails on a value outside the range of an int */ int fish_wcstoi(const wchar_t *str, wchar_t ** endptr, int base); diff --git a/xdgmime.cpp b/xdgmime.cpp index 5db32268f..39d0a1894 100644 --- a/xdgmime.cpp +++ b/xdgmime.cpp @@ -59,189 +59,193 @@ 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 { - time_t mtime; - char *directory_name; - int checked; - XdgDirTimeList *next; + time_t mtime; + char *directory_name; + int checked; + XdgDirTimeList *next; }; struct XdgCallbackList { - XdgCallbackList *next; - XdgCallbackList *prev; - int callback_id; - XdgMimeCallback callback; - void *data; - XdgMimeDestroy destroy; + XdgCallbackList *next; + XdgCallbackList *prev; + int callback_id; + XdgMimeCallback callback; + void *data; + XdgMimeDestroy destroy; }; /* 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); +typedef int (*XdgDirectoryFunc)(const char *directory, + void *user_data); static XdgDirTimeList * -xdg_dir_time_list_new (void) +xdg_dir_time_list_new(void) { - XdgDirTimeList *retval; + XdgDirTimeList *retval; - retval = (XdgDirTimeList *)calloc (1, sizeof (XdgDirTimeList)); - retval->checked = XDG_CHECKED_UNCHECKED; + retval = (XdgDirTimeList *)calloc(1, sizeof(XdgDirTimeList)); + retval->checked = XDG_CHECKED_UNCHECKED; - return retval; + return retval; } static void -xdg_dir_time_list_free (XdgDirTimeList *list) +xdg_dir_time_list_free(XdgDirTimeList *list) { - XdgDirTimeList *next; + XdgDirTimeList *next; - while (list) + while (list) { - next = list->next; - free (list->directory_name); - free (list); - list = next; + next = list->next; + free(list->directory_name); + free(list); + list = next; } } static int -xdg_mime_init_from_directory (const char *directory) +xdg_mime_init_from_directory(const char *directory) { - char *file_name; - struct stat st; - XdgDirTimeList *list; + char *file_name; + struct stat st; + XdgDirTimeList *list; - assert (directory != NULL); + assert(directory != NULL); - file_name = (char *)malloc (strlen (directory) + strlen ("/mime/globs") + 1); - strcpy (file_name, directory); strcat (file_name, "/mime/globs"); - if (stat (file_name, &st) == 0) + file_name = (char *)malloc(strlen(directory) + strlen("/mime/globs") + 1); + strcpy(file_name, directory); + strcat(file_name, "/mime/globs"); + if (stat(file_name, &st) == 0) { - _xdg_mime_glob_read_from_file (global_hash, file_name); + _xdg_mime_glob_read_from_file(global_hash, file_name); - list = xdg_dir_time_list_new (); - list->directory_name = file_name; - list->mtime = st.st_mtime; - list->next = dir_time_list; - dir_time_list = list; + list = xdg_dir_time_list_new(); + list->directory_name = file_name; + list->mtime = st.st_mtime; + list->next = dir_time_list; + dir_time_list = list; } - else + else { - free (file_name); + free(file_name); } - file_name = (char *)malloc (strlen (directory) + strlen ("/mime/magic") + 1); - strcpy (file_name, directory); strcat (file_name, "/mime/magic"); - if (stat (file_name, &st) == 0) + file_name = (char *)malloc(strlen(directory) + strlen("/mime/magic") + 1); + strcpy(file_name, directory); + strcat(file_name, "/mime/magic"); + if (stat(file_name, &st) == 0) { - _xdg_mime_magic_read_from_file (global_magic, file_name); + _xdg_mime_magic_read_from_file(global_magic, file_name); - list = xdg_dir_time_list_new (); - list->directory_name = file_name; - list->mtime = st.st_mtime; - list->next = dir_time_list; - dir_time_list = list; + list = xdg_dir_time_list_new(); + list->directory_name = file_name; + list->mtime = st.st_mtime; + list->next = dir_time_list; + dir_time_list = list; } - else + else { - free (file_name); + free(file_name); } - file_name = (char *)malloc (strlen (directory) + strlen ("/mime/aliases") + 1); - strcpy (file_name, directory); strcat (file_name, "/mime/aliases"); - _xdg_mime_alias_read_from_file (alias_list, file_name); - free (file_name); + file_name = (char *)malloc(strlen(directory) + strlen("/mime/aliases") + 1); + strcpy(file_name, directory); + strcat(file_name, "/mime/aliases"); + _xdg_mime_alias_read_from_file(alias_list, file_name); + free(file_name); - file_name = (char *)malloc (strlen (directory) + strlen ("/mime/subclasses") + 1); - strcpy (file_name, directory); strcat (file_name, "/mime/subclasses"); - _xdg_mime_parent_read_from_file (parent_list, file_name); - free (file_name); + file_name = (char *)malloc(strlen(directory) + strlen("/mime/subclasses") + 1); + strcpy(file_name, directory); + strcat(file_name, "/mime/subclasses"); + _xdg_mime_parent_read_from_file(parent_list, file_name); + free(file_name); - return FALSE; /* Keep processing */ + return FALSE; /* Keep processing */ } /* Runs a command on all the directories in the search path */ static void -xdg_run_command_on_dirs (XdgDirectoryFunc func, - void *user_data) +xdg_run_command_on_dirs(XdgDirectoryFunc func, + void *user_data) { - const char *xdg_data_home; - const char *xdg_data_dirs; - const char *ptr; + 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) { - if ((func) (xdg_data_home, user_data)) - return; + if ((func)(xdg_data_home, user_data)) + return; } - else + else { - const char *home; + const char *home; - home = getenv ("HOME"); - if (home != NULL) - { - char *guessed_xdg_home; - int stop_processing; + home = getenv("HOME"); + if (home != NULL) + { + 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"); - 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; - int stop_processing; + const char *end_ptr; + char *dir; + int len; + int stop_processing; - 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; + } - if (*end_ptr == ':') - len = end_ptr - ptr; - else - len = end_ptr - ptr + 1; - dir = (char *)malloc (len + 1); - strncpy (dir, ptr, len); - dir[len] = '\0'; - stop_processing = (func) (dir, user_data); - free (dir); + if (*end_ptr == ':') + len = end_ptr - ptr; + else + len = end_ptr - ptr + 1; + dir = (char *)malloc(len + 1); + strncpy(dir, ptr, len); + dir[len] = '\0'; + stop_processing = (func)(dir, user_data); + free(dir); - if (stop_processing) - return; + if (stop_processing) + return; - ptr = end_ptr; + ptr = end_ptr; } } @@ -252,92 +256,94 @@ xdg_run_command_on_dirs (XdgDirectoryFunc func, * FIXME: This doesn't protect against permission changes. */ static int -xdg_check_file (const char *file_path) +xdg_check_file(const char *file_path) { - struct stat st; + struct stat st; - /* If the file exists */ - if (stat (file_path, &st) == 0) + /* If the file exists */ + if (stat(file_path, &st) == 0) { - XdgDirTimeList *list; + 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; + 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; - return (list->checked != XDG_CHECKED_VALID); - } - } - return TRUE; + return (list->checked != XDG_CHECKED_VALID); + } + } + return TRUE; } - return FALSE; + return FALSE; } static int -xdg_check_dir (const char *directory, - int *invalid_dir_list) +xdg_check_dir(const char *directory, + int *invalid_dir_list) { - int invalid; - char *file_name; + int invalid; + char *file_name; - assert (directory != NULL); + assert(directory != NULL); - /* Check the globs file */ - file_name = (char *)malloc (strlen (directory) + strlen ("/mime/globs") + 1); - strcpy (file_name, directory); strcat (file_name, "/mime/globs"); - invalid = xdg_check_file (file_name); - free (file_name); - if (invalid) + /* Check the globs file */ + file_name = (char *)malloc(strlen(directory) + strlen("/mime/globs") + 1); + strcpy(file_name, directory); + strcat(file_name, "/mime/globs"); + invalid = xdg_check_file(file_name); + free(file_name); + if (invalid) { - *invalid_dir_list = TRUE; - return TRUE; + *invalid_dir_list = TRUE; + return TRUE; } - /* Check the magic file */ - file_name = (char *)malloc (strlen (directory) + strlen ("/mime/magic") + 1); - strcpy (file_name, directory); strcat (file_name, "/mime/magic"); - invalid = xdg_check_file (file_name); - free (file_name); - if (invalid) + /* Check the magic file */ + file_name = (char *)malloc(strlen(directory) + strlen("/mime/magic") + 1); + strcpy(file_name, directory); + strcat(file_name, "/mime/magic"); + invalid = xdg_check_file(file_name); + free(file_name); + if (invalid) { - *invalid_dir_list = TRUE; - return TRUE; + *invalid_dir_list = TRUE; + return TRUE; } - return FALSE; /* Keep processing */ + return FALSE; /* Keep processing */ } /* Walks through all the mime files stat()ing them to see if they've changed. * Returns TRUE if they have. */ static int -xdg_check_dirs (void) +xdg_check_dirs(void) { - XdgDirTimeList *list; - int invalid_dir_list = FALSE; + XdgDirTimeList *list; + int invalid_dir_list = FALSE; - for (list = dir_time_list; list; list = list->next) - list->checked = XDG_CHECKED_UNCHECKED; + for (list = dir_time_list; list; list = list->next) + list->checked = XDG_CHECKED_UNCHECKED; - xdg_run_command_on_dirs ((XdgDirectoryFunc) xdg_check_dir, - &invalid_dir_list); + xdg_run_command_on_dirs((XdgDirectoryFunc) xdg_check_dir, + &invalid_dir_list); - if (invalid_dir_list) - return TRUE; + if (invalid_dir_list) + return TRUE; - for (list = dir_time_list; list; list = list->next) + for (list = dir_time_list; list; list = list->next) { - if (list->checked != XDG_CHECKED_VALID) - return TRUE; + if (list->checked != XDG_CHECKED_VALID) + return TRUE; } - return FALSE; + return FALSE; } /* We want to avoid stat()ing on every single mime call, so we only look for @@ -345,372 +351,372 @@ xdg_check_dirs (void) * mime data from disk. */ static int -xdg_check_time_and_dirs (void) +xdg_check_time_and_dirs(void) { - struct timeval tv; - time_t current_time; - int retval = FALSE; + struct timeval tv; + time_t current_time; + int retval = FALSE; - gettimeofday (&tv, NULL); - current_time = tv.tv_sec; + gettimeofday(&tv, NULL); + current_time = tv.tv_sec; - if (current_time >= last_stat_time + 5) + if (current_time >= last_stat_time + 5) { - retval = xdg_check_dirs (); - last_stat_time = current_time; + retval = xdg_check_dirs(); + last_stat_time = current_time; } - return retval; + return retval; } /* Called in every public function. It reloads the hash function if need be. */ static void -xdg_mime_init (void) +xdg_mime_init(void) { - if (xdg_check_time_and_dirs ()) + if (xdg_check_time_and_dirs()) { - xdg_mime_shutdown (); + xdg_mime_shutdown(); } - if (need_reread) + if (need_reread) { - global_hash = _xdg_glob_hash_new (); - global_magic = _xdg_mime_magic_new (); - alias_list = _xdg_mime_alias_list_new (); - parent_list = _xdg_mime_parent_list_new (); + global_hash = _xdg_glob_hash_new(); + global_magic = _xdg_mime_magic_new(); + alias_list = _xdg_mime_alias_list_new(); + parent_list = _xdg_mime_parent_list_new(); - xdg_run_command_on_dirs ((XdgDirectoryFunc) xdg_mime_init_from_directory, - NULL); + xdg_run_command_on_dirs((XdgDirectoryFunc) xdg_mime_init_from_directory, + NULL); - need_reread = FALSE; + need_reread = FALSE; } } const char * -xdg_mime_get_mime_type_for_data (const void *data, - size_t len) +xdg_mime_get_mime_type_for_data(const void *data, + size_t len) { - const char *mime_type; + const char *mime_type; - xdg_mime_init (); + xdg_mime_init(); - mime_type = _xdg_mime_magic_lookup_data (global_magic, data, len); + mime_type = _xdg_mime_magic_lookup_data(global_magic, data, len); - if (mime_type) - return mime_type; + if (mime_type) + return mime_type; - return XDG_MIME_TYPE_UNKNOWN; + return XDG_MIME_TYPE_UNKNOWN; } const char * -xdg_mime_get_mime_type_for_file (const char *file_name) +xdg_mime_get_mime_type_for_file(const char *file_name) { - const char *mime_type; - FILE *file; - unsigned char *data; - int max_extent; - int bytes_read; - struct stat statbuf; - const char *base_name; + const char *mime_type; + FILE *file; + unsigned char *data; + int max_extent; + int bytes_read; + struct stat statbuf; + const char *base_name; - if (file_name == NULL) - return NULL; - if (! _xdg_utf8_validate (file_name)) - return NULL; + if (file_name == NULL) + return NULL; + if (! _xdg_utf8_validate(file_name)) + return NULL; - xdg_mime_init (); + xdg_mime_init(); - base_name = _xdg_get_base_name (file_name); - mime_type = xdg_mime_get_mime_type_from_file_name (base_name); + base_name = _xdg_get_base_name(file_name); + mime_type = xdg_mime_get_mime_type_from_file_name(base_name); - if (mime_type != XDG_MIME_TYPE_UNKNOWN) - return mime_type; + if (mime_type != XDG_MIME_TYPE_UNKNOWN) + return mime_type; - if (stat (file_name, &statbuf) != 0) - return XDG_MIME_TYPE_UNKNOWN; + if (stat(file_name, &statbuf) != 0) + return XDG_MIME_TYPE_UNKNOWN; - if (!S_ISREG (statbuf.st_mode)) - return XDG_MIME_TYPE_UNKNOWN; + if (!S_ISREG(statbuf.st_mode)) + return XDG_MIME_TYPE_UNKNOWN; - /* FIXME: Need to make sure that max_extent isn't totally broken. This could - * be large and need getting from a stream instead of just reading it all - * in. */ - max_extent = _xdg_mime_magic_get_buffer_extents (global_magic); - data = (unsigned char *)malloc (max_extent); - if (data == NULL) - return XDG_MIME_TYPE_UNKNOWN; + /* FIXME: Need to make sure that max_extent isn't totally broken. This could + * be large and need getting from a stream instead of just reading it all + * in. */ + max_extent = _xdg_mime_magic_get_buffer_extents(global_magic); + data = (unsigned char *)malloc(max_extent); + if (data == NULL) + return XDG_MIME_TYPE_UNKNOWN; - /* OK to not use CLO_EXEC here because mimedb is single threaded */ - file = fopen (file_name, "r"); - if (file == NULL) + /* OK to not use CLO_EXEC here because mimedb is single threaded */ + file = fopen(file_name, "r"); + if (file == NULL) { - free (data); - return XDG_MIME_TYPE_UNKNOWN; + free(data); + return XDG_MIME_TYPE_UNKNOWN; } - bytes_read = fread (data, 1, max_extent, file); - if (ferror (file)) + bytes_read = fread(data, 1, max_extent, file); + if (ferror(file)) { - free (data); - fclose (file); - return XDG_MIME_TYPE_UNKNOWN; + free(data); + fclose(file); + return XDG_MIME_TYPE_UNKNOWN; } - mime_type = _xdg_mime_magic_lookup_data (global_magic, data, bytes_read); + mime_type = _xdg_mime_magic_lookup_data(global_magic, data, bytes_read); - free (data); - fclose (file); + free(data); + fclose(file); - if (mime_type) - return mime_type; + if (mime_type) + return mime_type; - return XDG_MIME_TYPE_UNKNOWN; + return XDG_MIME_TYPE_UNKNOWN; } const char * -xdg_mime_get_mime_type_from_file_name (const char *file_name) +xdg_mime_get_mime_type_from_file_name(const char *file_name) { - const char *mime_type; + const char *mime_type; - xdg_mime_init (); + xdg_mime_init(); - mime_type = _xdg_glob_hash_lookup_file_name (global_hash, file_name); - if (mime_type) - return mime_type; - else - return XDG_MIME_TYPE_UNKNOWN; + mime_type = _xdg_glob_hash_lookup_file_name(global_hash, file_name); + if (mime_type) + return mime_type; + else + return XDG_MIME_TYPE_UNKNOWN; } int -xdg_mime_is_valid_mime_type (const char *mime_type) +xdg_mime_is_valid_mime_type(const char *mime_type) { - /* FIXME: We should make this a better test - */ - return _xdg_utf8_validate (mime_type); + /* FIXME: We should make this a better test + */ + return _xdg_utf8_validate(mime_type); } void -xdg_mime_shutdown (void) +xdg_mime_shutdown(void) { - XdgCallbackList *list; + XdgCallbackList *list; - /* FIXME: Need to make this (and the whole library) thread safe */ - if (dir_time_list) + /* FIXME: Need to make this (and the whole library) thread safe */ + if (dir_time_list) { - xdg_dir_time_list_free (dir_time_list); - dir_time_list = NULL; + xdg_dir_time_list_free(dir_time_list); + dir_time_list = NULL; } - if (global_hash) + if (global_hash) { - _xdg_glob_hash_free (global_hash); - global_hash = NULL; + _xdg_glob_hash_free(global_hash); + global_hash = NULL; } - if (global_magic) + if (global_magic) { - _xdg_mime_magic_free (global_magic); - global_magic = NULL; + _xdg_mime_magic_free(global_magic); + global_magic = NULL; } - if (alias_list) + if (alias_list) { - _xdg_mime_alias_list_free (alias_list); - alias_list = NULL; + _xdg_mime_alias_list_free(alias_list); + alias_list = NULL; } - if( parent_list ) - { - _xdg_mime_parent_list_free ( parent_list); - } + if (parent_list) + { + _xdg_mime_parent_list_free(parent_list); + } - for (list = callback_list; list; list = list->next) - (list->callback) (list->data); + for (list = callback_list; list; list = list->next) + (list->callback)(list->data); - need_reread = TRUE; + need_reread = TRUE; } int -xdg_mime_get_max_buffer_extents (void) +xdg_mime_get_max_buffer_extents(void) { - xdg_mime_init (); + xdg_mime_init(); - return _xdg_mime_magic_get_buffer_extents (global_magic); + return _xdg_mime_magic_get_buffer_extents(global_magic); } const char * -xdg_mime_unalias_mime_type (const char *mime_type) +xdg_mime_unalias_mime_type(const char *mime_type) { - const char *lookup; + const char *lookup; - xdg_mime_init (); + xdg_mime_init(); - if ((lookup = _xdg_mime_alias_list_lookup (alias_list, mime_type)) != NULL) - return lookup; + if ((lookup = _xdg_mime_alias_list_lookup(alias_list, mime_type)) != NULL) + return lookup; - return mime_type; + return mime_type; } int -xdg_mime_mime_type_equal (const char *mime_a, - const char *mime_b) +xdg_mime_mime_type_equal(const char *mime_a, + const char *mime_b) { - const char *unalias_a, *unalias_b; + const char *unalias_a, *unalias_b; - xdg_mime_init (); + xdg_mime_init(); - unalias_a = xdg_mime_unalias_mime_type (mime_a); - unalias_b = xdg_mime_unalias_mime_type (mime_b); + unalias_a = xdg_mime_unalias_mime_type(mime_a); + unalias_b = xdg_mime_unalias_mime_type(mime_b); - if (strcmp (unalias_a, unalias_b) == 0) - return 1; + if (strcmp(unalias_a, unalias_b) == 0) + return 1; - return 0; + return 0; } int -xdg_mime_media_type_equal (const char *mime_a, - const char *mime_b) +xdg_mime_media_type_equal(const char *mime_a, + const char *mime_b) { - char *sep; + char *sep; - xdg_mime_init (); + xdg_mime_init(); - sep = const_cast(strchr (mime_a, '/')); + sep = const_cast(strchr(mime_a, '/')); - if (sep && strncmp (mime_a, mime_b, sep - mime_a + 1) == 0) - return 1; + if (sep && strncmp(mime_a, mime_b, sep - mime_a + 1) == 0) + return 1; - return 0; + return 0; } #if 0 static int -xdg_mime_is_super_type (const char *mime) +xdg_mime_is_super_type(const char *mime) { - int length; - const char *type; + int length; + const char *type; - length = strlen (mime); - type = &(mime[length - 2]); + length = strlen(mime); + type = &(mime[length - 2]); - if (strcmp (type, "/*") == 0) - return 1; + if (strcmp(type, "/*") == 0) + return 1; - return 0; + return 0; } #endif int -xdg_mime_mime_type_subclass (const char *mime, - const char *base) +xdg_mime_mime_type_subclass(const char *mime, + const char *base) { - const char *umime, *ubase; - const char **parents; + const char *umime, *ubase; + const char **parents; - xdg_mime_init (); + xdg_mime_init(); - umime = xdg_mime_unalias_mime_type (mime); - ubase = xdg_mime_unalias_mime_type (base); + umime = xdg_mime_unalias_mime_type(mime); + ubase = xdg_mime_unalias_mime_type(base); - if (strcmp (umime, ubase) == 0) - return 1; + if (strcmp(umime, ubase) == 0) + return 1; #if 0 - /* Handle supertypes */ - if (xdg_mime_is_super_type (ubase) && - xdg_mime_media_type_equal (umime, ubase)) - return 1; + /* Handle supertypes */ + if (xdg_mime_is_super_type(ubase) && + xdg_mime_media_type_equal(umime, ubase)) + return 1; #endif - /* Handle special cases text/plain and application/octet-stream */ - if (strcmp (ubase, "text/plain") == 0 && - strncmp (umime, "text/", 5) == 0) - return 1; + /* Handle special cases text/plain and application/octet-stream */ + if (strcmp(ubase, "text/plain") == 0 && + strncmp(umime, "text/", 5) == 0) + return 1; - if (strcmp (ubase, "application/octet-stream") == 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++) + parents = _xdg_mime_parent_list_lookup(parent_list, umime); + for (; parents && *parents; parents++) { - if (xdg_mime_mime_type_subclass (*parents, ubase)) - return 1; + if (xdg_mime_mime_type_subclass(*parents, ubase)) + return 1; } - return 0; + return 0; } const char ** -xdg_mime_get_mime_parents (const char *mime) +xdg_mime_get_mime_parents(const char *mime) { - const char *umime; + const char *umime; - xdg_mime_init (); + xdg_mime_init(); - umime = xdg_mime_unalias_mime_type (mime); + umime = xdg_mime_unalias_mime_type(mime); - return _xdg_mime_parent_list_lookup (parent_list, umime); + return _xdg_mime_parent_list_lookup(parent_list, umime); } void -xdg_mime_dump (void) +xdg_mime_dump(void) { - printf ("*** ALIASES ***\n\n"); - _xdg_mime_alias_list_dump (alias_list); - printf ("\n*** PARENTS ***\n\n"); - _xdg_mime_parent_list_dump (parent_list); + printf("*** ALIASES ***\n\n"); + _xdg_mime_alias_list_dump(alias_list); + printf("\n*** PARENTS ***\n\n"); + _xdg_mime_parent_list_dump(parent_list); } /* Registers a function to be called every time the mime database reloads its files */ int -xdg_mime_register_reload_callback (XdgMimeCallback callback, - void *data, - XdgMimeDestroy destroy) +xdg_mime_register_reload_callback(XdgMimeCallback callback, + void *data, + XdgMimeDestroy destroy) { - XdgCallbackList *list_el; - static int callback_id = 1; + XdgCallbackList *list_el; + static int callback_id = 1; - /* Make a new list element */ - list_el = (XdgCallbackList *)calloc (1, sizeof (XdgCallbackList)); - list_el->callback_id = callback_id; - list_el->callback = callback; - list_el->data = data; - list_el->destroy = destroy; - list_el->next = callback_list; - if (list_el->next) - list_el->next->prev = list_el; + /* Make a new list element */ + list_el = (XdgCallbackList *)calloc(1, sizeof(XdgCallbackList)); + list_el->callback_id = callback_id; + list_el->callback = callback; + list_el->data = data; + list_el->destroy = destroy; + list_el->next = callback_list; + if (list_el->next) + list_el->next->prev = list_el; - callback_list = list_el; - callback_id ++; + callback_list = list_el; + callback_id ++; - return callback_id - 1; + return callback_id - 1; } void -xdg_mime_remove_callback (int callback_id) +xdg_mime_remove_callback(int callback_id) { - XdgCallbackList *list; + XdgCallbackList *list; - for (list = callback_list; list; list = list->next) + for (list = callback_list; list; list = list->next) { - if (list->callback_id == callback_id) - { - if (list->next) - list->next = list->prev; + if (list->callback_id == callback_id) + { + 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 36c61338f..b552be7b7 100644 --- a/xdgmime.h +++ b/xdgmime.h @@ -41,8 +41,8 @@ extern "C" { #define _XDG_ENTRY3(prefix,func) prefix##_##func #endif -typedef void (*XdgMimeCallback) (void *user_data); -typedef void (*XdgMimeDestroy) (void *user_data); + typedef void (*XdgMimeCallback)(void *user_data); + typedef void (*XdgMimeDestroy)(void *user_data); #ifdef XDG_PREFIX @@ -62,29 +62,29 @@ typedef void (*XdgMimeDestroy) (void *user_data); #define xdg_mime_type_unknown XDG_ENTRY(type_unknown) #endif -extern const char *xdg_mime_type_unknown; + 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); -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); -int xdg_mime_media_type_equal (const char *mime_a, - 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); -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 xdg_mime_remove_callback (int callback_id); + const char *xdg_mime_get_mime_type_for_data(const void *data, + 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); + int xdg_mime_media_type_equal(const char *mime_a, + 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); + 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 xdg_mime_remove_callback(int callback_id); #ifdef __cplusplus } diff --git a/xdgmimealias.cpp b/xdgmimealias.cpp index 4bb411eff..05a5e3579 100644 --- a/xdgmimealias.cpp +++ b/xdgmimealias.cpp @@ -49,136 +49,136 @@ typedef struct XdgAlias XdgAlias; struct XdgAlias { - char *alias; - char *mime_type; + char *alias; + char *mime_type; }; struct XdgAliasList { - struct XdgAlias *aliases; - int n_aliases; + struct XdgAlias *aliases; + int n_aliases; }; XdgAliasList * -_xdg_mime_alias_list_new (void) +_xdg_mime_alias_list_new(void) { - XdgAliasList *list; + XdgAliasList *list; - list = (XdgAliasList *)malloc (sizeof (XdgAliasList)); + list = (XdgAliasList *)malloc(sizeof(XdgAliasList)); - list->aliases = NULL; - list->n_aliases = 0; + list->aliases = NULL; + list->n_aliases = 0; - return list; + return list; } void -_xdg_mime_alias_list_free (XdgAliasList *list) +_xdg_mime_alias_list_free(XdgAliasList *list) { - int i; + int i; - if (list->aliases) + 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); + for (i = 0; i < list->n_aliases; i++) + { + free(list->aliases[i].alias); + free(list->aliases[i].mime_type); + } + free(list->aliases); } - free (list); + free(list); } static int -alias_entry_cmp (const void *v1, const void *v2) +alias_entry_cmp(const void *v1, const void *v2) { - return strcmp (((XdgAlias *)v1)->alias, ((XdgAlias *)v2)->alias); + return strcmp(((XdgAlias *)v1)->alias, ((XdgAlias *)v2)->alias); } const char * -_xdg_mime_alias_list_lookup (XdgAliasList *list, - const char *alias) +_xdg_mime_alias_list_lookup(XdgAliasList *list, + const char *alias) { - XdgAlias *entry; - XdgAlias key; + XdgAlias *entry; + XdgAlias key; - if (list->n_aliases > 0) + if (list->n_aliases > 0) { - key.alias = (char *)alias; - key.mime_type = 0; + key.alias = (char *)alias; + key.mime_type = 0; - entry = (XdgAlias *)bsearch (&key, list->aliases, list->n_aliases, - sizeof (XdgAlias), alias_entry_cmp); - if (entry) - return entry->mime_type; + entry = (XdgAlias *)bsearch(&key, list->aliases, list->n_aliases, + sizeof(XdgAlias), alias_entry_cmp); + if (entry) + return entry->mime_type; } - return NULL; + return NULL; } void -_xdg_mime_alias_read_from_file (XdgAliasList *list, - const char *file_name) +_xdg_mime_alias_read_from_file(XdgAliasList *list, + const char *file_name) { - FILE *file; - char line[255]; - int alloc; + FILE *file; + char line[255]; + int alloc; - /* OK to not use CLO_EXEC here because mimedb is single threaded */ - file = fopen (file_name, "r"); + /* OK to not use CLO_EXEC here because mimedb is single threaded */ + file = fopen(file_name, "r"); - if (file == NULL) - return; + if (file == NULL) + return; - /* FIXME: Not UTF-8 safe. Doesn't work if lines are greater than 255 chars. - * Blah */ - alloc = list->n_aliases + 16; - list->aliases = (XdgAlias *)realloc (list->aliases, alloc * sizeof (XdgAlias)); - while (fgets (line, 255, file) != NULL) + /* FIXME: Not UTF-8 safe. Doesn't work if lines are greater than 255 chars. + * Blah */ + alloc = list->n_aliases + 16; + list->aliases = (XdgAlias *)realloc(list->aliases, alloc * sizeof(XdgAlias)); + while (fgets(line, 255, file) != NULL) { - char *sep; - if (line[0] == '#') - continue; + char *sep; + if (line[0] == '#') + continue; - sep = strchr (line, ' '); - if (sep == NULL) - continue; - *(sep++) = '\000'; - sep[strlen (sep) -1] = '\000'; - if (list->n_aliases == alloc) - { - 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++; + sep = strchr(line, ' '); + if (sep == NULL) + continue; + *(sep++) = '\000'; + sep[strlen(sep) -1] = '\000'; + if (list->n_aliases == alloc) + { + 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, - sizeof (XdgAlias), alias_entry_cmp); + if (list->n_aliases > 1) + qsort(list->aliases, list->n_aliases, + sizeof(XdgAlias), alias_entry_cmp); } void -_xdg_mime_alias_list_dump (XdgAliasList *list) +_xdg_mime_alias_list_dump(XdgAliasList *list) { - int i; + int i; - if (list->aliases) + if (list->aliases) { - for (i = 0; i < list->n_aliases; i++) - { - printf ("%s %s\n", - list->aliases[i].alias, - list->aliases[i].mime_type); - } + for (i = 0; i < list->n_aliases; i++) + { + printf("%s %s\n", + list->aliases[i].alias, + list->aliases[i].mime_type); + } } } diff --git a/xdgmimealias.h b/xdgmimealias.h index d69593f0d..384d12769 100644 --- a/xdgmimealias.h +++ b/xdgmimealias.h @@ -39,12 +39,12 @@ typedef struct XdgAliasList XdgAliasList; #define _xdg_mime_alias_list_lookup XDG_ENTRY(alias_list_lookup) #endif -void _xdg_mime_alias_read_from_file (XdgAliasList *list, - 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); -void _xdg_mime_alias_list_dump (XdgAliasList *list); +void _xdg_mime_alias_read_from_file(XdgAliasList *list, + 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); +void _xdg_mime_alias_list_dump(XdgAliasList *list); #endif /* __XDG_MIME_ALIAS_H__ */ diff --git a/xdgmimeglob.cpp b/xdgmimeglob.cpp index 6b6c410eb..01245b5f0 100644 --- a/xdgmimeglob.cpp +++ b/xdgmimeglob.cpp @@ -50,97 +50,97 @@ typedef struct XdgGlobList XdgGlobList; struct XdgGlobHashNode { - xdg_unichar_t character; - const char *mime_type; - XdgGlobHashNode *next; - XdgGlobHashNode *child; + xdg_unichar_t character; + const char *mime_type; + XdgGlobHashNode *next; + XdgGlobHashNode *child; }; struct XdgGlobList { - const char *data; - const char *mime_type; - XdgGlobList *next; + const char *data; + const char *mime_type; + XdgGlobList *next; }; struct XdgGlobHash { - XdgGlobList *literal_list; - XdgGlobHashNode *simple_node; - XdgGlobList *full_list; + XdgGlobList *literal_list; + XdgGlobHashNode *simple_node; + XdgGlobList *full_list; }; /* XdgGlobList */ static XdgGlobList * -_xdg_glob_list_new (void) +_xdg_glob_list_new(void) { - XdgGlobList *new_element; + XdgGlobList *new_element; - new_element = (XdgGlobList *)calloc (1, sizeof (XdgGlobList)); + new_element = (XdgGlobList *)calloc(1, sizeof(XdgGlobList)); - return new_element; + return new_element; } /* Frees glob_list and all of it's children */ static void -_xdg_glob_list_free (XdgGlobList *glob_list) +_xdg_glob_list_free(XdgGlobList *glob_list) { - XdgGlobList *ptr, *next; + XdgGlobList *ptr, *next; - ptr = glob_list; + ptr = glob_list; - while (ptr != NULL) + while (ptr != NULL) { - next = ptr->next; + next = ptr->next; - if (ptr->data) - free ((void *) ptr->data); - if (ptr->mime_type) - free ((void *) ptr->mime_type); - free (ptr); + if (ptr->data) + free((void *) ptr->data); + if (ptr->mime_type) + free((void *) ptr->mime_type); + free(ptr); - ptr = next; + ptr = next; } } static XdgGlobList * -_xdg_glob_list_append (XdgGlobList *glob_list, - void *data, - const char *mime_type) +_xdg_glob_list_append(XdgGlobList *glob_list, + void *data, + const char *mime_type) { - XdgGlobList *new_element; - XdgGlobList *tmp_element; + XdgGlobList *new_element; + XdgGlobList *tmp_element; - new_element = _xdg_glob_list_new (); - new_element->data = (const char *)data; - new_element->mime_type = mime_type; - if (glob_list == NULL) - return new_element; + new_element = _xdg_glob_list_new(); + new_element->data = (const char *)data; + new_element->mime_type = mime_type; + if (glob_list == NULL) + return new_element; - tmp_element = glob_list; - while (tmp_element->next != NULL) - tmp_element = tmp_element->next; + tmp_element = glob_list; + while (tmp_element->next != NULL) + tmp_element = tmp_element->next; - tmp_element->next = new_element; + tmp_element->next = new_element; - return glob_list; + return glob_list; } #if 0 static XdgGlobList * -_xdg_glob_list_prepend (XdgGlobList *glob_list, - void *data, - const char *mime_type) +_xdg_glob_list_prepend(XdgGlobList *glob_list, + void *data, + const char *mime_type) { - XdgGlobList *new_element; + XdgGlobList *new_element; - new_element = _xdg_glob_list_new (); - new_element->data = data; - new_element->next = glob_list; - new_element->mime_type = mime_type; + new_element = _xdg_glob_list_new(); + new_element->data = data; + new_element->next = glob_list; + new_element->mime_type = mime_type; - return new_element; + return new_element; } #endif @@ -148,174 +148,174 @@ _xdg_glob_list_prepend (XdgGlobList *glob_list, */ static XdgGlobHashNode * -_xdg_glob_hash_node_new (void) +_xdg_glob_hash_node_new(void) { - XdgGlobHashNode *glob_hash_node; + XdgGlobHashNode *glob_hash_node; - glob_hash_node = (XdgGlobHashNode *)calloc (1, sizeof (XdgGlobHashNode)); + glob_hash_node = (XdgGlobHashNode *)calloc(1, sizeof(XdgGlobHashNode)); - return glob_hash_node; + return glob_hash_node; } static void -_xdg_glob_hash_node_dump (XdgGlobHashNode *glob_hash_node, - int depth) +_xdg_glob_hash_node_dump(XdgGlobHashNode *glob_hash_node, + int depth) { - int i; - for (i = 0; i < depth; i++) - printf (" "); + int i; + for (i = 0; i < depth; i++) + printf(" "); - printf ("%c", (char)glob_hash_node->character); - if (glob_hash_node->mime_type) - printf (" - %s\n", glob_hash_node->mime_type); - else - printf ("\n"); - if (glob_hash_node->child) - _xdg_glob_hash_node_dump (glob_hash_node->child, depth + 1); - if (glob_hash_node->next) - _xdg_glob_hash_node_dump (glob_hash_node->next, depth); + printf("%c", (char)glob_hash_node->character); + if (glob_hash_node->mime_type) + printf(" - %s\n", glob_hash_node->mime_type); + else + printf("\n"); + if (glob_hash_node->child) + _xdg_glob_hash_node_dump(glob_hash_node->child, depth + 1); + if (glob_hash_node->next) + _xdg_glob_hash_node_dump(glob_hash_node->next, depth); } static XdgGlobHashNode * -_xdg_glob_hash_insert_text (XdgGlobHashNode *glob_hash_node, - const char *text, - const char *mime_type) +_xdg_glob_hash_insert_text(XdgGlobHashNode *glob_hash_node, + const char *text, + const char *mime_type) { - XdgGlobHashNode *node; - xdg_unichar_t character; + XdgGlobHashNode *node; + xdg_unichar_t character; - character = _xdg_utf8_to_ucs4 (text); + character = _xdg_utf8_to_ucs4(text); - if ((glob_hash_node == NULL) || - (character < glob_hash_node->character)) + if ((glob_hash_node == NULL) || + (character < glob_hash_node->character)) { - node = _xdg_glob_hash_node_new (); - node->character = character; - node->next = glob_hash_node; - glob_hash_node = node; - } - else if (character == glob_hash_node->character) - { - node = glob_hash_node; - } - else - { - XdgGlobHashNode *prev_node; - int found_node = FALSE; - - /* Look for the first character of text in glob_hash_node, and insert it if we - * have to.*/ - prev_node = glob_hash_node; - node = prev_node->next; - - while (node != NULL) - { - if (character < node->character) - { - node = _xdg_glob_hash_node_new (); + 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; - } - - if (! found_node) - { - node = _xdg_glob_hash_node_new (); - node->character = character; - node->next = prev_node->next; - prev_node->next = node; - } + node->next = glob_hash_node; + glob_hash_node = node; } - - text = _xdg_utf8_next_char (text); - if (*text == '\000') + else if (character == glob_hash_node->character) { - node->mime_type = mime_type; + node = glob_hash_node; } - else + else { - node->child = _xdg_glob_hash_insert_text (node->child, text, mime_type); + XdgGlobHashNode *prev_node; + int found_node = FALSE; + + /* Look for the first character of text in glob_hash_node, and insert it if we + * have to.*/ + prev_node = 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; + + 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; + } } - return glob_hash_node; + + text = _xdg_utf8_next_char(text); + if (*text == '\000') + { + node->mime_type = mime_type; + } + else + { + node->child = _xdg_glob_hash_insert_text(node->child, text, mime_type); + } + return glob_hash_node; } static const char * -_xdg_glob_hash_node_lookup_file_name (XdgGlobHashNode *glob_hash_node, - const char *file_name, - int ignore_case) +_xdg_glob_hash_node_lookup_file_name(XdgGlobHashNode *glob_hash_node, + const char *file_name, + int ignore_case) { - XdgGlobHashNode *node; - xdg_unichar_t character; + XdgGlobHashNode *node; + xdg_unichar_t character; - if (glob_hash_node == NULL) - return NULL; + if (glob_hash_node == NULL) + return NULL; - character = _xdg_utf8_to_ucs4 (file_name); - if (ignore_case) - character = _xdg_ucs4_to_lower(character); + character = _xdg_utf8_to_ucs4(file_name); + if (ignore_case) + character = _xdg_ucs4_to_lower(character); - for (node = glob_hash_node; node && character >= node->character; node = node->next) + 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); - } + 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); + } } - return NULL; + return NULL; } const char * -_xdg_glob_hash_lookup_file_name (XdgGlobHash *glob_hash, - const char *file_name) +_xdg_glob_hash_lookup_file_name(XdgGlobHash *glob_hash, + const char *file_name) { - XdgGlobList *list; - const char *mime_type; - const char *ptr; - /* First, check the literals */ + XdgGlobList *list; + const char *mime_type; + const char *ptr; + /* First, check the literals */ - assert (file_name != NULL); + assert(file_name != NULL); - for (list = glob_hash->literal_list; list; list = list->next) - if (strcmp ((const char *)list->data, file_name) == 0) - return list->mime_type; + for (list = glob_hash->literal_list; list; list = list->next) + if (strcmp((const char *)list->data, file_name) == 0) + return list->mime_type; - ptr = strchr (file_name, '.'); - while (ptr != NULL) + ptr = strchr(file_name, '.'); + while (ptr != NULL) { - 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, 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; + 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, '.'); + ptr = strchr(ptr+1, '.'); } - /* FIXME: Not UTF-8 safe */ - for (list = glob_hash->full_list; list; list = list->next) - if (fnmatch ((const char *)list->data, file_name, 0) == 0) - return list->mime_type; + /* FIXME: Not UTF-8 safe */ + for (list = glob_hash->full_list; list; list = list->next) + if (fnmatch((const char *)list->data, file_name, 0) == 0) + return list->mime_type; - return NULL; + return NULL; } @@ -324,150 +324,150 @@ _xdg_glob_hash_lookup_file_name (XdgGlobHash *glob_hash, */ XdgGlobHash * -_xdg_glob_hash_new (void) +_xdg_glob_hash_new(void) { - XdgGlobHash *glob_hash; + XdgGlobHash *glob_hash; - glob_hash = (XdgGlobHash *)calloc (1, sizeof (XdgGlobHash)); + glob_hash = (XdgGlobHash *)calloc(1, sizeof(XdgGlobHash)); - return glob_hash; + return glob_hash; } static void -_xdg_glob_hash_free_nodes (XdgGlobHashNode *node) +_xdg_glob_hash_free_nodes(XdgGlobHashNode *node) { - if (node) + if (node) { - if (node->child) - _xdg_glob_hash_free_nodes (node->child); - if (node->next) - _xdg_glob_hash_free_nodes (node->next); - if (node->mime_type) - free ((void *) node->mime_type); - free (node); + if (node->child) + _xdg_glob_hash_free_nodes(node->child); + if (node->next) + _xdg_glob_hash_free_nodes(node->next); + if (node->mime_type) + free((void *) node->mime_type); + free(node); } } void -_xdg_glob_hash_free (XdgGlobHash *glob_hash) +_xdg_glob_hash_free(XdgGlobHash *glob_hash) { - _xdg_glob_list_free (glob_hash->literal_list); - _xdg_glob_list_free (glob_hash->full_list); - _xdg_glob_hash_free_nodes (glob_hash->simple_node); - free (glob_hash); + _xdg_glob_list_free(glob_hash->literal_list); + _xdg_glob_list_free(glob_hash->full_list); + _xdg_glob_hash_free_nodes(glob_hash->simple_node); + free(glob_hash); } XdgGlobType -_xdg_glob_determine_type (const char *glob) +_xdg_glob_determine_type(const char *glob) { - const char *ptr; - int maybe_in_simple_glob = FALSE; - int first_char = TRUE; + const char *ptr; + int maybe_in_simple_glob = FALSE; + int first_char = TRUE; - ptr = glob; + ptr = glob; - while (*ptr != '\000') + while (*ptr != '\000') { - if (*ptr == '*' && first_char) - maybe_in_simple_glob = TRUE; - else if (*ptr == '\\' || *ptr == '[' || *ptr == '?' || *ptr == '*') - return XDG_GLOB_FULL; + if (*ptr == '*' && first_char) + maybe_in_simple_glob = TRUE; + else if (*ptr == '\\' || *ptr == '[' || *ptr == '?' || *ptr == '*') + return XDG_GLOB_FULL; - first_char = FALSE; - ptr = _xdg_utf8_next_char (ptr); + first_char = FALSE; + ptr = _xdg_utf8_next_char(ptr); } - if (maybe_in_simple_glob) - return XDG_GLOB_SIMPLE; - else - return XDG_GLOB_LITERAL; + if (maybe_in_simple_glob) + return XDG_GLOB_SIMPLE; + else + return XDG_GLOB_LITERAL; } /* glob must be valid UTF-8 */ void -_xdg_glob_hash_append_glob (XdgGlobHash *glob_hash, - const char *glob, - const char *mime_type) +_xdg_glob_hash_append_glob(XdgGlobHash *glob_hash, + const char *glob, + const char *mime_type) { - XdgGlobType type; + XdgGlobType type; - assert (glob_hash != NULL); - assert (glob != NULL); + assert(glob_hash != NULL); + assert(glob != NULL); - type = _xdg_glob_determine_type (glob); + type = _xdg_glob_determine_type(glob); - switch (type) + switch (type) { case XDG_GLOB_LITERAL: - glob_hash->literal_list = _xdg_glob_list_append (glob_hash->literal_list, strdup (glob), strdup (mime_type)); - break; + glob_hash->literal_list = _xdg_glob_list_append(glob_hash->literal_list, strdup(glob), strdup(mime_type)); + break; case XDG_GLOB_SIMPLE: - glob_hash->simple_node = _xdg_glob_hash_insert_text (glob_hash->simple_node, glob + 1, strdup (mime_type)); - break; + glob_hash->simple_node = _xdg_glob_hash_insert_text(glob_hash->simple_node, glob + 1, strdup(mime_type)); + break; case XDG_GLOB_FULL: - glob_hash->full_list = _xdg_glob_list_append (glob_hash->full_list, strdup (glob), strdup (mime_type)); - break; + glob_hash->full_list = _xdg_glob_list_append(glob_hash->full_list, strdup(glob), strdup(mime_type)); + break; } } void -_xdg_glob_hash_dump (XdgGlobHash *glob_hash) +_xdg_glob_hash_dump(XdgGlobHash *glob_hash) { - XdgGlobList *list; - printf ("LITERAL STRINGS\n"); - if (glob_hash->literal_list == NULL) + XdgGlobList *list; + printf("LITERAL STRINGS\n"); + if (glob_hash->literal_list == NULL) { - printf (" None\n"); + printf(" None\n"); } - else + else { - for (list = glob_hash->literal_list; list; list = list->next) - printf (" %s - %s\n", (char *)list->data, list->mime_type); + for (list = glob_hash->literal_list; list; list = list->next) + printf(" %s - %s\n", (char *)list->data, list->mime_type); } - printf ("\nSIMPLE GLOBS\n"); - _xdg_glob_hash_node_dump (glob_hash->simple_node, 4); + printf("\nSIMPLE GLOBS\n"); + _xdg_glob_hash_node_dump(glob_hash->simple_node, 4); - printf ("\nFULL GLOBS\n"); - if (glob_hash->full_list == NULL) + printf("\nFULL GLOBS\n"); + if (glob_hash->full_list == NULL) { - printf (" None\n"); + printf(" None\n"); } - else + else { - for (list = glob_hash->full_list; list; list = list->next) - printf (" %s - %s\n", (char *)list->data, list->mime_type); + for (list = glob_hash->full_list; list; list = list->next) + printf(" %s - %s\n", (char *)list->data, list->mime_type); } } void -_xdg_mime_glob_read_from_file (XdgGlobHash *glob_hash, - const char *file_name) +_xdg_mime_glob_read_from_file(XdgGlobHash *glob_hash, + const char *file_name) { - FILE *glob_file; - char line[255]; + FILE *glob_file; + char line[255]; - /* OK to not use CLO_EXEC here because mimedb is single threaded */ - glob_file = fopen (file_name, "r"); + /* OK to not use CLO_EXEC here because mimedb is single threaded */ + glob_file = fopen(file_name, "r"); - if (glob_file == NULL) - return; + if (glob_file == NULL) + return; - /* FIXME: Not UTF-8 safe. Doesn't work if lines are greater than 255 chars. - * Blah */ - while (fgets (line, 255, glob_file) != NULL) + /* FIXME: Not UTF-8 safe. Doesn't work if lines are greater than 255 chars. + * Blah */ + while (fgets(line, 255, glob_file) != NULL) { - char *colon; - if (line[0] == '#') - continue; + char *colon; + if (line[0] == '#') + continue; - colon = strchr (line, ':'); - if (colon == NULL) - continue; - *(colon++) = '\000'; - colon[strlen (colon) -1] = '\000'; - _xdg_glob_hash_append_glob (glob_hash, colon, line); + colon = strchr(line, ':'); + if (colon == NULL) + continue; + *(colon++) = '\000'; + colon[strlen(colon) -1] = '\000'; + _xdg_glob_hash_append_glob(glob_hash, colon, line); } - fclose (glob_file); + fclose(glob_file); } diff --git a/xdgmimeglob.h b/xdgmimeglob.h index 2e6a577d5..7f2ea1d3f 100644 --- a/xdgmimeglob.h +++ b/xdgmimeglob.h @@ -34,9 +34,9 @@ typedef struct XdgGlobHash XdgGlobHash; typedef enum { - XDG_GLOB_LITERAL, /* Makefile */ - XDG_GLOB_SIMPLE, /* *.gif */ - XDG_GLOB_FULL /* x*.[ch] */ + XDG_GLOB_LITERAL, /* Makefile */ + XDG_GLOB_SIMPLE, /* *.gif */ + XDG_GLOB_FULL /* x*.[ch] */ } XdgGlobType; @@ -50,16 +50,16 @@ typedef enum #define _xdg_glob_hash_dump XDG_ENTRY(hash_dump) #endif -void _xdg_mime_glob_read_from_file (XdgGlobHash *glob_hash, - 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); -void _xdg_glob_hash_append_glob (XdgGlobHash *glob_hash, - const char *glob, - const char *mime_type); -XdgGlobType _xdg_glob_determine_type (const char *glob); -void _xdg_glob_hash_dump (XdgGlobHash *glob_hash); +void _xdg_mime_glob_read_from_file(XdgGlobHash *glob_hash, + 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); +void _xdg_glob_hash_append_glob(XdgGlobHash *glob_hash, + const char *glob, + const char *mime_type); +XdgGlobType _xdg_glob_determine_type(const char *glob); +void _xdg_glob_hash_dump(XdgGlobHash *glob_hash); #endif /* __XDG_MIME_GLOB_H__ */ diff --git a/xdgmimeint.cpp b/xdgmimeint.cpp index 72e1d78fe..a7847141a 100644 --- a/xdgmimeint.cpp +++ b/xdgmimeint.cpp @@ -41,15 +41,16 @@ #define TRUE (!FALSE) #endif -static const char _xdg_utf8_skip_data[256] = { - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, - 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, - 3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,4,4,4,4,4,4,4,4,5,5,5,5,6,6,1,1 +static const char _xdg_utf8_skip_data[256] = +{ + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,4,4,4,4,4,4,4,4,5,5,5,5,6,6,1,1 }; const char * const _xdg_utf8_skip = _xdg_utf8_skip_data; @@ -60,95 +61,95 @@ const char * const _xdg_utf8_skip = _xdg_utf8_skip_data; xdg_unichar_t _xdg_utf8_to_ucs4(const char *source) { - xdg_unichar_t ucs32; - if( ! ( *source & 0x80 ) ) + xdg_unichar_t ucs32; + if (!(*source & 0x80)) { - ucs32 = *source; + ucs32 = *source; } - else - { - int bytelength = 0; - xdg_unichar_t result; - if ( ! (*source & 0x40) ) - { - 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; - } + { + int bytelength = 0; + xdg_unichar_t result; + if (!(*source & 0x40)) + { + 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; + } - 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; + return ucs32; } /* hullo. this is great code. don't rewrite it */ xdg_unichar_t -_xdg_ucs4_to_lower (xdg_unichar_t source) +_xdg_ucs4_to_lower(xdg_unichar_t source) { - /* FIXME: Do a real to_upper sometime */ - /* CaseFolding-3.2.0.txt has a table of rules. */ - if ((source & 0xFF) == source) - return (xdg_unichar_t) tolower ((unsigned char) source); - return source; + /* FIXME: Do a real to_upper sometime */ + /* CaseFolding-3.2.0.txt has a table of rules. */ + if ((source & 0xFF) == source) + return (xdg_unichar_t) tolower((unsigned char) source); + return source; } int -_xdg_utf8_validate (const char *source) +_xdg_utf8_validate(const char *source) { - /* FIXME: actually write */ - return TRUE; + /* FIXME: actually write */ + return TRUE; } const char * -_xdg_get_base_name (const char *file_name) +_xdg_get_base_name(const char *file_name) { - const char *base_name; + const char *base_name; - if (file_name == NULL) - return NULL; + if (file_name == NULL) + return NULL; - base_name = strrchr (file_name, '/'); + base_name = strrchr(file_name, '/'); - if (base_name == NULL) - return file_name; - else - return base_name + 1; + if (base_name == NULL) + return file_name; + else + return base_name + 1; } diff --git a/xdgmimeint.h b/xdgmimeint.h index 4aa9d1a2c..33116df1d 100644 --- a/xdgmimeint.h +++ b/xdgmimeint.h @@ -65,9 +65,9 @@ extern const char *const _xdg_utf8_skip; #define _xdg_utf8_next_char(p) (char *)((p) + _xdg_utf8_skip[*(unsigned char *)(p)]) #define _xdg_utf8_char_size(p) (int) (_xdg_utf8_skip[*(unsigned char *)(p)]) -xdg_unichar_t _xdg_utf8_to_ucs4 (const char *source); -xdg_unichar_t _xdg_ucs4_to_lower (xdg_unichar_t source); -int _xdg_utf8_validate (const char *source); -const char *_xdg_get_base_name (const char *file_name); +xdg_unichar_t _xdg_utf8_to_ucs4(const char *source); +xdg_unichar_t _xdg_ucs4_to_lower(xdg_unichar_t source); +int _xdg_utf8_validate(const char *source); +const char *_xdg_get_base_name(const char *file_name); #endif /* __XDG_MIME_INT_H__ */ diff --git a/xdgmimemagic.cpp b/xdgmimemagic.cpp index 5caf10433..8074cc433 100644 --- a/xdgmimemagic.cpp +++ b/xdgmimemagic.cpp @@ -54,79 +54,79 @@ typedef struct XdgMimeMagicMatchlet XdgMimeMagicMatchlet; typedef enum { - XDG_MIME_MAGIC_SECTION, - XDG_MIME_MAGIC_MAGIC, - XDG_MIME_MAGIC_ERROR, - XDG_MIME_MAGIC_EOF + XDG_MIME_MAGIC_SECTION, + XDG_MIME_MAGIC_MAGIC, + XDG_MIME_MAGIC_ERROR, + XDG_MIME_MAGIC_EOF } XdgMimeMagicState; struct XdgMimeMagicMatch { - const char *mime_type; - int priority; - XdgMimeMagicMatchlet *matchlet; - XdgMimeMagicMatch *next; + const char *mime_type; + int priority; + XdgMimeMagicMatchlet *matchlet; + XdgMimeMagicMatch *next; }; struct XdgMimeMagicMatchlet { - int indent; - int offset; - unsigned int value_length; - unsigned char *value; - unsigned char *mask; - unsigned int range_length; - unsigned int word_size; - XdgMimeMagicMatchlet *next; + int indent; + int offset; + unsigned int value_length; + unsigned char *value; + unsigned char *mask; + unsigned int range_length; + unsigned int word_size; + XdgMimeMagicMatchlet *next; }; struct XdgMimeMagic { - XdgMimeMagicMatch *match_list; - int max_extent; + XdgMimeMagicMatch *match_list; + int max_extent; }; static XdgMimeMagicMatch * -_xdg_mime_magic_match_new (void) +_xdg_mime_magic_match_new(void) { - return (XdgMimeMagicMatch *)calloc (1, sizeof (XdgMimeMagicMatch)); + return (XdgMimeMagicMatch *)calloc(1, sizeof(XdgMimeMagicMatch)); } static XdgMimeMagicMatchlet * -_xdg_mime_magic_matchlet_new (void) +_xdg_mime_magic_matchlet_new(void) { - XdgMimeMagicMatchlet *matchlet; + XdgMimeMagicMatchlet *matchlet; - matchlet = (XdgMimeMagicMatchlet *)malloc (sizeof (XdgMimeMagicMatchlet)); + matchlet = (XdgMimeMagicMatchlet *)malloc(sizeof(XdgMimeMagicMatchlet)); - matchlet->indent = 0; - matchlet->offset = 0; - matchlet->value_length = 0; - matchlet->value = NULL; - matchlet->mask = NULL; - matchlet->range_length = 1; - matchlet->word_size = 1; - matchlet->next = NULL; + matchlet->indent = 0; + matchlet->offset = 0; + matchlet->value_length = 0; + matchlet->value = NULL; + matchlet->mask = NULL; + matchlet->range_length = 1; + matchlet->word_size = 1; + matchlet->next = NULL; - return matchlet; + return matchlet; } static void -_xdg_mime_magic_matchlet_free (XdgMimeMagicMatchlet *mime_magic_matchlet) +_xdg_mime_magic_matchlet_free(XdgMimeMagicMatchlet *mime_magic_matchlet) { - if (mime_magic_matchlet) + if (mime_magic_matchlet) { - if (mime_magic_matchlet->next) - _xdg_mime_magic_matchlet_free (mime_magic_matchlet->next); - if (mime_magic_matchlet->value) - free (mime_magic_matchlet->value); - if (mime_magic_matchlet->mask) - free (mime_magic_matchlet->mask); - free (mime_magic_matchlet); + if (mime_magic_matchlet->next) + _xdg_mime_magic_matchlet_free(mime_magic_matchlet->next); + if (mime_magic_matchlet->value) + free(mime_magic_matchlet->value); + if (mime_magic_matchlet->mask) + free(mime_magic_matchlet->mask); + free(mime_magic_matchlet); } } @@ -134,22 +134,22 @@ _xdg_mime_magic_matchlet_free (XdgMimeMagicMatchlet *mime_magic_matchlet) /* Frees mime_magic_match and the remainder of its list */ static void -_xdg_mime_magic_match_free (XdgMimeMagicMatch *mime_magic_match) +_xdg_mime_magic_match_free(XdgMimeMagicMatch *mime_magic_match) { - XdgMimeMagicMatch *ptr, *next; + XdgMimeMagicMatch *ptr, *next; - ptr = mime_magic_match; - while (ptr) + ptr = mime_magic_match; + while (ptr) { - next = ptr->next; + next = ptr->next; - if (ptr->mime_type) - free ((void *) ptr->mime_type); - if (ptr->matchlet) - _xdg_mime_magic_matchlet_free (ptr->matchlet); - free (ptr); + if (ptr->mime_type) + free((void *) ptr->mime_type); + if (ptr->matchlet) + _xdg_mime_magic_matchlet_free(ptr->matchlet); + free(ptr); - ptr = next; + ptr = next; } } @@ -157,149 +157,149 @@ _xdg_mime_magic_match_free (XdgMimeMagicMatch *mime_magic_match) * returned string is null terminated, and doesn't include the newline. */ static char * -_xdg_mime_magic_read_to_newline (FILE *magic_file, - int *end_of_file) +_xdg_mime_magic_read_to_newline(FILE *magic_file, + int *end_of_file) { - char *retval; - int c; - int len, pos; + char *retval; + int c; + int len, pos; - len = 128; - pos = 0; - retval = (char *)malloc (len); - *end_of_file = FALSE; + len = 128; + pos = 0; + retval = (char *)malloc(len); + *end_of_file = FALSE; - while (TRUE) + while (TRUE) { - c = getc_unlocked (magic_file); - if (c == EOF) - { - *end_of_file = TRUE; - break; - } - if (c == '\n' || c == '\000') - break; - retval[pos++] = (char) c; - if (pos % 128 == 127) - { - len = len + 128; - retval = (char *)realloc (retval, len); - } + c = getc_unlocked(magic_file); + if (c == EOF) + { + *end_of_file = TRUE; + break; + } + if (c == '\n' || c == '\000') + break; + retval[pos++] = (char) c; + if (pos % 128 == 127) + { + len = len + 128; + retval = (char *)realloc(retval, len); + } } - retval[pos] = '\000'; - return retval; + retval[pos] = '\000'; + return retval; } /* Returns the number read from the file, or -1 if no number could be read. */ static int -_xdg_mime_magic_read_a_number (FILE *magic_file, - int *end_of_file) +_xdg_mime_magic_read_a_number(FILE *magic_file, + int *end_of_file) { - /* LONG_MAX is about 20 characters on my system */ + /* LONG_MAX is about 20 characters on my system */ #define MAX_NUMBER_SIZE 30 - char number_string[MAX_NUMBER_SIZE + 1]; - int pos = 0; - int c; - long retval = -1; + char number_string[MAX_NUMBER_SIZE + 1]; + int pos = 0; + int c; + long retval = -1; - while (TRUE) + while (TRUE) { - c = getc_unlocked (magic_file); + c = getc_unlocked(magic_file); - if (c == EOF) - { - *end_of_file = TRUE; - break; - } - if (! isdigit (c)) - { - ungetc (c, magic_file); - break; - } - number_string[pos] = (char) c; - pos++; - if (pos == MAX_NUMBER_SIZE) - break; + if (c == EOF) + { + *end_of_file = TRUE; + break; + } + if (! isdigit(c)) + { + ungetc(c, magic_file); + break; + } + number_string[pos] = (char) c; + pos++; + if (pos == MAX_NUMBER_SIZE) + break; } - if (pos > 0) + if (pos > 0) { - number_string[pos] = '\000'; - errno = 0; - retval = strtol (number_string, NULL, 10); + number_string[pos] = '\000'; + errno = 0; + retval = strtol(number_string, NULL, 10); - if ((retval < INT_MIN) || (retval > INT_MAX) || (errno != 0)) - return -1; + if ((retval < INT_MIN) || (retval > INT_MAX) || (errno != 0)) + return -1; } - return retval; + return retval; } /* Headers are of the format: * [:] */ static XdgMimeMagicState -_xdg_mime_magic_parse_header (FILE *magic_file, XdgMimeMagicMatch *match) +_xdg_mime_magic_parse_header(FILE *magic_file, XdgMimeMagicMatch *match) { - int c; - char *buffer; - char *end_ptr; - int end_of_file = 0; + int c; + char *buffer; + char *end_ptr; + int end_of_file = 0; - assert (magic_file != NULL); - assert (match != NULL); + assert(magic_file != NULL); + assert(match != NULL); - c = getc_unlocked (magic_file); - if (c == EOF) - return XDG_MIME_MAGIC_EOF; - if (c != '[') - return XDG_MIME_MAGIC_ERROR; + c = getc_unlocked(magic_file); + if (c == EOF) + return XDG_MIME_MAGIC_EOF; + if (c != '[') + return XDG_MIME_MAGIC_ERROR; - match->priority = _xdg_mime_magic_read_a_number (magic_file, &end_of_file); - if (end_of_file) - return XDG_MIME_MAGIC_EOF; - if (match->priority == -1) - return XDG_MIME_MAGIC_ERROR; + match->priority = _xdg_mime_magic_read_a_number(magic_file, &end_of_file); + if (end_of_file) + return XDG_MIME_MAGIC_EOF; + if (match->priority == -1) + return XDG_MIME_MAGIC_ERROR; - c = getc_unlocked (magic_file); - if (c == EOF) - return XDG_MIME_MAGIC_EOF; - if (c != ':') - return XDG_MIME_MAGIC_ERROR; + c = getc_unlocked(magic_file); + if (c == EOF) + return XDG_MIME_MAGIC_EOF; + if (c != ':') + return XDG_MIME_MAGIC_ERROR; - buffer = _xdg_mime_magic_read_to_newline (magic_file, &end_of_file); - if (end_of_file) - return XDG_MIME_MAGIC_EOF; + buffer = _xdg_mime_magic_read_to_newline(magic_file, &end_of_file); + if (end_of_file) + return XDG_MIME_MAGIC_EOF; - end_ptr = buffer; - while (*end_ptr != ']' && *end_ptr != '\000' && *end_ptr != '\n') - end_ptr++; - if (*end_ptr != ']') + end_ptr = buffer; + while (*end_ptr != ']' && *end_ptr != '\000' && *end_ptr != '\n') + end_ptr++; + if (*end_ptr != ']') { - free (buffer); - return XDG_MIME_MAGIC_ERROR; + free(buffer); + return XDG_MIME_MAGIC_ERROR; } - *end_ptr = '\000'; + *end_ptr = '\000'; - match->mime_type = strdup (buffer); - free (buffer); + match->mime_type = strdup(buffer); + free(buffer); - return XDG_MIME_MAGIC_MAGIC; + return XDG_MIME_MAGIC_MAGIC; } static XdgMimeMagicState -_xdg_mime_magic_parse_error (FILE *magic_file) +_xdg_mime_magic_parse_error(FILE *magic_file) { - int c; + int c; - while (1) + while (1) { - c = getc_unlocked (magic_file); - if (c == EOF) - return XDG_MIME_MAGIC_EOF; - if (c == '\n') - return XDG_MIME_MAGIC_SECTION; + c = getc_unlocked(magic_file); + if (c == EOF) + return XDG_MIME_MAGIC_EOF; + if (c == '\n') + return XDG_MIME_MAGIC_SECTION; } } @@ -308,475 +308,476 @@ _xdg_mime_magic_parse_error (FILE *magic_file) * [ "&" mask ] [ "~" word-size ] [ "+" range-length ] "\n" */ static XdgMimeMagicState -_xdg_mime_magic_parse_magic_line (FILE *magic_file, - XdgMimeMagicMatch *match) +_xdg_mime_magic_parse_magic_line(FILE *magic_file, + XdgMimeMagicMatch *match) { - XdgMimeMagicMatchlet *matchlet; - int c; - int end_of_file; - int indent = 0; - size_t bytes_read; + XdgMimeMagicMatchlet *matchlet; + int c; + int end_of_file; + int indent = 0; + size_t bytes_read; - assert (magic_file != NULL); + assert(magic_file != NULL); - /* Sniff the buffer to make sure it's a valid line */ - c = getc_unlocked (magic_file); - if (c == EOF) - return XDG_MIME_MAGIC_EOF; - else if (c == '[') + /* Sniff the buffer to make sure it's a valid line */ + c = getc_unlocked(magic_file); + if (c == EOF) + return XDG_MIME_MAGIC_EOF; + else if (c == '[') { - ungetc (c, magic_file); - return XDG_MIME_MAGIC_SECTION; + ungetc(c, magic_file); + return XDG_MIME_MAGIC_SECTION; } - else if (c == '\n') - return XDG_MIME_MAGIC_MAGIC; + else if (c == '\n') + return XDG_MIME_MAGIC_MAGIC; - /* At this point, it must be a digit or a '>' */ - end_of_file = FALSE; - if (isdigit (c)) + /* At this point, it must be a digit or a '>' */ + end_of_file = FALSE; + if (isdigit(c)) { - 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; - if (indent == -1) - return XDG_MIME_MAGIC_ERROR; - c = getc_unlocked (magic_file); - if (c == EOF) - return XDG_MIME_MAGIC_EOF; + 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; + if (indent == -1) + return XDG_MIME_MAGIC_ERROR; + c = getc_unlocked(magic_file); + if (c == EOF) + return XDG_MIME_MAGIC_EOF; } - if (c != '>') - return XDG_MIME_MAGIC_ERROR; - - matchlet = _xdg_mime_magic_matchlet_new (); - matchlet->indent = indent; - matchlet->offset = _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; - } - if (matchlet->offset == -1) - { - _xdg_mime_magic_matchlet_free (matchlet); - return XDG_MIME_MAGIC_ERROR; - } - c = getc_unlocked (magic_file); - if (c == EOF) - { - _xdg_mime_magic_matchlet_free (matchlet); - return XDG_MIME_MAGIC_EOF; - } - else if (c != '=') - { - _xdg_mime_magic_matchlet_free (matchlet); - return XDG_MIME_MAGIC_ERROR; - } - - /* Next two bytes determine how long the value is */ - matchlet->value_length = 0; - c = getc_unlocked (magic_file); - if (c == EOF) - { - _xdg_mime_magic_matchlet_free (matchlet); - return XDG_MIME_MAGIC_EOF; - } - matchlet->value_length = c & 0xFF; - matchlet->value_length = matchlet->value_length << 8; - - c = getc_unlocked (magic_file); - if (c == EOF) - { - _xdg_mime_magic_matchlet_free (matchlet); - return XDG_MIME_MAGIC_EOF; - } - matchlet->value_length = matchlet->value_length + (c & 0xFF); - - matchlet->value = (unsigned char *)malloc (matchlet->value_length); - - /* OOM */ - if (matchlet->value == NULL) - { - _xdg_mime_magic_matchlet_free (matchlet); - return XDG_MIME_MAGIC_ERROR; - } - bytes_read = fread (matchlet->value, 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; - } - - c = getc_unlocked (magic_file); - if (c == '&') - { - matchlet->mask = (unsigned char *)malloc (matchlet->value_length); - /* OOM */ - if (matchlet->mask == NULL) - { - _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; - } - c = getc_unlocked (magic_file); - } - - if (c == '~') - { - 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; - } - 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; - } - c = getc_unlocked (magic_file); - } - - if (c == '+') - { - 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; - } - if (matchlet->range_length == (unsigned int)-1) - { - _xdg_mime_magic_matchlet_free (matchlet); - return XDG_MIME_MAGIC_ERROR; - } - c = getc_unlocked (magic_file); - } - - - if (c == '\n') - { - /* 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); + if (c != '>') return XDG_MIME_MAGIC_ERROR; - } - /* FIXME: need to get this defined in a style file */ + + matchlet = _xdg_mime_magic_matchlet_new(); + matchlet->indent = indent; + matchlet->offset = _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; + } + if (matchlet->offset == -1) + { + _xdg_mime_magic_matchlet_free(matchlet); + return XDG_MIME_MAGIC_ERROR; + } + c = getc_unlocked(magic_file); + if (c == EOF) + { + _xdg_mime_magic_matchlet_free(matchlet); + return XDG_MIME_MAGIC_EOF; + } + else if (c != '=') + { + _xdg_mime_magic_matchlet_free(matchlet); + return XDG_MIME_MAGIC_ERROR; + } + + /* Next two bytes determine how long the value is */ + matchlet->value_length = 0; + c = getc_unlocked(magic_file); + if (c == EOF) + { + _xdg_mime_magic_matchlet_free(matchlet); + return XDG_MIME_MAGIC_EOF; + } + matchlet->value_length = c & 0xFF; + matchlet->value_length = matchlet->value_length << 8; + + c = getc_unlocked(magic_file); + if (c == EOF) + { + _xdg_mime_magic_matchlet_free(matchlet); + return XDG_MIME_MAGIC_EOF; + } + matchlet->value_length = matchlet->value_length + (c & 0xFF); + + matchlet->value = (unsigned char *)malloc(matchlet->value_length); + + /* OOM */ + if (matchlet->value == NULL) + { + _xdg_mime_magic_matchlet_free(matchlet); + return XDG_MIME_MAGIC_ERROR; + } + bytes_read = fread(matchlet->value, 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; + } + + c = getc_unlocked(magic_file); + if (c == '&') + { + matchlet->mask = (unsigned char *)malloc(matchlet->value_length); + /* OOM */ + if (matchlet->mask == NULL) + { + _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; + } + c = getc_unlocked(magic_file); + } + + if (c == '~') + { + 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; + } + 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; + } + c = getc_unlocked(magic_file); + } + + if (c == '+') + { + 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; + } + if (matchlet->range_length == (unsigned int)-1) + { + _xdg_mime_magic_matchlet_free(matchlet); + return XDG_MIME_MAGIC_ERROR; + } + c = getc_unlocked(magic_file); + } + + + if (c == '\n') + { + /* 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 */ #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; + matchlet->next = match->matchlet; + match->matchlet = matchlet; - return XDG_MIME_MAGIC_MAGIC; + return XDG_MIME_MAGIC_MAGIC; } - _xdg_mime_magic_matchlet_free (matchlet); - if (c == EOF) - return XDG_MIME_MAGIC_EOF; + _xdg_mime_magic_matchlet_free(matchlet); + if (c == EOF) + return XDG_MIME_MAGIC_EOF; - return XDG_MIME_MAGIC_ERROR; + return XDG_MIME_MAGIC_ERROR; } static int -_xdg_mime_magic_matchlet_compare_to_data (XdgMimeMagicMatchlet *matchlet, - const void *data, - size_t len) +_xdg_mime_magic_matchlet_compare_to_data(XdgMimeMagicMatchlet *matchlet, + const void *data, + size_t len) { - size_t i, j; + size_t i, j; - for (i = matchlet->offset; i <= matchlet->offset + matchlet->range_length; i++) + for (i = matchlet->offset; i <= matchlet->offset + matchlet->range_length; i++) { - int valid_matchlet = TRUE; + int valid_matchlet = TRUE; - if (i + matchlet->value_length > len) - return FALSE; + if (i + matchlet->value_length > len) + 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; + 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; + } + } + } + else + { + 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; } - } - } - else - { - 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 FALSE; + return FALSE; } static int -_xdg_mime_magic_matchlet_compare_level (XdgMimeMagicMatchlet *matchlet, - const void *data, - size_t len, - int indent) +_xdg_mime_magic_matchlet_compare_level(XdgMimeMagicMatchlet *matchlet, + const void *data, + size_t len, + int indent) { - while ((matchlet != NULL) && (matchlet->indent == 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 (_xdg_mime_magic_matchlet_compare_to_data(matchlet, data, len)) + { + 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; - } - while (matchlet && matchlet->indent > indent); + do + { + matchlet = matchlet->next; + } + while (matchlet && matchlet->indent > indent); } - return FALSE; + return FALSE; } static int -_xdg_mime_magic_match_compare_to_data (XdgMimeMagicMatch *match, - const void *data, - size_t len) +_xdg_mime_magic_match_compare_to_data(XdgMimeMagicMatch *match, + const void *data, + size_t len) { - return _xdg_mime_magic_matchlet_compare_level (match->matchlet, data, len, 0); + return _xdg_mime_magic_matchlet_compare_level(match->matchlet, data, len, 0); } static void -_xdg_mime_magic_insert_match (XdgMimeMagic *mime_magic, - XdgMimeMagicMatch *match) +_xdg_mime_magic_insert_match(XdgMimeMagic *mime_magic, + XdgMimeMagicMatch *match) { - XdgMimeMagicMatch *list; + XdgMimeMagicMatch *list; - if (mime_magic->match_list == NULL) + if (mime_magic->match_list == NULL) { - mime_magic->match_list = match; - return; + mime_magic->match_list = match; + return; } - if (match->priority > mime_magic->match_list->priority) + if (match->priority > mime_magic->match_list->priority) { - match->next = mime_magic->match_list; - mime_magic->match_list = match; - return; + match->next = mime_magic->match_list; + mime_magic->match_list = match; + return; } - list = mime_magic->match_list; - while (list->next != NULL) + list = mime_magic->match_list; + while (list->next != NULL) { - if (list->next->priority < match->priority) - { - match->next = list->next; + if (list->next->priority < match->priority) + { + match->next = list->next; + list->next = match; + return; + } + list = list->next; + } list->next = match; - return; - } - list = list->next; - } - list->next = match; - match->next = NULL; + match->next = NULL; } XdgMimeMagic * -_xdg_mime_magic_new (void) +_xdg_mime_magic_new(void) { - return (XdgMimeMagic *)calloc (1, sizeof (XdgMimeMagic)); + return (XdgMimeMagic *)calloc(1, sizeof(XdgMimeMagic)); } void -_xdg_mime_magic_free (XdgMimeMagic *mime_magic) +_xdg_mime_magic_free(XdgMimeMagic *mime_magic) { - if (mime_magic) { - _xdg_mime_magic_match_free (mime_magic->match_list); - free (mime_magic); - } + if (mime_magic) + { + _xdg_mime_magic_match_free(mime_magic->match_list); + free(mime_magic); + } } int -_xdg_mime_magic_get_buffer_extents (XdgMimeMagic *mime_magic) +_xdg_mime_magic_get_buffer_extents(XdgMimeMagic *mime_magic) { - return mime_magic->max_extent; + return mime_magic->max_extent; } const char * -_xdg_mime_magic_lookup_data (XdgMimeMagic *mime_magic, - const void *data, - size_t len) +_xdg_mime_magic_lookup_data(XdgMimeMagic *mime_magic, + const void *data, + size_t len) { - XdgMimeMagicMatch *match; + XdgMimeMagicMatch *match; - for (match = mime_magic->match_list; match; match = match->next) + 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; - } + if (_xdg_mime_magic_match_compare_to_data(match, data, len)) + { + return match->mime_type; + } } - return NULL; + return NULL; } static void -_xdg_mime_update_mime_magic_extents (XdgMimeMagic *mime_magic) +_xdg_mime_update_mime_magic_extents(XdgMimeMagic *mime_magic) { - XdgMimeMagicMatch *match; - int max_extent = 0; + XdgMimeMagicMatch *match; + int max_extent = 0; - for (match = mime_magic->match_list; match; match = match->next) + for (match = mime_magic->match_list; match; match = match->next) { - XdgMimeMagicMatchlet *matchlet; + XdgMimeMagicMatchlet *matchlet; - for (matchlet = match->matchlet; matchlet; matchlet = matchlet->next) - { - int extent; + for (matchlet = match->matchlet; matchlet; matchlet = matchlet->next) + { + 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; + mime_magic->max_extent = max_extent; } static XdgMimeMagicMatchlet * -_xdg_mime_magic_matchlet_mirror (XdgMimeMagicMatchlet *matchlets) +_xdg_mime_magic_matchlet_mirror(XdgMimeMagicMatchlet *matchlets) { - XdgMimeMagicMatchlet *new_list; - XdgMimeMagicMatchlet *tmp; + XdgMimeMagicMatchlet *new_list; + XdgMimeMagicMatchlet *tmp; - if ((matchlets == NULL) || (matchlets->next == NULL)) - return matchlets; + if ((matchlets == NULL) || (matchlets->next == NULL)) + return matchlets; - new_list = NULL; - tmp = matchlets; - while (tmp != NULL) + new_list = NULL; + tmp = matchlets; + while (tmp != NULL) { - XdgMimeMagicMatchlet *matchlet; + XdgMimeMagicMatchlet *matchlet; - matchlet = tmp; - tmp = tmp->next; - matchlet->next = new_list; - new_list = matchlet; + matchlet = tmp; + tmp = tmp->next; + matchlet->next = new_list; + new_list = matchlet; } - return new_list; + return new_list; } static void -_xdg_mime_magic_read_magic_file (XdgMimeMagic *mime_magic, - FILE *magic_file) +_xdg_mime_magic_read_magic_file(XdgMimeMagic *mime_magic, + FILE *magic_file) { - XdgMimeMagicState state; - XdgMimeMagicMatch *match = NULL; /* Quiet compiler */ + XdgMimeMagicState state; + XdgMimeMagicMatch *match = NULL; /* Quiet compiler */ - state = XDG_MIME_MAGIC_SECTION; + state = XDG_MIME_MAGIC_SECTION; - while (state != XDG_MIME_MAGIC_EOF) + 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); - } + 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); + } } - _xdg_mime_update_mime_magic_extents (mime_magic); + _xdg_mime_update_mime_magic_extents(mime_magic); } void -_xdg_mime_magic_read_from_file (XdgMimeMagic *mime_magic, - const char *file_name) +_xdg_mime_magic_read_from_file(XdgMimeMagic *mime_magic, + const char *file_name) { - FILE *magic_file; - char header[12]; + FILE *magic_file; + char header[12]; - /* OK to not use CLO_EXEC here because mimedb is single threaded */ - magic_file = fopen (file_name, "r"); + /* OK to not use CLO_EXEC here because mimedb is single threaded */ + magic_file = fopen(file_name, "r"); - if (magic_file == NULL) - return; + if (magic_file == NULL) + return; - if (fread (header, 1, 12, magic_file) == 12) + if (fread(header, 1, 12, magic_file) == 12) { - if (memcmp ("MIME-Magic\0\n", header, 12) == 0) - _xdg_mime_magic_read_magic_file (mime_magic, magic_file); + if (memcmp("MIME-Magic\0\n", header, 12) == 0) + _xdg_mime_magic_read_magic_file(mime_magic, magic_file); } - fclose (magic_file); + fclose(magic_file); } diff --git a/xdgmimemagic.h b/xdgmimemagic.h index 558032681..61e9d20db 100644 --- a/xdgmimemagic.h +++ b/xdgmimemagic.h @@ -42,13 +42,13 @@ typedef struct XdgMimeMagic XdgMimeMagic; #endif -XdgMimeMagic *_xdg_mime_magic_new (void); -void _xdg_mime_magic_read_from_file (XdgMimeMagic *mime_magic, - 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); +XdgMimeMagic *_xdg_mime_magic_new(void); +void _xdg_mime_magic_read_from_file(XdgMimeMagic *mime_magic, + 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); #endif /* __XDG_MIME_MAGIC_H__ */ diff --git a/xdgmimeparent.cpp b/xdgmimeparent.cpp index 3024d9f9d..3e51c295c 100644 --- a/xdgmimeparent.cpp +++ b/xdgmimeparent.cpp @@ -49,171 +49,171 @@ typedef struct XdgMimeParents XdgMimeParents; struct XdgMimeParents { - char *mime; - char **parents; - int n_parents; + char *mime; + char **parents; + int n_parents; }; struct XdgParentList { - struct XdgMimeParents *parents; - int n_mimes; + struct XdgMimeParents *parents; + int n_mimes; }; XdgParentList * -_xdg_mime_parent_list_new (void) +_xdg_mime_parent_list_new(void) { - XdgParentList *list; + XdgParentList *list; - list = (XdgParentList *)malloc (sizeof (XdgParentList)); + list = (XdgParentList *)malloc(sizeof(XdgParentList)); - list->parents = NULL; - list->n_mimes = 0; + list->parents = NULL; + list->n_mimes = 0; - return list; + return list; } void -_xdg_mime_parent_list_free (XdgParentList *list) +_xdg_mime_parent_list_free(XdgParentList *list) { - int i; - char **p; + int i; + char **p; - if (list->parents) + if (list->parents) { - for (i = 0; i < list->n_mimes; i++) - { - for (p = list->parents[i].parents; *p; p++) - free (*p); + for (i = 0; i < list->n_mimes; i++) + { + for (p = list->parents[i].parents; *p; p++) + free(*p); - free (list->parents[i].parents); - free (list->parents[i].mime); - } - free (list->parents); + free(list->parents[i].parents); + free(list->parents[i].mime); + } + free(list->parents); } - free (list); + free(list); } static int -parent_entry_cmp (const void *v1, const void *v2) +parent_entry_cmp(const void *v1, const void *v2) { - return strcmp (((XdgMimeParents *)v1)->mime, ((XdgMimeParents *)v2)->mime); + return strcmp(((XdgMimeParents *)v1)->mime, ((XdgMimeParents *)v2)->mime); } const char ** -_xdg_mime_parent_list_lookup (XdgParentList *list, - const char *mime) +_xdg_mime_parent_list_lookup(XdgParentList *list, + const char *mime) { - XdgMimeParents *entry; - XdgMimeParents key; + XdgMimeParents *entry; + XdgMimeParents key; - if (list->n_mimes > 0) + if (list->n_mimes > 0) { - key.mime = (char *)mime; - key.parents = NULL; + key.mime = (char *)mime; + key.parents = NULL; - entry = (XdgMimeParents *)bsearch (&key, list->parents, list->n_mimes, - sizeof (XdgMimeParents), &parent_entry_cmp); - if (entry) - return (const char **)entry->parents; + entry = (XdgMimeParents *)bsearch(&key, list->parents, list->n_mimes, + sizeof(XdgMimeParents), &parent_entry_cmp); + if (entry) + return (const char **)entry->parents; } - return NULL; + return NULL; } void -_xdg_mime_parent_read_from_file (XdgParentList *list, - const char *file_name) +_xdg_mime_parent_read_from_file(XdgParentList *list, + const char *file_name) { - FILE *file; - char line[255]; - int i, alloc; - XdgMimeParents *entry; + FILE *file; + char line[255]; + int i, alloc; + XdgMimeParents *entry; - /* OK to not use CLO_EXEC here because mimedb is single threaded */ - file = fopen (file_name, "r"); + /* OK to not use CLO_EXEC here because mimedb is single threaded */ + file = fopen(file_name, "r"); - if (file == NULL) - return; + if (file == NULL) + return; - /* FIXME: Not UTF-8 safe. Doesn't work if lines are greater than 255 chars. - * Blah */ - alloc = list->n_mimes + 16; - list->parents = (XdgMimeParents *)realloc (list->parents, alloc * sizeof (XdgMimeParents)); - while (fgets (line, 255, file) != NULL) + /* FIXME: Not UTF-8 safe. Doesn't work if lines are greater than 255 chars. + * Blah */ + alloc = list->n_mimes + 16; + list->parents = (XdgMimeParents *)realloc(list->parents, alloc * sizeof(XdgMimeParents)); + while (fgets(line, 255, file) != NULL) { - char *sep; - if (line[0] == '#') - continue; + char *sep; + if (line[0] == '#') + continue; - sep = strchr (line, ' '); - if (sep == NULL) - 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; - } - } + sep = strchr(line, ' '); + if (sep == NULL) + 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 (!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 (!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 (!entry->parents) - { - 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->parents[entry->n_parents - 1] = strdup (sep); - entry->parents[entry->n_parents] = NULL; + if (!entry->parents) + { + 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->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, - sizeof (XdgMimeParents), &parent_entry_cmp); + if (list->n_mimes > 1) + qsort(list->parents, list->n_mimes, + sizeof(XdgMimeParents), &parent_entry_cmp); } void -_xdg_mime_parent_list_dump (XdgParentList *list) +_xdg_mime_parent_list_dump(XdgParentList *list) { - int i; - char **p; + int i; + char **p; - if (list->parents) + 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 (i = 0; i < list->n_mimes; i++) + { + 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 a10846b39..9baf44a3e 100644 --- a/xdgmimeparent.h +++ b/xdgmimeparent.h @@ -39,12 +39,12 @@ typedef struct XdgParentList XdgParentList; #define _xdg_mime_parent_list_lookup XDG_ENTRY(parent_list_lookup) #endif -void _xdg_mime_parent_read_from_file (XdgParentList *list, - 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); -void _xdg_mime_parent_list_dump (XdgParentList *list); +void _xdg_mime_parent_read_from_file(XdgParentList *list, + 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); +void _xdg_mime_parent_list_dump(XdgParentList *list); #endif /* __XDG_MIME_PARENT_H__ */